diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 3bd212e91..091b9ead5 100644 --- a/src/V3Dfg.h +++ b/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()) return true; + // Small constant select from variable + if (const DfgSel* const selp = cast()) { + if (!selp->fromp()->is()) 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()) { + if (catp->width() > VL_QUADSIZE) return false; + const DfgConst* const lCatp = catp->lhsp()->cast(); + if (!lCatp) return false; + if (!lCatp->isZero()) return false; + return catp->rhsp()->isCheaperThanLoad(); + } + // Otherwise probably not + return false; +} + +// }}} + // DfgGraph {{{ template diff --git a/src/V3DfgContext.h b/src/V3DfgContext.h index b67516c74..ea9c1e300 100644 --- a/src/V3DfgContext.h +++ b/src/V3DfgContext.h @@ -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 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 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}; diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index bd76071ec..379a08d72 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -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() - || vtxp->is() || vtxp->is(), + UASSERT_OBJ(vtxp->is() || vtxp->is() // + || !vtxp->hasMultipleSinks() || vtxp->isCheaperThanLoad(), // vtxp, "Intermediate DFG value with multiple uses"); iterate(vtxp); UASSERT_OBJ(m_resultp, vtxp, "Missing result"); diff --git a/src/V3DfgOptimizer.cpp b/src/V3DfgOptimizer.cpp index c6e1f9173..7dfb6a543 100644 --- a/src/V3DfgOptimizer.cpp +++ b/src/V3DfgOptimizer.cpp @@ -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& 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& 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; diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index bb755d235..f544ef1ba 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -27,11 +27,18 @@ VL_DEFINE_DEBUG_FUNCTIONS; void V3DfgPasses::inlineVars(DfgGraph& dfg) { for (DfgVertexVar& vtx : dfg.varVertices()) { - if (DfgVarPacked* const varp = vtx.cast()) { - 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()) continue; + if (srcp->is()) 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 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() && !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(); - 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()) { - // 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 pass) { + const auto run = [&](const std::string& name, bool dump, std::function 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); }); } diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index df672301d..e45404ba9 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -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; diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 40f301116..17a23e1ae 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1714,5 +1714,6 @@ public: }; void V3DfgPasses::peephole(DfgGraph& dfg, V3DfgPeepholeContext& ctx) { + if (!v3Global.opt.fDfgPeephole()) return; V3DfgPeephole::apply(dfg, ctx); } diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index acf470efd..85093ebcf 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -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()) continue; + if (vtx.is()) 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 gatherCyclicVariables() { + DfgUserMap vtx2Scc = m_dfg.makeUserMap(); + V3DfgPasses::colorStronglyConnectedComponents(m_dfg, vtx2Scc); + std::unordered_set 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()) { + // 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()) { + // 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()) { + // 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 + static void applyReplacement(AstVarRef* refp) { + AstNode* const tgtp = T_Scoped ? static_cast(refp->varScopep()) + : static_cast(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 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() || !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()) { + 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(); + 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()) return; + if (srcp->is()) 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(refp); }); + } else { + v3Global.rootp()->foreach([](AstVarRef* refp) { applyReplacement(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()) continue; + if (vtx.is()) 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()) return false; - // Smilarly to splice, UnitArray should never need one eitehr - if (vtx.is()) 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()) 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: diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp index dc030d581..f7bf3b295 100644 --- a/src/V3DfgSynthesize.cpp +++ b/src/V3DfgSynthesize.cpp @@ -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(); - 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(); + 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(); - AstNode* const tgtp = unresolvedp->singleSink()->as()->nodep(); - // cppcheck-suppress constVariablePointer - Variable* const varp = reinterpret_cast(tgtp); - DfgVertexVar* const resp = oSymTab.at(varp); + const DfgVertexVar* const varp = unresolvedp->singleSink()->as(); + DfgVertexVar* const resp = oSymTab.at(reinterpret_cast(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()); - 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 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()]); } diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 590c45b05..d114282cb 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -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() && !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 m_cfgp; - // Vertices this logic was synthesized into. Excluding variables - std::vector m_synth; + std::vector 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 diff --git a/test_regress/t/t_cover_toggle.py b/test_regress/t/t_cover_toggle.py index f8d39fb82..6f37d0c84 100755 --- a/test_regress/t/t_cover_toggle.py +++ b/test_regress/t/t_cover_toggle.py @@ -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", diff --git a/test_regress/t/t_dfg_stats_patterns.v b/test_regress/t/t_dfg_stats_patterns.v index 3494a4dda..ac78a5da3 100644 --- a/test_regress/t/t_dfg_stats_patterns.v +++ b/test_regress/t/t_dfg_stats_patterns.v @@ -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 diff --git a/test_regress/t/t_dfg_stats_patterns_post_inline.out b/test_regress/t/t_dfg_stats_patterns_post_inline.out index 10b836e67..5984277cd 100644 --- a/test_regress/t/t_dfg_stats_patterns_post_inline.out +++ b/test_regress/t/t_dfg_stats_patterns_post_inline.out @@ -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 diff --git a/test_regress/t/t_dfg_stats_patterns_pre_inline.out b/test_regress/t/t_dfg_stats_patterns_pre_inline.out index 0b817d93c..a34f6037c 100644 --- a/test_regress/t/t_dfg_stats_patterns_pre_inline.out +++ b/test_regress/t/t_dfg_stats_patterns_pre_inline.out @@ -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 diff --git a/test_regress/t/t_dfg_stats_patterns_scoped.out b/test_regress/t/t_dfg_stats_patterns_scoped.out index cf35ac494..bc5d34307 100644 --- a/test_regress/t/t_dfg_stats_patterns_scoped.out +++ b/test_regress/t/t_dfg_stats_patterns_scoped.out @@ -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 diff --git a/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out b/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out index 0a5648357..25b66bffc 100644 --- a/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out +++ b/test_regress/t/t_lint_always_comb_multidriven_compile_public_flat_bad.out @@ -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