P4C
The P4 Compiler
Loading...
Searching...
No Matches
visitor.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifndef IR_VISITOR_H_
18#define IR_VISITOR_H_
19
20#include <cassert>
21#include <cstddef>
22#include <cstdint>
23#include <functional>
24#include <iosfwd>
25#include <map>
26#include <memory>
27#include <type_traits>
28#include <typeinfo>
29#include <unordered_map>
30#include <utility>
31
32#include "absl/time/time.h"
33#include "ir/gen-tree-macro.h"
34#include "ir/ir-tree-macros.h"
35#include "ir/node.h"
36#include "ir/vector.h"
37#include "lib/castable.h"
38#include "lib/cstring.h"
39#include "lib/error.h"
40#include "lib/exceptions.h"
41#include "lib/null.h"
42#include "lib/source_file.h"
43
44namespace P4 {
45
46// declare this outside of Visitor so it can be forward declared in node.h
48 // We maintain a linked list of Context structures on the stack
49 // in the Visitor::apply_visitor functions as we do the recursive
50 // descent traversal. pre/postorder function can access this
51 // context via getContext/findContext
52 const Visitor_Context *parent;
53 const IR::Node *node, *original;
54 mutable const char *child_name;
55 mutable int child_index;
56 int depth;
57 template <class T>
58 inline const T *findContext(const Visitor_Context *&c) const {
59 c = this;
60 while ((c = c->parent))
61 if (auto *rv = c->node->to<T>()) return rv;
62 return nullptr;
63 }
64 template <class T>
65 inline const T *findContext() const {
66 const Visitor_Context *c = this;
67 return findContext<T>(c);
68 }
69};
70
73class Inspector;
74
75class Visitor {
76 public:
77 typedef Visitor_Context Context;
78 class profile_t {
79 // for profiling -- a profile_t object is created when a pass
80 // starts and destroyed when it ends. Moveable but not copyable.
81 Visitor &v;
82 absl::Time start;
83 explicit profile_t(Visitor &);
84 friend class Visitor;
85
86 public:
87 profile_t() = delete;
88 profile_t(const profile_t &) = delete;
89 profile_t &operator=(const profile_t &) = delete;
90 profile_t &operator=(profile_t &&) = delete;
91
92 ~profile_t();
93 profile_t(profile_t &&);
94 };
95 virtual ~Visitor() = default;
96
97 mutable cstring internalName;
98 // Some visitors are created and applied by other visitors.
99 // This field keeps track of the caller.
100 const Visitor *called_by = nullptr;
101
102 const Visitor &setCalledBy(const Visitor *visitor) {
103 called_by = visitor;
104 return *this;
105 }
106 // init_apply is called (once) when apply is called on an IR tree
107 // it expects to allocate a profile record which will be destroyed
108 // when the traversal completes. Visitor subclasses may extend this
109 // to do any additional initialization they need. They should call their
110 // parent's init_apply to do further initialization
111 virtual profile_t init_apply(const IR::Node *root);
112 profile_t init_apply(const IR::Node *root, const Context *parent_context);
113 // End_apply is called symmetrically with init_apply, after the visit
114 // is completed. Both functions will be called in the event of a normal
115 // completion, but only the 0-argument version will be called in the event
116 // of an exception, as there is no root in that case.
117 virtual void end_apply();
118 virtual void end_apply(const IR::Node *root);
119
120 // apply_visitor is the main traversal function that manages the
121 // depth-first recursive traversal. `visit` is a convenience function
122 // that calls it on a variable.
123 virtual const IR::Node *apply_visitor(const IR::Node *n, const char *name = 0) = 0;
124 void visit(const IR::Node *&n, const char *name = 0) { n = apply_visitor(n, name); }
125 void visit(const IR::Node *const &n, const char *name = 0) {
126 auto t = apply_visitor(n, name);
127 if (t != n) visitor_const_error();
128 }
129 void visit(const IR::Node *&n, const char *name, int cidx) {
130 ctxt->child_index = cidx;
131 n = apply_visitor(n, name);
132 }
133 void visit(const IR::Node *const &n, const char *name, int cidx) {
134 ctxt->child_index = cidx;
135 auto t = apply_visitor(n, name);
136 if (t != n) visitor_const_error();
137 }
138 void visit(IR::Node *&, const char * = 0, int = 0) { BUG("Can't visit non-const pointer"); }
139#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
140 void visit(const IR::CLASS *&n, const char *name = 0); \
141 void visit(const IR::CLASS *const &n, const char *name = 0); \
142 void visit(const IR::CLASS *&n, const char *name, int cidx); \
143 void visit(const IR::CLASS *const &n, const char *name, int cidx); \
144 void visit(IR::CLASS *&, const char * = 0, int = 0) { BUG("Can't visit non-const pointer"); }
145 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
146#undef DECLARE_VISIT_FUNCTIONS
147 void visit(IR::Node &n, const char *name = 0) {
148 if (name && ctxt) ctxt->child_name = name;
149 n.visit_children(*this);
150 }
151 void visit(const IR::Node &n, const char *name = 0) {
152 if (name && ctxt) ctxt->child_name = name;
153 n.visit_children(*this);
154 }
155 void visit(IR::Node &n, const char *name, int cidx) {
156 if (ctxt) {
157 ctxt->child_name = name;
158 ctxt->child_index = cidx;
159 }
160 n.visit_children(*this);
161 }
162 void visit(const IR::Node &n, const char *name, int cidx) {
163 if (ctxt) {
164 ctxt->child_name = name;
165 ctxt->child_index = cidx;
166 }
167 n.visit_children(*this);
168 }
169 template <class T>
170 void parallel_visit(IR::Vector<T> &v, const char *name = 0) {
171 if (name && ctxt) ctxt->child_name = name;
172 v.parallel_visit_children(*this);
173 }
174 template <class T>
175 void parallel_visit(const IR::Vector<T> &v, const char *name = 0) {
176 if (name && ctxt) ctxt->child_name = name;
177 v.parallel_visit_children(*this);
178 }
179 template <class T>
180 void parallel_visit(IR::Vector<T> &v, const char *name, int cidx) {
181 if (ctxt) {
182 ctxt->child_name = name;
183 ctxt->child_index = cidx;
184 }
185 v.parallel_visit_children(*this);
186 }
187 template <class T>
188 void parallel_visit(const IR::Vector<T> &v, const char *name, int cidx) {
189 if (ctxt) {
190 ctxt->child_name = name;
191 ctxt->child_index = cidx;
192 }
193 v.parallel_visit_children(*this);
194 }
195
196 virtual Visitor *clone() const {
197 BUG("need %s::clone method", name());
198 return nullptr;
199 }
200 virtual bool check_clone(const Visitor *a) { return typeid(*this) == typeid(*a); }
201
202 // Functions for IR visit_children to call for ControlFlowVisitors.
203 virtual ControlFlowVisitor *controlFlowVisitor() { return nullptr; }
204 virtual Visitor &flow_clone() { return *this; }
205 // all flow_clones share a split_link chain to allow stack walking
206 SplitFlowVisit_base *split_link_mem = nullptr, *&split_link;
207 Visitor() : split_link(split_link_mem) {}
208
212 virtual void flow_merge(Visitor &) {}
213 virtual bool flow_merge_closure(Visitor &) { BUG("%s pass does not support loops", name()); }
216 virtual void flow_merge_global_from(cstring) {}
217 virtual void erase_global(cstring) {}
218 virtual bool check_global(cstring) { return false; }
219 virtual void clear_globals() {}
220 virtual bool has_flow_joins() const { return false; }
221
222 static cstring demangle(const char *);
223 virtual const char *name() const {
224 if (!internalName) internalName = demangle(typeid(*this).name());
225 return internalName.c_str();
226 }
227 void setName(const char *name) { internalName = cstring(name); }
228 void print_context() const; // for debugging; can be called from debugger
229
230 // Context access/search functions. getContext returns the context
231 // that refers to the immediate parent of the node currently being
232 // visited. findContext searches up the context for a (grand)parent
233 // of a specific type. Orig versions return the original node (before
234 // any cloning or modifications)
235 const IR::Node *getOriginal() const { return ctxt->original; }
236 template <class T>
237 const T *getOriginal() const {
238 CHECK_NULL(ctxt->original);
239 BUG_CHECK(ctxt->original->is<T>(), "%1% does not have the expected type %2%",
240 ctxt->original, demangle(typeid(T).name()));
241 return ctxt->original->to<T>();
242 }
243 const Context *getChildContext() const { return ctxt; }
244 const Context *getContext() const { return ctxt->parent; }
245 template <class T>
246 const T *getParent() const {
247 return ctxt->parent ? ctxt->parent->node->to<T>() : nullptr;
248 }
249 int getChildrenVisited() const { return ctxt->child_index; }
250 int getContextDepth() const { return ctxt->depth - 1; }
251 template <class T>
252 inline const T *findContext(const Context *&c) const {
253 if (!c) c = ctxt;
254 if (!c) return nullptr;
255 while ((c = c->parent))
256 if (auto *rv = c->node->to<T>()) return rv;
257 return nullptr;
258 }
259 template <class T>
260 inline const T *findContext() const {
261 const Context *c = ctxt;
262 return findContext<T>(c);
263 }
264 template <class T>
265 inline const T *findOrigCtxt(const Context *&c) const {
266 if (!c) c = ctxt;
267 if (!c) return nullptr;
268 while ((c = c->parent))
269 if (auto *rv = c->original->to<T>()) return rv;
270 return nullptr;
271 }
272 template <class T>
273 inline const T *findOrigCtxt() const {
274 const Context *c = ctxt;
275 return findOrigCtxt<T>(c);
276 }
277 template <class T>
278 inline bool isInContext(const Context *&c) const {
279 if (!c) c = ctxt;
280 if (!c) return false;
281 while ((c = c->parent))
282 if (c->node->is<T>()) return true;
283 return false;
284 }
285 template <class T>
286 inline bool isInContext() const {
287 const Context *c = ctxt;
288 return isInContext<T>(c);
289 }
290 inline bool isInContext(const IR::Node *n) const {
291 for (auto *c = ctxt; c; c = c->parent) {
292 if (c->node == n || c->original == n) return true;
293 }
294 return false;
295 }
296
300 const IR::Node *getCurrentNode() const { return ctxt->node; }
301 template <class T>
302 const T *getCurrentNode() const {
303 return ctxt->node ? ctxt->node->to<T>() : nullptr;
304 }
305
309 bool warning_enabled(int warning_kind) const { return warning_enabled(this, warning_kind); }
312 static bool warning_enabled(const Visitor *visitor, int warning_kind);
313 template <class T, typename = std::enable_if_t<Util::has_SourceInfo_v<T>>, class... Args>
314 void warn(const int kind, const char *format, const T *node, Args &&...args) {
315 if (warning_enabled(kind)) ::P4::warning(kind, format, node, std::forward<Args>(args)...);
316 }
317
319 template <class T,
320 typename = std::enable_if_t<Util::has_SourceInfo_v<T> && !std::is_pointer_v<T>>,
321 class... Args>
322 void warn(const int kind, const char *format, const T &node, Args &&...args) {
323 if (warning_enabled(kind)) ::P4::warning(kind, format, node, std::forward<Args>(args)...);
324 }
325
326 protected:
327 // if visitDagOnce is set to 'false' (usually in the derived Visitor
328 // class constructor), nodes that appear multiple times in the tree
329 // will be visited repeatedly. If this is done in a Modifier or Transform
330 // pass, this will result in them being duplicated if they are modified.
331 bool visitDagOnce = true;
332 bool dontForwardChildrenBeforePreorder = false;
333 // if joinFlows is 'true', Visitor will track nodes with more than one parent and
334 // flow_merge the visitor from all the parents before visiting the node and its
335 // children. This only works for Inspector (not Modifier/Transform) currently.
336 bool joinFlows = false;
337
338 virtual void init_join_flows(const IR::Node *) {
339 BUG("joinFlows only supported in ControlFlowVisitor currently");
340 }
341
362 virtual bool join_flows(const IR::Node *) { return false; }
365 virtual void post_join_flows(const IR::Node *, const IR::Node *) {}
366
367 void visit_children(const IR::Node *, std::function<void()> fn) { fn(); }
368 class Tracker; // used by Inspector -- private to it
369 class ChangeTracker; // used by Modifier and Transform -- private to them
370 // This overrides visitDagOnce for a single node -- can only be called from
371 // preorder and postorder functions
372 // FIXME: It would be better named visitCurrentOnce() / visitCurrenAgain()
373 virtual void visitOnce() const { BUG("do not know how to handle request"); }
374 virtual void visitAgain() const { BUG("do not know how to handle request"); }
375
376 private:
377 virtual void visitor_const_error();
378 const Context *ctxt = nullptr; // should be readonly to subclasses
379 friend class Inspector;
380 friend class Modifier;
381 friend class Transform;
382 friend class ControlFlowVisitor;
383};
384
385class Modifier : public virtual Visitor {
386 std::shared_ptr<ChangeTracker> visited;
387 void visitor_const_error() override;
388 bool check_clone(const Visitor *) override;
389
390 public:
391 profile_t init_apply(const IR::Node *root) override;
392 const IR::Node *apply_visitor(const IR::Node *n, const char *name = 0) override;
393 virtual bool preorder(IR::Node *) { return true; }
394 virtual void postorder(IR::Node *) {}
395 virtual void revisit(const IR::Node *, const IR::Node *) {}
396 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
397#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
398 virtual bool preorder(IR::CLASS *); \
399 virtual void postorder(IR::CLASS *); \
400 virtual void revisit(const IR::CLASS *, const IR::CLASS *); \
401 virtual void loop_revisit(const IR::CLASS *);
402 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
403#undef DECLARE_VISIT_FUNCTIONS
404 void revisit_visited();
405 bool visit_in_progress(const IR::Node *) const;
406 void visitOnce() const override;
407 void visitAgain() const override;
408
409 protected:
410 bool forceClone = false; // force clone whole tree even if unchanged
411};
412
413class Inspector : public virtual Visitor {
414 std::shared_ptr<Tracker> visited;
415 bool check_clone(const Visitor *) override;
416
417 public:
418 profile_t init_apply(const IR::Node *root) override;
419 const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override;
420 virtual bool preorder(const IR::Node *) { return true; } // return 'false' to prune
421 virtual void postorder(const IR::Node *) {}
422 virtual void revisit(const IR::Node *) {}
423 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
424#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
425 virtual bool preorder(const IR::CLASS *); \
426 virtual void postorder(const IR::CLASS *); \
427 virtual void revisit(const IR::CLASS *); \
428 virtual void loop_revisit(const IR::CLASS *);
429 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
430#undef DECLARE_VISIT_FUNCTIONS
431 void revisit_visited();
432 bool visit_in_progress(const IR::Node *n) const;
433 void visitOnce() const override;
434 void visitAgain() const override;
435};
436
437class Transform : public virtual Visitor {
438 std::shared_ptr<ChangeTracker> visited;
439 bool prune_flag = false;
440 void visitor_const_error() override;
441 bool check_clone(const Visitor *) override;
442
443 public:
444 profile_t init_apply(const IR::Node *root) override;
445 const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override;
446 virtual const IR::Node *preorder(IR::Node *n) { return n; }
447 virtual const IR::Node *postorder(IR::Node *n) { return n; }
448 virtual void revisit(const IR::Node *, const IR::Node *) {}
449 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
450#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
451 virtual const IR::Node *preorder(IR::CLASS *); \
452 virtual const IR::Node *postorder(IR::CLASS *); \
453 virtual void revisit(const IR::CLASS *, const IR::Node *); \
454 virtual void loop_revisit(const IR::CLASS *);
455 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
456#undef DECLARE_VISIT_FUNCTIONS
457 void revisit_visited();
458 bool visit_in_progress(const IR::Node *) const;
459 void visitOnce() const override;
460 void visitAgain() const override;
461 // can only be called usefully from a 'preorder' function (directly or indirectly)
462 void prune() { prune_flag = true; }
463
464 protected:
465 const IR::Node *transform_child(const IR::Node *child) {
466 auto *rv = apply_visitor(child);
467 prune_flag = true;
468 return rv;
469 }
470 bool forceClone = false; // force clone whole tree even if unchanged
471};
472
473// turn this on for extra info tracking control joinFlows for debugging
474#define DEBUG_FLOW_JOIN 0
475
476class ControlFlowVisitor : public virtual Visitor {
477 std::shared_ptr<std::map<cstring, ControlFlowVisitor &>> globals;
478
479 protected:
480 ControlFlowVisitor *clone() const override = 0;
482 ControlFlowVisitor *vclone = nullptr; // a clone to accumulate info into
483 int count = 0; // additional parents needed -1
484 bool done = false;
485#if DEBUG_FLOW_JOIN
486 struct ctrs_t {
487 int exist = 0, visited = 0;
488 };
489 std::map<const IR::Node *, ctrs_t> parents;
490#endif
491 };
492 typedef std::map<const IR::Node *, flow_join_info_t> flow_join_points_t;
493 friend std::ostream &operator<<(std::ostream &, const flow_join_info_t &);
494 friend std::ostream &operator<<(std::ostream &, const flow_join_points_t &);
495 friend void dump(const flow_join_info_t &);
496 friend void dump(const flow_join_info_t *);
497 friend void dump(const flow_join_points_t &);
498 friend void dump(const flow_join_points_t *);
499
500 // Flag set by visit_children of nodes that are unconditional branches, to denote that
501 // the control flow is currently unreachable. Future flow_merges to this visitor should
502 // clear this if merging a reachable state.
503 bool unreachable = false;
504
505 flow_join_points_t *flow_join_points = 0;
506 class SetupJoinPoints : public Inspector {
507 protected:
508 flow_join_points_t &join_points;
509 bool preorder(const IR::Node *n) override {
510 BUG_CHECK(join_points.count(n) == 0, "oops");
511#if DEBUG_FLOW_JOIN
512 auto *ctxt = getContext();
513 join_points[n].parents[ctxt ? ctxt->original : nullptr].exist++;
514#endif
515 return true;
516 }
517 void revisit(const IR::Node *n) override {
518 ++join_points[n].count;
519#if DEBUG_FLOW_JOIN
520 auto *ctxt = getContext();
521 join_points[n].parents[ctxt ? ctxt->original : nullptr].exist++;
522#endif
523 }
524
525 public:
526 explicit SetupJoinPoints(flow_join_points_t &fjp) : join_points(fjp) {}
527 };
528 bool BackwardsCompatibleBroken = false; // enable old broken behavior for code that
529 // depends on it. Should not be set for new uses.
530
531 virtual void applySetupJoinPoints(const IR::Node *root) {
532 root->apply(SetupJoinPoints(*flow_join_points));
533 }
534 void init_join_flows(const IR::Node *root) override;
535 bool join_flows(const IR::Node *n) override;
536 void post_join_flows(const IR::Node *, const IR::Node *) override;
537
543 virtual bool filter_join_point(const IR::Node *) { return false; }
544 ControlFlowVisitor() : globals(std::make_shared<std::map<cstring, ControlFlowVisitor &>>()) {}
545
546 public:
547 ControlFlowVisitor *controlFlowVisitor() override { return this; }
548 ControlFlowVisitor &flow_clone() override;
549 void flow_merge(Visitor &) override = 0;
550 virtual void flow_copy(ControlFlowVisitor &) = 0;
551 virtual bool operator==(const ControlFlowVisitor &) const {
552 BUG("%s pass does not support loops", name());
553 }
554 bool operator!=(const ControlFlowVisitor &v) const { return !(*this == v); }
555 void setUnreachable() { unreachable = true; }
556 bool isUnreachable() { return unreachable; }
557 void flow_merge_global_to(cstring key) override {
558 if (auto other = globals->find(key); other != globals->end())
559 other->second.flow_merge(*this);
560 else
561 globals->emplace(key, flow_clone());
562 }
563 void flow_merge_global_from(cstring key) override {
564 if (auto other = globals->find(key); other != globals->end()) flow_merge(other->second);
565 }
566 void erase_global(cstring key) override { globals->erase(key); }
567 bool check_global(cstring key) override { return globals->count(key) != 0; }
568 void clear_globals() override { globals->clear(); }
569 std::pair<cstring, ControlFlowVisitor *> save_global(cstring key) {
570 ControlFlowVisitor *cfv = nullptr;
571 if (auto i = globals->find(key); i != globals->end()) {
572 cfv = &i->second;
573 globals->erase(i);
574 }
575 return std::make_pair(key, cfv);
576 }
577 void restore_global(std::pair<cstring, ControlFlowVisitor *> saved) {
578 globals->erase(saved.first);
579 if (saved.second) globals->emplace(saved.first, *saved.second);
580 }
581
583 class GuardGlobal {
584 Visitor &self;
585 cstring key;
586
587 public:
588 GuardGlobal(Visitor &self, cstring key) : self(self), key(key) {
589 BUG_CHECK(!self.check_global(key), "ControlFlowVisitor global %s in use", key);
590 }
591 ~GuardGlobal() { self.erase_global(key); }
592 };
593
594 class SaveGlobal {
595 ControlFlowVisitor &self;
596 std::vector<std::pair<cstring, ControlFlowVisitor *>> saved;
597
598 public:
599 SaveGlobal(ControlFlowVisitor &self, cstring key) : self(self) {
600 saved.push_back(self.save_global(key));
601 }
602 SaveGlobal(ControlFlowVisitor &self, cstring k1, cstring k2) : self(self) {
603 saved.push_back(self.save_global(k1));
604 saved.push_back(self.save_global(k2));
605 }
606 ~SaveGlobal() {
607 for (auto it = saved.rbegin(); it != saved.rend(); ++it) self.restore_global(*it);
608 }
609 };
610
611 bool has_flow_joins() const override { return !!flow_join_points; }
612 const flow_join_info_t *flow_join_status(const IR::Node *n) const {
613 if (!flow_join_points || !flow_join_points->count(n)) return nullptr;
614 return &flow_join_points->at(n);
615 }
616};
617
626class SplitFlowVisit_base {
627 protected:
628 Visitor &v;
629 SplitFlowVisit_base *prev;
630 std::vector<Visitor *> visitors;
631 int visit_next = 0, start_index = 0;
632 bool paused = false;
633 friend ControlFlowVisitor;
634
635 explicit SplitFlowVisit_base(Visitor &v) : v(v) {
636 prev = v.split_link;
637 v.split_link = this;
638 }
639 ~SplitFlowVisit_base() { v.split_link = prev; }
640 void *operator new(size_t); // declared and not defined, as this class can
641 // only be instantiated on the stack. Trying to allocate one on the heap will
642 // cause a linker error.
643
644 public:
645 bool finished() { return size_t(visit_next) >= visitors.size(); }
646 virtual bool ready() { return !finished() && !paused; }
647 void pause() { paused = true; }
648 void unpause() { paused = false; }
649 virtual void do_visit() = 0;
650 virtual void run_visit() {
651 auto *ctxt = v.getChildContext();
652 start_index = ctxt ? ctxt->child_index : 0;
653 paused = false;
654 while (!finished()) do_visit();
655 for (auto *cl : visitors) {
656 if (cl && cl != &v) v.flow_merge(*cl);
657 }
658 }
659 virtual void dbprint(std::ostream &) const = 0;
660 friend void dump(const SplitFlowVisit_base *);
661};
662
663template <class N>
664class SplitFlowVisit : public SplitFlowVisit_base {
665 std::vector<const N **> nodes;
666 std::vector<const N *const *> const_nodes;
667
668 public:
669 explicit SplitFlowVisit(Visitor &v) : SplitFlowVisit_base(v) {}
670 void addNode(const N *&node) {
671 BUG_CHECK(const_nodes.empty(), "Mixing const and non-const in SplitFlowVisit");
672 BUG_CHECK(visitors.size() == nodes.size(), "size mismatch in SplitFlowVisit");
673 BUG_CHECK(visit_next == 0, "Can't addNode to SplitFlowVisit after visiting started");
674 visitors.push_back(visitors.empty() ? &v : &v.flow_clone());
675 nodes.emplace_back(&node);
676 }
677 void addNode(const N *const &node) {
678 BUG_CHECK(nodes.empty(), "Mixing const and non-const in SplitFlowVisit");
679 BUG_CHECK(visitors.size() == const_nodes.size(), "size mismatch in SplitFlowVisit");
680 BUG_CHECK(visit_next == 0, "Can't addNode to SplitFlowVisit after visiting started");
681 visitors.push_back(visitors.empty() ? &v : &v.flow_clone());
682 const_nodes.emplace_back(&node);
683 }
684 template <class T1, class T2, class... Args>
685 void addNode(T1 &&t1, T2 &&t2, Args &&...args) {
686 addNode(std::forward<T1>(t1));
687 addNode(std::forward<T2>(t2), std::forward<Args>(args)...);
688 }
689 template <class... Args>
690 explicit SplitFlowVisit(Visitor &v, Args &&...args) : SplitFlowVisit(v) {
691 addNode(std::forward<Args>(args)...);
692 }
693 void do_visit() override {
694 if (!finished()) {
695 BUG_CHECK(!paused, "trying to visit paused split_flow_visitor");
696 int idx = visit_next++;
697 if (nodes.empty())
698 visitors.at(idx)->visit(*const_nodes.at(idx), nullptr, start_index + idx);
699 else
700 visitors.at(idx)->visit(*nodes.at(idx), nullptr, start_index + idx);
701 }
702 }
703 void dbprint(std::ostream &out) const override {
704 out << "SplitFlowVisit processed " << visit_next << " of " << visitors.size();
705 }
706};
707
708template <class N>
709class SplitFlowVisitVector : public SplitFlowVisit_base {
710 IR::Vector<N> *vec = nullptr;
711 const IR::Vector<N> *const_vec = nullptr;
712 std::vector<const IR::Node *> result;
713 void init_visit(size_t size) {
714 if (size > 0) visitors.push_back(&v);
715 while (visitors.size() < size) visitors.push_back(&v.flow_clone());
716 }
717
718 public:
719 SplitFlowVisitVector(Visitor &v, IR::Vector<N> &vec) : SplitFlowVisit_base(v), vec(&vec) {
720 result.resize(vec.size());
721 init_visit(vec.size());
722 }
723 SplitFlowVisitVector(Visitor &v, const IR::Vector<N> &vec)
724 : SplitFlowVisit_base(v), const_vec(&vec) {
725 init_visit(vec.size());
726 }
727 void do_visit() override {
728 if (!finished()) {
729 BUG_CHECK(!paused, "trying to visit paused split_flow_visitor");
730 int idx = visit_next++;
731 if (vec)
732 result[idx] = visitors.at(idx)->apply_visitor(vec->at(idx));
733 else
734 visitors.at(idx)->visit(const_vec->at(idx), nullptr, start_index + idx);
735 }
736 }
737 void run_visit() override {
738 SplitFlowVisit_base::run_visit();
739 if (vec) {
740 int idx = 0;
741 for (auto i = vec->begin(); i != vec->end(); ++idx) {
742 if (!result[idx] && *i) {
743 i = vec->erase(i);
744 } else if (result[idx] == *i) {
745 ++i;
746 } else if (auto l = result[idx]->template to<IR::Vector<N>>()) {
747 i = vec->erase(i);
748 i = vec->insert(i, l->begin(), l->end());
749 i += l->size();
750 } else if (auto v = result[idx]->template to<IR::VectorBase>()) {
751 if (v->empty()) {
752 i = vec->erase(i);
753 } else {
754 i = vec->insert(i, v->size() - 1, nullptr);
755 for (auto el : *v) {
756 CHECK_NULL(el);
757 if (auto e = el->template to<N>())
758 *i++ = e;
759 else
760 BUG("visitor returned invalid type %s for Vector<%s>",
761 el->node_type_name(), N::static_type_name());
762 }
763 }
764 } else if (auto e = result[idx]->template to<N>()) {
765 *i++ = e;
766 } else {
767 CHECK_NULL(result[idx]);
768 BUG("visitor returned invalid type %s for Vector<%s>",
769 result[idx]->node_type_name(), N::static_type_name());
770 }
771 }
772 }
773 }
774 void dbprint(std::ostream &out) const override {
775 out << "SplitFlowVisitVector processed " << visit_next << " of " << visitors.size();
776 }
777};
778
779class Backtrack : public virtual Visitor {
780 public:
781 struct trigger : public ICastable {
782 enum type_t { OK, OTHER } type;
783 explicit trigger(type_t t) : type(t) {}
784 virtual ~trigger();
785 virtual void dbprint(std::ostream &out) const { out << demangle(typeid(*this).name()); }
786
787 protected:
788 // must call this from the constructor if a trigger subclass contains pointers
789 // or references to GC objects
790 void register_for_gc(size_t);
791
792 DECLARE_TYPEINFO(trigger);
793 };
794 virtual bool backtrack(trigger &trig) = 0;
795 virtual bool never_backtracks() { return false; } // generally not overridden
796 // returns true for passes that will never catch a trigger (backtrack() is always false)
797};
798
799std::ostream &operator<<(std::ostream &out, const Backtrack::trigger &trigger);
800
801class P4WriteContext : public virtual Visitor {
802 public:
803 bool isWrite(bool root_value = false); // might write based on context
804 bool isRead(bool root_value = false); // might read based on context
805 // note that the context might (conservatively) return true for BOTH isWrite and isRead,
806 // as it might be an 'inout' access or it might be unable to decide.
807};
808
813template <typename NodeType, typename Func>
814void forAllMatching(const IR::Node *root, Func &&function) {
815 struct NodeVisitor : public Inspector {
816 explicit NodeVisitor(Func &&function) : function(function) {}
817 Func function;
818 void postorder(const NodeType *node) override { function(node); }
819 };
820 root->apply(NodeVisitor(std::forward<Func>(function)));
821}
822
829template <typename NodeType, typename RootType, typename Func>
830const RootType *modifyAllMatching(const RootType *root, Func &&function) {
831 struct NodeVisitor : public Modifier {
832 explicit NodeVisitor(Func &&function) : function(function) {}
833 Func function;
834 void postorder(NodeType *node) override { function(node); }
835 };
836 return root->apply(NodeVisitor(std::forward<Func>(function)))->template checkedTo<RootType>();
837}
838
845template <typename NodeType, typename Func>
846const IR::Node *transformAllMatching(const IR::Node *root, Func &&function) {
847 struct NodeVisitor : public Transform {
848 explicit NodeVisitor(Func &&function) : function(function) {}
849 Func function;
850 const IR::Node *postorder(NodeType *node) override { return function(node); }
851 };
852 return root->apply(NodeVisitor(std::forward<Func>(function)));
853}
854
855} // namespace P4
856
857#endif /* IR_VISITOR_H_ */
Definition visitor.h:779
Definition visitor.h:476
void flow_merge_global_to(cstring key) override
Definition visitor.h:557
bool join_flows(const IR::Node *n) override
Definition visitor.cpp:661
void flow_merge(Visitor &) override=0
void post_join_flows(const IR::Node *, const IR::Node *) override
Definition visitor.cpp:721
virtual bool filter_join_point(const IR::Node *)
Definition visitor.h:543
Definition castable.h:36
Definition node.h:94
Definition vector.h:59
Definition visitor.h:413
Definition visitor.h:385
Definition visitor.h:801
Definition visitor.h:626
Definition visitor.h:437
Definition visitor.h:78
Definition visitor.h:75
void warn(const int kind, const char *format, const T &node, Args &&...args)
The const ref variant of the above.
Definition visitor.h:322
const IR::Node * getCurrentNode() const
Definition visitor.h:300
virtual void flow_merge_global_to(cstring)
Definition visitor.h:215
virtual void post_join_flows(const IR::Node *, const IR::Node *)
Definition visitor.h:365
bool warning_enabled(int warning_kind) const
Definition visitor.h:309
virtual void flow_merge(Visitor &)
Definition visitor.h:212
virtual bool join_flows(const IR::Node *)
Definition visitor.h:362
Definition cstring.h:85
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
void warning(const char *format, Args &&...args)
Report a warning with the given message.
Definition lib/error.h:122
void forAllMatching(const IR::Node *root, Func &&function)
Definition visitor.h:814
const IR::Node * transformAllMatching(const IR::Node *root, Func &&function)
Definition visitor.h:846
const RootType * modifyAllMatching(const RootType *root, Func &&function)
Definition visitor.h:830
STL namespace.
Definition visitor.h:781
T * to() noexcept
Definition rtti.h:226
Definition visitor.h:47