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 = nullptr;
53 const IR::Node *node = nullptr, *original = nullptr;
54 mutable const char *child_name = nullptr;
55 mutable int child_index = 0;
56 int depth = 0;
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 (ctxt) ctxt->child_name = name;
149 n.visit_children(*this, name);
150 }
151 void visit(const IR::Node &n, const char *name = 0) {
152 if (ctxt) ctxt->child_name = name;
153 n.visit_children(*this, name);
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, name);
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, name);
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 std::function<void(const IR::Node *from, const IR::Node *to)> onNodeTransformedHook;
390
391 public:
392 profile_t init_apply(const IR::Node *root) override;
393 const IR::Node *apply_visitor(const IR::Node *n, const char *name = 0) override;
394 virtual bool preorder(IR::Node *) { return true; }
395 virtual void postorder(IR::Node *) {}
396 virtual void revisit(const IR::Node *, const IR::Node *) {}
397 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
398#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
399 virtual bool preorder(IR::CLASS *); \
400 virtual void postorder(IR::CLASS *); \
401 virtual void revisit(const IR::CLASS *, const IR::CLASS *); \
402 virtual void loop_revisit(const IR::CLASS *);
403 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
404#undef DECLARE_VISIT_FUNCTIONS
405 void revisit_visited();
406 bool visit_in_progress(const IR::Node *) const;
407 void visitOnce() const override;
408 void visitAgain() const override;
409 void setOnNodeTransformedHook(
410 const std::function<void(const IR::Node *from, const IR::Node *to)> &hook) {
411 onNodeTransformedHook = hook;
412 }
413
414 protected:
415 bool forceClone = false; // force clone whole tree even if unchanged
416};
417
418class Inspector : public virtual Visitor {
419 std::shared_ptr<Tracker> visited;
420 bool check_clone(const Visitor *) override;
421
422 public:
423 profile_t init_apply(const IR::Node *root) override;
424 const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override;
425 virtual bool preorder(const IR::Node *) { return true; } // return 'false' to prune
426 virtual void postorder(const IR::Node *) {}
427 virtual void revisit(const IR::Node *) {}
428 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
429#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
430 virtual bool preorder(const IR::CLASS *); \
431 virtual void postorder(const IR::CLASS *); \
432 virtual void revisit(const IR::CLASS *); \
433 virtual void loop_revisit(const IR::CLASS *);
434 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
435#undef DECLARE_VISIT_FUNCTIONS
436 void revisit_visited();
437 bool visit_in_progress(const IR::Node *n) const;
438 void visitOnce() const override;
439 void visitAgain() const override;
440};
441
442class Transform : public virtual Visitor {
443 std::shared_ptr<ChangeTracker> visited;
444 bool prune_flag = false;
445 void visitor_const_error() override;
446 bool check_clone(const Visitor *) override;
447 std::function<void(const IR::Node *from, const IR::Node *to)> onNodeTransformedHook;
448
449 public:
450 profile_t init_apply(const IR::Node *root) override;
451 const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override;
452 virtual const IR::Node *preorder(IR::Node *n) { return n; }
453 virtual const IR::Node *postorder(IR::Node *n) { return n; }
454 virtual void revisit(const IR::Node *, const IR::Node *) {}
455 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
456#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
457 virtual const IR::Node *preorder(IR::CLASS *); \
458 virtual const IR::Node *postorder(IR::CLASS *); \
459 virtual void revisit(const IR::CLASS *, const IR::Node *); \
460 virtual void loop_revisit(const IR::CLASS *);
461 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
462#undef DECLARE_VISIT_FUNCTIONS
463 void revisit_visited();
464 bool visit_in_progress(const IR::Node *) const;
465 void visitOnce() const override;
466 void visitAgain() const override;
467 // can only be called usefully from a 'preorder' function (directly or indirectly)
468 void prune() { prune_flag = true; }
469 void setOnNodeTransformedHook(
470 const std::function<void(const IR::Node *from, const IR::Node *to)> &hook) {
471 onNodeTransformedHook = hook;
472 }
473
474 protected:
475 const IR::Node *transform_child(const IR::Node *child) {
476 auto *rv = apply_visitor(child);
477 prune_flag = true;
478 return rv;
479 }
480 bool forceClone = false; // force clone whole tree even if unchanged
481};
482
483// turn this on for extra info tracking control joinFlows for debugging
484#define DEBUG_FLOW_JOIN 0
485
486class ControlFlowVisitor : public virtual Visitor {
487 std::shared_ptr<std::map<cstring, ControlFlowVisitor &>> globals;
488
489 protected:
490 ControlFlowVisitor *clone() const override = 0;
492 ControlFlowVisitor *vclone = nullptr; // a clone to accumulate info into
493 int count = 0; // additional parents needed -1
494 bool done = false;
495#if DEBUG_FLOW_JOIN
496 struct ctrs_t {
497 int exist = 0, visited = 0;
498 };
499 std::map<const IR::Node *, ctrs_t> parents;
500#endif
501 };
502 typedef std::map<const IR::Node *, flow_join_info_t> flow_join_points_t;
503 friend std::ostream &operator<<(std::ostream &, const flow_join_info_t &);
504 friend std::ostream &operator<<(std::ostream &, const flow_join_points_t &);
505 friend void dump(const flow_join_info_t &);
506 friend void dump(const flow_join_info_t *);
507 friend void dump(const flow_join_points_t &);
508 friend void dump(const flow_join_points_t *);
509
510 // Flag set by visit_children of nodes that are unconditional branches, to denote that
511 // the control flow is currently unreachable. Future flow_merges to this visitor should
512 // clear this if merging a reachable state.
513 bool unreachable = false;
514
515 flow_join_points_t *flow_join_points = 0;
516 class SetupJoinPoints : public Inspector {
517 protected:
518 flow_join_points_t &join_points;
519 bool preorder(const IR::Node *n) override {
520 BUG_CHECK(join_points.count(n) == 0, "oops");
521#if DEBUG_FLOW_JOIN
522 auto *ctxt = getContext();
523 join_points[n].parents[ctxt ? ctxt->original : nullptr].exist++;
524#endif
525 return true;
526 }
527 void revisit(const IR::Node *n) override {
528 ++join_points[n].count;
529#if DEBUG_FLOW_JOIN
530 auto *ctxt = getContext();
531 join_points[n].parents[ctxt ? ctxt->original : nullptr].exist++;
532#endif
533 }
534
535 public:
536 explicit SetupJoinPoints(flow_join_points_t &fjp) : join_points(fjp) {}
537 };
538 bool BackwardsCompatibleBroken = false; // enable old broken behavior for code that
539 // depends on it. Should not be set for new uses.
540
541 virtual void applySetupJoinPoints(const IR::Node *root) {
542 root->apply(SetupJoinPoints(*flow_join_points));
543 }
544 void init_join_flows(const IR::Node *root) override;
545 bool join_flows(const IR::Node *n) override;
546 void post_join_flows(const IR::Node *, const IR::Node *) override;
547
553 virtual bool filter_join_point(const IR::Node *) { return false; }
554 ControlFlowVisitor() : globals(std::make_shared<std::map<cstring, ControlFlowVisitor &>>()) {}
555
556 public:
557 ControlFlowVisitor *controlFlowVisitor() override { return this; }
558 ControlFlowVisitor &flow_clone() override;
559 void flow_merge(Visitor &) override = 0;
560 virtual void flow_copy(ControlFlowVisitor &) = 0;
561 virtual bool operator==(const ControlFlowVisitor &) const {
562 BUG("%s pass does not support loops", name());
563 }
564 bool operator!=(const ControlFlowVisitor &v) const { return !(*this == v); }
565 void setUnreachable() { unreachable = true; }
566 bool isUnreachable() { return unreachable; }
567 void flow_merge_global_to(cstring key) override {
568 if (auto other = globals->find(key); other != globals->end())
569 other->second.flow_merge(*this);
570 else
571 globals->emplace(key, flow_clone());
572 }
573 void flow_merge_global_from(cstring key) override {
574 if (auto other = globals->find(key); other != globals->end()) flow_merge(other->second);
575 }
576 void erase_global(cstring key) override { globals->erase(key); }
577 bool check_global(cstring key) override { return globals->count(key) != 0; }
578 void clear_globals() override { globals->clear(); }
579 std::pair<cstring, ControlFlowVisitor *> save_global(cstring key) {
580 ControlFlowVisitor *cfv = nullptr;
581 if (auto i = globals->find(key); i != globals->end()) {
582 cfv = &i->second;
583 globals->erase(i);
584 }
585 return std::make_pair(key, cfv);
586 }
587 void restore_global(std::pair<cstring, ControlFlowVisitor *> saved) {
588 globals->erase(saved.first);
589 if (saved.second) globals->emplace(saved.first, *saved.second);
590 }
591
593 class GuardGlobal {
594 Visitor &self;
595 cstring key;
596
597 public:
598 GuardGlobal(Visitor &self, cstring key) : self(self), key(key) {
599 BUG_CHECK(!self.check_global(key), "ControlFlowVisitor global %s in use", key);
600 }
601 ~GuardGlobal() { self.erase_global(key); }
602 };
603
604 class SaveGlobal {
605 ControlFlowVisitor &self;
606 std::vector<std::pair<cstring, ControlFlowVisitor *>> saved;
607
608 public:
609 SaveGlobal(ControlFlowVisitor &self, cstring key) : self(self) {
610 saved.push_back(self.save_global(key));
611 }
612 SaveGlobal(ControlFlowVisitor &self, cstring k1, cstring k2) : self(self) {
613 saved.push_back(self.save_global(k1));
614 saved.push_back(self.save_global(k2));
615 }
616 ~SaveGlobal() {
617 for (auto it = saved.rbegin(); it != saved.rend(); ++it) self.restore_global(*it);
618 }
619 };
620
621 bool has_flow_joins() const override { return !!flow_join_points; }
622 const flow_join_info_t *flow_join_status(const IR::Node *n) const {
623 if (!flow_join_points || !flow_join_points->count(n)) return nullptr;
624 return &flow_join_points->at(n);
625 }
626};
627
636class SplitFlowVisit_base {
637 protected:
638 Visitor &v;
639 SplitFlowVisit_base *prev;
640 std::vector<Visitor *> visitors;
641 int visit_next = 0, start_index = 0;
642 bool paused = false;
643 friend ControlFlowVisitor;
644
645 explicit SplitFlowVisit_base(Visitor &v) : v(v) {
646 prev = v.split_link;
647 v.split_link = this;
648 }
649 ~SplitFlowVisit_base() { v.split_link = prev; }
650 void *operator new(size_t); // declared and not defined, as this class can
651 // only be instantiated on the stack. Trying to allocate one on the heap will
652 // cause a linker error.
653
654 public:
655 bool finished() { return size_t(visit_next) >= visitors.size(); }
656 virtual bool ready() { return !finished() && !paused; }
657 void pause() { paused = true; }
658 void unpause() { paused = false; }
659 virtual void do_visit() = 0;
660 virtual void run_visit() {
661 auto *ctxt = v.getChildContext();
662 start_index = ctxt ? ctxt->child_index : 0;
663 paused = false;
664 while (!finished()) do_visit();
665 for (auto *cl : visitors) {
666 if (cl && cl != &v) v.flow_merge(*cl);
667 }
668 }
669 virtual void dbprint(std::ostream &) const = 0;
670 friend void dump(const SplitFlowVisit_base *);
671};
672
673template <class N>
674class SplitFlowVisit : public SplitFlowVisit_base {
675 std::vector<const N **> nodes;
676 std::vector<const N *const *> const_nodes;
677
678 public:
679 explicit SplitFlowVisit(Visitor &v) : SplitFlowVisit_base(v) {}
680 void addNode(const N *&node) {
681 BUG_CHECK(const_nodes.empty(), "Mixing const and non-const in SplitFlowVisit");
682 BUG_CHECK(visitors.size() == nodes.size(), "size mismatch in SplitFlowVisit");
683 BUG_CHECK(visit_next == 0, "Can't addNode to SplitFlowVisit after visiting started");
684 visitors.push_back(visitors.empty() ? &v : &v.flow_clone());
685 nodes.emplace_back(&node);
686 }
687 void addNode(const N *const &node) {
688 BUG_CHECK(nodes.empty(), "Mixing const and non-const in SplitFlowVisit");
689 BUG_CHECK(visitors.size() == const_nodes.size(), "size mismatch in SplitFlowVisit");
690 BUG_CHECK(visit_next == 0, "Can't addNode to SplitFlowVisit after visiting started");
691 visitors.push_back(visitors.empty() ? &v : &v.flow_clone());
692 const_nodes.emplace_back(&node);
693 }
694 template <class T1, class T2, class... Args>
695 void addNode(T1 &&t1, T2 &&t2, Args &&...args) {
696 addNode(std::forward<T1>(t1));
697 addNode(std::forward<T2>(t2), std::forward<Args>(args)...);
698 }
699 template <class... Args>
700 explicit SplitFlowVisit(Visitor &v, Args &&...args) : SplitFlowVisit(v) {
701 addNode(std::forward<Args>(args)...);
702 }
703 void do_visit() override {
704 if (!finished()) {
705 BUG_CHECK(!paused, "trying to visit paused split_flow_visitor");
706 int idx = visit_next++;
707 if (nodes.empty())
708 visitors.at(idx)->visit(*const_nodes.at(idx), nullptr, start_index + idx);
709 else
710 visitors.at(idx)->visit(*nodes.at(idx), nullptr, start_index + idx);
711 }
712 }
713 void dbprint(std::ostream &out) const override {
714 out << "SplitFlowVisit processed " << visit_next << " of " << visitors.size();
715 }
716};
717
718template <class N>
719class SplitFlowVisitVector : public SplitFlowVisit_base {
720 IR::Vector<N> *vec = nullptr;
721 const IR::Vector<N> *const_vec = nullptr;
722 std::vector<const IR::Node *> result;
723 void init_visit(size_t size) {
724 if (size > 0) visitors.push_back(&v);
725 while (visitors.size() < size) visitors.push_back(&v.flow_clone());
726 }
727
728 public:
729 SplitFlowVisitVector(Visitor &v, IR::Vector<N> &vec) : SplitFlowVisit_base(v), vec(&vec) {
730 result.resize(vec.size());
731 init_visit(vec.size());
732 }
733 SplitFlowVisitVector(Visitor &v, const IR::Vector<N> &vec)
734 : SplitFlowVisit_base(v), const_vec(&vec) {
735 init_visit(vec.size());
736 }
737 void do_visit() override {
738 if (!finished()) {
739 BUG_CHECK(!paused, "trying to visit paused split_flow_visitor");
740 int idx = visit_next++;
741 if (vec)
742 result[idx] = visitors.at(idx)->apply_visitor(vec->at(idx));
743 else
744 visitors.at(idx)->visit(const_vec->at(idx), nullptr, start_index + idx);
745 }
746 }
747 void run_visit() override {
748 SplitFlowVisit_base::run_visit();
749 if (vec) {
750 int idx = 0;
751 for (auto i = vec->begin(); i != vec->end(); ++idx) {
752 if (!result[idx] && *i) {
753 i = vec->erase(i);
754 } else if (result[idx] == *i) {
755 ++i;
756 } else if (auto l = result[idx]->template to<IR::Vector<N>>()) {
757 i = vec->erase(i);
758 i = vec->insert(i, l->begin(), l->end());
759 i += l->size();
760 } else if (auto v = result[idx]->template to<IR::VectorBase>()) {
761 if (v->empty()) {
762 i = vec->erase(i);
763 } else {
764 i = vec->insert(i, v->size() - 1, nullptr);
765 for (auto el : *v) {
766 CHECK_NULL(el);
767 if (auto e = el->template to<N>())
768 *i++ = e;
769 else
770 BUG("visitor returned invalid type %s for Vector<%s>",
771 el->node_type_name(), N::static_type_name());
772 }
773 }
774 } else if (auto e = result[idx]->template to<N>()) {
775 *i++ = e;
776 } else {
777 CHECK_NULL(result[idx]);
778 BUG("visitor returned invalid type %s for Vector<%s>",
779 result[idx]->node_type_name(), N::static_type_name());
780 }
781 }
782 }
783 }
784 void dbprint(std::ostream &out) const override {
785 out << "SplitFlowVisitVector processed " << visit_next << " of " << visitors.size();
786 }
787};
788
789class Backtrack : public virtual Visitor {
790 public:
791 struct trigger : public ICastable {
792 enum type_t { OK, OTHER } type;
793 explicit trigger(type_t t) : type(t) {}
794 virtual ~trigger();
795 virtual void dbprint(std::ostream &out) const { out << demangle(typeid(*this).name()); }
796
797 protected:
798 // must call this from the constructor if a trigger subclass contains pointers
799 // or references to GC objects
800 void register_for_gc(size_t);
801
802 DECLARE_TYPEINFO(trigger);
803 };
804 virtual bool backtrack(trigger &trig) = 0;
805 virtual bool never_backtracks() { return false; } // generally not overridden
806 // returns true for passes that will never catch a trigger (backtrack() is always false)
807};
808
809std::ostream &operator<<(std::ostream &out, const Backtrack::trigger &trigger);
810
811class P4WriteContext : public virtual Visitor {
812 public:
813 bool isWrite(bool root_value = false); // might write based on context
814 bool isRead(bool root_value = false); // might read based on context
815 // note that the context might (conservatively) return true for BOTH isWrite and isRead,
816 // as it might be an 'inout' access or it might be unable to decide.
817};
818
823template <typename NodeType, typename Func>
824void forAllMatching(const IR::Node *root, Func &&function) {
825 struct NodeVisitor : public Inspector {
826 explicit NodeVisitor(Func &&function) : function(function) {}
827 Func function;
828 void postorder(const NodeType *node) override { function(node); }
829 };
830 root->apply(NodeVisitor(std::forward<Func>(function)));
831}
832
839template <typename NodeType, typename RootType, typename Func>
840const RootType *modifyAllMatching(const RootType *root, Func &&function) {
841 struct NodeVisitor : public Modifier {
842 explicit NodeVisitor(Func &&function) : function(function) {}
843 Func function;
844 void postorder(NodeType *node) override { function(node); }
845 };
846 return root->apply(NodeVisitor(std::forward<Func>(function)))->template checkedTo<RootType>();
847}
848
855template <typename NodeType, typename Func>
856const IR::Node *transformAllMatching(const IR::Node *root, Func &&function) {
857 struct NodeVisitor : public Transform {
858 explicit NodeVisitor(Func &&function) : function(function) {}
859 Func function;
860 const IR::Node *postorder(NodeType *node) override { return function(node); }
861 };
862 return root->apply(NodeVisitor(std::forward<Func>(function)));
863}
864
865} // namespace P4
866
867#endif /* IR_VISITOR_H_ */
Definition visitor.h:789
Definition visitor.h:486
void flow_merge_global_to(cstring key) override
Definition visitor.h:567
bool join_flows(const IR::Node *n) override
Definition visitor.cpp:668
void flow_merge(Visitor &) override=0
void post_join_flows(const IR::Node *, const IR::Node *) override
Definition visitor.cpp:728
virtual bool filter_join_point(const IR::Node *)
Definition visitor.h:553
Definition castable.h:36
Definition node.h:53
Definition ir/vector.h:59
Definition visitor.h:418
Definition visitor.h:385
Definition visitor.h:811
Definition visitor.h:636
Definition visitor.h:442
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:128
void forAllMatching(const IR::Node *root, Func &&function)
Definition visitor.h:824
const IR::Node * transformAllMatching(const IR::Node *root, Func &&function)
Definition visitor.h:856
const RootType * modifyAllMatching(const RootType *root, Func &&function)
Definition visitor.h:840
STL namespace.
Definition visitor.h:791
T * to() noexcept
Definition rtti.h:226
Definition visitor.h:47