diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 4d33a0aa0..7d4a3842d 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -2352,7 +2352,7 @@ class AstSFormatF final : public AstNodeExpr { // @astgen op1 := exprsp : List[AstNodeExpr] // @astgen op2 := scopeNamep : Optional[AstScopeName] string m_text; - const bool m_hidden; // Under display, etc + bool m_hidden; // Under display, etc bool m_exprFormat = false; // Runtime Node* format, false = text() format code, false = possibly r bool m_optionalFormat @@ -2411,6 +2411,7 @@ public: return false; } bool hidden() const { return m_hidden; } + void hidden(bool flag) { m_hidden = flag; } bool exprFormat() const { return m_exprFormat; } void exprFormat(bool flag) { m_exprFormat = flag; } bool optionalFormat() const { return m_optionalFormat; } diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index e7214e3f2..336da7bae 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -1137,6 +1137,12 @@ public: fmtp(newp); this->lhsp(lhsp); } + AstSFormat(FileLine* fl, AstSFormatF* fmtp, AstNodeExpr* lhsp) + : ASTGEN_SUPER_SFormat(fl) { + this->fmtp(fmtp); + this->lhsp(lhsp); + fmtp->hidden(true); + } ASTGEN_MEMBERS_AstSFormat; const char* broken() const override { BROKEN_RTN(!fmtp()); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 43df3fac9..de2e1d64f 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2233,6 +2233,22 @@ class ConstVisitor final : public VNVisitor { return true; } + bool replaceAssignSFormatF(AstNodeAssign* nodep) { + // Rewrite 'x = sformatf(...)' into 'sformat(x, ...)', which is more efficient + // at the call site as it does not need to check if 'x' needs to be freed first. + // This is a somewhat common pattern after lowering assertions. + if (!VN_IS(nodep, Assign)) return false; + AstVarRef* const lhsp = VN_CAST(nodep->lhsp(), VarRef); + if (!lhsp) return false; + AstSFormatF* const fmtp = VN_CAST(nodep->rhsp(), SFormatF); + if (!fmtp) return false; + lhsp->unlinkFrBack(); + fmtp->unlinkFrBack(); + nodep->replaceWith(new AstSFormat{nodep->fileline(), fmtp, lhsp}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return true; + } + bool varNotReferenced(AstNode* nodep, AstVar* varp, int level = 0) { // Return true if varp never referenced under node. // Return false if referenced, or tree too deep to be worth it, or side effects @@ -2607,6 +2623,8 @@ class ConstVisitor final : public VNVisitor { } } else if (m_doV && replaceAssignMultiSel(nodep)) { return true; + } else if (replaceAssignSFormatF(nodep)) { + return true; } return false; } @@ -3822,6 +3840,25 @@ class ConstVisitor final : public VNVisitor { return; } } + void visit(AstSFormat* nodep) override { + iterateChildren(nodep); + if (!m_doNConst) return; + if (m_doNConst) { + // If it's a constant string written to a string variable, inline it as an assignment + AstSFormatF* const fmtp = nodep->fmtp(); + AstNodeExpr* const lhsp = nodep->lhsp(); + AstBasicDType* const basicp = VN_CAST(lhsp->dtypep()->skipRefp(), BasicDType); + if (basicp && basicp->isString() && !fmtp->exprsp() + && fmtp->text().find('%') == string::npos) { + AstConst* const strp + = new AstConst{fmtp->fileline(), AstConst::String{}, fmtp->text()}; + lhsp->unlinkFrBack(); + nodep->replaceWith(new AstAssign{nodep->fileline(), lhsp, strp}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + } + } void visit(AstNodeFTask* nodep) override { VL_RESTORER(m_underRecFunc); if (nodep->recursive()) m_underRecFunc = true;