diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 3a47e0c5c..1b4151ec3 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2115,7 +2115,7 @@ public: void setIgnoreSchedWrite() { m_ignoreSchedWrite = true; } bool dfgMultidriven() const { return m_dfgMultidriven; } void setDfgMultidriven() { m_dfgMultidriven = true; } - void isGlobalConstrained(bool flag) { m_globalCons = true; } + void setGlobalConstrained(bool flag) { m_globalCons = flag; } // METHODS void name(const string& name) override { m_name = name; } void tag(const string& text) override { m_tag = text; } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 7dfb6ca44..cc98ee923 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -143,6 +143,7 @@ class RandomizeMarkVisitor final : public VNVisitor { bool m_globalConsProcessed = false; AstWith* m_astWithp = nullptr; std::vector m_clonedConstraints; + std::unordered_set m_processedClasses; // Constants for global constraint processing static constexpr const char* GLOBAL_CONSTRAINT_SEPARATOR = "__DT__"; @@ -207,11 +208,24 @@ class RandomizeMarkVisitor final : public VNVisitor { } } void nameManipulation(AstVarRef* fromp, AstConstraint* cloneCons) { + if (!fromp || !cloneCons) { + UINFO(9, "Invalid parameters in nameManipulation\n"); + return; + } // Iterate through all variable references and replace them with member selections. cloneCons->name(fromp->name() + GLOBAL_CONSTRAINT_SEPARATOR + cloneCons->name()); cloneCons->foreach([&](AstVarRef* varRefp) { + if (!varRefp || !varRefp->varp()) { + UINFO(9, "Invalid variable reference in constraint\n"); + return; + } + AstVarRef* clonedFromp = fromp->cloneTree(false); + if (!clonedFromp) { + UINFO(9, "Failed to clone variable reference in nameManipulation\n"); + return; + } AstMemberSel* varMemberp = new AstMemberSel{cloneCons->fileline(), - fromp->cloneTree(false), varRefp->varp()}; + clonedFromp, varRefp->varp()}; varMemberp->user2p(m_classp); varRefp->replaceWith(varMemberp); VL_DO_DANGLING(varRefp->deleteTree(), varRefp); @@ -223,6 +237,8 @@ class RandomizeMarkVisitor final : public VNVisitor { VL_RESTORER(m_modp); m_modp = m_classp = nodep; m_globalConsProcessed = false; + // Clear processed classes for each new class context + m_processedClasses.clear(); iterateChildrenConst(nodep); for (int i = 0; i < m_clonedConstraints.size(); i++) { m_classp->addStmtsp(m_clonedConstraints[i]); @@ -507,7 +523,8 @@ class RandomizeMarkVisitor final : public VNVisitor { m_classp->user1(IS_RANDOMIZED_WITH_GLOBAL_CONSTRAINTS); else if (m_classp->user1() == IS_RANDOMIZED_INLINE) m_classp->user1(IS_RANDOMIZED_INLINE_WITH_GLOBAL_CONSTRAINTS); - VN_AS(nodep->fromp(), VarRef)->varp()->isGlobalConstrained(true); // not needed + // Mark the object variable as participating in global constraints + VN_AS(nodep->fromp(), VarRef)->varp()->setGlobalConstrained(true); // Global constraint processing algorithm: // 1. Detect globally constrained object variables in randomized classes @@ -516,25 +533,40 @@ class RandomizeMarkVisitor final : public VNVisitor { // 4. Insert cloned constraints into current class for solver processing // 5. Use basic randomization for non-constrained variables to avoid recursion - if (!m_globalConsProcessed) { // Only process once per object's global constraints - // TODO: Should be a vector to check if each class is - // processed - if (nodep->user1() && VN_IS(nodep->fromp()->dtypep()->skipRefp(), ClassRefDType) - && VN_AS(nodep->fromp(), VarRef)->varp()->isGlobalConstrained()) { - AstClass* gConsClass - = VN_AS(nodep->fromp()->dtypep()->skipRefp(), ClassRefDType)->classp(); + // Extract and validate components early to avoid repeated type checks + AstVarRef* const varRefp = VN_CAST(nodep->fromp(), VarRef); + if (!varRefp || !varRefp->varp()) return; + + const AstClassRefDType* const classRefp = + VN_CAST(varRefp->dtypep()->skipRefp(), ClassRefDType); + if (!classRefp || !classRefp->classp()) return; + + if (nodep->user1() && varRefp->varp()->isGlobalConstrained()) { + AstClass* gConsClass = classRefp->classp(); + + // Use class-specific processing to allow multiple object processing + if (m_processedClasses.find(gConsClass) == m_processedClasses.end()) { gConsClass->foreachMember( [&](AstClass* const classp, AstConstraint* const constrp) { + if (!constrp) { + UINFO(9, "Null constraint found in class " << classp->name() << "\n"); + return; + } AstConstraint* cloneConstrp = constrp->cloneTree(false); + if (!cloneConstrp) { + UINFO(9, "Failed to clone constraint " << constrp->name() << "\n"); + return; + } // Name manipulation - nameManipulation(VN_AS(nodep->fromp(), VarRef), cloneConstrp); + nameManipulation(varRefp, cloneConstrp); m_clonedConstraints.push_back(cloneConstrp); }); + + // Mark this class as processed + m_processedClasses.insert(gConsClass); } } - - m_globalConsProcessed = true; } } void visit(AstNodeModule* nodep) override {