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;
|
const VAlwaysKwd m_keyword;
|
||||||
|
|
||||||
public:
|
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)
|
: ASTGEN_SUPER_Always(fl, stmtsp)
|
||||||
, m_keyword{keyword} {
|
, m_keyword{keyword} {
|
||||||
this->sentreep(sentreep);
|
this->sentreep(sentreep);
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ void CfgGraph::rpoBlocks() {
|
||||||
// Assign edge IDs
|
// Assign edge IDs
|
||||||
size_t edgeCount = 0;
|
size_t edgeCount = 0;
|
||||||
for (V3GraphVertex& v : vertices()) {
|
for (V3GraphVertex& v : vertices()) {
|
||||||
|
// cppcheck-suppress constVariableReference // cppcheck is wrong
|
||||||
for (V3GraphEdge& e : v.outEdges()) static_cast<CfgEdge&>(e).m_id = edgeCount++;
|
for (V3GraphEdge& e : v.outEdges()) static_cast<CfgEdge&>(e).m_id = edgeCount++;
|
||||||
}
|
}
|
||||||
UASSERT_OBJ(edgeCount == m_nEdges, m_enterp, "Inconsistent edge count");
|
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
|
// STATE
|
||||||
CfgGraph* const m_cfgp; // The control flow graph this CfgBlock is under
|
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
|
// V3GraphEdge::user() is set to the unique id by CfgBuilder
|
||||||
std::vector<AstNodeStmt*> m_stmtps; // statements in this CfgBlock
|
std::vector<AstNodeStmt*> m_stmtps; // statements in this CfgBlock
|
||||||
|
|
||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
// ID (reverse post-order numpber) of this block
|
|
||||||
inline size_t id();
|
|
||||||
inline size_t id() const;
|
|
||||||
|
|
||||||
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
||||||
inline explicit CfgBlock(CfgGraph* cfgp);
|
inline explicit CfgBlock(CfgGraph* cfgp);
|
||||||
|
|
@ -109,6 +106,10 @@ class CfgBlock final : public V3GraphVertex {
|
||||||
public:
|
public:
|
||||||
// PUBLIC METHODS
|
// 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?
|
// Is this the entry block of the CFG?
|
||||||
bool isEnter() const { return inEmpty(); }
|
bool isEnter() const { return inEmpty(); }
|
||||||
// Is this the exit block of the CFG?
|
// Is this the exit block of the CFG?
|
||||||
|
|
@ -185,19 +186,20 @@ class CfgEdge final : public V3GraphEdge {
|
||||||
|
|
||||||
// STATE - Immutable after construction, set by CfgBuilder
|
// STATE - Immutable after construction, set by CfgBuilder
|
||||||
CfgGraph* const m_cfgp; // The control flow graph this CfgEdge is under
|
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
|
// PRIVATE METHODS
|
||||||
// Unique ID of this CfgEdge - no particular meaning
|
|
||||||
inline size_t id();
|
|
||||||
inline size_t id() const;
|
|
||||||
|
|
||||||
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
||||||
inline CfgEdge(CfgGraph* graphp, CfgBlock* srcp, CfgBlock* dstp);
|
inline CfgEdge(CfgGraph* graphp, CfgBlock* srcp, CfgBlock* dstp);
|
||||||
~CfgEdge() override = default;
|
~CfgEdge() override = default;
|
||||||
|
|
||||||
public:
|
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
|
// Source/destination CfgBlock
|
||||||
const CfgBlock* srcp() const { return static_cast<const CfgBlock*>(fromp()); }
|
const CfgBlock* srcp() const { return static_cast<const CfgBlock*>(fromp()); }
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ class CfgBuilder final : public VNVisitorConst {
|
||||||
while (!unreachableps.empty()) {
|
while (!unreachableps.empty()) {
|
||||||
V3GraphVertex* const vtxp = unreachableps.back();
|
V3GraphVertex* const vtxp = unreachableps.back();
|
||||||
unreachableps.pop_back();
|
unreachableps.pop_back();
|
||||||
for (V3GraphEdge& edge : vtxp->outEdges()) {
|
for (const V3GraphEdge& edge : vtxp->outEdges()) {
|
||||||
--m_cfgp->m_nEdges;
|
--m_cfgp->m_nEdges;
|
||||||
if (edge.top()->inSize1()) unreachableps.emplace_back(edge.top());
|
if (edge.top()->inSize1()) unreachableps.emplace_back(edge.top());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
440
src/V3Dfg.cpp
440
src/V3Dfg.cpp
|
|
@ -24,15 +24,42 @@
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
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)
|
DfgGraph::DfgGraph(AstModule* modulep, const string& name)
|
||||||
: m_modulep{modulep}
|
: m_modulep{modulep}
|
||||||
, m_name{name} {}
|
, m_name{name} {}
|
||||||
|
|
||||||
DfgGraph::~DfgGraph() {
|
DfgGraph::~DfgGraph() {
|
||||||
forEachVertex([](DfgVertex& vtxp) { delete &vtxp; });
|
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
||||||
|
|
@ -133,40 +160,21 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
||||||
// Constants have no inputs
|
// Constants have no inputs
|
||||||
// Hook up inputs of cloned variables
|
// Hook up inputs of cloned variables
|
||||||
for (const DfgVertexVar& vtx : m_varVertices) {
|
for (const DfgVertexVar& vtx : m_varVertices) {
|
||||||
// All variable vertices are unary
|
DfgVertexVar* const cp = vtxp2clonep.at(&vtx)->as<DfgVertexVar>();
|
||||||
if (const DfgVertex* const srcp = vtx.srcp()) {
|
if (const DfgVertex* const srcp = vtx.srcp()) cp->srcp(vtxp2clonep.at(srcp));
|
||||||
vtxp2clonep.at(&vtx)->as<DfgVertexVar>()->srcp(vtxp2clonep.at(srcp));
|
if (const DfgVertex* const defp = vtx.defaultp()) cp->defaultp(vtxp2clonep.at(defp));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Hook up inputs of cloned operation vertices
|
// Hook up inputs of cloned operation vertices
|
||||||
for (const DfgVertex& vtx : m_opVertices) {
|
for (const DfgVertex& vtx : m_opVertices) {
|
||||||
if (vtx.is<DfgVertexVariadic>()) {
|
if (vtx.is<DfgVertexVariadic>()) {
|
||||||
switch (vtx.type()) {
|
switch (vtx.type()) {
|
||||||
case VDfgType::atSpliceArray: {
|
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::atSplicePacked: {
|
case VDfgType::atSplicePacked: {
|
||||||
const DfgSplicePacked* const vp = vtx.as<DfgSplicePacked>();
|
const DfgVertexSplice* const vp = vtx.as<DfgVertexSplice>();
|
||||||
DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as<DfgSplicePacked>();
|
DfgVertexSplice* const cp = vtxp2clonep.at(vp)->as<DfgVertexSplice>();
|
||||||
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
vp->foreachDriver([&](const DfgVertex& src, uint32_t lo, FileLine* flp) {
|
||||||
if (const DfgVertex* const srcVp = edge.sourcep()) {
|
cp->addDriver(vtxp2clonep.at(&src), lo, flp);
|
||||||
DfgVertex* const srcCp = vtxp2clonep.at(srcVp);
|
return false;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -178,14 +186,8 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DfgVertex* const cp = vtxp2clonep.at(&vtx);
|
DfgVertex* const cp = vtxp2clonep.at(&vtx);
|
||||||
const auto oSourceEdges = vtx.sourceEdges();
|
for (size_t i = 0; i < vtx.nInputs(); ++i) {
|
||||||
auto cSourceEdges = cp->sourceEdges();
|
cp->inputp(i, vtxp2clonep.at(vtx.inputp(i)));
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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'.
|
// Variabels that are present in 'this', make them use the DfgVertexVar in 'this'.
|
||||||
if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to<DfgVertexVar*>()) {
|
if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to<DfgVertexVar*>()) {
|
||||||
DfgVertex* const srcp = vtxp->srcp();
|
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);
|
vtxp->replaceWith(altp);
|
||||||
if (srcp) altp->srcp(srcp);
|
if (srcp) altp->srcp(srcp);
|
||||||
|
if (defaultp) altp->defaultp(defaultp);
|
||||||
VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp);
|
VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -285,68 +290,53 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&
|
||||||
|
|
||||||
// Dump one DfgVertex in Graphviz format
|
// Dump one DfgVertex in Graphviz format
|
||||||
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
|
if (const DfgVertexVar* const varVtxp = vtx.cast<DfgVertexVar>()) {
|
||||||
if (const DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
|
|
||||||
const AstNode* const nodep = varVtxp->nodep();
|
const AstNode* const nodep = varVtxp->nodep();
|
||||||
const AstVar* const varp = varVtxp->varp();
|
const AstVar* const varp = varVtxp->varp();
|
||||||
os << toDotId(vtx);
|
os << toDotId(vtx);
|
||||||
os << " [label=\"" << nodep->prettyName() << '\n';
|
// Begin attributes
|
||||||
os << cvtToHex(varVtxp) << '\n';
|
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()) {
|
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);
|
varVtxp->dtypep()->dumpSmall(os);
|
||||||
os << " / F" << varVtxp->fanout() << '"';
|
os << " / F" << varVtxp->fanout();
|
||||||
|
// End 'label'
|
||||||
if (varp->direction() == VDirection::INPUT) {
|
os << '"';
|
||||||
os << ", shape=box, style=filled, fillcolor=chartreuse2"; // Green
|
// Shape
|
||||||
} else if (varp->direction() == VDirection::OUTPUT) {
|
if (varVtxp->is<DfgVarPacked>()) {
|
||||||
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 << ", shape=box";
|
os << ", shape=box";
|
||||||
}
|
} else if (varVtxp->is<DfgVarArray>()) {
|
||||||
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 {
|
|
||||||
os << ", shape=box3d";
|
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";
|
os << "]\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -405,8 +395,12 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
os << toDotId(vtx);
|
os << toDotId(vtx);
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
V3EmitV::debugVerilogForTree(logicp->nodep(), 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 << " [label=\"";
|
||||||
os << VString::replaceSubstr(VString::replaceSubstr(ss.str(), "\n", "\\l"), "\"", "\\\"");
|
os << str;
|
||||||
os << "\\n" << cvtToHex(&vtx);
|
os << "\\n" << cvtToHex(&vtx);
|
||||||
os << "\"\n";
|
os << "\"\n";
|
||||||
os << ", shape=box, style=\"rounded,filled\", fillcolor=cornsilk, nojustify=true";
|
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";
|
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,
|
void DfgGraph::dumpDot(std::ostream& os, const std::string& label,
|
||||||
std::function<bool(const DfgVertex&)> p) const {
|
std::function<bool(const DfgVertex&)> p) const {
|
||||||
// This generates a graphviz dump, https://www.graphviz.org
|
// 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);
|
dumpDotVertex(os, vtx);
|
||||||
});
|
});
|
||||||
// Emit all edges
|
// Emit all edges
|
||||||
forEachVertex([&](const DfgVertex& vtx) { //
|
forEachVertex([&](const DfgVertex& vtx) {
|
||||||
if (!p(vtx)) return;
|
if (!p(vtx)) return;
|
||||||
vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { //
|
for (size_t i = 0; i < vtx.nInputs(); ++i) {
|
||||||
if (!e.sourcep() || !p(*e.sourcep())) return;
|
DfgVertex* const srcp = vtx.inputp(i);
|
||||||
dumpDotEdge(os, e, i);
|
if (!srcp) continue;
|
||||||
});
|
if (!p(*srcp)) continue;
|
||||||
|
os << toDotId(*srcp) << " -> " << toDotId(vtx);
|
||||||
|
os << " [headlabel=\"" << vtx.srcName(i) << "\"]";
|
||||||
|
os << '\n';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
|
|
@ -527,9 +514,15 @@ dfgGraphCollectCone(const std::vector<const DfgVertex*>& vtxps) {
|
||||||
if (!resp->insert(vtxp).second) continue;
|
if (!resp->insert(vtxp).second) continue;
|
||||||
// Enqueue all siblings of this vertex.
|
// Enqueue all siblings of this vertex.
|
||||||
if VL_CONSTEXPR_CXX17 (T_SinksNotSources) {
|
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 {
|
} else {
|
||||||
vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); });
|
vtxp->foreachSource([&](const DfgVertex& src) {
|
||||||
|
queue.push_back(&src);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Done
|
// Done
|
||||||
|
|
@ -546,73 +539,8 @@ DfgGraph::sinkCone(const std::vector<const DfgVertex*>& vtxps) const {
|
||||||
return dfgGraphCollectCone<true>(vtxps);
|
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::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||||
: m_filelinep{flp}
|
: m_filelinep{flp}
|
||||||
|
|
@ -621,12 +549,6 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType*
|
||||||
dfg.addVertex(*this);
|
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 {
|
bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
||||||
// If same vertex, then equal
|
// If same vertex, then equal
|
||||||
if (this == &that) return true;
|
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 (this->dtypep() != that.dtypep()) return false;
|
||||||
|
|
||||||
// If different number of inputs, then not equal
|
// If different number of inputs, then not equal
|
||||||
auto thisPair = this->sourceEdges();
|
if (this->nInputs() != that.nInputs()) return false;
|
||||||
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;
|
|
||||||
|
|
||||||
// Check vertex specifics
|
// Check vertex specifics
|
||||||
if (!this->selfEquals(that)) return false;
|
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
|
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
|
||||||
uint8_t& result = cache[key];
|
uint8_t& result = cache[key];
|
||||||
if (!result) {
|
if (!result) {
|
||||||
result = 2; // Assume equals
|
const bool equal = [&]() {
|
||||||
for (size_t i = 0; i < thisArity; ++i) {
|
for (size_t i = 0; i < nInputs(); ++i) {
|
||||||
const DfgVertex* const thisSrcVtxp = thisSrcEdgesp[i].m_sourcep;
|
const DfgVertex* const ap = this->inputp(i);
|
||||||
const DfgVertex* const thatSrcVtxp = thatSrcEdgesp[i].m_sourcep;
|
const DfgVertex* const bp = that.inputp(i);
|
||||||
if (thisSrcVtxp == thatSrcVtxp) continue;
|
if (!ap && !bp) continue;
|
||||||
if (!thisSrcVtxp || !thatSrcVtxp || !thisSrcVtxp->equals(*thatSrcVtxp, cache)) {
|
if (!ap || !bp) return false;
|
||||||
result = 1; // Mark not equal
|
if (!ap->equals(*bp, cache)) return false;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}();
|
||||||
|
result = (static_cast<uint8_t>(equal) << 1) | 1;
|
||||||
}
|
}
|
||||||
return result >> 1;
|
return result >> 1;
|
||||||
}
|
}
|
||||||
|
|
@ -678,17 +595,11 @@ V3Hash DfgVertex::hash() {
|
||||||
// variables, which we rely on.
|
// variables, which we rely on.
|
||||||
if (!is<DfgVertexVar>()) {
|
if (!is<DfgVertexVar>()) {
|
||||||
hash += m_type;
|
hash += m_type;
|
||||||
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) {
|
hash += size();
|
||||||
hash += adtypep->elementsConst();
|
foreachSource([&](DfgVertex& vtx) {
|
||||||
// TODO: maybe include sub-dtype, but not hugely important at the moment
|
hash += vtx.hash();
|
||||||
} else {
|
return false;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
result = hash;
|
result = hash;
|
||||||
}
|
}
|
||||||
|
|
@ -697,7 +608,10 @@ V3Hash DfgVertex::hash() {
|
||||||
|
|
||||||
uint32_t DfgVertex::fanout() const {
|
uint32_t DfgVertex::fanout() const {
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
forEachSinkEdge([&](const DfgEdge&) { ++result; });
|
foreachSink([&](const DfgVertex&) {
|
||||||
|
++result;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -708,51 +622,52 @@ DfgVertexVar* DfgVertex::getResultVar() {
|
||||||
// Inspect existing variables written by this vertex, and choose one
|
// Inspect existing variables written by this vertex, and choose one
|
||||||
DfgVertexVar* resp = nullptr;
|
DfgVertexVar* resp = nullptr;
|
||||||
// cppcheck-has-bug-suppress constParameter
|
// cppcheck-has-bug-suppress constParameter
|
||||||
this->forEachSink([&resp](DfgVertex& sink) {
|
this->foreachSink([&resp](DfgVertex& sink) {
|
||||||
DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
|
DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
|
||||||
if (!varp) return;
|
if (!varp) return false;
|
||||||
// First variable found
|
// First variable found
|
||||||
if (!resp) {
|
if (!resp) {
|
||||||
resp = varp;
|
resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer those variables that must be kept anyway
|
// Prefer those variables that must be kept anyway
|
||||||
if (resp->hasExtRefs() != varp->hasExtRefs()) {
|
if (resp->hasExtRefs() != varp->hasExtRefs()) {
|
||||||
if (!resp->hasExtRefs()) resp = varp;
|
if (!resp->hasExtRefs()) resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (resp->hasModWrRefs() != varp->hasModWrRefs()) {
|
if (resp->hasModWrRefs() != varp->hasModWrRefs()) {
|
||||||
if (!resp->hasModWrRefs()) resp = varp;
|
if (!resp->hasModWrRefs()) resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (resp->hasDfgRefs() != varp->hasDfgRefs()) {
|
if (resp->hasDfgRefs() != varp->hasDfgRefs()) {
|
||||||
if (!resp->hasDfgRefs()) resp = varp;
|
if (!resp->hasDfgRefs()) resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Prefer those that already have module references
|
// Prefer those that already have module references
|
||||||
if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
|
if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
|
||||||
if (!resp->hasModRdRefs()) resp = varp;
|
if (!resp->hasModRdRefs()) resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Prefer real variabels over temporaries
|
// Prefer real variabels over temporaries
|
||||||
if (!resp->tmpForp() != !varp->tmpForp()) {
|
if (!resp->tmpForp() != !varp->tmpForp()) {
|
||||||
if (resp->tmpForp()) resp = varp;
|
if (resp->tmpForp()) resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Prefer the earlier one in source order
|
// Prefer the earlier one in source order
|
||||||
const FileLine& oldFlp = *(resp->fileline());
|
const FileLine& oldFlp = *(resp->fileline());
|
||||||
const FileLine& newFlp = *(varp->fileline());
|
const FileLine& newFlp = *(varp->fileline());
|
||||||
if (const int cmp = oldFlp.operatorCompare(newFlp)) {
|
if (const int cmp = oldFlp.operatorCompare(newFlp)) {
|
||||||
if (cmp > 0) resp = varp;
|
if (cmp > 0) resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Prefer the one with the lexically smaller name
|
// Prefer the one with the lexically smaller name
|
||||||
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
|
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
|
||||||
if (cmp > 0) resp = varp;
|
if (cmp > 0) resp = varp;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// 'resp' and 'varp' are all the same, keep using the existing 'resp'
|
// 'resp' and 'varp' are all the same, keep using the existing 'resp'
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
@ -777,14 +692,16 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
|
||||||
resultr = reinterpret_cast<AstScope*>(1);
|
resultr = reinterpret_cast<AstScope*>(1);
|
||||||
// Find scope based on sources, falling back on the root scope
|
// Find scope based on sources, falling back on the root scope
|
||||||
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
|
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
|
||||||
AstScope* foundp = rootp;
|
AstScope* foundp = nullptr;
|
||||||
const auto edges = sourceEdges();
|
foreachSource([&](DfgVertex& src) {
|
||||||
for (size_t i = 0; i < edges.second; ++i) {
|
AstScope* const scp = src.scopep(cache, true);
|
||||||
const DfgEdge& edge = edges.first[i];
|
if (scp != rootp) {
|
||||||
foundp = edge.sourcep()->scopep(cache, true);
|
foundp = scp;
|
||||||
if (foundp != rootp) break;
|
return true;
|
||||||
}
|
}
|
||||||
resultr = foundp;
|
return false;
|
||||||
|
});
|
||||||
|
resultr = foundp ? foundp : rootp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Die on a graph circular through operation vertices
|
// 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) {
|
void DfgVertex::unlinkDelete(DfgGraph& dfg) {
|
||||||
// Unlink source edges
|
|
||||||
forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
|
|
||||||
// Unlink sink edges
|
// Unlink sink edges
|
||||||
forEachSinkEdge([](DfgEdge& edge) { edge.unlinkSource(); });
|
while (!m_sinks.empty()) m_sinks.frontp()->unlinkSrcp();
|
||||||
// Remove from graph
|
// Remove from graph
|
||||||
dfg.removeVertex(*this);
|
dfg.removeVertex(*this);
|
||||||
// Delete
|
// Delete - this will unlink sources
|
||||||
delete this;
|
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
|
// DfgVisitor
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "V3Dfg__gen_visitor_defns.h" // From ./astgen
|
#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
|
// STATE
|
||||||
DfgGraph& m_dfg; // The graph being built
|
DfgGraph& m_dfg; // The graph being built
|
||||||
V3DfgAstToDfgContext& m_ctx; // The context for stats
|
V3DfgAstToDfgContext& m_ctx; // The context for stats
|
||||||
|
AstScope* m_scopep = nullptr; // The current scope, iff T_Scoped
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
static Variable* getTarget(const AstVarRef* refp) {
|
static Variable* getTarget(const AstVarRef* refp) {
|
||||||
|
|
@ -104,7 +105,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
if (VN_IS(vrefp, VarXRef)) return true;
|
if (VN_IS(vrefp, VarXRef)) return true;
|
||||||
if (vrefp->access().isReadOnly()) return false;
|
if (vrefp->access().isReadOnly()) return false;
|
||||||
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
|
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));
|
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
@ -126,7 +127,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
if (VN_IS(vrefp, VarXRef)) return true;
|
if (VN_IS(vrefp, VarXRef)) return true;
|
||||||
if (vrefp->access().isWriteOnly()) return false;
|
if (vrefp->access().isWriteOnly()) return false;
|
||||||
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
|
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));
|
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
@ -152,7 +153,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
|
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
|
||||||
resp->reserve(varps->size());
|
resp->reserve(varps->size());
|
||||||
for (Variable* const varp : *varps) {
|
for (Variable* const varp : *varps) {
|
||||||
if (!DfgGraph::isSupported(varp)) {
|
if (!V3Dfg::isSupported(varp)) {
|
||||||
++m_ctx.m_nonRepVar;
|
++m_ctx.m_nonRepVar;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +189,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherRead(nodep);
|
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherRead(nodep);
|
||||||
if (!iVarpsp) return false;
|
if (!iVarpsp) return false;
|
||||||
// Create the DfgLogic
|
// 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 it up
|
||||||
connect(*logicp, *iVarpsp, *oVarpsp);
|
connect(*logicp, *iVarpsp, *oVarpsp);
|
||||||
// Done
|
// Done
|
||||||
|
|
@ -218,7 +219,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherLive(*cfgp);
|
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherLive(*cfgp);
|
||||||
if (!iVarpsp) return false;
|
if (!iVarpsp) return false;
|
||||||
// Create the DfgLogic
|
// 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 it up
|
||||||
connect(*logicp, *iVarpsp, *oVarpsp);
|
connect(*logicp, *iVarpsp, *oVarpsp);
|
||||||
// Done
|
// Done
|
||||||
|
|
@ -241,7 +242,11 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); }
|
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 {
|
void visit(AstActive* nodep) override {
|
||||||
if (nodep->hasCombo()) {
|
if (nodep->hasCombo()) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
@ -268,6 +273,8 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
, m_ctx{ctx} {
|
, m_ctx{ctx} {
|
||||||
iterate(&root);
|
iterate(&root);
|
||||||
}
|
}
|
||||||
|
VL_UNCOPYABLE(AstToDfgVisitor);
|
||||||
|
VL_UNMOVABLE(AstToDfgVisitor);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) {
|
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
|
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_lsb = 0; // LSB to extract from the currently visited Vertex
|
||||||
uint32_t m_msb = 0; // MSB 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!
|
// Result of tracing the currently visited Vertex. Use SET_RESULT below!
|
||||||
DfgVertex* m_resp = nullptr;
|
DfgVertex* m_resp = nullptr;
|
||||||
std::vector<DfgVertex*> m_newVtxps; // New vertices created during the traversal
|
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");
|
"Should only create Const, Sel, Concat, Exend Vertices without aggressive mode");
|
||||||
|
|
||||||
if VL_CONSTEXPR_CXX17 (std::is_same<DfgConst, Vertex>::value) {
|
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);
|
vtxp->template setUser<uint64_t>(0);
|
||||||
m_newVtxps.emplace_back(vtxp);
|
m_newVtxps.emplace_back(vtxp);
|
||||||
return reinterpret_cast<Vertex*>(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 ...
|
// Vertex is DfgConst, in which case this code is unreachable ...
|
||||||
using Vtx = typename std::conditional<std::is_same<DfgConst, Vertex>::value, DfgSel,
|
using Vtx = typename std::conditional<std::is_same<DfgConst, Vertex>::value, DfgSel,
|
||||||
Vertex>::type;
|
Vertex>::type;
|
||||||
AstNodeDType* const dtypep = DfgGraph::dtypePacked(width);
|
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
||||||
Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep};
|
Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep};
|
||||||
vtxp->template setUser<uint64_t>(0);
|
vtxp->template setUser<uint64_t>(0);
|
||||||
m_newVtxps.emplace_back(vtxp);
|
m_newVtxps.emplace_back(vtxp);
|
||||||
|
|
@ -158,9 +159,13 @@ class TraceDriver final : public DfgVisitor {
|
||||||
// Trace the vertex
|
// Trace the vertex
|
||||||
onStackr = true;
|
onStackr = true;
|
||||||
|
|
||||||
if (vtxp->getUser<uint64_t>() != m_component) {
|
// If the currently traced vertex is in a different component, then we
|
||||||
// If the currently traced vertex is in a different component,
|
// found what we were looking for. However, keep going past a splice,
|
||||||
// then we found what we were looking for.
|
// 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) {
|
if (msb != vtxp->width() - 1 || lsb != 0) {
|
||||||
// Apply a Sel to extract the relevant bits if only a part is needed
|
// Apply a Sel to extract the relevant bits if only a part is needed
|
||||||
DfgSel* const selp = make<DfgSel>(vtxp, msb - lsb + 1);
|
DfgSel* const selp = make<DfgSel>(vtxp, msb - lsb + 1);
|
||||||
|
|
@ -294,30 +299,25 @@ class TraceDriver final : public DfgVisitor {
|
||||||
, m_msb{msb} {}
|
, m_msb{msb} {}
|
||||||
};
|
};
|
||||||
std::vector<Driver> drivers;
|
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
|
// Look at all the drivers, one might cover the whole range, but also gathe all drivers
|
||||||
const auto pair = vtxp->sourceEdges();
|
bool tryWholeDefault = m_defaultp;
|
||||||
bool tryWholeDefault = defaultp;
|
const bool done = vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
|
||||||
for (size_t i = 0; i < pair.second; ++i) {
|
const uint32_t msb = lsb + src.width() - 1;
|
||||||
DfgVertex* const srcp = pair.first[i].sourcep();
|
drivers.emplace_back(&src, lsb, msb);
|
||||||
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);
|
|
||||||
// Check if this driver covers any of the bits, then we can't use whole default
|
// 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 (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false;
|
||||||
// If it does not cover the whole searched bit range, move on
|
// 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
|
// Driver covers whole search range, trace that and we are done
|
||||||
SET_RESULT(trace(srcp, m_msb - lsb, m_lsb - lsb));
|
SET_RESULT(trace(&src, m_msb - lsb, m_lsb - lsb));
|
||||||
return;
|
return true;
|
||||||
}
|
});
|
||||||
|
if (done) return;
|
||||||
|
|
||||||
// Trace the default driver if no other drivers cover the searched range
|
// Trace the default driver if no other drivers cover the searched range
|
||||||
if (defaultp && tryWholeDefault) {
|
if (tryWholeDefault) {
|
||||||
SET_RESULT(trace(defaultp, m_msb, m_lsb));
|
SET_RESULT(trace(m_defaultp, m_msb, m_lsb));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -336,8 +336,8 @@ class TraceDriver final : public DfgVisitor {
|
||||||
if (driver.m_lsb > m_msb) break;
|
if (driver.m_lsb > m_msb) break;
|
||||||
// Gap below this driver, trace default to fill it
|
// Gap below this driver, trace default to fill it
|
||||||
if (driver.m_lsb > m_lsb) {
|
if (driver.m_lsb > m_lsb) {
|
||||||
if (!defaultp) return;
|
if (!m_defaultp) return;
|
||||||
DfgVertex* const termp = trace(defaultp, driver.m_lsb - 1, m_lsb);
|
DfgVertex* const termp = trace(m_defaultp, driver.m_lsb - 1, m_lsb);
|
||||||
if (!termp) return;
|
if (!termp) return;
|
||||||
termps.emplace_back(termp);
|
termps.emplace_back(termp);
|
||||||
m_lsb = driver.m_lsb;
|
m_lsb = driver.m_lsb;
|
||||||
|
|
@ -351,8 +351,8 @@ class TraceDriver final : public DfgVisitor {
|
||||||
m_lsb = lim + 1;
|
m_lsb = lim + 1;
|
||||||
}
|
}
|
||||||
if (m_msb >= m_lsb) {
|
if (m_msb >= m_lsb) {
|
||||||
if (!defaultp) return;
|
if (!m_defaultp) return;
|
||||||
DfgVertex* const termp = trace(defaultp, m_msb, m_lsb);
|
DfgVertex* const termp = trace(m_defaultp, m_msb, m_lsb);
|
||||||
if (!termp) return;
|
if (!termp) return;
|
||||||
termps.emplace_back(termp);
|
termps.emplace_back(termp);
|
||||||
}
|
}
|
||||||
|
|
@ -374,6 +374,8 @@ class TraceDriver final : public DfgVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(DfgVarPacked* vtxp) override {
|
void visit(DfgVarPacked* vtxp) override {
|
||||||
|
VL_RESTORER(m_defaultp);
|
||||||
|
m_defaultp = vtxp->defaultp();
|
||||||
if (DfgVertex* const srcp = vtxp->srcp()) {
|
if (DfgVertex* const srcp = vtxp->srcp()) {
|
||||||
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
||||||
return;
|
return;
|
||||||
|
|
@ -719,17 +721,30 @@ class IndependentBits final : public DfgVisitor {
|
||||||
void visit(DfgSplicePacked* vtxp) override {
|
void visit(DfgSplicePacked* vtxp) override {
|
||||||
// Combine the masks of all drivers
|
// Combine the masks of all drivers
|
||||||
V3Number& m = MASK(vtxp);
|
V3Number& m = MASK(vtxp);
|
||||||
DfgVertex* const defaultp = vtxp->defaultp();
|
vtxp->foreachDriver([&](DfgVertex& src, uint32_t lo) {
|
||||||
if (defaultp) m = MASK(defaultp);
|
m.opSelInto(MASK(&src), lo, src.width());
|
||||||
vtxp->forEachSourceEdge([&](DfgEdge& edge, size_t i) {
|
return false;
|
||||||
const DfgVertex* const srcp = edge.sourcep();
|
|
||||||
if (srcp == defaultp) return;
|
|
||||||
m.opSelInto(MASK(srcp), vtxp->driverLo(i), srcp->width());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(DfgVarPacked* vtxp) override {
|
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 {
|
void visit(DfgArraySel* vtxp) override {
|
||||||
|
|
@ -924,8 +939,9 @@ class IndependentBits final : public DfgVisitor {
|
||||||
if (VN_IS(currp->dtypep(), UnpackArrayDType)) {
|
if (VN_IS(currp->dtypep(), UnpackArrayDType)) {
|
||||||
// For an unpacked array vertex, just enque it's sinks.
|
// For an unpacked array vertex, just enque it's sinks.
|
||||||
// (There can be no loops through arrays directly)
|
// (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);
|
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -940,8 +956,9 @@ class IndependentBits final : public DfgVisitor {
|
||||||
|
|
||||||
// If mask changed, enqueue sinks
|
// If mask changed, enqueue sinks
|
||||||
if (!prevMask.isCaseEq(maskCurr)) {
|
if (!prevMask.isCaseEq(maskCurr)) {
|
||||||
currp->forEachSink([&](DfgVertex& vtx) {
|
currp->foreachSink([&](DfgVertex& vtx) {
|
||||||
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&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)
|
// 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) {
|
static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||||
size_t nImprovements = 0;
|
size_t nImprovements = 0;
|
||||||
const uint64_t component = vtxp->getUser<uint64_t>();
|
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
|
// 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
|
// Only handle Sel
|
||||||
DfgSel* const selp = sink.cast<DfgSel>();
|
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
|
// Try to find the driver of the selected bits outside the cycle
|
||||||
DfgVertex* const fixp
|
DfgVertex* const fixp
|
||||||
= TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false);
|
= 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.
|
// Found an out-of-cycle driver. We can replace this sel with that.
|
||||||
selp->replaceWith(fixp);
|
selp->replaceWith(fixp);
|
||||||
selp->unlinkDelete(dfg);
|
selp->unlinkDelete(dfg);
|
||||||
++nImprovements;
|
++nImprovements;
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
return nImprovements;
|
return nImprovements;
|
||||||
}
|
}
|
||||||
|
|
@ -998,25 +1016,25 @@ class FixUpSelDrivers final {
|
||||||
static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
|
||||||
size_t nImprovements = 0;
|
size_t nImprovements = 0;
|
||||||
const uint64_t component = vtxp->getUser<uint64_t>();
|
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
|
// 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
|
// Only handle ArraySels
|
||||||
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
||||||
if (!aselp) return;
|
if (!aselp) return false;
|
||||||
// First, try to fix up the whole word
|
// First, try to fix up the whole word
|
||||||
DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false);
|
if (DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false)) {
|
||||||
if (fixp) {
|
|
||||||
// Found an out-of-cycle driver for the whole ArraySel
|
// Found an out-of-cycle driver for the whole ArraySel
|
||||||
aselp->replaceWith(fixp);
|
aselp->replaceWith(fixp);
|
||||||
aselp->unlinkDelete(dfg);
|
aselp->unlinkDelete(dfg);
|
||||||
++nImprovements;
|
++nImprovements;
|
||||||
} else {
|
return false;
|
||||||
|
}
|
||||||
// Attempt to fix up piece-wise at Sels applied to the ArraySel
|
// Attempt to fix up piece-wise at Sels applied to the ArraySel
|
||||||
nImprovements += fixUpSelSinks(dfg, aselp);
|
nImprovements += fixUpSelSinks(dfg, aselp);
|
||||||
// Remove if became unused
|
// Remove if became unused
|
||||||
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
||||||
}
|
return false;
|
||||||
});
|
});
|
||||||
return nImprovements;
|
return nImprovements;
|
||||||
}
|
}
|
||||||
|
|
@ -1045,16 +1063,17 @@ class FixUpIndependentRanges final {
|
||||||
// Returns a bitmask set if that bit of 'vtxp' is used (has a sink)
|
// Returns a bitmask set if that bit of 'vtxp' is used (has a sink)
|
||||||
static V3Number computeUsedBits(DfgVertex* vtxp) {
|
static V3Number computeUsedBits(DfgVertex* vtxp) {
|
||||||
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
|
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 used via a Sel, mark the selected bits used
|
||||||
if (const DfgSel* const selp = sink.cast<DfgSel>()) {
|
if (const DfgSel* const selp = sink.cast<DfgSel>()) {
|
||||||
uint32_t lsb = selp->lsb();
|
uint32_t lsb = selp->lsb();
|
||||||
uint32_t msb = lsb + selp->width() - 1;
|
uint32_t msb = lsb + selp->width() - 1;
|
||||||
for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '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
|
// Used without a Sel, so all bits are used
|
||||||
result.setAllBits1();
|
result.setAllBits1();
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -1097,7 +1116,7 @@ class FixUpIndependentRanges final {
|
||||||
}
|
}
|
||||||
// Fall back on using the part of the variable (if dependent, or trace failed)
|
// Fall back on using the part of the variable (if dependent, or trace failed)
|
||||||
if (!termp) {
|
if (!termp) {
|
||||||
AstNodeDType* const dtypep = DfgGraph::dtypePacked(width);
|
AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
|
||||||
DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep};
|
DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep};
|
||||||
// Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp'
|
// Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp'
|
||||||
selp->setUser<uint64_t>(vtxp->getUser<uint64_t>());
|
selp->setUser<uint64_t>(vtxp->getUser<uint64_t>());
|
||||||
|
|
@ -1155,7 +1174,7 @@ class FixUpIndependentRanges final {
|
||||||
nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb);
|
nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb);
|
||||||
} else {
|
} else {
|
||||||
// The range is not used, just use constant 0 as a placeholder
|
// 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);
|
constp->setUser<uint64_t>(0);
|
||||||
termps.emplace_back(constp);
|
termps.emplace_back(constp);
|
||||||
}
|
}
|
||||||
|
|
@ -1178,7 +1197,7 @@ class FixUpIndependentRanges final {
|
||||||
for (size_t i = 1; i < termps.size(); ++i) {
|
for (size_t i = 1; i < termps.size(); ++i) {
|
||||||
DfgVertex* const termp = termps[i];
|
DfgVertex* const termp = termps[i];
|
||||||
const uint32_t catWidth = replacementp->width() + termp->width();
|
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};
|
DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep};
|
||||||
catp->rhsp(replacementp);
|
catp->rhsp(replacementp);
|
||||||
catp->lhsp(termp);
|
catp->lhsp(termp);
|
||||||
|
|
@ -1220,17 +1239,18 @@ public:
|
||||||
} else if (varp->is<DfgVarArray>()) {
|
} else if (varp->is<DfgVarArray>()) {
|
||||||
// For array variables, fix up element-wise
|
// For array variables, fix up element-wise
|
||||||
const uint64_t component = varp->getUser<uint64_t>();
|
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
|
// 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
|
// Only handle ArraySels with constant index
|
||||||
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
DfgArraySel* const aselp = sink.cast<DfgArraySel>();
|
||||||
if (!aselp) return;
|
if (!aselp) return false;
|
||||||
if (!aselp->bitp()->is<DfgConst>()) return;
|
if (!aselp->bitp()->is<DfgConst>()) return false;
|
||||||
// Fix up the word
|
// Fix up the word
|
||||||
nImprovements += fixUpPacked(dfg, aselp);
|
nImprovements += fixUpPacked(dfg, aselp);
|
||||||
// Remove if became unused
|
// Remove if became unused
|
||||||
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
|
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) { //
|
inline void setOperands(DfgVertexUnary* vtxp, DfgVertex* src0p) { //
|
||||||
vtxp->relinkSource<0>(src0p);
|
vtxp->inputp(0, src0p);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) {
|
inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) {
|
||||||
vtxp->relinkSource<0>(src0p);
|
vtxp->inputp(0, src0p);
|
||||||
vtxp->relinkSource<1>(src1p);
|
vtxp->inputp(1, src1p);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p,
|
inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p,
|
||||||
DfgVertex* src2p) {
|
DfgVertex* src2p) {
|
||||||
vtxp->relinkSource<0>(src0p);
|
vtxp->inputp(0, src0p);
|
||||||
vtxp->relinkSource<1>(src1p);
|
vtxp->inputp(1, src1p);
|
||||||
vtxp->relinkSource<2>(src2p);
|
vtxp->inputp(2, src2p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get or create (and insert) vertex with given operands
|
// 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) {
|
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;
|
if (!entrypr) entrypr = vtxp;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void cache(CacheBinary& cache, DfgVertexBinary* vtxp) {
|
inline void cache(CacheBinary& cache, DfgVertexBinary* vtxp) {
|
||||||
DfgVertexBinary*& entrypr
|
DfgVertexBinary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1));
|
||||||
= getEntry(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>());
|
|
||||||
if (!entrypr) entrypr = vtxp;
|
if (!entrypr) entrypr = vtxp;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) {
|
inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) {
|
||||||
DfgVertexTernary*& entrypr
|
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;
|
if (!entrypr) entrypr = vtxp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,18 +287,17 @@ inline void invalidateByValue(CacheSel& cache, const DfgSel* vtxp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void invalidateByValue(CacheUnary& cache, const DfgVertexUnary* 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);
|
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void invalidateByValue(CacheBinary& cache, const DfgVertexBinary* vtxp) {
|
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);
|
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) {
|
inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) {
|
||||||
const auto it
|
const auto it = find(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
|
||||||
= find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>());
|
|
||||||
if (it != cache.end() && it->second == vtxp) cache.erase(it);
|
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;
|
const size_t rootIndex = vtxState.index = ++m_index;
|
||||||
|
|
||||||
// Visit children
|
// Visit children
|
||||||
vtx.forEachSink([&](DfgVertex& child) {
|
vtx.foreachSink([&](DfgVertex& child) {
|
||||||
VertexState& childSatate = child.user<VertexState>();
|
VertexState& childSatate = child.user<VertexState>();
|
||||||
// If the child has not yet been visited, then continue traversal
|
// If the child has not yet been visited, then continue traversal
|
||||||
if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate);
|
if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate);
|
||||||
|
|
@ -63,6 +63,7 @@ class ColorStronglyConnectedComponents final {
|
||||||
if (childSatate.component == UNASSIGNED) {
|
if (childSatate.component == UNASSIGNED) {
|
||||||
if (vtxState.index > childSatate.index) vtxState.index = childSatate.index;
|
if (vtxState.index > childSatate.index) vtxState.index = childSatate.index;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (vtxState.index == rootIndex) {
|
if (vtxState.index == rootIndex) {
|
||||||
|
|
@ -73,7 +74,7 @@ class ColorStronglyConnectedComponents final {
|
||||||
|| m_stack.back()->getUser<VertexState>().index < rootIndex;
|
|| m_stack.back()->getUser<VertexState>().index < rootIndex;
|
||||||
// We also need a separate component for vertices that drive themselves (which can
|
// 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).
|
// 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;
|
return &vtx == &sink;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -107,7 +108,7 @@ class ColorStronglyConnectedComponents final {
|
||||||
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||||
VertexState& vtxState = vtx.user<VertexState>();
|
VertexState& vtxState = vtx.user<VertexState>();
|
||||||
// If it has no input or no outputs, it cannot be part of a non-trivial SCC.
|
// 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,
|
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0,
|
||||||
&vtx, "Non circular variable must be in a trivial SCC"););
|
&vtx, "Non circular variable must be in a trivial SCC"););
|
||||||
vtxState.index = 0;
|
vtxState.index = 0;
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,14 @@ class SplitIntoComponents final {
|
||||||
item.user<size_t>() = m_componentCounter;
|
item.user<size_t>() = m_componentCounter;
|
||||||
|
|
||||||
// Enqueue all sources and sinks of this vertex.
|
// Enqueue all sources and sinks of this vertex.
|
||||||
item.forEachSource([&](DfgVertex& src) { queue.push_back(&src); });
|
item.foreachSource([&](DfgVertex& src) {
|
||||||
item.forEachSink([&](DfgVertex& dst) { queue.push_back(&dst); });
|
queue.push_back(&src);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
item.foreachSink([&](DfgVertex& dst) {
|
||||||
|
queue.push_back(&dst);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
} while (!queue.empty());
|
} while (!queue.empty());
|
||||||
|
|
||||||
// Done with this component
|
// Done with this component
|
||||||
|
|
@ -118,21 +124,6 @@ std::vector<std::unique_ptr<DfgGraph>> DfgGraph::splitIntoComponents(const std::
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtractCyclicComponents final {
|
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
|
// STATE
|
||||||
DfgGraph& m_dfg; // The input graph
|
DfgGraph& m_dfg; // The input graph
|
||||||
const std::string m_prefix; // Component name prefix
|
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;
|
std::unordered_map<const DfgVertexVar*, std::unordered_map<uint64_t, DfgVertexVar*>> m_clones;
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
void visitMergeSCCs(DfgVertex& vtx, uint64_t targetComponent) {
|
void addVertexAndExpandSiblings(DfgVertex& vtx, uint64_t component) {
|
||||||
VertexState vtxState{vtx};
|
// 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
|
void expandSiblings(DfgVertex& vtx, uint64_t component) {
|
||||||
if (vtxState.merged()) return;
|
UASSERT_OBJ(vtx.getUser<uint64_t>() == component, &vtx, "Traversal didn't stop");
|
||||||
|
vtx.foreachSink([&](DfgVertex& v) {
|
||||||
// Visiting vertex
|
addVertexAndExpandSiblings(v, component);
|
||||||
vtxState.setMerged();
|
return false;
|
||||||
|
|
||||||
// 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);
|
|
||||||
});
|
});
|
||||||
vtx.forEachSink([this, targetComponent](DfgVertex& other) {
|
vtx.foreachSource([&](DfgVertex& v) {
|
||||||
if (other.is<DfgVertexVar>()) return;
|
addVertexAndExpandSiblings(v, component);
|
||||||
visitMergeSCCs(other, targetComponent);
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void mergeSCCs() {
|
void expandComponents() {
|
||||||
// Ensure that component boundaries are always at variables, by merging SCCs. Merging stops
|
// Important fact that we will assume below: There are no path between
|
||||||
// at variable boundaries, so we don't need to iterate variables. Constants are reachable
|
// any two SCCs that do not go through a variable before reaching the
|
||||||
// from their sinks, or are unused, so we don't need to iterate them either.
|
// 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()) {
|
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||||
// Start DFS from each vertex that is in a non-trivial SCC, and merge everything
|
UASSERT_OBJ(!vtx.hasMultipleSinks(), &vtx, "Operation has multiple sinks");
|
||||||
// that is reachable from it into this component.
|
}
|
||||||
if (const uint64_t target = VertexState{vtx}.component()) visitMergeSCCs(vtx, target);
|
}
|
||||||
|
|
||||||
|
// 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()) {
|
||||||
|
if (const uint64_t targetComponent = vtx.getUser<uint64_t>()) {
|
||||||
|
expandSiblings(vtx, targetComponent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve clone of vertex in the given component
|
// Retrieve clone of vertex in the given component
|
||||||
DfgVertexVar& getClone(DfgVertexVar& vtx, uint64_t component) {
|
DfgVertexVar* getClone(DfgVertexVar& vtx, uint64_t component) {
|
||||||
UASSERT_OBJ(VertexState{vtx}.component() != component, &vtx,
|
UASSERT_OBJ(vtx.getUser<uint64_t>() != component, &vtx, "Vertex is in that component");
|
||||||
"Vertex is in that component");
|
|
||||||
DfgVertexVar*& clonep = m_clones[&vtx][component];
|
DfgVertexVar*& clonep = m_clones[&vtx][component];
|
||||||
if (!clonep) {
|
if (!clonep) {
|
||||||
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
|
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
|
||||||
|
|
@ -201,40 +233,52 @@ class ExtractCyclicComponents final {
|
||||||
clonep->setUser<uint64_t>(component);
|
clonep->setUser<uint64_t>(component);
|
||||||
clonep->tmpForp(vtx.tmpForp());
|
clonep->tmpForp(vtx.tmpForp());
|
||||||
}
|
}
|
||||||
return *clonep;
|
return clonep;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix edges that cross components
|
// Fix edges that cross components
|
||||||
void fixEdges(DfgVertexVar& vtx) {
|
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
|
// Fix up srcp and dstp (they must be the same component, or variable)
|
||||||
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) {
|
if (DfgVertex* const sp = vtx.srcp()) {
|
||||||
DfgVertex* const srcp = edge.sourcep();
|
const uint64_t srcComponent = sp->getUser<uint64_t>();
|
||||||
if (!srcp) return;
|
if (srcComponent != component) {
|
||||||
const uint64_t sourceComponent = VertexState{*srcp}.component();
|
UASSERT_OBJ(sp->is<DfgVertexVar>(), &vtx, "'srcp' in different component");
|
||||||
// Same component is OK
|
getClone(vtx, srcComponent)->srcp(sp);
|
||||||
if (sourceComponent == component) return;
|
vtx.srcp(nullptr);
|
||||||
// Relink the source to write the clone
|
}
|
||||||
edge.unlinkSource();
|
}
|
||||||
getClone(vtx, sourceComponent).srcp(srcp);
|
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;
|
||||||
});
|
});
|
||||||
|
for (DfgVertex* const sinkp : sinkps) {
|
||||||
// Fix up sinks in a different component
|
const uint64_t sinkComponent = sinkp->getUser<uint64_t>();
|
||||||
vtx.forEachSinkEdge([&](DfgEdge& edge) {
|
|
||||||
const uint64_t sinkComponent = VertexState{*edge.sinkp()}.component();
|
|
||||||
// Same component is OK
|
// Same component is OK
|
||||||
if (sinkComponent == component) return;
|
if (sinkComponent == component) continue;
|
||||||
// Relink the sink to read the clone
|
DfgVertex* const clonep = getClone(vtx, sinkComponent);
|
||||||
edge.relinkSource(&getClone(vtx, sinkComponent));
|
for (size_t i = 0; i < sinkp->nInputs(); ++i) {
|
||||||
});
|
if (sinkp->inputp(i) == &vtx) sinkp->inputp(i, clonep);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Vertex>
|
template <typename Vertex>
|
||||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||||
DfgVertex& vtx = *vtxp;
|
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_dfg.removeVertex(vtx);
|
||||||
m_components[component - 1]->addVertex(vtx);
|
m_components[component - 1]->addVertex(vtx);
|
||||||
}
|
}
|
||||||
|
|
@ -242,20 +286,15 @@ class ExtractCyclicComponents final {
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkEdges(DfgGraph& dfg) const {
|
void checkEdges(DfgGraph& dfg) const {
|
||||||
// Check that:
|
// Check that edges only cross components at variable boundaries
|
||||||
// - Edges only cross components at variable boundaries
|
|
||||||
// - Variable vertex sources are all connected.
|
|
||||||
dfg.forEachVertex([&](DfgVertex& vtx) {
|
dfg.forEachVertex([&](DfgVertex& vtx) {
|
||||||
const uint64_t component = VertexState{vtx}.component();
|
if (vtx.is<DfgVarPacked>()) return;
|
||||||
vtx.forEachSource([&](DfgVertex& src) {
|
const uint64_t component = vtx.getUser<uint64_t>();
|
||||||
if (src.is<DfgVertexVar>()) return; // OK to cross at variables
|
vtx.foreachSink([&](DfgVertex& snk) {
|
||||||
UASSERT_OBJ(component == VertexState{src}.component(), &vtx,
|
if (snk.is<DfgVertexVar>()) return false; // OK to cross at variables
|
||||||
"Edge crossing components without variable involvement");
|
UASSERT_OBJ(component == snk.getUser<uint64_t>(), &vtx,
|
||||||
});
|
|
||||||
vtx.forEachSink([&](DfgVertex& snk) {
|
|
||||||
if (snk.is<DfgVertexVar>()) return; // OK to cross at variables
|
|
||||||
UASSERT_OBJ(component == VertexState{snk}.component(), &vtx,
|
|
||||||
"Edge crossing components without variable involvement");
|
"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
|
// Check that each edge connects to a vertex that is within the same graph
|
||||||
dfg.forEachVertex([&](const DfgVertex& vtx) {
|
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");
|
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");
|
UASSERT_OBJ(vertices.count(&snk), &snk, "Sink vertex not in graph");
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -319,14 +360,13 @@ class ExtractCyclicComponents final {
|
||||||
: m_dfg{dfg}
|
: m_dfg{dfg}
|
||||||
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
|
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
|
||||||
// DfgVertex::user<uint64_t> is set to the SCC number by colorStronglyConnectedComponents,
|
// 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();
|
const auto userDataInUse = dfg.userDataInUse();
|
||||||
// Find all the non-trivial SCCs (and trivial cycles) in the graph
|
// Find all the non-trivial SCCs (and trivial cycles) in the graph
|
||||||
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg);
|
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg);
|
||||||
// If the graph was acyclic (which should be the common case), then we are done.
|
// If the graph was acyclic (which should be the common case), then we are done.
|
||||||
if (!numNonTrivialSCCs) return;
|
if (!numNonTrivialSCCs) return;
|
||||||
// Ensure that component boundaries are always at variables, by merging SCCs
|
// Ensure that component boundaries are always at variables, by expanding SCCs
|
||||||
mergeSCCs();
|
expandComponents();
|
||||||
// Extract the components
|
// Extract the components
|
||||||
extractComponents(numNonTrivialSCCs);
|
extractComponents(numNonTrivialSCCs);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,32 +92,23 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||||
const VNUser2InUse m_user2InUse;
|
const VNUser2InUse m_user2InUse;
|
||||||
|
|
||||||
// TYPES
|
// TYPES
|
||||||
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
||||||
struct Assignment final {
|
using Container = std::conditional_t<T_Scoped, AstActive, AstNodeModule>;
|
||||||
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} {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped
|
AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped
|
||||||
V3DfgDfgToAstContext& m_ctx; // The context for stats
|
V3DfgDfgToAstContext& m_ctx; // The context for stats
|
||||||
AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal
|
AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal
|
||||||
std::vector<Assignment> m_assignments; // Assignments to currently rendered variable
|
AstAlways* m_alwaysp = nullptr; // Process to add assignments to, if have a default driver
|
||||||
std::vector<Assignment> m_defaults; // Default assignments to currently rendered variable
|
Container* m_containerp = nullptr; // The AstNodeModule or AstActive to insert assigns into
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
static VariableType* getNode(const DfgVertexVar* vtxp) {
|
static Variable* getNode(const DfgVertexVar* vtxp) {
|
||||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||||
return reinterpret_cast<VariableType*>(vtxp->varScopep());
|
return reinterpret_cast<Variable*>(vtxp->varScopep());
|
||||||
} else {
|
} else {
|
||||||
return reinterpret_cast<VariableType*>(vtxp->varp());
|
return reinterpret_cast<Variable*>(vtxp->varp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,49 +149,57 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||||
return resultp;
|
return resultp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertDriver(std::vector<Assignment>& assignments, FileLine* flp, AstNodeExpr* lhsp,
|
void createAssignment(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
|
||||||
DfgVertex* driverp) {
|
// Keep track of statisticss
|
||||||
if (DfgSplicePacked* const sPackedp = driverp->cast<DfgSplicePacked>()) {
|
++m_ctx.m_resultEquations;
|
||||||
// Render defaults first
|
// Render the driver
|
||||||
if (DfgVertex* const defaultp = sPackedp->defaultp()) {
|
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp);
|
||||||
convertDriver(m_defaults, flp, lhsp, defaultp);
|
// 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;
|
||||||
}
|
}
|
||||||
// Render partial assignments of packed value
|
|
||||||
sPackedp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
// Otherwise create an AssignW
|
||||||
DfgVertex* const srcp = edge.sourcep();
|
m_containerp->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
|
||||||
if (srcp == sPackedp->defaultp()) return;
|
}
|
||||||
|
|
||||||
|
void convertDriver(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
|
||||||
|
if (DfgSplicePacked* const sPackedp = driverp->cast<DfgSplicePacked>()) {
|
||||||
|
// Partial assignment of packed value
|
||||||
|
sPackedp->foreachDriver([&](DfgVertex& src, uint32_t lo, FileLine* dflp) {
|
||||||
// Create Sel
|
// Create Sel
|
||||||
FileLine* const dflp = sPackedp->driverFileLine(i);
|
AstConst* const lsbp = new AstConst{dflp, lo};
|
||||||
AstConst* const lsbp = new AstConst{dflp, sPackedp->driverLo(i)};
|
const int width = static_cast<int>(src.width());
|
||||||
const int width = static_cast<int>(srcp->width());
|
|
||||||
AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width};
|
AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width};
|
||||||
// Convert source
|
// Convert source
|
||||||
convertDriver(assignments, dflp, nLhsp, srcp);
|
convertDriver(dflp, nLhsp, &src);
|
||||||
// Delete Sel - was cloned
|
// Delete Sel - was cloned
|
||||||
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DfgSpliceArray* const sArrayp = driverp->cast<DfgSpliceArray>()) {
|
if (DfgSpliceArray* const sArrayp = driverp->cast<DfgSpliceArray>()) {
|
||||||
UASSERT_OBJ(!sArrayp->defaultp(), flp, "Should not have a default assignment yet");
|
|
||||||
// Partial assignment of array variable
|
// Partial assignment of array variable
|
||||||
sArrayp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
sArrayp->foreachDriver([&](DfgVertex& src, uint32_t lo, FileLine* dflp) {
|
||||||
DfgVertex* const driverp = edge.sourcep();
|
UASSERT_OBJ(src.size() == 1, &src, "We only handle single elements");
|
||||||
UASSERT_OBJ(driverp, sArrayp, "Should have removed undriven sources");
|
|
||||||
UASSERT_OBJ(driverp->size() == 1, driverp, "We only handle single elements");
|
|
||||||
// Create ArraySel
|
// Create ArraySel
|
||||||
FileLine* const dflp = sArrayp->driverFileLine(i);
|
AstConst* const idxp = new AstConst{dflp, lo};
|
||||||
AstConst* const idxp = new AstConst{dflp, sArrayp->driverLo(i)};
|
|
||||||
AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp};
|
AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp};
|
||||||
// Convert source
|
// Convert source
|
||||||
if (const DfgUnitArray* const uap = driverp->cast<DfgUnitArray>()) {
|
if (const DfgUnitArray* const uap = src.cast<DfgUnitArray>()) {
|
||||||
convertDriver(assignments, dflp, nLhsp, uap->srcp());
|
convertDriver(dflp, nLhsp, uap->srcp());
|
||||||
} else {
|
} else {
|
||||||
convertDriver(assignments, dflp, nLhsp, driverp);
|
convertDriver(dflp, nLhsp, &src);
|
||||||
}
|
}
|
||||||
// Delete ArraySel - was cloned
|
// Delete ArraySel - was cloned
|
||||||
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -210,17 +209,14 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||||
AstConst* const idxp = new AstConst{flp, 0};
|
AstConst* const idxp = new AstConst{flp, 0};
|
||||||
AstArraySel* const nLhsp = new AstArraySel{flp, lhsp->cloneTreePure(false), idxp};
|
AstArraySel* const nLhsp = new AstArraySel{flp, lhsp->cloneTreePure(false), idxp};
|
||||||
// Convert source
|
// Convert source
|
||||||
convertDriver(assignments, flp, nLhsp, uap->srcp());
|
convertDriver(flp, nLhsp, uap->srcp());
|
||||||
// Delete ArraySel - was cloned
|
// Delete ArraySel - was cloned
|
||||||
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base case: assign vertex to current lhs
|
// Base case: assign vertex to current lhs
|
||||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp);
|
createAssignment(flp, lhsp->cloneTreePure(false), driverp);
|
||||||
assignments.emplace_back(flp, lhsp->cloneTreePure(false), rhsp);
|
|
||||||
++m_ctx.m_resultEquations;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
|
|
@ -258,7 +254,7 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||||
#include "V3Dfg__gen_dfg_to_ast.h"
|
#include "V3Dfg__gen_dfg_to_ast.h"
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
explicit DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx)
|
DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx)
|
||||||
: m_modp{dfg.modulep()}
|
: m_modp{dfg.modulep()}
|
||||||
, m_ctx{ctx} {
|
, m_ctx{ctx} {
|
||||||
// Convert the graph back to combinational assignments
|
// 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
|
// The graph must have been regularized, so we only need to render assignments
|
||||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||||
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
// 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;
|
++m_ctx.m_outputVariables;
|
||||||
|
|
||||||
// Render variable assignments
|
// Render variable assignments
|
||||||
FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline();
|
FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline();
|
||||||
AstVarRef* const lhsp = new AstVarRef{flp, getNode(&vtx), VAccess::WRITE};
|
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
|
// convetDriver always clones lhsp
|
||||||
VL_DO_DANGLING(lhsp->deleteTree(), 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
|
// Mark interfaces that might be referenced by a virtual interface
|
||||||
if (v3Global.hasVirtIfaces()) {
|
if (v3Global.hasVirtIfaces()) {
|
||||||
netlistp->typeTablep()->foreach([](AstIfaceRefDType* nodep) {
|
netlistp->typeTablep()->foreach([](const AstIfaceRefDType* nodep) {
|
||||||
if (!nodep->isVirtual()) return;
|
if (!nodep->isVirtual()) return;
|
||||||
nodep->ifaceViaCellp()->setHasVirtualRef();
|
nodep->ifaceViaCellp()->setHasVirtualRef();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,7 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
||||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||||
if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) {
|
if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) {
|
||||||
if (varp->hasSinks() && varp->isDrivenFullyByDfg()) {
|
if (varp->hasSinks() && varp->isDrivenFullyByDfg()) {
|
||||||
DfgVertex* const driverp = varp->srcp();
|
varp->replaceWith(varp->srcp());
|
||||||
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,16 +147,17 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||||
if (varp->hasDfgRefs()) continue;
|
if (varp->hasDfgRefs()) continue;
|
||||||
}
|
}
|
||||||
// Add sources of unused vertex to work list
|
// 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
|
// 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>();
|
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 already in work list then nothing to do
|
||||||
if (src.getUser<DfgVertex*>()) return;
|
if (src.getUser<DfgVertex*>()) return false;
|
||||||
// Actually add to work list.
|
// Actually add to work list.
|
||||||
src.setUser<DfgVertex*>(workListp);
|
src.setUser<DfgVertex*>(workListp);
|
||||||
workListp = &src;
|
workListp = &src;
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
// Remove the unused vertex
|
// Remove the unused vertex
|
||||||
vtxp->unlinkDelete(dfg);
|
vtxp->unlinkDelete(dfg);
|
||||||
|
|
@ -214,10 +214,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||||
if (DfgVertex* const sinkp = vtxp->singleSink()) {
|
if (DfgVertex* const sinkp = vtxp->singleSink()) {
|
||||||
if (sinkp->is<DfgNot>()) return useOk(sinkp, !inv);
|
if (sinkp->is<DfgNot>()) return useOk(sinkp, !inv);
|
||||||
}
|
}
|
||||||
return !vtxp->findSink<DfgCond>([vtxp, inv](const DfgCond& sink) {
|
const bool condTree = vtxp->foreachSink([&](const DfgVertex& sink) {
|
||||||
if (sink.condp() != vtxp) return false;
|
const DfgCond* const condp = sink.cast<DfgCond>();
|
||||||
return inv ? sink.thenp()->is<DfgCond>() : sink.elsep()->is<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
|
// 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
|
// Required data types
|
||||||
AstNodeDType* const idxDTypep = srcp->dtypep();
|
AstNodeDType* const idxDTypep = srcp->dtypep();
|
||||||
AstNodeDType* const bitDTypep = DfgGraph::dtypePacked(1);
|
AstNodeDType* const bitDTypep = V3Dfg::dtypePacked(1);
|
||||||
AstUnpackArrayDType* const tabDTypep = new AstUnpackArrayDType{
|
AstUnpackArrayDType* const tabDTypep = new AstUnpackArrayDType{
|
||||||
flp, bitDTypep, new AstRange{flp, static_cast<int>(nBits - 1), 0}};
|
flp, bitDTypep, new AstRange{flp, static_cast<int>(nBits - 1), 0}};
|
||||||
v3Global.rootp()->typeTablep()->addTypesp(tabDTypep);
|
v3Global.rootp()->typeTablep()->addTypesp(tabDTypep);
|
||||||
|
|
@ -438,10 +441,11 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
||||||
const auto addToWorkList = [&](DfgVertex& vtx) {
|
const auto addToWorkList = [&](DfgVertex& vtx) {
|
||||||
// If already in work list then nothing to do
|
// If already in work list then nothing to do
|
||||||
DfgVertex*& nextInWorklistp = vtx.user<DfgVertex*>();
|
DfgVertex*& nextInWorklistp = vtx.user<DfgVertex*>();
|
||||||
if (nextInWorklistp) return;
|
if (nextInWorklistp) return false;
|
||||||
// Actually add to work list.
|
// Actually add to work list.
|
||||||
nextInWorklistp = workListp;
|
nextInWorklistp = workListp;
|
||||||
workListp = &vtx;
|
workListp = &vtx;
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// List of variables (AstVar or AstVarScope) we are replacing
|
// 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
|
// Remove unused non-variable vertices
|
||||||
if (!vtxp->is<DfgVertexVar>() && !vtxp->hasSinks()) {
|
if (!vtxp->is<DfgVertexVar>() && !vtxp->hasSinks()) {
|
||||||
// Add sources of removed vertex to work list
|
// Add sources of removed vertex to work list
|
||||||
vtxp->forEachSource(addToWorkList);
|
vtxp->foreachSource(addToWorkList);
|
||||||
// Remove the unused vertex
|
// Remove the unused vertex
|
||||||
vtxp->unlinkDelete(dfg);
|
vtxp->unlinkDelete(dfg);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -517,7 +521,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add sources of redundant variable to the work list
|
// Add sources of redundant variable to the work list
|
||||||
vtxp->forEachSource(addToWorkList);
|
vtxp->foreachSource(addToWorkList);
|
||||||
// Remove the redundant variable
|
// Remove the redundant variable
|
||||||
vtxp->unlinkDelete(dfg);
|
vtxp->unlinkDelete(dfg);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,9 +118,10 @@ class V3DfgPatternStats final {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operands
|
// Operands
|
||||||
vtx.forEachSource([&](const DfgVertex& src) {
|
vtx.foreachSource([&](const DfgVertex& src) {
|
||||||
ss << ' ';
|
ss << ' ';
|
||||||
if (render(ss, src, depth - 1)) deep = true;
|
if (render(ss, src, depth - 1)) deep = true;
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
// S-expression end
|
// S-expression end
|
||||||
ss << ')';
|
ss << ')';
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
// STATE
|
// STATE
|
||||||
DfgGraph& m_dfg; // The DfgGraph being visited
|
DfgGraph& m_dfg; // The DfgGraph being visited
|
||||||
V3DfgPeepholeContext& m_ctx; // The config structure
|
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
|
// 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
|
// 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
|
// 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) {
|
void addSourcesToWorkList(DfgVertex* vtxp) {
|
||||||
vtxp->forEachSource([&](DfgVertex& src) { addToWorkList(&src); });
|
vtxp->foreachSource([&](DfgVertex& src) {
|
||||||
|
addToWorkList(&src);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSinksToWorkList(DfgVertex* vtxp) {
|
void addSinksToWorkList(DfgVertex* vtxp) {
|
||||||
vtxp->forEachSink([&](DfgVertex& src) { addToWorkList(&src); });
|
vtxp->foreachSink([&](DfgVertex& src) {
|
||||||
|
addToWorkList(&src);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteVertex(DfgVertex* vtxp) {
|
void deleteVertex(DfgVertex* vtxp) {
|
||||||
|
|
@ -179,8 +185,6 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
// Otherwise we can delete it now.
|
// Otherwise we can delete it now.
|
||||||
// Remove from cache
|
// Remove from cache
|
||||||
m_cache.invalidateByValue(vtxp);
|
m_cache.invalidateByValue(vtxp);
|
||||||
// Unlink source edges
|
|
||||||
vtxp->forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
|
|
||||||
// Should not have sinks
|
// Should not have sinks
|
||||||
UASSERT_OBJ(!vtxp->hasSinks(), vtxp, "Should not delete used vertex");
|
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
|
// Add replacement to the work list
|
||||||
addToWorkList(replacementp);
|
addToWorkList(replacementp);
|
||||||
// Replace vertex with the replacement
|
// 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);
|
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
|
// Vertex is now unused, so delete it
|
||||||
deleteVertex(vtxp);
|
deleteVertex(vtxp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shorthand
|
// 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
|
// Create a 32-bit DfgConst vertex
|
||||||
DfgConst* makeI32(FileLine* flp, uint32_t val) { return new DfgConst{m_dfg, flp, 32, val}; }
|
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
|
// 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
|
// Create a new vertex of the given type
|
||||||
template <typename Vertex, typename... Operands>
|
template <typename Vertex, typename... Operands>
|
||||||
|
|
@ -246,6 +258,16 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
return aConstp->num().isCaseEq(bConstp->num());
|
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
|
// 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
|
// caller must not do any further changes, so the caller must check the return value, otherwise
|
||||||
// there will be hard to debug issues.
|
// 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) {
|
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_base_of<DfgVertexBinary, Vertex>::value, "Must invoke on binary");
|
||||||
static_assert(std::is_final<Vertex>::value, "Must invoke on final class");
|
static_assert(std::is_final<Vertex>::value, "Must invoke on final class");
|
||||||
if (DfgConst* const lhsp = vtxp->lhsp()->template cast<DfgConst>()) {
|
if (DfgConst* const lhsp = vtxp->inputp(0)->template cast<DfgConst>()) {
|
||||||
if (DfgConst* const rhsp = vtxp->rhsp()->template cast<DfgConst>()) {
|
if (DfgConst* const rhsp = vtxp->inputp(1)->template cast<DfgConst>()) {
|
||||||
APPLYING(FOLD_BINARY) {
|
APPLYING(FOLD_BINARY) {
|
||||||
DfgConst* const resultp = makeZero(vtxp->fileline(), vtxp->width());
|
DfgConst* const resultp = makeZero(vtxp->fileline(), vtxp->width());
|
||||||
foldOp<Vertex>(resultp->num(), lhsp->num(), rhsp->num());
|
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_base_of<DfgVertexBinary, Shift>::value, "Must invoke on binary");
|
||||||
static_assert(std::is_final<Shift>::value, "Must invoke on final class");
|
static_assert(std::is_final<Shift>::value, "Must invoke on final class");
|
||||||
if (const DfgConcat* const concatp = vtxp->rhsp()->template cast<DfgConcat>()) {
|
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) {
|
APPLYING(REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT) {
|
||||||
Shift* const replacementp = make<Shift>(vtxp, vtxp->lhsp(), concatp->rhsp());
|
Shift* const replacementp = make<Shift>(vtxp, vtxp->lhsp(), concatp->rhsp());
|
||||||
replace(vtxp, replacementp);
|
replace(vtxp, replacementp);
|
||||||
|
|
@ -919,27 +941,25 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
// Must be a splice, otherwise it would have been inlined
|
// Must be a splice, otherwise it would have been inlined
|
||||||
DfgSplicePacked* const splicep = varp->srcp()->as<DfgSplicePacked>();
|
DfgSplicePacked* const splicep = varp->srcp()->as<DfgSplicePacked>();
|
||||||
|
|
||||||
const auto pair = splicep->sourceEdges();
|
DfgSel* replacementp = nullptr;
|
||||||
for (size_t i = 0; i < pair.second; ++i) {
|
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLsb) {
|
||||||
DfgVertex* const driverp = pair.first[i].sourcep();
|
const uint32_t dMsb = dLsb + src.width() - 1;
|
||||||
// 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;
|
|
||||||
// If it does not cover the whole searched bit range, move on
|
// 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
|
// Replace with sel from driver
|
||||||
APPLYING(PUSH_SEL_THROUGH_SPLICE) {
|
APPLYING(PUSH_SEL_THROUGH_SPLICE) {
|
||||||
DfgSel* const replacementp = make<DfgSel>(vtxp, driverp, lsb - dLsb);
|
|
||||||
replace(vtxp, replacementp);
|
replace(vtxp, replacementp);
|
||||||
// Special case just for this pattern: delete temporary if became unsued
|
// Special case just for this pattern: delete temporary if became unsued
|
||||||
if (!varp->hasSinks() && !varp->hasDfgRefs()) {
|
if (!varp->hasSinks() && !varp->hasDfgRefs()) {
|
||||||
addToWorkList(splicep); // So it can be delete itself if unused
|
addToWorkList(splicep); // So it can be delete itself if unused
|
||||||
VL_DO_DANGLING(varp->unlinkDelete(m_dfg), varp); // Delete it
|
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 lhsConcatp = lhsp->cast<DfgConcat>()) {
|
||||||
if (DfgConcat* const rhsConcatp = rhsp->cast<DfgConcat>()) {
|
if (DfgConcat* const rhsConcatp = rhsp->cast<DfgConcat>()) {
|
||||||
if (lhsConcatp->lhsp()->dtypep() == rhsConcatp->lhsp()->dtypep()) {
|
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) {
|
APPLYING(REPLACE_OR_OF_CONCAT_ZERO_LHS_AND_CONCAT_RHS_ZERO) {
|
||||||
DfgConcat* const replacementp
|
DfgConcat* const replacementp
|
||||||
= make<DfgConcat>(vtxp, rhsConcatp->lhsp(), lhsConcatp->rhsp());
|
= make<DfgConcat>(vtxp, rhsConcatp->lhsp(), lhsConcatp->rhsp());
|
||||||
|
|
@ -1094,7 +1114,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
return;
|
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) {
|
APPLYING(REPLACE_OR_OF_CONCAT_LHS_ZERO_AND_CONCAT_ZERO_RHS) {
|
||||||
DfgConcat* const replacementp
|
DfgConcat* const replacementp
|
||||||
= make<DfgConcat>(vtxp, lhsConcatp->lhsp(), rhsConcatp->rhsp());
|
= make<DfgConcat>(vtxp, lhsConcatp->lhsp(), rhsConcatp->rhsp());
|
||||||
|
|
@ -1261,7 +1281,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
|
|
||||||
FileLine* const flp = vtxp->fileline();
|
FileLine* const flp = vtxp->fileline();
|
||||||
|
|
||||||
if (lhsp->isZero()) {
|
if (isZero(lhsp)) {
|
||||||
DfgConst* const lConstp = lhsp->as<DfgConst>();
|
DfgConst* const lConstp = lhsp->as<DfgConst>();
|
||||||
if (DfgSel* const rSelp = rhsp->cast<DfgSel>()) {
|
if (DfgSel* const rSelp = rhsp->cast<DfgSel>()) {
|
||||||
if (vtxp->dtypep() == rSelp->fromp()->dtypep()
|
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>();
|
DfgConst* const rConstp = rhsp->as<DfgConst>();
|
||||||
if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) {
|
if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) {
|
||||||
if (vtxp->dtypep() == lSelp->fromp()->dtypep() && lSelp->lsb() == 0) {
|
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->dtypep() != m_bitDType) return;
|
||||||
|
|
||||||
if (condp->isOnes()) {
|
if (isOnes(condp)) {
|
||||||
APPLYING(REMOVE_COND_WITH_TRUE_CONDITION) {
|
APPLYING(REMOVE_COND_WITH_TRUE_CONDITION) {
|
||||||
replace(vtxp, thenp);
|
replace(vtxp, thenp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (condp->isZero()) {
|
if (isZero(condp)) {
|
||||||
APPLYING(REMOVE_COND_WITH_FALSE_CONDITION) {
|
APPLYING(REMOVE_COND_WITH_FALSE_CONDITION) {
|
||||||
replace(vtxp, elsep);
|
replace(vtxp, elsep);
|
||||||
return;
|
return;
|
||||||
|
|
@ -1689,7 +1709,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vtxp->dtypep() == m_bitDType) {
|
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) {
|
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
|
||||||
DfgNot* const notp = make<DfgNot>(vtxp, condp);
|
DfgNot* const notp = make<DfgNot>(vtxp, condp);
|
||||||
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, notp, elsep);
|
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, notp, elsep);
|
||||||
|
|
@ -1704,21 +1724,21 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
return;
|
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) {
|
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
|
||||||
DfgOr* const repalcementp = make<DfgOr>(vtxp, condp, elsep);
|
DfgOr* const repalcementp = make<DfgOr>(vtxp, condp, elsep);
|
||||||
replace(vtxp, repalcementp);
|
replace(vtxp, repalcementp);
|
||||||
return;
|
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) {
|
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
|
||||||
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, condp, thenp);
|
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, condp, thenp);
|
||||||
replace(vtxp, repalcementp);
|
replace(vtxp, repalcementp);
|
||||||
return;
|
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) {
|
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
|
||||||
DfgNot* const notp = make<DfgNot>(vtxp, condp);
|
DfgNot* const notp = make<DfgNot>(vtxp, condp);
|
||||||
DfgOr* const repalcementp = make<DfgOr>(vtxp, notp, thenp);
|
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
|
// This is an op that requires a result variable. Ensure it is
|
||||||
// assigned to one, and redirect other sinks read that variable.
|
// assigned to one, and redirect other sinks read that variable.
|
||||||
if (DfgVertexVar* const varp = vtx.getResultVar()) {
|
if (DfgVertexVar* const varp = vtx.getResultVar()) {
|
||||||
varp->sourceEdge<0>()->unlinkSource();
|
varp->srcp(nullptr);
|
||||||
vtx.replaceWith(varp);
|
vtx.replaceWith(varp);
|
||||||
varp->srcp(&vtx);
|
varp->srcp(&vtx);
|
||||||
} else {
|
} 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
|
#define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE
|
||||||
#endif
|
#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)
|
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)
|
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.
|
// Location of driver of this variable. Only used for converting back to Ast. Might be nullptr.
|
||||||
FileLine* m_driverFileLine = 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;
|
AstNode* m_tmpForp = nullptr;
|
||||||
|
|
||||||
bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED;
|
bool selfEquals(const DfgVertex& that) const final {
|
||||||
V3Hash selfHash() const final VL_MT_DISABLED;
|
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:
|
public:
|
||||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp);
|
~DfgVertexVar() {
|
||||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp);
|
// Decrement reference count
|
||||||
inline ~DfgVertexVar();
|
AstNode* const variablep = nodep();
|
||||||
|
variablep->user1(variablep->user1() - 0x10);
|
||||||
|
UASSERT_OBJ((variablep->user1() >> 4) >= 0, variablep, "Reference count underflow");
|
||||||
|
}
|
||||||
ASTGEN_MEMBERS_DfgVertexVar;
|
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; }
|
AstVar* varp() const { return m_varp; }
|
||||||
AstVarScope* varScopep() const { return m_varScopep; }
|
AstVarScope* varScopep() const { return m_varScopep; }
|
||||||
AstNode* nodep() const {
|
AstNode* nodep() const {
|
||||||
return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp);
|
return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileLine* driverFileLine() const { return m_driverFileLine; }
|
// If this is a temporary, the Ast variable it stands for, or same as
|
||||||
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
|
// 'nodep()' if it's a temporary with no associated original Ast variable.
|
||||||
|
|
||||||
AstNode* tmpForp() const { return m_tmpForp; }
|
AstNode* tmpForp() const { return m_tmpForp; }
|
||||||
void tmpForp(AstNode* nodep) { m_tmpForp = nodep; }
|
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 {
|
bool isDrivenFullyByDfg() const {
|
||||||
return srcp() && !srcp()->is<DfgVertexSplice>() && !varp()->isForced()
|
return srcp() && !srcp()->is<DfgVertexSplice>() && !varp()->isForced()
|
||||||
&& !varp()->isSigUserRWPublic();
|
&& !varp()->isSigUserRWPublic();
|
||||||
|
|
@ -105,212 +167,7 @@ public:
|
||||||
return false;
|
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 {
|
class DfgVarArray final : public DfgVertexVar {
|
||||||
friend class DfgVertex;
|
friend class DfgVertex;
|
||||||
friend class DfgVisitor;
|
friend class DfgVisitor;
|
||||||
|
|
@ -326,6 +183,7 @@ public:
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_DfgVarArray;
|
ASTGEN_MEMBERS_DfgVarArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DfgVarPacked final : public DfgVertexVar {
|
class DfgVarPacked final : public DfgVertexVar {
|
||||||
friend class DfgVertex;
|
friend class DfgVertex;
|
||||||
friend class DfgVisitor;
|
friend class DfgVisitor;
|
||||||
|
|
@ -342,59 +200,286 @@ public:
|
||||||
ASTGEN_MEMBERS_DfgVarPacked;
|
ASTGEN_MEMBERS_DfgVarPacked;
|
||||||
};
|
};
|
||||||
|
|
||||||
// === DfgVertexVariadic ===
|
//------------------------------------------------------------------------------
|
||||||
class DfgLogic final : public DfgVertexVariadic {
|
// Nullary vertices - 0 inputs
|
||||||
// Generic vertex representing a whole combinational process
|
|
||||||
AstNode* const m_nodep; // The Ast logic represented by this vertex
|
class DfgVertexNullary VL_NOT_FINAL : public DfgVertex {
|
||||||
const std::unique_ptr<CfgGraph> m_cfgp;
|
protected:
|
||||||
// Vertices this logic was synthesized into. Excluding variables
|
DfgVertexNullary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||||
std::vector<DfgVertex*> m_synth;
|
: DfgVertex{dfg, type, flp, dtypep} {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DfgLogic(DfgGraph& dfg, AstAssignW* nodep)
|
ASTGEN_MEMBERS_DfgVertexNullary;
|
||||||
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u}
|
std::string srcName(size_t) const override final { V3ERROR_NA_RETURN(""); }
|
||||||
, 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 ""; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DfgUnresolved final : public DfgVertexVariadic {
|
class DfgConst final : public DfgVertexNullary {
|
||||||
// Represents a collection of unresolved variable drivers before synthesis
|
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:
|
public:
|
||||||
DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp)
|
DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num)
|
||||||
: DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtypep(), 1u} {}
|
: DfgVertexNullary{dfg, dfgType(), flp, V3Dfg::dtypePacked(num.width())}
|
||||||
ASTGEN_MEMBERS_DfgUnresolved;
|
, 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
|
ASTGEN_MEMBERS_DfgConst;
|
||||||
void addDriver(DfgLogic* vtxp) { addSource()->relinkSource(vtxp); }
|
|
||||||
void addDriver(DfgVertexSplice* vtxp) { addSource()->relinkSource(vtxp); }
|
|
||||||
|
|
||||||
// cppcheck-suppress duplInheritedMember
|
V3Number& num() { return m_num; }
|
||||||
void clearSources() { DfgVertexVariadic::clearSources(); }
|
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 {
|
class DfgSpliceArray final : public DfgVertexSplice {
|
||||||
friend class DfgVertex;
|
friend class DfgVertex;
|
||||||
friend class DfgVisitor;
|
friend class DfgVisitor;
|
||||||
|
|
@ -406,6 +491,7 @@ public:
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_DfgSpliceArray;
|
ASTGEN_MEMBERS_DfgSpliceArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DfgSplicePacked final : public DfgVertexSplice {
|
class DfgSplicePacked final : public DfgVertexSplice {
|
||||||
friend class DfgVertex;
|
friend class DfgVertex;
|
||||||
friend class DfgVisitor;
|
friend class DfgVisitor;
|
||||||
|
|
@ -418,4 +504,70 @@ public:
|
||||||
ASTGEN_MEMBERS_DfgSplicePacked;
|
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
|
#endif
|
||||||
|
|
|
||||||
36
src/astgen
36
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(
|
sys.exit("%Error: Non-final {b} subclasses must be named {b}*: {p}{n}".format(
|
||||||
b=baseClass, p=prefix, n=node.name))
|
b=baseClass, p=prefix, n=node.name))
|
||||||
|
|
||||||
|
# Skip ordering check for Dfg
|
||||||
|
if prefix == "Dfg":
|
||||||
|
return
|
||||||
|
|
||||||
# Check ordering of node definitions
|
# Check ordering of node definitions
|
||||||
hasOrderingError = False
|
hasOrderingError = False
|
||||||
|
|
||||||
|
|
@ -1169,6 +1173,9 @@ def write_dfg_macros(filename):
|
||||||
" ").format(**fmt).replace("\n", " \\\n"))
|
" ").format(**fmt).replace("\n", " \\\n"))
|
||||||
|
|
||||||
for node in DfgVertexList:
|
for node in DfgVertexList:
|
||||||
|
if node.isRoot:
|
||||||
|
continue
|
||||||
|
|
||||||
fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name))
|
fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name))
|
||||||
|
|
||||||
if node.isLeaf:
|
if node.isLeaf:
|
||||||
|
|
@ -1183,22 +1190,24 @@ def write_dfg_macros(filename):
|
||||||
for n in range(1, node.arity + 1):
|
for n in range(1, node.arity + 1):
|
||||||
name, _, _, _ = node.getOp(n)
|
name, _, _, _ = node.getOp(n)
|
||||||
emitBlock('''\
|
emitBlock('''\
|
||||||
DfgVertex* {name}() const {{ return source<{n}>(); }}
|
DfgVertex* {name}() const {{ return inputp({n}); }}
|
||||||
void {name}(DfgVertex* vtxp) {{ relinkSource<{n}>(vtxp); }}
|
void {name}(DfgVertex* vtxp) {{ inputp({n}, vtxp); }}
|
||||||
''',
|
''',
|
||||||
name=name,
|
name=name,
|
||||||
n=n - 1)
|
n=n - 1)
|
||||||
|
|
||||||
|
if node.isLeaf and node.arity > 1:
|
||||||
operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1))
|
operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1))
|
||||||
if operandNames:
|
if operandNames:
|
||||||
emitBlock('''\
|
emitBlock('''\
|
||||||
const std::string srcName(size_t idx) const override {{
|
std::string srcName(size_t idx) const override final {{
|
||||||
static const char* names[{a}] = {{ {ns} }};
|
static const char* names[{a}] = {{ {ns} }};
|
||||||
return names[idx];
|
return names[idx];
|
||||||
}}
|
}}
|
||||||
''',
|
''',
|
||||||
a=node.arity,
|
a=node.arity,
|
||||||
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames)))
|
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames)))
|
||||||
|
|
||||||
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
|
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1215,6 +1224,8 @@ def write_dfg_auto_classes(filename):
|
||||||
|
|
||||||
emitBlock('''\
|
emitBlock('''\
|
||||||
class Dfg{t} final : public Dfg{s} {{
|
class Dfg{t} final : public Dfg{s} {{
|
||||||
|
bool selfEquals(const DfgVertex&) const final {{ return true; }}
|
||||||
|
V3Hash selfHash() const final {{ return V3Hash{{}}; }}
|
||||||
public:
|
public:
|
||||||
Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||||
: Dfg{s}{{dfg, dfgType(), flp, 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_foundUnhandled = true;\n")
|
||||||
fh.write(" ++m_ctx.m_conv.nonRepNode;\n")
|
fh.write(" ++m_ctx.m_conv.nonRepNode;\n")
|
||||||
fh.write(" return;\n")
|
fh.write(" return;\n")
|
||||||
fh.write(" }\n\n")
|
fh.write(" }\n")
|
||||||
fh.write(" m_logicp->synth().emplace_back(vtxp);")
|
fh.write(" m_logicp->synth().emplace_back(vtxp);\n\n")
|
||||||
for i in range(node.arity):
|
for i in range(node.arity):
|
||||||
fh.write(
|
fh.write(
|
||||||
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user2u().to<DfgVertex*>());\n".
|
" vtxp->inputp({i}, nodep->op{j}p()->user2u().to<DfgVertex*>());\n".format(
|
||||||
format(i=i, j=i + 1))
|
i=i, j=i + 1))
|
||||||
fh.write("\n")
|
fh.write("\n")
|
||||||
fh.write(" nodep->user2p(vtxp);\n")
|
fh.write(" nodep->user2p(vtxp);\n")
|
||||||
fh.write("}\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))
|
fh.write("void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
|
||||||
for i in range(node.arity):
|
for i in range(node.arity):
|
||||||
fh.write(
|
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))
|
.format(i=i, j=i + 1))
|
||||||
fh.write(" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name))
|
fh.write(" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name))
|
||||||
for i in range(node.arity):
|
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.
|
# 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.
|
# These are standalone so we don't need to parse the sources for this.
|
||||||
DfgVertices["Vertex"] = Node("Vertex", None)
|
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
|
# AstNodeExpr that are not representable in Dfg
|
||||||
DfgIgnored = (
|
DfgIgnored = (
|
||||||
|
|
@ -1424,6 +1427,7 @@ DfgIgnored = (
|
||||||
"ReplicateN",
|
"ReplicateN",
|
||||||
"SubstrN",
|
"SubstrN",
|
||||||
"ToLowerN",
|
"ToLowerN",
|
||||||
|
"ToStringN",
|
||||||
"ToUpperN",
|
"ToUpperN",
|
||||||
# Effectful
|
# Effectful
|
||||||
"PostAdd",
|
"PostAdd",
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@ unusedPrivateFunction:src/V3Const.cpp
|
||||||
// These are all inappropriate
|
// These are all inappropriate
|
||||||
constVariablePointer:src/V3DfgPeephole.cpp
|
constVariablePointer:src/V3DfgPeephole.cpp
|
||||||
constParameterPointer: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
|
// TODO: these need fixing/signing off in source
|
||||||
constParameterPointer:src/V3EmitCFunc.cpp
|
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