From ba6a9f8d5e2b690fb98466bb68bbd2341f09655b Mon Sep 17 00:00:00 2001 From: Chris Dodd Date: Mon, 11 Nov 2024 06:28:41 +0000 Subject: [PATCH] Framework for copy-on-write visitors (WIP) Signed-off-by: Chris Dodd --- CMakeLists.txt | 4 +- frontends/p4/removeReturns.cpp | 14 +- frontends/p4/removeReturns.h | 22 +-- ir/copy_on_write_inl.h | 256 +++++++++++++++++++++++++++++++++ ir/copy_on_write_ptr.h | 72 ++++++++++ ir/node.h | 25 ++++ ir/visitor.h | 66 +++++++++ tools/ir-generator/irclass.cpp | 43 +++++- tools/ir-generator/irclass.h | 1 + 9 files changed, 482 insertions(+), 21 deletions(-) create mode 100644 ir/copy_on_write_inl.h create mode 100644 ir/copy_on_write_ptr.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 14180926591..46a831ed4d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ endif() project (P4C) -# set (CMAKE_CXX_EXTENSIONS OFF) # prefer using -std=c++17 rather than -std=gnu++17 -set (CMAKE_CXX_STANDARD 17) +# set (CMAKE_CXX_EXTENSIONS OFF) # prefer using -std=c++20 rather than -std=gnu++20 +set (CMAKE_CXX_STANDARD 20) set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) diff --git a/frontends/p4/removeReturns.cpp b/frontends/p4/removeReturns.cpp index f84512d0770..925ba7bb5e8 100644 --- a/frontends/p4/removeReturns.cpp +++ b/frontends/p4/removeReturns.cpp @@ -20,7 +20,7 @@ limitations under the License. namespace P4 { -bool MoveToElseAfterBranch::preorder(IR::BlockStatement *block) { +bool MoveToElseAfterBranch::preorder(IR::COWptr block) { movedToIfBranch = false; for (auto it = block->components.begin(); it != block->components.end();) { if (movedToIfBranch) @@ -32,7 +32,7 @@ bool MoveToElseAfterBranch::preorder(IR::BlockStatement *block) { return false; } -bool MoveToElseAfterBranch::moveFromParentTo(const IR::Statement *&child) { +template bool MoveToElseAfterBranch::moveFromParentTo(T &child) { auto parent = getParent(); size_t next = getContext()->child_index + 1; if (!parent || next >= parent->components.size()) { @@ -43,9 +43,9 @@ bool MoveToElseAfterBranch::moveFromParentTo(const IR::Statement *&child) { IR::BlockStatement *modified = nullptr; if (!child) modified = new IR::BlockStatement; - else if (auto *t = child->to()) + else if (auto *t = child->template to()) modified = t->clone(); - else if (child->is()) + else if (child->template is()) modified = new IR::BlockStatement; else modified = new IR::BlockStatement({child}); @@ -55,7 +55,7 @@ bool MoveToElseAfterBranch::moveFromParentTo(const IR::Statement *&child) { return true; } -bool MoveToElseAfterBranch::preorder(IR::IfStatement *ifStmt) { +bool MoveToElseAfterBranch::preorder(IR::COWptr ifStmt) { hasJumped = false; bool movedCode = false; visit(ifStmt->ifTrue, "ifTrue", 1); @@ -74,7 +74,7 @@ bool MoveToElseAfterBranch::preorder(IR::IfStatement *ifStmt) { return false; } -bool MoveToElseAfterBranch::preorder(IR::SwitchStatement *swch) { +bool MoveToElseAfterBranch::preorder(IR::COWptr swch) { // TBD: if there is exactly one case that falls through (all others end with a branch) // then we could move subsequent code into that case, as it done with 'if' bool canFallThrough = false; @@ -87,7 +87,7 @@ bool MoveToElseAfterBranch::preorder(IR::SwitchStatement *swch) { return false; } -void MoveToElseAfterBranch::postorder(IR::LoopStatement *) { +void MoveToElseAfterBranch::postorder(IR::COWptr) { // after a loop body is never unreachable hasJumped = false; } diff --git a/frontends/p4/removeReturns.h b/frontends/p4/removeReturns.h index f13ea82e8f9..2a0e1862711 100644 --- a/frontends/p4/removeReturns.h +++ b/frontends/p4/removeReturns.h @@ -65,7 +65,7 @@ introduce a boolean flag and extra tests to remove those branches. precondition: switchAddDefault pass has run to ensure switch statements cover all cases */ -class MoveToElseAfterBranch : public Modifier { +class MoveToElseAfterBranch : public COWModifier { /* This pass does not use (inherit from) ControlFlowVisitor, even though it is doing * control flow analysis, as it turns out to be more efficient to do it directly here * by overloading the branching constructs (if/switch/loops) and not cloning the visitor, @@ -80,22 +80,22 @@ class MoveToElseAfterBranch : public Modifier { * indicating that it needs to be removed from the BlockStatment */ bool movedToIfBranch = false; - bool preorder(IR::BlockStatement *) override; - bool moveFromParentTo(const IR::Statement *&child); - bool preorder(IR::IfStatement *) override; - bool preorder(IR::SwitchStatement *) override; - void postorder(IR::LoopStatement *) override; + bool preorder(IR::COWptr) override; + template bool moveFromParentTo(T &child); + bool preorder(IR::COWptr) override; + bool preorder(IR::COWptr) override; + void postorder(IR::COWptr) override; bool branch() { hasJumped = true; // no need to visit children return false; } - bool preorder(IR::BreakStatement *) override { return branch(); } - bool preorder(IR::ContinueStatement *) override { return branch(); } - bool preorder(IR::ExitStatement *) override { return branch(); } - bool preorder(IR::ReturnStatement *) override { return branch(); } + bool preorder(IR::COWptr) override { return branch(); } + bool preorder(IR::COWptr) override { return branch(); } + bool preorder(IR::COWptr) override { return branch(); } + bool preorder(IR::COWptr) override { return branch(); } // Only visit statements, skip all expressions - bool preorder(IR::Expression *) override { return false; } + bool preorder(IR::COWptr) override { return false; } public: MoveToElseAfterBranch() {} diff --git a/ir/copy_on_write_inl.h b/ir/copy_on_write_inl.h new file mode 100644 index 00000000000..9d488513a9e --- /dev/null +++ b/ir/copy_on_write_inl.h @@ -0,0 +1,256 @@ +/* +Copyright 2024 NVIDIA CORPORATION. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef IR_COPY_ON_WRITE_INL_H_ +#define IR_COPY_ON_WRITE_INL_H_ + +/* template methods declared in "copy_on_write_ptr.h" that can't be defined there + * due to order-of-declaration issues. */ + +namespace P4::IR { + +template +const U &COWfieldref::get() const { + if (info->clone) return info->clone->checkedTo()->*field; + return info->orig->checkedTo()->*field; +} + +template +U &COWfieldref::modify() const { + return info->mk_clone()->checkedTo()->*field; +} + +template +const U &COWfieldref::operator=(const U &val) const { + if (!info->clone) { + if (info->orig->checkedTo()->*field == val) return val; + info->clone = info->orig->clone(); + } + return info->clone->checkedTo()->*field = val; +} + +template +const U &COWfieldref::operator=(U &&val) const { + if (!info->clone) { + if (info->orig->checkedTo()->*field == val) return val; + info->clone = info->orig->clone(); + } + return info->clone->checkedTo()->*field = std::move(val); +} + +/* specializations for IR::Node pointers */ +template +requires std::derived_from +struct COWfieldref { + COWNode_info *info; + + const U * const &get() const; + const U *&modify() const; + operator const U * const&() const { return get(); } + const U *operator=(const U *val) const { + if (!info->clone) { + if (info->orig->checkedTo()->*field == val) return val; + info->clone = info->orig->clone(); + } + return info->clone->checkedTo()->*field = val; + } + void set(const U *val) const { *this = val; } + void set(const Node *val) const { *this = val ? val->checkedTo() : (U *)nullptr; } +}; + +/* specializations for IR::Vector */ +template T::*field> +struct COWfieldref, field> { + COWNode_info *info; + + struct element_ref { + COWNode_info *info; + bool is_const; + union { + typename Vector::const_iterator ci; + typename Vector::iterator ni; + }; + element_ref(COWNode_info *inf, typename Vector::const_iterator i) + : info(inf), is_const(true) { ci = i; } + element_ref(COWNode_info *inf, typename Vector::iterator i) + : info(inf), is_const(false) { ni = i; } + void clone_fixup() { + if (is_const) { + // messy problem -- need to clone (iff not yet cloned) and then find the + // corresponding iterator in the clone + auto i = (info->mk_clone()->checkedTo()->*field).begin(); + auto &orig_vec = info->orig->checkedTo()->*field; + for (auto oi = orig_vec.begin(); oi != ci; ++oi, ++i) + BUG_CHECK(oi != orig_vec.end(), "Invalid iterator in clone_fixup"); + ni = i; + is_const = false; + } + } + const U *get() const { + if (is_const && info->clone) clone_fixup(); + return *ci; + } + operator const U *() const { + if (is_const && info->clone) clone_fixup(); + return *ci; + } + const U *operator=(const U *val) const { + clone_fixup(); + return *ni = val; + } + void set(const U *val) const { + clone_fixup(); + *ni = val; + } + void set(const Node *val) const { + set(val ? val->checkedTo() : (U *)nullptr); + } + }; + + struct iterator { + element_ref ref; + iterator(COWNode_info *inf, typename Vector::const_iterator i) : ref(inf, i) {} + iterator(COWNode_info *inf, typename Vector::iterator i) : ref(inf, i) {} + iterator &operator++() { ++ref.ci; return *this; } + iterator operator++(int) { iterator rv = *this; ++ref.ci; return rv; } + iterator &operator--() { --ref.ci; return *this; } + iterator operator--(int) { iterator rv = *this; --ref.ci; return rv; } + bool operator==(const iterator &i) const { return ref.ci == i.ref.ci; } + bool operator!=(const iterator &i) const { return ref.ci != i.ref.ci; } + element_ref operator *() { return ref; } + }; + + const Vector &get() const { + if (info->clone) return info->clone->checkedTo()->*field; + return info->orig->checkedTo()->*field; + } + Vector &modify() const { return info->mk_clone()->checkedTo()->*field; } + operator const Vector&() const { return get(); } + Vector &operator=(const Vector &val) const { return modify() = val; } + Vector &operator=(Vector &&val) const { return modify() = std::move(val); } + iterator begin() { + if (info->clone) + return iterator(info, (info->clone->checkedTo()->*field).begin()); + else + return iterator(info, (info->orig->checkedTo()->*field).begin()); + } + iterator end() { + if (info->clone) + return iterator(info, (info->clone->checkedTo()->*field).begin()); + else + return iterator(info, (info->orig->checkedTo()->*field).begin()); + } + iterator erase(iterator i) { + i.ref.clone_fixup(); + Vector &vec = info->clone->checkedTo()->*field; + return vec.erase(i.ref.ni); + } + // FIXME need to add insert/appeand/prepend/emplace_back specializations +}; + +/* specializations for IR::IndexedVector */ +template T::*field> +struct COWfieldref, field> { + COWNode_info *info; + + struct element_ref { + COWNode_info *info; + bool is_const; + union { + typename IndexedVector::const_iterator ci; + typename IndexedVector::iterator ni; + }; + element_ref(COWNode_info *inf, typename IndexedVector::const_iterator i) + : info(inf), is_const(true) { ci = i; } + element_ref(COWNode_info *inf, typename IndexedVector::iterator i) + : info(inf), is_const(false) { ni = i; } + void clone_fixup() { + if (is_const) { + // messy problem -- need to clone (iff not yet cloned) and then find the + // corresponding iterator in the clone + auto i = (info->mk_clone()->checkedTo()->*field).begin(); + auto &orig_vec = info->orig->checkedTo()->*field; + for (auto oi = orig_vec.begin(); oi != ci; ++oi, ++i) + BUG_CHECK(oi != orig_vec.end(), "Invalid iterator in clone_fixup"); + ni = i; + is_const = false; + } + } + const U *get() const { + if (is_const && info->clone) clone_fixup(); + return *ci; + } + operator const U *() const { + if (is_const && info->clone) clone_fixup(); + return *ci; + } + const U *operator=(const U *val) const { + clone_fixup(); + return *ni = val; + } + void set(const U *val) const { + clone_fixup(); + *ni = val; + } + void set(const Node *val) const { + set(val ? val->checkedTo() : (U *)nullptr); + } + }; + + struct iterator { + element_ref ref; + iterator(COWNode_info *inf, typename IndexedVector::const_iterator i) : ref(inf, i) {} + iterator(COWNode_info *inf, typename IndexedVector::iterator i) : ref(inf, i) {} + iterator &operator++() { ++ref.ci; return *this; } + iterator operator++(int) { iterator rv = *this; ++ref.ci; return rv; } + iterator &operator--() { --ref.ci; return *this; } + iterator operator--(int) { iterator rv = *this; --ref.ci; return rv; } + bool operator==(const iterator &i) const { return ref.ci == i.ref.ci; } + bool operator!=(const iterator &i) const { return ref.ci != i.ref.ci; } + element_ref operator *() { return ref; } + }; + + const IndexedVector &get() const { + if (info->clone) return info->clone->checkedTo()->*field; + return info->orig->checkedTo()->*field; + } + IndexedVector &modify() const { return info->mk_clone()->checkedTo()->*field; } + operator const IndexedVector&() const { return get(); } + IndexedVector &operator=(const IndexedVector &val) const { return modify() = val; } + IndexedVector &operator=(IndexedVector &&val) const { return modify() = std::move(val); } + iterator begin() { + if (info->clone) + return iterator(info, (info->clone->checkedTo()->*field).begin()); + else + return iterator(info, (info->orig->checkedTo()->*field).begin()); + } + iterator end() { + if (info->clone) + return iterator(info, (info->clone->checkedTo()->*field).begin()); + else + return iterator(info, (info->orig->checkedTo()->*field).begin()); + } + iterator erase(iterator i) { + i.ref.clone_fixup(); + IndexedVector &vec = info->clone->checkedTo()->*field; + return vec.erase(i.ref.ni); + } + // FIXME need to add insert/appeand/prepend/emplace_back/removeByName specializations +}; + +} // namespace P4::IR + +#endif /* IR_COPY_ON_WRITE_INL_H_ */ diff --git a/ir/copy_on_write_ptr.h b/ir/copy_on_write_ptr.h new file mode 100644 index 00000000000..c9211463b0f --- /dev/null +++ b/ir/copy_on_write_ptr.h @@ -0,0 +1,72 @@ +/* +Copyright 2024 NVIDIA CORPORATION. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef IR_COPY_ON_WRITE_PTR_H_ +#define IR_COPY_ON_WRITE_PTR_H_ + +#include + +namespace P4::IR { + +class Node; + +template +concept COWref = requires(T r) { + r.set(r.get()); +}; + +struct COWNode_info { + const Node *orig; + Node *clone; + Node *mk_clone() const; + + explicit COWNode_info(const Node *n) : orig(n), clone(nullptr) {} + COWNode_info(const COWNode_info &) = delete; +}; + +template class COWinfo : public COWNode_info { +}; + +template struct COWfieldref { + COWNode_info *info; + + const U &get() const; + U &modify() const; + operator const U&() const { return get(); } + const U &operator=(const U &val) const; + const U &operator=(U &&val) const; + void set(const U &val) const { *this = val; } +}; + +template class COWptr { + std::shared_ptr> ptr; + +public: + COWptr() = default; + COWptr(const COWptr &) = default; + COWptr(COWptr &&) = default; + ~COWptr() = default; + COWptr &operator=(const COWptr &) = default; + COWptr &operator=(COWptr &&) = default; + + operator const T *() const { return ptr->clone ? ptr->clone : ptr->orig; } + typename T::COWref operator->() const { return typename T::COWref(ptr.get()); } + typename T::COWref operator*() const { return typename T::COWref(ptr.get()); } +}; + +} + +#endif /* IR_COPY_ON_WRITE_PTR_H_ */ diff --git a/ir/node.h b/ir/node.h index 53e5eab1b10..bead1ce1e42 100644 --- a/ir/node.h +++ b/ir/node.h @@ -21,6 +21,7 @@ limitations under the License. #include "ir-tree-macros.h" #include "ir/gen-tree-macro.h" +#include "ir/copy_on_write_ptr.h" #include "lib/castable.h" #include "lib/cstring.h" #include "lib/exceptions.h" @@ -72,11 +73,19 @@ class INode : public Util::IHasSourceInfo, public IHasDbPrint, public ICastable virtual cstring node_type_name() const = 0; virtual void validate() const {} +#if 1 + using ICastable::checkedTo; +#else // default checkedTo implementation for nodes: just fallback to generic ICastable method template std::enable_if_t, const T *> checkedTo() const { return ICastable::checkedTo(); } + template + std::enable_if_t, T *> checkedTo() { + return ICastable::checkedTo(); + } +#endif // alternative checkedTo implementation that produces slightly better error message // due to node_type_name() / static_type_name() being available @@ -87,6 +96,13 @@ class INode : public Util::IHasSourceInfo, public IHasDbPrint, public ICastable T::static_type_name()); return result; } + template + std::enable_if_t, T *> checkedTo() { + const auto *result = to(); + BUG_CHECK(result, "Cast failed: %1% with type %2% is not a %3%.", this, node_type_name(), + T::static_type_name()); + return result; + } DECLARE_TYPEINFO_WITH_TYPEID(INode, NodeKind::INode); }; @@ -172,6 +188,15 @@ class Node : public virtual INode { sink.Append(n->toString()); } + union COWref { + private: + COWNode_info *_info; + public: + COWfieldref srcInfo; + COWref(COWNode_info *i) { _info = i; } + COWref *operator->() { return this; } + }; + DECLARE_TYPEINFO_WITH_TYPEID(Node, NodeKind::Node, INode); }; diff --git a/ir/visitor.h b/ir/visitor.h index 15fdd14d650..8f95a7b8bd6 100644 --- a/ir/visitor.h +++ b/ir/visitor.h @@ -33,6 +33,7 @@ limitations under the License. #include "ir/gen-tree-macro.h" #include "ir/ir-tree-macros.h" #include "ir/node.h" +#include "ir/copy_on_write_ptr.h" #include "ir/vector.h" #include "lib/castable.h" #include "lib/cstring.h" @@ -166,6 +167,13 @@ class Visitor { } n.visit_children(*this); } + template requires IR::COWref + void visit(COW ref, const char *name = 0) { + auto o = ref.get(); + auto n = apply_visitor(o, name); + if (n != o) ref.set(n); + } + template void parallel_visit(IR::Vector &v, const char *name = 0) { if (name && ctxt) ctxt->child_name = name; @@ -365,7 +373,9 @@ class Visitor { const Context *ctxt = nullptr; // should be readonly to subclasses friend class Inspector; friend class Modifier; + friend class COWModifier; friend class Transform; + friend class COWTransform; friend class ControlFlowVisitor; }; @@ -397,6 +407,34 @@ class Modifier : public virtual Visitor { bool forceClone = false; // force clone whole tree even if unchanged }; +class COWModifier : public virtual Visitor { + std::shared_ptr visited; + void visitor_const_error() override; + bool check_clone(const Visitor *) override; + + public: + profile_t init_apply(const IR::Node *root) override; + const IR::Node *apply_visitor(const IR::Node *n, const char *name = 0) override; + virtual bool preorder(IR::COWptr) { return true; } + virtual void postorder(IR::COWptr) {} + virtual void revisit(IR::COWptr, const IR::Node *) {} + virtual void loop_revisit(IR::COWptr) { BUG("IR loop detected"); } +#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \ + virtual bool preorder(IR::COWptr); \ + virtual void postorder(IR::COWptr); \ + virtual void revisit(IR::COWptr, const IR::CLASS *); \ + virtual void loop_revisit(IR::COWptr); + IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS) +#undef DECLARE_VISIT_FUNCTIONS + void revisit_visited(); + bool visit_in_progress(const IR::Node *) const; + void visitOnce() const override; + void visitAgain() const override; + + protected: + bool forceClone = false; // force clone whole tree even if unchanged +}; + class Inspector : public virtual Visitor { std::shared_ptr visited; bool check_clone(const Visitor *) override; @@ -457,6 +495,34 @@ class Transform : public virtual Visitor { bool forceClone = false; // force clone whole tree even if unchanged }; +class COWTransform : public virtual Visitor { + std::shared_ptr visited; + bool prune_flag = false; + void visitor_const_error() override; + bool check_clone(const Visitor *) override; + + public: + profile_t init_apply(const IR::Node *root) override; + const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override; + virtual const IR::Node *preorder(IR::COWptr n) { return n; } + virtual const IR::Node *postorder(IR::COWptr n) { return n; } + virtual void revisit(IR::COWptr, const IR::Node *) {} + virtual void loop_revisit(IR::COWptr) { BUG("IR loop detected"); } +#define DECLARE_VISIT_FUNCTIONS(CLASS, BASE) \ + virtual const IR::Node *preorder(IR::COWptr); \ + virtual const IR::Node *postorder(IR::COWptr); \ + virtual void revisit(IR::COWptr, const IR::Node *); \ + virtual void loop_revisit(IR::COWptr); + IRNODE_ALL_SUBCLASSES(DECLARE_VISIT_FUNCTIONS) +#undef DECLARE_VISIT_FUNCTIONS + void revisit_visited(); + bool visit_in_progress(const IR::Node *) const; + void visitOnce() const override; + void visitAgain() const override; + // can only be called usefully from a 'preorder' function (directly or indirectly) + void prune() { prune_flag = true; } +}; + // turn this on for extra info tracking control joinFlows for debugging #define DEBUG_FLOW_JOIN 0 diff --git a/tools/ir-generator/irclass.cpp b/tools/ir-generator/irclass.cpp index 96ee569a435..aa879a3eb10 100644 --- a/tools/ir-generator/irclass.cpp +++ b/tools/ir-generator/irclass.cpp @@ -125,6 +125,7 @@ void IrDefinitions::generate(std::ostream &t, std::ostream &out, std::ostream &i << "#include \n\n" << "#include \"lib/big_int.h\" // IWYU pragma: keep\n" << "// Special IR classes and types\n" + << "#include \"ir/copy_on_write_ptr.h\" // IWYU pragma: keep\n" << "#include \"ir/dbprint.h\" // IWYU pragma: keep\n" << "#include \"ir/id.h\" // IWYU pragma: keep\n" << "#include \"ir/indexed_vector.h\" // IWYU pragma: keep\n" @@ -132,6 +133,8 @@ void IrDefinitions::generate(std::ostream &t, std::ostream &out, std::ostream &i << "#include \"ir/node.h\" // IWYU pragma: keep\n" << "#include \"ir/nodemap.h\" // IWYU pragma: keep\n" << "#include \"ir/vector.h\" // IWYU pragma: keep\n" + << "// copy_on_write_inl.h must be after vector.h and indexed_vector.h\n" + << "#include \"ir/copy_on_write_inl.h\" // IWYU pragma: keep\n" << "#include \"lib/ordered_map.h\" // IWYU pragma: keep\n" << std::endl << "namespace P4 {\n" @@ -408,6 +411,30 @@ cstring IrClass::qualified_name(const IrNamespace *in) const { return rv; } +IrElement::access_t IrClass::outputCOWfieldrefs(std::ostream &out) const { + auto access = IrElement::Private; + if (concreteParent) { + access = concreteParent->outputCOWfieldrefs(out); + } else { + out << indent << (access = IrElement::Public); + out << indent << indent << "COWfieldref srcInfo;\n"; + } + for (auto e : elements) { + if (auto *fld = e->to()) { + if (fld->isStatic) continue; + if (e->access != access) out << indent << (access = e->access); + out << indent << indent << "COWfieldref<" << name << ", "; + const IrClass *cls = fld->type->resolve(fld->clss ? fld->clss->containedIn : nullptr); + if (cls != nullptr && !fld->isInline) out << "const "; + out << fld->type->toString(); + if (cls != nullptr && !fld->isInline) out << "*"; + out << fld->type->declSuffix() << ", &" << name << "::" << fld->name << "> " + << fld->name << ";\n"; + } + } + return access; +} + void IrClass::generate_hdr(std::ostream &out) const { if (kind != NodeKind::Nested) { out << "namespace P4::IR {" << std::endl; @@ -418,7 +445,10 @@ void IrClass::generate_hdr(std::ostream &out) const { bool concreteParent = false; for (auto p : parentClasses) { - if (p->kind != NodeKind::Interface) concreteParent = true; + if (p->kind != NodeKind::Interface) { + BUG_CHECK(!concreteParent && p == this->concreteParent, "inconsisten concreteParent"); + concreteParent = true; + } } const char *sep = " : "; @@ -448,6 +478,17 @@ void IrClass::generate_hdr(std::ostream &out) const { out << indent << "IRNODE" << (kind == NodeKind::Abstract ? "_ABSTRACT" : "") << "_SUBCLASS(" << name << ")" << std::endl; + if (kind == NodeKind::Concrete || kind == NodeKind::Template) { + out << indent << "union COWref {\n"; + out << indent << IrElement::Private; + out << indent << indent << "COWNode_info *_info;\n"; + if (outputCOWfieldrefs(out) != IrElement::Public) + out << indent << IrElement::Public; + out << indent << indent << "COWref(COWNode_info *i) { _info = i; }\n"; + out << indent << indent << "COWref *operator->() { return this; }\n"; + out << indent << "};\n"; + } + auto *irNamespace = IrNamespace::get(nullptr, "IR"_cs); if (kind != NodeKind::Nested) { out << indent << "DECLARE_TYPEINFO_WITH_TYPEID(" << name diff --git a/tools/ir-generator/irclass.h b/tools/ir-generator/irclass.h index d7a316739aa..6a4baff9561 100644 --- a/tools/ir-generator/irclass.h +++ b/tools/ir-generator/irclass.h @@ -322,6 +322,7 @@ class IrClass : public IrElement { void generate_hdr(std::ostream &out) const override; void generate_impl(std::ostream &out) const override; void generateTreeMacro(std::ostream &out) const; + access_t outputCOWfieldrefs(std::ostream &out) const; void resolve() override; cstring toString() const override { return name; } std::string fullName() const;