mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
Optimize DFG temporary sharing across module instances (#8456)
This commit is contained in:
+27
-19
@@ -21,6 +21,7 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -31,6 +32,8 @@ DfgGraph::DfgGraph(const string& name)
|
||||
: m_name{name} {}
|
||||
|
||||
DfgGraph::~DfgGraph() {
|
||||
V3Stats::addStatSum("Optimizations, DFG, temporary declarations reused",
|
||||
static_cast<double>(m_tempDeclarationsReused));
|
||||
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
|
||||
}
|
||||
|
||||
@@ -98,26 +101,31 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) {
|
||||
// Construct the tmpNameStub if we have not done so yet
|
||||
if (m_tmpNameStub.empty()) {
|
||||
// Use the hash of the graph name (avoid long names and non-identifiers)
|
||||
const std::string hash = V3Hash{m_name}.toString();
|
||||
// We need to keep every variable globally unique, and graph hashed
|
||||
// names might not be, so keep a static table to track multiplicity
|
||||
static std::unordered_map<std::string, uint32_t> s_multiplicity;
|
||||
m_tmpNameStub += '_' + hash + '_' + std::to_string(s_multiplicity[hash]++) + '_';
|
||||
}
|
||||
// Assemble the globally unique name
|
||||
return "__Vdfg" + prefix + m_tmpNameStub + std::to_string(n);
|
||||
}
|
||||
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name,
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& prefix,
|
||||
const DfgDataType& dtype, AstScope* scopep) {
|
||||
// Create AstVar
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtype.astDtypep()};
|
||||
// Add AstVar to the scope's module
|
||||
scopep->modp()->addStmtsp(varp);
|
||||
// AstVar declarations outlive all DFG graphs. Splitting or merging graphs
|
||||
// does not transfer slots: each graph creates globally unique declarations.
|
||||
TempDeclarations& temps = m_temporaries[scopep->modp()][{prefix, dtype.astDtypep()}];
|
||||
const size_t slot = temps.m_scopeCounts[scopep]++;
|
||||
AstVar* varp;
|
||||
if (slot == temps.m_declps.size()) {
|
||||
// Construct the name stub on the first new declaration in this graph
|
||||
if (m_tmpNameStub.empty()) {
|
||||
// Use the hash of the graph name (avoid long names and non-identifiers)
|
||||
const std::string hash = V3Hash{m_name}.toString();
|
||||
// Graph hashes may collide, so track multiplicity to keep names globally unique
|
||||
static std::unordered_map<std::string, uint32_t> s_multiplicity;
|
||||
m_tmpNameStub += '_' + hash + '_' + std::to_string(s_multiplicity[hash]++) + '_';
|
||||
}
|
||||
const std::string varName
|
||||
= "__Vdfg" + prefix + m_tmpNameStub + std::to_string(m_tmpNameCount++);
|
||||
varp = new AstVar{flp, VVarType::MODULETEMP, varName, dtype.astDtypep()};
|
||||
scopep->modp()->addStmtsp(varp);
|
||||
temps.m_declps.emplace_back(varp);
|
||||
} else {
|
||||
varp = temps.m_declps[slot];
|
||||
++m_tempDeclarationsReused;
|
||||
}
|
||||
// Create AstVarScope
|
||||
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
|
||||
// Add to scope
|
||||
|
||||
+20
-8
@@ -46,6 +46,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
@@ -429,6 +430,19 @@ class DfgGraph final {
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
const std::string m_name; // Name of graph - need not be unique
|
||||
std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy
|
||||
size_t m_tmpNameCount = 0; // Sequence number for newly created temporary declarations
|
||||
|
||||
// Slots are local to this graph and keyed by module, prefix and type.
|
||||
// Different prefixes may carry different AstVar attributes, but temporaries
|
||||
// with the same prefix may share an AstVar, so they must have identical ones.
|
||||
// Each scope consumes each slot at most once.
|
||||
struct TempDeclarations final {
|
||||
std::map<AstScope*, size_t> m_scopeCounts; // Next slot for each instance
|
||||
std::vector<AstVar*> m_declps; // Declarations indexed by slot
|
||||
};
|
||||
std::map<AstNodeModule*, std::map<std::pair<std::string, AstNodeDType*>, TempDeclarations>>
|
||||
m_temporaries; // Shared slots indexed by module, purpose, and type
|
||||
uint64_t m_tempDeclarationsReused = 0; // Declarations shared across instance scopes
|
||||
|
||||
// The only way to access thes is via DfgUserMap, so mutable is appropriate,
|
||||
// the map can change while the graph is const.
|
||||
@@ -527,14 +541,12 @@ public:
|
||||
// DfgVertexVar instances representing the same Ast variable are unified.
|
||||
void mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) VL_MT_DISABLED;
|
||||
|
||||
// Genarete a unique name. The provided 'prefix' and 'n' values will be part of the name, and
|
||||
// must be unique (as a pair) in each invocation for this graph.
|
||||
std::string makeUniqueName(const std::string& prefix, size_t n) VL_MT_DISABLED;
|
||||
|
||||
// Create a new variable with the given name and data type. For a Scoped
|
||||
// Dfg, the AstScope where the corresponding AstVarScope will be inserted
|
||||
// must be provided
|
||||
DfgVertexVar* makeNewVar(FileLine*, const std::string& name, const DfgDataType&,
|
||||
// Create a new scoped variable. Instances of a module share temporary
|
||||
// declarations of the same prefix and type, but have independent storage.
|
||||
// Each scope uses a declaration at most once; new declarations get unique names.
|
||||
// As the AstVar may be shared, callers must set identical AstVar attributes
|
||||
// on all temporaries created with the same prefix.
|
||||
DfgVertexVar* makeNewVar(FileLine*, const std::string& prefix, const DfgDataType&,
|
||||
AstScope*) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
|
||||
@@ -239,12 +239,10 @@ class TraceDriver final : public DfgVisitor {
|
||||
|
||||
// Create temporary capable of holding the result of 'vtxp'
|
||||
DfgVertexVar* createTmp(const char* prefix, DfgVertex* vtxp) {
|
||||
AstNode* nodep = v3Global.rootp();
|
||||
const std::string name = m_dfg.makeUniqueName(prefix, nodep->user2Inc());
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
AstScope* const scopep = vtxp->scopep(scopeCache);
|
||||
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
|
||||
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, prefix, vtxp->dtype(), scopep);
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
varp->tmpForp(varp->vscp());
|
||||
m_sccInfo.add(*varp, 0);
|
||||
@@ -1654,9 +1652,6 @@ void breakCycles(DfgGraph& dfg, V3DfgBreakCyclesContext& ctx) {
|
||||
if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed("breakCycles-" + name);
|
||||
};
|
||||
|
||||
// AstNetlist/AstNodeModule user2 used as sequence numbers for temporaries
|
||||
const VNUser2InUse user2InUse;
|
||||
|
||||
// Show input for debugging
|
||||
dump(7, dfg, "input");
|
||||
|
||||
|
||||
+3
-10
@@ -344,9 +344,6 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
// Fast path exit if we surely don't need to convet anything
|
||||
if (nTerms < TERM_LIMIT) return;
|
||||
|
||||
// Sequence numbers for name generation
|
||||
size_t nTables = 0;
|
||||
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
|
||||
// Create decoders for each srcp
|
||||
@@ -419,8 +416,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
// If there is an existing result variable, use that
|
||||
if (DfgVertexVar* const vp = srcp->getResultVar()) return vp->as<DfgVarPacked>();
|
||||
// Otherwise create a new variable
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, "BinToOneHot_Idx", idxDType, scopep);
|
||||
vtxp->vscp()->varp()->isInternal(true);
|
||||
vtxp->srcp(srcp);
|
||||
return vtxp->as<DfgVarPacked>();
|
||||
@@ -428,8 +424,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
AstVarScope* const idxVscp = idxVtxp->vscp();
|
||||
// The previous index variable - we don't need a vertex for this
|
||||
AstVarScope* const preVscp = [&]() {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Pre", nTables);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, "BinToOneHot_Pre", idxDType, scopep);
|
||||
AstVarScope* const vscp = vtxp->vscp();
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
vscp->varp()->isInternal(true);
|
||||
@@ -439,15 +434,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
}();
|
||||
// The table variable
|
||||
DfgVarArray* const tabVtxp = [&]() {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Tab", nTables);
|
||||
DfgVertexVar* const varp = dfg.makeNewVar(flp, name, tabDType, scopep);
|
||||
DfgVertexVar* const varp = dfg.makeNewVar(flp, "BinToOneHot_Tab", tabDType, scopep);
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
varp->vscp()->varp()->noReset(true);
|
||||
varp->setHasModWrRefs();
|
||||
return varp->as<DfgVarArray>();
|
||||
}();
|
||||
|
||||
++nTables;
|
||||
++ctx.m_decodersCreated;
|
||||
|
||||
// Initialize 'tab' and 'pre' variables statically
|
||||
|
||||
@@ -209,7 +209,6 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||
DfgVertex* m_vtxp = nullptr; // Currently considered vertex
|
||||
size_t m_currentGeneration = 0; // Current generation number
|
||||
size_t m_lastId = 0; // Last unique vertex ID assigned
|
||||
size_t m_nTemps = 0; // Number of temporary variables created
|
||||
// Scope for transient temporariy variables cerated in this pass. They should all be
|
||||
// eliminated wihtin this pass, so anything should be ok, pick the top scope as easy to find.
|
||||
AstScope* const m_tmpScopep = v3Global.rootp()->topScopep()->scopep();
|
||||
@@ -2181,9 +2180,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||
DfgSplicePacked* const sp = new DfgSplicePacked{m_dfg, flp, vtxp->dtype()};
|
||||
m_vInfo[sp].m_id = ++m_lastId;
|
||||
sp->addDriver(catp, lsb, flp);
|
||||
const std::string name = m_dfg.makeUniqueName("PeepholeNarrow", m_nTemps++);
|
||||
DfgVertexVar* const varp
|
||||
= m_dfg.makeNewVar(flp, name, vtxp->dtype(), m_tmpScopep);
|
||||
= m_dfg.makeNewVar(flp, "PeepholeNarrow", vtxp->dtype(), m_tmpScopep);
|
||||
varp->tmpForp(varp->vscp());
|
||||
m_vInfo[varp].m_id = ++m_lastId;
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
|
||||
@@ -30,7 +30,6 @@ class DfgRegularize final {
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
V3DfgRegularizeContext& m_ctx; // The optimization context for stats
|
||||
size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only
|
||||
VNDeleter m_deleter; // Deletes replacement nodes at the end
|
||||
|
||||
// METHODS
|
||||
@@ -208,11 +207,10 @@ class DfgRegularize final {
|
||||
if (!needsTemporary(*vtxp, *vtxp)) continue;
|
||||
// Need to create an intermediate variable
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
AstScope* const scopep = vtxp->scopep(scopeCache);
|
||||
DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
|
||||
++m_nTmps;
|
||||
DfgVertexVar* const newp
|
||||
= m_dfg.makeNewVar(flp, "Regularize", vtxp->dtype(), scopep);
|
||||
// Replace vertex with the variable, make it drive the variable
|
||||
vtxp->replaceWith(newp);
|
||||
newp->srcp(vtxp);
|
||||
|
||||
+6
-17
@@ -58,8 +58,6 @@ DfgArraySel* makeVertex<DfgArraySel, AstArraySel>(const AstArraySel* nodep, DfgG
|
||||
class AstToDfgConverter final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node
|
||||
// AstVar::user3() -> int temporary counter for variable
|
||||
const VNUser3InUse m_user3InUse;
|
||||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being built
|
||||
@@ -74,8 +72,6 @@ class AstToDfgConverter final : public VNVisitor {
|
||||
bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit'
|
||||
bool m_converting = false; // We are trying to convert some logic at the moment
|
||||
|
||||
size_t m_nUnpack = 0; // Sequence numbers for temporaries
|
||||
|
||||
// METHODS
|
||||
|
||||
// Allocate a new non-variable vertex, add it to the currently synthesized logic
|
||||
@@ -273,8 +269,7 @@ class AstToDfgConverter final : public VNVisitor {
|
||||
// Assigning compound expressions to a concatenated LHS requires a temporary
|
||||
// to avoid multiple use of the expression
|
||||
if (VN_IS(lhsp, Concat) && !vtxp->is<DfgVertexVar>() && !vtxp->is<DfgConst>()) {
|
||||
const size_t n = ++m_nUnpack;
|
||||
DfgVertexVar* const tmpp = createTmp(*m_logicp, flp, vtxp->dtype(), "Unpack", n);
|
||||
DfgVertexVar* const tmpp = createTmp(*m_logicp, flp, vtxp->dtype(), "Unpack");
|
||||
tmpp->srcp(vtxp);
|
||||
vtxp = tmpp;
|
||||
}
|
||||
@@ -496,9 +491,8 @@ public:
|
||||
|
||||
// Create temporay variable capable of holding the given type
|
||||
DfgVertexVar* createTmp(DfgLogic& logic, FileLine* flp, const DfgDataType& dtype,
|
||||
const std::string& prefix, size_t tmpCount) {
|
||||
const std::string name = m_dfg.makeUniqueName(prefix, tmpCount);
|
||||
DfgVertexVar* const vtxp = m_dfg.makeNewVar(flp, name, dtype, logic.scopep());
|
||||
const std::string& prefix) {
|
||||
DfgVertexVar* const vtxp = m_dfg.makeNewVar(flp, prefix, dtype, logic.scopep());
|
||||
logic.synth().emplace_back(vtxp);
|
||||
vtxp->vscp()->varp()->isInternal(true);
|
||||
vtxp->tmpForp(vtxp->vscp());
|
||||
@@ -511,8 +505,7 @@ public:
|
||||
FileLine* const flp = astVarp->fileline();
|
||||
const DfgDataType& dtype = *DfgDataType::fromAst(astVarp->dtypep());
|
||||
const std::string prfx = prefix + "_" + astVarp->name();
|
||||
const size_t tmpCount = astVarp->user3Inc();
|
||||
DfgVertexVar* const vtxp = createTmp(logic, flp, dtype, prfx, tmpCount);
|
||||
DfgVertexVar* const vtxp = createTmp(logic, flp, dtype, prfx);
|
||||
vtxp->tmpForp(vscp);
|
||||
return vtxp;
|
||||
}
|
||||
@@ -624,8 +617,6 @@ class AstToDfgSynthesize final {
|
||||
DfgGraph& m_dfg; // The graph being built
|
||||
V3DfgSynthesisContext& m_ctx; // The context for stats
|
||||
AstToDfgConverter m_converter; // The convert instance to use for each construct
|
||||
size_t m_nBranchCond = 0; // Sequence numbers for temporaries
|
||||
size_t m_nPathPred = 0; // Sequence numbers for temporaries
|
||||
DfgWorklist m_toRevert{m_dfg}; // We need a worklist for reverting synthesis
|
||||
|
||||
// STATE - for current DfgLogic being synthesized
|
||||
@@ -1539,12 +1530,11 @@ class AstToDfgSynthesize final {
|
||||
return resp;
|
||||
}();
|
||||
|
||||
size_t n = m_nPathPred++; // Sequence number for temporaries
|
||||
const DfgDataType& dtype = predp->dtype();
|
||||
|
||||
const auto mkTmp = [&](FileLine* flp, const char* name, DfgVertex* srcp) {
|
||||
const std::string prefix = "_BB" + std::to_string(bb.id()) + "_" + name;
|
||||
DfgVertexVar* const tmpp = m_converter.createTmp(*m_logicp, flp, dtype, prefix, n);
|
||||
DfgVertexVar* const tmpp = m_converter.createTmp(*m_logicp, flp, dtype, prefix);
|
||||
tmpp->srcp(srcp);
|
||||
return tmpp;
|
||||
};
|
||||
@@ -1717,8 +1707,7 @@ class AstToDfgSynthesize final {
|
||||
FileLine* const flp = condp->fileline();
|
||||
const DfgDataType& dtype = condp->dtype();
|
||||
const std::string prefix = "_BB" + std::to_string(bb.id()) + "_Cond";
|
||||
const size_t n = m_nBranchCond++;
|
||||
DfgVertexVar* const vp = m_converter.createTmp(*m_logicp, flp, dtype, prefix, n);
|
||||
DfgVertexVar* const vp = m_converter.createTmp(*m_logicp, flp, dtype, prefix);
|
||||
vp->srcp(condp);
|
||||
m_bbToCondp[bb] = vp;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user