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
This commit is contained in:
Geza Lore 2026-06-08 17:01:50 +01:00 committed by GitHub
parent e6a070b93b
commit ece4d71e5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 76 additions and 4 deletions

View File

@ -91,6 +91,11 @@ std::unique_ptr<DfgGraph> 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<DfgSel>()->lsb());
@ -132,11 +137,14 @@ std::unique_ptr<DfgGraph> 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<DfgAstRd>();
CHECK(v.isPacked() || v.isArray(), "Should be Packed or Array type");

View File

@ -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;
}

View File

@ -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<DfgConst>()->num().isCaseEq(b.as<DfgConst>()->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<DfgCReset>()) 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

View File

@ -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<DfgVertexVar>(), sinkp, "CReset should drive a variable");
AstVar* const varp = sinkp->as<DfgVertexVar>()->vscp()->varp();
m_resultp = new AstCReset{vtxp->fileline(), varp, false};
}
void visit(DfgRep* vtxp) override {
FileLine* const flp = vtxp->fileline();

View File

@ -120,6 +120,8 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
// Partial driver cannot be inlined
if (srcp->is<DfgVertexSplice>()) continue;
if (srcp->is<DfgUnitArray>()) continue;
// Don't inline CReset
if (srcp->is<DfgCReset>()) continue;
// Okie dokie, here we go ...
vtx.replaceWith(srcp);
}

View File

@ -1158,7 +1158,7 @@ class V3DfgPeephole final : public DfgVisitor {
// Sel from a partial variable (including narrowed vertex)
if (DfgVarPacked* const varp = fromp->cast<DfgVarPacked>()) {
if (varp->srcp() && !varp->isVolatile()) {
if (varp->srcp() && !varp->isVolatile() && !varp->srcp()->is<DfgCReset>()) {
// Must be a splice, otherwise it would have been inlined
DfgSplicePacked* splicep = varp->srcp()->as<DfgSplicePacked>();
DfgVertex* driverp = nullptr;

View File

@ -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();

View File

@ -100,6 +100,9 @@ class DfgRegularize final {
// No need to add a temporary if the single sink is a variable already
if (sink.is<DfgVertexVar>()) return false;
// CReset always needs to be driving a variable
if (aVtx.is<DfgCReset>()) return true;
// Do not inline expressions into a loop body
if (const DfgAstRd* const astRdp = sink.cast<DfgAstRd>()) { return astRdp->inLoop(); }

View File

@ -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<DfgCReset>(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");

View File

@ -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

View File

@ -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