diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 9b406f816..99f8096d0 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -999,43 +999,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig //====================================================================== // Debugging -void AstNode::checkTreeIter(AstNode* backp) { +void AstNode::checkTreeIter(const AstNode* backp) const { // private: Check a tree and children UASSERT_OBJ(backp == this->backp(), this, "Back node inconsistent"); - if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) { - // Termops have a short-circuited iterateChildren, so check usage - UASSERT_OBJ(!(op1p() || op2p() || op3p() || op4p()), this, - "Terminal operation with non-terminals"); - } - if (m_op1p) m_op1p->checkTreeIterList(this); - if (m_op2p) m_op2p->checkTreeIterList(this); - if (m_op3p) m_op3p->checkTreeIterList(this); - if (m_op4p) m_op4p->checkTreeIterList(this); -} - -void AstNode::checkTreeIterList(AstNode* backp) { - // private: Check a (possible) list of nodes, this is always the head of the list - // Audited to make sure this is never nullptr - AstNode* const headp = this; - const AstNode* tailp = this; - for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) { - nodep->checkTreeIter(backp); - UASSERT_OBJ(headp == this || !nextp(), this, - "Headtailp should be null in middle of lists"); - tailp = nodep; - backp = nodep; - } - UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent"); - UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent"); -} - -void AstNode::checkTree() { - if (!debug()) return; - if (this->backp()) { - // Linked tree- check only the passed node - this->checkTreeIter(this->backp()); - } else { - this->checkTreeIterList(this->backp()); + switch (this->type()) { +#include "V3Ast__gen_op_checks.h" + default: VL_UNREACHABLE; // LCOV_EXCL_LINE } } diff --git a/src/V3Ast.h b/src/V3Ast.h index 87349cc68..a945ea6a2 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1470,8 +1470,7 @@ class AstNode VL_NOT_FINAL { private: AstNode* cloneTreeIter(); AstNode* cloneTreeIterList(); - void checkTreeIter(AstNode* backp); - void checkTreeIterList(AstNode* backp); + void checkTreeIter(const AstNode* backp) const; bool gateTreeIter() const; static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly); @@ -1826,7 +1825,9 @@ public: // Does tree of this == node2p?, not allowing non-isGateOptimizable inline bool sameGateTree(const AstNode* node2p) const; void deleteTree(); // Always deletes the next link - void checkTree(); // User Interface version + void checkTree() const { + if (v3Global.opt.debugCheck()) checkTreeIter(backp()); + } void checkIter() const; void dumpPtrs(std::ostream& os = std::cout) const; void dumpTree(std::ostream& os = std::cout, const string& indent = " ", diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 107579b69..198ed0ec5 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -101,10 +101,10 @@ AstPin::AstPin(FileLine* fl, int pinNum, AstVarRef* varname, AstNode* exprp) AstDpiExportUpdated::AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep) : ASTGEN_SUPER_DpiExportUpdated(fl) { - addOp1p(new AstVarRef{fl, varScopep, VAccess::WRITE}); + this->varRefp(new AstVarRef{fl, varScopep, VAccess::WRITE}); } -AstVarScope* AstDpiExportUpdated::varScopep() const { return VN_AS(op1p(), VarRef)->varScopep(); } +AstVarScope* AstDpiExportUpdated::varScopep() const { return varRefp()->varScopep(); } AstPackArrayDType::AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep) diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index c1af84cc3..4da216427 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -281,7 +281,7 @@ public: class AstAssocArrayDType final : public AstNodeDType { // Associative array data type, ie "[some_dtype]" // @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width - // @astgen op2 := keyChildDTypep : AstNodeDType // the key, which remains here as a pointer + // @astgen op2 := keyChildDTypep : Optional[AstNodeDType] private: AstNodeDType* m_refDTypep; // Elements of this type (after widthing) AstNodeDType* m_keyDTypep; // Keys of this type (after widthing) @@ -996,7 +996,7 @@ public: bool isCompound() const override { return true; } }; class AstRefDType final : public AstNodeDType { - // @astgen op1 := typeofp : AstNode + // @astgen op1 := typeofp : Optional[AstNode] // @astgen op2 := classOrPackageOpp : Optional[AstNode] // @astgen op3 := paramsp : List[AstPin] private: diff --git a/src/V3AstNodeMath.h b/src/V3AstNodeMath.h index 2d5a6f57f..977a49b96 100644 --- a/src/V3AstNodeMath.h +++ b/src/V3AstNodeMath.h @@ -187,7 +187,6 @@ protected: public: ASTGEN_MEMBERS_NodeTermop; // Know no children, and hot function, so skip iterator for speed - // See checkTreeIter also that asserts no children // cppcheck-suppress functionConst void iterateChildren(VNVisitor& v) {} void dump(std::ostream& str) const override; @@ -339,7 +338,6 @@ public: AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } // Know no children, and hot function, so skip iterator for speed - // See checkTreeIter also that asserts no children // cppcheck-suppress functionConst void iterateChildren(VNVisitor& v) {} }; @@ -825,7 +823,7 @@ class AstImplication final : public AstNodeMath { // Verilog |-> |=> // @astgen op1 := lhsp : AstNode // @astgen op2 := rhsp : AstNode - // @astgen op3 := sentreep : AstSenTree + // @astgen op3 := sentreep : Optional[AstSenTree] public: AstImplication(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : ASTGEN_SUPER_Implication(fl) { diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 12b832507..c386496ed 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -429,7 +429,7 @@ public: }; class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeStmt { // A reference to a task (or function) - // @astgen op1 := namep : AstNode + // @astgen op1 := namep : Optional[AstNode] // op2 used by some sub-types only // @astgen op3 := pinsp : List[AstNode] // @astgen op4 := scopeNamep : Optional[AstScopeName] @@ -1728,7 +1728,7 @@ public: }; class AstSenItem final : public AstNode { // Parents: SENTREE - // @astgen op1 := sensp : AstNode // Sensitivity expression + // @astgen op1 := sensp : Optional[AstNode] // Sensitivity expression VEdgeType m_edgeType; // Edge type public: class Combo {}; // for creator type-overload selection @@ -3077,6 +3077,7 @@ public: }; class AstDpiExportUpdated final : public AstNodeStmt { // Denotes that the referenced variable may have been updated via a DPI Export + // @astgen op1 := varRefp : AstVarRef public: inline AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep); ASTGEN_MEMBERS_DpiExportUpdated; @@ -3496,7 +3497,8 @@ class AstTraceDecl final : public AstNodeStmt { // Trace point declaration // Separate from AstTraceInc; as a declaration can't be deleted // Parents: {statement list} - // @astgen op1 := valuep : AstNode // Expressio being traced + // Expression being traced - Moved to AstTraceInc by V3Trace + // @astgen op1 := valuep : Optional[AstNode] private: uint32_t m_code = 0; // Trace identifier code; converted to ASCII by trace routines const string m_showname; // Name of variable diff --git a/src/astgen b/src/astgen index 56956768d..7b95c081b 100755 --- a/src/astgen +++ b/src/astgen @@ -904,6 +904,84 @@ def write_macros(filename): fh.write("\n") +def write_op_checks(filename): + with open_file(filename) as fh: + + indent = "" + + def emitBlock(pattern, **fmt): + fh.write(textwrap.indent(textwrap.dedent(pattern), indent).format(**fmt)) + + for node in SortedNodes: + if not node.isLeaf: + continue + + emitBlock('''\ + case VNType::at{nodeName}: {{ + const Ast{nodeName}* const currp = static_cast(this); + ''', + nodeName=node.name) + indent = " " + for n in range(1, 5): + op = node.getOp(n) + emitBlock("// Checking op{n}p\n",n=n) + if op: + name, monad, kind = op + if not monad: + emitBlock('''\ + UASSERT_OBJ(currp->{opName}(), currp, "Ast{nodeName} must have non nullptr {opName}()"); + UASSERT_OBJ(!currp->{opName}()->nextp(), currp, "Ast{nodeName}::{opName}() cannot have a non nullptr nextp()"); + currp->{opName}()->checkTreeIter(currp); + ''', + n=n, + nodeName=node.name, + opName=name) + elif monad == "Optional": + emitBlock('''\ + if (Ast{kind}* const opp = currp->{opName}()) {{ + UASSERT_OBJ(!currp->{opName}()->nextp(), currp, "Ast{nodeName}::{opName}() cannot have a non nullptr nextp()"); + opp->checkTreeIter(currp); + }} + ''', + n=n, + nodeName=node.name, + opName=name, + kind=kind) + elif monad == "List": + emitBlock('''\ + if (const Ast{kind}* const headp = currp->{opName}()) {{ + const AstNode* backp = currp; + const Ast{kind}* tailp = headp; + const Ast{kind}* opp = headp; + do {{ + opp->checkTreeIter(backp); + UASSERT_OBJ(opp == headp || !opp->nextp() || !opp->m_headtailp, opp, "Headtailp should be null in middle of lists"); + backp = tailp = opp; + opp = {next}; + }} while (opp); + UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent"); + UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent"); + }} + ''', + n=n, + nodeName=node.name, + opName=name, + kind=kind, +next = "VN_AS(opp->nextp(), {kind})".format(kind=kind) if kind != "Node" else "opp->nextp()" +) + else: + sys.exit("Unknown operand type") + else: + emitBlock('''\ + UASSERT_OBJ(!currp->op{n}p(), currp, "Ast{nodeName} does not use op{n}p()"); + ''', n=n, nodeName=node.name) + indent = "" + emitBlock('''\ + break; + }} + ''') + + ###################################################################### # main @@ -1005,6 +1083,7 @@ if Args.classes: write_types("V3Ast__gen_types.h") write_yystype("V3Ast__gen_yystype.h") write_macros("V3Ast__gen_macros.h") + write_op_checks("V3Ast__gen_op_checks.h") for cpt in Args.infiles: if not re.search(r'.cpp$', cpt):