Support function calls with random arguments in constraints (#7061) (#7083)

This commit is contained in:
Yilou Wang
2026-02-19 15:37:55 +05:30
committed by GitHub
parent f843780aad
commit 0e26b049ea
6 changed files with 233 additions and 1 deletions
+104 -1
View File
@@ -40,6 +40,7 @@
#include "V3FileLine.h"
#include "V3Global.h"
#include "V3MemberMap.h"
#include "V3Task.h"
#include "V3UniqueNames.h"
#include <queue>
@@ -547,7 +548,16 @@ class RandomizeMarkVisitor final : public VNVisitor {
return;
}
if (nodep->name() != "randomize") return;
if (nodep->name() != "randomize") {
// Propagate user1 from children (same pattern as visit(AstNodeExpr*))
if (m_constraintExprGenp || m_inStdWith) {
nodep->user1((nodep->op1p() && nodep->op1p()->user1())
|| (nodep->op2p() && nodep->op2p()->user1())
|| (nodep->op3p() && nodep->op3p()->user1())
|| (nodep->op4p() && nodep->op4p()->user1()));
}
return;
}
AstClass* classp = m_classp;
if (const AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall)) {
if (const AstClassRefDType* const classRefp
@@ -862,6 +872,55 @@ class ConstraintExprVisitor final : public VNVisitor {
return selp;
}
// Extract return expression from a simple function body, or nullptr if too complex.
// Uses foreach to walk all assigns regardless of body organization (JumpBlock nesting etc.).
// Takes the last assignment to the return variable -- the first is the initializer.
AstNodeExpr* extractReturnExpr(AstFunc* funcp) {
AstVar* const retVarp = VN_CAST(funcp->fvarp(), Var);
if (!retVarp) return nullptr;
AstNodeExpr* retExprp = nullptr;
int assignCount = 0;
funcp->foreach([&](AstAssign* assignp) {
AstVarRef* const lhsp = VN_CAST(assignp->lhsp(), VarRef);
if (!lhsp || lhsp->varp() != retVarp) return;
retExprp = assignp->rhsp();
++assignCount;
});
// Simple function: initializer + return (2) or just return (1)
if (assignCount > 2 || !retExprp) return nullptr;
return retExprp;
}
// Bottom-up user1 propagation on inlined expression tree
void propagateUser1InlineRecurse(AstNodeExpr* nodep) {
if (VN_IS(nodep, NodeVarRef)) {
nodep->user1(VN_AS(nodep, NodeVarRef)->varp()->rand().isRandomizable());
return;
}
if (VN_IS(nodep, Const)) {
nodep->user1(false);
return;
}
bool anyChild = false;
if (AstNodeExpr* const cp = VN_CAST(nodep->op1p(), NodeExpr)) {
propagateUser1InlineRecurse(cp);
anyChild |= cp->user1();
}
if (AstNodeExpr* const cp = VN_CAST(nodep->op2p(), NodeExpr)) {
propagateUser1InlineRecurse(cp);
anyChild |= cp->user1();
}
if (AstNodeExpr* const cp = VN_CAST(nodep->op3p(), NodeExpr)) {
propagateUser1InlineRecurse(cp);
anyChild |= cp->user1();
}
if (AstNodeExpr* const cp = VN_CAST(nodep->op4p(), NodeExpr)) {
propagateUser1InlineRecurse(cp);
anyChild |= cp->user1();
}
nodep->user1(anyChild);
}
// VISITORS
void visit(AstNodeVarRef* nodep) override {
AstVar* varp = nodep->varp();
@@ -1892,6 +1951,50 @@ class ConstraintExprVisitor final : public VNVisitor {
if (editFormat(nodep)) return;
nodep->v3fatalSrc("Method not handled in constraints? " << nodep);
}
void visit(AstNodeFTaskRef* nodep) override {
if (editFormat(nodep)) return;
AstFunc* const funcp = VN_CAST(nodep->taskp(), Func);
if (!funcp) {
// Tasks have no return value and can't appear in expressions.
// Parser rejects this, so reaching here indicates a compiler bug.
nodep->v3fatalSrc("Unexpected task call in constraint expression");
return;
}
AstNodeExpr* const retExprp = extractReturnExpr(funcp);
if (!retExprp) {
nodep->v3warn(CONSTRAINTIGN,
"Unsupported: complex function in constraint, treating as state");
nodep->user1(false);
if (editFormat(nodep)) return;
return;
}
// Map formal parameters to actual arguments using V3Task infrastructure
const V3TaskConnects tconnects
= V3Task::taskConnects(nodep, funcp->stmtsp(), nullptr, false);
// Clone return expression, substitute params with args
AstNodeExpr* const inlinedp = retExprp->cloneTreePure(false);
for (const auto& tconnect : tconnects) {
const AstVar* const portp = tconnect.first;
AstArg* const argp = tconnect.second;
if (!argp || !argp->exprp()) continue;
inlinedp->foreach([&](AstVarRef* refp) {
if (refp->varp() == portp) {
refp->replaceWith(argp->exprp()->cloneTreePure(false));
VL_DO_DANGLING(refp->deleteTree(), refp);
}
});
}
inlinedp->dtypep(nodep->dtypep());
propagateUser1InlineRecurse(inlinedp);
nodep->replaceWith(inlinedp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
iterate(inlinedp);
}
void visit(AstNodeExpr* nodep) override {
if (editFormat(nodep)) return;
nodep->v3fatalSrc(