Optimize Ast read references in Dfg directly (#7354)
Introduce a new DfgAstRd vertex, which holds an AstNodeExpr that is a reference to a variable. This enables tracking all read references in Dfg, which both enables more optimization, and allows inlining of expressions in place of the reference more intelligently (e.g, when the expression is only used once, and is not in a loop). This can get rid of 20-30% of temporary variables introduced in Dfg in some designs. Note V3Gate later got rid of a lot of those, this is a step towards making V3Gate redundant. The more intelligent expression inlining is worth ~10% runtime speed on some designs.
This commit is contained in:
parent
dbd4823323
commit
b4a0ca8ba6
112
src/V3Dfg.cpp
112
src/V3Dfg.cpp
|
|
@ -80,6 +80,22 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
|||
|
||||
if (AstNode* const tmpForp = vp->tmpForp()) cp->tmpForp(tmpForp);
|
||||
}
|
||||
// Clone ast reference vertices
|
||||
for (const DfgVertexAst& vtx : m_astVertices) { // LCOV_EXCL_START
|
||||
switch (vtx.type()) {
|
||||
case VDfgType::AstRd: {
|
||||
const DfgAstRd* const vp = vtx.as<DfgAstRd>();
|
||||
DfgAstRd* const cp = new DfgAstRd{*clonep, vp->exprp(), vp->inSenItem(), vp->inLoop()};
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
vtx.v3fatalSrc("Unhandled ast reference vertex type: " + vtx.typeName());
|
||||
VL_UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // LCOV_EXCL_STOP
|
||||
// Clone operation vertices
|
||||
for (const DfgVertex& vtx : m_opVertices) {
|
||||
switch (vtx.type()) {
|
||||
|
|
@ -136,6 +152,22 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
|||
if (const DfgVertex* const srcp = vtx.srcp()) cp->srcp(vtxp2clonep.at(srcp));
|
||||
if (const DfgVertex* const defp = vtx.defaultp()) cp->defaultp(vtxp2clonep.at(defp));
|
||||
}
|
||||
// Hook up inputs of cloned ast references
|
||||
for (const DfgVertexAst& vtx : m_astVertices) { // LCOV_EXCL_START
|
||||
switch (vtx.type()) {
|
||||
case VDfgType::AstRd: {
|
||||
const DfgAstRd* const vp = vtx.as<DfgAstRd>();
|
||||
DfgAstRd* const cp = vtxp2clonep.at(&vtx)->as<DfgAstRd>();
|
||||
if (const DfgVertex* const srcp = vp->srcp()) cp->srcp(vtxp2clonep.at(srcp));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
vtx.v3fatalSrc("Unhandled DfgVertexAst sub type: " + vtx.typeName());
|
||||
VL_UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // LCOV_EXCL_STOP
|
||||
// Hook up inputs of cloned operation vertices
|
||||
for (const DfgVertex& vtx : m_opVertices) {
|
||||
if (vtx.is<DfgVertexVariadic>()) {
|
||||
|
|
@ -201,6 +233,14 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
|||
#endif
|
||||
}
|
||||
m_varVertices.splice(m_varVertices.end(), otherp->m_varVertices);
|
||||
// Process Ast references
|
||||
for (DfgVertexAst* const vtxp : otherp->m_astVertices.unlinkable()) {
|
||||
vtxp->m_userGeneration = 0;
|
||||
#ifdef VL_DEBUG
|
||||
vtxp->m_dfgp = this;
|
||||
#endif
|
||||
}
|
||||
m_astVertices.splice(m_astVertices.end(), otherp->m_astVertices);
|
||||
// Process constants
|
||||
for (DfgConst& vtx : otherp->m_constVertices) {
|
||||
vtx.m_userGeneration = 0;
|
||||
|
|
@ -270,7 +310,6 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&
|
|||
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||
if (const DfgVertexVar* const varVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
const AstNode* const nodep = varVtxp->nodep();
|
||||
const AstVar* const varp = varVtxp->varp();
|
||||
os << toDotId(vtx);
|
||||
// Begin attributes
|
||||
os << " [";
|
||||
|
|
@ -292,7 +331,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
os << " / ";
|
||||
static const char* const rwmn[2][2] = {{"_", "W"}, {"R", "M"}};
|
||||
os << rwmn[varVtxp->hasExtRdRefs()][varVtxp->hasExtWrRefs()];
|
||||
os << rwmn[varVtxp->hasModRdRefs()][varVtxp->hasModWrRefs()];
|
||||
os << rwmn[false][varVtxp->hasModWrRefs()];
|
||||
os << (varVtxp->hasDfgRefs() ? "D" : "_");
|
||||
// End 'label'
|
||||
os << '"';
|
||||
|
|
@ -305,21 +344,13 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
varVtxp->v3fatalSrc("Unhandled DfgVertexVar sub-type");
|
||||
}
|
||||
// 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";
|
||||
}
|
||||
const char* const colorp = varVtxp->hasExtRefs() ? "firebrick2" // Red
|
||||
: varVtxp->hasModWrRefs() ? "darkorange1" // Orange
|
||||
: varVtxp->hasDfgRefs() ? "gold2" // Yellow
|
||||
: varVtxp->tmpForp() ? "gray95" // Gray
|
||||
: "white";
|
||||
os << ", style=filled";
|
||||
os << ", fillcolor=\"" << colorp << "\"";
|
||||
// End attributes
|
||||
os << "]\n";
|
||||
return;
|
||||
|
|
@ -379,7 +410,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
os << toDotId(vtx);
|
||||
std::stringstream ss;
|
||||
V3EmitV::debugVerilogForTree(logicp->nodep(), ss);
|
||||
std::string str = ss.str();
|
||||
std::string str = "AstNode: " + cvtToHex(logicp->nodep()) + "\n" + ss.str();
|
||||
str = VString::quoteBackslash(str);
|
||||
str = VString::quoteAny(str, '"', '\\');
|
||||
str = VString::replaceSubstr(str, "\n", "\\l");
|
||||
|
|
@ -390,7 +421,32 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
|
||||
os << " [label=\"";
|
||||
os << str;
|
||||
os << "\\n" << cvtToHex(&vtx);
|
||||
os << '\n' << vtx.typeName() << '\n' << cvtToHex(&vtx);
|
||||
os << "\"\n";
|
||||
os << ", shape=box, style=\"rounded,filled\", nojustify=true";
|
||||
os << ", fillcolor=\"" << colorp << "\"";
|
||||
os << "]\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (const DfgVertexAst* const astVtxp = vtx.cast<DfgVertexAst>()) {
|
||||
os << toDotId(vtx);
|
||||
std::stringstream ss;
|
||||
V3EmitV::debugVerilogForTree(astVtxp->exprp(), ss);
|
||||
std::string str = "AstNode: " + cvtToHex(astVtxp->exprp()) + "\n" + ss.str() + "\n";
|
||||
str = VString::quoteBackslash(str);
|
||||
str = VString::quoteAny(str, '"', '\\');
|
||||
str = VString::replaceSubstr(str, "\n", "\\l");
|
||||
const DfgAstRd* const astRdVtxp = astVtxp->cast<DfgAstRd>();
|
||||
const char* const colorp = astRdVtxp ? "#80ff80" // Green
|
||||
: "#ffff80"; // Yellow
|
||||
os << " [label=\"";
|
||||
os << str;
|
||||
os << '\n' << vtx.typeName() << '\n' << cvtToHex(&vtx) << '\n';
|
||||
vtx.dtype().astDtypep()->dumpSmall(os);
|
||||
os << " / ";
|
||||
os << "_S"[astRdVtxp->inSenItem()];
|
||||
os << "_L"[astRdVtxp->inLoop()];
|
||||
os << "\"\n";
|
||||
os << ", shape=box, style=\"rounded,filled\", nojustify=true";
|
||||
os << ", fillcolor=\"" << colorp << "\"";
|
||||
|
|
@ -550,6 +606,11 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const {
|
|||
CHECK(isPacked(), "Should be Packed type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::AstRd: {
|
||||
const DfgAstRd& v = *as<DfgAstRd>();
|
||||
CHECK(v.isPacked() || v.isArray(), "Should be Packed or Array type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::VarArray:
|
||||
case VDfgType::VarPacked: {
|
||||
const DfgVertexVar& v = *as<DfgVertexVar>();
|
||||
|
|
@ -781,16 +842,21 @@ DfgVertexVar* DfgVertex::getResultVar() {
|
|||
if (!resp->hasDfgRefs()) resp = varp;
|
||||
return false;
|
||||
}
|
||||
// Prefer those that already have module references
|
||||
if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
|
||||
if (!resp->hasModRdRefs()) resp = varp;
|
||||
|
||||
// Prefer real variables over temporaries
|
||||
const bool resIsTemp
|
||||
= resp->varp() ? resp->varp()->isTemp() : resp->varScopep()->varp()->isTemp();
|
||||
const bool varIsTemp
|
||||
= varp->varp() ? varp->varp()->isTemp() : varp->varScopep()->varp()->isTemp();
|
||||
if (resIsTemp != varIsTemp) {
|
||||
if (resIsTemp) resp = varp;
|
||||
return false;
|
||||
}
|
||||
// Prefer real variabels over temporaries
|
||||
if (!resp->tmpForp() != !varp->tmpForp()) {
|
||||
if (resp->tmpForp()) resp = varp;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer the earlier one in source order
|
||||
const FileLine& oldFlp = *(resp->fileline());
|
||||
const FileLine& newFlp = *(varp->fileline());
|
||||
|
|
|
|||
23
src/V3Dfg.h
23
src/V3Dfg.h
|
|
@ -382,6 +382,7 @@ class DfgGraph final {
|
|||
// enables significant Verilation performance gains, so we keep these in separate lists for
|
||||
// direct access.
|
||||
DfgVertex::List<DfgVertexVar> m_varVertices; // The variable vertices in the graph
|
||||
DfgVertex::List<DfgVertexAst> m_astVertices; // The ast reference vertices in the graph
|
||||
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
|
||||
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
|
|
@ -417,6 +418,8 @@ public:
|
|||
// Access to vertex lists
|
||||
DfgVertex::List<DfgVertexVar>& varVertices() { return m_varVertices; }
|
||||
const DfgVertex::List<DfgVertexVar>& varVertices() const { return m_varVertices; }
|
||||
DfgVertex::List<DfgVertexAst>& astVertices() { return m_astVertices; }
|
||||
const DfgVertex::List<DfgVertexAst>& astVertices() const { return m_astVertices; }
|
||||
DfgVertex::List<DfgConst>& constVertices() { return m_constVertices; }
|
||||
const DfgVertex::List<DfgConst>& constVertices() const { return m_constVertices; }
|
||||
DfgVertex::List<DfgVertex>& opVertices() { return m_opVertices; }
|
||||
|
|
@ -429,10 +432,12 @@ public:
|
|||
#endif
|
||||
// Note: changes here need to be replicated in DfgGraph::mergeGraphs
|
||||
++m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.linkBack(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.linkBack(vVtxp);
|
||||
} else if (DfgVertexAst* const aVtxp = vtx.cast<DfgVertexAst>()) {
|
||||
m_astVertices.linkBack(aVtxp);
|
||||
} else if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.linkBack(cVtxp);
|
||||
} else {
|
||||
m_opVertices.linkBack(&vtx);
|
||||
}
|
||||
|
|
@ -449,10 +454,12 @@ public:
|
|||
#endif
|
||||
// Note: changes here need to be replicated in DfgGraph::mergeGraphs
|
||||
--m_size;
|
||||
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.unlink(cVtxp);
|
||||
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
|
||||
m_varVertices.unlink(vVtxp);
|
||||
} else if (DfgVertexAst* const aVtxp = vtx.cast<DfgVertexAst>()) {
|
||||
m_astVertices.unlink(aVtxp);
|
||||
} else if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
|
||||
m_constVertices.unlink(cVtxp);
|
||||
} else {
|
||||
m_opVertices.unlink(&vtx);
|
||||
}
|
||||
|
|
@ -467,6 +474,7 @@ public:
|
|||
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
|
||||
void forEachVertex(std::function<void(DfgVertex&)> f) {
|
||||
for (DfgVertexVar* const vtxp : m_varVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgVertexAst* const vtxp : m_astVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgConst* const vtxp : m_constVertices.unlinkable()) f(*vtxp);
|
||||
for (DfgVertex* const vtxp : m_opVertices.unlinkable()) f(*vtxp);
|
||||
}
|
||||
|
|
@ -474,6 +482,7 @@ public:
|
|||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
void forEachVertex(std::function<void(const DfgVertex&)> f) const {
|
||||
for (const DfgVertexVar& vtx : m_varVertices) f(vtx);
|
||||
for (const DfgVertexAst& vtx : m_astVertices) f(vtx);
|
||||
for (const DfgConst& vtx : m_constVertices) f(vtx);
|
||||
for (const DfgVertex& vtx : m_opVertices) f(vtx);
|
||||
}
|
||||
|
|
@ -809,6 +818,8 @@ void DfgEdge::relinkSrcp(DfgVertex* srcp) {
|
|||
// DfgVertex {{{
|
||||
|
||||
bool DfgVertex::isCheaperThanLoad() const {
|
||||
// Constants
|
||||
if (is<DfgConst>()) return true;
|
||||
// Array sels are just address computation
|
||||
if (is<DfgArraySel>()) return true;
|
||||
// Small constant select from variable
|
||||
|
|
|
|||
|
|
@ -28,10 +28,89 @@
|
|||
#include "V3Dfg.h"
|
||||
#include "V3DfgPasses.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
template <bool T_Scoped>
|
||||
class AstToDfgAddAstRefs final : public VNVisitorConst {
|
||||
// TYPES
|
||||
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
||||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
// Function to get the DfgVertexVar for a Variable
|
||||
const std::function<DfgVertexVar*(Variable*)> m_getVarVertex;
|
||||
bool m_inSenItem = false; // Inside an AstSenItem
|
||||
bool m_inLoop = false; // Inside an AstLoop
|
||||
|
||||
// METHODS
|
||||
static Variable* getTarget(const AstVarRef* refp) {
|
||||
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
return reinterpret_cast<Variable*>(refp->varScopep());
|
||||
} else {
|
||||
return reinterpret_cast<Variable*>(refp->varp());
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
void visit(AstSenItem* nodep) override {
|
||||
VL_RESTORER(m_inSenItem);
|
||||
m_inSenItem = true;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
||||
void visit(AstLoop* nodep) override {
|
||||
VL_RESTORER(m_inLoop);
|
||||
m_inLoop = true;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
||||
void visit(AstVarRef* nodep) override {
|
||||
// Disguised hierarchical reference handled as external reference, ignore
|
||||
if (nodep->classOrPackagep()) return;
|
||||
// If target is not supported, ignore
|
||||
Variable* const tgtp = getTarget(nodep);
|
||||
if (!V3Dfg::isSupported(tgtp)) return;
|
||||
// V3Dfg::isSupported should reject vars with READWRITE references
|
||||
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Variable with READWRITE ref not rejected");
|
||||
// Get target variable vergtex, ignore if not given one
|
||||
DfgVertexVar* const varp = m_getVarVertex(tgtp);
|
||||
if (!varp) return;
|
||||
// Create Ast reference vertices
|
||||
if (nodep->access().isReadOnly()) {
|
||||
DfgAstRd* const astp = new DfgAstRd{m_dfg, nodep, m_inSenItem, m_inLoop};
|
||||
astp->srcp(varp);
|
||||
return;
|
||||
}
|
||||
// Mark as written from non-DFG logic
|
||||
DfgVertexVar::setHasModWrRefs(tgtp);
|
||||
}
|
||||
|
||||
AstToDfgAddAstRefs(DfgGraph& dfg, AstNode* nodep,
|
||||
std::function<DfgVertexVar*(Variable*)> getVarVertex)
|
||||
: m_dfg{dfg}
|
||||
, m_getVarVertex{getVarVertex} {
|
||||
iterateConst(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
static void apply(DfgGraph& dfg, AstNode* nodep,
|
||||
std::function<DfgVertexVar*(Variable*)> getVarVertex) {
|
||||
AstToDfgAddAstRefs{dfg, nodep, getVarVertex};
|
||||
}
|
||||
};
|
||||
|
||||
void V3DfgPasses::addAstRefs(DfgGraph& dfg, AstNode* nodep,
|
||||
std::function<DfgVertexVar*(AstNode*)> getVarVertex) {
|
||||
if (dfg.modulep()) {
|
||||
AstToDfgAddAstRefs</* T_Scoped: */ false>::apply(dfg, nodep, getVarVertex);
|
||||
} else {
|
||||
AstToDfgAddAstRefs</* T_Scoped: */ true>::apply(dfg, nodep, getVarVertex);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool T_Scoped>
|
||||
class AstToDfgVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
|
|
@ -72,13 +151,9 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
// Mark variables referenced under node
|
||||
static void markReferenced(const AstNode* nodep) {
|
||||
nodep->foreach([](const AstVarRef* refp) {
|
||||
Variable* const tgtp = getTarget(refp);
|
||||
// Mark as read from non-DFG logic
|
||||
if (refp->access().isReadOrRW()) DfgVertexVar::setHasModRdRefs(tgtp);
|
||||
// Mark as written from non-DFG logic
|
||||
if (refp->access().isWriteOrRW()) DfgVertexVar::setHasModWrRefs(tgtp);
|
||||
void markReferenced(AstNode* nodep) {
|
||||
V3DfgPasses::addAstRefs(m_dfg, nodep, [this](AstNode* varp) { //
|
||||
return getVarVertex(static_cast<Variable*>(varp));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +361,12 @@ public:
|
|||
AstToDfgVisitor{dfg, root, ctx};
|
||||
// Remove unread and undriven variables (created when something failed to convert)
|
||||
for (DfgVertexVar* const varp : dfg.varVertices().unlinkable()) {
|
||||
if (!varp->srcp() && !varp->hasSinks()) VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
|
||||
if (varp->srcp()) continue;
|
||||
const bool keep = varp->foreachSink([&](DfgVertex& d) { return !d.is<DfgAstRd>(); });
|
||||
if (!keep) {
|
||||
while (varp->hasSinks()) varp->firtsSinkp()->unlinkDelete(dfg);
|
||||
VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -105,6 +105,12 @@ class ColorStronglyConnectedComponents final {
|
|||
component(vtx) = 0;
|
||||
}
|
||||
|
||||
// We know ast references have no output or input edges
|
||||
for (const DfgVertexAst& vtx : m_dfg.astVertices()) {
|
||||
index(vtx) = 0;
|
||||
component(vtx) = 0;
|
||||
}
|
||||
|
||||
// Initialize state of variable vertices
|
||||
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
// If it has no inputs or no outputs, it cannot be part of a non-trivial SCC.
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ public:
|
|||
VDouble0 m_outputVariables; // Number of output variables
|
||||
VDouble0 m_outputVariablesWithDefault; // Number of outptu variables with a default driver
|
||||
VDouble0 m_resultEquations; // Number of result combinational equations
|
||||
VDouble0 m_expressionsInlined; // Number of expressions inlined directly into the Ast
|
||||
VDouble0 m_varRefsSubstituted; // Number of variable references substituted in the Ast
|
||||
|
||||
private:
|
||||
V3DfgDfgToAstContext(V3DfgContext& ctx, const std::string& label)
|
||||
|
|
@ -154,6 +156,8 @@ private:
|
|||
addStat("output variables", m_outputVariables);
|
||||
addStat("output variables with default driver", m_outputVariablesWithDefault);
|
||||
addStat("result equations", m_resultEquations);
|
||||
addStat("expressions inlined", m_expressionsInlined);
|
||||
addStat("var refs substituted", m_varRefsSubstituted);
|
||||
}
|
||||
};
|
||||
class V3DfgPeepholeContext final : public V3DfgSubContext {
|
||||
|
|
@ -166,6 +170,7 @@ public:
|
|||
std::array<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
|
||||
// Count of applications for each optimization (for statistics)
|
||||
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> m_count;
|
||||
std::vector<AstNode*> m_deleteps; // AstVar/AstVarScope that can be deleted at the end
|
||||
|
||||
private:
|
||||
V3DfgPeepholeContext(V3DfgContext& ctx, const std::string& label) VL_MT_DISABLED;
|
||||
|
|
@ -193,13 +198,10 @@ class V3DfgRegularizeContext final : public V3DfgSubContext {
|
|||
|
||||
public:
|
||||
// STATE
|
||||
VDouble0 m_temporariesOmitted; // Number of temporaries omitted as cheaper to re-compute
|
||||
VDouble0 m_temporariesIntroduced; // Number of temporaries introduced
|
||||
|
||||
std::vector<AstNode*> m_deleteps; // AstVar/AstVarScope that can be deleted at the end
|
||||
VDouble0 m_usedVarsReplaced; // Number of used variables replaced with equivalent ones
|
||||
VDouble0 m_usedVarsInlined; // Number of used variables inlined
|
||||
VDouble0 m_unusedRemoved; // Number of unused vertices remoevd
|
||||
VDouble0 m_temporariesIntroduced; // Number of temporaries introduced for reuse
|
||||
|
||||
private:
|
||||
V3DfgRegularizeContext(V3DfgContext& ctx, const std::string& label)
|
||||
|
|
@ -208,11 +210,8 @@ private:
|
|||
for (AstNode* const nodep : m_deleteps) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
addStat("used variables replaced", m_usedVarsReplaced);
|
||||
addStat("used variables inlined", m_usedVarsInlined);
|
||||
addStat("unused vertices removed", m_unusedRemoved);
|
||||
|
||||
addStat("temporaries omitted", m_temporariesOmitted);
|
||||
addStat("temporaries introduced", m_temporariesIntroduced);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ class V3DfgCse final {
|
|||
// Special vertices
|
||||
case VDfgType::Const: // LCOV_EXCL_START
|
||||
case VDfgType::VarArray:
|
||||
case VDfgType::VarPacked: // LCOV_EXCL_STOP
|
||||
case VDfgType::VarPacked:
|
||||
case VDfgType::AstRd: // LCOV_EXCL_STOP
|
||||
vtx.v3fatalSrc("Hash should have been pre-computed");
|
||||
|
||||
// Vertices with internal information
|
||||
|
|
@ -164,12 +165,16 @@ class V3DfgCse final {
|
|||
case VDfgType::Unresolved: // LCOV_EXCL_STOP
|
||||
a.v3fatalSrc("Should not have reached CSE");
|
||||
|
||||
// Not reachable via operation vertices
|
||||
case VDfgType::AstRd: // LCOV_EXCL_LINE
|
||||
a.v3fatalSrc("Should not be reachable via operation vertices");
|
||||
|
||||
// Special vertices
|
||||
case VDfgType::Const: return a.as<DfgConst>()->num().isCaseEq(b.as<DfgConst>()->num());
|
||||
|
||||
case VDfgType::VarArray:
|
||||
case VDfgType::VarPacked:
|
||||
return false; // CSE does not combine variables
|
||||
case VDfgType::VarPacked: // CSE does not combine variables
|
||||
return false;
|
||||
|
||||
// Vertices with internal information
|
||||
case VDfgType::Sel: return a.as<DfgSel>()->lsb() == b.as<DfgSel>()->lsb();
|
||||
|
|
@ -298,6 +303,8 @@ class V3DfgCse final {
|
|||
// Pre-hash variables, these are all unique, so just set their hash to a unique value
|
||||
uint32_t varHash = 0;
|
||||
for (const DfgVertexVar& vtx : dfg.varVertices()) m_hashCache[vtx] = V3Hash{++varHash};
|
||||
// Pre-hash Ast references, these are all unique like variables
|
||||
for (const DfgVertexAst& vtx : dfg.astVertices()) m_hashCache[vtx] = V3Hash{++varHash};
|
||||
|
||||
// Similarly pre-hash constants for speed. While we don't combine constants, we do want
|
||||
// expressions using the same constants to be combined, so we do need to hash equal
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ class SplitIntoComponents final {
|
|||
}
|
||||
// Move the vertices to the component graphs
|
||||
moveVertices(m_dfg.varVertices());
|
||||
moveVertices(m_dfg.astVertices());
|
||||
moveVertices(m_dfg.constVertices());
|
||||
moveVertices(m_dfg.opVertices());
|
||||
//
|
||||
|
|
@ -202,12 +203,12 @@ class ExtractCyclicComponents final {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// are reachable from their sinks, and Ast refs cannot be in an SCC,
|
||||
// so only need to process op vertices. We do this by starting a DFS
|
||||
// from each vertex that is part of a component and add all reachable
|
||||
// non-variable vertices to the same.
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
if (const uint64_t targetComponent = m_component.at(vtx)) {
|
||||
expandSiblings(vtx, targetComponent);
|
||||
|
|
@ -349,6 +350,7 @@ class ExtractCyclicComponents final {
|
|||
// Move other vertices to their component graphs
|
||||
// After this, vertex states are invalid as we moved the vertices
|
||||
moveVertices(m_dfg.varVertices());
|
||||
moveVertices(m_dfg.astVertices());
|
||||
moveVertices(m_dfg.constVertices());
|
||||
moveVertices(m_dfg.opVertices());
|
||||
|
||||
|
|
|
|||
|
|
@ -268,8 +268,10 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
, m_ctx{ctx} {
|
||||
if (v3Global.opt.debugCheck()) V3DfgPasses::typeCheck(dfg);
|
||||
|
||||
// Convert the graph back to combinational assignments
|
||||
// The graph must have been regularized, so we only need to render assignments
|
||||
// Convert the graph back to combinational assignments. The graph must have been
|
||||
// regularized, so we only need to render variable assignments and update Ast references.
|
||||
|
||||
// Render variable assignments
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
||||
if (!vtx.srcp()) {
|
||||
|
|
@ -309,6 +311,30 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
// convetDriver always clones lhsp
|
||||
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
|
||||
}
|
||||
|
||||
// Update Ast References
|
||||
for (DfgVertexAst& vtx : dfg.astVertices()) {
|
||||
if (DfgAstRd* const rVtxp = vtx.cast<DfgAstRd>()) {
|
||||
// Render the driver
|
||||
AstNodeExpr* const exprp = convertDfgVertexToAstNodeExpr(rVtxp->srcp());
|
||||
// If it's the same as the reference, do not repalce it so FileLines are preserved
|
||||
if (exprp->sameTree(rVtxp->exprp())) {
|
||||
VL_DO_DANGLING(exprp->deleteTree(), exprp);
|
||||
continue;
|
||||
}
|
||||
// Replace the reference with the expression
|
||||
if (VN_IS(exprp, VarRef)) {
|
||||
++m_ctx.m_varRefsSubstituted;
|
||||
} else {
|
||||
UASSERT_OBJ(!dfg.modulep(), &vtx,
|
||||
"Expressions should only be inlined on final scoped run");
|
||||
++m_ctx.m_expressionsInlined;
|
||||
}
|
||||
rVtxp->exprp()->replaceWith(exprp);
|
||||
rVtxp->exprp()->deleteTree();
|
||||
rVtxp->exprp(exprp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "V3DfgOptimizer.h"
|
||||
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Dfg.h"
|
||||
#include "V3DfgPasses.h"
|
||||
#include "V3Graph.h"
|
||||
|
|
@ -281,16 +282,10 @@ class DataflowOptimize final {
|
|||
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(vscp);
|
||||
return;
|
||||
}
|
||||
// TODO: remove once Actives can tolerate NEVER SenItems
|
||||
if (AstSenItem* senItemp = VN_CAST(nodep, SenItem)) {
|
||||
senItemp->foreach([](const AstVarRef* refp) {
|
||||
DfgVertexVar::setHasExtRdRefs(refp->varScopep());
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Check direct references
|
||||
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
|
||||
if (refp->access().isRW()) DfgVertexVar::setHasRWRefs(refp->varScopep());
|
||||
UASSERT_OBJ(!refp->classOrPackagep(), refp, "V3Scope should have removed");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -305,7 +300,13 @@ class DataflowOptimize final {
|
|||
}
|
||||
// Check direct references
|
||||
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
|
||||
if (refp->access().isRW()) DfgVertexVar::setHasRWRefs(refp->varp());
|
||||
AstVar* const varp = refp->varp();
|
||||
if (refp->access().isRW()) DfgVertexVar::setHasRWRefs(varp);
|
||||
// With classOrPackagep set this is a disguised hierarchical reference, mark
|
||||
if (refp->classOrPackagep()) {
|
||||
if (refp->access().isReadOrRW()) DfgVertexVar::setHasExtRdRefs(varp);
|
||||
if (refp->access().isWriteOrRW()) DfgVertexVar::setHasExtWrRefs(varp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -396,6 +397,24 @@ class DataflowOptimize final {
|
|||
endOfStage("regularize", &dfg);
|
||||
}
|
||||
|
||||
void removeNeverActives(AstNetlist* netlistp) {
|
||||
std::vector<AstActive*> neverActiveps;
|
||||
netlistp->foreach([&](AstActive* activep) {
|
||||
AstSenTree* const senTreep = activep->sentreep();
|
||||
if (!senTreep) return;
|
||||
const AstNode* const nodep = V3Const::constifyEdit(senTreep);
|
||||
UASSERT_OBJ(nodep == senTreep, nodep, "Should not have been repalced");
|
||||
if (senTreep->sensesp()->isNever()) {
|
||||
UASSERT_OBJ(!senTreep->sensesp()->nextp(), nodep,
|
||||
"Never senitem should be alone, else the never should be eliminated.");
|
||||
neverActiveps.emplace_back(activep);
|
||||
}
|
||||
});
|
||||
for (AstActive* const activep : neverActiveps) {
|
||||
VL_DO_DANGLING(activep->unlinkFrBack()->deleteTree(), activep);
|
||||
}
|
||||
}
|
||||
|
||||
DataflowOptimize(AstNetlist* netlistp, const string& label)
|
||||
: m_ctx{label}
|
||||
, m_scoped{!!netlistp->topScopep()} {
|
||||
|
|
@ -440,6 +459,8 @@ class DataflowOptimize final {
|
|||
// Convert back to Ast
|
||||
V3DfgPasses::dfgToAst(*dfgp, m_ctx);
|
||||
endOfStage("dfg-to-ast", dfgp.get());
|
||||
// Some sentrees might have become constant, remove them
|
||||
removeNeverActives(netlistp);
|
||||
}
|
||||
|
||||
// Reset interned types so the corresponding Ast types can be garbage collected
|
||||
|
|
|
|||
|
|
@ -89,14 +89,20 @@ void V3DfgPasses::removeUnobservable(DfgGraph& dfg, V3DfgContext& dfgCtx) {
|
|||
// Finally remove logic from the Dfg if it drives no variables in the graph.
|
||||
// These should only be those with side effects.
|
||||
for (DfgVertex* const vtxp : dfg.opVertices().unlinkable()) {
|
||||
if (!vtxp->is<DfgLogic>()) continue;
|
||||
if (vtxp->hasSinks()) continue;
|
||||
// Input variables will be read in Ast code, mark as such
|
||||
vtxp->foreachSource([](DfgVertex& src) {
|
||||
src.as<DfgVertexVar>()->setHasModRdRefs();
|
||||
DfgLogic* const logicp = vtxp->cast<DfgLogic>();
|
||||
if (!logicp) continue;
|
||||
if (logicp->hasSinks()) continue;
|
||||
// Input variables will be read in Ast code, add Ast reference vertices
|
||||
// AstVar/AstVarScope::user2p() -> corresponding DfgVertexVar* in the graph
|
||||
const VNUser2InUse m_user2InUse;
|
||||
logicp->foreachSource([](DfgVertex& src) {
|
||||
src.as<DfgVertexVar>()->nodep()->user2p(&src);
|
||||
return false;
|
||||
});
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
V3DfgPasses::addAstRefs(dfg, logicp->nodep(), [](AstNode* varp) { //
|
||||
return varp->user2u().to<DfgVertexVar*>();
|
||||
});
|
||||
VL_DO_DANGLING(logicp->unlinkDelete(dfg), logicp);
|
||||
++ctx.m_logicRemoved;
|
||||
}
|
||||
}
|
||||
|
|
@ -319,20 +325,17 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
const DfgDataType& tabDType = DfgDataType::array(bitDType, nBits);
|
||||
|
||||
// The index variable
|
||||
AstVar* const idxVarp = [&]() {
|
||||
// If there is an existing result variable, use that, otherwise create a new variable
|
||||
DfgVarPacked* varp = nullptr;
|
||||
if (DfgVertexVar* const vp = srcp->getResultVar()) {
|
||||
varp = vp->as<DfgVarPacked>();
|
||||
} else {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
||||
varp = dfg.makeNewVar(flp, name, idxDType, nullptr)->as<DfgVarPacked>();
|
||||
varp->varp()->isInternal(true);
|
||||
varp->srcp(srcp);
|
||||
}
|
||||
varp->setHasModRdRefs();
|
||||
return varp->varp();
|
||||
DfgVarPacked* idxVtxp = [&]() {
|
||||
// If there is an existing result variable, use that
|
||||
if (DfgVertexVar* const vp = srcp->getResultVar()) return vp->as<DfgVarPacked>();
|
||||
// Otherwise create a new variable
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, nullptr);
|
||||
vtxp->varp()->isInternal(true);
|
||||
vtxp->srcp(srcp);
|
||||
return vtxp->as<DfgVarPacked>();
|
||||
}();
|
||||
AstVar* const idxVarp = idxVtxp->varp();
|
||||
// The previous index variable - we don't need a vertex for this
|
||||
AstVar* const preVarp = [&]() {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Pre", nTables);
|
||||
|
|
@ -385,16 +388,22 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
}
|
||||
{ // tab[idx] = 1
|
||||
AstVarRef* const idxRefp = new AstVarRef{flp, idxVarp, VAccess::READ};
|
||||
logicp->addStmtsp(new AstAssign{
|
||||
flp, //
|
||||
new AstArraySel{flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE},
|
||||
new AstVarRef{flp, idxVarp, VAccess::READ}}, //
|
||||
idxRefp}, //
|
||||
new AstConst{flp, AstConst::BitTrue{}}});
|
||||
DfgAstRd* const astRdp = new DfgAstRd{dfg, idxRefp, false, false};
|
||||
astRdp->srcp(idxVtxp);
|
||||
}
|
||||
{ // pre = idx
|
||||
AstVarRef* const idxRefp = new AstVarRef{flp, idxVarp, VAccess::READ};
|
||||
logicp->addStmtsp(new AstAssign{flp, //
|
||||
new AstVarRef{flp, preVarp, VAccess::WRITE}, //
|
||||
new AstVarRef{flp, idxVarp, VAccess::READ}});
|
||||
idxRefp});
|
||||
DfgAstRd* const astRdp = new DfgAstRd{dfg, idxRefp, false, false};
|
||||
astRdp->srcp(idxVtxp);
|
||||
}
|
||||
|
||||
// Replace terms with ArraySels
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@ std::unique_ptr<DfgGraph> astToDfg(AstModule&, V3DfgContext&) VL_MT_DISABLED;
|
|||
// Same as above, but for the entire netlist, after V3Scope
|
||||
std::unique_ptr<DfgGraph> astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Add DfgVertexAst to the given DfgGraph for all references in the given AstNode.
|
||||
// The function 'getVarVertex' is used to get the DfgVertexVar for an AstVar/AstVarScope.
|
||||
// If it returns nullptr, the reference will be ignored.
|
||||
void addAstRefs(DfgGraph& dfg, AstNode* nodep,
|
||||
std::function<DfgVertexVar*(AstNode*)> getVarVertex) VL_MT_DISABLED;
|
||||
|
||||
// Remove unobservable variabels and logic that drives only such variables
|
||||
void removeUnobservable(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ V3DfgPeepholeContext::V3DfgPeepholeContext(V3DfgContext& ctx, const std::string&
|
|||
}
|
||||
|
||||
V3DfgPeepholeContext::~V3DfgPeepholeContext() {
|
||||
for (AstNode* const nodep : m_deleteps) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
const auto emitStat = [this](VDfgPeepholePattern id) {
|
||||
std::string str{id.ascii()};
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { //
|
||||
|
|
@ -248,7 +251,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
if (varp && !varp->isVolatile() && !varp->hasDfgRefs()) {
|
||||
AstNode* const nodep = varp->nodep();
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
m_ctx.m_deleteps.push_back(nodep);
|
||||
} else {
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
||||
}
|
||||
|
|
@ -2575,12 +2578,16 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
|
||||
// Otherwise remove if there is only one sink that is not a removable variable
|
||||
bool foundOne = false;
|
||||
const bool keep = vtxp->srcp()->foreachSink([&](DfgVertex& sink) {
|
||||
const bool keep = vtxp->srcp()->foreachSink([&](DfgVertex& sink) -> bool {
|
||||
// Ignore non-observable variable sinks. These can be eliminated.
|
||||
if (const DfgVertexVar* const varp = sink.cast<DfgVertexVar>()) {
|
||||
if (!varp->hasSinks() && !varp->isObserved()) return false;
|
||||
}
|
||||
// Keep before final scoped run if feeds an Ast reference
|
||||
if (sink.is<DfgVertexAst>() && m_dfg.modulep()) return true;
|
||||
// Keep if found more than one sink
|
||||
if (foundOne) return true;
|
||||
// Mark first sink found
|
||||
foundOne = true;
|
||||
return false;
|
||||
});
|
||||
|
|
@ -2634,6 +2641,9 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Ast references are not considered
|
||||
if (m_vtxp->is<DfgVertexAst>()) continue;
|
||||
|
||||
// Unsued vertices should have been removed immediately
|
||||
UASSERT_OBJ(m_vtxp->hasSinks(), m_vtxp, "Operation vertex should have sinks");
|
||||
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ class DfgRegularize final {
|
|||
// For all operation vetices, if they drive multiple variables, pick
|
||||
// a "canonical" one and uninline the logic through that variable.
|
||||
void uninlineVariables() {
|
||||
// Const vertices we can just emit as drivers to multiple sinks
|
||||
// directly. Variable vertices, would have been inlined if equivalent,
|
||||
// Variable vertices, would have been inlined if equivalent,
|
||||
// so no need to process them here, they are where they must be.
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Don't process LValue operations
|
||||
|
|
@ -55,6 +54,26 @@ class DfgRegularize final {
|
|||
vtx.replaceWith(varp);
|
||||
varp->srcp(&vtx);
|
||||
}
|
||||
|
||||
// Const vertices driving an Ast reference can only be inlined in scoped
|
||||
// mode as some algorithms assume VarRefs in certain places.
|
||||
if (m_dfg.modulep()) {
|
||||
for (DfgConst& vtx : m_dfg.constVertices()) {
|
||||
const bool drivesAstRef = vtx.foreachSink([](const DfgVertex& dst) { //
|
||||
return dst.is<DfgAstRd>();
|
||||
});
|
||||
if (!drivesAstRef) continue;
|
||||
|
||||
// The prefered result variable is the canonical one if exists
|
||||
DfgVertexVar* const varp = vtx.getResultVar();
|
||||
if (!varp) continue;
|
||||
|
||||
// Relink all other sinks reading this vertex to read 'varp'
|
||||
varp->srcp(nullptr);
|
||||
vtx.replaceWith(varp);
|
||||
varp->srcp(&vtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<const DfgVertexVar*> gatherCyclicVariables() {
|
||||
|
|
@ -72,74 +91,55 @@ class DfgRegularize final {
|
|||
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
||||
// There is only one Dfg when running this pass
|
||||
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
|
||||
if (varp->hasModRefs()) return false;
|
||||
if (varp->hasModWrRefs()) return false;
|
||||
if (varp->hasExtRefs()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Given a variable and its driver, return true iff the variable can be
|
||||
// replaced with its driver. Record replacement to be applied in the Ast
|
||||
// in user2p of the replaced variable.
|
||||
bool replaceable(DfgVertexVar* varp, DfgVertex* srcp) {
|
||||
// The given variable has no external references, and is read in the module
|
||||
// Predicate to determine if a temporary should be inserted or if a variable
|
||||
// should be preserved. The given vertices are either the same, or aVtxp is
|
||||
// the sole driver of bVtx, or aVtxp is cheaper to recompute and might have
|
||||
// multiple sinks. In either case, bVtx can be used to check sinks, and aVtx
|
||||
// can be used to check the operation.
|
||||
bool needsTemporary(DfgVertex& aVtx, DfgVertex& bVtx) {
|
||||
UASSERT_OBJ(&aVtx == &bVtx || aVtx.isCheaperThanLoad() || aVtx.singleSink() == &bVtx,
|
||||
&aVtx, "Mismatched vertices");
|
||||
UASSERT_OBJ(!aVtx.is<DfgVertexVar>(), &aVtx, "Should be an operation vertex");
|
||||
|
||||
// Make sure we are not trying to double replace something
|
||||
AstNode* const nodep = varp->nodep();
|
||||
UASSERT_OBJ(!nodep->user2p(), nodep, "Replacement already exists");
|
||||
if (bVtx.hasMultipleSinks()) {
|
||||
// We are not inlining expressions prior to the final scoped run
|
||||
if (m_dfg.modulep()) return true;
|
||||
|
||||
// If it is driven from another variable, it can be replaced by that variable.
|
||||
if (const DfgVarPacked* const drvp = srcp->cast<DfgVarPacked>()) {
|
||||
// Record replacement
|
||||
nodep->user2p(drvp->nodep());
|
||||
// The replacement will be read in the module, mark as such so it doesn't get removed.
|
||||
drvp->setHasModRdRefs();
|
||||
drvp->varp()->propagateAttrFrom(varp->varp());
|
||||
return true;
|
||||
// Add a temporary if it's cheaper to store and load from memory than recompute
|
||||
if (!aVtx.isCheaperThanLoad()) return true;
|
||||
|
||||
// Not adding temporary
|
||||
return false;
|
||||
}
|
||||
|
||||
// Expressions can only be inlined after V3Scope, as some passes assume variables.
|
||||
if (m_dfg.modulep()) return false;
|
||||
DfgVertex& sink = *bVtx.singleSink();
|
||||
|
||||
// If it is driven from a constant, it can be replaced by that constant.
|
||||
if (const DfgConst* const constp = srcp->cast<DfgConst>()) {
|
||||
// Need to create the AstConst
|
||||
AstConst* const newp = new AstConst{constp->fileline(), constp->num()};
|
||||
m_deleter.pushDeletep(newp);
|
||||
// Record replacement
|
||||
nodep->user2p(newp);
|
||||
return true;
|
||||
// No need to add a temporary if the single sink is a variable already
|
||||
if (sink.is<DfgVertexVar>()) return false;
|
||||
|
||||
// Do not inline expressions prior to the final scoped run, or if they are in a loop
|
||||
if (const DfgAstRd* const astRdp = sink.cast<DfgAstRd>()) {
|
||||
return m_dfg.modulep() || astRdp->inLoop();
|
||||
}
|
||||
|
||||
// Don't replace
|
||||
// Make sure roots of wide concatenation trees are written to variables,
|
||||
// this enables V3FuncOpt to split them which can be a big speed gain
|
||||
// without expanding them.
|
||||
if (aVtx.is<DfgConcat>()) {
|
||||
if (sink.is<DfgConcat>()) return false; // Not root of tree
|
||||
return VL_WORDS_I(static_cast<int>(aVtx.width())) > v3Global.opt.expandLimit();
|
||||
}
|
||||
|
||||
// No need for a temporary otherwise
|
||||
return false;
|
||||
}
|
||||
|
||||
template <bool T_Scoped>
|
||||
static void applyReplacement(AstVarRef* refp) {
|
||||
AstNode* const tgtp = T_Scoped ? static_cast<AstNode*>(refp->varScopep())
|
||||
: static_cast<AstNode*>(refp->varp());
|
||||
AstNode* replacementp = tgtp;
|
||||
while (AstNode* const altp = replacementp->user2p()) replacementp = altp;
|
||||
if (replacementp == tgtp) return;
|
||||
UASSERT_OBJ(refp->access().isReadOnly(), refp, "Replacing write AstVarRef");
|
||||
// If it's an inlined expression, repalce the VarRef entirely
|
||||
if (AstNodeExpr* const newp = VN_CAST(replacementp, NodeExpr)) {
|
||||
refp->replaceWith(newp->cloneTreePure(false));
|
||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||
return;
|
||||
}
|
||||
// Otherwise just re-point the VarRef to the new variable
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
AstVarScope* const newp = VN_AS(replacementp, VarScope);
|
||||
refp->varScopep(newp);
|
||||
refp->varp(newp->varp());
|
||||
} else {
|
||||
AstVar* const newp = VN_AS(replacementp, Var);
|
||||
refp->varp(newp);
|
||||
}
|
||||
}
|
||||
|
||||
void eliminateVars() {
|
||||
// Although we could eliminate some circular variables, doing so would
|
||||
// make UNOPTFLAT traces fairly usesless, so we will not do so.
|
||||
|
|
@ -150,13 +150,10 @@ class DfgRegularize final {
|
|||
|
||||
// Add all variables and all vertices with no sinks to the worklist
|
||||
m_dfg.forEachVertex([&](DfgVertex& vtx) {
|
||||
if (vtx.is<DfgVertexAst>()) return;
|
||||
if (vtx.is<DfgVertexVar>() || !vtx.hasSinks()) workList.push_front(vtx);
|
||||
});
|
||||
|
||||
// AstVar::user2p() : AstVar*/AstNodeExpr* -> The replacement variable/expression
|
||||
// AstVarScope::user2p() : AstVarScope*/AstNodeExpr* -> The replacement variable/expression
|
||||
const VNUser2InUse user2InUse;
|
||||
|
||||
// Remove vertex, enqueue it's sources
|
||||
const auto removeVertex = [&](DfgVertex& vtx) {
|
||||
// Add sources of removed vertex to work list
|
||||
|
|
@ -172,9 +169,6 @@ class DfgRegularize final {
|
|||
vtx.unlinkDelete(m_dfg);
|
||||
};
|
||||
|
||||
// Used to check if we need to apply variable replacements
|
||||
const VDouble0 usedVarsReplacedBefore = m_ctx.m_usedVarsReplaced;
|
||||
|
||||
// Process the work list
|
||||
workList.foreach([&](DfgVertex& vtx) {
|
||||
// Remove unused vertices
|
||||
|
|
@ -192,8 +186,6 @@ class DfgRegularize final {
|
|||
DfgVertex* const srcp = varp->srcp();
|
||||
if (!srcp) return;
|
||||
|
||||
// If it has multiple sinks, it can't be eliminated - would increase logic size
|
||||
if (varp->hasMultipleSinks()) return;
|
||||
// Can't eliminate if referenced external to the module - can't replace those refs
|
||||
if (varp->hasExtRefs()) return;
|
||||
// Can't eliminate if written in the module - the write needs to go somewhere, and
|
||||
|
|
@ -202,50 +194,24 @@ class DfgRegularize final {
|
|||
// There is only one Dfg when running this pass
|
||||
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
|
||||
|
||||
// At this point, the variable is used, driven only in this Dfg,
|
||||
// it has exactly 0 or 1 sinks in this Dfg, and might be read in
|
||||
// the host module, but no other references exist.
|
||||
|
||||
// Do not eliminate circular variables - need to preserve UNOPTFLAT traces
|
||||
if (circularVariables.count(varp)) return;
|
||||
|
||||
// If it is not read in the module, it can be inlined into the
|
||||
// Dfg unless partially driven (the partial driver network
|
||||
// can't be fed into arbitrary logic. TODO: we should peeophole
|
||||
// these away entirely).
|
||||
if (!varp->hasModRdRefs()) {
|
||||
UASSERT_OBJ(varp->hasSinks(), varp, "Shouldn't have made it here without sinks");
|
||||
// Don't inline if partially driven
|
||||
if (varp->defaultp()) return;
|
||||
if (srcp->is<DfgVertexSplice>()) return;
|
||||
if (srcp->is<DfgUnitArray>()) return;
|
||||
// Inline this variable into its single sink
|
||||
++m_ctx.m_usedVarsInlined;
|
||||
varp->replaceWith(varp->srcp());
|
||||
removeVertex(*varp);
|
||||
return;
|
||||
}
|
||||
// Do not inline if partially driven (the partial driver network can't be fed into
|
||||
// arbitrary logic. TODO: we should peeophole these away entirely)
|
||||
if (varp->defaultp()) return;
|
||||
if (srcp->is<DfgVertexSplice>()) return;
|
||||
if (srcp->is<DfgUnitArray>()) return;
|
||||
|
||||
// The varable is read in the module. We might still be able to replace it.
|
||||
if (replaceable(varp, srcp)) {
|
||||
// Replace this variable with its driver
|
||||
++m_ctx.m_usedVarsReplaced;
|
||||
// Inline it if it has a sink
|
||||
if (varp->hasSinks()) varp->replaceWith(srcp);
|
||||
// Delete the repalced variabel
|
||||
removeVertex(*varp);
|
||||
}
|
||||
// Do not eliminate variables that are driven from a vertex that needs a temporary
|
||||
if (!srcp->is<DfgVertexVar>() && needsTemporary(*srcp, *varp)) return;
|
||||
|
||||
// Inline this variable into its single sink
|
||||
++m_ctx.m_usedVarsInlined;
|
||||
varp->replaceWith(varp->srcp());
|
||||
removeVertex(*varp);
|
||||
return;
|
||||
});
|
||||
|
||||
// Job done if no replacements need to be applied
|
||||
if (m_ctx.m_usedVarsReplaced == usedVarsReplacedBefore) return;
|
||||
|
||||
// Apply variable replacements
|
||||
if (AstModule* const modp = m_dfg.modulep()) {
|
||||
modp->foreach([](AstVarRef* refp) { applyReplacement<false>(refp); });
|
||||
} else {
|
||||
v3Global.rootp()->foreach([](AstVarRef* refp) { applyReplacement<true>(refp); });
|
||||
}
|
||||
}
|
||||
|
||||
void insertTemporaries() {
|
||||
|
|
@ -257,32 +223,19 @@ class DfgRegularize final {
|
|||
|
||||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// LValue vertices feed into variables eventually and need not temporaries
|
||||
// LValue vertices feed into variables eventually and need no temporaries
|
||||
if (vtx.is<DfgVertexSplice>()) continue;
|
||||
if (vtx.is<DfgUnitArray>()) continue;
|
||||
|
||||
// If this Vertex was driving a variable, 'unlinline' would have
|
||||
// made that the single sink, so if there are multiple sinks
|
||||
// remaining, they must be non-variables. So nothing to do if:
|
||||
if (!vtx.hasMultipleSinks()) continue;
|
||||
|
||||
// 'uninline' would have made the result var cannonical, so there shouldn't be one
|
||||
UASSERT_OBJ(!vtx.getResultVar(), &vtx, "Failed to uninline variable");
|
||||
|
||||
// Do not add a temporary if it's cheaper to re-compute than to
|
||||
// load it from memory. This also increases available parallelism.
|
||||
if (vtx.isCheaperThanLoad()) {
|
||||
++m_ctx.m_temporariesOmitted;
|
||||
continue;
|
||||
}
|
||||
if (!needsTemporary(vtx, vtx)) continue;
|
||||
|
||||
// Need to create an intermediate variable
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
|
||||
FileLine* const flp = vtx.fileline();
|
||||
AstScope* const scopep = scoped ? vtx.scopep(scopeCache) : nullptr;
|
||||
DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtx.dtype(), scopep);
|
||||
++m_nTmps;
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
// Replace vertex with the variable, make it drive the variable
|
||||
vtx.replaceWith(newp);
|
||||
newp->srcp(&vtx);
|
||||
|
|
|
|||
|
|
@ -492,9 +492,12 @@ public:
|
|||
return false;
|
||||
}
|
||||
// For now, only direct array assignment is supported (e.g. a = b, but not a = _ ? b : c)
|
||||
if (rDtypep->isArray() && !VN_IS(rhsp, VarRef)) {
|
||||
++m_ctx.m_conv.nonRepDType;
|
||||
return false;
|
||||
if (rDtypep->isArray()) {
|
||||
if (!VN_IS(rhsp, VarRef)
|
||||
|| !lhsp->dtypep()->skipRefp()->sameTree(rhsp->dtypep()->skipRefp())) {
|
||||
++m_ctx.m_conv.nonRepDType;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Widths should match at this point
|
||||
UASSERT_OBJ(lhsp->width() == rhsp->width(), nodep, "Mismatched width reached DFG");
|
||||
|
|
@ -1746,11 +1749,16 @@ class AstToDfgSynthesize final {
|
|||
if (logicp->selectedForSynthesis()) continue;
|
||||
// There should be no sinks left for unselected DfgLogic, delete them here
|
||||
UASSERT_OBJ(!logicp->hasSinks(), vtxp, "Unselected 'DfgLogic' with sinks remaining");
|
||||
// Input variables will be read in Ast code, mark as such
|
||||
// Input variables will be read in Ast code, add Ast reference vertices
|
||||
// AstVar/AstVarScope::user4p() -> corresponding DfgVertexVar* in the graph
|
||||
const VNUser4InUse m_user4InUse;
|
||||
logicp->foreachSource([](DfgVertex& src) {
|
||||
src.as<DfgVertexVar>()->setHasModRdRefs();
|
||||
src.as<DfgVertexVar>()->nodep()->user4p(&src);
|
||||
return false;
|
||||
});
|
||||
V3DfgPasses::addAstRefs(m_dfg, logicp->nodep(), [](AstNode* varp) { //
|
||||
return varp->user4u().to<DfgVertexVar*>();
|
||||
});
|
||||
VL_DO_DANGLING(logicp->unlinkDelete(m_dfg), logicp);
|
||||
}
|
||||
debugDump("synth-selected");
|
||||
|
|
@ -1856,12 +1864,17 @@ class AstToDfgSynthesize final {
|
|||
// If synthesized, delete the corresponding AstNode. It is now in Dfg.
|
||||
logicp->nodep()->unlinkFrBack()->deleteTree();
|
||||
} else {
|
||||
// Not synthesized. Logic stays in Ast. Mark source variables
|
||||
//as read in module. Outputs already marked by revertTransivelyAndRemove.
|
||||
logicp->foreachSource([](DfgVertex& src) { //
|
||||
src.as<DfgVertexVar>()->setHasModRdRefs();
|
||||
// Not synthesized. Logic stays in Ast. Add Ast reference vertices.
|
||||
// Outputs already marked by revertTransivelyAndRemove.
|
||||
// AstVar/AstVarScope::user4p() -> corresponding DfgVertexVar* in the graph
|
||||
const VNUser4InUse m_user4InUse;
|
||||
logicp->foreachSource([](DfgVertex& src) {
|
||||
src.as<DfgVertexVar>()->nodep()->user4p(&src);
|
||||
return false;
|
||||
});
|
||||
V3DfgPasses::addAstRefs(m_dfg, logicp->nodep(), [](AstNode* varp) { //
|
||||
return varp->user4u().to<DfgVertexVar*>();
|
||||
});
|
||||
}
|
||||
|
||||
// Delete this DfgLogic
|
||||
|
|
|
|||
|
|
@ -121,14 +121,9 @@ public:
|
|||
bool hasDfgRefs() const { return nodep()->user1() >> 6; } // I.e.: (nodep()->user1() >> 5) > 1
|
||||
|
||||
// Variable referenced from Ast code in the same module/netlist
|
||||
static bool hasModRdRefs(const AstNode* nodep) { return nodep->user1() & 0x04; }
|
||||
static bool hasModWrRefs(const AstNode* nodep) { return nodep->user1() & 0x08; }
|
||||
static void setHasModRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x04); }
|
||||
static void setHasModWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x08); }
|
||||
bool hasModRdRefs() const { return hasModRdRefs(nodep()); }
|
||||
bool hasModWrRefs() const { return hasModWrRefs(nodep()); }
|
||||
bool hasModRefs() const { return hasModRdRefs() || hasModWrRefs(); }
|
||||
void setHasModRdRefs() const { setHasModRdRefs(nodep()); }
|
||||
void setHasModWrRefs() const { setHasModWrRefs(nodep()); }
|
||||
|
||||
// Variable referenced outside the containing module/netlist.
|
||||
|
|
@ -149,7 +144,7 @@ public:
|
|||
// A DfgVarVertex is written in exactly one DfgGraph, and might be read in an arbitrary
|
||||
// number of other DfgGraphs. If it's driven in this DfgGraph, it's read in others.
|
||||
if (hasDfgRefs()) return srcp() || defaultp();
|
||||
return hasExtRdRefs() || hasModRdRefs();
|
||||
return hasExtRdRefs();
|
||||
}
|
||||
|
||||
// The value of this vertex might differ from what is defined by its drivers
|
||||
|
|
@ -193,6 +188,48 @@ public:
|
|||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Ast reference vertices
|
||||
|
||||
class DfgVertexAst VL_NOT_FINAL : public DfgVertex {
|
||||
// Represents a reference in the Ast
|
||||
|
||||
AstNodeExpr* m_exprp; // The AstNodeExpr representing this reference
|
||||
|
||||
public:
|
||||
DfgVertexAst(DfgGraph& dfg, VDfgType type, AstNodeExpr* exprp)
|
||||
: DfgVertex{dfg, type, exprp->fileline(), *DfgDataType::fromAst(exprp->dtypep())}
|
||||
, m_exprp{exprp} {}
|
||||
ASTGEN_MEMBERS_DfgVertexAst;
|
||||
|
||||
AstNodeExpr* exprp() const { return m_exprp; }
|
||||
void exprp(AstNodeExpr* exprp) { m_exprp = exprp; }
|
||||
};
|
||||
|
||||
class DfgAstRd final : public DfgVertexAst {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
||||
const bool m_inSenItem; // Reference is in a sensitivity list
|
||||
const bool m_inLoop; // Reference is in a loop
|
||||
|
||||
public:
|
||||
DfgAstRd(DfgGraph& dfg, AstNodeExpr* exprp, bool inSenItem, bool inLoop)
|
||||
: DfgVertexAst{dfg, dfgType(), exprp}
|
||||
, m_inSenItem{inSenItem}
|
||||
, m_inLoop{inLoop} {
|
||||
// Allocate sources
|
||||
newInput();
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgAstRd;
|
||||
|
||||
DfgVertex* srcp() const { return inputp(0); }
|
||||
void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
|
||||
std::string srcName(size_t) const override final { return ""; }
|
||||
bool inSenItem() const { return m_inSenItem; }
|
||||
bool inLoop() const { return m_inLoop; }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Nullary vertices - 0 inputs
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%Error: Internal Error: t/t_opt_const.v:12:8: ../V3Ast.cpp:#: widthMismatch detected 'lhsp()->widthMin() != rhsp()->widthMin()' @ ../V3AstNodes.cpp:#OUT:(G/wu32/1) LHS:(G/w32) RHS:(G/wu32/1)
|
||||
12 | module t(
|
||||
| ^
|
||||
%Error: Internal Error: t/t_opt_const.v:142:26: ../V3Ast.cpp:#: widthMismatch detected 'lhsp()->widthMin() != rhsp()->widthMin()' @ ../V3AstNodes.cpp:#OUT:(G/wu32/5) LHS:(G/w32) RHS:(G/wu32/5)
|
||||
142 | bug3182 i_bug3182(.in(d[4:0]), .out(bug3182_out));
|
||||
| ^
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
5 i == '{ 0: 255, 1: 255, 2: 255, 3: 255, 4: 255, 5: 255, 6: 255, 7: 255}
|
||||
15 i == '{ 0: 255, 1: 254, 2: 253, 3: 252, 4: 251, 5: 250, 6: 249, 7: 248}
|
||||
25 i == '{ 0: 255, 1: 253, 2: 251, 3: 249, 4: 247, 5: 245, 6: 243, 7: 241}
|
||||
35 i == '{ 0: 255, 1: 252, 2: 249, 3: 246, 4: 243, 5: 240, 6: 237, 7: 234}
|
||||
45 i == '{ 0: 255, 1: 251, 2: 247, 3: 243, 4: 239, 5: 235, 6: 231, 7: 227}
|
||||
55 i == '{ 0: 255, 1: 250, 2: 245, 3: 240, 4: 235, 5: 230, 6: 225, 7: 220}
|
||||
65 i == '{ 0: 255, 1: 249, 2: 243, 3: 237, 4: 231, 5: 225, 6: 219, 7: 213}
|
||||
75 i == '{ 0: 255, 1: 248, 2: 241, 3: 234, 4: 227, 5: 220, 6: 213, 7: 206}
|
||||
85 i == '{ 0: 255, 1: 247, 2: 239, 3: 231, 4: 223, 5: 215, 6: 207, 7: 199}
|
||||
95 i == '{ 0: 255, 1: 246, 2: 237, 3: 228, 4: 219, 5: 210, 6: 201, 7: 192}
|
||||
105 i == '{ 0: 255, 1: 245, 2: 235, 3: 225, 4: 215, 5: 205, 6: 195, 7: 185}
|
||||
115 i == '{ 0: 255, 1: 244, 2: 233, 3: 222, 4: 211, 5: 200, 6: 189, 7: 178}
|
||||
125 i == '{ 0: 255, 1: 243, 2: 231, 3: 219, 4: 207, 5: 195, 6: 183, 7: 171}
|
||||
135 i == '{ 0: 255, 1: 242, 2: 229, 3: 216, 4: 203, 5: 190, 6: 177, 7: 164}
|
||||
145 i == '{ 0: 255, 1: 241, 2: 227, 3: 213, 4: 199, 5: 185, 6: 171, 7: 157}
|
||||
155 i == '{ 0: 255, 1: 240, 2: 225, 3: 210, 4: 195, 5: 180, 6: 165, 7: 150}
|
||||
165 i == '{ 0: 255, 1: 239, 2: 223, 3: 207, 4: 191, 5: 175, 6: 159, 7: 143}
|
||||
175 i == '{ 0: 255, 1: 238, 2: 221, 3: 204, 4: 187, 5: 170, 6: 153, 7: 136}
|
||||
185 i == '{ 0: 255, 1: 237, 2: 219, 3: 201, 4: 183, 5: 165, 6: 147, 7: 129}
|
||||
195 i == '{ 0: 255, 1: 236, 2: 217, 3: 198, 4: 179, 5: 160, 6: 141, 7: 122}
|
||||
205 i == '{ 0: 255, 1: 235, 2: 215, 3: 195, 4: 175, 5: 155, 6: 135, 7: 115}
|
||||
215 i == '{ 0: 255, 1: 234, 2: 213, 3: 192, 4: 171, 5: 150, 6: 129, 7: 108}
|
||||
225 i == '{ 0: 255, 1: 233, 2: 211, 3: 189, 4: 167, 5: 145, 6: 123, 7: 101}
|
||||
235 i == '{ 0: 255, 1: 232, 2: 209, 3: 186, 4: 163, 5: 140, 6: 117, 7: 94}
|
||||
245 i == '{ 0: 255, 1: 231, 2: 207, 3: 183, 4: 159, 5: 135, 6: 111, 7: 87}
|
||||
255 i == '{ 0: 255, 1: 230, 2: 205, 3: 180, 4: 155, 5: 130, 6: 105, 7: 80}
|
||||
265 i == '{ 0: 255, 1: 229, 2: 203, 3: 177, 4: 151, 5: 125, 6: 99, 7: 73}
|
||||
275 i == '{ 0: 255, 1: 228, 2: 201, 3: 174, 4: 147, 5: 120, 6: 93, 7: 66}
|
||||
285 i == '{ 0: 255, 1: 227, 2: 199, 3: 171, 4: 143, 5: 115, 6: 87, 7: 59}
|
||||
295 i == '{ 0: 255, 1: 226, 2: 197, 3: 168, 4: 139, 5: 110, 6: 81, 7: 52}
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
|
||||
logic clk = 0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
initial begin
|
||||
#300;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
localparam int N = 8;
|
||||
|
||||
logic [N-1:0][7:0] i = '0;
|
||||
logic [N-1:0][7:0] o;
|
||||
|
||||
always @(posedge clk) begin
|
||||
for (int n = 0; n < N; ++n) begin
|
||||
i[n] <= i[n] + 8'(n);
|
||||
end
|
||||
end
|
||||
|
||||
for (genvar n = 0; n < N; ++n) begin
|
||||
assign o[n] = ~i[n];
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
$write("%05t i == '{", $time);
|
||||
for (int n = 0; n < N; ++n) begin
|
||||
if (n > 0) $write(", ");
|
||||
$write("%2d: %4d", n, o[n]);
|
||||
end
|
||||
$write("}\n");
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
|
||||
bit clk = 1'b0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
initial begin
|
||||
#300;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
// Complicated way to write constant 0 that only Dfg can decipher
|
||||
wire [1:0] constant = 2'b0 ^ (({2{clk}} & ~{2{clk}}));
|
||||
|
||||
always @(posedge constant[0]) begin
|
||||
$stop;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
DFG 'post inline' patterns with depth 1
|
||||
9 (CONCAT _A:1 _B:a):b
|
||||
8 (REDXOR _A:a):1
|
||||
4 (ASTRD vA:a):a
|
||||
3 (NOT vA:a)*:a
|
||||
2 (AND _A:a _B:a):a
|
||||
1 (AND _A:a _B:a)*:a
|
||||
|
|
|
|||
|
|
@ -20,6 +20,6 @@ if test.vlt_all:
|
|||
out_filename,
|
||||
r'{"type":"VAR","name":"t.f0.clock_gate.clken_latched","addr":"[^"]*","loc":"\w,44:[^"]*","dtypep":"\(\w+\)",.*"origName":"clken_latched",.*"isLatched":true,.*"dtypeName":"logic"'
|
||||
)
|
||||
test.file_grep(test.stats, r'Optimizations, Gate sigs deduped\s+(\d+)', 4)
|
||||
test.file_grep(test.stats, r'Optimizations, Gate sigs deduped\s+(\d+)', 2)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
Loading…
Reference in New Issue