verilator/src/V3DfgRegularize.cpp

124 lines
5.4 KiB
C++
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Convert DfgGraph to AstModule
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2024 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// - Ensures intermediate values (other than simple memory references or
// constants) with multiple uses are assigned to variables
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Dfg.h"
#include "V3DfgPasses.h"
#include "V3UniqueNames.h"
VL_DEFINE_DEBUG_FUNCTIONS;
class DfgRegularize final {
DfgGraph& m_dfg; // The graph being processed
V3DfgRegularizeContext& m_ctx; // The optimization context for stats
// For generating temporary names
V3UniqueNames m_tmpNames{"__VdfgRegularize_" + m_ctx.ident() + "_" + m_dfg.modulep()->name()
+ "_tmp"};
// Return canonical variable that can be used to hold the value of this vertex
DfgVarPacked* getCanonicalVariable(DfgVertex* vtxp) {
// First gather all existing variables fully written by this vertex
std::vector<DfgVarPacked*> varVtxps;
vtxp->forEachSink([&](DfgVertex& vtx) {
if (DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
if (varVtxp->isDrivenFullyByDfg()) 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 = vtxp->fileline();
const std::string name = m_tmpNames.get(vtxp->hash().toString());
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, vtxp->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}
, m_ctx{ctx} {
// Used by DfgVertex::hash
const auto userDataInUse = m_dfg.userDataInUse();
// Ensure intermediate values used multiple times are written to variables
for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) {
nextp = vtxp->verticesNext();
// Operations without multiple sinks need no variables
if (!vtxp->hasMultipleSinks()) continue;
// Array selects need no variables, they are just memory references
if (vtxp->is<DfgArraySel>()) continue;
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
DfgVarPacked* const varp = getCanonicalVariable(vtxp);
if (varp->arity()) {
// Existing variable
FileLine* const flp = varp->driverFileLine(0);
varp->sourceEdge(0)->unlinkSource();
varp->resetSources();
vtxp->replaceWith(varp);
varp->addDriver(flp, 0, vtxp);
} else {
// Temporary variable
vtxp->replaceWith(varp);
varp->addDriver(vtxp->fileline(), 0, vtxp);
}
}
}
public:
static void apply(DfgGraph& dfg, V3DfgRegularizeContext& ctx) { DfgRegularize{dfg, ctx}; }
};
void V3DfgPasses::regularize(DfgGraph& dfg, V3DfgRegularizeContext& ctx) {
DfgRegularize::apply(dfg, ctx);
}