Improve Dfg variable removal and temporary insertion (#6401)
Combined Dfg variable elimination into the regularization pass that runs before converting back to Ast. This avoids introducing some unnecessary temporaries. Added replacing of variables with constants in the Ast if after the Dfg passes they are known to be constants. This is only done in final scoped Dfg application. Avoid introducing temporaries for common sub-expressions that are cheaper to re-compute than store in a temporary variable. Enable removal of redundant unpacked array variables. Also fixes #6394 as this patch involved changes to that code.
This commit is contained in:
parent
6c69210037
commit
c6b0918db5
30
src/V3Dfg.h
30
src/V3Dfg.h
|
|
@ -315,6 +315,9 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
// Is this vertex cheaper to re-compute than to load out of memoy
|
||||
inline bool isCheaperThanLoad() const;
|
||||
|
||||
// Methods that allow DfgVertex to participate in error reporting/messaging
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEnd(str);
|
||||
|
|
@ -797,6 +800,33 @@ void DfgEdge::relinkSrcp(DfgVertex* srcp) {
|
|||
|
||||
// }}}
|
||||
|
||||
// DfgVertex {{{
|
||||
|
||||
bool DfgVertex::isCheaperThanLoad() const {
|
||||
// Array sels are just address computation
|
||||
if (is<DfgArraySel>()) return true;
|
||||
// Small constant select from variable
|
||||
if (const DfgSel* const selp = cast<DfgSel>()) {
|
||||
if (!selp->fromp()->is<DfgVarPacked>()) return false;
|
||||
if (selp->fromp()->width() <= VL_QUADSIZE) return true;
|
||||
const uint32_t lsb = selp->lsb();
|
||||
const uint32_t msb = lsb + selp->width() - 1;
|
||||
return VL_BITWORD_E(msb) == VL_BITWORD_E(lsb);
|
||||
}
|
||||
// Zero extend of a cheap vertex - Extend(_) was converted to Concat(0, _)
|
||||
if (const DfgConcat* const catp = cast<DfgConcat>()) {
|
||||
if (catp->width() > VL_QUADSIZE) return false;
|
||||
const DfgConst* const lCatp = catp->lhsp()->cast<DfgConst>();
|
||||
if (!lCatp) return false;
|
||||
if (!lCatp->isZero()) return false;
|
||||
return catp->rhsp()->isCheaperThanLoad();
|
||||
}
|
||||
// Otherwise probably not
|
||||
return false;
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// DfgGraph {{{
|
||||
|
||||
template <typename T_User>
|
||||
|
|
|
|||
|
|
@ -156,27 +156,6 @@ private:
|
|||
addStat("result equations", m_resultEquations);
|
||||
}
|
||||
};
|
||||
class V3DfgEliminateVarsContext final : public V3DfgSubContext {
|
||||
// Only V3DfgContext can create an instance
|
||||
friend class V3DfgContext;
|
||||
|
||||
public:
|
||||
// STATE
|
||||
std::vector<AstNode*> m_deleteps; // AstVar/AstVarScope that can be deleted at the end
|
||||
VDouble0 m_varsReplaced; // Number of variables replaced
|
||||
VDouble0 m_varsRemoved; // Number of variables removed
|
||||
|
||||
private:
|
||||
V3DfgEliminateVarsContext(V3DfgContext& ctx, const std::string& label)
|
||||
: V3DfgSubContext{ctx, label, "EliminateVars"} {}
|
||||
~V3DfgEliminateVarsContext() {
|
||||
for (AstNode* const nodep : m_deleteps) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
addStat("variables replaced", m_varsReplaced);
|
||||
addStat("variables removed", m_varsRemoved);
|
||||
}
|
||||
};
|
||||
class V3DfgPeepholeContext final : public V3DfgSubContext {
|
||||
// Only V3DfgContext can create an instance
|
||||
friend class V3DfgContext;
|
||||
|
|
@ -198,12 +177,28 @@ 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
|
||||
|
||||
private:
|
||||
V3DfgRegularizeContext(V3DfgContext& ctx, const std::string& label)
|
||||
: V3DfgSubContext{ctx, label, "Regularize"} {}
|
||||
~V3DfgRegularizeContext() { addStat("temporaries introduced", m_temporariesIntroduced); }
|
||||
~V3DfgRegularizeContext() {
|
||||
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);
|
||||
}
|
||||
};
|
||||
class V3DfgSynthesisContext final : public V3DfgSubContext {
|
||||
// Only V3DfgContext can create an instance
|
||||
|
|
@ -352,7 +347,6 @@ public:
|
|||
V3DfgCseContext m_cseContext0{*this, m_label + " 1st"};
|
||||
V3DfgCseContext m_cseContext1{*this, m_label + " 2nd"};
|
||||
V3DfgDfgToAstContext m_dfg2AstContext{*this, m_label};
|
||||
V3DfgEliminateVarsContext m_eliminateVarsContext{*this, m_label};
|
||||
V3DfgPeepholeContext m_peepholeContext{*this, m_label};
|
||||
V3DfgRegularizeContext m_regularizeContext{*this, m_label};
|
||||
V3DfgSynthesisContext m_synthContext{*this, m_label};
|
||||
|
|
|
|||
|
|
@ -139,8 +139,8 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
|
||||
AstNodeExpr* convertDfgVertexToAstNodeExpr(DfgVertex* vtxp) {
|
||||
UASSERT_OBJ(!m_resultp, vtxp, "Result already computed");
|
||||
UASSERT_OBJ(!vtxp->hasMultipleSinks() || vtxp->is<DfgVertexVar>()
|
||||
|| vtxp->is<DfgArraySel>() || vtxp->is<DfgConst>(),
|
||||
UASSERT_OBJ(vtxp->is<DfgVertexVar>() || vtxp->is<DfgConst>() //
|
||||
|| !vtxp->hasMultipleSinks() || vtxp->isCheaperThanLoad(), //
|
||||
vtxp, "Intermediate DFG value with multiple uses");
|
||||
iterate(vtxp);
|
||||
UASSERT_OBJ(m_resultp, vtxp, "Missing result");
|
||||
|
|
|
|||
|
|
@ -253,7 +253,58 @@ class DataflowOptimize final {
|
|||
// STATE
|
||||
V3DfgContext m_ctx; // The context holding values that need to persist across multiple graphs
|
||||
|
||||
static void markExternallyReferencedVariables(AstNetlist* netlistp, bool scoped) {
|
||||
netlistp->foreach([scoped](AstNode* nodep) {
|
||||
// Check variabel flags
|
||||
if (scoped) {
|
||||
if (AstVarScope* const vscp = VN_CAST(nodep, VarScope)) {
|
||||
const AstVar* const varp = vscp->varp();
|
||||
// Force and trace have already been processed
|
||||
const bool hasExtRd = varp->isPrimaryIO() || varp->isSigUserRdPublic();
|
||||
const bool hasExtWr = varp->isPrimaryIO() || varp->isSigUserRWPublic();
|
||||
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(vscp);
|
||||
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(vscp);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
const bool hasExtRd = varp->isPrimaryIO() || varp->isSigUserRdPublic() //
|
||||
|| varp->isForced() || varp->isTrace();
|
||||
const bool hasExtWr = varp->isPrimaryIO() || varp->isSigUserRWPublic() //
|
||||
|| varp->isForced();
|
||||
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(varp);
|
||||
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(varp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check hierarchical references
|
||||
if (const AstVarXRef* const xrefp = VN_CAST(nodep, VarXRef)) {
|
||||
AstVar* const tgtp = xrefp->varp();
|
||||
if (!tgtp) return;
|
||||
if (xrefp->access().isReadOrRW()) DfgVertexVar::setHasExtRdRefs(tgtp);
|
||||
if (xrefp->access().isWriteOrRW()) DfgVertexVar::setHasExtWrRefs(tgtp);
|
||||
return;
|
||||
}
|
||||
// Check cell ports
|
||||
if (const AstCell* const cellp = VN_CAST(nodep, Cell)) {
|
||||
for (const AstPin *pinp = cellp->pinsp(), *nextp; pinp; pinp = nextp) {
|
||||
nextp = VN_AS(pinp->nextp(), Pin);
|
||||
AstVar* const tgtp = pinp->modVarp();
|
||||
if (!tgtp) return;
|
||||
const VDirection dir = tgtp->direction();
|
||||
// hasExtRd/hasExtWr from perspective of Pin
|
||||
const bool hasExtRd = dir == VDirection::OUTPUT || dir.isInoutOrRef();
|
||||
const bool hasExtWr = dir == VDirection::INPUT || dir.isInoutOrRef();
|
||||
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(tgtp);
|
||||
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(tgtp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void optimize(DfgGraph& dfg) {
|
||||
// Dump the initial graph for debugging
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "dfg-in");
|
||||
|
||||
// Synthesize DfgLogic vertices
|
||||
|
|
@ -295,27 +346,22 @@ class DataflowOptimize final {
|
|||
// Quick sanity check
|
||||
UASSERT(dfg.size() == 0, "DfgGraph should have become empty");
|
||||
|
||||
// For each acyclic component
|
||||
// Optimize each acyclic component
|
||||
for (const std::unique_ptr<DfgGraph>& component : acyclicComponents) {
|
||||
// Optimize the component
|
||||
V3DfgPasses::optimize(*component, m_ctx);
|
||||
}
|
||||
// Merge back under the main DFG (we will convert everything back in one go)
|
||||
|
||||
// Merge everything back under the main DFG
|
||||
dfg.mergeGraphs(std::move(acyclicComponents));
|
||||
|
||||
// Eliminate redundant variables. Run this on the whole acyclic DFG. It needs to traverse
|
||||
// the module/netlist to perform variable substitutions. Doing this by component would do
|
||||
// redundant traversals and can be extremely slow when we have many components.
|
||||
V3DfgPasses::eliminateVars(dfg, m_ctx.m_eliminateVarsContext);
|
||||
|
||||
// For each cyclic component
|
||||
for (const std::unique_ptr<DfgGraph>& component : cyclicComponents) {
|
||||
// Converting back to Ast assumes the 'regularize' pass was run, so we must run it
|
||||
V3DfgPasses::regularize(*component, m_ctx.m_regularizeContext);
|
||||
}
|
||||
// Merge back under the main DFG (we will convert everything back in one go)
|
||||
dfg.mergeGraphs(std::move(cyclicComponents));
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "optimized");
|
||||
|
||||
// Regularize the graph after merging it all back together so all
|
||||
// references are known and we only need to iterate the Ast once
|
||||
// to replace redundant variables.
|
||||
V3DfgPasses::regularize(dfg, m_ctx.m_regularizeContext);
|
||||
|
||||
// Dump the final graph for debugging
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotFilePrefixed(m_ctx.prefix() + "dfg-out");
|
||||
}
|
||||
|
||||
|
|
@ -330,22 +376,18 @@ class DataflowOptimize final {
|
|||
});
|
||||
}
|
||||
|
||||
if (!netlistp->topScopep()) {
|
||||
// Running after V3Scope
|
||||
const bool scoped = netlistp->topScopep();
|
||||
|
||||
// Mark variables with external references
|
||||
markExternallyReferencedVariables(netlistp, scoped);
|
||||
|
||||
if (!scoped) {
|
||||
// Pre V3Scope application. Run on each module separately.
|
||||
|
||||
// Mark cross-referenced variables
|
||||
netlistp->foreach([](const AstVarXRef* xrefp) {
|
||||
AstVar* const tgtp = xrefp->varp();
|
||||
if (xrefp->access().isReadOrRW()) DfgVertexVar::setHasRdXRefs(tgtp);
|
||||
if (xrefp->access().isWriteOrRW()) DfgVertexVar::setHasWrXRefs(tgtp);
|
||||
});
|
||||
|
||||
// Run the optimization
|
||||
for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) {
|
||||
// Only optimize proper modules
|
||||
AstModule* const modp = VN_CAST(nodep, Module);
|
||||
if (!modp) continue;
|
||||
|
||||
// Pre V3Scope application. Run on module.
|
||||
UINFO(4, "Applying DFG optimization to module '" << modp->name() << "'");
|
||||
++m_ctx.m_modules;
|
||||
|
|
|
|||
|
|
@ -27,11 +27,18 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
|
||||
void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) {
|
||||
if (varp->hasSinks() && varp->isDrivenFullyByDfg()) {
|
||||
varp->replaceWith(varp->srcp());
|
||||
}
|
||||
}
|
||||
// Nothing to inline it into
|
||||
if (!vtx.hasSinks()) continue;
|
||||
// Nohting to inline
|
||||
DfgVertex* const srcp = vtx.srcp();
|
||||
if (!srcp) continue;
|
||||
// Value can differ from driver
|
||||
if (vtx.isVolatile()) continue;
|
||||
// Partial driver cannot be inlined
|
||||
if (srcp->is<DfgVertexSplice>()) continue;
|
||||
if (srcp->is<DfgUnitArray>()) continue;
|
||||
// Okie dokie, here we go ...
|
||||
vtx.replaceWith(srcp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +99,7 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
}
|
||||
|
||||
void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
UASSERT(dfg.modulep(), "binToOneHot only works with unscoped DfgGraphs for now");
|
||||
if (!dfg.modulep()) return; // binToOneHot only works with unscoped DfgGraphs for now
|
||||
|
||||
// Structure to keep track of comparison details
|
||||
struct Term final {
|
||||
|
|
@ -330,138 +337,31 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
||||
// Worklist based algoritm
|
||||
DfgWorklist workList{dfg};
|
||||
|
||||
// Add all variables to the work list
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) workList.push_front(vtx);
|
||||
|
||||
// List of variables (AstVar or AstVarScope) we are replacing
|
||||
std::vector<AstNode*> replacedVariables;
|
||||
// AstVar::user2p() : AstVar* -> The replacement variables
|
||||
// AstVarScope::user2p() : AstVarScope* -> The replacement variables
|
||||
const VNUser2InUse user2InUse;
|
||||
|
||||
// Whether we need to apply variable replacements
|
||||
bool doReplace = false;
|
||||
|
||||
// Process the work list
|
||||
workList.foreach([&](DfgVertex& vtx) {
|
||||
// Remove unused non-variable vertices
|
||||
if (!vtx.is<DfgVertexVar>() && !vtx.hasSinks()) {
|
||||
// Add sources of removed vertex to work list
|
||||
vtx.foreachSource([&](DfgVertex& src) {
|
||||
workList.push_front(src);
|
||||
return false;
|
||||
});
|
||||
// Remove the unused vertex
|
||||
vtx.unlinkDelete(dfg);
|
||||
return;
|
||||
}
|
||||
|
||||
// We can only eliminate DfgVarPacked vertices at the moment
|
||||
DfgVarPacked* const varp = vtx.cast<DfgVarPacked>();
|
||||
if (!varp) return;
|
||||
|
||||
if (!varp->tmpForp()) {
|
||||
// Can't remove regular variable if it has external drivers
|
||||
if (!varp->isDrivenFullyByDfg()) return;
|
||||
} else {
|
||||
// Can't remove partially driven used temporaries
|
||||
if (!varp->isDrivenFullyByDfg() && varp->hasSinks()) return;
|
||||
}
|
||||
|
||||
// Can't remove if referenced external to the module/netlist
|
||||
if (varp->hasExtRefs()) return;
|
||||
// Can't remove if written in the module
|
||||
if (varp->hasModWrRefs()) return;
|
||||
// Can't remove if referenced in other DFGs of the same module
|
||||
if (varp->hasDfgRefs()) return;
|
||||
|
||||
// If it has multiple sinks, it can't be eliminated
|
||||
if (varp->hasMultipleSinks()) return;
|
||||
|
||||
if (!varp->hasModRefs()) {
|
||||
// If it is only referenced in this DFG, it can be removed
|
||||
++ctx.m_varsRemoved;
|
||||
varp->replaceWith(varp->srcp());
|
||||
ctx.m_deleteps.push_back(varp->nodep()); // Delete variable at the end
|
||||
} else if (const DfgVarPacked* const driverp = varp->srcp()->cast<DfgVarPacked>()) {
|
||||
// If it's driven from another variable, it can be replaced by that.
|
||||
// Mark it for replacement
|
||||
++ctx.m_varsReplaced;
|
||||
UASSERT_OBJ(!varp->hasSinks(), varp, "Variable inlining should make this impossible");
|
||||
// Grab the AstVar/AstVarScope
|
||||
AstNode* const nodep = varp->nodep();
|
||||
UASSERT_OBJ(!nodep->user2p(), nodep, "Replacement already exists");
|
||||
doReplace = true;
|
||||
ctx.m_deleteps.push_back(nodep); // Delete variable at the end
|
||||
nodep->user2p(driverp->nodep());
|
||||
} else {
|
||||
// Otherwise this *is* the canonical var, keep it
|
||||
return;
|
||||
}
|
||||
|
||||
// Add sources of redundant variable to the work list
|
||||
vtx.foreachSource([&](DfgVertex& src) {
|
||||
workList.push_front(src);
|
||||
return false;
|
||||
});
|
||||
// Remove the redundant variable
|
||||
vtx.unlinkDelete(dfg);
|
||||
});
|
||||
|
||||
// Job done if no replacements possible
|
||||
if (!doReplace) return;
|
||||
|
||||
// Apply variable replacements
|
||||
if (AstModule* const modp = dfg.modulep()) {
|
||||
modp->foreach([&](AstVarRef* refp) {
|
||||
AstVar* varp = refp->varp();
|
||||
while (AstVar* const replacep = VN_AS(varp->user2p(), Var)) varp = replacep;
|
||||
refp->varp(varp);
|
||||
});
|
||||
} else {
|
||||
v3Global.rootp()->foreach([&](AstVarRef* refp) {
|
||||
AstVarScope* vscp = refp->varScopep();
|
||||
while (AstVarScope* const replacep = VN_AS(vscp->user2p(), VarScope)) vscp = replacep;
|
||||
refp->varScopep(vscp);
|
||||
refp->varp(vscp->varp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) {
|
||||
// There is absolutely nothing useful we can do with a graph of size 2 or less
|
||||
if (dfg.size() <= 2) return;
|
||||
|
||||
int passNumber = 0;
|
||||
|
||||
const auto apply = [&](int dumpLevel, const string& name, std::function<void()> pass) {
|
||||
const auto run = [&](const std::string& name, bool dump, std::function<void()> pass) {
|
||||
// Apply the pass
|
||||
pass();
|
||||
if (dumpDfgLevel() >= dumpLevel) {
|
||||
const string strippedName = VString::removeWhitespace(name);
|
||||
const string label
|
||||
= ctx.prefix() + "pass-" + cvtToStr(passNumber) + "-" + strippedName;
|
||||
dfg.dumpDotFilePrefixed(label);
|
||||
}
|
||||
// Debug dump
|
||||
if (dump) dfg.dumpDotFilePrefixed(ctx.prefix() + "opt-" + VString::removeWhitespace(name));
|
||||
// Internal type check
|
||||
if (v3Global.opt.debugCheck()) V3DfgPasses::typeCheck(dfg);
|
||||
++passNumber;
|
||||
};
|
||||
|
||||
apply(3, "input ", [&]() {});
|
||||
apply(4, "inlineVars ", [&]() { inlineVars(dfg); });
|
||||
apply(4, "cse0 ", [&]() { cse(dfg, ctx.m_cseContext0); });
|
||||
if (dfg.modulep()) {
|
||||
apply(4, "binToOneHot ", [&]() { binToOneHot(dfg, ctx.m_binToOneHotContext); });
|
||||
}
|
||||
if (v3Global.opt.fDfgPeephole()) {
|
||||
apply(4, "peephole ", [&]() { peephole(dfg, ctx.m_peepholeContext); });
|
||||
// We just did CSE above, so without peephole there is no need to run it again these
|
||||
apply(4, "cse1 ", [&]() { cse(dfg, ctx.m_cseContext1); });
|
||||
}
|
||||
// Currend debug dump level
|
||||
const uint32_t dumpLvl = dumpDfgLevel();
|
||||
|
||||
// Run passes
|
||||
run("input ", dumpLvl >= 3, [&]() { /* debug dump only */ });
|
||||
run("inlineVars ", dumpLvl >= 4, [&]() { inlineVars(dfg); });
|
||||
run("cse0 ", dumpLvl >= 4, [&]() { cse(dfg, ctx.m_cseContext0); });
|
||||
run("binToOneHot ", dumpLvl >= 4, [&]() { binToOneHot(dfg, ctx.m_binToOneHotContext); });
|
||||
run("peephole ", dumpLvl >= 4, [&]() { peephole(dfg, ctx.m_peepholeContext); });
|
||||
run("cse1 ", dumpLvl >= 4, [&]() { cse(dfg, ctx.m_cseContext1); });
|
||||
run("output ", dumpLvl >= 3, [&]() { /* debug dump only */ });
|
||||
|
||||
// Accumulate patterns for reporting
|
||||
if (v3Global.opt.stats()) ctx.m_patternStats.accumulate(dfg);
|
||||
apply(4, "regularize", [&]() { regularize(dfg, ctx.m_regularizeContext); });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,8 +82,6 @@ void peephole(DfgGraph&, V3DfgPeepholeContext&) VL_MT_DISABLED;
|
|||
void regularize(DfgGraph&, V3DfgRegularizeContext&) VL_MT_DISABLED;
|
||||
// Remove unused nodes
|
||||
void removeUnused(DfgGraph&) VL_MT_DISABLED;
|
||||
// Eliminate (remove or replace) redundant variables. Also removes resulting unused logic.
|
||||
void eliminateVars(DfgGraph&, V3DfgEliminateVarsContext&) VL_MT_DISABLED;
|
||||
// Check all types are consistent. This will not return if there is a type error.
|
||||
void typeCheck(const DfgGraph&) VL_MT_DISABLED;
|
||||
|
||||
|
|
|
|||
|
|
@ -1714,5 +1714,6 @@ public:
|
|||
};
|
||||
|
||||
void V3DfgPasses::peephole(DfgGraph& dfg, V3DfgPeepholeContext& ctx) {
|
||||
if (!v3Global.opt.fDfgPeephole()) return;
|
||||
V3DfgPeephole::apply(dfg, ctx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,58 +27,279 @@
|
|||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
class DfgRegularize final {
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
V3DfgRegularizeContext& m_ctx; // The optimization context for stats
|
||||
|
||||
// Prefix of temporary variable names
|
||||
size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only
|
||||
VNDeleter m_deleter; // Deletes replacement nodes at the end
|
||||
|
||||
// METHODS
|
||||
|
||||
// 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,
|
||||
// so no need to process them here, they are where they must be.
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
// Don't process LValue operations
|
||||
if (vtx.is<DfgVertexSplice>()) continue;
|
||||
if (vtx.is<DfgUnitArray>()) 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() {
|
||||
DfgUserMap<uint64_t> vtx2Scc = m_dfg.makeUserMap<uint64_t>();
|
||||
V3DfgPasses::colorStronglyConnectedComponents(m_dfg, vtx2Scc);
|
||||
std::unordered_set<const DfgVertexVar*> circularVariables;
|
||||
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
|
||||
if (vtx2Scc[vtx]) circularVariables.emplace(&vtx);
|
||||
}
|
||||
return circularVariables;
|
||||
}
|
||||
|
||||
bool isUnused(const DfgVertex& vtx) {
|
||||
if (vtx.hasSinks()) return false;
|
||||
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->hasExtRefs()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Given a variable and its driver, return true iff the variable can be
|
||||
// repalced 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
|
||||
|
||||
// Make sure we are not trying to double replace something
|
||||
AstNode* const nodep = varp->nodep();
|
||||
UASSERT_OBJ(!nodep->user2p(), nodep, "Replacement already exists");
|
||||
|
||||
// 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 repalcement will be read in the module, mark as such so it doesn't get removed.
|
||||
drvp->setHasModRdRefs();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Expressions can only be inlined after V3Scope, as some passes assume variables.
|
||||
if (m_dfg.modulep()) return false;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Don't replace
|
||||
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));
|
||||
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.
|
||||
const std::unordered_set<const DfgVertexVar*> circularVariables = gatherCyclicVariables();
|
||||
|
||||
// Worklist based algoritm
|
||||
DfgWorklist workList{m_dfg};
|
||||
|
||||
// Add all variables and all vertices with no sinks to the worklist
|
||||
m_dfg.forEachVertex([&](DfgVertex& vtx) {
|
||||
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
|
||||
vtx.foreachSource([&](DfgVertex& src) {
|
||||
workList.push_front(src);
|
||||
return false;
|
||||
});
|
||||
// Delete corresponsing Ast variable at the end
|
||||
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
||||
m_ctx.m_deleteps.push_back(varp->nodep());
|
||||
}
|
||||
// Remove the unused vertex
|
||||
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
|
||||
if (isUnused(vtx)) {
|
||||
++m_ctx.m_unusedRemoved;
|
||||
removeVertex(vtx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Consider eliminating variables
|
||||
DfgVertexVar* const varp = vtx.cast<DfgVertexVar>();
|
||||
if (!varp) return;
|
||||
|
||||
// If it has no driver (in this Dfg), there is nothing further we can optimize
|
||||
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
|
||||
// we need to observe the write in this graph if the variable has sinks
|
||||
if (varp->hasModWrRefs()) return;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
|
||||
// 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() {
|
||||
// Insert a temporary variable for all vertices that have multiple non-variable sinks
|
||||
|
||||
// Scope cache for below
|
||||
const bool scoped = !m_dfg.modulep();
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
|
||||
// Need to create an intermediate variable
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert intermediate variables for vertices with multiple sinks (or use an existing one)
|
||||
DfgRegularize(DfgGraph& dfg, V3DfgRegularizeContext& ctx)
|
||||
: m_dfg{dfg}
|
||||
, m_ctx{ctx} {
|
||||
|
||||
// Scope cache for below
|
||||
const bool scoped = !dfg.modulep();
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
uninlineVariables();
|
||||
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "regularize-uninlined");
|
||||
|
||||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
const bool needsIntermediateVariable = [&]() {
|
||||
// Splice vertices represent partial assignments. The must flow
|
||||
// into variables, so they should never need a temporary.
|
||||
if (vtx.is<DfgVertexSplice>()) return false;
|
||||
// Smilarly to splice, UnitArray should never need one eitehr
|
||||
if (vtx.is<DfgUnitArray>()) return false;
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtx.hasMultipleSinks()) return false;
|
||||
// Array selects need no variables, they are just memory references
|
||||
if (vtx.is<DfgArraySel>()) return false;
|
||||
// Otherwise needs an intermediate variable
|
||||
return true;
|
||||
}();
|
||||
eliminateVars();
|
||||
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "regularize-eliminate");
|
||||
|
||||
if (!needsIntermediateVariable) continue;
|
||||
|
||||
// This is an op that requires a result variable. Ensure it is
|
||||
// assigned to one, and redirect other sinks read that variable.
|
||||
if (DfgVertexVar* const varp = vtx.getResultVar()) {
|
||||
varp->srcp(nullptr);
|
||||
vtx.replaceWith(varp);
|
||||
varp->srcp(&vtx);
|
||||
} else {
|
||||
// Need to create an intermediate variable
|
||||
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 and add back driver
|
||||
vtx.replaceWith(newp);
|
||||
newp->srcp(&vtx);
|
||||
}
|
||||
}
|
||||
insertTemporaries();
|
||||
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "regularize-inserttmp");
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -900,22 +900,6 @@ class AstToDfgSynthesize final {
|
|||
return splicep;
|
||||
}
|
||||
|
||||
// If any written variables are forced or otherwise udpated from outside,
|
||||
// we generally cannot synthesize the construct, as we will likely need to
|
||||
// introduce intermediate values that would not be updated.
|
||||
static bool hasExternallyWrittenVariable(DfgLogic& vtx) {
|
||||
return vtx.foreachSink([&](const DfgVertex& sink) {
|
||||
// 'sink' is a splice (for which 'vtxp' is an unresolved driver),
|
||||
// which drives the target variable.
|
||||
const DfgVertexVar* varp = sink.singleSink()->as<DfgVertexVar>();
|
||||
if (varp->hasXRefs()) return true; // Target of a hierarchical reference
|
||||
const AstVar* const astVarp = varp->varp();
|
||||
if (astVarp->isForced()) return true; // Forced
|
||||
if (astVarp->isSigPublic()) return true; // Public
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Initialzie input symbol table of entry CfgBlock
|
||||
void initializeEntrySymbolTable(SymTab& iSymTab) {
|
||||
m_logicp->foreachSource([&](DfgVertex& src) {
|
||||
|
|
@ -1447,6 +1431,22 @@ class AstToDfgSynthesize final {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns true if all external updates to volatile variables are observed correctly
|
||||
bool checkExtWrites() {
|
||||
for (const DfgVertex* const vtxp : m_logicp->synth()) {
|
||||
const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>();
|
||||
if (!varp) continue;
|
||||
// If the variable we synthesized this vertex for is volatile, and
|
||||
// the value of the synthesized temporary is observed, we might be
|
||||
// missing an external update, so we mut give up.
|
||||
if (!varp->hasSinks()) continue;
|
||||
if (!DfgVertexVar::isVolatile(varp->tmpForp())) continue;
|
||||
++m_ctx.m_synt.nonSynExtWrite;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add the synthesized values as drivers to the output variables of the current DfgLogic
|
||||
bool addSynthesizedOutput(SymTab& oSymTab) {
|
||||
// It's possible we think a variable is written by the DfgLogic when
|
||||
|
|
@ -1467,13 +1467,10 @@ class AstToDfgSynthesize final {
|
|||
}
|
||||
|
||||
// Add sinks to read the computed values for the target variables
|
||||
bool hasUsedArrayOutput = false;
|
||||
m_logicp->foreachSink([&](DfgVertex& sink) {
|
||||
return !m_logicp->foreachSink([&](DfgVertex& sink) {
|
||||
DfgUnresolved* const unresolvedp = sink.as<DfgUnresolved>();
|
||||
AstNode* const tgtp = unresolvedp->singleSink()->as<DfgVertexVar>()->nodep();
|
||||
// cppcheck-suppress constVariablePointer
|
||||
Variable* const varp = reinterpret_cast<Variable*>(tgtp);
|
||||
DfgVertexVar* const resp = oSymTab.at(varp);
|
||||
const DfgVertexVar* const varp = unresolvedp->singleSink()->as<DfgVertexVar>();
|
||||
DfgVertexVar* const resp = oSymTab.at(reinterpret_cast<Variable*>(varp->nodep()));
|
||||
UASSERT_OBJ(resp->srcp(), resp, "Undriven result");
|
||||
|
||||
// If the output is not used further in the synthesized logic itself,
|
||||
|
|
@ -1481,15 +1478,14 @@ class AstToDfgSynthesize final {
|
|||
// its splice directly without ending up with a multi-use operation.
|
||||
if (!resp->hasSinks()) {
|
||||
unresolvedp->addDriver(resp->srcp()->as<DfgVertexSplice>());
|
||||
return false;
|
||||
return false; // OK, continue.
|
||||
}
|
||||
|
||||
// TODO: computePropagatedDrivers cannot handle arrays, should
|
||||
// never happen with AssignW
|
||||
if (!resp->isPacked()) {
|
||||
if (!hasUsedArrayOutput) ++m_ctx.m_synt.nonSynArray;
|
||||
hasUsedArrayOutput = true;
|
||||
return false;
|
||||
++m_ctx.m_synt.nonSynArray;
|
||||
return true; // Not OK, give up
|
||||
}
|
||||
|
||||
// We need to add a new splice to avoid multi-use of the original splice
|
||||
|
|
@ -1499,9 +1495,8 @@ class AstToDfgSynthesize final {
|
|||
const std::vector<Driver> drivers = computePropagatedDrivers({}, resp);
|
||||
for (const Driver& d : drivers) splicep->addDriver(d.m_vtxp, d.m_lo, d.m_flp);
|
||||
unresolvedp->addDriver(splicep);
|
||||
return false;
|
||||
return false; // OK, continue
|
||||
});
|
||||
return !hasUsedArrayOutput;
|
||||
}
|
||||
|
||||
// Synthesize the given AstAssignW. Returns true on success.
|
||||
|
|
@ -1528,6 +1523,9 @@ class AstToDfgSynthesize final {
|
|||
VL_DO_DANGLING(assignp->deleteTree(), assignp);
|
||||
if (!success) return false;
|
||||
|
||||
// Check exernal writes are observed correctly
|
||||
if (!checkExtWrites()) return false;
|
||||
|
||||
// Add resolved output variable drivers
|
||||
return addSynthesizedOutput(oSymTab);
|
||||
}
|
||||
|
|
@ -1536,11 +1534,6 @@ class AstToDfgSynthesize final {
|
|||
bool synthesizeCfg(CfgGraph& cfg) {
|
||||
++m_ctx.m_synt.inputAlways;
|
||||
|
||||
if (hasExternallyWrittenVariable(*m_logicp)) {
|
||||
++m_ctx.m_synt.nonSynExtWrite;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is a backward edge (loop), we can't synthesize it
|
||||
if (cfg.containsLoop()) {
|
||||
++m_ctx.m_synt.nonSynLoop;
|
||||
|
|
@ -1592,6 +1585,9 @@ class AstToDfgSynthesize final {
|
|||
assignPathPredicates(bb);
|
||||
}
|
||||
|
||||
// Check exernal writes are observed correctly
|
||||
if (!checkExtWrites()) return false;
|
||||
|
||||
// Add resolved output variable drivers
|
||||
return addSynthesizedOutput(m_bbToOSymTab[cfg.exit()]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,41 +117,36 @@ public:
|
|||
FileLine* driverFileLine() const { return m_driverFileLine; }
|
||||
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
|
||||
|
||||
bool isDrivenFullyByDfg() const {
|
||||
return srcp() && !srcp()->is<DfgVertexSplice>() && !varp()->isForced()
|
||||
&& !varp()->isSigUserRWPublic();
|
||||
}
|
||||
|
||||
// Variable referenced via an AstVarXRef (hierarchical reference)
|
||||
bool hasXRefs() const { return nodep()->user1() & 0x03; }
|
||||
static void setHasRdXRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x01); }
|
||||
static void setHasWrXRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x02); }
|
||||
|
||||
// Variable referenced from Ast code in the same module/netlist
|
||||
bool hasModRdRefs() const { return nodep()->user1() & 0x04; }
|
||||
bool hasModWrRefs() const { return nodep()->user1() & 0x08; }
|
||||
bool hasModRefs() const { return nodep()->user1() & 0x0c; }
|
||||
static void setHasModRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x04); }
|
||||
static void setHasModWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x08); }
|
||||
void setHasModRdRefs() const { setHasModRdRefs(nodep()); }
|
||||
void setHasModWrRefs() const { setHasModWrRefs(nodep()); }
|
||||
|
||||
// Variable referenced from other DFG in the same module/netlist
|
||||
bool hasDfgRefs() const { return nodep()->user1() >> 5; } // I.e.: (nodep()->user1() >> 4) > 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.
|
||||
bool hasExtRefs() const {
|
||||
// In scoped mode, we can ignrore some of these as they were made explicit by then
|
||||
if (!m_varScopep) {
|
||||
if (m_varp->isIO()) return true; // Ports
|
||||
if (m_varp->isTrace()) return true; // Traced
|
||||
if (m_varp->isForced()) return true; // Forced
|
||||
if (hasXRefs()) return true; // Target of a hierarchical reference
|
||||
}
|
||||
if (m_varp->isPrimaryIO()) return true; // Top level ports
|
||||
if (m_varp->isSigPublic()) return true; // Public
|
||||
return false;
|
||||
static bool hasExtRdRefs(const AstNode* nodep) { return nodep->user1() & 0x01; }
|
||||
static bool hasExtWrRefs(const AstNode* nodep) { return nodep->user1() & 0x02; }
|
||||
static void setHasExtRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x01); }
|
||||
static void setHasExtWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x02); }
|
||||
bool hasExtRdRefs() const { return hasExtRdRefs(nodep()); }
|
||||
bool hasExtWrRefs() const { return hasExtWrRefs(nodep()); }
|
||||
bool hasExtRefs() const { return hasExtRdRefs() || hasExtWrRefs(); }
|
||||
|
||||
// The value of this vertex might differ from what is defined by its drivers
|
||||
// 'srcp' and 'defaultp'. That is, it might be assigned, possibly partially,
|
||||
// or abruptly outside the graph, hence it is not equivalent to its 'srcp'.
|
||||
static bool isVolatile(const AstNode* nodep) {
|
||||
return hasModWrRefs(nodep) || hasExtWrRefs(nodep);
|
||||
}
|
||||
bool isVolatile() const { return isVolatile(nodep()); }
|
||||
};
|
||||
|
||||
class DfgVarArray final : public DfgVertexVar {
|
||||
|
|
@ -465,8 +460,7 @@ class DfgLogic final : public DfgVertexVariadic {
|
|||
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;
|
||||
std::vector<DfgVertex*> m_synth; // Vertices this logic was synthesized into
|
||||
bool m_selectedForSynthesis = false; // Logic selected for synthesis
|
||||
bool m_nonSynthesizable = false; // Logic is not synthesizeable (by DfgSynthesis)
|
||||
bool m_reverted = false; // Logic was synthesized (in part if non synthesizable) then reverted
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ test.inline_checks()
|
|||
test.file_grep_not(test.obj_dir + "/coverage.dat", "largeish")
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 14)
|
||||
test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 13)
|
||||
|
||||
test.run(cmd=[
|
||||
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
|
||||
|
|
|
|||
|
|
@ -4,34 +4,23 @@
|
|||
// any use, without warranty, 2024 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
|
||||
module t (
|
||||
input wire [3:0] a,
|
||||
input wire [3:0] b,
|
||||
input wire [3:0] c,
|
||||
output wire [3:0] x,
|
||||
output wire [3:0] y,
|
||||
output wire [3:0] z,
|
||||
output wire [ 0:0] w1,
|
||||
output wire [ 7:0] w8,
|
||||
output wire [15:0] w16,
|
||||
output wire [31:0] w32,
|
||||
output wire [63:0] w64a,
|
||||
output wire [63:0] w64b,
|
||||
output wire [62:0] w63,
|
||||
output wire [95:0] w96
|
||||
input wire [3:0] a,
|
||||
input wire [3:0] b,
|
||||
input wire [3:0] c,
|
||||
output wire [10:0] o
|
||||
);
|
||||
wire [ 3:0] x = ~a & ~b;
|
||||
wire [ 3:0] y = ~b & ~c;
|
||||
wire [ 3:0] z = ~c & ~a;
|
||||
wire [ 0:0] w1 = x[0];
|
||||
wire [ 7:0] w8 = {8{x[1]}};
|
||||
wire [15:0] w16 = {2{w8}};
|
||||
wire [31:0] w32 = {2{w16}};
|
||||
wire [63:0] w64a = {2{w32}};
|
||||
wire [63:0] w64b = {2{~w32}};
|
||||
wire [62:0] w63 = 63'({2{~w32}});
|
||||
wire [95:0] w96 = 96'(w64a);
|
||||
|
||||
assign x = ~a & ~b;
|
||||
assign y = ~b & ~c;
|
||||
assign z = ~c & ~a;
|
||||
assign w1 = x[0];
|
||||
assign w8 = {8{x[1]}};
|
||||
assign w16 = {2{w8}};
|
||||
assign w32 = {2{w16}};
|
||||
assign w64a = {2{w32}};
|
||||
assign w64b = {2{~w32}};
|
||||
assign w63 = 63'({2{~w32}});
|
||||
assign w96 = 96'(w64a);
|
||||
|
||||
assign o = {^x, ^y, ^z, ^w1, ^w8, ^w16, ^w32, ^w64a, ^w64b, ^w63, ^w96};
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -1,50 +1,96 @@
|
|||
DFG 'post inline' patterns with depth 1
|
||||
9 (CONCAT _A:1 _B:a):b
|
||||
8 (REDXOR _A:a):1
|
||||
3 (AND _A:a _B:a)*:a
|
||||
3 (NOT vA:a)*:a
|
||||
2 (AND _A:a _B:a):a
|
||||
2 (REPLICATE _A:a cA:a)*:b
|
||||
1 (AND _A:a _B:a)*:a
|
||||
1 (CONCAT '0:a _A:b):A
|
||||
1 (CONCAT _A:1 _B:1):a
|
||||
1 (NOT _A:a):a
|
||||
1 (REDXOR _A:a)*:1
|
||||
1 (REPLICATE _A:1 cA:a)*:b
|
||||
1 (REPLICATE _A:a cA:b)*:b
|
||||
1 (REPLICATE _A:a cA:b)*:c
|
||||
1 (SEL@0 _A:a):1
|
||||
1 (SEL@0 _A:a):b
|
||||
1 (SEL@0 _A:a)*:1
|
||||
1 (SEL@0 _A:a)*:b
|
||||
1 (SEL@A _A:a):1
|
||||
|
||||
DFG 'post inline' patterns with depth 2
|
||||
2 (AND (NOT vA:a)*:a (NOT vB:a)*:a):a
|
||||
1 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
|
||||
6 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:b):c):d
|
||||
3 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
|
||||
3 (REDXOR (AND _A:a _B:a)*:a):1
|
||||
1 (CONCAT '0:a (REPLICATE _A:a cA:a)*:b):A
|
||||
1 (CONCAT (REDXOR _A:a)*:1 (CONCAT _B:1 _C:b):c):d
|
||||
1 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:1):b):c
|
||||
1 (CONCAT (REDXOR _A:a):1 (REDXOR _B:b)*:1):c
|
||||
1 (CONCAT (SEL@0 _A:a)*:1 (CONCAT _B:1 _C:b):c):d
|
||||
1 (NOT (REPLICATE _A:a cA:b)*:b):b
|
||||
1 (REDXOR (REPLICATE _A:1 cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:a)*:b)*:1
|
||||
1 (REDXOR (REPLICATE _A:a cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:b)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 _A:a)*:b):1
|
||||
1 (REPLICATE (NOT _A:a):a cA:a)*:b
|
||||
1 (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c
|
||||
1 (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c
|
||||
1 (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b
|
||||
1 (REPLICATE (SEL@A _A:a):1 cA:b)*:c
|
||||
1 (SEL@0 (AND _A:a _B:a)*:a):1
|
||||
1 (SEL@0 (REPLICATE _A:a cA:a)*:b):c
|
||||
1 (SEL@0 (AND _A:a _B:a)*:a)*:1
|
||||
1 (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c
|
||||
1 (SEL@A (AND _A:a _B:a)*:a):1
|
||||
|
||||
DFG 'post inline' patterns with depth 3
|
||||
3 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
2 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE _A:b cA:a)*:a cA:a)*:c):A
|
||||
1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (SEL@0 _C:a)*:1 (CONCAT _D:1 _E:b):c):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:1 cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b)*:1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:1):c):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (REDXOR _C:b)*:1):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:b):1 (CONCAT (REDXOR _B:c)*:1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:c):1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (SEL@0 _A:a)*:b):1 (REDXOR (REPLICATE _B:c cA:c)*:a)*:1):d
|
||||
1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a)*:1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b
|
||||
1 (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b
|
||||
1 (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c)*:1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1
|
||||
1 (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c):1
|
||||
1 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c
|
||||
1 (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a
|
||||
1 (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b)*:d
|
||||
1 (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d
|
||||
1 (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c
|
||||
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c
|
||||
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a)*:1
|
||||
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b)*:c
|
||||
1 (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
|
||||
DFG 'post inline' patterns with depth 4
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE (REPLICATE _A:b cA:a)*:c cA:a)*:a cA:a)*:d):A
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (SEL@0 _C:a)*:1 (CONCAT _D:1 _E:b):c):d):e):f
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a)*:1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b):f
|
||||
1 (CONCAT (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1 (CONCAT (REDXOR (SEL@0 _B:b)*:c):1 (REDXOR (REPLICATE _A:a cA:a)*:b)*:1):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:a)*:a):1 (CONCAT (REDXOR _C:d)*:1 (CONCAT _D:1 _E:e):f):g):h):i
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c)*:1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:c):1 (CONCAT (REDXOR _C:d):1 (REDXOR _D:c)*:1):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:d)*:1 (CONCAT (REDXOR _C:d):1 (CONCAT _D:1 _E:1):e):f):g):h
|
||||
1 (CONCAT (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:b)*:d):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:e):a):f):g):h
|
||||
1 (CONCAT (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c):1 (REDXOR (REPLICATE (REPLICATE _B:d cA:a)*:a cA:a)*:b)*:1):e
|
||||
1 (CONCAT (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a)*:1 (CONCAT (REDXOR (REPLICATE _A:1 cA:b)*:c):1 (CONCAT (REDXOR _B:d):1 (CONCAT _C:1 _D:a):e):f):g):c
|
||||
1 (NOT (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):a
|
||||
1 (REDXOR (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b)*:d)*:1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d):1
|
||||
1 (REDXOR (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b)*:c):1
|
||||
1 (REPLICATE (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b cA:b)*:d
|
||||
1 (REPLICATE (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a cB:a)*:d
|
||||
1 (REPLICATE (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d cB:b)*:b
|
||||
1 (REPLICATE (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c cB:b)*:d
|
||||
1 (REPLICATE (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 cA:b)*:c
|
||||
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):d
|
||||
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c)*:d
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +1,96 @@
|
|||
DFG 'pre inline' patterns with depth 1
|
||||
9 (CONCAT _A:1 _B:a):b
|
||||
8 (REDXOR _A:a):1
|
||||
3 (AND _A:a _B:a)*:a
|
||||
3 (NOT vA:a)*:a
|
||||
2 (AND _A:a _B:a):a
|
||||
2 (REPLICATE _A:a cA:a)*:b
|
||||
1 (AND _A:a _B:a)*:a
|
||||
1 (CONCAT '0:a _A:b):A
|
||||
1 (CONCAT _A:1 _B:1):a
|
||||
1 (NOT _A:a):a
|
||||
1 (REDXOR _A:a)*:1
|
||||
1 (REPLICATE _A:1 cA:a)*:b
|
||||
1 (REPLICATE _A:a cA:b)*:b
|
||||
1 (REPLICATE _A:a cA:b)*:c
|
||||
1 (SEL@0 _A:a):1
|
||||
1 (SEL@0 _A:a):b
|
||||
1 (SEL@0 _A:a)*:1
|
||||
1 (SEL@0 _A:a)*:b
|
||||
1 (SEL@A _A:a):1
|
||||
|
||||
DFG 'pre inline' patterns with depth 2
|
||||
2 (AND (NOT vA:a)*:a (NOT vB:a)*:a):a
|
||||
1 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
|
||||
6 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:b):c):d
|
||||
3 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
|
||||
3 (REDXOR (AND _A:a _B:a)*:a):1
|
||||
1 (CONCAT '0:a (REPLICATE _A:a cA:a)*:b):A
|
||||
1 (CONCAT (REDXOR _A:a)*:1 (CONCAT _B:1 _C:b):c):d
|
||||
1 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:1):b):c
|
||||
1 (CONCAT (REDXOR _A:a):1 (REDXOR _B:b)*:1):c
|
||||
1 (CONCAT (SEL@0 _A:a)*:1 (CONCAT _B:1 _C:b):c):d
|
||||
1 (NOT (REPLICATE _A:a cA:b)*:b):b
|
||||
1 (REDXOR (REPLICATE _A:1 cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:a)*:b)*:1
|
||||
1 (REDXOR (REPLICATE _A:a cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:b)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 _A:a)*:b):1
|
||||
1 (REPLICATE (NOT _A:a):a cA:a)*:b
|
||||
1 (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c
|
||||
1 (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c
|
||||
1 (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b
|
||||
1 (REPLICATE (SEL@A _A:a):1 cA:b)*:c
|
||||
1 (SEL@0 (AND _A:a _B:a)*:a):1
|
||||
1 (SEL@0 (REPLICATE _A:a cA:a)*:b):c
|
||||
1 (SEL@0 (AND _A:a _B:a)*:a)*:1
|
||||
1 (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c
|
||||
1 (SEL@A (AND _A:a _B:a)*:a):1
|
||||
|
||||
DFG 'pre inline' patterns with depth 3
|
||||
3 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
2 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE _A:b cA:a)*:a cA:a)*:c):A
|
||||
1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (SEL@0 _C:a)*:1 (CONCAT _D:1 _E:b):c):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:1 cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b)*:1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:1):c):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (REDXOR _C:b)*:1):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:b):1 (CONCAT (REDXOR _B:c)*:1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:c):1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (SEL@0 _A:a)*:b):1 (REDXOR (REPLICATE _B:c cA:c)*:a)*:1):d
|
||||
1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a)*:1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b
|
||||
1 (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b
|
||||
1 (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c)*:1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1
|
||||
1 (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c):1
|
||||
1 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c
|
||||
1 (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a
|
||||
1 (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b)*:d
|
||||
1 (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d
|
||||
1 (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c
|
||||
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c
|
||||
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a)*:1
|
||||
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b)*:c
|
||||
1 (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
|
||||
DFG 'pre inline' patterns with depth 4
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE (REPLICATE _A:b cA:a)*:c cA:a)*:a cA:a)*:d):A
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (SEL@0 _C:a)*:1 (CONCAT _D:1 _E:b):c):d):e):f
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a)*:1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b):f
|
||||
1 (CONCAT (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1 (CONCAT (REDXOR (SEL@0 _B:b)*:c):1 (REDXOR (REPLICATE _A:a cA:a)*:b)*:1):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:a)*:a):1 (CONCAT (REDXOR _C:d)*:1 (CONCAT _D:1 _E:e):f):g):h):i
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c)*:1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:c):1 (CONCAT (REDXOR _C:d):1 (REDXOR _D:c)*:1):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:d)*:1 (CONCAT (REDXOR _C:d):1 (CONCAT _D:1 _E:1):e):f):g):h
|
||||
1 (CONCAT (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:b)*:d):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:e):a):f):g):h
|
||||
1 (CONCAT (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c):1 (REDXOR (REPLICATE (REPLICATE _B:d cA:a)*:a cA:a)*:b)*:1):e
|
||||
1 (CONCAT (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a)*:1 (CONCAT (REDXOR (REPLICATE _A:1 cA:b)*:c):1 (CONCAT (REDXOR _B:d):1 (CONCAT _C:1 _D:a):e):f):g):c
|
||||
1 (NOT (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):a
|
||||
1 (REDXOR (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b)*:d)*:1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d):1
|
||||
1 (REDXOR (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b)*:c):1
|
||||
1 (REPLICATE (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b cA:b)*:d
|
||||
1 (REPLICATE (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a cB:a)*:d
|
||||
1 (REPLICATE (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d cB:b)*:b
|
||||
1 (REPLICATE (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c cB:b)*:d
|
||||
1 (REPLICATE (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 cA:b)*:c
|
||||
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):d
|
||||
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c)*:d
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +1,96 @@
|
|||
DFG 'scoped' patterns with depth 1
|
||||
9 (CONCAT _A:1 _B:a):b
|
||||
8 (REDXOR _A:a):1
|
||||
3 (AND _A:a _B:a)*:a
|
||||
3 (NOT vA:a)*:a
|
||||
2 (AND _A:a _B:a):a
|
||||
2 (REPLICATE _A:a cA:a)*:b
|
||||
1 (AND _A:a _B:a)*:a
|
||||
1 (CONCAT '0:a _A:b):A
|
||||
1 (CONCAT _A:1 _B:1):a
|
||||
1 (NOT _A:a):a
|
||||
1 (REDXOR _A:a)*:1
|
||||
1 (REPLICATE _A:1 cA:a)*:b
|
||||
1 (REPLICATE _A:a cA:b)*:b
|
||||
1 (REPLICATE _A:a cA:b)*:c
|
||||
1 (SEL@0 _A:a):1
|
||||
1 (SEL@0 _A:a):b
|
||||
1 (SEL@0 _A:a)*:1
|
||||
1 (SEL@0 _A:a)*:b
|
||||
1 (SEL@A _A:a):1
|
||||
|
||||
DFG 'scoped' patterns with depth 2
|
||||
2 (AND (NOT vA:a)*:a (NOT vB:a)*:a):a
|
||||
1 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
|
||||
6 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:b):c):d
|
||||
3 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
|
||||
3 (REDXOR (AND _A:a _B:a)*:a):1
|
||||
1 (CONCAT '0:a (REPLICATE _A:a cA:a)*:b):A
|
||||
1 (CONCAT (REDXOR _A:a)*:1 (CONCAT _B:1 _C:b):c):d
|
||||
1 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:1):b):c
|
||||
1 (CONCAT (REDXOR _A:a):1 (REDXOR _B:b)*:1):c
|
||||
1 (CONCAT (SEL@0 _A:a)*:1 (CONCAT _B:1 _C:b):c):d
|
||||
1 (NOT (REPLICATE _A:a cA:b)*:b):b
|
||||
1 (REDXOR (REPLICATE _A:1 cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:a)*:b)*:1
|
||||
1 (REDXOR (REPLICATE _A:a cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:b)*:b):1
|
||||
1 (REDXOR (REPLICATE _A:a cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 _A:a)*:b):1
|
||||
1 (REPLICATE (NOT _A:a):a cA:a)*:b
|
||||
1 (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c
|
||||
1 (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c
|
||||
1 (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b
|
||||
1 (REPLICATE (SEL@A _A:a):1 cA:b)*:c
|
||||
1 (SEL@0 (AND _A:a _B:a)*:a):1
|
||||
1 (SEL@0 (REPLICATE _A:a cA:a)*:b):c
|
||||
1 (SEL@0 (AND _A:a _B:a)*:a)*:1
|
||||
1 (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c
|
||||
1 (SEL@A (AND _A:a _B:a)*:a):1
|
||||
|
||||
DFG 'scoped' patterns with depth 3
|
||||
3 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
2 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE _A:b cA:a)*:a cA:a)*:c):A
|
||||
1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (SEL@0 _C:a)*:1 (CONCAT _D:1 _E:b):c):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:1 cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b)*:1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:1):c):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (REDXOR _C:b)*:1):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:b):1 (CONCAT (REDXOR _B:c)*:1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:c):1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:d):e):f):g
|
||||
1 (CONCAT (REDXOR (SEL@0 _A:a)*:b):1 (REDXOR (REPLICATE _B:c cA:c)*:a)*:1):d
|
||||
1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a)*:1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b
|
||||
1 (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b
|
||||
1 (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c)*:1
|
||||
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1
|
||||
1 (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c):1
|
||||
1 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c
|
||||
1 (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a
|
||||
1 (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b)*:d
|
||||
1 (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d
|
||||
1 (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c
|
||||
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c
|
||||
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a)*:1
|
||||
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b)*:c
|
||||
1 (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
|
||||
|
||||
DFG 'scoped' patterns with depth 4
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE (REPLICATE _A:b cA:a)*:c cA:a)*:a cA:a)*:d):A
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (SEL@0 _C:a)*:1 (CONCAT _D:1 _E:b):c):d):e):f
|
||||
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a)*:1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b):f
|
||||
1 (CONCAT (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1 (CONCAT (REDXOR (SEL@0 _B:b)*:c):1 (REDXOR (REPLICATE _A:a cA:a)*:b)*:1):d):e
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:a)*:a):1 (CONCAT (REDXOR _C:d)*:1 (CONCAT _D:1 _E:e):f):g):h):i
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b)*:c)*:1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:c):1 (CONCAT (REDXOR _C:d):1 (REDXOR _D:c)*:1):e):f):g
|
||||
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:d)*:1 (CONCAT (REDXOR _C:d):1 (CONCAT _D:1 _E:1):e):f):g):h
|
||||
1 (CONCAT (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:b)*:d):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:e):a):f):g):h
|
||||
1 (CONCAT (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b)*:c):1 (REDXOR (REPLICATE (REPLICATE _B:d cA:a)*:a cA:a)*:b)*:1):e
|
||||
1 (CONCAT (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a)*:1 (CONCAT (REDXOR (REPLICATE _A:1 cA:b)*:c):1 (CONCAT (REDXOR _B:d):1 (CONCAT _C:1 _D:a):e):f):g):c
|
||||
1 (NOT (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):a
|
||||
1 (REDXOR (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b)*:d)*:1
|
||||
1 (REDXOR (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d):1
|
||||
1 (REDXOR (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c):1
|
||||
1 (REDXOR (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b)*:c):1
|
||||
1 (REPLICATE (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b cA:b)*:d
|
||||
1 (REPLICATE (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a cB:a)*:d
|
||||
1 (REPLICATE (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d cB:b)*:b
|
||||
1 (REPLICATE (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c cB:b)*:d
|
||||
1 (REPLICATE (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 cA:b)*:c
|
||||
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):d
|
||||
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c)*:d
|
||||
|
||||
|
|
|
|||
|
|
@ -48,4 +48,36 @@
|
|||
t/t_lint_always_comb_multidriven_bad.v:40:16: ... Location of other write
|
||||
40 | always_comb out6 = d;
|
||||
| ^~~~
|
||||
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:17:15: Bit [0] of signal 'out2' have multiple combinational drivers. This can cause performance degradation.
|
||||
: ... note: In instance 't'
|
||||
t/t_lint_always_comb_multidriven_bad.v:28:16: ... Location of offending driver
|
||||
28 | assign out2 = d;
|
||||
| ^
|
||||
t/t_lint_always_comb_multidriven_bad.v:29:21: ... Location of offending driver
|
||||
29 | always_comb out2 = 1'b0;
|
||||
| ^
|
||||
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:19:15: Bit [0] of signal 'out4' have multiple combinational drivers. This can cause performance degradation.
|
||||
: ... note: In instance 't'
|
||||
t/t_lint_always_comb_multidriven_bad.v:34:21: ... Location of offending driver
|
||||
34 | always_comb out4 = 1'b0;
|
||||
| ^
|
||||
t/t_lint_always_comb_multidriven_bad.v:35:16: ... Location of offending driver
|
||||
35 | assign out4 = d;
|
||||
| ^
|
||||
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:20:15: Bit [0] of signal 'out5' have multiple combinational drivers. This can cause performance degradation.
|
||||
: ... note: In instance 't'
|
||||
t/t_lint_always_comb_multidriven_bad.v:37:21: ... Location of offending driver
|
||||
37 | always_comb out5 = 1'b0;
|
||||
| ^
|
||||
t/t_lint_always_comb_multidriven_bad.v:38:21: ... Location of offending driver
|
||||
38 | always_comb out5 = d;
|
||||
| ^
|
||||
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:21:15: Bit [0] of signal 'out6' have multiple combinational drivers. This can cause performance degradation.
|
||||
: ... note: In instance 't'
|
||||
t/t_lint_always_comb_multidriven_bad.v:40:21: ... Location of offending driver
|
||||
40 | always_comb out6 = d;
|
||||
| ^
|
||||
t/t_lint_always_comb_multidriven_bad.v:41:21: ... Location of offending driver
|
||||
41 | always_comb out6 = 1'b0;
|
||||
| ^
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
Loading…
Reference in New Issue