61 unsigned max_buff_size = 0;
63 bool preorder(
const IR::BFN::PacketRVal *rval)
override {
64 auto extract = findContext<IR::BFN::Extract>();
65 if (extract && rval->range.lo < 0) {
66 auto state = findContext<IR::BFN::ParserState>();
67 unsigned shift = (-rval->range.lo + 7) / 8;
68 if (shift > max_buff_size) {
69 LOG1(
"State " << state->name <<
" requires " << shift <<
" B shift");
70 historic_states[state] = std::max(historic_states[state], shift);
71 parsers[state] = findContext<IR::BFN::Parser>();
78 profile_t init_apply(
const IR::Node *node)
override {
79 auto rv = ParserInspector::init_apply(node);
83 historic_states.clear();
88 void end_apply()
override {
90 for (
auto kv : historic_states) {
92 auto state = kv.first;
93 auto max_idx_value = kv.second;
95 unsigned delay_shift =
96 delay_shift_from_predecessor(state, parsers[state], max_idx_value);
97 BUG_CHECK(delay_shift,
98 "In parse state %s: a value that is %d B backwards from the current "
99 "parsing position is being accessed/used. Unable to identify an "
100 "amount to delay the previuos shift by to allow access to this data. "
101 "As a possible workaround try moving around the extracts (possibly by "
102 "using methods advance and lookahead or splitting some headers).",
103 state->name, max_idx_value);
105 LOG3(
"State " << state->name <<
" needs a value " << max_idx_value
106 <<
"B back and generates a delay shift of " << delay_shift <<
"B");
109 LOG3(
"CollectNegativeExtractOutOfBufferStates has finished.");
119 std::map<const IR::BFN::ParserState *, unsigned> historic_states;
125 std::map<const IR::BFN::ParserState *, const IR::BFN::Parser *> parsers;
135 unsigned delay_shift_from_predecessor(
const IR::BFN::ParserState *state,
136 const IR::BFN::Parser *parser,
int required_history) {
137 BUG_CHECK(state,
"Parser state cannot be null!");
139 auto graph = parserInfo.graph(parser);
140 auto preds = graph.predecessors().at(state);
141 if (preds.size() > 1) {
143 "Cannot resolve negative extract because of multiple paths to "
147 const auto *pred = *preds.begin();
149 unsigned curr_shift = 0;
150 std::for_each(pred->transitions.begin(), pred->transitions.end(),
151 [&curr_shift](
const auto *tr) {
152 curr_shift = curr_shift > tr->shift ? curr_shift : tr->shift;
154 unsigned min_shift = required_history;
155 unsigned max_keep = curr_shift - min_shift;
158 std::map<int, int> end_to_earliest_start;
159 for (
const auto *stmt : pred->statements) {
160 if (
const auto *extract = stmt->to<IR::BFN::Extract>()) {
161 if (
const auto *rval = extract->source->to<IR::BFN::InputBufferRVal>()) {
162 auto range = rval->range;
163 auto hi_byte = range.hiByte();
164 auto lo_byte = range.loByte();
165 if (end_to_earliest_start.count(hi_byte))
166 end_to_earliest_start[hi_byte] =
167 std::min(lo_byte, end_to_earliest_start[hi_byte]);
169 end_to_earliest_start[hi_byte] = lo_byte;
176 if (end_to_earliest_start.size() == 0 ||
177 end_to_earliest_start.rbegin()->first <
static_cast<int>(max_keep)) {
190 int max_end = end_to_earliest_start.rbegin()->first;
191 std::vector<int> end_to_earliest_start_merged(max_end + 1);
192 for (
int i = 0; i <= max_end; i++) {
193 end_to_earliest_start_merged[i] = i;
195 if (end_to_earliest_start.count(i))
197 std::min(end_to_earliest_start[i], end_to_earliest_start_merged[min_idx]);
198 for (
int j = std::max(0, min_idx); j <= i; j++)
199 end_to_earliest_start_merged[j] = min_idx;
204 if (end_to_earliest_start_merged[max_keep] > 0) {
205 unsigned keep =
static_cast<unsigned>(end_to_earliest_start_merged[max_keep]);
206 unsigned delay_shift = curr_shift - keep;
224 : collectNegative(cg) {}
226 std::map<cstring, std::vector<const IR::BFN::Extract *>> delayed_statements;
227 std::map<cstring, unsigned> state_delay;
228 std::map<cstring, cstring> state_pred;
230 unsigned max_buff_size = 0;
233 auto rv = ParserModifier::init_apply(node);
235 delayed_statements.clear();
241 bool preorder(IR::BFN::ParserState *state)
override {
245 unsigned delay_shift = collectNegative.
state_to_shift.at(state->name);
248 unsigned curr_shift = 0;
249 std::for_each(state->transitions.begin(), state->transitions.end(),
250 [
this, state, &curr_shift, delay_shift](
const auto *tr) {
251 curr_shift = curr_shift > tr->shift ? curr_shift : tr->shift;
253 this->state_delay[tr->next->name] = delay_shift;
254 this->state_pred[tr->next->name] = state->name;
259 unsigned pending_shift =
260 state_delay.count(state->name) ? state_delay.at(state->name) : 0;
265 for (
const auto *stmt : state->statements) {
267 if (
const auto *extract = stmt->to<IR::BFN::Extract>()) {
268 if (
const auto *rval = extract->source->to<IR::BFN::InputBufferRVal>()) {
269 if (rval->range.hiByte() >=
static_cast<int>(max_buff_size)) {
270 auto *rval_clone = rval->clone();
271 rval_clone->range.hi -= (curr_shift - pending_shift) * 8;
272 rval_clone->range.lo -= (curr_shift - pending_shift) * 8;
273 auto *extract_clone = extract->clone();
274 extract_clone->source = rval_clone;
275 delayed_statements[state->name].push_back(extract_clone);
280 if (keep) new_statements.push_back(stmt);
282 state->statements = new_statements;
286 if (state_delay.count(state->name)) {
287 cstring pred = state_pred[state->name];
291 for (
const auto *stmt : delayed_statements[pred])
292 new_statements.push_back(stmt->clone());
293 new_statements.insert(new_statements.end(), state->statements.begin(),
294 state->statements.end());
295 state->statements = new_statements;
301 bool preorder(IR::BFN::Transition *transition)
override {
302 auto state = findContext<IR::BFN::ParserState>();
303 BUG_CHECK(state,
"State cannot be null!");
305 if (collectNegative.state_to_shift.count(state->name)) {
306 transition->shift -= collectNegative.state_to_shift.at(state->name);
307 LOG3(
"Adjusting transition from " << state->name <<
", match { "
309 <<
" } to shift value = " << transition->shift);
311 if (state_delay.count(state->name)) {
312 transition->shift += state_delay.at(state->name);
313 LOG3(
"Adjusting transition from " << state->name <<
", match { "
315 <<
" } to shift value = " << transition->shift);
321 bool preorder(IR::BFN::PacketRVal *rval)
override {
322 auto state = findContext<IR::BFN::ParserState>();
323 auto extract = findContext<IR::BFN::Extract>();
325 if (state_delay.count(state->name)) {
326 unsigned shift = state_delay.at(state->name) * 8;
327 rval->range.lo += shift;
328 rval->range.hi += shift;
330 LOG3(
"Adjusting field " << extract->dest->field->toString() <<
" to "
331 << shift / 8 <<
" byte offset (lo = " << rval->range.lo
332 <<
", hi = " << rval->range.hi <<
")");
395 std::map<const IR::BFN::Transition *, std::pair<const IR::BFN::ParserState *, int>>
400 bool preorder(
const IR::BFN::PacketRVal *rval)
override {
401 auto extract = findContext<IR::BFN::Extract>();
402 if (extract && rval->range.lo < 0) {
403 auto state = findContext<IR::BFN::ParserState>();
404 unsigned shift = (-rval->range.lo + 7) / 8;
406 LOG1(
"State " << state->name <<
" requires " << shift <<
" B shift");
407 historic_states[state] = std::max(historic_states[state], shift);
408 parsers[state] = findContext<IR::BFN::Parser>();
414 profile_t init_apply(
const IR::Node *node)
override {
415 auto rv = ParserInspector::init_apply(node);
417 transition_shift.clear();
418 state_to_shift.clear();
419 historic_states.clear();
420 state_to_duplicate.clear();
425 void end_apply()
override {
428 for (
auto kv : historic_states) {
430 auto state = kv.first;
431 auto max_idx_value = kv.second;
432 BUG_CHECK(max_idx_value <= max_buff_size,
433 "In parse state %s: a value that is %d B backwards from the current "
434 "parsing position is being accessed/used. It is only possible to "
435 "access %d B backwards from the current parsing position. As a "
436 "possible workaround try moving around the extracts (possibly "
437 "by using methods advance and lookahead or splitting some headers).",
438 state->name, max_idx_value, max_buff_size);
440 distribute_shift_to_node(state,
nullptr, parsers[state], max_idx_value);
446 for (
auto trans : state->transitions) {
447 LOG1(
" state has transitions " << trans);
448 unsigned shift_value = trans->shift + max_idx_value;
449 transition_shift[state->name][trans->value] = shift_value;
453 for (
auto kv : state_to_shift)
454 LOG3(kv.first <<
" needs " << kv.second <<
" bytes of shift");
456 for (
auto kv : transition_shift) {
457 for (
auto tr_config : kv.second) {
458 std::stringstream ss;
459 ss <<
"Transition with match { " << tr_config.first <<
" } from state "
460 << kv.first <<
" needs will be set with the shift value "
466 LOG3(
"ResolveNegativeExtract has been finished.");
476 std::map<const IR::BFN::ParserState *, unsigned> historic_states;
482 std::map<const IR::BFN::ParserState *, const IR::BFN::Parser *> parsers;
491 unsigned get_transition_shift(
const IR::BFN::ParserState *src,
492 const IR::BFN::Transition *tr) {
493 BUG_CHECK(tr !=
nullptr,
"Transition node cannot be null!");
494 if (transition_shift.count(src->name) &&
495 transition_shift.at(src->name).count(tr->value)) {
496 return transition_shift[src->name][tr->value];
517 void adjust_shift_buffer(
const IR::BFN::ParserState *state,
518 const IR::BFN::ParserState *state_child,
519 const IR::BFN::Parser *parser,
unsigned tr_shift,
520 unsigned state_shift) {
521 auto graph = parserInfo.graph(parser);
522 for (
auto state_trans : state->transitions) {
523 auto state_succ = state_trans->next;
524 transition_shift[state->name][state_trans->value] = tr_shift;
525 LOG4(
"Adding transition { " << state_trans->value <<
" } shift value " << tr_shift
526 <<
" B from state " << state->name <<
" to state "
527 << state_succ->name);
533 remainder_before_exit[state_trans] = state_shift;
537 if (graph.predecessors().at(state_succ).size() <= 1) {
538 state_to_shift[state_succ->name] = state_shift;
539 LOG4(
"Setting shift value " << state_shift <<
" B for state "
540 << state_succ->name);
545 if (state_succ == state_child) {
546 LOG4(
"Skipping transition adjustment for " << state_succ->name
547 <<
" (will be set later).");
555 if (graph.predecessors().at(state_succ).size() > 1) {
556 state_to_duplicate.emplace(state_trans,
557 std::make_pair(state_succ, state_shift));
561 for (
auto succ_tr : state_succ->transitions) {
562 if (transition_shift[state_succ->name].count(succ_tr->value) > 0)
continue;
564 unsigned new_shift = get_transition_shift(state_succ, succ_tr) + state_shift;
565 transition_shift[state_succ->name][succ_tr->value] = new_shift;
566 LOG4(
"Adding transition { "
567 << succ_tr->value <<
" } shift value " << new_shift <<
" B from state "
568 << state_succ->name <<
" to state "
569 << (succ_tr->next !=
nullptr ? succ_tr->next->name :
"EXIT"));
582 void distribute_shift_to_node(
const IR::BFN::ParserState *state,
583 const IR::BFN::ParserState *succ,
584 const IR::BFN::Parser *parser,
int required_history) {
587 BUG_CHECK(state,
"Parser state cannot be null!");
588 if (state_to_shift.count(state->name) > 0) {
590 "Current path with historic data usage has an intersection with"
591 " a previously analyzed historic data path at node %1%!",
595 int deficit = required_history;
596 auto graph = parserInfo.graph(parser);
597 auto transitions = graph.transitions(state, succ);
600 LOG5(
"Transitions size from state " << state->name <<
" to state " << succ->name
601 <<
" is " << transitions.size());
603 if (transitions.size() > 0 && required_history > 0) {
606 unsigned shift_value = get_transition_shift(state, *transitions.begin());
607 deficit = required_history - shift_value;
610 LOG4(
"Shift distribution for node " << state->name
611 <<
", to distribute = " << required_history
612 <<
" (deficit = " << deficit <<
" B)");
619 auto preds = graph.predecessors().at(state);
620 if (preds.size() > 1) {
622 "Cannot resolve negative extract because of multiple paths to "
626 distribute_shift_to_node(*preds.begin(), state, parser, deficit);
630 if (transitions.size() == 0 && !succ) {
631 LOG4(
"Initial node " << state->name <<
" has been reached.");
640 const int old_tr_shift = get_transition_shift(state, *transitions.begin());
643 int new_state_shift = old_tr_shift + deficit;
644 int new_tr_shift = 0;
648 new_tr_shift = -deficit;
650 Log::TempIndent indent;
651 LOG4(
"Adjusting shift for state "
652 << state->name <<
" and transition to state " << succ->name
653 <<
" (new transition shift = " << new_tr_shift <<
" B)" << indent);
654 adjust_shift_buffer(state, succ, parser, new_tr_shift, new_state_shift);
663 std::map<const IR::BFN::ParserState *, std::pair<IR::BFN::ParserState *, int>>
667 auto rv = ParserModifier::init_apply(node);
668 duplicated_states.clear();
672 bool preorder(IR::BFN::Transition *transition)
override {
673 auto state = findContext<IR::BFN::ParserState>();
674 auto orig_transition = getOriginal()->to<IR::BFN::Transition>();
675 BUG_CHECK(state,
"State cannot be null!");
676 BUG_CHECK(orig_transition,
"Original IR::BFN::Transition cannot be null!");
679 collectNegative.
transition_shift.at(state->name).count(orig_transition->value)) {
681 transition->shift = tr_map.at(orig_transition->value);
682 LOG3(
"Adjusting transition from " << state->name <<
", match { "
683 << orig_transition->value
684 <<
" } to shift value = " << transition->shift);
693 auto remainder_state =
new IR::BFN::ParserState(
694 state->p4States, state->name +
"$final_shift", state->gress);
695 transition->next = remainder_state;
696 auto end_transition =
new IR::BFN::Transition(
match_t(), state_shift);
697 remainder_state->transitions.push_back(end_transition);
698 LOG5(
"Transition from state "
699 << state->name <<
" with match value " << orig_transition->value
700 <<
" leads to exit, adding new state " << remainder_state->name
701 <<
" to consume " << state_shift <<
" bytes.");
705 auto [orig_state, state_shift] =
707 LOG5(
"Duplicating transition from state " << state->name <<
" with match value "
708 << orig_transition->value <<
" to state "
709 << orig_state->name);
711 IR::BFN::ParserState *duplicated_state =
nullptr;
712 if (duplicated_states.count(orig_state)) {
713 int prev_state_shift;
714 std::tie(duplicated_state, prev_state_shift) = duplicated_states[orig_state];
715 BUG_CHECK(state_shift == prev_state_shift,
716 "New state shift %1% does not match previous "
717 "value %2% for duplicated state %3%",
718 state_shift, prev_state_shift, state->name);
720 duplicated_state =
new IR::BFN::ParserState(
721 orig_state->p4States, orig_state->name +
"$dup", orig_state->gress);
722 for (
auto tr : orig_state->transitions) {
723 auto new_trans =
new IR::BFN::Transition(tr->srcInfo, tr->value,
724 tr->shift + state_shift, tr->next);
725 duplicated_state->transitions.push_back(new_trans);
727 for (
const auto stmt : orig_state->statements) {
728 duplicated_state->statements.push_back(stmt->clone());
730 duplicated_states.emplace(orig_state,
731 std::make_pair(duplicated_state, state_shift));
734 transition->next = duplicated_state;
740 bool preorder(IR::BFN::PacketRVal *rval)
override {
741 auto state = findContext<IR::BFN::ParserState>();
742 auto extract = findContext<IR::BFN::Extract>();
745 unsigned shift = collectNegative.
state_to_shift.at(state->name) * 8;
746 rval->range.lo += shift;
747 rval->range.hi += shift;
749 LOG3(
"Adjusting field " << extract->dest->field->toString() <<
" to "
750 << shift / 8 <<
" byte offset (lo = " << rval->range.lo
751 <<
", hi = " << rval->range.hi <<
")");