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