Optimize circular logic in Dfg (#7902)

Introduce a new DfgPrev vertex, representing the value of a variable
before any in-graph assignments. This can be used to break all remaining
cycles in the graph, so all Dfgs become acyclic after V3DfgBreakCycles.
The circular dataflow is still represented, and is taken care of by the
scheduler, it is just the DfgGraph that represents the logic that
becomes acyclic.

This makes V3DfgBreakCycles a mandatory transform, so drop the disabling
-fno-dfg-break-cycles option (still parsed, but has no effect).

Note the effect of this is small, as most cycles can be fixed up by
driver tracing, which is unchanged, but this is required for some
upcoming work.
This commit is contained in:
Geza Lore
2026-08-18 14:54:12 +02:00
committed by GitHub
parent 94a63dd407
commit ba893abb2c
19 changed files with 228 additions and 127 deletions
+6 -16
View File
@@ -56,16 +56,6 @@ class DfgRegularize final {
}
}
std::unordered_set<const DfgVertexVar*> gatherCyclicVariables() {
DfgUserMap<uint64_t> vtx2Scc = m_dfg.makeUserMap<uint64_t>();
V3DfgPasses::colorStronglyConnectedComponents(m_dfg, vtx2Scc);
std::unordered_set<const DfgVertexVar*> circularVariables;
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
if (vtx2Scc[vtx]) circularVariables.emplace(&vtx);
}
return circularVariables;
}
static bool isUnused(const DfgVertex& vtx) {
if (vtx.hasSinks()) return false;
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
@@ -73,6 +63,7 @@ class DfgRegularize final {
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
if (varp->hasModWrRefs()) return false;
if (varp->hasExtRefs()) return false;
if (varp->hasPrev()) return false;
}
return true;
}
@@ -87,6 +78,9 @@ class DfgRegularize final {
&aVtx, "Mismatched vertices");
UASSERT_OBJ(!aVtx.is<DfgVertexVar>(), &aVtx, "Should be an operation vertex");
// Prev is just a variable reference
if (aVtx.is<DfgPrev>()) return false;
if (bVtx.hasMultipleSinks()) {
// Add a temporary if it's cheaper to store and load from memory than recompute
if (!aVtx.isCheaperThanLoad()) return true;
@@ -119,10 +113,6 @@ class DfgRegularize final {
}
void eliminateVars() {
// Although we could eliminate some circular variables, doing so would
// make UNOPTFLAT traces fairly usesless, so we will not do so.
const std::unordered_set<const DfgVertexVar*> circularVariables = gatherCyclicVariables();
// Worklist based algoritm
DfgWorklist workList{m_dfg};
@@ -141,7 +131,7 @@ class DfgRegularize final {
});
// Delete corresponsing Ast variable at the end
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
m_ctx.m_deleteps.push_back(varp->vscp());
if (!varp->hasPrev()) m_ctx.m_deleteps.push_back(varp->vscp());
}
// Remove the unused vertex
vtx.unlinkDelete(m_dfg);
@@ -173,7 +163,7 @@ class DfgRegularize final {
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
// Do not eliminate circular variables - need to preserve UNOPTFLAT traces
if (circularVariables.count(varp)) return;
if (varp->hasPrev()) return;
// Do not inline if partially driven (the partial driver network can't be fed into
// arbitrary logic. TODO: we should peeophole these away entirely)