V3Covergroup: fix coverpoint cleanup for unsupported covergroups

Two bugs fixed:
1. AstCReset: mark as ineligible for coverage expressions via
   isExprCoverageEligible() override, preventing verilogForTree
   from being called on CReset nodes (which has no V3EmitV handler).

2. generateCrossCode: when a cross references an unknown coverpoint,
   don't delete the cross node early. The caller's cleanup loop
   (in visit(AstClass*)) is responsible for deleting all coverpoints
   and crosses. Early deletion left a dangling pointer in m_coverCrosses
   causing a use-after-free segfault.

3. hasUnsupportedEvent path: added coverpoint/cross cleanup before
   returning so AST nodes don't reach downstream passes (V3EmitCFunc,
   V3MergeCond) which no longer have stub visitors for them.

All 60 covergroup tests now pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Matthew Ballance 2026-02-28 20:34:14 +00:00
parent 14c67621ac
commit a5f10c9abf
2 changed files with 15 additions and 3 deletions

View File

@ -690,6 +690,7 @@ public:
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool isExprCoverageEligible() const override { return false; }
const char* broken() const override {
BROKEN_RTN(!VN_IS(backp(), NodeAssign)); // V3Emit* assumption
return nullptr;

View File

@ -1345,8 +1345,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
refp->v3warn(COVERIGN,
"Ignoring unsupported: cross references unknown coverpoint: "
+ refp->name());
// Delete the entire cross since we can't generate it
VL_DO_DANGLING(crossp->unlinkFrBack()->deleteTree(), crossp);
// Don't delete crossp here - the caller's cleanup loop will delete it
return;
}
@ -1830,7 +1829,19 @@ class FunctionalCoverageVisitor final : public VNVisitor {
}
// If covergroup has unsupported clocking event, skip processing it
if (hasUnsupportedEvent) return;
// but still clean up coverpoints so they don't reach downstream passes
if (hasUnsupportedEvent) {
iterateChildren(nodep);
for (AstCoverpoint* cpp : m_coverpoints) {
cpp->unlinkFrBack();
VL_DO_DANGLING(cpp->deleteTree(), cpp);
}
for (AstCoverCross* crossp : m_coverCrosses) {
crossp->unlinkFrBack();
VL_DO_DANGLING(crossp->deleteTree(), crossp);
}
return;
}
// Find the sample() method and constructor
m_sampleFuncp = VN_CAST(m_memberMap.findMember(nodep, "sample"), Func);