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:
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();
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 inline bool isInContext(const IR::Node *n) const {
278 for (auto *c = ctxt; c; c = c->parent) {
279 if (c->node == n || c->original == n) return true;
280 }
281 return false;
282 }
283
287 const IR::Node *getCurrentNode() const { return ctxt->node; }
288 template <class T>
289 const T *getCurrentNode() const {
290 return ctxt->node ? ctxt->node->to<T>() : nullptr;
291 }
292
296 bool warning_enabled(int warning_kind) const { return warning_enabled(this, warning_kind); }
299 static bool warning_enabled(const Visitor *visitor, int warning_kind);
300 template <class T, typename = std::enable_if_t<Util::has_SourceInfo_v<T>>, class... Args>
301 void warn(const int kind, const char *format, const T *node, Args &&...args) {
302 if (warning_enabled(kind)) ::P4::warning(kind, format, node, std::forward<Args>(args)...);
303 }
304
306 template <class T,
307 typename = std::enable_if_t<Util::has_SourceInfo_v<T> && !std::is_pointer_v<T>>,
308 class... Args>
309 void warn(const int kind, const char *format, const T &node, Args &&...args) {
310 if (warning_enabled(kind)) ::P4::warning(kind, format, node, std::forward<Args>(args)...);
311 }
312
313 protected:
314 // if visitDagOnce is set to 'false' (usually in the derived Visitor
315 // class constructor), nodes that appear multiple times in the tree
316 // will be visited repeatedly. If this is done in a Modifier or Transform
317 // pass, this will result in them being duplicated if they are modified.
318 bool visitDagOnce = true;
319 bool dontForwardChildrenBeforePreorder = false;
320 // if joinFlows is 'true', Visitor will track nodes with more than one parent and
321 // flow_merge the visitor from all the parents before visiting the node and its
322 // children. This only works for Inspector (not Modifier/Transform) currently.
323 bool joinFlows = false;
324
325 virtual void init_join_flows(const IR::Node *) {
326 BUG("joinFlows only supported in ControlFlowVisitor currently");
327 }
328
349 virtual bool join_flows(const IR::Node *) { return false; }
352 virtual void post_join_flows(const IR::Node *, const IR::Node *) {}
353
354 void visit_children(const IR::Node *, std::function<void()> fn) { fn(); }
355 class Tracker; // used by Inspector -- private to it
356 class ChangeTracker; // used by Modifier and Transform -- private to them
357 // This overrides visitDagOnce for a single node -- can only be called from
358 // preorder and postorder functions
359 // FIXME: It would be better named visitCurrentOnce() / visitCurrenAgain()
360 virtual void visitOnce() const { BUG("do not know how to handle request"); }
361 virtual void visitAgain() const { BUG("do not know how to handle request"); }
362
363 private:
364 virtual void visitor_const_error();
365 const Context *ctxt = nullptr; // should be readonly to subclasses
366 friend class Inspector;
367 friend class Modifier;
368 friend class Transform;
369 friend class ControlFlowVisitor;
370};
371
372class Modifier : public virtual Visitor {
373 std::shared_ptr<ChangeTracker> visited;
374 void visitor_const_error() override;
375 bool check_clone(const Visitor *) override;
376
377 public:
378 profile_t init_apply(const IR::Node *root) override;
379 const IR::Node *apply_visitor(const IR::Node *n, const char *name = 0) override;
380 virtual bool preorder(IR::Node *) { return true; }
381 virtual void postorder(IR::Node *) {}
382 virtual void revisit(const IR::Node *, const IR::Node *) {}
383 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
384#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
385 virtual bool preorder(IR::CLASS *); \
386 virtual void postorder(IR::CLASS *); \
387 virtual void revisit(const IR::CLASS *, const IR::CLASS *); \
388 virtual void loop_revisit(const IR::CLASS *);
389 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
390#undef DECLARE_VISIT_FUNCTIONS
391 void revisit_visited();
392 bool visit_in_progress(const IR::Node *) const;
393 void visitOnce() const override;
394 void visitAgain() const override;
395
396 protected:
397 bool forceClone = false; // force clone whole tree even if unchanged
398};
399
400class Inspector : public virtual Visitor {
401 std::shared_ptr<Tracker> visited;
402 bool check_clone(const Visitor *) override;
403
404 public:
405 profile_t init_apply(const IR::Node *root) override;
406 const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override;
407 virtual bool preorder(const IR::Node *) { return true; } // return 'false' to prune
408 virtual void postorder(const IR::Node *) {}
409 virtual void revisit(const IR::Node *) {}
410 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
411#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
412 virtual bool preorder(const IR::CLASS *); \
413 virtual void postorder(const IR::CLASS *); \
414 virtual void revisit(const IR::CLASS *); \
415 virtual void loop_revisit(const IR::CLASS *);
416 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
417#undef DECLARE_VISIT_FUNCTIONS
418 void revisit_visited();
419 bool visit_in_progress(const IR::Node *n) const;
420 void visitOnce() const override;
421 void visitAgain() const override;
422};
423
424class Transform : public virtual Visitor {
425 std::shared_ptr<ChangeTracker> visited;
426 bool prune_flag = false;
427 void visitor_const_error() override;
428 bool check_clone(const Visitor *) override;
429
430 public:
431 profile_t init_apply(const IR::Node *root) override;
432 const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override;
433 virtual const IR::Node *preorder(IR::Node *n) { return n; }
434 virtual const IR::Node *postorder(IR::Node *n) { return n; }
435 virtual void revisit(const IR::Node *, const IR::Node *) {}
436 virtual void loop_revisit(const IR::Node *) { BUG("IR loop detected"); }
437#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \
438 virtual const IR::Node *preorder(IR::CLASS *); \
439 virtual const IR::Node *postorder(IR::CLASS *); \
440 virtual void revisit(const IR::CLASS *, const IR::Node *); \
441 virtual void loop_revisit(const IR::CLASS *);
442 IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS)
443#undef DECLARE_VISIT_FUNCTIONS
444 void revisit_visited();
445 bool visit_in_progress(const IR::Node *) const;
446 void visitOnce() const override;
447 void visitAgain() const override;
448 // can only be called usefully from a 'preorder' function (directly or indirectly)
449 void prune() { prune_flag = true; }
450
451 protected:
452 const IR::Node *transform_child(const IR::Node *child) {
453 auto *rv = apply_visitor(child);
454 prune_flag = true;
455 return rv;
456 }
457 bool forceClone = false; // force clone whole tree even if unchanged
458};
459
460// turn this on for extra info tracking control joinFlows for debugging
461#define DEBUG_FLOW_JOIN 0
462
463class ControlFlowVisitor : public virtual Visitor {
464 std::map<cstring, ControlFlowVisitor &> &globals;
465
466 protected:
467 ControlFlowVisitor *clone() const override = 0;
469 ControlFlowVisitor *vclone = nullptr; // a clone to accumulate info into
470 int count = 0; // additional parents needed -1
471 bool done = false;
472#if DEBUG_FLOW_JOIN
473 struct ctrs_t {
474 int exist = 0, visited = 0;
475 };
476 std::map<const IR::Node *, ctrs_t> parents;
477#endif
478 };
479 typedef std::map<const IR::Node *, flow_join_info_t> flow_join_points_t;
480 friend std::ostream &operator<<(std::ostream &, const flow_join_info_t &);
481 friend std::ostream &operator<<(std::ostream &, const flow_join_points_t &);
482 friend void dump(const flow_join_info_t &);
483 friend void dump(const flow_join_info_t *);
484 friend void dump(const flow_join_points_t &);
485 friend void dump(const flow_join_points_t *);
486
487 // Flag set by visit_children of nodes that are unconditional branches, to denote that
488 // the control flow is currently unreachable. Future flow_merges to this visitor should
489 // clear this if merging a reachable state.
490 bool unreachable = false;
491
492 flow_join_points_t *flow_join_points = 0;
493 class SetupJoinPoints : public Inspector {
494 protected:
495 flow_join_points_t &join_points;
496 bool preorder(const IR::Node *n) override {
497 BUG_CHECK(join_points.count(n) == 0, "oops");
498#if DEBUG_FLOW_JOIN
499 auto *ctxt = getContext();
500 join_points[n].parents[ctxt ? ctxt->original : nullptr].exist++;
501#endif
502 return true;
503 }
504 void revisit(const IR::Node *n) override {
505 ++join_points[n].count;
506#if DEBUG_FLOW_JOIN
507 auto *ctxt = getContext();
508 join_points[n].parents[ctxt ? ctxt->original : nullptr].exist++;
509#endif
510 }
511
512 public:
513 explicit SetupJoinPoints(flow_join_points_t &fjp) : join_points(fjp) {}
514 };
515 bool BackwardsCompatibleBroken = false; // enable old broken behavior for code that
516 // depends on it. Should not be set for new uses.
517
518 virtual void applySetupJoinPoints(const IR::Node *root) {
519 root->apply(SetupJoinPoints(*flow_join_points));
520 }
521 void init_join_flows(const IR::Node *root) override;
522 bool join_flows(const IR::Node *n) override;
523 void post_join_flows(const IR::Node *, const IR::Node *) override;
524
530 virtual bool filter_join_point(const IR::Node *) { return false; }
531 ControlFlowVisitor() : globals(*new std::map<cstring, ControlFlowVisitor &>) {}
532
533 public:
534 ControlFlowVisitor *controlFlowVisitor() override { return this; }
535 ControlFlowVisitor &flow_clone() override;
536 void flow_merge(Visitor &) override = 0;
537 virtual void flow_copy(ControlFlowVisitor &) = 0;
538 virtual bool operator==(const ControlFlowVisitor &) const {
539 BUG("%s pass does not support loops", name());
540 }
541 bool operator!=(const ControlFlowVisitor &v) const { return !(*this == v); }
542 void setUnreachable() { unreachable = true; }
543 bool isUnreachable() { return unreachable; }
544 void flow_merge_global_to(cstring key) override {
545 if (globals.count(key))
546 globals.at(key).flow_merge(*this);
547 else
548 globals.emplace(key, flow_clone());
549 }
550 void flow_merge_global_from(cstring key) override {
551 if (globals.count(key)) flow_merge(globals.at(key));
552 }
553 void erase_global(cstring key) override { globals.erase(key); }
554 bool check_global(cstring key) override { return globals.count(key) != 0; }
555 void clear_globals() override { globals.clear(); }
556 std::pair<cstring, ControlFlowVisitor *> save_global(cstring key) {
557 ControlFlowVisitor *cfv = nullptr;
558 if (auto i = globals.find(key); i != globals.end()) {
559 cfv = &i->second;
560 globals.erase(i);
561 }
562 return std::make_pair(key, cfv);
563 }
564 void restore_global(std::pair<cstring, ControlFlowVisitor *> saved) {
565 globals.erase(saved.first);
566 if (saved.second) globals.emplace(saved.first, *saved.second);
567 }
568
571 Visitor &self;
572 cstring key;
573
574 public:
575 GuardGlobal(Visitor &self, cstring key) : self(self), key(key) {
576 BUG_CHECK(!self.check_global(key), "ControlFlowVisitor global %s in use", key);
577 }
578 ~GuardGlobal() { self.erase_global(key); }
579 };
582 ControlFlowVisitor &self;
583 std::vector<std::pair<cstring, ControlFlowVisitor *>> saved;
584
585 public:
586 SaveGlobal(ControlFlowVisitor &self, cstring key) : self(self) {
587 saved.push_back(self.save_global(key));
588 }
589 SaveGlobal(ControlFlowVisitor &self, cstring k1, cstring k2) : self(self) {
590 saved.push_back(self.save_global(k1));
591 saved.push_back(self.save_global(k2));
592 }
593 ~SaveGlobal() {
594 for (auto it = saved.rbegin(); it != saved.rend(); ++it) self.restore_global(*it);
595 }
596 };
597
598 bool has_flow_joins() const override { return !!flow_join_points; }
599 const flow_join_info_t *flow_join_status(const IR::Node *n) const {
600 if (!flow_join_points || !flow_join_points->count(n)) return nullptr;
601 return &flow_join_points->at(n);
602 }
603};
604
614 protected:
615 Visitor &v;
617 std::vector<Visitor *> visitors;
618 int visit_next = 0, start_index = 0;
619 bool paused = false;
620 friend ControlFlowVisitor;
621
622 explicit SplitFlowVisit_base(Visitor &v) : v(v) {
623 prev = v.split_link;
624 v.split_link = this;
625 }
626 ~SplitFlowVisit_base() { v.split_link = prev; }
627 void *operator new(size_t); // declared and not defined, as this class can
628 // only be instantiated on the stack. Trying to allocate one on the heap will
629 // cause a linker error.
630
631 public:
632 bool finished() { return size_t(visit_next) >= visitors.size(); }
633 virtual bool ready() { return !finished() && !paused; }
634 void pause() { paused = true; }
635 void unpause() { paused = false; }
636 virtual void do_visit() = 0;
637 virtual void run_visit() {
638 auto *ctxt = v.getChildContext();
639 start_index = ctxt ? ctxt->child_index : 0;
640 paused = false;
641 while (!finished()) do_visit();
642 for (auto *cl : visitors) {
643 if (cl && cl != &v) v.flow_merge(*cl);
644 }
645 }
646 virtual void dbprint(std::ostream &) const = 0;
647 friend void dump(const SplitFlowVisit_base *);
648};
649
650template <class N>
652 std::vector<const N **> nodes;
653 std::vector<const N *const *> const_nodes;
654
655 public:
656 explicit SplitFlowVisit(Visitor &v) : SplitFlowVisit_base(v) {}
657 void addNode(const N *&node) {
658 BUG_CHECK(const_nodes.empty(), "Mixing const and non-const in SplitFlowVisit");
659 BUG_CHECK(visitors.size() == nodes.size(), "size mismatch in SplitFlowVisit");
660 BUG_CHECK(visit_next == 0, "Can't addNode to SplitFlowVisit after visiting started");
661 visitors.push_back(visitors.empty() ? &v : &v.flow_clone());
662 nodes.emplace_back(&node);
663 }
664 void addNode(const N *const &node) {
665 BUG_CHECK(nodes.empty(), "Mixing const and non-const in SplitFlowVisit");
666 BUG_CHECK(visitors.size() == const_nodes.size(), "size mismatch in SplitFlowVisit");
667 BUG_CHECK(visit_next == 0, "Can't addNode to SplitFlowVisit after visiting started");
668 visitors.push_back(visitors.empty() ? &v : &v.flow_clone());
669 const_nodes.emplace_back(&node);
670 }
671 template <class T1, class T2, class... Args>
672 void addNode(T1 &&t1, T2 &&t2, Args &&...args) {
673 addNode(std::forward<T1>(t1));
674 addNode(std::forward<T2>(t2), std::forward<Args>(args)...);
675 }
676 template <class... Args>
677 explicit SplitFlowVisit(Visitor &v, Args &&...args) : SplitFlowVisit(v) {
678 addNode(std::forward<Args>(args)...);
679 }
680 void do_visit() override {
681 if (!finished()) {
682 BUG_CHECK(!paused, "trying to visit paused split_flow_visitor");
683 int idx = visit_next++;
684 if (nodes.empty())
685 visitors.at(idx)->visit(*const_nodes.at(idx), nullptr, start_index + idx);
686 else
687 visitors.at(idx)->visit(*nodes.at(idx), nullptr, start_index + idx);
688 }
689 }
690 void dbprint(std::ostream &out) const override {
691 out << "SplitFlowVisit processed " << visit_next << " of " << visitors.size();
692 }
693};
694
695template <class N>
697 IR::Vector<N> *vec = nullptr;
698 const IR::Vector<N> *const_vec = nullptr;
699 std::vector<const IR::Node *> result;
700 void init_visit(size_t size) {
701 if (size > 0) visitors.push_back(&v);
702 while (visitors.size() < size) visitors.push_back(&v.flow_clone());
703 }
704
705 public:
707 result.resize(vec.size());
708 init_visit(vec.size());
709 }
711 : SplitFlowVisit_base(v), const_vec(&vec) {
712 init_visit(vec.size());
713 }
714 void do_visit() override {
715 if (!finished()) {
716 BUG_CHECK(!paused, "trying to visit paused split_flow_visitor");
717 int idx = visit_next++;
718 if (vec)
719 result[idx] = visitors.at(idx)->apply_visitor(vec->at(idx));
720 else
721 visitors.at(idx)->visit(const_vec->at(idx), nullptr, start_index + idx);
722 }
723 }
724 void run_visit() override {
725 SplitFlowVisit_base::run_visit();
726 if (vec) {
727 int idx = 0;
728 for (auto i = vec->begin(); i != vec->end(); ++idx) {
729 if (!result[idx] && *i) {
730 i = vec->erase(i);
731 } else if (result[idx] == *i) {
732 ++i;
733 } else if (auto l = result[idx]->template to<IR::Vector<N>>()) {
734 i = vec->erase(i);
735 i = vec->insert(i, l->begin(), l->end());
736 i += l->size();
737 } else if (auto v = result[idx]->template to<IR::VectorBase>()) {
738 if (v->empty()) {
739 i = vec->erase(i);
740 } else {
741 i = vec->insert(i, v->size() - 1, nullptr);
742 for (auto el : *v) {
743 CHECK_NULL(el);
744 if (auto e = el->template to<N>())
745 *i++ = e;
746 else
747 BUG("visitor returned invalid type %s for Vector<%s>",
748 el->node_type_name(), N::static_type_name());
749 }
750 }
751 } else if (auto e = result[idx]->template to<N>()) {
752 *i++ = e;
753 } else {
754 CHECK_NULL(result[idx]);
755 BUG("visitor returned invalid type %s for Vector<%s>",
756 result[idx]->node_type_name(), N::static_type_name());
757 }
758 }
759 }
760 }
761 void dbprint(std::ostream &out) const override {
762 out << "SplitFlowVisitVector processed " << visit_next << " of " << visitors.size();
763 }
764};
765
766class Backtrack : public virtual Visitor {
767 public:
768 struct trigger : public ICastable {
769 enum type_t { OK, OTHER } type;
770 explicit trigger(type_t t) : type(t) {}
771 virtual ~trigger();
772 virtual void dbprint(std::ostream &out) const { out << demangle(typeid(*this).name()); }
773
774 protected:
775 // must call this from the constructor if a trigger subclass contains pointers
776 // or references to GC objects
777 void register_for_gc(size_t);
778
779 DECLARE_TYPEINFO(trigger);
780 };
781 virtual bool backtrack(trigger &trig) = 0;
782 virtual bool never_backtracks() { return false; } // generally not overridden
783 // returns true for passes that will never catch a trigger (backtrack() is always false)
784};
785
786std::ostream &operator<<(std::ostream &out, const Backtrack::trigger &trigger);
787
788class P4WriteContext : public virtual Visitor {
789 public:
790 bool isWrite(bool root_value = false); // might write based on context
791 bool isRead(bool root_value = false); // might read based on context
792 // note that the context might (conservatively) return true for BOTH isWrite and isRead,
793 // as it might be an 'inout' access or it might be unable to decide.
794};
795
800template <typename NodeType, typename Func>
801void forAllMatching(const IR::Node *root, Func &&function) {
802 struct NodeVisitor : public Inspector {
803 explicit NodeVisitor(Func &&function) : function(function) {}
804 Func function;
805 void postorder(const NodeType *node) override { function(node); }
806 };
807 root->apply(NodeVisitor(std::forward<Func>(function)));
808}
809
816template <typename NodeType, typename RootType, typename Func>
817const RootType *modifyAllMatching(const RootType *root, Func &&function) {
818 struct NodeVisitor : public Modifier {
819 explicit NodeVisitor(Func &&function) : function(function) {}
820 Func function;
821 void postorder(NodeType *node) override { function(node); }
822 };
823 return root->apply(NodeVisitor(std::forward<Func>(function)));
824}
825
832template <typename NodeType, typename Func>
833const IR::Node *transformAllMatching(const IR::Node *root, Func &&function) {
834 struct NodeVisitor : public Transform {
835 explicit NodeVisitor(Func &&function) : function(function) {}
836 Func function;
837 const IR::Node *postorder(NodeType *node) override { return function(node); }
838 };
839 return root->apply(NodeVisitor(std::forward<Func>(function)));
840}
841
842} // namespace P4
843
844#endif /* IR_VISITOR_H_ */
Definition visitor.h:766
RAII class to ensure global key is only used in one place.
Definition visitor.h:570
RAII class to save and restore one or more global keys.
Definition visitor.h:581
Definition visitor.h:463
void flow_merge_global_to(cstring key) override
Definition visitor.h:544
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:530
Definition castable.h:36
Definition node.h:94
Definition vector.h:59
Definition visitor.h:400
Definition visitor.h:372
Definition visitor.h:788
Definition visitor.h:613
Definition visitor.h:651
Definition visitor.h:696
Definition visitor.h:424
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:309
const IR::Node * getCurrentNode() const
Definition visitor.h:287
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:352
bool warning_enabled(int warning_kind) const
Definition visitor.h:296
virtual void flow_merge(Visitor &)
Definition visitor.h:212
virtual bool join_flows(const IR::Node *)
Definition visitor.h:349
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:115
void forAllMatching(const IR::Node *root, Func &&function)
Definition visitor.h:801
const IR::Node * transformAllMatching(const IR::Node *root, Func &&function)
Definition visitor.h:833
const RootType * modifyAllMatching(const RootType *root, Func &&function)
Definition visitor.h:817
STL namespace.
Definition visitor.h:768
T * to() noexcept
Definition rtti.h:226
bool is() const noexcept
Definition rtti.h:216
Definition visitor.h:47