From ece4d71e5b1d72392d18d2c1046a75cb9d1e9f96 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 8 Jun 2026 17:01:50 +0100 Subject: [PATCH] Optimize CReset in Dfg (#7737) Teach DFG about CReset. This is not so much to optimize CReset itself, but to enable synthesizing logic involving CReset, which does appear with automatic variables used only in certain branches --- src/V3Dfg.cpp | 18 +++++++++++++++--- src/V3DfgColorSCCs.cpp | 6 ++++++ src/V3DfgCse.cpp | 6 ++++++ src/V3DfgDfgToAst.cpp | 7 +++++++ src/V3DfgPasses.cpp | 2 ++ src/V3DfgPeephole.cpp | 2 +- src/V3DfgPushDownSels.cpp | 3 +++ src/V3DfgRegularize.cpp | 3 +++ src/V3DfgSynthesize.cpp | 18 ++++++++++++++++++ src/V3DfgVertices.h | 8 ++++++++ test_regress/t/t_dfg_synthesis.v | 7 +++++++ 11 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index d51250bc3..273d1dcff 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -91,6 +91,11 @@ std::unique_ptr DfgGraph::clone() const { for (const DfgVertex& vtx : m_opVertices) { switch (vtx.type()) { #include "V3Dfg__gen_clone_cases.h" // From ./astgen + case VDfgType::CReset: { // LCOV_EXCL_START - No algorithm actually hits this today + DfgCReset* const cp = new DfgCReset{*clonep, vtx.fileline(), vtx.dtype()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } // LCOV_EXCL_STOP case VDfgType::Sel: { DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtype()}; cp->lsb(vtx.as()->lsb()); @@ -132,11 +137,14 @@ std::unique_ptr DfgGraph::clone() const { VL_UNREACHABLE; break; } - default: { - vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName()); + case VDfgType::AstRd: // LCOV_EXCL_START + case VDfgType::Const: + case VDfgType::VarArray: + case VDfgType::VarPacked: { + vtx.v3fatalSrc("Vertex should have been handled above: " + vtx.typeName()); VL_UNREACHABLE; break; - } + } // LCOV_EXCL_STOP } } UASSERT(size() == clonep->size(), "Size of clone should be the same"); @@ -618,6 +626,10 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const { CHECK(isPacked(), "Should be Packed type"); return; } + case VDfgType::CReset: { + CHECK(isPacked() || isArray(), "Should be Packed or Array type"); + return; + } case VDfgType::AstRd: { const DfgAstRd& v = *as(); CHECK(v.isPacked() || v.isArray(), "Should be Packed or Array type"); diff --git a/src/V3DfgColorSCCs.cpp b/src/V3DfgColorSCCs.cpp index 6bdce8ffb..1986a8775 100644 --- a/src/V3DfgColorSCCs.cpp +++ b/src/V3DfgColorSCCs.cpp @@ -125,6 +125,12 @@ class ColorStronglyConnectedComponents final { // Initialize state of operation vertices for (const DfgVertex& vtx : m_dfg.opVertices()) { + // If it has no inputs or no outputs, it cannot be part of a non-trivial SCC. + if (!vtx.nInputs() || !vtx.hasSinks()) { + index(vtx) = 0; + component(vtx) = 0; + continue; + } index(vtx) = UNASSIGNED; component(vtx) = UNASSIGNED; } diff --git a/src/V3DfgCse.cpp b/src/V3DfgCse.cpp index 40aa85190..7a60b4be7 100644 --- a/src/V3DfgCse.cpp +++ b/src/V3DfgCse.cpp @@ -52,6 +52,7 @@ class V3DfgCse final { // Special vertices case VDfgType::Const: // LCOV_EXCL_START + case VDfgType::CReset: case VDfgType::VarArray: case VDfgType::VarPacked: case VDfgType::AstRd: // LCOV_EXCL_STOP @@ -170,6 +171,7 @@ class V3DfgCse final { // Special vertices case VDfgType::Const: return a.as()->num().isCaseEq(b.as()->num()); + case VDfgType::CReset: return false; case VDfgType::VarArray: case VDfgType::VarPacked: // CSE does not combine variables @@ -303,6 +305,10 @@ class V3DfgCse final { for (const DfgVertexVar& vtx : dfg.varVertices()) m_hashCache[vtx] = V3Hash{++varHash}; // Pre-hash Ast references, these are all unique like variables for (const DfgVertexAst& vtx : dfg.astVertices()) m_hashCache[vtx] = V3Hash{++varHash}; + // Pre-hash CReset vertices, these are all unique + for (const DfgVertex& vtx : dfg.opVertices()) { + if (vtx.is()) m_hashCache[vtx] = V3Hash{++varHash}; + } // Similarly pre-hash constants for speed. While we don't combine constants, we do want // expressions using the same constants to be combined, so we do need to hash equal diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 6da7bfc8d..ca349ccb3 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -227,6 +227,13 @@ class DfgToAstVisitor final : DfgVisitor { void visit(DfgConst* vtxp) override { // m_resultp = new AstConst{vtxp->fileline(), vtxp->num()}; } + void visit(DfgCReset* vtxp) override { + DfgVertex* const sinkp = vtxp->singleSink(); + UASSERT_OBJ(sinkp, vtxp, "CReset should only have one sink"); + UASSERT_OBJ(sinkp->is(), sinkp, "CReset should drive a variable"); + AstVar* const varp = sinkp->as()->vscp()->varp(); + m_resultp = new AstCReset{vtxp->fileline(), varp, false}; + } void visit(DfgRep* vtxp) override { FileLine* const flp = vtxp->fileline(); diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index eaad8872e..3e14397c6 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -120,6 +120,8 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) { // Partial driver cannot be inlined if (srcp->is()) continue; if (srcp->is()) continue; + // Don't inline CReset + if (srcp->is()) continue; // Okie dokie, here we go ... vtx.replaceWith(srcp); } diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 8181dcfab..f89285eb7 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1158,7 +1158,7 @@ class V3DfgPeephole final : public DfgVisitor { // Sel from a partial variable (including narrowed vertex) if (DfgVarPacked* const varp = fromp->cast()) { - if (varp->srcp() && !varp->isVolatile()) { + if (varp->srcp() && !varp->isVolatile() && !varp->srcp()->is()) { // Must be a splice, otherwise it would have been inlined DfgSplicePacked* splicep = varp->srcp()->as(); DfgVertex* driverp = nullptr; diff --git a/src/V3DfgPushDownSels.cpp b/src/V3DfgPushDownSels.cpp index 658be1b34..71b2bfaca 100644 --- a/src/V3DfgPushDownSels.cpp +++ b/src/V3DfgPushDownSels.cpp @@ -89,6 +89,9 @@ class V3DfgPushDownSels final { m_stack.push_back(&vtx); } for (DfgConst& vtx : m_dfg.constVertices()) m_stack.push_back(&vtx); + for (DfgVertex& vtx : m_dfg.opVertices()) { + if (!vtx.nInputs()) m_stack.push_back(&vtx); + } // Reverse post order number to assign to next vertex uint32_t rpoNext = m_dfg.size(); diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index 588dbd52c..f8e7b7935 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -100,6 +100,9 @@ class DfgRegularize final { // No need to add a temporary if the single sink is a variable already if (sink.is()) return false; + // CReset always needs to be driving a variable + if (aVtx.is()) return true; + // Do not inline expressions into a loop body if (const DfgAstRd* const astRdp = sink.cast()) { return astRdp->inLoop(); } diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp index 827bede0e..706c9c0c9 100644 --- a/src/V3DfgSynthesize.cpp +++ b/src/V3DfgSynthesize.cpp @@ -384,6 +384,24 @@ class AstToDfgConverter final : public VNVisitor { nodep->user2p(vtxp); } } + void visit(AstCReset* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + + const DfgDataType* const dtypep = DfgDataType::fromAst(nodep->dtypep()); + if (!dtypep) { + m_foundUnhandled = true; + ++m_ctx.m_conv.nonRepDType; + return; + } + + UASSERT_OBJ(!nodep->constructing(), nodep, + "CReset should be non-constructing at this stage"); + + DfgVertex* const vtxp = make(nodep->fileline(), *dtypep); + nodep->user2p(vtxp); + } void visit(AstReplicate* nodep) override { UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 8ab12c214..a3d3adf8e 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -248,6 +248,14 @@ public: } }; +class DfgCReset final : public DfgVertexNullary { +public: + DfgCReset(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype) + : DfgVertexNullary{dfg, dfgType(), flp, dtype} {} + + ASTGEN_MEMBERS_DfgCReset; +}; + //------------------------------------------------------------------------------ // Unary vertices - 1 inputs diff --git a/test_regress/t/t_dfg_synthesis.v b/test_regress/t/t_dfg_synthesis.v index 8bcb19f55..9076b4101 100644 --- a/test_regress/t/t_dfg_synthesis.v +++ b/test_regress/t/t_dfg_synthesis.v @@ -570,4 +570,11 @@ module t ( wire logic [7:0] func_3 = pkg::branchy(rand_a[7:0], rand_b[7:0]); `signal(FUNC_3, func_3); + logic [1:0] via_creset; + always_comb begin + automatic logic [1:0] a /* = AstCReset */; + via_creset = a + 2'd1; + end + `signal(VIA_CRESET, via_creset); + endmodule