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:
Geza Lore 2025-09-10 12:38:49 +01:00 committed by GitHub
parent 6c69210037
commit c6b0918db5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 685 additions and 350 deletions

View File

@ -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>

View File

@ -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};

View File

@ -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");

View File

@ -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;

View File

@ -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); });
}

View File

@ -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;

View File

@ -1714,5 +1714,6 @@ public:
};
void V3DfgPasses::peephole(DfgGraph& dfg, V3DfgPeepholeContext& ctx) {
if (!v3Global.opt.fDfgPeephole()) return;
V3DfgPeephole::apply(dfg, ctx);
}

View File

@ -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:

View File

@ -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()]);
}

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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