Fix side effects when using select (#6460)

This commit is contained in:
Igor Zaworski
2025-09-29 08:20:54 -04:00
committed by GitHub
parent bdae48f6ae
commit 83f4db956b
5 changed files with 130 additions and 40 deletions
+76 -23
View File
@@ -57,6 +57,7 @@ class UnknownVisitor final : public VNVisitor {
// STATE - for current visit position (use VL_RESTORER)
AstNodeModule* m_modp = nullptr; // Current module
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstAssignW* m_assignwp = nullptr; // Current assignment
AstAssignDly* m_assigndlyp = nullptr; // Current assignment
AstNode* m_timingControlp = nullptr; // Current assignment's intra timing control
@@ -140,6 +141,46 @@ class UnknownVisitor final : public VNVisitor {
}
}
AstVar* createAddTemp(const AstNodeExpr* const nodep) {
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::XTEMP,
m_xrandNames->get(nodep), nodep->dtypep()};
if (m_ftaskp) {
varp->funcLocal(true);
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
} else {
m_modp->stmtsp()->addHereThisAsNext(varp);
}
return varp;
}
// Returns true if it is known at compile time that `msbConstp` is greater than or equal
// `exprp`
static bool isStaticlyGte(AstConst* const msbConstp, const AstNodeExpr* const exprp) {
if (msbConstp->width() >= exprp->width()
&& msbConstp->num().toSInt() >= (1 << exprp->width()) - 1) {
return true;
}
if (const AstConst* const constp = VN_CAST(exprp, Const)) {
if (V3Number{msbConstp}.opGte(msbConstp->num(), constp->num()).isNeqZero()) {
return true;
}
}
return false;
}
AstNodeExpr* newExprStmtOrClone(AstNodeExpr*& exprp) {
if (!exprp->isPure()) {
AstVar* const varp = createAddTemp(exprp);
FileLine* const fl = exprp->fileline();
exprp = new AstExprStmt{
fl, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, exprp},
new AstVarRef{fl, varp, VAccess::READ}};
return new AstVarRef{fl, varp, VAccess::READ};
}
return exprp->cloneTreePure(false);
}
// VISITORS
void visit(AstNodeModule* nodep) override {
UINFO(4, " MOD " << nodep);
@@ -158,6 +199,11 @@ class UnknownVisitor final : public VNVisitor {
xrandNames.swap(m_xrandNames);
}
}
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_ftaskp);
m_ftaskp = nodep;
iterateChildren(nodep);
}
void visit(AstAssignDly* nodep) override {
VL_RESTORER(m_assigndlyp);
VL_RESTORER(m_timingControlp);
@@ -387,22 +433,25 @@ class UnknownVisitor final : public VNVisitor {
}
// Find range of dtype we are selecting from
// Similar code in V3Const::warnSelect
const int maxmsb = nodep->fromp()->dtypep()->width() - 1;
const uint32_t maxmsb = nodep->fromp()->dtypep()->width() - 1;
UINFOTREE(9, nodep, "", "sel_old");
// If (maxmsb >= selected), we're in bound
AstNodeExpr* condp
= new AstGte{nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::WidthedValue{},
nodep->lsbp()->width(), maxmsb),
nodep->lsbp()->cloneTreePure(false)};
// See if the condition is constant true (e.g. always in bound due to constant select)
// Note below has null backp(); the Edit function knows how to deal with that.
condp = V3Const::constifyEdit(condp);
if (condp->isOne()) {
AstConst* const maxmsbConstp = new AstConst{
nodep->fileline(), AstConst::WidthedValue{}, nodep->lsbp()->width(), maxmsb};
AstNodeExpr* lsbp = V3Const::constifyEdit(nodep->lsbp()->unlinkFrBack());
if (isStaticlyGte(maxmsbConstp, lsbp)) {
// We don't need to add a conditional; we know the existing expression is ok
VL_DO_DANGLING(condp->deleteTree(), condp);
} else if (!lvalue) {
VL_DO_DANGLING(maxmsbConstp->deleteTree(), maxmsbConstp);
nodep->lsbp(lsbp);
return;
}
nodep->lsbp(newExprStmtOrClone(lsbp));
AstNodeExpr* condp
= V3Const::constifyEdit(new AstGte{nodep->fileline(), maxmsbConstp, lsbp});
if (!lvalue) {
// SEL(...) -> COND(LTE(bit<=maxmsb), ARRAYSEL(...), {width{1'bx}})
VNRelinker replaceHandle;
nodep->unlinkFrBack(&replaceHandle);
@@ -466,21 +515,25 @@ class UnknownVisitor final : public VNVisitor {
}
}
}
// See if the condition is constant true
AstNodeExpr* condp
= new AstGte{nodep->fileline(),
new AstConst(nodep->fileline(), AstConst::WidthedValue{},
nodep->bitp()->width(), declElements - 1),
nodep->bitp()->cloneTreePure(false)};
// Note below has null backp(); the Edit function knows how to deal with that.
condp = V3Const::constifyEdit(condp);
const AstNodeDType* const nodeDtp = nodep->dtypep()->skipRefp();
if (condp->isOne()) {
AstConst* const declElementsp
= new AstConst{nodep->fileline(), AstConst::WidthedValue{}, nodep->bitp()->width(),
static_cast<uint32_t>(declElements - 1)};
AstNodeExpr* bitp = V3Const::constifyEdit(nodep->bitp()->unlinkFrBack());
if (isStaticlyGte(declElementsp, bitp)) {
// We don't need to add a conditional; we know the existing expression is ok
VL_DO_DANGLING(condp->deleteTree(), condp);
} else if (!lvalue
// Making a scalar would break if we're making an array
&& VN_IS(nodeDtp, BasicDType)) {
VL_DO_DANGLING(declElementsp->deleteTree(), declElementsp);
nodep->bitp(bitp);
return;
}
nodep->bitp(newExprStmtOrClone(bitp));
AstNodeExpr* condp = new AstGte{nodep->fileline(), declElementsp, bitp};
// Note below has null backp(); the Edit function knows how to deal with that.
const AstNodeDType* const nodeDtp = nodep->dtypep()->skipRefp();
if (!lvalue
// Making a scalar would break if we're making an array
&& VN_IS(nodeDtp, BasicDType)) {
// ARRAYSEL(...) -> COND(LT(bit<maxbit), ARRAYSEL(...), {width{1'bx}})
VNRelinker replaceHandle;
nodep->unlinkFrBack(&replaceHandle);