Optimize logic and variable removal early in Dfg (#7081)
After conversion of Ast to Dfg, but before synthesizing AstAlways into primitives, run a pass to remove variables that are not observable, and all logic that only computes such variables. This can get rid of a lot of content early so we don't build redundant Dfgs, and also enables synthesizing always blocks that use temporaries only in some branches, which will come in a follow up.
This commit is contained in:
parent
ed2f018729
commit
5834f22944
|
|
@ -216,6 +216,27 @@ private:
|
|||
addStat("temporaries introduced", m_temporariesIntroduced);
|
||||
}
|
||||
};
|
||||
class V3DfgRemoveUnobservableContext final : public V3DfgSubContext {
|
||||
// Only V3DfgContext can create an instance
|
||||
friend class V3DfgContext;
|
||||
|
||||
public:
|
||||
// STATE
|
||||
VDouble0 m_varsRemoved; // Number of variables removed from the Dfg
|
||||
VDouble0 m_varsDeleted; // Number of variables removed from the Dfg and the Ast
|
||||
VDouble0 m_logicRemoved; // Number of logic blocks removed from the Dfg
|
||||
VDouble0 m_logicDeleted; // Number of logic blocks removed from the Dfg and the Ast
|
||||
|
||||
private:
|
||||
V3DfgRemoveUnobservableContext(V3DfgContext& ctx, const std::string& label)
|
||||
: V3DfgSubContext{ctx, label, "RemoveUnobservable"} {}
|
||||
~V3DfgRemoveUnobservableContext() {
|
||||
addStat("variables removed", m_varsRemoved);
|
||||
addStat("variables deleted", m_varsDeleted);
|
||||
addStat("logic removed", m_logicRemoved);
|
||||
addStat("logic deleted", m_logicDeleted);
|
||||
}
|
||||
};
|
||||
class V3DfgSynthesisContext final : public V3DfgSubContext {
|
||||
// Only V3DfgContext can create an instance
|
||||
friend class V3DfgContext;
|
||||
|
|
@ -366,6 +387,7 @@ public:
|
|||
V3DfgPeepholeContext m_peepholeContext{*this, m_label};
|
||||
V3DfgPushDownSelsContext m_pushDownSelsContext{*this, m_label};
|
||||
V3DfgRegularizeContext m_regularizeContext{*this, m_label};
|
||||
V3DfgRemoveUnobservableContext m_removeUnobservableContext{*this, m_label};
|
||||
V3DfgSynthesisContext m_synthContext{*this, m_label};
|
||||
|
||||
// Node pattern collector
|
||||
|
|
|
|||
|
|
@ -314,6 +314,10 @@ class DataflowOptimize final {
|
|||
// Dump the initial graph for debugging
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "dfg-in");
|
||||
|
||||
// Remove unobservable variabels and logic that drives only such variables
|
||||
V3DfgPasses::removeUnobservable(dfg, m_ctx);
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "pruned");
|
||||
|
||||
// Synthesize DfgLogic vertices
|
||||
V3DfgPasses::synthesize(dfg, m_ctx);
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "synth");
|
||||
|
|
|
|||
|
|
@ -25,6 +25,82 @@
|
|||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
void V3DfgPasses::removeUnobservable(DfgGraph& dfg, V3DfgContext& dfgCtx) {
|
||||
V3DfgRemoveUnobservableContext& ctx = dfgCtx.m_removeUnobservableContext;
|
||||
|
||||
// Enqueue all DfgLogic vertices to work list
|
||||
DfgWorklist workList{dfg};
|
||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
if (vtx.is<DfgLogic>()) workList.push_front(vtx);
|
||||
}
|
||||
|
||||
// Remove all logic that only drives unobservable variables
|
||||
workList.foreach([&](DfgVertex& vtx) {
|
||||
DfgLogic* const logicp = vtx.as<DfgLogic>();
|
||||
// Check all variables driven by this logic are removable
|
||||
bool used = logicp->foreachSink([&](DfgVertex& snk) {
|
||||
DfgUnresolved* const uVtxp = snk.as<DfgUnresolved>();
|
||||
DfgVertexVar* const vVtxp = uVtxp->firtsSinkp()->as<DfgVertexVar>();
|
||||
if (vVtxp->hasSinks()) return true;
|
||||
if (vVtxp->isObserved()) return true;
|
||||
return false;
|
||||
});
|
||||
// If some are used, the logic must stay in the Ast
|
||||
if (used) return;
|
||||
// If impure, it must stay in the Ast
|
||||
if (!logicp->isPure()) return;
|
||||
// Enqueue logic driving the inputs of this logic we are about to delete
|
||||
logicp->foreachSource([&](DfgVertex& src) {
|
||||
DfgVertexVar* const varp = src.as<DfgVertexVar>();
|
||||
if (!varp->srcp()) return false;
|
||||
varp->srcp()->as<DfgUnresolved>()->foreachSource([&](DfgVertex& driver) {
|
||||
workList.push_front(*driver.as<DfgLogic>());
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
// Delete this logic both from the Dfg and the Ast
|
||||
AstNode* const nodep = logicp->nodep();
|
||||
VL_DO_DANGLING(logicp->unlinkDelete(dfg), logicp);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
++ctx.m_logicDeleted;
|
||||
});
|
||||
|
||||
// Remove unobservable variables
|
||||
for (DfgVertexVar* const vVtxp : dfg.varVertices().unlinkable()) {
|
||||
if (vVtxp->hasSinks()) continue;
|
||||
if (vVtxp->isObserved()) continue;
|
||||
DfgVertex* const srcp = vVtxp->srcp(); // Must be a DfgUnresolved or nullptr
|
||||
AstNode* const varp = vVtxp->nodep();
|
||||
// Can delete the Ast variable too if it has no other references
|
||||
const bool delAst = (!srcp || !srcp->nInputs()) //
|
||||
&& !vVtxp->hasExtWrRefs() //
|
||||
&& !vVtxp->hasModWrRefs();
|
||||
VL_DO_DANGLING(vVtxp->unlinkDelete(dfg), vVtxp);
|
||||
if (srcp) VL_DO_DANGLING(srcp->unlinkDelete(dfg), srcp);
|
||||
if (delAst) {
|
||||
VL_DO_DANGLING(varp->unlinkFrBack()->deleteTree(), varp);
|
||||
++ctx.m_varsDeleted;
|
||||
} else {
|
||||
++ctx.m_varsRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally remove logic from the Dfg if it drives no variables in the graph.
|
||||
// These should only be those with side effects.
|
||||
for (DfgVertex* const vtxp : dfg.opVertices().unlinkable()) {
|
||||
if (!vtxp->is<DfgLogic>()) continue;
|
||||
if (vtxp->hasSinks()) continue;
|
||||
// Input variables will be read in Ast code, mark as such
|
||||
vtxp->foreachSource([](DfgVertex& src) {
|
||||
src.as<DfgVertexVar>()->setHasModRdRefs();
|
||||
return false;
|
||||
});
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
++ctx.m_logicRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
// Nothing to inline it into
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ std::unique_ptr<DfgGraph> astToDfg(AstModule&, V3DfgContext&) VL_MT_DISABLED;
|
|||
// Same as above, but for the entire netlist, after V3Scope
|
||||
std::unique_ptr<DfgGraph> astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Remove unobservable variabels and logic that drives only such variables
|
||||
void removeUnobservable(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Synthesize DfgLogic vertices into primitive operations.
|
||||
// Removes all DfgLogic (even those that were not synthesized).
|
||||
void synthesize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
|
|
|||
|
|
@ -465,6 +465,7 @@ class DfgLogic final : public DfgVertexVariadic {
|
|||
bool m_selectedForSynthesis = false; // Logic selected for synthesis
|
||||
bool m_nonSynthesizable = false; // Logic is not synthesizeable (by DfgSynthesis)
|
||||
bool m_reverted = false; // Logic was synthesized (in part if non synthesizable) then reverted
|
||||
mutable uint8_t m_cachedPure = 0; // Cached purity of the logic
|
||||
|
||||
public:
|
||||
DfgLogic(DfgGraph& dfg, AstAlways* nodep, AstScope* scopep, std::unique_ptr<CfgGraph> cfgp)
|
||||
|
|
@ -493,6 +494,16 @@ public:
|
|||
void setNonSynthesizable() { m_nonSynthesizable = true; }
|
||||
bool reverted() const { return m_reverted; }
|
||||
void setReverted() { m_reverted = true; }
|
||||
// Logic has no side-effect, just computes its output variables based on its input variables
|
||||
bool isPure() const {
|
||||
if (!m_cachedPure) {
|
||||
// This is a sledgehamer, but AstNodeStmts don't compute their 'purity' properly,
|
||||
// not that 'purity' makes sense for statements... We don't call this often and cached.
|
||||
const bool pure = m_nodep->forall([](AstNode* nodep) { return nodep->isPure(); });
|
||||
m_cachedPure = static_cast<uint8_t>(pure) | 0x2;
|
||||
}
|
||||
return m_cachedPure & 0x01;
|
||||
}
|
||||
};
|
||||
|
||||
class DfgUnresolved final : public DfgVertexVariadic {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ test.execute()
|
|||
|
||||
if test.vlt:
|
||||
test.file_grep(test.stats, r'Optimizations, DFG scoped PushDownSels, sels pushed down\s+(\d+)',
|
||||
50)
|
||||
49)
|
||||
test.file_grep(test.stats, r'Optimizations, DFG scoped PushDownSels, would be cyclic\s+(\d+)',
|
||||
1)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename
|
|||
test.execute()
|
||||
|
||||
if test.vlt:
|
||||
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 42)
|
||||
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 43)
|
||||
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
Loading…
Reference in New Issue