diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 747edbe11..9f858955f 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2721,9 +2721,11 @@ public: void dumpJson(std::ostream& str = std::cout) const override; }; class AstCoverCross final : public AstNodeFuncCovItem { - // @astgen op1 := itemsp : List[AstCoverpointRef] - // @astgen op2 := binsp : List[AstCoverCrossBins] - // @astgen op3 := optionsp : List[AstCoverOption] + // @astgen op1 := itemsp : List[AstCoverpointRef] + // @astgen op2 := binsp : List[AstCoverCrossBins] // post-LinkParse only + // @astgen op3 := optionsp : List[AstCoverOption] // post-LinkParse only + // @astgen op4 := rawBodyp : List[AstNode] // Parse: raw cross_body items; + // // post-LinkParse: empty public: AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp) : ASTGEN_SUPER_CoverCross(fl, name) { @@ -2740,9 +2742,12 @@ class AstCoverpoint final : public AstNodeFuncCovItem { // @astgen op3 := iffp : Optional[AstNodeExpr] // @astgen op4 := optionsp : List[AstCoverOption] public: - AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp) + AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp, + AstNodeExpr* iffp = nullptr, AstNode* binsp = nullptr) : ASTGEN_SUPER_Coverpoint(fl, name) { this->exprp(exprp); + this->iffp(iffp); + if (binsp) addBinsp(binsp); } ASTGEN_MEMBERS_AstCoverpoint; void dump(std::ostream& str) const override; diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 017e5d2ea..4d629bed0 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -1361,6 +1361,26 @@ class LinkParseVisitor final : public VNVisitor { iterateChildren(nodep); } + void visit(AstCoverCross* nodep) override { + cleanFileline(nodep); + // Distribute the parse-time raw cross_body list (rawBodyp, op4) into the + // typed binsp and optionsp slots. Nodes are properly in-tree here so + // unlinkFrBack() works cleanly with no bison-list hackery. + for (AstNode *itemp = nodep->rawBodyp(), *nextp; itemp; itemp = nextp) { + nextp = itemp->nextp(); + itemp->unlinkFrBack(); + if (AstCoverOption* const optp = VN_CAST(itemp, CoverOption)) { + nodep->addOptionsp(optp); + } else if (AstCoverCrossBins* const binp = VN_CAST(itemp, CoverCrossBins)) { + nodep->addBinsp(binp); + } else { + // AstCgOptionAssign, AstFunc, and other unsupported items + VL_DO_DANGLING(itemp->deleteTree(), itemp); + } + } + iterateChildren(nodep); + } + void visit(AstNode* nodep) override { // Default: Just iterate cleanFileline(nodep); diff --git a/src/verilog.y b/src/verilog.y index 8c031df45..4ac6ef605 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7000,46 +7000,25 @@ coverage_option: // ==IEEE: coverage_option cover_point: // ==IEEE: cover_point // // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT yCOVERPOINT expr iffE bins_or_empty - { AstCoverpoint* const cp = new AstCoverpoint{$1, "", $2}; - if ($3) cp->iffp($3); - if ($4) cp->addBinsp($4); - $$ = cp; } + { $$ = new AstCoverpoint{$1, "", $2, $3, $4}; } // // 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($5); - if ($6) cp->addBinsp($6); - $$ = cp; } + { $$ = new AstCoverpoint{$3, *$1, $4, $5, $6}; } // // 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($6); - if ($7) cp->addBinsp($7); - $$ = cp; + { $$ = new AstCoverpoint{$4, *$2, $5, $6, $7}; 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($7); - if ($8) cp->addBinsp($8); - $$ = cp; + { $$ = new AstCoverpoint{$5, *$3, $6, $7, $8}; 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($7); - if ($8) cp->addBinsp($8); - $$ = cp; + { $$ = new AstCoverpoint{$5, *$3, $6, $7, $8}; 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($7); - if ($8) cp->addBinsp($8); - $$ = cp; + { $$ = new AstCoverpoint{$5, *$3, $6, $7, $8}; 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($6); - if ($7) cp->addBinsp($7); - $$ = cp; } + { $$ = new AstCoverpoint{$4, *$2, $5, $6, $7}; } // // IEEE-2012: | bins_or_empty { $$ = $1; } ; @@ -7224,40 +7203,7 @@ cover_cross: // ==IEEE: cover_cross { AstCoverCross* const nodep = new AstCoverCross{$3, *$1, VN_AS($4, CoverpointRef)}; - if ($6) { // cross_body items (options, bins) - for (AstNode* itemp = $6; itemp; ) { - AstNode* const nextp = itemp->nextp(); - // Helper: unlink itemp from the standalone bison list. - // Head nodes have m_backp==nullptr; use nextp->unlinkFrBackWithNext() - // to detach the rest of the list so itemp->m_nextp becomes null. - const auto unlinkItem = [&]() { - if (itemp->backp()) { - itemp->unlinkFrBack(); - } else if (nextp) { - nextp->unlinkFrBackWithNext(); - } - }; - if (AstCoverOption* optp = VN_CAST(itemp, CoverOption)) { - unlinkItem(); - nodep->addOptionsp(optp); - } else if (AstCoverCrossBins* binp = VN_CAST(itemp, CoverCrossBins)) { - unlinkItem(); - nodep->addBinsp(binp); - } else if (VN_IS(itemp, CgOptionAssign)) { - unlinkItem(); - VL_DO_DANGLING(itemp->deleteTree(), itemp); - } else if (VN_IS(itemp, Func)) { - // Function declarations in cross bodies are unsupported - // Skip them - they will be deleted when bins expressions referencing - // them are deleted via DEL() in the cross_body_item rules - } else { - // Delete other unsupported items - unlinkItem(); - VL_DO_DANGLING(itemp->deleteTree(), itemp); - } - itemp = nextp; - } - } + if ($6) nodep->addRawBodyp($6); if ($5) { $5->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross"); VL_DO_DANGLING($5->deleteTree(), $5); @@ -7269,40 +7215,7 @@ cover_cross: // ==IEEE: cover_cross AstCoverCross* const nodep = new AstCoverCross{$1, "__cross" + cvtToStr(GRAMMARP->s_typeImpNum++), VN_AS($2, CoverpointRef)}; - if ($4) { // cross_body items (options, bins) - for (AstNode* itemp = $4; itemp; ) { - AstNode* const nextp = itemp->nextp(); - // Helper: unlink itemp from the standalone bison list. - // Head nodes have m_backp==nullptr; use nextp->unlinkFrBackWithNext() - // to detach the rest of the list so itemp->m_nextp becomes null. - const auto unlinkItem = [&]() { - if (itemp->backp()) { - itemp->unlinkFrBack(); - } else if (nextp) { - nextp->unlinkFrBackWithNext(); - } - }; - if (AstCoverOption* optp = VN_CAST(itemp, CoverOption)) { - unlinkItem(); - nodep->addOptionsp(optp); - } else if (AstCoverCrossBins* binp = VN_CAST(itemp, CoverCrossBins)) { - unlinkItem(); - nodep->addBinsp(binp); - } else if (VN_IS(itemp, CgOptionAssign)) { - unlinkItem(); - VL_DO_DANGLING(itemp->deleteTree(), itemp); - } else if (VN_IS(itemp, Func)) { - // Function declarations in cross bodies are unsupported - // Skip them - they will be deleted when bins expressions referencing - // them are deleted via DEL() in the cross_body_item rules - } else { - // Delete other unsupported items - unlinkItem(); - VL_DO_DANGLING(itemp->deleteTree(), itemp); - } - itemp = nextp; - } - } + if ($4) nodep->addRawBodyp($4); if ($3) { $3->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross"); VL_DO_DANGLING($3->deleteTree(), $3);