diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 59d2917c8..d4ffd9188 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2737,7 +2737,7 @@ public: }; class AstCoverpoint final : public AstNodeFuncCovItem { // @astgen op1 := exprp : AstNodeExpr - // @astgen op2 := binsp : List[AstCoverBin] + // @astgen op2 := binsp : List[AstNode] // Parse: mixed AstCoverBin/AstCgOptionAssign; post-LinkParse: AstCoverBin only // @astgen op3 := iffp : Optional[AstNodeExpr] // @astgen op4 := optionsp : List[AstCoverOption] public: diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index dd3e7d753..c6b2a65f1 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -227,12 +227,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { iterateAndNextConstNull(nodep->rhsp()); if (!m_suppressSemi) puts(";\n"); } - void visit(AstAssignDly* nodep) override { - iterateAndNextConstNull(nodep->lhsp()); - putfs(nodep, " <= "); - iterateAndNextConstNull(nodep->rhsp()); - puts(";\n"); - } void visit(AstAlias* nodep) override { putbs("alias "); iterateConst(nodep->itemsp()); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index c1b5e473a..3014b04a2 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -1327,6 +1327,40 @@ class LinkParseVisitor final : public VNVisitor { iterate(cgClassp); } + void visit(AstCoverpoint* nodep) override { + cleanFileline(nodep); + // Re-sort the parse-time mixed bins list (AstCoverBin + AstCgOptionAssign) + // into the typed binsp and optionsp slots. The grammar attaches both node types + // to binsp (op2) as a raw List[AstNode]; now that they are properly parented we + // can iterate and split them without any temporary-parent tricks. + for (AstNode *itemp = nodep->binsp(), *nextp; itemp; itemp = nextp) { + nextp = itemp->nextp(); + if (AstCgOptionAssign* const optp = VN_CAST(itemp, CgOptionAssign)) { + optp->unlinkFrBack(); + VCoverOptionType optType = VCoverOptionType::COMMENT; + if (optp->name() == "at_least") { + optType = VCoverOptionType::AT_LEAST; + } else if (optp->name() == "weight") { + optType = VCoverOptionType::WEIGHT; + } else if (optp->name() == "goal") { + optType = VCoverOptionType::GOAL; + } else if (optp->name() == "auto_bin_max") { + optType = VCoverOptionType::AUTO_BIN_MAX; + } else if (optp->name() == "per_instance") { + optType = VCoverOptionType::PER_INSTANCE; + } else if (optp->name() == "comment") { + optType = VCoverOptionType::COMMENT; + } else { + optp->v3warn(COVERIGN, "Ignoring unsupported coverage option: " + optp->name()); + } + nodep->addOptionsp( + new AstCoverOption{optp->fileline(), optType, optp->valuep()->cloneTree(false)}); + VL_DO_DANGLING(optp->deleteTree(), optp); + } + } + iterateChildren(nodep); + } + void visit(AstNode* nodep) override { // Default: Just iterate cleanFileline(nodep); diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index 7e6d7abba..7cadd2487 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -295,6 +295,8 @@ class CodeMotionAnalysisVisitor final : public VNVisitorConst { iterateChildrenConst(nodep); } + // VISITORS + void visit(AstNode* nodep) override { // Push a new stack entry at the start of a list, but only if the list is not a // single element (this saves a lot of allocations in expressions) diff --git a/src/V3ParseGrammar.h b/src/V3ParseGrammar.h index f3f6ea41b..7ec327170 100644 --- a/src/V3ParseGrammar.h +++ b/src/V3ParseGrammar.h @@ -96,66 +96,6 @@ public: nodep->trace(singletonp()->allTracingOn(fileline)); return nodep; } - // Helper to move bins from parser list to coverpoint - void addCoverpointBins(AstCoverpoint* cp, AstNode* binsList) { - if (!binsList) return; - - // CRITICAL FIX: The parser creates a linked list of bins. When we try to move them - // to the coverpoint one by one while they're still linked, the addNext() logic - // that updates headtailp pointers creates circular references. We must fully - // unlink ALL bins before adding ANY to the coverpoint. - std::vector bins; - std::vector options; - - // To unlink the head node (which has no backp), create a temporary parent - AstBegin* tempParent = new AstBegin{binsList->fileline(), "[TEMP]", nullptr, true}; - tempParent->addStmtsp(binsList); // Now binsList has a backp - - // Now unlink all bins - they all have backp now - for (AstNode *binp = binsList, *nextp; binp; binp = nextp) { - nextp = binp->nextp(); - - if (AstCoverBin* cbinp = VN_CAST(binp, CoverBin)) { - cbinp->unlinkFrBack(); // Now this works for all bins including head - bins.push_back(cbinp); - } else if (AstCgOptionAssign* optp = VN_CAST(binp, CgOptionAssign)) { - optp->unlinkFrBack(); - // Convert AstCgOptionAssign to AstCoverOption - VCoverOptionType optType = VCoverOptionType::COMMENT; // default - if (optp->name() == "at_least") { - optType = VCoverOptionType::AT_LEAST; - } else if (optp->name() == "weight") { - optType = VCoverOptionType::WEIGHT; - } else if (optp->name() == "goal") { - optType = VCoverOptionType::GOAL; - } else if (optp->name() == "auto_bin_max") { - optType = VCoverOptionType::AUTO_BIN_MAX; - } else if (optp->name() == "per_instance") { - optType = VCoverOptionType::PER_INSTANCE; - } else if (optp->name() == "comment") { - optType = VCoverOptionType::COMMENT; - } else { - optp->v3warn(COVERIGN, - "Ignoring unsupported coverage option: " + optp->name()); - } - AstCoverOption* coverOptp = new AstCoverOption{optp->fileline(), optType, - optp->valuep()->cloneTree(false)}; - options.push_back(coverOptp); - VL_DO_DANGLING(optp->deleteTree(), optp); - } else { - binp->v3warn(COVERIGN, - "Unexpected node in bins list, ignoring"); // LCOV_EXCL_LINE - VL_DO_DANGLING(binp->deleteTree(), binp); - } - } - - // Delete the temporary parent - VL_DO_DANGLING(tempParent->deleteTree(), tempParent); - - // Now add standalone bins and options to coverpoint - for (AstCoverBin* cbinp : bins) { cp->addBinsp(cbinp); } - for (AstCoverOption* optp : options) { cp->addOptionsp(optp); } - } AstDisplay* createDisplayError(FileLine* fileline) { AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr}; AstNode::addNext(nodep, new AstStop{fileline, false}); diff --git a/src/verilog.y b/src/verilog.y index 79bf8181a..16e1da99f 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7001,43 +7001,43 @@ cover_point: // ==IEEE: cover_point yCOVERPOINT expr iffE bins_or_empty { AstCoverpoint* const cp = new AstCoverpoint{$1, "", $2}; if ($3) cp->iffp(VN_AS($3, NodeExpr)); - GRAMMARP->addCoverpointBins(cp, $4); + if ($4) cp->addBinsp($4); $$ = cp; } // // IEEE-2012: class_scope before an ID | id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty { AstCoverpoint* const cp = new AstCoverpoint{$3, *$1, $4}; if ($5) cp->iffp(VN_AS($5, NodeExpr)); - GRAMMARP->addCoverpointBins(cp, $6); + if ($6) cp->addBinsp($6); $$ = cp; } // // data_type_or_implicit expansion | data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty { AstCoverpoint* const cp = new AstCoverpoint{$4, *$2, $5}; if ($6) cp->iffp(VN_AS($6, NodeExpr)); - GRAMMARP->addCoverpointBins(cp, $7); + if ($7) cp->addBinsp($7); $$ = cp; DEL($1); } | yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty { AstCoverpoint* const cp = new AstCoverpoint{$5, *$3, $6}; if ($7) cp->iffp(VN_AS($7, NodeExpr)); - GRAMMARP->addCoverpointBins(cp, $8); + if ($8) cp->addBinsp($8); $$ = cp; DEL($2); } | yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty { AstCoverpoint* const cp = new AstCoverpoint{$5, *$3, $6}; if ($7) cp->iffp(VN_AS($7, NodeExpr)); - GRAMMARP->addCoverpointBins(cp, $8); + if ($8) cp->addBinsp($8); $$ = cp; DEL($2); } | signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty { AstCoverpoint* const cp = new AstCoverpoint{$5, *$3, $6}; if ($7) cp->iffp(VN_AS($7, NodeExpr)); - GRAMMARP->addCoverpointBins(cp, $8); + if ($8) cp->addBinsp($8); $$ = cp; DEL($2); } | signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty { AstCoverpoint* const cp = new AstCoverpoint{$4, *$2, $5}; if ($6) cp->iffp(VN_AS($6, NodeExpr)); - GRAMMARP->addCoverpointBins(cp, $7); + if ($7) cp->addBinsp($7); $$ = cp; } // // IEEE-2012: | bins_or_empty { $$ = $1; }