Internals: Improve DFG implementation details (#6355)
Large scale refactoring to simplify some of the more obtuse internals of DFG. Remove multiple redundant internal APIs, simplify representation of variables, fix potential unsoundness in circular decomposition. No functional change intended.
This commit is contained in:
parent
67f26508ba
commit
a6f26b85b3
|
|
@ -2617,7 +2617,7 @@ class AstAlways final : public AstNodeProcedure {
|
|||
const VAlwaysKwd m_keyword;
|
||||
|
||||
public:
|
||||
AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sentreep, AstNode* stmtsp)
|
||||
AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sentreep, AstNode* stmtsp = nullptr)
|
||||
: ASTGEN_SUPER_Always(fl, stmtsp)
|
||||
, m_keyword{keyword} {
|
||||
this->sentreep(sentreep);
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ void CfgGraph::rpoBlocks() {
|
|||
// Assign edge IDs
|
||||
size_t edgeCount = 0;
|
||||
for (V3GraphVertex& v : vertices()) {
|
||||
// cppcheck-suppress constVariableReference // cppcheck is wrong
|
||||
for (V3GraphEdge& e : v.outEdges()) static_cast<CfgEdge&>(e).m_id = edgeCount++;
|
||||
}
|
||||
UASSERT_OBJ(edgeCount == m_nEdges, m_enterp, "Inconsistent edge count");
|
||||
|
|
|
|||
20
src/V3Cfg.h
20
src/V3Cfg.h
|
|
@ -92,15 +92,12 @@ class CfgBlock final : public V3GraphVertex {
|
|||
|
||||
// STATE
|
||||
CfgGraph* const m_cfgp; // The control flow graph this CfgBlock is under
|
||||
size_t m_rpoNumber; // Reverse post-order number and unique ID of this CfgBlock
|
||||
size_t m_rpoNumber = 0; // Reverse post-order number and unique ID of this CfgBlock
|
||||
|
||||
// V3GraphEdge::user() is set to the unique id by CfgBuilder
|
||||
std::vector<AstNodeStmt*> m_stmtps; // statements in this CfgBlock
|
||||
|
||||
// PRIVATE METHODS
|
||||
// ID (reverse post-order numpber) of this block
|
||||
inline size_t id();
|
||||
inline size_t id() const;
|
||||
|
||||
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
||||
inline explicit CfgBlock(CfgGraph* cfgp);
|
||||
|
|
@ -109,6 +106,10 @@ class CfgBlock final : public V3GraphVertex {
|
|||
public:
|
||||
// PUBLIC METHODS
|
||||
|
||||
// ID (reverse post-order number) of this block
|
||||
inline size_t id();
|
||||
inline size_t id() const;
|
||||
|
||||
// Is this the entry block of the CFG?
|
||||
bool isEnter() const { return inEmpty(); }
|
||||
// Is this the exit block of the CFG?
|
||||
|
|
@ -185,19 +186,20 @@ class CfgEdge final : public V3GraphEdge {
|
|||
|
||||
// STATE - Immutable after construction, set by CfgBuilder
|
||||
CfgGraph* const m_cfgp; // The control flow graph this CfgEdge is under
|
||||
size_t m_id; // Unique ID of this vertex
|
||||
size_t m_id = 0; // Unique ID of this vertex
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Unique ID of this CfgEdge - no particular meaning
|
||||
inline size_t id();
|
||||
inline size_t id() const;
|
||||
|
||||
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
||||
inline CfgEdge(CfgGraph* graphp, CfgBlock* srcp, CfgBlock* dstp);
|
||||
~CfgEdge() override = default;
|
||||
|
||||
public:
|
||||
// METHODS - all const
|
||||
// METHODS
|
||||
|
||||
// Unique ID of this CfgEdge - no particular meaning
|
||||
inline size_t id();
|
||||
inline size_t id() const;
|
||||
|
||||
// Source/destination CfgBlock
|
||||
const CfgBlock* srcp() const { return static_cast<const CfgBlock*>(fromp()); }
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ class CfgBuilder final : public VNVisitorConst {
|
|||
while (!unreachableps.empty()) {
|
||||
V3GraphVertex* const vtxp = unreachableps.back();
|
||||
unreachableps.pop_back();
|
||||
for (V3GraphEdge& edge : vtxp->outEdges()) {
|
||||
for (const V3GraphEdge& edge : vtxp->outEdges()) {
|
||||
--m_cfgp->m_nEdges;
|
||||
if (edge.top()->inSize1()) unreachableps.emplace_back(edge.top());
|
||||
}
|
||||
|
|
|
|||
442
src/V3Dfg.cpp
442
src/V3Dfg.cpp
|
|
@ -24,15 +24,42 @@
|
|||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgGraph
|
||||
// V3Dfg
|
||||
|
||||
// predicate for supported data types
|
||||
static bool dfgGraphIsSupportedDTypePacked(const AstNodeDType* dtypep) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) {
|
||||
return typep->keyword().isIntNumeric();
|
||||
}
|
||||
if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
|
||||
}
|
||||
if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
return typep->packed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool V3Dfg::isSupported(const AstNodeDType* dtypep) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
// Support 1 dimensional unpacked arrays of packed types
|
||||
if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
|
||||
}
|
||||
// Support packed types
|
||||
return dfgGraphIsSupportedDTypePacked(dtypep);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgGraph
|
||||
|
||||
DfgGraph::DfgGraph(AstModule* modulep, const string& name)
|
||||
: m_modulep{modulep}
|
||||
, m_name{name} {}
|
||||
|
||||
DfgGraph::~DfgGraph() {
|
||||
forEachVertex([](DfgVertex& vtxp) { delete &vtxp; });
|
||||
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
|
||||
}
|
||||
|
||||
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
||||
|
|
@ -133,40 +160,21 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
|||
// Constants have no inputs
|
||||
// Hook up inputs of cloned variables
|
||||
for (const DfgVertexVar& vtx : m_varVertices) {
|
||||
// All variable vertices are unary
|
||||
if (const DfgVertex* const srcp = vtx.srcp()) {
|
||||
vtxp2clonep.at(&vtx)->as<DfgVertexVar>()->srcp(vtxp2clonep.at(srcp));
|
||||
}
|
||||
DfgVertexVar* const cp = vtxp2clonep.at(&vtx)->as<DfgVertexVar>();
|
||||
if (const DfgVertex* const srcp = vtx.srcp()) cp->srcp(vtxp2clonep.at(srcp));
|
||||
if (const DfgVertex* const defp = vtx.defaultp()) cp->defaultp(vtxp2clonep.at(defp));
|
||||
}
|
||||
// Hook up inputs of cloned operation vertices
|
||||
for (const DfgVertex& vtx : m_opVertices) {
|
||||
if (vtx.is<DfgVertexVariadic>()) {
|
||||
switch (vtx.type()) {
|
||||
case VDfgType::atSpliceArray: {
|
||||
const DfgSpliceArray* const vp = vtx.as<DfgSpliceArray>();
|
||||
DfgSpliceArray* const cp = vtxp2clonep.at(vp)->as<DfgSpliceArray>();
|
||||
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||
if (const DfgVertex* const srcp = edge.sourcep()) {
|
||||
cp->addDriver(vp->driverFileLine(i), //
|
||||
vp->driverLo(i), //
|
||||
vtxp2clonep.at(srcp));
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case VDfgType::atSpliceArray:
|
||||
case VDfgType::atSplicePacked: {
|
||||
const DfgSplicePacked* const vp = vtx.as<DfgSplicePacked>();
|
||||
DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as<DfgSplicePacked>();
|
||||
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||
if (const DfgVertex* const srcVp = edge.sourcep()) {
|
||||
DfgVertex* const srcCp = vtxp2clonep.at(srcVp);
|
||||
UASSERT_OBJ(!srcCp->is<DfgLogic>(), srcCp, "Cannot clone DfgLogic");
|
||||
if (srcVp == vp->defaultp()) {
|
||||
cp->defaultp(srcCp);
|
||||
} else {
|
||||
cp->addDriver(vp->driverFileLine(i), vp->driverLo(i), srcCp);
|
||||
}
|
||||
}
|
||||
const DfgVertexSplice* const vp = vtx.as<DfgVertexSplice>();
|
||||
DfgVertexSplice* const cp = vtxp2clonep.at(vp)->as<DfgVertexSplice>();
|
||||
vp->foreachDriver([&](const DfgVertex& src, uint32_t lo, FileLine* flp) {
|
||||
cp->addDriver(vtxp2clonep.at(&src), lo, flp);
|
||||
return false;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -178,14 +186,8 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
|||
}
|
||||
} else {
|
||||
DfgVertex* const cp = vtxp2clonep.at(&vtx);
|
||||
const auto oSourceEdges = vtx.sourceEdges();
|
||||
auto cSourceEdges = cp->sourceEdges();
|
||||
UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx,
|
||||
"Mismatched source count");
|
||||
for (size_t i = 0; i < oSourceEdges.second; ++i) {
|
||||
if (const DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) {
|
||||
cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp));
|
||||
}
|
||||
for (size_t i = 0; i < vtx.nInputs(); ++i) {
|
||||
cp->inputp(i, vtxp2clonep.at(vtx.inputp(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,9 +212,12 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
|||
// Variabels that are present in 'this', make them use the DfgVertexVar in 'this'.
|
||||
if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to<DfgVertexVar*>()) {
|
||||
DfgVertex* const srcp = vtxp->srcp();
|
||||
UASSERT_OBJ(!srcp || !altp->srcp(), vtxp, "At most one alias should be driven");
|
||||
DfgVertex* const defaultp = vtxp->defaultp();
|
||||
UASSERT_OBJ(!(srcp || defaultp) || (!altp->srcp() && !altp->defaultp()), vtxp,
|
||||
"At most one alias should be driven");
|
||||
vtxp->replaceWith(altp);
|
||||
if (srcp) altp->srcp(srcp);
|
||||
if (defaultp) altp->defaultp(defaultp);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -285,68 +290,53 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&
|
|||
|
||||
// Dump one DfgVertex in Graphviz format
|
||||
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||
|
||||
if (const DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
|
||||
if (const DfgVertexVar* const varVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
const AstNode* const nodep = varVtxp->nodep();
|
||||
const AstVar* const varp = varVtxp->varp();
|
||||
os << toDotId(vtx);
|
||||
os << " [label=\"" << nodep->prettyName() << '\n';
|
||||
os << cvtToHex(varVtxp) << '\n';
|
||||
// Begin attributes
|
||||
os << " [";
|
||||
// Begin 'label'
|
||||
os << "label=\"";
|
||||
// Name
|
||||
os << nodep->prettyName();
|
||||
// Address
|
||||
os << '\n' << cvtToHex(varVtxp);
|
||||
// Original variable, if any
|
||||
if (const AstNode* const tmpForp = varVtxp->tmpForp()) {
|
||||
os << "temporary for: " << tmpForp->prettyName() << "\n";
|
||||
if (tmpForp != nodep) os << "\ntemporary for: " << tmpForp->prettyName();
|
||||
}
|
||||
// Type and fanout
|
||||
os << '\n';
|
||||
varVtxp->dtypep()->dumpSmall(os);
|
||||
os << " / F" << varVtxp->fanout() << '"';
|
||||
|
||||
if (varp->direction() == VDirection::INPUT) {
|
||||
os << ", shape=box, style=filled, fillcolor=chartreuse2"; // Green
|
||||
} else if (varp->direction() == VDirection::OUTPUT) {
|
||||
os << ", shape=box, style=filled, fillcolor=cyan2"; // Cyan
|
||||
} else if (varp->direction() == VDirection::INOUT) {
|
||||
os << ", shape=box, style=filled, fillcolor=darkorchid2"; // Purple
|
||||
} else if (varVtxp->hasExtRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=firebrick2"; // Red
|
||||
} else if (varVtxp->hasModRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=darkorange1"; // Orange
|
||||
} else if (varVtxp->hasDfgRefs()) {
|
||||
os << ", shape=box, style=filled, fillcolor=gold2"; // Yellow
|
||||
} else if (varVtxp->tmpForp()) {
|
||||
os << ", shape=box, style=filled, fillcolor=gray80";
|
||||
} else {
|
||||
os << " / F" << varVtxp->fanout();
|
||||
// End 'label'
|
||||
os << '"';
|
||||
// Shape
|
||||
if (varVtxp->is<DfgVarPacked>()) {
|
||||
os << ", shape=box";
|
||||
}
|
||||
os << "]\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (const DfgVarArray* const arrVtxp = vtx.cast<DfgVarArray>()) {
|
||||
const AstNode* const nodep = arrVtxp->nodep();
|
||||
const AstVar* const varp = arrVtxp->varp();
|
||||
os << toDotId(vtx);
|
||||
os << " [label=\"" << nodep->prettyName() << '\n';
|
||||
os << cvtToHex(arrVtxp) << '\n';
|
||||
if (const AstNode* const tmpForp = arrVtxp->tmpForp()) {
|
||||
os << "temporary for: " << tmpForp->prettyName() << "\n";
|
||||
}
|
||||
arrVtxp->dtypep()->dumpSmall(os);
|
||||
os << " / F" << arrVtxp->fanout() << '"';
|
||||
if (varp->direction() == VDirection::INPUT) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=chartreuse2"; // Green
|
||||
} else if (varp->direction() == VDirection::OUTPUT) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=cyan2"; // Cyan
|
||||
} else if (varp->direction() == VDirection::INOUT) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=darkorchid2"; // Purple
|
||||
} else if (arrVtxp->hasExtRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=firebrick2"; // Red
|
||||
} else if (arrVtxp->hasModRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=darkorange1"; // Orange
|
||||
} else if (arrVtxp->hasDfgRefs()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=gold2"; // Yellow
|
||||
} else if (arrVtxp->tmpForp()) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=gray80";
|
||||
} else {
|
||||
} else if (varVtxp->is<DfgVarArray>()) {
|
||||
os << ", shape=box3d";
|
||||
} else {
|
||||
varVtxp->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE
|
||||
}
|
||||
// Color
|
||||
if (varp->direction() == VDirection::INPUT) {
|
||||
os << ", style=filled, fillcolor=chartreuse2"; // Green
|
||||
} else if (varp->direction() == VDirection::OUTPUT) {
|
||||
os << ", style=filled, fillcolor=cyan2"; // Cyan
|
||||
} else if (varp->direction() == VDirection::INOUT) {
|
||||
os << ", style=filled, fillcolor=darkorchid2"; // Purple
|
||||
} else if (varVtxp->hasExtRefs()) {
|
||||
os << ", style=filled, fillcolor=firebrick2"; // Red
|
||||
} else if (varVtxp->hasModRefs()) {
|
||||
os << ", style=filled, fillcolor=darkorange1"; // Orange
|
||||
} else if (varVtxp->hasDfgRefs()) {
|
||||
os << ", style=filled, fillcolor=gold2"; // Yellow
|
||||
} else if (varVtxp->tmpForp()) {
|
||||
os << ", style=filled, fillcolor=gray95";
|
||||
}
|
||||
// End attributes
|
||||
os << "]\n";
|
||||
return;
|
||||
}
|
||||
|
|
@ -405,8 +395,12 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
os << toDotId(vtx);
|
||||
std::stringstream ss;
|
||||
V3EmitV::debugVerilogForTree(logicp->nodep(), ss);
|
||||
std::string str = ss.str();
|
||||
str = VString::quoteBackslash(str);
|
||||
str = VString::quoteAny(str, '"', '\\');
|
||||
str = VString::replaceSubstr(str, "\n", "\\l");
|
||||
os << " [label=\"";
|
||||
os << VString::replaceSubstr(VString::replaceSubstr(ss.str(), "\n", "\\l"), "\"", "\\\"");
|
||||
os << str;
|
||||
os << "\\n" << cvtToHex(&vtx);
|
||||
os << "\"\n";
|
||||
os << ", shape=box, style=\"rounded,filled\", fillcolor=cornsilk, nojustify=true";
|
||||
|
|
@ -427,17 +421,6 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
os << "]\n";
|
||||
}
|
||||
|
||||
// Dump one DfgEdge in Graphviz format
|
||||
static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, size_t idx) {
|
||||
UASSERT(edge.sourcep(), "Can't dump unconnected DfgEdge");
|
||||
const DfgVertex& sink = *edge.sinkp(); // sink is never nullptr
|
||||
os << toDotId(*edge.sourcep()) << " -> " << toDotId(sink);
|
||||
if (sink.arity() > 1 || sink.is<DfgVertexSplice>()) {
|
||||
os << " [headlabel=\"" << sink.srcName(idx) << "\"]";
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
|
||||
void DfgGraph::dumpDot(std::ostream& os, const std::string& label,
|
||||
std::function<bool(const DfgVertex&)> p) const {
|
||||
// This generates a graphviz dump, https://www.graphviz.org
|
||||
|
|
@ -473,12 +456,16 @@ void DfgGraph::dumpDot(std::ostream& os, const std::string& label,
|
|||
dumpDotVertex(os, vtx);
|
||||
});
|
||||
// Emit all edges
|
||||
forEachVertex([&](const DfgVertex& vtx) { //
|
||||
forEachVertex([&](const DfgVertex& vtx) {
|
||||
if (!p(vtx)) return;
|
||||
vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { //
|
||||
if (!e.sourcep() || !p(*e.sourcep())) return;
|
||||
dumpDotEdge(os, e, i);
|
||||
});
|
||||
for (size_t i = 0; i < vtx.nInputs(); ++i) {
|
||||
DfgVertex* const srcp = vtx.inputp(i);
|
||||
if (!srcp) continue;
|
||||
if (!p(*srcp)) continue;
|
||||
os << toDotId(*srcp) << " -> " << toDotId(vtx);
|
||||
os << " [headlabel=\"" << vtx.srcName(i) << "\"]";
|
||||
os << '\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Footer
|
||||
|
|
@ -527,9 +514,15 @@ dfgGraphCollectCone(const std::vector<const DfgVertex*>& vtxps) {
|
|||
if (!resp->insert(vtxp).second) continue;
|
||||
// Enqueue all siblings of this vertex.
|
||||
if VL_CONSTEXPR_CXX17 (T_SinksNotSources) {
|
||||
vtxp->forEachSink([&](const DfgVertex& sink) { queue.push_back(&sink); });
|
||||
vtxp->foreachSink([&](const DfgVertex& sink) {
|
||||
queue.push_back(&sink);
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); });
|
||||
vtxp->foreachSource([&](const DfgVertex& src) {
|
||||
queue.push_back(&src);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
// Done
|
||||
|
|
@ -546,73 +539,8 @@ DfgGraph::sinkCone(const std::vector<const DfgVertex*>& vtxps) const {
|
|||
return dfgGraphCollectCone<true>(vtxps);
|
||||
}
|
||||
|
||||
// predicate for supported data types
|
||||
static bool dfgGraphIsSupportedDTypePacked(const AstNodeDType* dtypep) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) {
|
||||
return typep->keyword().isIntNumeric();
|
||||
}
|
||||
if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
|
||||
}
|
||||
if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
return typep->packed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DfgGraph::isSupported(const AstNodeDType* dtypep) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
// Support 1 dimensional unpacked arrays of packed types
|
||||
if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
|
||||
}
|
||||
// Support packed types
|
||||
return dfgGraphIsSupportedDTypePacked(dtypep);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgEdge
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void DfgEdge::unlinkSource() {
|
||||
if (!m_sourcep) return;
|
||||
#ifdef VL_DEBUG
|
||||
{
|
||||
DfgEdge* currp = m_sourcep->m_sinksp;
|
||||
while (currp) {
|
||||
if (currp == this) break;
|
||||
currp = currp->m_nextp;
|
||||
}
|
||||
UASSERT(currp, "'m_sourcep' does not have this edge as sink");
|
||||
}
|
||||
#endif
|
||||
// Relink pointers of predecessor and successor
|
||||
if (m_prevp) m_prevp->m_nextp = m_nextp;
|
||||
if (m_nextp) m_nextp->m_prevp = m_prevp;
|
||||
// If head of list in source, update source's head pointer
|
||||
if (m_sourcep->m_sinksp == this) m_sourcep->m_sinksp = m_nextp;
|
||||
// Mark source as unconnected
|
||||
m_sourcep = nullptr;
|
||||
// Clear links. This is not strictly necessary, but might catch bugs.
|
||||
m_prevp = nullptr;
|
||||
m_nextp = nullptr;
|
||||
}
|
||||
|
||||
void DfgEdge::relinkSource(DfgVertex* newSourcep) {
|
||||
// Unlink current source, if any
|
||||
unlinkSource();
|
||||
// Link new source
|
||||
m_sourcep = newSourcep;
|
||||
// Prepend to sink list in source
|
||||
m_nextp = newSourcep->m_sinksp;
|
||||
if (m_nextp) m_nextp->m_prevp = this;
|
||||
newSourcep->m_sinksp = this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVertex
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: m_filelinep{flp}
|
||||
|
|
@ -621,12 +549,6 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType*
|
|||
dfg.addVertex(*this);
|
||||
}
|
||||
|
||||
DfgVertex::~DfgVertex() {}
|
||||
|
||||
bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; }
|
||||
|
||||
V3Hash DfgVertex::selfHash() const { return V3Hash{}; }
|
||||
|
||||
bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
||||
// If same vertex, then equal
|
||||
if (this == &that) return true;
|
||||
|
|
@ -638,13 +560,7 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
|||
if (this->dtypep() != that.dtypep()) return false;
|
||||
|
||||
// If different number of inputs, then not equal
|
||||
auto thisPair = this->sourceEdges();
|
||||
const DfgEdge* const thisSrcEdgesp = thisPair.first;
|
||||
const size_t thisArity = thisPair.second;
|
||||
auto thatPair = that.sourceEdges();
|
||||
const DfgEdge* const thatSrcEdgesp = thatPair.first;
|
||||
const size_t thatArity = thatPair.second;
|
||||
if (thisArity != thatArity) return false;
|
||||
if (this->nInputs() != that.nInputs()) return false;
|
||||
|
||||
// Check vertex specifics
|
||||
if (!this->selfEquals(that)) return false;
|
||||
|
|
@ -655,16 +571,17 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
|||
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
|
||||
uint8_t& result = cache[key];
|
||||
if (!result) {
|
||||
result = 2; // Assume equals
|
||||
for (size_t i = 0; i < thisArity; ++i) {
|
||||
const DfgVertex* const thisSrcVtxp = thisSrcEdgesp[i].m_sourcep;
|
||||
const DfgVertex* const thatSrcVtxp = thatSrcEdgesp[i].m_sourcep;
|
||||
if (thisSrcVtxp == thatSrcVtxp) continue;
|
||||
if (!thisSrcVtxp || !thatSrcVtxp || !thisSrcVtxp->equals(*thatSrcVtxp, cache)) {
|
||||
result = 1; // Mark not equal
|
||||
break;
|
||||
const bool equal = [&]() {
|
||||
for (size_t i = 0; i < nInputs(); ++i) {
|
||||
const DfgVertex* const ap = this->inputp(i);
|
||||
const DfgVertex* const bp = that.inputp(i);
|
||||
if (!ap && !bp) continue;
|
||||
if (!ap || !bp) return false;
|
||||
if (!ap->equals(*bp, cache)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
result = (static_cast<uint8_t>(equal) << 1) | 1;
|
||||
}
|
||||
return result >> 1;
|
||||
}
|
||||
|
|
@ -678,17 +595,11 @@ V3Hash DfgVertex::hash() {
|
|||
// variables, which we rely on.
|
||||
if (!is<DfgVertexVar>()) {
|
||||
hash += m_type;
|
||||
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) {
|
||||
hash += adtypep->elementsConst();
|
||||
// TODO: maybe include sub-dtype, but not hugely important at the moment
|
||||
} else {
|
||||
hash += width();
|
||||
}
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t nEdges = pair.second;
|
||||
// Sources must always be connected in well-formed graphs
|
||||
for (size_t i = 0; i < nEdges; ++i) hash += edgesp[i].m_sourcep->hash();
|
||||
hash += size();
|
||||
foreachSource([&](DfgVertex& vtx) {
|
||||
hash += vtx.hash();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
result = hash;
|
||||
}
|
||||
|
|
@ -697,7 +608,10 @@ V3Hash DfgVertex::hash() {
|
|||
|
||||
uint32_t DfgVertex::fanout() const {
|
||||
uint32_t result = 0;
|
||||
forEachSinkEdge([&](const DfgEdge&) { ++result; });
|
||||
foreachSink([&](const DfgVertex&) {
|
||||
++result;
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -708,51 +622,52 @@ DfgVertexVar* DfgVertex::getResultVar() {
|
|||
// Inspect existing variables written by this vertex, and choose one
|
||||
DfgVertexVar* resp = nullptr;
|
||||
// cppcheck-has-bug-suppress constParameter
|
||||
this->forEachSink([&resp](DfgVertex& sink) {
|
||||
this->foreachSink([&resp](DfgVertex& sink) {
|
||||
DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
|
||||
if (!varp) return;
|
||||
if (!varp) return false;
|
||||
// First variable found
|
||||
if (!resp) {
|
||||
resp = varp;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer those variables that must be kept anyway
|
||||
if (resp->hasExtRefs() != varp->hasExtRefs()) {
|
||||
if (!resp->hasExtRefs()) resp = varp;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (resp->hasModWrRefs() != varp->hasModWrRefs()) {
|
||||
if (!resp->hasModWrRefs()) resp = varp;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (resp->hasDfgRefs() != varp->hasDfgRefs()) {
|
||||
if (!resp->hasDfgRefs()) resp = varp;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// Prefer those that already have module references
|
||||
if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
|
||||
if (!resp->hasModRdRefs()) resp = varp;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// Prefer real variabels over temporaries
|
||||
if (!resp->tmpForp() != !varp->tmpForp()) {
|
||||
if (resp->tmpForp()) resp = varp;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// 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;
|
||||
return false;
|
||||
}
|
||||
// Prefer the one with the lexically smaller name
|
||||
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
|
||||
if (cmp > 0) resp = varp;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// 'resp' and 'varp' are all the same, keep using the existing 'resp'
|
||||
return false;
|
||||
});
|
||||
return resp;
|
||||
}
|
||||
|
|
@ -777,14 +692,16 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
|
|||
resultr = reinterpret_cast<AstScope*>(1);
|
||||
// Find scope based on sources, falling back on the root scope
|
||||
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
|
||||
AstScope* foundp = rootp;
|
||||
const auto edges = sourceEdges();
|
||||
for (size_t i = 0; i < edges.second; ++i) {
|
||||
const DfgEdge& edge = edges.first[i];
|
||||
foundp = edge.sourcep()->scopep(cache, true);
|
||||
if (foundp != rootp) break;
|
||||
}
|
||||
resultr = foundp;
|
||||
AstScope* foundp = nullptr;
|
||||
foreachSource([&](DfgVertex& src) {
|
||||
AstScope* const scp = src.scopep(cache, true);
|
||||
if (scp != rootp) {
|
||||
foundp = scp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
resultr = foundp ? foundp : rootp;
|
||||
}
|
||||
|
||||
// Die on a graph circular through operation vertices
|
||||
|
|
@ -796,80 +713,15 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
|
|||
}
|
||||
|
||||
void DfgVertex::unlinkDelete(DfgGraph& dfg) {
|
||||
// Unlink source edges
|
||||
forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
|
||||
// Unlink sink edges
|
||||
forEachSinkEdge([](DfgEdge& edge) { edge.unlinkSource(); });
|
||||
while (!m_sinks.empty()) m_sinks.frontp()->unlinkSrcp();
|
||||
// Remove from graph
|
||||
dfg.removeVertex(*this);
|
||||
// Delete
|
||||
// Delete - this will unlink sources
|
||||
delete this;
|
||||
}
|
||||
|
||||
void DfgVertex::replaceWith(DfgVertex* newSorucep) {
|
||||
while (m_sinksp) m_sinksp->relinkSource(newSorucep);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Vertex classes
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// DfgConst ----------
|
||||
|
||||
bool DfgConst::selfEquals(const DfgVertex& that) const {
|
||||
return num().isCaseEq(that.as<DfgConst>()->num());
|
||||
}
|
||||
|
||||
V3Hash DfgConst::selfHash() const { return num().toHash(); }
|
||||
|
||||
// DfgSel ----------
|
||||
|
||||
bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as<DfgSel>()->lsb(); }
|
||||
|
||||
V3Hash DfgSel::selfHash() const { return V3Hash{lsb()}; }
|
||||
|
||||
// DfgVertexSplice ----------
|
||||
|
||||
bool DfgVertexSplice::selfEquals(const DfgVertex& that) const {
|
||||
const DfgVertexSplice* const thatp = that.as<DfgVertexSplice>();
|
||||
if (!defaultp() != !thatp->defaultp()) return false;
|
||||
const size_t arity = this->arity();
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (i == 0 && defaultp()) continue;
|
||||
if (driverLo(i) != thatp->driverLo(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
V3Hash DfgVertexSplice::selfHash() const {
|
||||
V3Hash hash;
|
||||
const size_t arity = this->arity();
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (i == 0 && defaultp()) continue;
|
||||
hash += driverLo(i);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// DfgVertexVar ----------
|
||||
|
||||
bool DfgVertexVar::selfEquals(const DfgVertex& that) const {
|
||||
UASSERT_OBJ(nodep()->type() == that.as<DfgVertexVar>()->nodep()->type(), this,
|
||||
"Both DfgVertexVar should be scoped or unscoped");
|
||||
UASSERT_OBJ(nodep() != that.as<DfgVertexVar>()->nodep(), this,
|
||||
"There should only be one DfgVertexVar for a given AstVar or AstVarScope");
|
||||
return false;
|
||||
}
|
||||
|
||||
V3Hash DfgVertexVar::selfHash() const {
|
||||
V3Hash hash;
|
||||
hash += nodep()->name();
|
||||
hash += varp()->varType();
|
||||
return hash;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVisitor
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include "V3Dfg__gen_visitor_defns.h" // From ./astgen
|
||||
|
|
|
|||
898
src/V3Dfg.h
898
src/V3Dfg.h
File diff suppressed because it is too large
Load Diff
|
|
@ -46,6 +46,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being built
|
||||
V3DfgAstToDfgContext& m_ctx; // The context for stats
|
||||
AstScope* m_scopep = nullptr; // The current scope, iff T_Scoped
|
||||
|
||||
// METHODS
|
||||
static Variable* getTarget(const AstVarRef* refp) {
|
||||
|
|
@ -104,7 +105,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
if (VN_IS(vrefp, VarXRef)) return true;
|
||||
if (vrefp->access().isReadOnly()) return false;
|
||||
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
|
||||
if (!DfgGraph::isSupported(varp)) return true;
|
||||
if (!V3Dfg::isSupported(varp)) return true;
|
||||
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
|
||||
return false;
|
||||
});
|
||||
|
|
@ -126,7 +127,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
if (VN_IS(vrefp, VarXRef)) return true;
|
||||
if (vrefp->access().isWriteOnly()) return false;
|
||||
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
|
||||
if (!DfgGraph::isSupported(varp)) return true;
|
||||
if (!V3Dfg::isSupported(varp)) return true;
|
||||
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
|
||||
return false;
|
||||
});
|
||||
|
|
@ -152,7 +153,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
|
||||
resp->reserve(varps->size());
|
||||
for (Variable* const varp : *varps) {
|
||||
if (!DfgGraph::isSupported(varp)) {
|
||||
if (!V3Dfg::isSupported(varp)) {
|
||||
++m_ctx.m_nonRepVar;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -188,7 +189,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherRead(nodep);
|
||||
if (!iVarpsp) return false;
|
||||
// Create the DfgLogic
|
||||
DfgLogic* const logicp = new DfgLogic{m_dfg, nodep};
|
||||
DfgLogic* const logicp = new DfgLogic{m_dfg, nodep, m_scopep};
|
||||
// Connect it up
|
||||
connect(*logicp, *iVarpsp, *oVarpsp);
|
||||
// Done
|
||||
|
|
@ -218,7 +219,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherLive(*cfgp);
|
||||
if (!iVarpsp) return false;
|
||||
// Create the DfgLogic
|
||||
DfgLogic* const logicp = new DfgLogic{m_dfg, nodep, std::move(cfgp)};
|
||||
DfgLogic* const logicp = new DfgLogic{m_dfg, nodep, m_scopep, std::move(cfgp)};
|
||||
// Connect it up
|
||||
connect(*logicp, *iVarpsp, *oVarpsp);
|
||||
// Done
|
||||
|
|
@ -241,7 +242,11 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); }
|
||||
void visit(AstScope* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_scopep);
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstActive* nodep) override {
|
||||
if (nodep->hasCombo()) {
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -268,6 +273,8 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
, m_ctx{ctx} {
|
||||
iterate(&root);
|
||||
}
|
||||
VL_UNCOPYABLE(AstToDfgVisitor);
|
||||
VL_UNMOVABLE(AstToDfgVisitor);
|
||||
|
||||
public:
|
||||
static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ class TraceDriver final : public DfgVisitor {
|
|||
const bool m_aggressive; // Trace aggressively, creating intermediate ops
|
||||
uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex
|
||||
uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex
|
||||
DfgVertex* m_defaultp = nullptr; // When tracing a variable, this is its 'defaultp', if any
|
||||
// Result of tracing the currently visited Vertex. Use SET_RESULT below!
|
||||
DfgVertex* m_resp = nullptr;
|
||||
std::vector<DfgVertex*> m_newVtxps; // New vertices created during the traversal
|
||||
|
|
@ -108,7 +109,7 @@ class TraceDriver final : public DfgVisitor {
|
|||
"Should only create Const, Sel, Concat, Exend Vertices without aggressive mode");
|
||||
|
||||
if VL_CONSTEXPR_CXX17 (std::is_same<DfgConst, Vertex>::value) {
|
||||
DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width};
|
||||
DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width, 0};
|
||||
vtxp->template setUser<uint64_t>(0);
|
||||
m_newVtxps.emplace_back(vtxp);
|
||||
return reinterpret_cast<Vertex*>(vtxp);
|
||||
|
|
@ -118,7 +119,7 @@ class TraceDriver final : public DfgVisitor {
|
|||
// Vertex is DfgConst, in which case this code is unreachable ...
|
||||
using Vtx = typename std::conditional<std::is_same<DfgConst, Vertex>::value, DfgSel,
|
||||
Vertex>::type;
|
||||
AstNodeDType* const dtypep = DfgGraph::dtypePacked(width);
|
||||
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
||||
Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep};
|
||||
vtxp->template setUser<uint64_t>(0);
|
||||
m_newVtxps.emplace_back(vtxp);
|
||||
|
|
@ -158,9 +159,13 @@ class TraceDriver final : public DfgVisitor {
|
|||
// Trace the vertex
|
||||
onStackr = true;
|
||||
|
||||
if (vtxp->getUser<uint64_t>() != m_component) {
|
||||
// If the currently traced vertex is in a different component,
|
||||
// then we found what we were looking for.
|
||||
// If the currently traced vertex is in a different component, then we
|
||||
// found what we were looking for. However, keep going past a splice,
|
||||
// or a unit as they cannot be use directly (they must always feed into
|
||||
// a variable, so we can't make them drive arbitrary logic)
|
||||
if (vtxp->getUser<uint64_t>() != m_component //
|
||||
&& !vtxp->is<DfgVertexSplice>() //
|
||||
&& !vtxp->is<DfgUnitArray>()) {
|
||||
if (msb != vtxp->width() - 1 || lsb != 0) {
|
||||
// Apply a Sel to extract the relevant bits if only a part is needed
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, msb - lsb + 1);
|
||||
|
|
@ -294,30 +299,25 @@ class TraceDriver final : public DfgVisitor {
|
|||
, m_msb{msb} {}
|
||||
};
|
||||
std::vector<Driver> drivers;
|
||||
DfgVertex* const defaultp = vtxp->defaultp();
|
||||
|
||||
// Look at all the drivers, one might cover the whole range, but also gathe all drivers
|
||||
const auto pair = vtxp->sourceEdges();
|
||||
bool tryWholeDefault = defaultp;
|
||||
for (size_t i = 0; i < pair.second; ++i) {
|
||||
DfgVertex* const srcp = pair.first[i].sourcep();
|
||||
if (srcp == defaultp) continue;
|
||||
|
||||
const uint32_t lsb = vtxp->driverLo(i);
|
||||
const uint32_t msb = lsb + srcp->width() - 1;
|
||||
drivers.emplace_back(srcp, lsb, msb);
|
||||
bool tryWholeDefault = m_defaultp;
|
||||
const bool done = vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
|
||||
const uint32_t msb = lsb + src.width() - 1;
|
||||
drivers.emplace_back(&src, lsb, msb);
|
||||
// Check if this driver covers any of the bits, then we can't use whole default
|
||||
if (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false;
|
||||
// If it does not cover the whole searched bit range, move on
|
||||
if (m_lsb < lsb || msb < m_msb) continue;
|
||||
if (m_lsb < lsb || msb < m_msb) return false;
|
||||
// Driver covers whole search range, trace that and we are done
|
||||
SET_RESULT(trace(srcp, m_msb - lsb, m_lsb - lsb));
|
||||
return;
|
||||
}
|
||||
SET_RESULT(trace(&src, m_msb - lsb, m_lsb - lsb));
|
||||
return true;
|
||||
});
|
||||
if (done) return;
|
||||
|
||||
// Trace the default driver if no other drivers cover the searched range
|
||||
if (defaultp && tryWholeDefault) {
|
||||
SET_RESULT(trace(defaultp, m_msb, m_lsb));
|
||||
if (tryWholeDefault) {
|
||||
SET_RESULT(trace(m_defaultp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -336,8 +336,8 @@ class TraceDriver final : public DfgVisitor {
|
|||
if (driver.m_lsb > m_msb) break;
|
||||
// Gap below this driver, trace default to fill it
|
||||
if (driver.m_lsb > m_lsb) {
|
||||
if (!defaultp) return;
|
||||
DfgVertex* const termp = trace(defaultp, driver.m_lsb - 1, m_lsb);
|
||||
if (!m_defaultp) return;
|
||||
DfgVertex* const termp = trace(m_defaultp, driver.m_lsb - 1, m_lsb);
|
||||
if (!termp) return;
|
||||
termps.emplace_back(termp);
|
||||
m_lsb = driver.m_lsb;
|
||||
|
|
@ -351,8 +351,8 @@ class TraceDriver final : public DfgVisitor {
|
|||
m_lsb = lim + 1;
|
||||
}
|
||||
if (m_msb >= m_lsb) {
|
||||
if (!defaultp) return;
|
||||
DfgVertex* const termp = trace(defaultp, m_msb, m_lsb);
|
||||
if (!m_defaultp) return;
|
||||
DfgVertex* const termp = trace(m_defaultp, m_msb, m_lsb);
|
||||
if (!termp) return;
|
||||
termps.emplace_back(termp);
|
||||
}
|
||||
|
|
@ -374,6 +374,8 @@ class TraceDriver final : public DfgVisitor {
|
|||
}
|
||||
|
||||
void visit(DfgVarPacked* vtxp) override {
|
||||
VL_RESTORER(m_defaultp);
|
||||
m_defaultp = vtxp->defaultp();
|
||||
if (DfgVertex* const srcp = vtxp->srcp()) {
|
||||
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
||||
return;
|
||||
|
|
@ -719,17 +721,30 @@ class IndependentBits final : public DfgVisitor {
|
|||
void visit(DfgSplicePacked* vtxp) override {
|
||||
// Combine the masks of all drivers
|
||||
V3Number& m = MASK(vtxp);
|
||||
DfgVertex* const defaultp = vtxp->defaultp();
|
||||
if (defaultp) m = MASK(defaultp);
|
||||
vtxp->forEachSourceEdge([&](DfgEdge& edge, size_t i) {
|
||||
const DfgVertex* const srcp = edge.sourcep();
|
||||
if (srcp == defaultp) return;
|
||||
m.opSelInto(MASK(srcp), vtxp->driverLo(i), srcp->width());
|
||||
vtxp->foreachDriver([&](DfgVertex& src, uint32_t lo) {
|
||||
m.opSelInto(MASK(&src), lo, src.width());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void visit(DfgVarPacked* vtxp) override {
|
||||
if (DfgVertex* const srcp = vtxp->srcp()) MASK(vtxp) = MASK(srcp);
|
||||
DfgVertex* const srcp = vtxp->srcp();
|
||||
if (srcp && srcp->is<DfgSpliceArray>()) return;
|
||||
|
||||
V3Number& m = MASK(vtxp);
|
||||
DfgVertex* const defaultp = vtxp->defaultp();
|
||||
if (defaultp) m = MASK(defaultp);
|
||||
if (!srcp) return;
|
||||
|
||||
if (DfgSplicePacked* const splicep = srcp->cast<DfgSplicePacked>()) {
|
||||
splicep->foreachDriver([&](DfgVertex& src, uint32_t lo) {
|
||||
m.opSelInto(MASK(&src), lo, src.width());
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
m = MASK(srcp);
|
||||
}
|
||||
|
||||
void visit(DfgArraySel* vtxp) override {
|
||||
|
|
@ -924,8 +939,9 @@ class IndependentBits final : public DfgVisitor {
|
|||
if (VN_IS(currp->dtypep(), UnpackArrayDType)) {
|
||||
// For an unpacked array vertex, just enque it's sinks.
|
||||
// (There can be no loops through arrays directly)
|
||||
currp->forEachSink([&](DfgVertex& vtx) {
|
||||
currp->foreachSink([&](DfgVertex& vtx) {
|
||||
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
||||
return false;
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
|
@ -940,8 +956,9 @@ class IndependentBits final : public DfgVisitor {
|
|||
|
||||
// If mask changed, enqueue sinks
|
||||
if (!prevMask.isCaseEq(maskCurr)) {
|
||||
currp->forEachSink([&](DfgVertex& vtx) {
|
||||
currp->foreachSink([&](DfgVertex& vtx) {
|
||||
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
||||
return false;
|
||||
});
|
||||
|
||||
// Check the mask only ever contrects (no bit goes 0 -> 1)
|
||||
|
|
@ -977,20 +994,21 @@ class FixUpSelDrivers final {
|
|||
static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||
size_t nImprovements = 0;
|
||||
const uint64_t component = vtxp->getUser<uint64_t>();
|
||||
vtxp->forEachSink([&](DfgVertex& sink) {
|
||||
vtxp->foreachSink([&](DfgVertex& sink) {
|
||||
// Ignore if sink is not part of same cycle
|
||||
if (sink.getUser<uint64_t>() != component) return;
|
||||
if (sink.getUser<uint64_t>() != component) return false;
|
||||
// Only handle Sel
|
||||
DfgSel* const selp = sink.cast<DfgSel>();
|
||||
if (!selp) return;
|
||||
if (!selp) return false;
|
||||
// Try to find the driver of the selected bits outside the cycle
|
||||
DfgVertex* const fixp
|
||||
= TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false);
|
||||
if (!fixp) return;
|
||||
if (!fixp) return false;
|
||||
// Found an out-of-cycle driver. We can replace this sel with that.
|
||||
selp->replaceWith(fixp);
|
||||
selp->unlinkDelete(dfg);
|
||||
++nImprovements;
|
||||
return false;
|
||||
});
|
||||
return nImprovements;
|
||||
}
|
||||
|
|
@ -998,25 +1016,25 @@ class FixUpSelDrivers final {
|
|||
static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||
size_t nImprovements = 0;
|
||||
const uint64_t component = vtxp->getUser<uint64_t>();
|
||||
vtxp->forEachSink([&](DfgVertex& sink) {
|
||||
vtxp->foreachSink([&](DfgVertex& sink) {
|
||||
// Ignore if sink is not part of same cycle
|
||||
if (sink.getUser<uint64_t>() != component) return;
|
||||
if (sink.getUser<uint64_t>() != component) return false;
|
||||
// Only handle ArraySels
|
||||
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
||||
if (!aselp) return;
|
||||
if (!aselp) return false;
|
||||
// First, try to fix up the whole word
|
||||
DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false);
|
||||
if (fixp) {
|
||||
if (DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false)) {
|
||||
// Found an out-of-cycle driver for the whole ArraySel
|
||||
aselp->replaceWith(fixp);
|
||||
aselp->unlinkDelete(dfg);
|
||||
++nImprovements;
|
||||
} else {
|
||||
// Attempt to fix up piece-wise at Sels applied to the ArraySel
|
||||
nImprovements += fixUpSelSinks(dfg, aselp);
|
||||
// Remove if became unused
|
||||
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
||||
return false;
|
||||
}
|
||||
// Attempt to fix up piece-wise at Sels applied to the ArraySel
|
||||
nImprovements += fixUpSelSinks(dfg, aselp);
|
||||
// Remove if became unused
|
||||
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
||||
return false;
|
||||
});
|
||||
return nImprovements;
|
||||
}
|
||||
|
|
@ -1045,16 +1063,17 @@ class FixUpIndependentRanges final {
|
|||
// Returns a bitmask set if that bit of 'vtxp' is used (has a sink)
|
||||
static V3Number computeUsedBits(DfgVertex* vtxp) {
|
||||
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
||||
vtxp->forEachSink([&result](DfgVertex& sink) {
|
||||
vtxp->foreachSink([&result](DfgVertex& sink) {
|
||||
// If used via a Sel, mark the selected bits used
|
||||
if (const DfgSel* const selp = sink.cast<DfgSel>()) {
|
||||
uint32_t lsb = selp->lsb();
|
||||
uint32_t msb = lsb + selp->width() - 1;
|
||||
for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '1');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// Used without a Sel, so all bits are used
|
||||
result.setAllBits1();
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1097,7 +1116,7 @@ class FixUpIndependentRanges final {
|
|||
}
|
||||
// Fall back on using the part of the variable (if dependent, or trace failed)
|
||||
if (!termp) {
|
||||
AstNodeDType* const dtypep = DfgGraph::dtypePacked(width);
|
||||
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
||||
DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep};
|
||||
// Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp'
|
||||
selp->setUser<uint64_t>(vtxp->getUser<uint64_t>());
|
||||
|
|
@ -1155,7 +1174,7 @@ class FixUpIndependentRanges final {
|
|||
nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb);
|
||||
} else {
|
||||
// The range is not used, just use constant 0 as a placeholder
|
||||
DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1};
|
||||
DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1, 0};
|
||||
constp->setUser<uint64_t>(0);
|
||||
termps.emplace_back(constp);
|
||||
}
|
||||
|
|
@ -1178,7 +1197,7 @@ class FixUpIndependentRanges final {
|
|||
for (size_t i = 1; i < termps.size(); ++i) {
|
||||
DfgVertex* const termp = termps[i];
|
||||
const uint32_t catWidth = replacementp->width() + termp->width();
|
||||
AstNodeDType* const dtypep = DfgGraph::dtypePacked(catWidth);
|
||||
AstNodeDType* const dtypep = V3Dfg::dtypePacked(catWidth);
|
||||
DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep};
|
||||
catp->rhsp(replacementp);
|
||||
catp->lhsp(termp);
|
||||
|
|
@ -1220,17 +1239,18 @@ public:
|
|||
} else if (varp->is<DfgVarArray>()) {
|
||||
// For array variables, fix up element-wise
|
||||
const uint64_t component = varp->getUser<uint64_t>();
|
||||
varp->forEachSink([&](DfgVertex& sink) {
|
||||
varp->foreachSink([&](DfgVertex& sink) {
|
||||
// Ignore if sink is not part of same cycle
|
||||
if (sink.getUser<uint64_t>() != component) return;
|
||||
if (sink.getUser<uint64_t>() != component) return false;
|
||||
// Only handle ArraySels with constant index
|
||||
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
||||
if (!aselp) return;
|
||||
if (!aselp->bitp()->is<DfgConst>()) return;
|
||||
if (!aselp) return false;
|
||||
if (!aselp->bitp()->is<DfgConst>()) return false;
|
||||
// Fix up the word
|
||||
nImprovements += fixUpPacked(dfg, aselp);
|
||||
// Remove if became unused
|
||||
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -230,19 +230,19 @@ inline void setOperands(DfgSel* vtxp, DfgVertex* fromp, uint32_t lsb) {
|
|||
}
|
||||
|
||||
inline void setOperands(DfgVertexUnary* vtxp, DfgVertex* src0p) { //
|
||||
vtxp->relinkSource<0>(src0p);
|
||||
vtxp->inputp(0, src0p);
|
||||
}
|
||||
|
||||
inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) {
|
||||
vtxp->relinkSource<0>(src0p);
|
||||
vtxp->relinkSource<1>(src1p);
|
||||
vtxp->inputp(0, src0p);
|
||||
vtxp->inputp(1, src1p);
|
||||
}
|
||||
|
||||
inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p,
|
||||
DfgVertex* src2p) {
|
||||
vtxp->relinkSource<0>(src0p);
|
||||
vtxp->relinkSource<1>(src1p);
|
||||
vtxp->relinkSource<2>(src2p);
|
||||
vtxp->inputp(0, src0p);
|
||||
vtxp->inputp(1, src1p);
|
||||
vtxp->inputp(2, src2p);
|
||||
}
|
||||
|
||||
// Get or create (and insert) vertex with given operands
|
||||
|
|
@ -265,19 +265,18 @@ inline void cache(CacheSel& cache, DfgSel* vtxp) {
|
|||
}
|
||||
|
||||
inline void cache(CacheUnary& cache, DfgVertexUnary* vtxp) {
|
||||
DfgVertexUnary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->source<0>());
|
||||
DfgVertexUnary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->inputp(0));
|
||||
if (!entrypr) entrypr = vtxp;
|
||||
}
|
||||
|
||||
inline void cache(CacheBinary& cache, DfgVertexBinary* vtxp) {
|
||||
DfgVertexBinary*& entrypr
|
||||
= getEntry(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>());
|
||||
DfgVertexBinary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1));
|
||||
if (!entrypr) entrypr = vtxp;
|
||||
}
|
||||
|
||||
inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) {
|
||||
DfgVertexTernary*& entrypr
|
||||
= getEntry(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>());
|
||||
= getEntry(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
|
||||
if (!entrypr) entrypr = vtxp;
|
||||
}
|
||||
|
||||
|
|
@ -288,18 +287,17 @@ inline void invalidateByValue(CacheSel& cache, const DfgSel* vtxp) {
|
|||
}
|
||||
|
||||
inline void invalidateByValue(CacheUnary& cache, const DfgVertexUnary* vtxp) {
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>());
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->inputp(0));
|
||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||
}
|
||||
|
||||
inline void invalidateByValue(CacheBinary& cache, const DfgVertexBinary* vtxp) {
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>());
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1));
|
||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||
}
|
||||
|
||||
inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) {
|
||||
const auto it
|
||||
= find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>());
|
||||
const auto it = find(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
|
||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class ColorStronglyConnectedComponents final {
|
|||
const size_t rootIndex = vtxState.index = ++m_index;
|
||||
|
||||
// Visit children
|
||||
vtx.forEachSink([&](DfgVertex& child) {
|
||||
vtx.foreachSink([&](DfgVertex& child) {
|
||||
VertexState& childSatate = child.user<VertexState>();
|
||||
// If the child has not yet been visited, then continue traversal
|
||||
if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate);
|
||||
|
|
@ -63,6 +63,7 @@ class ColorStronglyConnectedComponents final {
|
|||
if (childSatate.component == UNASSIGNED) {
|
||||
if (vtxState.index > childSatate.index) vtxState.index = childSatate.index;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (vtxState.index == rootIndex) {
|
||||
|
|
@ -73,7 +74,7 @@ class ColorStronglyConnectedComponents final {
|
|||
|| m_stack.back()->getUser<VertexState>().index < rootIndex;
|
||||
// We also need a separate component for vertices that drive themselves (which can
|
||||
// happen for input like 'assign a = a'), as we want to extract them (they are cyclic).
|
||||
const bool drivesSelf = vtx.findSink<DfgVertex>([&vtx](const DfgVertex& sink) { //
|
||||
const bool drivesSelf = vtx.foreachSink([&](const DfgVertex& sink) { //
|
||||
return &vtx == &sink;
|
||||
});
|
||||
|
||||
|
|
@ -107,7 +108,7 @@ class ColorStronglyConnectedComponents final {
|
|||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
VertexState& vtxState = vtx.user<VertexState>();
|
||||
// If it has no input or no outputs, it cannot be part of a non-trivial SCC.
|
||||
if (vtx.arity() == 0 || !vtx.hasSinks()) {
|
||||
if ((!vtx.srcp() && !vtx.defaultp()) || !vtx.hasSinks()) {
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0,
|
||||
&vtx, "Non circular variable must be in a trivial SCC"););
|
||||
vtxState.index = 0;
|
||||
|
|
|
|||
|
|
@ -65,8 +65,14 @@ class SplitIntoComponents final {
|
|||
item.user<size_t>() = m_componentCounter;
|
||||
|
||||
// Enqueue all sources and sinks of this vertex.
|
||||
item.forEachSource([&](DfgVertex& src) { queue.push_back(&src); });
|
||||
item.forEachSink([&](DfgVertex& dst) { queue.push_back(&dst); });
|
||||
item.foreachSource([&](DfgVertex& src) {
|
||||
queue.push_back(&src);
|
||||
return false;
|
||||
});
|
||||
item.foreachSink([&](DfgVertex& dst) {
|
||||
queue.push_back(&dst);
|
||||
return false;
|
||||
});
|
||||
} while (!queue.empty());
|
||||
|
||||
// Done with this component
|
||||
|
|
@ -118,21 +124,6 @@ std::vector<std::unique_ptr<DfgGraph>> DfgGraph::splitIntoComponents(const std::
|
|||
}
|
||||
|
||||
class ExtractCyclicComponents final {
|
||||
// TYPES
|
||||
// We reuse the DfgVertex::user state set by V3DfgPasses::colorStronglyConnectedComponents.
|
||||
// We sneak an extra flag into the MSB to indicate the vertex was merged already.
|
||||
class VertexState final {
|
||||
uint64_t& m_userr;
|
||||
|
||||
public:
|
||||
explicit VertexState(DfgVertex& vtx)
|
||||
: m_userr{vtx.getUser<uint64_t>()} {}
|
||||
bool merged() const { return m_userr >> 63; }
|
||||
void setMerged() { m_userr |= 1ULL << 63; }
|
||||
uint64_t component() const { return m_userr & ~(1ULL << 63); }
|
||||
void component(uint64_t value) { m_userr = (m_userr & (1ULL << 63)) | value; }
|
||||
};
|
||||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The input graph
|
||||
const std::string m_prefix; // Component name prefix
|
||||
|
|
@ -143,45 +134,86 @@ class ExtractCyclicComponents final {
|
|||
std::unordered_map<const DfgVertexVar*, std::unordered_map<uint64_t, DfgVertexVar*>> m_clones;
|
||||
|
||||
// METHODS
|
||||
void visitMergeSCCs(DfgVertex& vtx, uint64_t targetComponent) {
|
||||
VertexState vtxState{vtx};
|
||||
void addVertexAndExpandSiblings(DfgVertex& vtx, uint64_t component) {
|
||||
// Do not go past a variable, we will partition the graph there
|
||||
if (vtx.is<DfgVertexVar>()) return;
|
||||
// Don't need to recurse if the vertex is already in the same component,
|
||||
// it was either marked through an earlier traversal, in which case it
|
||||
// was processed recursively, or it will be processed later.
|
||||
if (vtx.getUser<uint64_t>() == component) return;
|
||||
// Because all cycles are through a variable, we can't reach another SCC.
|
||||
UASSERT_OBJ(!vtx.getUser<uint64_t>(), &vtx, "Cycle without variable involvement");
|
||||
// Put this vertex in the component, and continue recursively
|
||||
vtx.setUser<uint64_t>(component);
|
||||
expandSiblings(vtx, component);
|
||||
}
|
||||
|
||||
// Move on if already visited
|
||||
if (vtxState.merged()) return;
|
||||
|
||||
// Visiting vertex
|
||||
vtxState.setMerged();
|
||||
|
||||
// Assign vertex to the target component
|
||||
vtxState.component(targetComponent);
|
||||
|
||||
// Visit all neighbors. We stop at variable boundaries,
|
||||
// which is where we will split the graphs
|
||||
vtx.forEachSource([this, targetComponent](DfgVertex& other) {
|
||||
if (other.is<DfgVertexVar>()) return;
|
||||
visitMergeSCCs(other, targetComponent);
|
||||
void expandSiblings(DfgVertex& vtx, uint64_t component) {
|
||||
UASSERT_OBJ(vtx.getUser<uint64_t>() == component, &vtx, "Traversal didn't stop");
|
||||
vtx.foreachSink([&](DfgVertex& v) {
|
||||
addVertexAndExpandSiblings(v, component);
|
||||
return false;
|
||||
});
|
||||
vtx.forEachSink([this, targetComponent](DfgVertex& other) {
|
||||
if (other.is<DfgVertexVar>()) return;
|
||||
visitMergeSCCs(other, targetComponent);
|
||||
vtx.foreachSource([&](DfgVertex& v) {
|
||||
addVertexAndExpandSiblings(v, component);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void mergeSCCs() {
|
||||
// Ensure that component boundaries are always at variables, by merging SCCs. Merging stops
|
||||
// at variable boundaries, so we don't need to iterate variables. Constants are reachable
|
||||
// from their sinks, or are unused, so we don't need to iterate them either.
|
||||
void expandComponents() {
|
||||
// Important fact that we will assume below: There are no path between
|
||||
// any two SCCs that do not go through a variable before reaching the
|
||||
// destination SCC. That is, to get from one SCC to another, you must
|
||||
// go through a variable that is not part of the destination SCC. This
|
||||
// holds because no operation vertex can have multiple sinks at this
|
||||
// point (constants have no inputs, so they are not in an SCC).
|
||||
if (m_doExpensiveChecks) {
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
UASSERT_OBJ(!vtx.hasMultipleSinks(), &vtx, "Operation has multiple sinks");
|
||||
}
|
||||
}
|
||||
|
||||
// We will break the graph at variable boundaries, but we want both
|
||||
// 'srcp', and 'defaultp' to be in the same component, so for each
|
||||
// cyclic variable, put both its 'srcp' and 'defaultp' into the same
|
||||
// component if they are not variables themselves. The assertions below
|
||||
// must hold because of the assumption above.
|
||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
const uint64_t varComponent = vtx.getUser<uint64_t>();
|
||||
if (!varComponent) continue;
|
||||
if (DfgVertex* const srcp = vtx.srcp()) {
|
||||
if (!srcp->is<DfgVertexVar>()) {
|
||||
const uint64_t srcComponent = srcp->getUser<uint64_t>();
|
||||
UASSERT_OBJ(!srcComponent || srcComponent == varComponent, srcp,
|
||||
"Cycle through 'srcp' that does not go through variable.");
|
||||
srcp->setUser<uint64_t>(varComponent);
|
||||
}
|
||||
}
|
||||
if (DfgVertex* const defp = vtx.defaultp()) {
|
||||
if (!defp->is<DfgVertexVar>()) {
|
||||
const uint64_t defComponent = defp->getUser<uint64_t>();
|
||||
UASSERT_OBJ(!defComponent || defComponent == varComponent, defp,
|
||||
"Cycle through 'defaultp' that does not go through variable");
|
||||
defp->setUser<uint64_t>(varComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To ensure all component boundaries are at variables, expand
|
||||
// components to include all reachable non-variable vertices. Constants
|
||||
// are reachable from their sinks, so only need to process op vertices.
|
||||
// We do this by staring a DFS from each vertex that is part of an
|
||||
// component and add all reachable non-variable vertices to the same.
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Start DFS from each vertex that is in a non-trivial SCC, and merge everything
|
||||
// that is reachable from it into this component.
|
||||
if (const uint64_t target = VertexState{vtx}.component()) visitMergeSCCs(vtx, target);
|
||||
if (const uint64_t targetComponent = vtx.getUser<uint64_t>()) {
|
||||
expandSiblings(vtx, targetComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve clone of vertex in the given component
|
||||
DfgVertexVar& getClone(DfgVertexVar& vtx, uint64_t component) {
|
||||
UASSERT_OBJ(VertexState{vtx}.component() != component, &vtx,
|
||||
"Vertex is in that component");
|
||||
DfgVertexVar* getClone(DfgVertexVar& vtx, uint64_t component) {
|
||||
UASSERT_OBJ(vtx.getUser<uint64_t>() != component, &vtx, "Vertex is in that component");
|
||||
DfgVertexVar*& clonep = m_clones[&vtx][component];
|
||||
if (!clonep) {
|
||||
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
|
||||
|
|
@ -201,40 +233,52 @@ class ExtractCyclicComponents final {
|
|||
clonep->setUser<uint64_t>(component);
|
||||
clonep->tmpForp(vtx.tmpForp());
|
||||
}
|
||||
return *clonep;
|
||||
return clonep;
|
||||
}
|
||||
|
||||
// Fix edges that cross components
|
||||
void fixEdges(DfgVertexVar& vtx) {
|
||||
const uint64_t component = VertexState{vtx}.component();
|
||||
const uint64_t component = vtx.getUser<uint64_t>();
|
||||
|
||||
// Fix up sources in a different component
|
||||
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) {
|
||||
DfgVertex* const srcp = edge.sourcep();
|
||||
if (!srcp) return;
|
||||
const uint64_t sourceComponent = VertexState{*srcp}.component();
|
||||
// Same component is OK
|
||||
if (sourceComponent == component) return;
|
||||
// Relink the source to write the clone
|
||||
edge.unlinkSource();
|
||||
getClone(vtx, sourceComponent).srcp(srcp);
|
||||
// Fix up srcp and dstp (they must be the same component, or variable)
|
||||
if (DfgVertex* const sp = vtx.srcp()) {
|
||||
const uint64_t srcComponent = sp->getUser<uint64_t>();
|
||||
if (srcComponent != component) {
|
||||
UASSERT_OBJ(sp->is<DfgVertexVar>(), &vtx, "'srcp' in different component");
|
||||
getClone(vtx, srcComponent)->srcp(sp);
|
||||
vtx.srcp(nullptr);
|
||||
}
|
||||
}
|
||||
if (DfgVertex* const dp = vtx.defaultp()) {
|
||||
const uint64_t defaultComponent = dp->getUser<uint64_t>();
|
||||
if (defaultComponent != component) {
|
||||
UASSERT_OBJ(dp->is<DfgVertexVar>(), &vtx, "'defaultp' in different component");
|
||||
getClone(vtx, defaultComponent)->defaultp(dp);
|
||||
vtx.defaultp(nullptr);
|
||||
}
|
||||
}
|
||||
// Fix up sinks in a different component to read the clone
|
||||
std::vector<DfgVertex*> sinkps;
|
||||
vtx.foreachSink([&](DfgVertex& sink) {
|
||||
sinkps.emplace_back(&sink);
|
||||
return false;
|
||||
});
|
||||
|
||||
// Fix up sinks in a different component
|
||||
vtx.forEachSinkEdge([&](DfgEdge& edge) {
|
||||
const uint64_t sinkComponent = VertexState{*edge.sinkp()}.component();
|
||||
for (DfgVertex* const sinkp : sinkps) {
|
||||
const uint64_t sinkComponent = sinkp->getUser<uint64_t>();
|
||||
// Same component is OK
|
||||
if (sinkComponent == component) return;
|
||||
// Relink the sink to read the clone
|
||||
edge.relinkSource(&getClone(vtx, sinkComponent));
|
||||
});
|
||||
if (sinkComponent == component) continue;
|
||||
DfgVertex* const clonep = getClone(vtx, sinkComponent);
|
||||
for (size_t i = 0; i < sinkp->nInputs(); ++i) {
|
||||
if (sinkp->inputp(i) == &vtx) sinkp->inputp(i, clonep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
DfgVertex& vtx = *vtxp;
|
||||
if (const uint64_t component = VertexState{vtx}.component()) {
|
||||
if (const uint64_t component = vtx.getUser<uint64_t>()) {
|
||||
m_dfg.removeVertex(vtx);
|
||||
m_components[component - 1]->addVertex(vtx);
|
||||
}
|
||||
|
|
@ -242,20 +286,15 @@ class ExtractCyclicComponents final {
|
|||
}
|
||||
|
||||
void checkEdges(DfgGraph& dfg) const {
|
||||
// Check that:
|
||||
// - Edges only cross components at variable boundaries
|
||||
// - Variable vertex sources are all connected.
|
||||
// Check that edges only cross components at variable boundaries
|
||||
dfg.forEachVertex([&](DfgVertex& vtx) {
|
||||
const uint64_t component = VertexState{vtx}.component();
|
||||
vtx.forEachSource([&](DfgVertex& src) {
|
||||
if (src.is<DfgVertexVar>()) return; // OK to cross at variables
|
||||
UASSERT_OBJ(component == VertexState{src}.component(), &vtx,
|
||||
"Edge crossing components without variable involvement");
|
||||
});
|
||||
vtx.forEachSink([&](DfgVertex& snk) {
|
||||
if (snk.is<DfgVertexVar>()) return; // OK to cross at variables
|
||||
UASSERT_OBJ(component == VertexState{snk}.component(), &vtx,
|
||||
if (vtx.is<DfgVarPacked>()) return;
|
||||
const uint64_t component = vtx.getUser<uint64_t>();
|
||||
vtx.foreachSink([&](DfgVertex& snk) {
|
||||
if (snk.is<DfgVertexVar>()) return false; // OK to cross at variables
|
||||
UASSERT_OBJ(component == snk.getUser<uint64_t>(), &vtx,
|
||||
"Edge crossing components without variable involvement");
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -267,11 +306,13 @@ class ExtractCyclicComponents final {
|
|||
|
||||
// Check that each edge connects to a vertex that is within the same graph
|
||||
dfg.forEachVertex([&](const DfgVertex& vtx) {
|
||||
vtx.forEachSource([&](const DfgVertex& src) {
|
||||
vtx.foreachSource([&](const DfgVertex& src) {
|
||||
UASSERT_OBJ(vertices.count(&src), &vtx, "Source vertex not in graph");
|
||||
return false;
|
||||
});
|
||||
vtx.forEachSink([&](const DfgVertex& snk) {
|
||||
vtx.foreachSink([&](const DfgVertex& snk) {
|
||||
UASSERT_OBJ(vertices.count(&snk), &snk, "Sink vertex not in graph");
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -319,14 +360,13 @@ class ExtractCyclicComponents final {
|
|||
: m_dfg{dfg}
|
||||
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
|
||||
// DfgVertex::user<uint64_t> is set to the SCC number by colorStronglyConnectedComponents,
|
||||
// Then we use VertexState to handle the MSB as an extra flag.
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
// Find all the non-trivial SCCs (and trivial cycles) in the graph
|
||||
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg);
|
||||
// If the graph was acyclic (which should be the common case), then we are done.
|
||||
if (!numNonTrivialSCCs) return;
|
||||
// Ensure that component boundaries are always at variables, by merging SCCs
|
||||
mergeSCCs();
|
||||
// Ensure that component boundaries are always at variables, by expanding SCCs
|
||||
expandComponents();
|
||||
// Extract the components
|
||||
extractComponents(numNonTrivialSCCs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,32 +92,23 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
const VNUser2InUse m_user2InUse;
|
||||
|
||||
// TYPES
|
||||
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
||||
struct Assignment final {
|
||||
FileLine* m_flp;
|
||||
AstNodeExpr* m_lhsp;
|
||||
AstNodeExpr* m_rhsp;
|
||||
Assignment() = delete;
|
||||
Assignment(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* m_rhsp)
|
||||
: m_flp{flp}
|
||||
, m_lhsp{lhsp}
|
||||
, m_rhsp{m_rhsp} {}
|
||||
};
|
||||
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
||||
using Container = std::conditional_t<T_Scoped, AstActive, AstNodeModule>;
|
||||
|
||||
// STATE
|
||||
AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped
|
||||
V3DfgDfgToAstContext& m_ctx; // The context for stats
|
||||
AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal
|
||||
std::vector<Assignment> m_assignments; // Assignments to currently rendered variable
|
||||
std::vector<Assignment> m_defaults; // Default assignments to currently rendered variable
|
||||
AstAlways* m_alwaysp = nullptr; // Process to add assignments to, if have a default driver
|
||||
Container* m_containerp = nullptr; // The AstNodeModule or AstActive to insert assigns into
|
||||
|
||||
// METHODS
|
||||
|
||||
static VariableType* getNode(const DfgVertexVar* vtxp) {
|
||||
static Variable* getNode(const DfgVertexVar* vtxp) {
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
return reinterpret_cast<VariableType*>(vtxp->varScopep());
|
||||
return reinterpret_cast<Variable*>(vtxp->varScopep());
|
||||
} else {
|
||||
return reinterpret_cast<VariableType*>(vtxp->varp());
|
||||
return reinterpret_cast<Variable*>(vtxp->varp());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -158,49 +149,57 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
return resultp;
|
||||
}
|
||||
|
||||
void convertDriver(std::vector<Assignment>& assignments, FileLine* flp, AstNodeExpr* lhsp,
|
||||
DfgVertex* driverp) {
|
||||
void createAssignment(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
|
||||
// Keep track of statisticss
|
||||
++m_ctx.m_resultEquations;
|
||||
// Render the driver
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp);
|
||||
// Update LHS locations to reflect the location of the original driver
|
||||
lhsp->foreach([&](AstNode* nodep) { nodep->fileline(flp); });
|
||||
|
||||
// If using a process, add Assign there
|
||||
if (m_alwaysp) {
|
||||
m_alwaysp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise create an AssignW
|
||||
m_containerp->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
|
||||
}
|
||||
|
||||
void convertDriver(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
|
||||
if (DfgSplicePacked* const sPackedp = driverp->cast<DfgSplicePacked>()) {
|
||||
// Render defaults first
|
||||
if (DfgVertex* const defaultp = sPackedp->defaultp()) {
|
||||
convertDriver(m_defaults, flp, lhsp, defaultp);
|
||||
}
|
||||
// Render partial assignments of packed value
|
||||
sPackedp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||
DfgVertex* const srcp = edge.sourcep();
|
||||
if (srcp == sPackedp->defaultp()) return;
|
||||
// Partial assignment of packed value
|
||||
sPackedp->foreachDriver([&](DfgVertex& src, uint32_t lo, FileLine* dflp) {
|
||||
// Create Sel
|
||||
FileLine* const dflp = sPackedp->driverFileLine(i);
|
||||
AstConst* const lsbp = new AstConst{dflp, sPackedp->driverLo(i)};
|
||||
const int width = static_cast<int>(srcp->width());
|
||||
AstConst* const lsbp = new AstConst{dflp, lo};
|
||||
const int width = static_cast<int>(src.width());
|
||||
AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width};
|
||||
// Convert source
|
||||
convertDriver(assignments, dflp, nLhsp, srcp);
|
||||
convertDriver(dflp, nLhsp, &src);
|
||||
// Delete Sel - was cloned
|
||||
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (DfgSpliceArray* const sArrayp = driverp->cast<DfgSpliceArray>()) {
|
||||
UASSERT_OBJ(!sArrayp->defaultp(), flp, "Should not have a default assignment yet");
|
||||
// Partial assignment of array variable
|
||||
sArrayp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||
DfgVertex* const driverp = edge.sourcep();
|
||||
UASSERT_OBJ(driverp, sArrayp, "Should have removed undriven sources");
|
||||
UASSERT_OBJ(driverp->size() == 1, driverp, "We only handle single elements");
|
||||
sArrayp->foreachDriver([&](DfgVertex& src, uint32_t lo, FileLine* dflp) {
|
||||
UASSERT_OBJ(src.size() == 1, &src, "We only handle single elements");
|
||||
// Create ArraySel
|
||||
FileLine* const dflp = sArrayp->driverFileLine(i);
|
||||
AstConst* const idxp = new AstConst{dflp, sArrayp->driverLo(i)};
|
||||
AstConst* const idxp = new AstConst{dflp, lo};
|
||||
AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp};
|
||||
// Convert source
|
||||
if (const DfgUnitArray* const uap = driverp->cast<DfgUnitArray>()) {
|
||||
convertDriver(assignments, dflp, nLhsp, uap->srcp());
|
||||
if (const DfgUnitArray* const uap = src.cast<DfgUnitArray>()) {
|
||||
convertDriver(dflp, nLhsp, uap->srcp());
|
||||
} else {
|
||||
convertDriver(assignments, dflp, nLhsp, driverp);
|
||||
convertDriver(dflp, nLhsp, &src);
|
||||
}
|
||||
// Delete ArraySel - was cloned
|
||||
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -210,17 +209,14 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
AstConst* const idxp = new AstConst{flp, 0};
|
||||
AstArraySel* const nLhsp = new AstArraySel{flp, lhsp->cloneTreePure(false), idxp};
|
||||
// Convert source
|
||||
convertDriver(assignments, flp, nLhsp, uap->srcp());
|
||||
convertDriver(flp, nLhsp, uap->srcp());
|
||||
// Delete ArraySel - was cloned
|
||||
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Base case: assign vertex to current lhs
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp);
|
||||
assignments.emplace_back(flp, lhsp->cloneTreePure(false), rhsp);
|
||||
++m_ctx.m_resultEquations;
|
||||
return;
|
||||
createAssignment(flp, lhsp->cloneTreePure(false), driverp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
|
@ -258,7 +254,7 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
#include "V3Dfg__gen_dfg_to_ast.h"
|
||||
|
||||
// Constructor
|
||||
explicit DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx)
|
||||
DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx)
|
||||
: m_modp{dfg.modulep()}
|
||||
, m_ctx{ctx} {
|
||||
// Convert the graph back to combinational assignments
|
||||
|
|
@ -266,55 +262,42 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
// The graph must have been regularized, so we only need to render assignments
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
||||
if (!vtx.srcp()) continue;
|
||||
if (!vtx.srcp()) {
|
||||
UASSERT_OBJ(!vtx.defaultp(), &vtx, "Only default driver on variable");
|
||||
continue;
|
||||
}
|
||||
|
||||
++m_ctx.m_outputVariables;
|
||||
|
||||
// Render variable assignments
|
||||
FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline();
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, getNode(&vtx), VAccess::WRITE};
|
||||
convertDriver(m_assignments, flp, lhsp, vtx.srcp());
|
||||
|
||||
VL_RESTORER(m_containerp);
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
// Add it to the scope holding the target variable
|
||||
AstActive* const activep = getCombActive(vtx.varScopep()->scopep());
|
||||
m_containerp = reinterpret_cast<Container*>(activep);
|
||||
} else {
|
||||
// Add it to the parent module of the DfgGraph
|
||||
m_containerp = reinterpret_cast<Container*>(m_modp);
|
||||
}
|
||||
|
||||
// If there is a default value, render all drivers under an AstAlways
|
||||
VL_RESTORER(m_alwaysp);
|
||||
if (DfgVertex* const defaultp = vtx.defaultp()) {
|
||||
++m_ctx.m_outputVariablesWithDefault;
|
||||
m_alwaysp = new AstAlways{vtx.fileline(), VAlwaysKwd::ALWAYS_COMB, nullptr};
|
||||
m_containerp->addStmtsp(m_alwaysp);
|
||||
// The default assignment needs to go first
|
||||
createAssignment(vtx.fileline(), lhsp->cloneTreePure(false), defaultp);
|
||||
}
|
||||
|
||||
// Render the drivers
|
||||
convertDriver(flp, lhsp, vtx.srcp());
|
||||
|
||||
// convetDriver always clones lhsp
|
||||
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
|
||||
|
||||
if (m_defaults.empty()) {
|
||||
// If there are no default assignments, render each driver as an AssignW
|
||||
for (const Assignment& a : m_assignments) {
|
||||
AstAssignW* const assignp = new AstAssignW{a.m_flp, a.m_lhsp, a.m_rhsp};
|
||||
a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); });
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
// Add it to the scope holding the target variable
|
||||
getCombActive(vtx.varScopep()->scopep())->addStmtsp(assignp);
|
||||
} else {
|
||||
// Add it to the parent module of the DfgGraph
|
||||
m_modp->addStmtsp(assignp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++m_ctx.m_outputVariablesWithDefault;
|
||||
// If there are default assignments, render all drivers under an AstAlways
|
||||
AstAlways* const alwaysp
|
||||
= new AstAlways{vtx.fileline(), VAlwaysKwd::ALWAYS_COMB, nullptr, nullptr};
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
// Add it to the scope holding the target variable
|
||||
getCombActive(vtx.varScopep()->scopep())->addStmtsp(alwaysp);
|
||||
} else {
|
||||
// Add it to the parent module of the DfgGraph
|
||||
m_modp->addStmtsp(alwaysp);
|
||||
}
|
||||
for (const Assignment& a : m_defaults) {
|
||||
AstAssign* const assignp = new AstAssign{a.m_flp, a.m_lhsp, a.m_rhsp};
|
||||
a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); });
|
||||
alwaysp->addStmtsp(assignp);
|
||||
}
|
||||
for (const Assignment& a : m_assignments) {
|
||||
AstAssign* const assignp = new AstAssign{a.m_flp, a.m_lhsp, a.m_rhsp};
|
||||
a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); });
|
||||
alwaysp->addStmtsp(assignp);
|
||||
}
|
||||
}
|
||||
m_assignments.clear();
|
||||
m_defaults.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ class DataflowOptimize final {
|
|||
|
||||
// Mark interfaces that might be referenced by a virtual interface
|
||||
if (v3Global.hasVirtIfaces()) {
|
||||
netlistp->typeTablep()->foreach([](AstIfaceRefDType* nodep) {
|
||||
netlistp->typeTablep()->foreach([](const AstIfaceRefDType* nodep) {
|
||||
if (!nodep->isVirtual()) return;
|
||||
nodep->ifaceViaCellp()->setHasVirtualRef();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -84,8 +84,7 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
|||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) {
|
||||
if (varp->hasSinks() && varp->isDrivenFullyByDfg()) {
|
||||
DfgVertex* const driverp = varp->srcp();
|
||||
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
|
||||
varp->replaceWith(varp->srcp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -148,16 +147,17 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
if (varp->hasDfgRefs()) continue;
|
||||
}
|
||||
// Add sources of unused vertex to work list
|
||||
vtxp->forEachSource([&](DfgVertex& src) {
|
||||
vtxp->foreachSource([&](DfgVertex& src) {
|
||||
// We only remove actual operation vertices and synthesis temporaries in this loop
|
||||
if (src.is<DfgConst>()) return;
|
||||
if (src.is<DfgConst>()) return false;
|
||||
const DfgVertexVar* const varp = src.cast<DfgVertexVar>();
|
||||
if (varp && !varp->tmpForp()) return;
|
||||
if (varp && !varp->tmpForp()) return false;
|
||||
// If already in work list then nothing to do
|
||||
if (src.getUser<DfgVertex*>()) return;
|
||||
if (src.getUser<DfgVertex*>()) return false;
|
||||
// Actually add to work list.
|
||||
src.setUser<DfgVertex*>(workListp);
|
||||
workListp = &src;
|
||||
return false;
|
||||
});
|
||||
// Remove the unused vertex
|
||||
vtxp->unlinkDelete(dfg);
|
||||
|
|
@ -214,10 +214,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
if (DfgVertex* const sinkp = vtxp->singleSink()) {
|
||||
if (sinkp->is<DfgNot>()) return useOk(sinkp, !inv);
|
||||
}
|
||||
return !vtxp->findSink<DfgCond>([vtxp, inv](const DfgCond& sink) {
|
||||
if (sink.condp() != vtxp) return false;
|
||||
return inv ? sink.thenp()->is<DfgCond>() : sink.elsep()->is<DfgCond>();
|
||||
const bool condTree = vtxp->foreachSink([&](const DfgVertex& sink) {
|
||||
const DfgCond* const condp = sink.cast<DfgCond>();
|
||||
if (!condp) return false;
|
||||
if (condp->condp() != vtxp) return false;
|
||||
return inv ? condp->thenp()->is<DfgCond>() : condp->elsep()->is<DfgCond>();
|
||||
});
|
||||
return !condTree;
|
||||
};
|
||||
|
||||
// Look at all comparison nodes and build the 'Val2Terms' map for each source vertex
|
||||
|
|
@ -306,7 +309,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
|
||||
// Required data types
|
||||
AstNodeDType* const idxDTypep = srcp->dtypep();
|
||||
AstNodeDType* const bitDTypep = DfgGraph::dtypePacked(1);
|
||||
AstNodeDType* const bitDTypep = V3Dfg::dtypePacked(1);
|
||||
AstUnpackArrayDType* const tabDTypep = new AstUnpackArrayDType{
|
||||
flp, bitDTypep, new AstRange{flp, static_cast<int>(nBits - 1), 0}};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(tabDTypep);
|
||||
|
|
@ -438,10 +441,11 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
const auto addToWorkList = [&](DfgVertex& vtx) {
|
||||
// If already in work list then nothing to do
|
||||
DfgVertex*& nextInWorklistp = vtx.user<DfgVertex*>();
|
||||
if (nextInWorklistp) return;
|
||||
if (nextInWorklistp) return false;
|
||||
// Actually add to work list.
|
||||
nextInWorklistp = workListp;
|
||||
workListp = &vtx;
|
||||
return false;
|
||||
};
|
||||
|
||||
// List of variables (AstVar or AstVarScope) we are replacing
|
||||
|
|
@ -467,7 +471,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
// Remove unused non-variable vertices
|
||||
if (!vtxp->is<DfgVertexVar>() && !vtxp->hasSinks()) {
|
||||
// Add sources of removed vertex to work list
|
||||
vtxp->forEachSource(addToWorkList);
|
||||
vtxp->foreachSource(addToWorkList);
|
||||
// Remove the unused vertex
|
||||
vtxp->unlinkDelete(dfg);
|
||||
continue;
|
||||
|
|
@ -517,7 +521,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
}
|
||||
|
||||
// Add sources of redundant variable to the work list
|
||||
vtxp->forEachSource(addToWorkList);
|
||||
vtxp->foreachSource(addToWorkList);
|
||||
// Remove the redundant variable
|
||||
vtxp->unlinkDelete(dfg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,9 +118,10 @@ class V3DfgPatternStats final {
|
|||
}
|
||||
|
||||
// Operands
|
||||
vtx.forEachSource([&](const DfgVertex& src) {
|
||||
vtx.foreachSource([&](const DfgVertex& src) {
|
||||
ss << ' ';
|
||||
if (render(ss, src, depth - 1)) deep = true;
|
||||
return false;
|
||||
});
|
||||
// S-expression end
|
||||
ss << ')';
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
// STATE
|
||||
DfgGraph& m_dfg; // The DfgGraph being visited
|
||||
V3DfgPeepholeContext& m_ctx; // The config structure
|
||||
AstNodeDType* const m_bitDType = DfgGraph::dtypePacked(1); // Common, so grab it up front
|
||||
AstNodeDType* const m_bitDType = V3Dfg::dtypePacked(1); // Common, so grab it up front
|
||||
// Head of work list. Note that we want all next pointers in the list to be non-zero (including
|
||||
// that of the last element). This allows as to do two important things: detect if an element
|
||||
// is in the list by checking for a non-zero next pointer, and easy prefetching without
|
||||
|
|
@ -163,11 +163,17 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
void addSourcesToWorkList(DfgVertex* vtxp) {
|
||||
vtxp->forEachSource([&](DfgVertex& src) { addToWorkList(&src); });
|
||||
vtxp->foreachSource([&](DfgVertex& src) {
|
||||
addToWorkList(&src);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void addSinksToWorkList(DfgVertex* vtxp) {
|
||||
vtxp->forEachSink([&](DfgVertex& src) { addToWorkList(&src); });
|
||||
vtxp->foreachSink([&](DfgVertex& src) {
|
||||
addToWorkList(&src);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void deleteVertex(DfgVertex* vtxp) {
|
||||
|
|
@ -179,8 +185,6 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
// Otherwise we can delete it now.
|
||||
// Remove from cache
|
||||
m_cache.invalidateByValue(vtxp);
|
||||
// Unlink source edges
|
||||
vtxp->forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
|
||||
// Should not have sinks
|
||||
UASSERT_OBJ(!vtxp->hasSinks(), vtxp, "Should not delete used vertex");
|
||||
//
|
||||
|
|
@ -197,21 +201,29 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
// Add replacement to the work list
|
||||
addToWorkList(replacementp);
|
||||
// Replace vertex with the replacement
|
||||
vtxp->forEachSink([&](DfgVertex& sink) { m_cache.invalidateByValue(&sink); });
|
||||
vtxp->foreachSink([&](DfgVertex& sink) {
|
||||
m_cache.invalidateByValue(&sink);
|
||||
return false;
|
||||
});
|
||||
vtxp->replaceWith(replacementp);
|
||||
replacementp->forEachSink([&](DfgVertex& sink) { m_cache.cache(&sink); });
|
||||
replacementp->foreachSink([&](DfgVertex& sink) {
|
||||
m_cache.cache(&sink);
|
||||
return false;
|
||||
});
|
||||
// Vertex is now unused, so delete it
|
||||
deleteVertex(vtxp);
|
||||
}
|
||||
|
||||
// Shorthand
|
||||
static AstNodeDType* dtypePacked(uint32_t width) { return DfgGraph::dtypePacked(width); }
|
||||
static AstNodeDType* dtypePacked(uint32_t width) { return V3Dfg::dtypePacked(width); }
|
||||
|
||||
// Create a 32-bit DfgConst vertex
|
||||
DfgConst* makeI32(FileLine* flp, uint32_t val) { return new DfgConst{m_dfg, flp, 32, val}; }
|
||||
|
||||
// Create a DfgConst vertex with the given width and value zero
|
||||
DfgConst* makeZero(FileLine* flp, uint32_t width) { return new DfgConst{m_dfg, flp, width}; }
|
||||
DfgConst* makeZero(FileLine* flp, uint32_t width) {
|
||||
return new DfgConst{m_dfg, flp, width, 0};
|
||||
}
|
||||
|
||||
// Create a new vertex of the given type
|
||||
template <typename Vertex, typename... Operands>
|
||||
|
|
@ -246,6 +258,16 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
return aConstp->num().isCaseEq(bConstp->num());
|
||||
}
|
||||
|
||||
static bool isZero(const DfgVertex* vtxp) {
|
||||
if (const DfgConst* const constp = vtxp->cast<DfgConst>()) return constp->isZero();
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isOnes(const DfgVertex* vtxp) {
|
||||
if (const DfgConst* const constp = vtxp->cast<DfgConst>()) return constp->isOnes();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: If any of the following transformers return true, then the vertex was replaced and the
|
||||
// caller must not do any further changes, so the caller must check the return value, otherwise
|
||||
// there will be hard to debug issues.
|
||||
|
|
@ -271,8 +293,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
VL_ATTR_WARN_UNUSED_RESULT bool foldBinary(Vertex* vtxp) {
|
||||
static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value, "Must invoke on binary");
|
||||
static_assert(std::is_final<Vertex>::value, "Must invoke on final class");
|
||||
if (DfgConst* const lhsp = vtxp->lhsp()->template cast<DfgConst>()) {
|
||||
if (DfgConst* const rhsp = vtxp->rhsp()->template cast<DfgConst>()) {
|
||||
if (DfgConst* const lhsp = vtxp->inputp(0)->template cast<DfgConst>()) {
|
||||
if (DfgConst* const rhsp = vtxp->inputp(1)->template cast<DfgConst>()) {
|
||||
APPLYING(FOLD_BINARY) {
|
||||
DfgConst* const resultp = makeZero(vtxp->fileline(), vtxp->width());
|
||||
foldOp<Vertex>(resultp->num(), lhsp->num(), rhsp->num());
|
||||
|
|
@ -642,7 +664,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
static_assert(std::is_base_of<DfgVertexBinary, Shift>::value, "Must invoke on binary");
|
||||
static_assert(std::is_final<Shift>::value, "Must invoke on final class");
|
||||
if (const DfgConcat* const concatp = vtxp->rhsp()->template cast<DfgConcat>()) {
|
||||
if (concatp->lhsp()->isZero()) { // Drop redundant zero extension
|
||||
if (isZero(concatp->lhsp())) { // Drop redundant zero extension
|
||||
APPLYING(REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT) {
|
||||
Shift* const replacementp = make<Shift>(vtxp, vtxp->lhsp(), concatp->rhsp());
|
||||
replace(vtxp, replacementp);
|
||||
|
|
@ -919,27 +941,25 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
// Must be a splice, otherwise it would have been inlined
|
||||
DfgSplicePacked* const splicep = varp->srcp()->as<DfgSplicePacked>();
|
||||
|
||||
const auto pair = splicep->sourceEdges();
|
||||
for (size_t i = 0; i < pair.second; ++i) {
|
||||
DfgVertex* const driverp = pair.first[i].sourcep();
|
||||
// Ignore default, we won't select into that for now ...
|
||||
if (driverp == splicep->defaultp()) continue;
|
||||
|
||||
const uint32_t dLsb = splicep->driverLo(i);
|
||||
const uint32_t dMsb = dLsb + driverp->width() - 1;
|
||||
DfgSel* replacementp = nullptr;
|
||||
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLsb) {
|
||||
const uint32_t dMsb = dLsb + src.width() - 1;
|
||||
// If it does not cover the whole searched bit range, move on
|
||||
if (lsb < dLsb || dMsb < msb) continue;
|
||||
if (lsb < dLsb || dMsb < msb) return false;
|
||||
// Replace with sel from driver
|
||||
replacementp = make<DfgSel>(vtxp, &src, lsb - dLsb);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (replacementp) {
|
||||
// Replace with sel from driver
|
||||
APPLYING(PUSH_SEL_THROUGH_SPLICE) {
|
||||
DfgSel* const replacementp = make<DfgSel>(vtxp, driverp, lsb - dLsb);
|
||||
replace(vtxp, replacementp);
|
||||
// Special case just for this pattern: delete temporary if became unsued
|
||||
if (!varp->hasSinks() && !varp->hasDfgRefs()) {
|
||||
addToWorkList(splicep); // So it can be delete itself if unused
|
||||
VL_DO_DANGLING(varp->unlinkDelete(m_dfg), varp); // Delete it
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1086,7 +1106,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
if (DfgConcat* const lhsConcatp = lhsp->cast<DfgConcat>()) {
|
||||
if (DfgConcat* const rhsConcatp = rhsp->cast<DfgConcat>()) {
|
||||
if (lhsConcatp->lhsp()->dtypep() == rhsConcatp->lhsp()->dtypep()) {
|
||||
if (lhsConcatp->lhsp()->isZero() && rhsConcatp->rhsp()->isZero()) {
|
||||
if (isZero(lhsConcatp->lhsp()) && isZero(rhsConcatp->rhsp())) {
|
||||
APPLYING(REPLACE_OR_OF_CONCAT_ZERO_LHS_AND_CONCAT_RHS_ZERO) {
|
||||
DfgConcat* const replacementp
|
||||
= make<DfgConcat>(vtxp, rhsConcatp->lhsp(), lhsConcatp->rhsp());
|
||||
|
|
@ -1094,7 +1114,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (lhsConcatp->rhsp()->isZero() && rhsConcatp->lhsp()->isZero()) {
|
||||
if (isZero(lhsConcatp->rhsp()) && isZero(rhsConcatp->lhsp())) {
|
||||
APPLYING(REPLACE_OR_OF_CONCAT_LHS_ZERO_AND_CONCAT_ZERO_RHS) {
|
||||
DfgConcat* const replacementp
|
||||
= make<DfgConcat>(vtxp, lhsConcatp->lhsp(), rhsConcatp->rhsp());
|
||||
|
|
@ -1261,7 +1281,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
|
||||
if (lhsp->isZero()) {
|
||||
if (isZero(lhsp)) {
|
||||
DfgConst* const lConstp = lhsp->as<DfgConst>();
|
||||
if (DfgSel* const rSelp = rhsp->cast<DfgSel>()) {
|
||||
if (vtxp->dtypep() == rSelp->fromp()->dtypep()
|
||||
|
|
@ -1276,7 +1296,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
if (rhsp->isZero()) {
|
||||
if (isZero(rhsp)) {
|
||||
DfgConst* const rConstp = rhsp->as<DfgConst>();
|
||||
if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) {
|
||||
if (vtxp->dtypep() == lSelp->fromp()->dtypep() && lSelp->lsb() == 0) {
|
||||
|
|
@ -1561,14 +1581,14 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
if (condp->dtypep() != m_bitDType) return;
|
||||
|
||||
if (condp->isOnes()) {
|
||||
if (isOnes(condp)) {
|
||||
APPLYING(REMOVE_COND_WITH_TRUE_CONDITION) {
|
||||
replace(vtxp, thenp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (condp->isZero()) {
|
||||
if (isZero(condp)) {
|
||||
APPLYING(REMOVE_COND_WITH_FALSE_CONDITION) {
|
||||
replace(vtxp, elsep);
|
||||
return;
|
||||
|
|
@ -1689,7 +1709,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
if (vtxp->dtypep() == m_bitDType) {
|
||||
if (thenp->isZero()) { // a ? 0 : b becomes ~a & b
|
||||
if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
|
||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
|
||||
DfgNot* const notp = make<DfgNot>(vtxp, condp);
|
||||
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, notp, elsep);
|
||||
|
|
@ -1704,21 +1724,21 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (thenp->isOnes()) { // a ? 1 : b becomes a | b
|
||||
if (isOnes(thenp)) { // a ? 1 : b becomes a | b
|
||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
|
||||
DfgOr* const repalcementp = make<DfgOr>(vtxp, condp, elsep);
|
||||
replace(vtxp, repalcementp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (elsep->isZero()) { // a ? b : 0 becomes a & b
|
||||
if (isZero(elsep)) { // a ? b : 0 becomes a & b
|
||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
|
||||
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, condp, thenp);
|
||||
replace(vtxp, repalcementp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (elsep->isOnes()) { // a ? b : 1 becomes ~a | b
|
||||
if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b
|
||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
|
||||
DfgNot* const notp = make<DfgNot>(vtxp, condp);
|
||||
DfgOr* const repalcementp = make<DfgOr>(vtxp, notp, thenp);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class DfgRegularize final {
|
|||
// This is an op that requires a result variable. Ensure it is
|
||||
// assigned to one, and redirect other sinks read that variable.
|
||||
if (DfgVertexVar* const varp = vtx.getResultVar()) {
|
||||
varp->sourceEdge<0>()->unlinkSource();
|
||||
varp->srcp(nullptr);
|
||||
vtx.replaceWith(varp);
|
||||
varp->srcp(&vtx);
|
||||
} else {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -36,39 +36,101 @@
|
|||
#define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE
|
||||
#endif
|
||||
|
||||
// === Abstract base node types (DfgVertex*) ===================================
|
||||
// Include macros generated by 'astgen'. These include DFGGEN_MEMBERS_<Node>
|
||||
// for each DfgVertex sub-type. The generated members include boilerplate
|
||||
// methods related to cloning, visitor dispatch, and other functionality.
|
||||
// For precise details please read the generated macros.
|
||||
#include "V3Dfg__gen_macros.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Variable vertices - represent a variables
|
||||
|
||||
class DfgVertexVar VL_NOT_FINAL : public DfgVertex {
|
||||
// Represents a variable. It has 2 optional inputs, 'srcp' and 'defaultp'.
|
||||
|
||||
class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary {
|
||||
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex)
|
||||
AstVarScope* const m_varScopep; // The AstVarScope associated with this vertex (not owned)
|
||||
// Location of driver of this variable. Only used for converting back to Ast. Might be nullptr.
|
||||
FileLine* m_driverFileLine = nullptr;
|
||||
// If this DfgVertexVar is a synthesized temporary, this is the Var/VarScope it stands for.
|
||||
// If this DfgVertexVar is a synthesized temporary, this is the original Var/VarScope it stands
|
||||
// for. It might point to m_varp/m_varScopep itself to indicate it's a temporary without an
|
||||
// associated input Var/VarScope.
|
||||
AstNode* m_tmpForp = nullptr;
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED;
|
||||
V3Hash selfHash() const final VL_MT_DISABLED;
|
||||
bool selfEquals(const DfgVertex& that) const final {
|
||||
UASSERT_OBJ(nodep() != that.as<DfgVertexVar>()->nodep(), this,
|
||||
"There should only be one DfgVertexVar for a given AstVar/AstVarScope");
|
||||
return false;
|
||||
}
|
||||
|
||||
V3Hash selfHash() const final {
|
||||
V3Hash hash;
|
||||
hash += nodep()->name();
|
||||
hash += varp()->varType();
|
||||
return hash;
|
||||
}
|
||||
|
||||
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, AstVarScope* vscp)
|
||||
: DfgVertex{dfg, type, varp->fileline(), V3Dfg::toDfgDType(varp->dtypep())}
|
||||
, m_varp{varp}
|
||||
, m_varScopep{vscp} {
|
||||
#ifdef VL_DEBUG
|
||||
if (v3Global.rootp()->topScopep()) {
|
||||
UASSERT_OBJ(vscp, varp, "Un-scoped DfgVertexVar created in scoped DfgGraph");
|
||||
} else {
|
||||
UASSERT_OBJ(!vscp, varp, "Scoped DfgVertexVar created in un-scoped DfgGraph");
|
||||
}
|
||||
UASSERT_OBJ(V3Dfg::isSupported(dtypep()), varp, "Not representable by DfgVertexVar");
|
||||
#endif
|
||||
// Increment reference count
|
||||
AstNode* const variablep = nodep();
|
||||
variablep->user1(variablep->user1() + 0x10);
|
||||
UASSERT_OBJ((variablep->user1() >> 4) > 0, variablep, "Reference count overflow");
|
||||
// Allocate sources
|
||||
newInput();
|
||||
newInput();
|
||||
}
|
||||
|
||||
protected:
|
||||
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp)
|
||||
: DfgVertexVar{dfg, type, varp, nullptr} {}
|
||||
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp)
|
||||
: DfgVertexVar{dfg, type, vscp->varp(), vscp} {}
|
||||
|
||||
public:
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp);
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp);
|
||||
inline ~DfgVertexVar();
|
||||
~DfgVertexVar() {
|
||||
// Decrement reference count
|
||||
AstNode* const variablep = nodep();
|
||||
variablep->user1(variablep->user1() - 0x10);
|
||||
UASSERT_OBJ((variablep->user1() >> 4) >= 0, variablep, "Reference count underflow");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgVertexVar;
|
||||
|
||||
const std::string srcName(size_t) const override { return ""; }
|
||||
// The driver of the variable. Might be nullptr if driven exernally (input to DfgGraph).
|
||||
DfgVertex* srcp() const { return inputp(0); }
|
||||
void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
|
||||
// The default value of the variable. This defines the parts not driven by 'srcp', maybe null
|
||||
DfgVertex* defaultp() const { return inputp(1); }
|
||||
void defaultp(DfgVertex* vtxp) { inputp(1, vtxp); }
|
||||
|
||||
std::string srcName(size_t idx) const override final { return idx ? "defaultp" : "srcp"; }
|
||||
|
||||
// The Ast variable this vertex representess
|
||||
AstVar* varp() const { return m_varp; }
|
||||
AstVarScope* varScopep() const { return m_varScopep; }
|
||||
AstNode* nodep() const {
|
||||
return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp);
|
||||
}
|
||||
|
||||
FileLine* driverFileLine() const { return m_driverFileLine; }
|
||||
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
|
||||
|
||||
// If this is a temporary, the Ast variable it stands for, or same as
|
||||
// 'nodep()' if it's a temporary with no associated original Ast variable.
|
||||
AstNode* tmpForp() const { return m_tmpForp; }
|
||||
void tmpForp(AstNode* nodep) { m_tmpForp = nodep; }
|
||||
|
||||
// Location of driver of variable (only used if 'srcp' is not a splice)
|
||||
FileLine* driverFileLine() const { return m_driverFileLine; }
|
||||
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
|
||||
|
||||
bool isDrivenFullyByDfg() const {
|
||||
return srcp() && !srcp()->is<DfgVertexSplice>() && !varp()->isForced()
|
||||
&& !varp()->isSigUserRWPublic();
|
||||
|
|
@ -105,212 +167,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
};
|
||||
class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic {
|
||||
protected:
|
||||
struct DriverData final {
|
||||
FileLine* m_flp; // Location of this driver
|
||||
uint32_t m_lo; // Low index of range driven by this driver
|
||||
DriverData() = delete;
|
||||
DriverData(FileLine* flp, uint32_t lo)
|
||||
: m_flp{flp}
|
||||
, m_lo{lo} {}
|
||||
};
|
||||
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
|
||||
V3Hash selfHash() const override VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexVariadic{dfg, type, flp, dtypep, 2u} {
|
||||
// Add optional source for 'defaultp'
|
||||
addSource();
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgVertexSplice;
|
||||
|
||||
std::pair<const DfgEdge*, size_t> sourceEdges() const override {
|
||||
const std::pair<const DfgEdge*, size_t> pair = DfgVertexVariadic::sourceEdges();
|
||||
UASSERT_OBJ(pair.second > 0, this, "default driver edge is missing");
|
||||
// If it has a default driver that's it
|
||||
if (pair.first->sourcep()) return pair;
|
||||
// Otherwise there is one less source
|
||||
return {pair.first + 1, pair.second - 1};
|
||||
}
|
||||
std::pair<DfgEdge*, size_t> sourceEdges() override {
|
||||
const auto pair = const_cast<const DfgVertexSplice*>(this)->sourceEdges();
|
||||
return {const_cast<DfgEdge*>(pair.first), pair.second};
|
||||
}
|
||||
|
||||
// Named getter/setter for optional default driver
|
||||
DfgVertex* defaultp() const { return DfgVertexVariadic::source(0); }
|
||||
void defaultp(DfgVertex* vtxp) {
|
||||
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "default driver can't be a DfgLogic");
|
||||
const bool found = findSourceEdge([vtxp](const DfgEdge& e, size_t) -> bool { //
|
||||
return e.sourcep() == vtxp;
|
||||
});
|
||||
UASSERT_OBJ(!found, this, "adding existing driver as default");
|
||||
DfgVertexVariadic::sourceEdge(0)->relinkSource(vtxp);
|
||||
}
|
||||
|
||||
// Add resolved driver
|
||||
void addDriver(FileLine* flp, uint32_t lo, DfgVertex* vtxp) {
|
||||
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "addDriver called with DfgLogic");
|
||||
UASSERT_OBJ(vtxp != defaultp(), this, "adding default driver as resolved");
|
||||
m_driverData.emplace_back(flp, lo);
|
||||
DfgVertexVariadic::addSource()->relinkSource(vtxp);
|
||||
}
|
||||
|
||||
FileLine* driverFileLine(size_t idx) const {
|
||||
UASSERT_OBJ(!defaultp() || idx > 0, this, "'driverFileLine' called on default driver");
|
||||
if (defaultp()) --idx;
|
||||
return m_driverData.at(idx).m_flp;
|
||||
}
|
||||
|
||||
uint32_t driverLo(size_t idx) const {
|
||||
UASSERT_OBJ(!defaultp() || idx > 0, this, "'driverLo' called on default driver");
|
||||
if (defaultp()) --idx;
|
||||
const DriverData& dd = m_driverData.at(idx);
|
||||
return dd.m_lo;
|
||||
}
|
||||
|
||||
DfgVertex* driverAt(size_t idx) const {
|
||||
const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge& e, size_t i) { //
|
||||
// Don't pick the default driver
|
||||
if (i == 0 && defaultp()) return false;
|
||||
return driverLo(i) == idx;
|
||||
});
|
||||
return edgep ? edgep->sourcep() : nullptr;
|
||||
}
|
||||
|
||||
// If drives the whole result explicitly (not through defaultp), this is
|
||||
// the actual driver this DfgVertexSplice can be replaced with.
|
||||
inline DfgVertex* wholep() const;
|
||||
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
void resetSources() {
|
||||
m_driverData.clear();
|
||||
// Unlink default driver
|
||||
DfgVertex* const dp = defaultp();
|
||||
DfgVertexVariadic::sourceEdge(0)->unlinkSource();
|
||||
// Reset DfgVertexVariadic sources
|
||||
DfgVertexVariadic::resetSources();
|
||||
// Add back the default driver if present
|
||||
DfgEdge* const edgep = DfgVertexVariadic::addSource();
|
||||
if (dp) edgep->relinkSource(dp);
|
||||
}
|
||||
|
||||
const std::string srcName(size_t idx) const override {
|
||||
if (idx == 0 && defaultp()) return "default";
|
||||
const uint32_t lo = driverLo(idx);
|
||||
const uint32_t hi = lo + DfgVertexVariadic::source(idx + !defaultp())->size() - 1;
|
||||
return '[' + std::to_string(hi) + ':' + std::to_string(lo) + ']';
|
||||
}
|
||||
};
|
||||
|
||||
// === Concrete node types =====================================================
|
||||
|
||||
// === DfgVertex ===
|
||||
class DfgConst final : public DfgVertex {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
||||
V3Number m_num; // Constant value
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
|
||||
V3Hash selfHash() const override VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
inline DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num);
|
||||
inline DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value = 0);
|
||||
ASTGEN_MEMBERS_DfgConst;
|
||||
|
||||
V3Number& num() { return m_num; }
|
||||
const V3Number& num() const { return m_num; }
|
||||
|
||||
size_t toSizeT() const {
|
||||
if VL_CONSTEXPR_CXX17 (sizeof(size_t) > sizeof(uint32_t)) {
|
||||
return static_cast<size_t>(num().toUQuad());
|
||||
}
|
||||
return static_cast<size_t>(num().toUInt());
|
||||
}
|
||||
|
||||
uint32_t toU32() const { return static_cast<size_t>(num().toUInt()); }
|
||||
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
bool isZero() const { return num().isEqZero(); }
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
bool isOnes() const { return num().isEqAllOnes(width()); }
|
||||
|
||||
// Does this DfgConst have the given value? Note this is not easy to answer if wider than 32.
|
||||
bool hasValue(uint32_t value) const {
|
||||
return !num().isFourState() && num().edataWord(0) == value && num().mostSetBitP1() <= 32;
|
||||
}
|
||||
|
||||
std::pair<DfgEdge*, size_t> sourceEdges() override { return {nullptr, 0}; }
|
||||
std::pair<const DfgEdge*, size_t> sourceEdges() const override { return {nullptr, 0}; }
|
||||
const string srcName(size_t) const override { // LCOV_EXCL_START
|
||||
VL_UNREACHABLE;
|
||||
return "";
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
|
||||
// === DfgVertexBinary ===
|
||||
class DfgMux final : public DfgVertexBinary {
|
||||
// AstSel is ternary, but the 'widthp' is always constant and is hence redundant, and
|
||||
// 'lsbp' is very often constant. As AstSel is fairly common, we special case as a DfgSel for
|
||||
// the constant 'lsbp', and as 'DfgMux` for the non-constant 'lsbp'.
|
||||
public:
|
||||
DfgMux(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexBinary{dfg, dfgType(), flp, dtypep} {}
|
||||
ASTGEN_MEMBERS_DfgMux;
|
||||
|
||||
DfgVertex* fromp() const { return source<0>(); }
|
||||
void fromp(DfgVertex* vtxp) { relinkSource<0>(vtxp); }
|
||||
DfgVertex* lsbp() const { return source<1>(); }
|
||||
void lsbp(DfgVertex* vtxp) { relinkSource<1>(vtxp); }
|
||||
|
||||
const string srcName(size_t idx) const override { return idx ? "lsbp" : "fromp"; }
|
||||
};
|
||||
|
||||
// === DfgVertexUnary ===
|
||||
|
||||
class DfgSel final : public DfgVertexUnary {
|
||||
// AstSel is ternary, but the 'widthp' is always constant and is hence redundant, and
|
||||
// 'lsbp' is very often constant. As AstSel is fairly common, we special case as a DfgSel for
|
||||
// the constant 'lsbp', and as 'DfgMux` for the non-constant 'lsbp'.
|
||||
uint32_t m_lsb = 0; // The LSB index
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
|
||||
V3Hash selfHash() const override VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
DfgSel(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {}
|
||||
ASTGEN_MEMBERS_DfgSel;
|
||||
|
||||
DfgVertex* fromp() const { return source<0>(); }
|
||||
void fromp(DfgVertex* vtxp) { relinkSource<0>(vtxp); }
|
||||
uint32_t lsb() const { return m_lsb; }
|
||||
void lsb(uint32_t value) { m_lsb = value; }
|
||||
|
||||
const string srcName(size_t) const override { return "fromp"; }
|
||||
};
|
||||
|
||||
class DfgUnitArray final : public DfgVertexUnary {
|
||||
// This is a type adapter for modeling arrays. It's a single element array,
|
||||
// with the value of the single element being the source operand.
|
||||
public:
|
||||
DfgUnitArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {
|
||||
UASSERT_OBJ(this->dtypep(), flp, "Non array DfgUnitArray");
|
||||
UASSERT_OBJ(this->size() == 1, flp, "DfgUnitArray must have a single element");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgUnitArray;
|
||||
|
||||
const std::string srcName(size_t) const override { return ""; }
|
||||
};
|
||||
|
||||
// === DfgVertexVar ===
|
||||
class DfgVarArray final : public DfgVertexVar {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
|
@ -326,6 +183,7 @@ public:
|
|||
}
|
||||
ASTGEN_MEMBERS_DfgVarArray;
|
||||
};
|
||||
|
||||
class DfgVarPacked final : public DfgVertexVar {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
|
@ -342,59 +200,286 @@ public:
|
|||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
};
|
||||
|
||||
// === DfgVertexVariadic ===
|
||||
class DfgLogic final : public DfgVertexVariadic {
|
||||
// Generic vertex representing a whole combinational process
|
||||
AstNode* const m_nodep; // The Ast logic represented by this vertex
|
||||
const std::unique_ptr<CfgGraph> m_cfgp;
|
||||
// Vertices this logic was synthesized into. Excluding variables
|
||||
std::vector<DfgVertex*> m_synth;
|
||||
//------------------------------------------------------------------------------
|
||||
// Nullary vertices - 0 inputs
|
||||
|
||||
class DfgVertexNullary VL_NOT_FINAL : public DfgVertex {
|
||||
protected:
|
||||
DfgVertexNullary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertex{dfg, type, flp, dtypep} {}
|
||||
|
||||
public:
|
||||
DfgLogic(DfgGraph& dfg, AstAssignW* nodep)
|
||||
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u}
|
||||
, m_nodep{nodep}
|
||||
, m_cfgp{nullptr} {}
|
||||
|
||||
DfgLogic(DfgGraph& dfg, AstAlways* nodep, std::unique_ptr<CfgGraph> cfgp)
|
||||
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u}
|
||||
, m_nodep{nodep}
|
||||
, m_cfgp{std::move(cfgp)} {}
|
||||
|
||||
ASTGEN_MEMBERS_DfgLogic;
|
||||
|
||||
void addInput(DfgVertexVar* varp) { addSource()->relinkSource(varp); }
|
||||
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
CfgGraph& cfg() { return *m_cfgp; }
|
||||
const CfgGraph& cfg() const { return *m_cfgp; }
|
||||
std::vector<DfgVertex*>& synth() { return m_synth; }
|
||||
const std::vector<DfgVertex*>& synth() const { return m_synth; }
|
||||
|
||||
const std::string srcName(size_t) const override { return ""; }
|
||||
ASTGEN_MEMBERS_DfgVertexNullary;
|
||||
std::string srcName(size_t) const override final { V3ERROR_NA_RETURN(""); }
|
||||
};
|
||||
|
||||
class DfgUnresolved final : public DfgVertexVariadic {
|
||||
// Represents a collection of unresolved variable drivers before synthesis
|
||||
class DfgConst final : public DfgVertexNullary {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
||||
V3Number m_num; // Constant value
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override {
|
||||
return num().isCaseEq(that.as<DfgConst>()->num());
|
||||
}
|
||||
V3Hash selfHash() const override { return num().toHash(); }
|
||||
|
||||
public:
|
||||
DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp)
|
||||
: DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtypep(), 1u} {}
|
||||
ASTGEN_MEMBERS_DfgUnresolved;
|
||||
DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num)
|
||||
: DfgVertexNullary{dfg, dfgType(), flp, V3Dfg::dtypePacked(num.width())}
|
||||
, m_num{num} {}
|
||||
DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value)
|
||||
: DfgVertexNullary{dfg, dfgType(), flp, V3Dfg::dtypePacked(width)}
|
||||
, m_num{flp, static_cast<int>(width), value} {}
|
||||
|
||||
// Can only be driven by DfgLogic or DfgVertexSplice
|
||||
void addDriver(DfgLogic* vtxp) { addSource()->relinkSource(vtxp); }
|
||||
void addDriver(DfgVertexSplice* vtxp) { addSource()->relinkSource(vtxp); }
|
||||
ASTGEN_MEMBERS_DfgConst;
|
||||
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
void clearSources() { DfgVertexVariadic::clearSources(); }
|
||||
V3Number& num() { return m_num; }
|
||||
const V3Number& num() const { return m_num; }
|
||||
|
||||
DfgVertex* singleSource() const { return arity() == 1 ? source(0) : nullptr; }
|
||||
size_t toSizeT() const { return static_cast<size_t>(num().toUQuad()); }
|
||||
uint32_t toU32() const { return num().toUInt(); }
|
||||
|
||||
const std::string srcName(size_t) const override { return ""; }
|
||||
bool isZero() const { return num().isEqZero(); }
|
||||
bool isOnes() const { return num().isEqAllOnes(width()); }
|
||||
|
||||
// Does this DfgConst have the given value? Note this is not easy to answer if wider than 32.
|
||||
bool hasValue(uint32_t value) const {
|
||||
return !num().isFourState() && num().edataWord(0) == value && num().mostSetBitP1() <= 32;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Unary vertices - 1 inputs
|
||||
|
||||
class DfgVertexUnary VL_NOT_FINAL : public DfgVertex {
|
||||
protected:
|
||||
DfgVertexUnary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertex{dfg, type, flp, dtypep} {
|
||||
newInput();
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_DfgVertexUnary;
|
||||
DfgVertex* srcp() const { return inputp(0); }
|
||||
void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
|
||||
std::string srcName(size_t) const override final { return ""; }
|
||||
};
|
||||
|
||||
class DfgSel final : public DfgVertexUnary {
|
||||
// AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
|
||||
// common, we special case as a DfgSel for the constant 'lsbp', and as
|
||||
// 'DfgMux` for the non-constant 'lsbp'.
|
||||
uint32_t m_lsb = 0; // The LSB index
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override {
|
||||
return lsb() == that.as<DfgSel>()->lsb();
|
||||
}
|
||||
V3Hash selfHash() const override { return V3Hash{lsb()}; }
|
||||
|
||||
public:
|
||||
DfgSel(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {}
|
||||
ASTGEN_MEMBERS_DfgSel;
|
||||
|
||||
DfgVertex* fromp() const { return srcp(); }
|
||||
void fromp(DfgVertex* vtxp) { srcp(vtxp); }
|
||||
uint32_t lsb() const { return m_lsb; }
|
||||
void lsb(uint32_t value) { m_lsb = value; }
|
||||
};
|
||||
|
||||
class DfgUnitArray final : public DfgVertexUnary {
|
||||
// This is a type adapter for modeling arrays. It's a single element array,
|
||||
// with the value of the single element being the source operand.
|
||||
bool selfEquals(const DfgVertex&) const final { return true; }
|
||||
V3Hash selfHash() const final { return V3Hash{}; }
|
||||
|
||||
public:
|
||||
DfgUnitArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {
|
||||
UASSERT_OBJ(this->dtypep(), flp, "Non array DfgUnitArray");
|
||||
UASSERT_OBJ(this->size() == 1, flp, "DfgUnitArray must have a single element");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgUnitArray;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Binary vertices - 2 inputs
|
||||
|
||||
class DfgVertexBinary VL_NOT_FINAL : public DfgVertex {
|
||||
protected:
|
||||
DfgVertexBinary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertex{dfg, type, flp, dtypep} {
|
||||
newInput();
|
||||
newInput();
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_DfgVertexBinary;
|
||||
};
|
||||
|
||||
class DfgMux final : public DfgVertexBinary {
|
||||
// AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
|
||||
// common, we special case as a DfgSel for the constant 'lsbp', and as
|
||||
// 'DfgMux` for the non-constant 'lsbp'.
|
||||
bool selfEquals(const DfgVertex&) const override { return true; }
|
||||
V3Hash selfHash() const override { return V3Hash{}; }
|
||||
|
||||
public:
|
||||
DfgMux(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexBinary{dfg, dfgType(), flp, dtypep} {}
|
||||
ASTGEN_MEMBERS_DfgMux;
|
||||
|
||||
DfgVertex* fromp() const { return inputp(0); }
|
||||
void fromp(DfgVertex* vtxp) { inputp(0, vtxp); }
|
||||
DfgVertex* lsbp() const { return inputp(1); }
|
||||
void lsbp(DfgVertex* vtxp) { inputp(1, vtxp); }
|
||||
|
||||
std::string srcName(size_t idx) const override { return idx ? "lsbp" : "fromp"; }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Ternary vertices - 3 inputs
|
||||
|
||||
class DfgVertexTernary VL_NOT_FINAL : public DfgVertex {
|
||||
protected:
|
||||
DfgVertexTernary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertex{dfg, type, flp, dtypep} {
|
||||
newInput();
|
||||
newInput();
|
||||
newInput();
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_DfgVertexTernary;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Variadic vertices - variable number of inputs
|
||||
|
||||
class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex {
|
||||
protected:
|
||||
DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertex{dfg, type, flp, dtypep} {}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_DfgVertexVariadic;
|
||||
};
|
||||
|
||||
class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic {
|
||||
// Represents a partial update to a varibale
|
||||
|
||||
struct DriverData final {
|
||||
uint32_t m_lo; // Low index of range driven by this driver
|
||||
FileLine* m_flp; // Location of this driver
|
||||
DriverData() = delete;
|
||||
DriverData(uint32_t lo, FileLine* flp)
|
||||
: m_lo{lo}
|
||||
, m_flp{flp} {}
|
||||
};
|
||||
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override final {
|
||||
const DfgVertexSplice* const thatp = that.as<DfgVertexSplice>();
|
||||
for (size_t i = 0; i < nInputs(); ++i) {
|
||||
if (m_driverData[i].m_lo != thatp->m_driverData[i].m_lo) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
V3Hash selfHash() const override final {
|
||||
V3Hash hash;
|
||||
const size_t n = nInputs();
|
||||
for (size_t i = 0; i < n; ++i) hash += m_driverData[i].m_lo;
|
||||
return hash;
|
||||
}
|
||||
|
||||
protected:
|
||||
DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexVariadic{dfg, type, flp, dtypep} {}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_DfgVertexSplice;
|
||||
|
||||
// Add driver
|
||||
void addDriver(DfgVertex* vtxp, uint32_t lo, FileLine* flp) {
|
||||
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "addDriver called with DfgLogic");
|
||||
m_driverData.emplace_back(lo, flp);
|
||||
newInput()->relinkSrcp(vtxp);
|
||||
}
|
||||
|
||||
void resetDrivers() {
|
||||
resetInputs();
|
||||
m_driverData.clear();
|
||||
}
|
||||
|
||||
std::string srcName(size_t idx) const override final {
|
||||
const uint32_t lo = m_driverData[idx].m_lo;
|
||||
const uint32_t hi = lo + inputp(idx)->size() - 1;
|
||||
return '[' + std::to_string(hi) + ':' + std::to_string(lo) + ']';
|
||||
}
|
||||
|
||||
FileLine* driverFileLine(size_t idx) const { return m_driverData.at(idx).m_flp; }
|
||||
|
||||
DfgVertex* driverAt(size_t idx) {
|
||||
const size_t n = nInputs();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (m_driverData[i].m_lo == idx) return inputp(i);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const DfgVertex* driverAt(size_t idx) const {
|
||||
for (const DriverData& dd : m_driverData) {
|
||||
if (dd.m_lo == idx) return inputp(idx);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If drives the whole result explicitly (not through defaultp), this is
|
||||
// the actual driver this DfgVertexSplice can be replaced with.
|
||||
DfgVertex* wholep() {
|
||||
if (nInputs() != 1) return nullptr;
|
||||
if (m_driverData[0].m_lo != 0) return nullptr;
|
||||
DfgVertex* const vtxp = inputp(0);
|
||||
if (vtxp->size() != size()) return nullptr;
|
||||
if (const DfgUnitArray* const uap = vtxp->cast<DfgUnitArray>()) {
|
||||
if (DfgVertexSplice* const splicep = uap->srcp()->cast<DfgVertexSplice>()) {
|
||||
if (!splicep->wholep()) return nullptr;
|
||||
}
|
||||
}
|
||||
return vtxp;
|
||||
}
|
||||
|
||||
bool foreachDriver(std::function<bool(DfgVertex&, uint32_t, FileLine*)> f) {
|
||||
const size_t n = nInputs();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t, FileLine*)> f) const {
|
||||
const size_t n = nInputs();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool foreachDriver(std::function<bool(DfgVertex&, uint32_t)> f) {
|
||||
const size_t n = nInputs();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (f(*inputp(i), m_driverData[i].m_lo)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t)> f) const {
|
||||
const size_t n = nInputs();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (f(*inputp(i), m_driverData[i].m_lo)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// === DfgVertexSplice ===
|
||||
class DfgSpliceArray final : public DfgVertexSplice {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
|
@ -406,6 +491,7 @@ public:
|
|||
}
|
||||
ASTGEN_MEMBERS_DfgSpliceArray;
|
||||
};
|
||||
|
||||
class DfgSplicePacked final : public DfgVertexSplice {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
|
@ -418,4 +504,70 @@ public:
|
|||
ASTGEN_MEMBERS_DfgSplicePacked;
|
||||
};
|
||||
|
||||
class DfgLogic final : public DfgVertexVariadic {
|
||||
// Generic vertex representing a whole combinational process
|
||||
AstNode* const m_nodep; // The Ast logic represented by this vertex
|
||||
AstScope* const m_scopep; // The AstScope m_nodep is under, iff scoped
|
||||
const std::unique_ptr<CfgGraph> m_cfgp;
|
||||
// Vertices this logic was synthesized into. Excluding variables
|
||||
std::vector<DfgVertex*> m_synth;
|
||||
|
||||
// Used very early, should never be needed
|
||||
bool selfEquals(const DfgVertex&) const final { V3ERROR_NA_RETURN(false); }
|
||||
V3Hash selfHash() const final { V3ERROR_NA_RETURN(V3Hash{}); }
|
||||
|
||||
public:
|
||||
DfgLogic(DfgGraph& dfg, AstAssignW* nodep, AstScope* scopep)
|
||||
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr}
|
||||
, m_nodep{nodep}
|
||||
, m_scopep{scopep}
|
||||
, m_cfgp{nullptr} {}
|
||||
|
||||
DfgLogic(DfgGraph& dfg, AstAlways* nodep, AstScope* scopep, std::unique_ptr<CfgGraph> cfgp)
|
||||
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr}
|
||||
, m_nodep{nodep}
|
||||
, m_scopep{scopep}
|
||||
, m_cfgp{std::move(cfgp)} {}
|
||||
|
||||
ASTGEN_MEMBERS_DfgLogic;
|
||||
|
||||
std::string srcName(size_t) const override final { return ""; }
|
||||
|
||||
// Can only be driven by DfgVertexVar
|
||||
void addInput(DfgVertexVar* varp) { newInput()->relinkSrcp(varp); }
|
||||
|
||||
// Accessors
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
CfgGraph& cfg() { return *m_cfgp; }
|
||||
const CfgGraph& cfg() const { return *m_cfgp; }
|
||||
std::vector<DfgVertex*>& synth() { return m_synth; }
|
||||
const std::vector<DfgVertex*>& synth() const { return m_synth; }
|
||||
};
|
||||
|
||||
class DfgUnresolved final : public DfgVertexVariadic {
|
||||
// Represents a collection of unresolved variable drivers before synthesis
|
||||
|
||||
bool selfEquals(const DfgVertex&) const final { return true; }
|
||||
V3Hash selfHash() const final { return V3Hash{}; }
|
||||
|
||||
public:
|
||||
DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp)
|
||||
: DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtypep()} {}
|
||||
ASTGEN_MEMBERS_DfgUnresolved;
|
||||
|
||||
std::string srcName(size_t) const override final { return ""; }
|
||||
|
||||
// Can only be driven by DfgLogic or DfgVertexSplice
|
||||
void addDriver(DfgLogic* vtxp) { newInput()->relinkSrcp(vtxp); }
|
||||
void addDriver(DfgVertexSplice* vtxp) { newInput()->relinkSrcp(vtxp); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// The rest of the vertex types are generated by 'astgen' from AstNodeExpr
|
||||
#include "V3Dfg__gen_auto_classes.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions
|
||||
|
||||
#endif
|
||||
|
|
|
|||
54
src/astgen
54
src/astgen
|
|
@ -678,6 +678,10 @@ def check_types(sortedTypes, prefix, abstractPrefix):
|
|||
sys.exit("%Error: Non-final {b} subclasses must be named {b}*: {p}{n}".format(
|
||||
b=baseClass, p=prefix, n=node.name))
|
||||
|
||||
# Skip ordering check for Dfg
|
||||
if prefix == "Dfg":
|
||||
return
|
||||
|
||||
# Check ordering of node definitions
|
||||
hasOrderingError = False
|
||||
|
||||
|
|
@ -1169,6 +1173,9 @@ def write_dfg_macros(filename):
|
|||
" ").format(**fmt).replace("\n", " \\\n"))
|
||||
|
||||
for node in DfgVertexList:
|
||||
if node.isRoot:
|
||||
continue
|
||||
|
||||
fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name))
|
||||
|
||||
if node.isLeaf:
|
||||
|
|
@ -1183,22 +1190,24 @@ def write_dfg_macros(filename):
|
|||
for n in range(1, node.arity + 1):
|
||||
name, _, _, _ = node.getOp(n)
|
||||
emitBlock('''\
|
||||
DfgVertex* {name}() const {{ return source<{n}>(); }}
|
||||
void {name}(DfgVertex* vtxp) {{ relinkSource<{n}>(vtxp); }}
|
||||
DfgVertex* {name}() const {{ return inputp({n}); }}
|
||||
void {name}(DfgVertex* vtxp) {{ inputp({n}, vtxp); }}
|
||||
''',
|
||||
name=name,
|
||||
n=n - 1)
|
||||
|
||||
operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1))
|
||||
if operandNames:
|
||||
emitBlock('''\
|
||||
const std::string srcName(size_t idx) const override {{
|
||||
static const char* names[{a}] = {{ {ns} }};
|
||||
return names[idx];
|
||||
}}
|
||||
''',
|
||||
a=node.arity,
|
||||
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames)))
|
||||
if node.isLeaf and node.arity > 1:
|
||||
operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1))
|
||||
if operandNames:
|
||||
emitBlock('''\
|
||||
std::string srcName(size_t idx) const override final {{
|
||||
static const char* names[{a}] = {{ {ns} }};
|
||||
return names[idx];
|
||||
}}
|
||||
''',
|
||||
a=node.arity,
|
||||
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames)))
|
||||
|
||||
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
|
||||
|
||||
|
||||
|
|
@ -1215,6 +1224,8 @@ def write_dfg_auto_classes(filename):
|
|||
|
||||
emitBlock('''\
|
||||
class Dfg{t} final : public Dfg{s} {{
|
||||
bool selfEquals(const DfgVertex&) const final {{ return true; }}
|
||||
V3Hash selfHash() const final {{ return V3Hash{{}}; }}
|
||||
public:
|
||||
Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: Dfg{s}{{dfg, dfgType(), flp, dtypep}} {{}}
|
||||
|
|
@ -1275,12 +1286,12 @@ def write_dfg_ast_to_dfg(filename):
|
|||
fh.write(" m_foundUnhandled = true;\n")
|
||||
fh.write(" ++m_ctx.m_conv.nonRepNode;\n")
|
||||
fh.write(" return;\n")
|
||||
fh.write(" }\n\n")
|
||||
fh.write(" m_logicp->synth().emplace_back(vtxp);")
|
||||
fh.write(" }\n")
|
||||
fh.write(" m_logicp->synth().emplace_back(vtxp);\n\n")
|
||||
for i in range(node.arity):
|
||||
fh.write(
|
||||
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user2u().to<DfgVertex*>());\n".
|
||||
format(i=i, j=i + 1))
|
||||
" vtxp->inputp({i}, nodep->op{j}p()->user2u().to<DfgVertex*>());\n".format(
|
||||
i=i, j=i + 1))
|
||||
fh.write("\n")
|
||||
fh.write(" nodep->user2p(vtxp);\n")
|
||||
fh.write("}\n")
|
||||
|
|
@ -1296,7 +1307,7 @@ def write_dfg_dfg_to_ast(filename):
|
|||
fh.write("void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
|
||||
for i in range(node.arity):
|
||||
fh.write(
|
||||
" AstNodeExpr* const op{j}p = convertDfgVertexToAstNodeExpr(vtxp->source<{i}>());\n"
|
||||
" AstNodeExpr* const op{j}p = convertDfgVertexToAstNodeExpr(vtxp->inputp({i}));\n"
|
||||
.format(i=i, j=i + 1))
|
||||
fh.write(" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name))
|
||||
for i in range(node.arity):
|
||||
|
|
@ -1355,14 +1366,6 @@ check_types(AstNodeList, "Ast", "Node")
|
|||
# Set up the root DfgVertex type and some other hand-written base types.
|
||||
# These are standalone so we don't need to parse the sources for this.
|
||||
DfgVertices["Vertex"] = Node("Vertex", None)
|
||||
DfgVertices["VertexUnary"] = Node("VertexUnary", DfgVertices["Vertex"])
|
||||
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexUnary"])
|
||||
DfgVertices["VertexBinary"] = Node("VertexBinary", DfgVertices["Vertex"])
|
||||
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexBinary"])
|
||||
DfgVertices["VertexTernary"] = Node("VertexTernary", DfgVertices["Vertex"])
|
||||
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexTernary"])
|
||||
DfgVertices["VertexVariadic"] = Node("VertexVariadic", DfgVertices["Vertex"])
|
||||
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexVariadic"])
|
||||
|
||||
# AstNodeExpr that are not representable in Dfg
|
||||
DfgIgnored = (
|
||||
|
|
@ -1424,6 +1427,7 @@ DfgIgnored = (
|
|||
"ReplicateN",
|
||||
"SubstrN",
|
||||
"ToLowerN",
|
||||
"ToStringN",
|
||||
"ToUpperN",
|
||||
# Effectful
|
||||
"PostAdd",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ unusedPrivateFunction:src/V3Const.cpp
|
|||
// These are all inappropriate
|
||||
constVariablePointer:src/V3DfgPeephole.cpp
|
||||
constParameterPointer:src/V3DfgPeephole.cpp
|
||||
|
||||
// Class in non-copyable
|
||||
noCopyConstructor:src/V3DfgDfgToAst.cpp
|
||||
noOperatorEq:src/V3DfgDfgToAst.cpp
|
||||
|
||||
// TODO: these need fixing/signing off in source
|
||||
constParameterPointer:src/V3EmitCFunc.cpp
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// DESCRIPTION: Verilator: DFG optimizer equivalence testing
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2022 by Geza Lore.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
//
|
||||
|
||||
#include <verilated.h>
|
||||
#include <verilated_cov.h>
|
||||
|
||||
#include <Vopt.h>
|
||||
#include <Vref.h>
|
||||
#include <iostream>
|
||||
|
||||
void rngUpdate(uint64_t& x) {
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
// Create contexts
|
||||
VerilatedContext ctx;
|
||||
|
||||
// Create models
|
||||
Vref ref{&ctx};
|
||||
Vopt opt{&ctx};
|
||||
|
||||
uint64_t rand_a = 0x5aef0c8dd70a4497;
|
||||
uint64_t rand_b = 0xf0c0a8dd75ae4497;
|
||||
uint64_t srand_a = 0x00fa8dcc7ae4957;
|
||||
uint64_t srand_b = 0x0fa8dc7ae3c9574;
|
||||
|
||||
for (size_t n = 0; n < 200000; ++n) {
|
||||
// Update rngs
|
||||
rngUpdate(rand_a);
|
||||
rngUpdate(rand_b);
|
||||
rngUpdate(srand_a);
|
||||
rngUpdate(srand_b);
|
||||
|
||||
// Assign inputs
|
||||
ref.rand_a = opt.rand_a = rand_a;
|
||||
ref.rand_b = opt.rand_b = rand_b;
|
||||
ref.srand_a = opt.srand_a = srand_a;
|
||||
ref.srand_b = opt.srand_b = srand_b;
|
||||
|
||||
// Evaluate both models
|
||||
ref.eval();
|
||||
opt.eval();
|
||||
|
||||
// Check equivalence
|
||||
#include "checks.h"
|
||||
|
||||
// increment time
|
||||
ctx.timeInc(1);
|
||||
}
|
||||
|
||||
std::cout << "*-* All Finished *-*\n";
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
test.sim_time = 2000000
|
||||
|
||||
root = ".."
|
||||
|
||||
if not os.path.exists(root + "/.git"):
|
||||
test.skip("Not in a git repository")
|
||||
|
||||
# Generate the equivalence checks and declaration boilerplate
|
||||
rdFile = test.top_filename
|
||||
plistFile = test.obj_dir + "/portlist.vh"
|
||||
pdeclFile = test.obj_dir + "/portdecl.vh"
|
||||
checkFile = test.obj_dir + "/checks.h"
|
||||
nAlwaysSynthesized = 0
|
||||
nAlwaysNotSynthesized = 0
|
||||
nAlwaysReverted = 0
|
||||
with open(rdFile, 'r', encoding="utf8") as rdFh, \
|
||||
open(plistFile, 'w', encoding="utf8") as plistFh, \
|
||||
open(pdeclFile, 'w', encoding="utf8") as pdeclFh, \
|
||||
open(checkFile, 'w', encoding="utf8") as checkFh:
|
||||
for line in rdFh:
|
||||
if re.search(r'^\s*always.*//\s*nosynth$', line):
|
||||
nAlwaysNotSynthesized += 1
|
||||
elif re.search(r'^\s*always.*//\s*revert$', line):
|
||||
nAlwaysReverted += 1
|
||||
elif re.search(r'^\s*always', line):
|
||||
nAlwaysSynthesized += 1
|
||||
line = line.split("//")[0]
|
||||
m = re.search(r'`signal\((\w+),', line)
|
||||
if not m:
|
||||
continue
|
||||
sig = m.group(1)
|
||||
plistFh.write(sig + ",\n")
|
||||
pdeclFh.write("output " + sig + ";\n")
|
||||
checkFh.write("if (ref." + sig + " != opt." + sig + ") {\n")
|
||||
checkFh.write(" std::cout << \"Mismatched " + sig + "\" << std::endl;\n")
|
||||
checkFh.write(" std::cout << \"Ref: 0x\" << std::hex << (ref." + sig +
|
||||
" + 0) << std::endl;\n")
|
||||
checkFh.write(" std::cout << \"Opt: 0x\" << std::hex << (opt." + sig +
|
||||
" + 0) << std::endl;\n")
|
||||
checkFh.write(" std::exit(1);\n")
|
||||
checkFh.write("}\n")
|
||||
|
||||
# Compile un-optimized
|
||||
test.compile(verilator_flags2=[
|
||||
"--stats",
|
||||
"--build",
|
||||
"-fno-dfg",
|
||||
"+incdir+" + test.obj_dir,
|
||||
"-Mdir", test.obj_dir + "/obj_ref",
|
||||
"--prefix", "Vref",
|
||||
"-Wno-UNOPTFLAT"
|
||||
]) # yapf:disable
|
||||
|
||||
test.file_grep_not(test.obj_dir + "/obj_ref/Vref__stats.txt", r'DFG.*Synthesis')
|
||||
|
||||
# Compile optimized - also builds executable
|
||||
test.compile(verilator_flags2=[
|
||||
"--stats",
|
||||
"--build",
|
||||
"--fdfg-synthesize-all",
|
||||
"-fno-dfg-post-inline",
|
||||
"-fno-dfg-scoped",
|
||||
"--exe",
|
||||
"+incdir+" + test.obj_dir,
|
||||
"-Mdir", test.obj_dir + "/obj_opt",
|
||||
"--prefix", "Vopt",
|
||||
"-fno-const-before-dfg", # Otherwise V3Const makes testing painful
|
||||
"-fno-split", # Dfg will take care of it
|
||||
"--debug", "--debugi", "0", "--dumpi-tree", "0",
|
||||
"-CFLAGS \"-I .. -I ../obj_ref\"",
|
||||
"../obj_ref/Vref__ALL.a",
|
||||
"../../t/" + test.name + ".cpp"
|
||||
]) # yapf:disable
|
||||
|
||||
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
|
||||
r'DFG pre inline Synthesis, synt / always blocks considered\s+(\d+)$',
|
||||
nAlwaysSynthesized + nAlwaysReverted + nAlwaysNotSynthesized)
|
||||
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
|
||||
r'DFG pre inline Synthesis, synt / always blocks synthesized\s+(\d+)$',
|
||||
nAlwaysSynthesized + nAlwaysReverted)
|
||||
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
|
||||
r'DFG pre inline Synthesis, synt / reverted \(multidrive\)\s+(\d)$',
|
||||
nAlwaysReverted)
|
||||
|
||||
# Execute test to check equivalence
|
||||
test.execute(executable=test.obj_dir + "/obj_opt/Vopt")
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Geza Lore.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`define signal(name, expr) wire [$bits(expr)-1:0] ``name = expr
|
||||
|
||||
module t (
|
||||
`include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py
|
||||
rand_a, rand_b, srand_a, srand_b
|
||||
);
|
||||
|
||||
`include "portdecl.vh" // Boilerplate generated by t_dfg_break_cycles.py
|
||||
|
||||
input rand_a;
|
||||
input rand_b;
|
||||
input srand_a;
|
||||
input srand_b;
|
||||
wire logic [63:0] rand_a;
|
||||
wire logic [63:0] rand_b;
|
||||
wire logic signed [63:0] srand_a;
|
||||
wire logic signed [63:0] srand_b;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
logic concat_lhs_a;
|
||||
logic concat_lhs_b;
|
||||
always_comb begin
|
||||
{concat_lhs_a, concat_lhs_b} = rand_a[1:0] + rand_b[1:0];
|
||||
end
|
||||
`signal(CONCAT_LHS, {concat_lhs_a, concat_lhs_b});
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue