From 970e7983d3788fa48c2a19c01487adf74e70e77e Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 3 Jun 2026 08:41:58 +0100 Subject: [PATCH] Optimize $sformat in V3Life (#7703) Treat AstSFormat as a special form of assignment in V3Life. This allows eliminating earlier redundant assignments to strings when an $sformat later sets the string. UVM has lot of these. --- src/V3Life.cpp | 55 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 52cca6bf3..77ac85c87 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -78,12 +78,14 @@ public: m_setBeforeUse = setBeforeUse; } - void simpleAssign(AstNodeAssign* nodep) { // New simple A=.... assignment + void simpleAssign(AstNodeStmt* nodep) { // New simple A=.... assignment UASSERT_OBJ(!m_isNew, nodep, "Uninitialized new entry"); m_assignp = nodep; m_constp = nullptr; m_everSet = true; - if (VN_IS(nodep->rhsp(), Const)) m_constp = VN_AS(nodep->rhsp(), Const); + if (AstNodeAssign* const assp = VN_CAST(nodep, Assign)) { + if (VN_IS(assp->rhsp(), Const)) m_constp = VN_AS(assp->rhsp(), Const); + } } void complexAssign() { // A[x]=... or some complicated assignment UASSERT(!m_isNew, "Uninitialized new entry"); @@ -144,7 +146,7 @@ public: ++m_statep->m_statAssnDel; VL_DO_DANGLING(m_deleter.pushDeletep(oldassp), oldassp); } - void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) { + void simpleAssign(AstVarScope* nodep, AstNodeStmt* assp) { // Do we have a old assignment we can nuke? UINFO(4, " ASSIGNof: " << nodep); UINFO(7, " new: " << assp); @@ -267,6 +269,29 @@ class LifeVisitor final : public VNVisitor { m_lifep->clear(); } + void processAssignment(AstNodeStmt* nodep, AstNodeExpr* lhsp, AstNodeExpr* rhsp) { + // Collect any used variables first, as lhs may also be on rhs + // Similar code in V3Dead + VL_RESTORER(m_sideEffect); + m_sideEffect = false; + m_lifep->clearReplaced(); + rhsp = VN_AS(iterateSubtreeReturnEdits(rhsp), NodeExpr); + if (m_lifep->replaced()) { + // We changed something, try to constant propagate, but don't delete the + // assignment as we still need nodep to remain. + V3Const::constifyEdit(rhsp); + VL_DANGLING(rhsp); // rhsp may change + } + // Has to be direct assignment without any EXTRACTing. + if (VN_IS(lhsp, VarRef) && !m_sideEffect && !m_noopt) { + AstVarScope* const vscp = VN_AS(lhsp, VarRef)->varScopep(); + UASSERT_OBJ(vscp, nodep, "Scope lost on variable"); + m_lifep->simpleAssign(vscp, nodep); + } else { + iterateAndNextNull(lhsp); + } + } + // VISITORS void visit(AstVarRef* nodep) override { // Consumption/generation of a variable, @@ -290,25 +315,11 @@ class LifeVisitor final : public VNVisitor { iterateChildren(nodep); return; } - // Collect any used variables first, as lhs may also be on rhs - // Similar code in V3Dead - VL_RESTORER(m_sideEffect); - m_sideEffect = false; - m_lifep->clearReplaced(); - iterateAndNextNull(nodep->rhsp()); - if (m_lifep->replaced()) { - // We changed something, try to constant propagate, but don't delete the - // assignment as we still need nodep to remain. - V3Const::constifyEdit(nodep->rhsp()); // rhsp may change - } - // Has to be direct assignment without any EXTRACTing. - if (VN_IS(nodep->lhsp(), VarRef) && !m_sideEffect && !m_noopt) { - AstVarScope* const vscp = VN_AS(nodep->lhsp(), VarRef)->varScopep(); - UASSERT_OBJ(vscp, nodep, "Scope lost on variable"); - m_lifep->simpleAssign(vscp, nodep); - } else { - iterateAndNextNull(nodep->lhsp()); - } + processAssignment(nodep, nodep->lhsp(), nodep->rhsp()); + } + void visit(AstSFormat* nodep) override { + // AstSFormat is just a special form of assignment + processAssignment(nodep, nodep->lhsp(), nodep->fmtp()); } void visit(AstAssignDly* nodep) override { // V3Life doesn't understand time sense