50 const CollectParserInfo &parserInfo;
57 explicit CollectNegativeExtractOutOfBufferStates(
const CollectParserInfo &pi)
60 unsigned max_buff_size = 0;
62 bool preorder(
const IR::BFN::PacketRVal *rval)
override {
63 auto extract = findContext<IR::BFN::Extract>();
64 if (extract && rval->range.lo < 0) {
65 auto state = findContext<IR::BFN::ParserState>();
66 unsigned shift = (-rval->range.lo + 7) / 8;
67 if (shift > max_buff_size) {
68 LOG1(
"State " << state->name <<
" requires " << shift <<
" B shift");
69 historic_states[state] = std::max(historic_states[state], shift);
70 parsers[state] = findContext<IR::BFN::Parser>();
77 profile_t init_apply(
const IR::Node *node)
override {
78 auto rv = ParserInspector::init_apply(node);
82 historic_states.clear();
87 void end_apply()
override {
89 for (
auto kv : historic_states) {
91 auto state = kv.first;
92 auto max_idx_value = kv.second;
94 unsigned delay_shift =
95 delay_shift_from_predecessor(state, parsers[state], max_idx_value);
96 BUG_CHECK(delay_shift,
97 "In parse state %s: a value that is %d B backwards from the current "
98 "parsing position is being accessed/used. Unable to identify an "
99 "amount to delay the previuos shift by to allow access to this data. "
100 "As a possible workaround try moving around the extracts (possibly by "
101 "using methods advance and lookahead or splitting some headers).",
102 state->name, max_idx_value);
104 LOG3(
"State " << state->name <<
" needs a value " << max_idx_value
105 <<
"B back and generates a delay shift of " << delay_shift <<
"B");
108 LOG3(
"CollectNegativeExtractOutOfBufferStates has finished.");
118 std::map<const IR::BFN::ParserState *, unsigned> historic_states;
124 std::map<const IR::BFN::ParserState *, const IR::BFN::Parser *> parsers;
134 unsigned delay_shift_from_predecessor(
const IR::BFN::ParserState *state,
135 const IR::BFN::Parser *parser,
int required_history) {
136 BUG_CHECK(state,
"Parser state cannot be null!");
138 auto graph = parserInfo.graph(parser);
139 auto preds = graph.predecessors().at(state);
140 if (preds.size() > 1) {
142 "Cannot resolve negative extract because of multiple paths to "
146 const auto *pred = *preds.begin();
148 unsigned curr_shift = 0;
149 std::for_each(pred->transitions.begin(), pred->transitions.end(),
150 [&curr_shift](
const auto *tr) {
151 curr_shift = curr_shift > tr->shift ? curr_shift : tr->shift;
153 unsigned min_shift = required_history;
154 unsigned max_keep = curr_shift - min_shift;
157 std::map<int, int> end_to_earliest_start;
158 for (
const auto *stmt : pred->statements) {
159 if (
const auto *extract = stmt->to<IR::BFN::Extract>()) {
160 if (
const auto *rval = extract->source->to<IR::BFN::InputBufferRVal>()) {
161 auto range = rval->range;
162 auto hi_byte = range.hiByte();
163 auto lo_byte = range.loByte();
164 if (end_to_earliest_start.count(hi_byte))
165 end_to_earliest_start[hi_byte] =
166 std::min(lo_byte, end_to_earliest_start[hi_byte]);
168 end_to_earliest_start[hi_byte] = lo_byte;
175 if (end_to_earliest_start.size() == 0 ||
176 end_to_earliest_start.rbegin()->first <
static_cast<int>(max_keep)) {
189 int max_end = end_to_earliest_start.rbegin()->first;
190 std::vector<int> end_to_earliest_start_merged(max_end + 1);
191 for (
int i = 0; i <= max_end; i++) {
192 end_to_earliest_start_merged[i] = i;
194 if (end_to_earliest_start.count(i))
196 std::min(end_to_earliest_start[i], end_to_earliest_start_merged[min_idx]);
197 for (
int j = std::max(0, min_idx); j <= i; j++)
198 end_to_earliest_start_merged[j] = min_idx;
203 if (end_to_earliest_start_merged[max_keep] > 0) {
204 unsigned keep =
static_cast<unsigned>(end_to_earliest_start_merged[max_keep]);
205 unsigned delay_shift = curr_shift - keep;
223 : collectNegative(cg) {}
225 std::map<cstring, std::vector<const IR::BFN::Extract *>> delayed_statements;
226 std::map<cstring, unsigned> state_delay;
227 std::map<cstring, cstring> state_pred;
229 unsigned max_buff_size = 0;
232 auto rv = ParserModifier::init_apply(node);
233 max_buff_size = Device::pardeSpec().byteInputBufferSize();
234 delayed_statements.clear();
240 bool preorder(IR::BFN::ParserState *state)
override {
242 if (collectNegative.state_to_shift.count(state->name)) {
244 unsigned delay_shift = collectNegative.state_to_shift.at(state->name);
247 unsigned curr_shift = 0;
248 std::for_each(state->transitions.begin(), state->transitions.end(),
249 [
this, state, &curr_shift, delay_shift](
const auto *tr) {
250 curr_shift = curr_shift > tr->shift ? curr_shift : tr->shift;
252 this->state_delay[tr->next->name] = delay_shift;
253 this->state_pred[tr->next->name] = state->name;
258 unsigned pending_shift =
259 state_delay.count(state->name) ? state_delay.at(state->name) : 0;
264 for (
const auto *stmt : state->statements) {
266 if (
const auto *extract = stmt->to<IR::BFN::Extract>()) {
267 if (
const auto *rval = extract->source->to<IR::BFN::InputBufferRVal>()) {
268 if (rval->range.hiByte() >=
static_cast<int>(max_buff_size)) {
269 auto *rval_clone = rval->clone();
270 rval_clone->range.hi -= (curr_shift - pending_shift) * 8;
271 rval_clone->range.lo -= (curr_shift - pending_shift) * 8;
272 auto *extract_clone = extract->clone();
273 extract_clone->source = rval_clone;
274 delayed_statements[state->name].push_back(extract_clone);
279 if (keep) new_statements.push_back(stmt);
281 state->statements = new_statements;
285 if (state_delay.count(state->name)) {
286 cstring pred = state_pred[state->name];
290 for (
const auto *stmt : delayed_statements[pred])
291 new_statements.push_back(stmt->clone());
292 new_statements.insert(new_statements.end(), state->statements.begin(),
293 state->statements.end());
294 state->statements = new_statements;
300 bool preorder(IR::BFN::Transition *transition)
override {
301 auto state = findContext<IR::BFN::ParserState>();
302 BUG_CHECK(state,
"State cannot be null!");
304 if (collectNegative.state_to_shift.count(state->name)) {
305 transition->shift -= collectNegative.state_to_shift.at(state->name);
306 LOG3(
"Adjusting transition from " << state->name <<
", match { "
308 <<
" } to shift value = " << transition->shift);
310 if (state_delay.count(state->name)) {
311 transition->shift += state_delay.at(state->name);
312 LOG3(
"Adjusting transition from " << state->name <<
", match { "
314 <<
" } to shift value = " << transition->shift);
320 bool preorder(IR::BFN::PacketRVal *rval)
override {
321 auto state = findContext<IR::BFN::ParserState>();
322 auto extract = findContext<IR::BFN::Extract>();
324 if (state_delay.count(state->name)) {
325 unsigned shift = state_delay.at(state->name) * 8;
326 rval->range.lo += shift;
327 rval->range.hi += shift;
329 LOG3(
"Adjusting field " << extract->dest->field->toString() <<
" to "
330 << shift / 8 <<
" byte offset (lo = " << rval->range.lo
331 <<
", hi = " << rval->range.hi <<
")");
342 const CollectParserInfo &parserInfo;
394 std::map<const IR::BFN::Transition *, std::pair<const IR::BFN::ParserState *, int>>
397 explicit CollectNegativeExtractStates(
const CollectParserInfo &pi) : parserInfo(pi) {}
399 bool preorder(
const IR::BFN::PacketRVal *rval)
override {
400 auto extract = findContext<IR::BFN::Extract>();
401 if (extract && rval->range.lo < 0) {
402 auto state = findContext<IR::BFN::ParserState>();
403 unsigned shift = (-rval->range.lo + 7) / 8;
405 LOG1(
"State " << state->name <<
" requires " << shift <<
" B shift");
406 historic_states[state] = std::max(historic_states[state], shift);
407 parsers[state] = findContext<IR::BFN::Parser>();
413 profile_t init_apply(
const IR::Node *node)
override {
414 auto rv = ParserInspector::init_apply(node);
416 transition_shift.clear();
417 state_to_shift.clear();
418 historic_states.clear();
419 state_to_duplicate.clear();
424 void end_apply()
override {
427 for (
auto kv : historic_states) {
429 auto state = kv.first;
430 auto max_idx_value = kv.second;
431 BUG_CHECK(max_idx_value <= max_buff_size,
432 "In parse state %s: a value that is %d B backwards from the current "
433 "parsing position is being accessed/used. It is only possible to "
434 "access %d B backwards from the current parsing position. As a "
435 "possible workaround try moving around the extracts (possibly "
436 "by using methods advance and lookahead or splitting some headers).",
437 state->name, max_idx_value, max_buff_size);
439 distribute_shift_to_node(state,
nullptr, parsers[state], max_idx_value);
445 for (
auto trans : state->transitions) {
446 LOG1(
" state has transitions " << trans);
447 unsigned shift_value = trans->shift + max_idx_value;
448 transition_shift[state->name][trans->value] = shift_value;
452 for (
auto kv : state_to_shift)
453 LOG3(kv.first <<
" needs " << kv.second <<
" bytes of shift");
455 for (
auto kv : transition_shift) {
456 for (
auto tr_config : kv.second) {
457 std::stringstream ss;
458 ss <<
"Transition with match { " << tr_config.first <<
" } from state "
459 << kv.first <<
" needs will be set with the shift value "
465 LOG3(
"ResolveNegativeExtract has been finished.");
475 std::map<const IR::BFN::ParserState *, unsigned> historic_states;
481 std::map<const IR::BFN::ParserState *, const IR::BFN::Parser *> parsers;
490 unsigned get_transition_shift(
const IR::BFN::ParserState *src,
491 const IR::BFN::Transition *tr) {
492 BUG_CHECK(tr !=
nullptr,
"Transition node cannot be null!");
493 if (transition_shift.count(src->name) &&
494 transition_shift.at(src->name).count(tr->value)) {
495 return transition_shift[src->name][tr->value];
516 void adjust_shift_buffer(
const IR::BFN::ParserState *state,
517 const IR::BFN::ParserState *state_child,
518 const IR::BFN::Parser *parser,
unsigned tr_shift,
519 unsigned state_shift) {
520 auto graph = parserInfo.graph(parser);
521 for (
auto state_trans : state->transitions) {
522 auto state_succ = state_trans->next;
523 transition_shift[state->name][state_trans->value] = tr_shift;
524 LOG4(
"Adding transition { " << state_trans->value <<
" } shift value " << tr_shift
525 <<
" B from state " << state->name <<
" to state "
526 << state_succ->name);
532 remainder_before_exit[state_trans] = state_shift;
536 if (graph.predecessors().at(state_succ).size() <= 1) {
537 state_to_shift[state_succ->name] = state_shift;
538 LOG4(
"Setting shift value " << state_shift <<
" B for state "
539 << state_succ->name);
544 if (state_succ == state_child) {
545 LOG4(
"Skipping transition adjustment for " << state_succ->name
546 <<
" (will be set later).");
554 if (graph.predecessors().at(state_succ).size() > 1) {
555 state_to_duplicate.emplace(state_trans,
556 std::make_pair(state_succ, state_shift));
560 for (
auto succ_tr : state_succ->transitions) {
561 if (transition_shift[state_succ->name].count(succ_tr->value) > 0)
continue;
563 unsigned new_shift = get_transition_shift(state_succ, succ_tr) + state_shift;
564 transition_shift[state_succ->name][succ_tr->value] = new_shift;
565 LOG4(
"Adding transition { "
566 << succ_tr->value <<
" } shift value " << new_shift <<
" B from state "
567 << state_succ->name <<
" to state "
568 << (succ_tr->next !=
nullptr ? succ_tr->next->name.c_str() :
"EXIT"));
581 void distribute_shift_to_node(
const IR::BFN::ParserState *state,
582 const IR::BFN::ParserState *succ,
583 const IR::BFN::Parser *parser,
int required_history) {
586 BUG_CHECK(state,
"Parser state cannot be null!");
587 if (state_to_shift.count(state->name) > 0) {
589 "Current path with historic data usage has an intersection with"
590 " a previously analyzed historic data path at node %1%!",
594 int deficit = required_history;
595 auto graph = parserInfo.graph(parser);
596 auto transitions = graph.transitions(state, succ);
599 LOG5(
"Transitions size from state " << state->name <<
" to state " << succ->name
600 <<
" is " << transitions.size());
602 if (transitions.size() > 0 && required_history > 0) {
605 unsigned shift_value = get_transition_shift(state, *transitions.begin());
606 deficit = required_history - shift_value;
609 LOG4(
"Shift distribution for node " << state->name
610 <<
", to distribute = " << required_history
611 <<
" (deficit = " << deficit <<
" B)");
618 auto preds = graph.predecessors().at(state);
619 if (preds.size() > 1) {
621 "Cannot resolve negative extract because of multiple paths to "
625 distribute_shift_to_node(*preds.begin(), state, parser, deficit);
629 if (transitions.size() == 0 && !succ) {
630 LOG4(
"Initial node " << state->name <<
" has been reached.");
639 const int old_tr_shift = get_transition_shift(state, *transitions.begin());
642 int new_state_shift = old_tr_shift + deficit;
643 int new_tr_shift = 0;
647 new_tr_shift = -deficit;
649 Log::TempIndent indent;
650 LOG4(
"Adjusting shift for state "
651 << state->name <<
" and transition to state " << succ->name
652 <<
" (new transition shift = " << new_tr_shift <<
" B)" << indent);
653 adjust_shift_buffer(state, succ, parser, new_tr_shift, new_state_shift);
662 std::map<const IR::BFN::ParserState *, std::pair<IR::BFN::ParserState *, int>>
666 auto rv = ParserModifier::init_apply(node);
667 duplicated_states.clear();
671 bool preorder(IR::BFN::Transition *transition)
override {
672 auto state = findContext<IR::BFN::ParserState>();
673 auto orig_transition = getOriginal()->to<IR::BFN::Transition>();
674 BUG_CHECK(state,
"State cannot be null!");
675 BUG_CHECK(orig_transition,
"Original IR::BFN::Transition cannot be null!");
677 if (collectNegative.transition_shift.count(state->name) &&
678 collectNegative.transition_shift.at(state->name).count(orig_transition->value)) {
679 const auto &tr_map = collectNegative.transition_shift.at(state->name);
680 transition->shift = tr_map.at(orig_transition->value);
681 LOG3(
"Adjusting transition from " << state->name <<
", match { "
682 << orig_transition->value
683 <<
" } to shift value = " << transition->shift);
686 if (collectNegative.remainder_before_exit.count(orig_transition)) {
690 unsigned state_shift = collectNegative.remainder_before_exit.at(orig_transition);
692 auto remainder_state =
new IR::BFN::ParserState(
693 state->p4States, state->name +
"$final_shift", state->gress);
694 transition->next = remainder_state;
695 auto end_transition =
new IR::BFN::Transition(
match_t(), state_shift);
696 remainder_state->transitions.push_back(end_transition);
697 LOG5(
"Transition from state "
698 << state->name <<
" with match value " << orig_transition->value
699 <<
" leads to exit, adding new state " << remainder_state->name
700 <<
" to consume " << state_shift <<
" bytes.");
703 if (collectNegative.state_to_duplicate.count(orig_transition)) {
704 auto [orig_state, state_shift] =
705 collectNegative.state_to_duplicate.at(orig_transition);
706 LOG5(
"Duplicating transition from state " << state->name <<
" with match value "
707 << orig_transition->value <<
" to state "
708 << orig_state->name);
710 IR::BFN::ParserState *duplicated_state =
nullptr;
711 if (duplicated_states.count(orig_state)) {
712 int prev_state_shift;
713 std::tie(duplicated_state, prev_state_shift) = duplicated_states[orig_state];
714 BUG_CHECK(state_shift == prev_state_shift,
715 "New state shift %1% does not match previous "
716 "value %2% for duplicated state %3%",
717 state_shift, prev_state_shift, state->name);
719 duplicated_state =
new IR::BFN::ParserState(
720 orig_state->p4States, orig_state->name +
"$dup", orig_state->gress);
721 for (
auto tr : orig_state->transitions) {
722 auto new_trans =
new IR::BFN::Transition(tr->srcInfo, tr->value,
723 tr->shift + state_shift, tr->next);
724 duplicated_state->transitions.push_back(new_trans);
726 for (
const auto stmt : orig_state->statements) {
727 duplicated_state->statements.push_back(stmt->clone());
729 duplicated_states.emplace(orig_state,
730 std::make_pair(duplicated_state, state_shift));
733 transition->next = duplicated_state;
739 bool preorder(IR::BFN::PacketRVal *rval)
override {
740 auto state = findContext<IR::BFN::ParserState>();
741 auto extract = findContext<IR::BFN::Extract>();
743 if (collectNegative.state_to_shift.count(state->name)) {
744 unsigned shift = collectNegative.state_to_shift.at(state->name) * 8;
745 rval->range.lo += shift;
746 rval->range.hi += shift;
748 LOG3(
"Adjusting field " << extract->dest->field->toString() <<
" to "
749 << shift / 8 <<
" byte offset (lo = " << rval->range.lo
750 <<
", hi = " << rval->range.hi <<
")");