P4C
The P4 Compiler
Loading...
Searching...
No Matches
parser_info.h
1
19#ifndef BACKENDS_TOFINO_BF_P4C_PARDE_PARSER_INFO_H_
20#define BACKENDS_TOFINO_BF_P4C_PARDE_PARSER_INFO_H_
21
22#include <numeric>
23#include <optional>
24
25#include <boost/graph/adjacency_list.hpp>
26#include <boost/graph/copy.hpp>
27#include <boost/graph/graphviz.hpp>
28#include <boost/graph/topological_sort.hpp>
29
30#include "bf-p4c/ir/control_flow_visitor.h"
31#include "bf-p4c/ir/gress.h"
32#include "bf-p4c/lib/assoc.h"
33#include "bf-p4c/parde/parde_visitor.h"
34#include "ir/ir.h"
35#include "lib/cstring.h"
36
37namespace boost {
38enum vertex_state_t { vertex_state };
39BOOST_INSTALL_PROPERTY(vertex, state);
40
41enum edge_transition_t { edge_transition };
42BOOST_INSTALL_PROPERTY(edge, transition);
43} // namespace boost
44
57 public:
58 typedef boost::adjacency_list<
59 boost::vecS, boost::vecS, boost::bidirectionalS,
60 boost::property<boost::vertex_state_t, const IR::BFN::ParserState *>,
61 boost::property<boost::edge_transition_t, const IR::BFN::Transition *>>
62 Graph;
63
64 Graph graph;
65 const IR::BFN::Parser *parser = nullptr;
66 std::optional<gress_t> gress;
67
68 typename Graph::vertex_descriptor get_entry_point() {
69 if (entry_point) return *entry_point;
70
71 BUG_CHECK(parser, "Cannot get entry point, as provided parser is a nullptr.");
72 if (state_to_vertex.count(parser->start)) return state_to_vertex.at(parser->start);
73
74 BUG("Cannot get entry point of parser.");
75 }
76
77 private:
78 std::optional<typename Graph::vertex_descriptor> entry_point;
79
80 public:
81 std::optional<typename Graph::vertex_descriptor> end;
82
85
86 bool empty() const {
87 auto all_vertices = boost::vertices(graph);
88 if (++all_vertices.first == all_vertices.second) {
89 return true;
90 }
91 return false;
92 }
93
94 bool contains(const IR::BFN::ParserState *state) { return state_to_vertex.count(state); }
95
96 typename Graph::vertex_descriptor add_vertex(const IR::BFN::ParserState *state) {
97 if (state != nullptr && !gress) gress = state->gress;
98
99 if (contains(state)) {
100 LOG1("State " << ((state) ? state->name : "END") << " already exists");
101 return state_to_vertex.at(state);
102 }
103
104 auto vertex = boost::add_vertex(state, graph);
105 if (state)
106 LOG1("Added vertex " << vertex << " for state " << state->name);
107 else
108 LOG1("Added vertex " << vertex << " for state END");
109
110 if (state && state->name.find("$entry_point")) entry_point = vertex;
111 if (state == nullptr) end = vertex;
112
113 state_to_vertex[state] = vertex;
114 vertex_to_state[vertex] = state;
115
116 return vertex;
117 }
118
119 std::pair<typename Graph::edge_descriptor, bool> add_edge(
120 const IR::BFN::ParserState *source, const IR::BFN::ParserState *destination,
121 const IR::BFN::Transition *transition) {
122 typename Graph::vertex_descriptor source_vertex, destination_vertex;
123 source_vertex = add_vertex(source);
124 destination_vertex = add_vertex(destination);
125
126 // Look for a pre-existing edge.
127 typename Graph::out_edge_iterator out, end;
128 for (boost::tie(out, end) = boost::out_edges(source_vertex, graph); out != end; ++out) {
129 if (boost::target(*out, graph) == destination_vertex) return {*out, false};
130 }
131 // No pre-existing edge, so make one.
132 auto edge = boost::add_edge(source_vertex, destination_vertex, graph);
133 if (!edge.second)
134 // A vector-based adjacency_list (i.e. Graph) is a multigraph.
135 // Inserting edges should always create new edges.
136 BUG("Boost Graph Library failed to add edge.");
137 boost::get(boost::edge_transition, graph)[edge.first] = transition;
138 return {edge.first, true};
139 }
140
142
144 boost::copy_graph(other.graph, graph);
145 }
146};
147
149 typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS> Graph;
150
151 public:
152 DirectedGraph() {}
153
154 DirectedGraph(const DirectedGraph &other) { boost::copy_graph(other._graph, _graph); }
155
156 ~DirectedGraph() {}
157
158 int add_vertex() {
159 int id = boost::add_vertex(_graph);
160 return id;
161 }
162
163 void add_edge(int src, int dst) { boost::add_edge(src, dst, _graph); }
164
165 std::vector<int> topological_sort() const {
166 std::vector<int> result;
167 boost::topological_sort(_graph, std::back_inserter(result));
168 std::reverse(result.begin(), result.end());
169 return result;
170 }
171
172 void print_stats() const {
173 std::cout << "num vertices: " << num_vertices(_graph) << std::endl;
174 std::cout << "num edges: " << num_edges(_graph) << std::endl;
175 boost::write_graphviz(std::cout, _graph);
176 }
177
178 private:
179 Graph _graph;
180};
181
182template <class State>
183struct ParserStateMap : public ordered_map<const State *, ordered_set<const State *>> {};
184
185template <class Parser, class State, class Transition>
187 public:
188 template <class P, class S, class T>
189 friend class CollectParserInfoImpl;
190
191 explicit ParserGraphImpl(const Parser *parser) : root(parser->start) {}
192
193 const State *const root;
194
195 const ordered_set<const State *> &states() const { return _states; }
196
197 const ParserStateMap<State> &successors() const { return _succs; }
198
199 const ParserStateMap<State> &predecessors() const { return _preds; }
200
202 return _to_pipe;
203 }
204
205 ordered_set<const Transition *> transitions(const State *src, const State *dst) const {
206 auto it = _transitions.find({src, dst});
207 if (it != _transitions.end()) return it->second;
208
209 return {};
210 }
211
212 ordered_set<const Transition *> transitions_to(const State *dst) const {
214 auto has_dst = [dst](const auto &kv) { return kv.first.second == dst; };
215 auto it = std::find_if(_transitions.begin(), _transitions.end(), has_dst);
216 while (it != _transitions.end()) {
217 ordered_set<const Transition *> value = it->second;
218 transitions.insert(value.begin(), value.end());
219 it = std::find_if(++it, _transitions.end(), has_dst);
220 }
221 return transitions;
222 }
223
224 ordered_set<const Transition *> to_pipe(const State *src) const {
225 if (_to_pipe.count(src)) return _to_pipe.at(src);
226
227 return {};
228 }
229
231 const {
232 return _loopbacks;
233 }
234
235 bool is_loopback_state(cstring state) const {
236 for (auto &kv : _loopbacks) {
237 if (stripThreadPrefix(state) == stripThreadPrefix(kv.first.second)) return true;
238 }
239
240 return false;
241 }
242
243 const State *get_state(cstring name) const {
244 for (auto s : states()) {
245 if (name == s->name) return s;
246 }
247
248 return nullptr;
249 }
250
251 private:
254
255 public:
257 bool is_ancestor(const State *src, const State *dst) const {
258 if (src == dst) return false;
259
260 if (!predecessors().count(dst)) return false;
261
262 if (predecessors().at(dst).count(src)) return true;
263
264 if (is_ancestor_.count(src) && is_ancestor_.at(src).count(dst))
265 return is_ancestor_.at(src).at(dst);
266
268 for (auto p : predecessors().at(dst))
269 if (is_ancestor(src, p)) {
270 is_ancestor_[dst][src] = false;
271 return is_ancestor_[src][dst] = true;
272 }
273
274 return is_ancestor_[src][dst] = false;
275 }
276
277 bool is_descendant(const State *src, const State *dst) const { return is_ancestor(dst, src); }
278
279 bool is_loop_reachable(const State *src, const State *dst) const {
280 for (auto &kv : _loopbacks) {
281 if (kv.first.first == src) {
282 auto loop_state = get_state(kv.first.second);
283 if (loop_state == dst || is_ancestor(loop_state, dst)) return true;
284 }
285 }
286
287 return false;
288 }
289
335 bool is_reachable(const State *src, const State *dst) const {
336 // One way is that src is an ancestor of dst
337 if (is_ancestor(src, dst)) return true;
338 // Otherwise we must check all possible loopbacks
339 for (auto &kv : _loopbacks) {
340 auto loop_from = kv.first.first;
341 auto loop_to = get_state(kv.first.second);
342 // Can we get to this loopback from source (without any other loopbacks)?
343 if (src == loop_from || is_ancestor(src, loop_from)) {
344 // If the loopback goes to dst we are done
345 if (loop_to == dst) return true;
346 // Loopback needs to get us somewhere new
347 // (where we couldn't directly reach from src)
348 // And we need to be able to reach the dst from this new state
349 // (possibly via more loops)
350 if (src != loop_to && !is_ancestor(src, loop_to) && is_reachable(loop_to, dst))
351 return true;
352 }
353 }
354 // We didn't find anything => state dst is not reacheable from src
355 return false;
356 }
357
361 bool is_dominated_by_set(const State *s, const ordered_set<const State *> &set) {
362 // If there are no predecessors domination is false
363 if (!predecessors().count(s) || predecessors().at(s).empty()) return false;
364 // Otherwise we need every predecessor to be from the set or recursively
365 // dominated by the set
366 for (auto ns : predecessors().at(s)) {
367 if (!set.count(ns) && !is_dominated_by_set(ns, set)) {
368 return false;
369 }
370 }
371 return true;
372 }
373
377 bool is_postdominated_by_set(const State *s, const ordered_set<const State *> &set) {
378 // If there are no successors postdomination is false
379 if (!successors().count(s) || successors().at(s).empty()) return false;
380 // Otherwise we need every successor to be from the set or recursively
381 // postdominated by the set
382 for (auto ns : successors().at(s)) {
383 if (!set.count(ns) && !is_postdominated_by_set(ns, set)) {
384 return false;
385 }
386 }
387 return true;
388 }
389
409 std::pair<const State *, const State *> is_loopback_reassignment(
410 const State *s, const ordered_set<const State *> &set) {
411 for (auto &kv : _loopbacks) {
412 auto le = kv.first.first;
413 auto ls = get_state(kv.first.second);
414 // Is this state even within this loopback?
415 if (s == le || s == ls || (is_ancestor(ls, s) && is_ancestor(s, le))) {
416 // If it is check this particular loopback
417 // Check if the loopback has postdomination happening inside
418 // And also that loop exit is dominated (or the loopback is reflexive)
419 if (_is_loopback_postdominated_by_set_impl(ls, le, set) &&
420 ((le == ls && set.count(le)) || is_dominated_by_set(le, set))) {
421 return std::make_pair(ls, le);
422 }
423 }
424 }
425
426 return std::make_pair(nullptr, nullptr);
427 }
428
431 bool is_mutex(const State *a, const State *b) const {
432 return a != b && !is_ancestor(a, b) && !is_ancestor(b, a) && !is_loop_reachable(a, b) &&
433 !is_loop_reachable(b, a);
434 }
435
438 bool is_mutex(const ordered_set<const State *> &states) const {
439 for (auto it1 = states.begin(); it1 != states.end(); ++it1) {
440 for (auto it2 = it1; it2 != states.end(); ++it2) {
441 if (it1 == it2) continue;
442 if (!is_mutex(*it1, *it2)) return false;
443 }
444 }
445
446 return true;
447 }
448
449 bool is_mutex(const Transition *a, const Transition *b) const {
450 if (a == b) return false;
451
452 auto a_dst = get_dst(a);
453 auto a_src = get_src(a);
454
455 auto b_dst = get_dst(b);
456 auto b_src = get_src(b);
457
458 if (a_dst == b_src) return false;
459 if (b_dst == a_src) return false;
460
461 if (is_ancestor(a_dst, b_src)) return false;
462 if (is_ancestor(b_dst, a_src)) return false;
463
464 if (is_loop_reachable(a_dst, b_src)) return false;
465 if (is_loop_reachable(b_dst, a_src)) return false;
466
467 return true;
468 }
469
470 ordered_set<const State *> get_all_descendants(const State *src) const {
472 get_all_descendants_impl(src, rv);
473 return rv;
474 }
475
476 std::vector<const State *> topological_sort() const {
477 std::vector<int> result = DirectedGraph::topological_sort();
478 std::vector<const State *> mapped_result;
479 for (auto id : result) mapped_result.push_back(get_state(id));
480 return mapped_result;
481 }
482
483 // longest path (in states) from src to end of parser
484 std::vector<const State *> longest_path_states(const State *src) const {
486 return min_max_path_impl(src, path_map, true, true, true).second;
487 }
488
489 // longest path (in states) from start of parser to dest
490 std::vector<const State *> longest_path_states_to(const State *dest) const {
492 return min_max_path_impl(dest, path_map, true, false, true).second;
493 }
494
495 // shortest path from src to end of parser
496 std::vector<const State *> shortest_path_states(const State *src) const {
498 return min_max_path_impl(src, path_map, false, true, true).second;
499 }
500
501 // longest path (in bytes) from src to end of parser
502 std::pair<unsigned, std::vector<const State *>> longest_path_bytes(const State *src) const {
504 return min_max_path_impl(src, path_map, true, true, false);
505 }
506
507 // shortest path (in bytes) from src to end of parser
508 std::pair<unsigned, std::vector<const State *>> shortest_path_bytes(const State *src) const {
510 return min_max_path_impl(src, path_map, false, true, false);
511 }
512
513 // shortest path (in bytes) from src to end of parser
514 std::pair<unsigned, std::vector<const State *>> shortest_path_thru_bytes(
515 const State *src) const {
518
519 auto from = min_max_path_impl(src, path_map_from, false, true, false);
520 auto to = min_max_path_impl(src, path_map_to, false, false, false);
521
522 std::vector<const State *> ret = to.second;
523 ret.insert(ret.end(), ++from.second.begin(), from.second.end());
524
525 return std::make_pair(from.first + to.first, ret);
526 }
527
528 const State *get_src(const Transition *t) const {
529 for (auto &kv : _transitions) {
530 if (kv.second.count(t)) return kv.first.first;
531 }
532
533 for (auto &kv : _to_pipe) {
534 if (kv.second.count(t)) return kv.first;
535 }
536
537 for (auto &kv : _loopbacks) {
538 if (kv.second.count(t)) return kv.first.first;
539 }
540
541 return nullptr;
542 }
543
544 const State *get_dst(const Transition *t) const {
545 for (auto &kv : _transitions) {
546 if (kv.second.count(t)) return kv.first.second;
547 }
548 return nullptr;
549 }
550
551 private:
556 bool _is_loopback_postdominated_by_set_impl(const State *s, const State *loop_exit,
557 const ordered_set<const State *> &set) {
558 // Check if we are at the loop_exit
559 if (s == loop_exit) {
560 if (set.count(loop_exit)) {
561 return true;
562 } else {
563 return false;
564 }
565 }
566 // If there are no successors postdomination is false
567 if (!successors().count(s) || successors().at(s).empty()) return false;
568 // Otherwise we need every successor to be from the set or recursively
569 // postdominated by the set
570 for (auto ns : successors().at(s)) {
571 if (!set.count(ns) && !_is_loopback_postdominated_by_set_impl(ns, loop_exit, set)) {
572 return false;
573 }
574 }
575 return true;
576 }
577
578 std::vector<const State *> longest_or_shortest_path_states_impl(
579 const State *src, assoc::map<const State *, std::vector<const State *>> &path_map,
580 bool longest) const {
581 if (path_map.count(src)) return path_map.at(src);
582
583 const State *best_succ = nullptr;
584 std::vector<const State *> best_succ_path;
585
586 if ((longest || to_pipe(src).size() == 0) && successors().count(src)) {
587 for (auto succ : successors().at(src)) {
588 auto succ_path = longest_or_shortest_path_states_impl(succ, path_map, longest);
589
590 bool gt = succ_path.size() > best_succ_path.size();
591 bool lt = succ_path.size() < best_succ_path.size();
592 if (!best_succ || (longest ? gt : lt)) {
593 best_succ_path = succ_path;
594 best_succ = succ;
595 }
596 }
597 }
598
599 best_succ_path.insert(best_succ_path.begin(), src);
600
601 path_map[src] = best_succ_path;
602
603 return best_succ_path;
604 }
605
615 std::pair<unsigned, std::vector<const State *>> min_max_path_impl(
616 const State *state,
617 assoc::map<const State *, std::pair<unsigned, std::vector<const State *>>> &path_map,
618 bool longest, bool origin, bool states) const {
619 if (path_map.count(state)) return path_map.at(state);
620
621 const State *best = nullptr;
622 std::vector<const State *> best_path;
623 unsigned best_len = 0;
624
625 auto max = [](unsigned v, const Transition *t) { return v > t->shift ? v : t->shift; };
626 auto min = [](unsigned v, const Transition *t) { return v < t->shift ? v : t->shift; };
627
628 auto next_map = origin ? successors() : predecessors();
629 auto exit_trans = origin ? to_pipe(state) : ordered_set<const Transition *>();
630
631 if ((longest || exit_trans.size() == 0) && next_map.count(state)) {
632 for (auto next : next_map.at(state)) {
633 auto next_path = min_max_path_impl(next, path_map, longest, origin, states);
634 auto next_trans = origin ? transitions(state, next) : transitions(next, state);
635 unsigned next_inc =
636 states ? 1
637 : std::accumulate(next_trans.begin(), next_trans.end(),
638 longest ? 0u : SIZE_MAX, longest ? max : min);
639
640 unsigned path_len = next_inc + next_path.first;
641 bool better = longest ? path_len > best_len : path_len < best_len;
642 if (!best || better) {
643 best_path = next_path.second;
644 best = next;
645 best_len = path_len;
646 }
647 }
648 } else if (exit_trans.size()) {
649 best_len = states ? 1
650 : std::accumulate(exit_trans.begin(), exit_trans.end(),
651 longest ? 0u : SIZE_MAX, longest ? max : min);
652 }
653
654 best_path.insert(origin ? best_path.begin() : best_path.end(), state);
655
656 path_map[state] = std::make_pair(best_len, best_path);
657
658 return path_map[state];
659 }
660
661 void get_all_descendants_impl(const State *src, ordered_set<const State *> &rv) const {
662 if (!successors().count(src)) return;
663
664 for (auto succ : successors().at(src)) {
665 if (!rv.count(succ)) {
666 rv.insert(succ);
667 get_all_descendants_impl(succ, rv);
668 }
669 }
670 }
671
672 void add_state(const State *s) { _states.insert(s); }
673
674 void add_transition(const State *state, const Transition *t) {
675 add_state(state);
676
677 if (t->next) {
678 add_state(t->next);
679 _succs[state].insert(t->next);
680 _preds[t->next].insert(state);
681 _transitions[{state, t->next}].insert(t);
682 } else if (t->loop) {
683 _loopbacks[{state, t->loop}].insert(t);
684 } else {
685 _to_pipe[state].insert(t);
686 }
687 }
688
689 void map_to_boost_graph() {
690 for (auto s : _states) {
691 int id = DirectedGraph::add_vertex();
692 _state_to_id[s] = id;
693 _id_to_state[id] = s;
694 }
695
696 for (auto t : _succs)
697 for (auto dst : t.second) DirectedGraph::add_edge(get_id(t.first), get_id(dst));
698 }
699
700 int get_id(const State *s) {
701 if (_state_to_id.count(s) == 0) add_state(s);
702 return _state_to_id.at(s);
703 }
704
705 const State *get_state(int id) const { return _id_to_state.at(id); }
706
708
709 ParserStateMap<State> _succs, _preds;
710
712 _transitions;
713
714 // the iteration order is not actually needed to be fixed except for
715 // dump_parser
717
718 // the iteration order is not actually needed to be fixed except for
719 // dump_parser
721
724};
725
726namespace P4 {
727namespace IR {
728namespace BFN {
729
731
732using LoweredParserGraph = ParserGraphImpl<IR::BFN::LoweredParser, IR::BFN::LoweredParserState,
733 IR::BFN::LoweredParserMatch>;
734} // namespace BFN
735} // namespace IR
736} // namespace P4
737
738template <class Parser, class State, class Transition>
741
742 public:
743 const ordered_map<const Parser *, GraphType *> &graphs() const { return _graphs; }
744 const GraphType &graph(const Parser *p) const { return *(_graphs.at(p)); }
745 const GraphType &graph(const State *s) const { return graph(parser(s)); }
746
747 const Parser *parser(const State *state) const { return _state_to_parser.at(state); }
748
749 private:
750 Visitor::profile_t init_apply(const IR::Node *root) override {
751 auto rv = Inspector::init_apply(root);
752
753 clear_cache();
754 _graphs.clear();
755 _state_to_parser.clear();
756
757 return rv;
758 }
759
760 bool preorder(const Parser *parser) override {
761 _graphs[parser] = new GraphType(parser);
762 revisit_visited();
763 return true;
764 }
765
766 bool preorder(const State *state) override {
767 auto parser = findContext<Parser>();
768 _state_to_parser[state] = parser;
769
770 auto g = _graphs.at(parser);
771
772 g->add_state(state);
773
774 for (auto t : state->transitions) g->add_transition(state, t);
775
776 return true;
777 }
778
780 void clear_cache() { all_shift_amounts_.clear(); }
781
782 // Memoization table. Only contains results for forward paths in the graph.
784 all_shift_amounts_;
785
786 public:
791 const std::set<int> *get_all_shift_amounts(const State *dst) const {
792 return get_all_shift_amounts(graph(dst).root, dst);
793 }
794
799 int get_min_shift_amount(const State *dst) const {
800 return *get_all_shift_amounts(dst)->begin();
801 }
802
807 int get_max_shift_amount(const State *dst) const {
808 return *get_all_shift_amounts(dst)->rbegin();
809 }
810
818 const std::set<int> *get_all_shift_amounts(const State *src, const State *dst) const {
819 bool reverse_path = graphs().at(parser(src))->is_ancestor(dst, src);
820 if (reverse_path) std::swap(src, dst);
821
822 auto result = get_all_forward_path_shift_amounts(src, dst);
823
824 if (reverse_path) {
825 // Need to negate result.
826 auto negated = new std::set<int>();
827 for (auto shift : *result) negated->insert(-shift);
828 result = negated;
829 }
830
831 return result;
832 }
833
834 private:
835 const std::set<int> *get_all_forward_path_shift_amounts(const State *src,
836 const State *dst) const {
837 if (src == dst) return new std::set<int>({0});
838
839 if (all_shift_amounts_.count(src) && all_shift_amounts_.at(src).count(dst))
840 return all_shift_amounts_.at(src).at(dst);
841
842 auto graph = graphs().at(parser(src));
843 auto result = new std::set<int>();
844
845 if (graph->is_mutex(src, dst)) {
846 return all_shift_amounts_[src][dst] = all_shift_amounts_[dst][src] = result;
847 }
848
849 if (!graph->is_ancestor(src, dst)) {
850 return all_shift_amounts_[src][dst] = result;
851 }
852
853 // Recurse with the successors of the source.
854 BUG_CHECK(graph->successors().count(src), "State %s has a descendant %s, but no successors",
855 src->name, dst->name);
856 for (auto succ : graph->successors().at(src)) {
857 auto amounts = get_all_forward_path_shift_amounts(succ, dst);
858 if (!amounts->size()) continue;
859
860 auto transitions = graph->transitions(src, succ);
861 BUG_CHECK(!transitions.empty(), "Missing parser transition from %s to %s", src->name,
862 succ->name);
863
864 auto t = *(transitions.begin());
865
866 for (auto amount : *amounts) result->insert(amount + t->shift * 8);
867 }
868
869 return all_shift_amounts_[src][dst] = result;
870 }
871
872 void end_apply() override {
873 clear_cache();
874 for (auto g : _graphs) g.second->map_to_boost_graph();
875 }
876
879};
880
881using CollectParserInfo =
883
885 CollectParserInfoImpl<IR::BFN::LoweredParser, IR::BFN::LoweredParserState,
886 IR::BFN::LoweredParserMatch>;
887
888 // end of group parde
889
890#endif /* BACKENDS_TOFINO_BF_P4C_PARDE_PARSER_INFO_H_ */
Definition node.h:95
Definition visitor.h:78
Definition cstring.h:85
Definition ordered_map.h:32
Definition ordered_set.h:32
Definition assoc.h:300
bool is_reachable(const State *src, const State *dst) const
Definition parser_info.h:335
bool is_mutex(const State *a, const State *b) const
Definition parser_info.h:431
const std::set< int > * get_all_shift_amounts(const State *dst) const
Definition parser_info.h:791
const std::set< int > * get_all_shift_amounts(const State *src, const State *dst) const
Definition parser_info.h:818
int get_min_shift_amount(const State *dst) const
Definition parser_info.h:799
bool is_dominated_by_set(const State *s, const ordered_set< const State * > &set)
Definition parser_info.h:361
bool is_ancestor(const State *src, const State *dst) const
Is "src" an ancestor of "dst"?
Definition parser_info.h:257
bool is_postdominated_by_set(const State *s, const ordered_set< const State * > &set)
Definition parser_info.h:377
std::pair< const State *, const State * > is_loopback_reassignment(const State *s, const ordered_set< const State * > &set)
Definition parser_info.h:409
bool is_mutex(const ordered_set< const State * > &states) const
Definition parser_info.h:438
int get_max_shift_amount(const State *dst) const
Definition parser_info.h:807
Definition parser_info.h:739
Definition parser_info.h:148
Definition parde_visitor.h:27
Definition parser_info.h:186
Definition parser_info.h:56
Definition parser_info.h:183
The namespace encapsulating Barefoot/Intel-specific stuff.
Definition add_t2na_meta.cpp:21
The namespace encapsulating IR node classes.
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
Definition table_flow_graph.h:33