Internals: Refactor DFG getCanonicalVariable for reusability (#6094)
This changes hashed names in the generated code, but otherwise no functional change.
This commit is contained in:
parent
5e5b5ab69d
commit
832629c602
|
@ -55,6 +55,30 @@ void DfgGraph::addGraph(DfgGraph& other) {
|
|||
m_opVertices.splice(m_opVertices.end(), other.m_opVertices);
|
||||
}
|
||||
|
||||
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 name = 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 += '_' + name + '_' + std::to_string(s_multiplicity[name]++) + '_';
|
||||
}
|
||||
// Assemble the globally unique name
|
||||
return "__Vdfg" + prefix + m_tmpNameStub + std::to_string(n);
|
||||
}
|
||||
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name, AstNodeDType* dtypep) {
|
||||
// Add AstVar to containing module
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
|
||||
modulep()->addStmtsp(varp);
|
||||
|
||||
// Create and return the corresponding variable vertex
|
||||
if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) return new DfgVarArray{*this, varp};
|
||||
return new DfgVarPacked{*this, varp};
|
||||
}
|
||||
|
||||
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
|
||||
|
||||
// Dump one DfgVertex in Graphviz format
|
||||
|
@ -411,6 +435,55 @@ uint32_t DfgVertex::fanout() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
DfgVarPacked* DfgVertex::getResultVar() {
|
||||
UASSERT_OBJ(!this->is<DfgVarArray>(), this, "Arrays are not supported by " << __FUNCTION__);
|
||||
|
||||
// It's easy if the vertex is already a variable ...
|
||||
if (DfgVarPacked* const varp = this->cast<DfgVarPacked>()) return varp;
|
||||
|
||||
// Inspect existing variables fully written by this vertex, and choose one
|
||||
DfgVarPacked* resp = nullptr;
|
||||
this->forEachSink([&resp](DfgVertex& sink) {
|
||||
DfgVarPacked* const varp = sink.cast<DfgVarPacked>();
|
||||
if (!varp) return;
|
||||
if (!varp->isDrivenFullyByDfg()) return;
|
||||
// Ignore SystemC variables, they cannot participate in expressions or
|
||||
// be assigned rvalue expressions.
|
||||
if (varp->varp()->isSc()) return;
|
||||
// First variable found
|
||||
if (!resp) {
|
||||
resp = varp;
|
||||
return;
|
||||
}
|
||||
// Prefer those variables that must be kept anyway
|
||||
const bool keepOld = resp->keep() || resp->hasDfgRefs();
|
||||
const bool keepNew = varp->keep() || varp->hasDfgRefs();
|
||||
if (keepOld != keepNew) {
|
||||
if (!keepOld) resp = varp;
|
||||
return;
|
||||
}
|
||||
// Prefer those that already have module references
|
||||
if (resp->hasModRefs() != varp->hasModRefs()) {
|
||||
if (!resp->hasModRefs()) resp = varp;
|
||||
return;
|
||||
}
|
||||
// Prefer the earlier one in source order
|
||||
const FileLine& oldFlp = *(resp->fileline());
|
||||
const FileLine& newFlp = *(varp->fileline());
|
||||
if (const int cmp = oldFlp.operatorCompare(newFlp)) {
|
||||
if (cmp > 0) resp = varp;
|
||||
return;
|
||||
}
|
||||
// Prefer the one with the lexically smaller name
|
||||
if (const int cmp = resp->varp()->name().compare(varp->varp()->name())) {
|
||||
if (cmp > 0) resp = varp;
|
||||
return;
|
||||
}
|
||||
// 'resp' and 'varp' are all the same, keep using the existing 'resp'
|
||||
});
|
||||
return resp;
|
||||
}
|
||||
|
||||
void DfgVertex::unlinkDelete(DfgGraph& dfg) {
|
||||
// Unlink source edges
|
||||
forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
|
||||
|
|
14
src/V3Dfg.h
14
src/V3Dfg.h
|
@ -280,6 +280,10 @@ public:
|
|||
// Fanout (number of sinks) of this vertex (expensive to compute)
|
||||
uint32_t fanout() const VL_MT_DISABLED;
|
||||
|
||||
// Return a canonical variable vertex that holds the value of this vertex,
|
||||
// or nullptr if no such variable exists in the graph. This is O(fanout).
|
||||
DfgVarPacked* getResultVar() VL_MT_DISABLED;
|
||||
|
||||
// Unlink from container (graph or builder), then delete this vertex
|
||||
void unlinkDelete(DfgGraph& dfg) VL_MT_DISABLED;
|
||||
|
||||
|
@ -636,7 +640,8 @@ class DfgGraph final {
|
|||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
|
||||
AstModule* const m_modulep;
|
||||
const string m_name; // Name of graph (for debugging)
|
||||
const std::string m_name; // Name of graph - need not be unique
|
||||
std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
|
@ -685,6 +690,13 @@ public:
|
|||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) 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
|
||||
DfgVertexVar* makeNewVar(FileLine*, const std::string& name, AstNodeDType*) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
// Leaves 'this' graph empty.
|
||||
|
|
|
@ -43,14 +43,9 @@ public:
|
|||
class V3DfgRegularizeContext final {
|
||||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
// Used to generate unique names for different DFGs within the same hashed name
|
||||
std::unordered_map<std::string, uint32_t> m_multiplicity;
|
||||
|
||||
public:
|
||||
VDouble0 m_temporariesIntroduced; // Number of temporaries introduced
|
||||
|
||||
std::string tmpNamePrefix(const DfgGraph&); // Return prefix to use for given graph
|
||||
|
||||
explicit V3DfgRegularizeContext(const std::string& label)
|
||||
: m_label{label} {}
|
||||
~V3DfgRegularizeContext() VL_MT_DISABLED;
|
||||
|
|
|
@ -26,73 +26,13 @@
|
|||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
std::string V3DfgRegularizeContext::tmpNamePrefix(const DfgGraph& dfg) {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash{dfg.modulep()->name()};
|
||||
hash += m_label;
|
||||
std::string name = hash.toString();
|
||||
const uint32_t sequenceNumber = m_multiplicity[name]++;
|
||||
name += '_' + std::to_string(sequenceNumber);
|
||||
return name;
|
||||
}
|
||||
|
||||
class DfgRegularize final {
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
V3DfgRegularizeContext& m_ctx; // The optimization context for stats
|
||||
|
||||
// Prefix of temporary variable names
|
||||
const std::string m_tmpNamePrefix = "__VdfgRegularize_" + m_ctx.tmpNamePrefix(m_dfg) + '_';
|
||||
size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only
|
||||
|
||||
// Return canonical variable that can be used to hold the value of this vertex
|
||||
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
|
||||
// First gather all existing variables fully written by this vertex. Ignore SystemC
|
||||
// variables, those cannot act as canonical variables, as they cannot participate in
|
||||
// expressions or be assigned rvalues.
|
||||
std::vector<DfgVarPacked*> varVtxps;
|
||||
vtx.forEachSink([&](DfgVertex& sink) {
|
||||
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
|
||||
if (varVtxp->isDrivenFullyByDfg() && !varVtxp->varp()->isSc()) {
|
||||
varVtxps.push_back(varVtxp);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!varVtxps.empty()) {
|
||||
// There is at least one usable, existing variable. Pick the first one in source
|
||||
// order for deterministic results.
|
||||
std::stable_sort(varVtxps.begin(), varVtxps.end(),
|
||||
[](const DfgVarPacked* ap, const DfgVarPacked* bp) {
|
||||
// Prefer those variables that must be kept anyway
|
||||
const bool keepA = ap->keep() || ap->hasDfgRefs();
|
||||
const bool keepB = bp->keep() || bp->hasDfgRefs();
|
||||
if (keepA != keepB) return keepA;
|
||||
// Prefer those that already have module references, so we don't
|
||||
// have to support recursive substitutions.
|
||||
if (ap->hasModRefs() != bp->hasModRefs()) return ap->hasModRefs();
|
||||
// Otherwise source order
|
||||
const FileLine& aFl = *(ap->fileline());
|
||||
const FileLine& bFl = *(bp->fileline());
|
||||
if (const int cmp = aFl.operatorCompare(bFl)) return cmp < 0;
|
||||
// Fall back on names if all else fails
|
||||
return ap->varp()->name() < bp->varp()->name();
|
||||
});
|
||||
return varVtxps.front();
|
||||
}
|
||||
|
||||
// We need to introduce a temporary
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
|
||||
// Add temporary AstVar to containing module
|
||||
FileLine* const flp = vtx.fileline();
|
||||
const std::string name = m_tmpNamePrefix + std::to_string(m_nTmps++);
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, vtx.dtypep()};
|
||||
m_dfg.modulep()->addStmtsp(varp);
|
||||
|
||||
// Create and return a variable vertex for the temporary
|
||||
return new DfgVarPacked{m_dfg, varp};
|
||||
}
|
||||
|
||||
// Insert intermediate variables for vertices with multiple sinks (or use an existing one)
|
||||
DfgRegularize(DfgGraph& dfg, V3DfgRegularizeContext& ctx)
|
||||
: m_dfg{dfg}
|
||||
|
@ -117,19 +57,27 @@ class DfgRegularize final {
|
|||
|
||||
if (!needsIntermediateVariable) continue;
|
||||
|
||||
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
|
||||
DfgVarPacked* const varp = getCanonicalVariable(vtx);
|
||||
if (varp->arity()) {
|
||||
// Existing variable
|
||||
// This is an op that requires a result variable. Ensure it is
|
||||
// assigned to one, and redirect other sinks read that variable.
|
||||
if (DfgVarPacked* const varp = vtx.getResultVar()) {
|
||||
// A variable already exists
|
||||
UASSERT_OBJ(varp->arity() == 1, varp, "Result variable with multiple drivers");
|
||||
FileLine* const flp = varp->driverFileLine(0);
|
||||
varp->sourceEdge(0)->unlinkSource();
|
||||
varp->resetSources();
|
||||
vtx.replaceWith(varp);
|
||||
varp->addDriver(flp, 0, &vtx);
|
||||
} else {
|
||||
// Temporary variable
|
||||
vtx.replaceWith(varp);
|
||||
varp->addDriver(vtx.fileline(), 0, &vtx);
|
||||
// Need to create an intermediate variable
|
||||
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
|
||||
FileLine* const flp = vtx.fileline();
|
||||
DfgVarPacked* const newp
|
||||
= m_dfg.makeNewVar(flp, name, vtx.dtypep())->as<DfgVarPacked>();
|
||||
++m_nTmps;
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
// Replace vertex with the variable and add back driver
|
||||
vtx.replaceWith(newp);
|
||||
newp->addDriver(vtx.fileline(), 0, &vtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue