// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Persistent context for DFG algorithms // // Code available from: https://verilator.org // //************************************************************************* // // This program is free software; you can redistribute it and/or modify it // under the terms of either the GNU Lesser General Public License Version 3 // or the Perl Artistic License Version 2.0. // SPDX-FileCopyrightText: 2003-2026 Wilson Snyder // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* // // Various context objects hold data that need to persist across invocations // of a DFG algorithms. // //************************************************************************* #ifndef VERILATOR_V3DFGCONTEXT_H_ #define VERILATOR_V3DFGCONTEXT_H_ #include "config_build.h" #include "verilatedos.h" #include "V3Ast.h" #include "V3DfgPeepholePatterns.h" #include "V3Error.h" #include "V3File.h" #include "V3Global.h" #include "V3Stats.h" class V3DfgContext; //###################################################################### // Base class for all context objects class V3DfgSubContext VL_NOT_FINAL { const std::string m_name; // Pass/algorithm name, to be used in statistics protected: VL_DEFINE_DEBUG_FUNCTIONS; V3DfgSubContext(const std::string& name) : m_name{name} {} void addStat(const std::string& what, double value) { V3Stats::addStat("Optimizations, DFG, " + m_name + ", " + what, value); } }; //###################################################################### // Contexts for various algorithms - keep sorted class V3DfgAstToDfgContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE VDouble0 m_inputs; // Number of input processes (logic constructs) VDouble0 m_representable; // Number of combinational equations representable VDouble0 m_nonRepCfg; // Non-representable due to failing to build CFG VDouble0 m_nonRepLive; // Non-representable due to failing liveness analysis VDouble0 m_nonRepVar; // Non-representable due to unsupported variable properties private: V3DfgAstToDfgContext() : V3DfgSubContext{"AstToDfg"} {} ~V3DfgAstToDfgContext() { addStat("input processes", m_inputs); addStat("representable", m_representable); addStat("non-representable (cfg)", m_nonRepCfg); addStat("non-representable (live)", m_nonRepLive); addStat("non-representable (var)", m_nonRepVar); // Check the stats are consistent UASSERT(m_representable + m_nonRepCfg + m_nonRepLive + m_nonRepVar == m_inputs, "Inconsistent statistics"); } }; class V3DfgBinToOneHotContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE VDouble0 m_decodersCreated; // Number of bianry to one-hot decoders created private: V3DfgBinToOneHotContext() : V3DfgSubContext{"BinToOneHot"} {} ~V3DfgBinToOneHotContext() { addStat("decoders created", m_decodersCreated); } }; class V3DfgBreakCyclesContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE VDouble0 m_nFixed; // Number of graphs that became acyclic VDouble0 m_nImproved; // Number of graphs that were imporoved but still cyclic VDouble0 m_nUnchanged; // Number of graphs that were left unchanged VDouble0 m_nTrivial; // Number of graphs that were not changed VDouble0 m_nImprovements; // Number of changes made to graphs private: V3DfgBreakCyclesContext() : V3DfgSubContext{"BreakCycles"} {} ~V3DfgBreakCyclesContext() { addStat("made acyclic", m_nFixed); addStat("improved", m_nImproved); addStat("left unchanged", m_nUnchanged); addStat("trivial", m_nTrivial); addStat("changes applied", m_nImprovements); } }; class V3DfgCseContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE VDouble0 m_eliminated; // Number of common sub-expressions eliminated private: explicit V3DfgCseContext(const std::string& label) : V3DfgSubContext{"CSE " + label} {} ~V3DfgCseContext() { addStat("expressions eliminated", m_eliminated); } }; class V3DfgDfgToAstContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE VDouble0 m_outputVariables; // Number of output variables VDouble0 m_outputVariablesWithDefault; // Number of outptu variables with a default driver VDouble0 m_resultEquations; // Number of result combinational equations VDouble0 m_expressionsInlined; // Number of expressions inlined directly into the Ast VDouble0 m_varRefsSubstituted; // Number of variable references substituted in the Ast private: V3DfgDfgToAstContext() : V3DfgSubContext{"DfgToAst"} {} ~V3DfgDfgToAstContext() { addStat("output variables", m_outputVariables); addStat("output variables with default driver", m_outputVariablesWithDefault); addStat("result equations", m_resultEquations); addStat("expressions inlined", m_expressionsInlined); addStat("var refs substituted", m_varRefsSubstituted); } }; class V3DfgPeepholeContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE // Enable flags for each optimization std::array m_enabled; // Count of applications for each optimization (for statistics) std::array m_count; std::vector m_deleteps; // AstVar/AstVarScope that can be deleted at the end private: V3DfgPeepholeContext() : V3DfgSubContext{"Peephole"} { const auto checkEnabled = [this](VDfgPeepholePattern id) { std::string str{id.ascii()}; std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { // return c == '_' ? '-' : std::tolower(c); }); m_enabled[id] = v3Global.opt.fDfgPeepholeEnabled(str); }; #define OPTIMIZATION_CHECK_ENABLED(id, name) checkEnabled(VDfgPeepholePattern::id); FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(OPTIMIZATION_CHECK_ENABLED) #undef OPTIMIZATION_CHECK_ENABLED } ~V3DfgPeepholeContext() { for (AstNode* const nodep : m_deleteps) { VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } const auto emitStat = [this](VDfgPeepholePattern id) { std::string str{id.ascii()}; std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { // return c == '_' ? ' ' : std::tolower(c); }); addStat(str, m_count[id]); }; #define OPTIMIZATION_EMIT_STATS(id, name) emitStat(VDfgPeepholePattern::id); FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(OPTIMIZATION_EMIT_STATS) #undef OPTIMIZATION_EMIT_STATS } }; class V3DfgPushDownSelsContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE size_t m_pushedDown = 0; // Number of selects pushed down through concatenations size_t m_wouldBeCyclic = 0; // Number of selects not pushed due to cycle private: V3DfgPushDownSelsContext() : V3DfgSubContext{"PushDownSels"} {} ~V3DfgPushDownSelsContext() { addStat("sels pushed down", m_pushedDown); addStat("would be cyclic", m_wouldBeCyclic); } }; class V3DfgRegularizeContext 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_usedVarsInlined; // Number of used variables inlined VDouble0 m_unusedRemoved; // Number of unused vertices remoevd VDouble0 m_temporariesIntroduced; // Number of temporaries introduced for reuse private: V3DfgRegularizeContext() : V3DfgSubContext{"Regularize"} {} ~V3DfgRegularizeContext() { for (AstNode* const nodep : m_deleteps) { VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } addStat("used variables inlined", m_usedVarsInlined); addStat("unused vertices removed", m_unusedRemoved); addStat("temporaries introduced", m_temporariesIntroduced); } }; class V3DfgRemoveUnobservableContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE VDouble0 m_varsRemoved; // Number of variables removed from the Dfg VDouble0 m_varsDeleted; // Number of variables removed from the Dfg and the Ast VDouble0 m_logicRemoved; // Number of logic blocks removed from the Dfg VDouble0 m_logicDeleted; // Number of logic blocks removed from the Dfg and the Ast private: V3DfgRemoveUnobservableContext() : V3DfgSubContext{"RemoveUnobservable"} {} ~V3DfgRemoveUnobservableContext() { addStat("variables removed", m_varsRemoved); addStat("variables deleted", m_varsDeleted); addStat("logic removed", m_logicRemoved); addStat("logic deleted", m_logicDeleted); } }; class V3DfgSynthesisContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE // Stats for conversion struct { // Inputs VDouble0 inputAssignments; // Number of input assignments VDouble0 inputExpressions; // Number of input equations // Successful VDouble0 representable; // Number of representable constructs // Unsuccessful VDouble0 nonRepImpure; // Non representable: impure VDouble0 nonRepDType; // Non representable: unsupported data type VDouble0 nonRepLValue; // Non representable: unsupported LValue form VDouble0 nonRepVarRef; // Non representable: unsupported var reference VDouble0 nonRepOOBSel; // Non representable: out of bounds select VDouble0 nonRepNode; // Non representable: unsupported AstNode type VDouble0 nonRepUnknown; // Non representable: unhandled AstNode type } m_conv; // Stats for synthesis struct { // Inputs VDouble0 inputAlways; // Number of always blocks attempted VDouble0 inputAssign; // Number of continuous assignments attempted // Successful VDouble0 synthAlways; // Number of always blocks successfully synthesized VDouble0 synthAssign; // Number of continuous assignments successfully synthesized // Unsuccessful VDouble0 nonSynConv; // Non-synthesizable: non-representable (above) VDouble0 nonSynExtWrite; // Non-synthesizable: has externally written variable VDouble0 nonSynLoop; // Non-synthesizable: loop in CFG VDouble0 nonSynStmt; // Non-synthesizable: unsupported statement VDouble0 nonSynMultidrive; // Non-synthesizable: multidriven value within statement VDouble0 nonSynArray; // Non-synthesizable: array type unhandled VDouble0 nonSynLatch; // Non-synthesizable: maybe latch VDouble0 nonSynJoinInput; // Non-synthesizable: needing to join input variable VDouble0 nonSynFalseWrite; // Non-synthesizable: does not write output // Reverted VDouble0 revertNonSyn; // Reverted due to being driven from non-synthesizable vertex VDouble0 revertMultidrive; // Reverted due to multiple drivers // Additional stats VDouble0 cfgTrivial; // Trivial input CFGs VDouble0 cfgSp; // Series-paralel input CFGs VDouble0 cfgDag; // Generic loop free input CFGs VDouble0 cfgCyclic; // Cyclic input CFGs VDouble0 joinUsingPathPredicate; // Num control flow joins using full path predicate VDouble0 joinUsingBranchCondition; // Num Control flow joins using dominating branch cond } m_synt; private: V3DfgSynthesisContext() : V3DfgSubContext{"Synthesis"} {} ~V3DfgSynthesisContext() { // Conversion statistics addStat("conv / input assignments", m_conv.inputAssignments); addStat("conv / input expressions", m_conv.inputExpressions); addStat("conv / representable inputs", m_conv.representable); addStat("conv / non-representable (impure)", m_conv.nonRepImpure); addStat("conv / non-representable (dtype)", m_conv.nonRepDType); addStat("conv / non-representable (lhs)", m_conv.nonRepLValue); addStat("conv / non-representable (varref)", m_conv.nonRepVarRef); addStat("conv / non-representable (oobsel)", m_conv.nonRepOOBSel); addStat("conv / non-representable (node)", m_conv.nonRepNode); addStat("conv / non-representable (unknown)", m_conv.nonRepUnknown); VDouble0 nConvNonRep; nConvNonRep += m_conv.nonRepImpure; nConvNonRep += m_conv.nonRepDType; nConvNonRep += m_conv.nonRepLValue; nConvNonRep += m_conv.nonRepVarRef; nConvNonRep += m_conv.nonRepOOBSel; nConvNonRep += m_conv.nonRepNode; nConvNonRep += m_conv.nonRepUnknown; VDouble0 nConvExpect; nConvExpect += m_conv.inputAssignments; nConvExpect += m_conv.inputExpressions; nConvExpect -= m_conv.representable; UASSERT(nConvNonRep == nConvExpect, "Inconsistent statistics / conv"); // Synthesis statistics addStat("synt / always blocks considered", m_synt.inputAlways); addStat("synt / always blocks synthesized", m_synt.synthAlways); addStat("synt / continuous assignments considered", m_synt.inputAssign); addStat("synt / continuous assignments synthesized", m_synt.synthAssign); addStat("synt / non-synthesizable (conv)", m_synt.nonSynConv); addStat("synt / non-synthesizable (ext write)", m_synt.nonSynExtWrite); addStat("synt / non-synthesizable (loop)", m_synt.nonSynLoop); addStat("synt / non-synthesizable (stmt)", m_synt.nonSynStmt); addStat("synt / non-synthesizable (multidrive)", m_synt.nonSynMultidrive); addStat("synt / non-synthesizable (array)", m_synt.nonSynArray); addStat("synt / non-synthesizable (latch)", m_synt.nonSynLatch); addStat("synt / non-synthesizable (join input)", m_synt.nonSynJoinInput); addStat("synt / non-synthesizable (false write)", m_synt.nonSynFalseWrite); addStat("synt / reverted (non-synthesizable)", m_synt.revertNonSyn); addStat("synt / reverted (multidrive)", m_synt.revertMultidrive); VDouble0 nSyntNonSyn; nSyntNonSyn += m_synt.nonSynConv; nSyntNonSyn += m_synt.nonSynExtWrite; nSyntNonSyn += m_synt.nonSynLoop; nSyntNonSyn += m_synt.nonSynStmt; nSyntNonSyn += m_synt.nonSynMultidrive; nSyntNonSyn += m_synt.nonSynArray; nSyntNonSyn += m_synt.nonSynLatch; nSyntNonSyn += m_synt.nonSynJoinInput; nSyntNonSyn += m_synt.nonSynFalseWrite; VDouble0 nSyntExpect; nSyntExpect += m_synt.inputAlways; nSyntExpect += m_synt.inputAssign; nSyntExpect -= m_synt.synthAlways; nSyntExpect -= m_synt.synthAssign; UASSERT(nSyntNonSyn == nSyntExpect, "Inconsistent statistics / synt"); addStat("synt / input CFG trivial", m_synt.cfgTrivial); addStat("synt / input CFG sp", m_synt.cfgSp); addStat("synt / input CFG dag", m_synt.cfgDag); addStat("synt / input CFG cyclic", m_synt.cfgCyclic); addStat("synt / joins using path predicate", m_synt.joinUsingPathPredicate); addStat("synt / joins using branch condition", m_synt.joinUsingBranchCondition); } }; //###################################################################### // Top level V3DfgContext class V3DfgContext final { public: // STATE // Sub contexts - keep sorted by type V3DfgAstToDfgContext m_ast2DfgContext; V3DfgBinToOneHotContext m_binToOneHotContext; V3DfgBreakCyclesContext m_breakCyclesContext; V3DfgCseContext m_cseContext0{"1st"}; V3DfgCseContext m_cseContext1{"2nd"}; V3DfgDfgToAstContext m_dfg2AstContext; V3DfgPeepholeContext m_peepholeContext; V3DfgPushDownSelsContext m_pushDownSelsContext; V3DfgRegularizeContext m_regularizeContext; V3DfgRemoveUnobservableContext m_removeUnobservableContext; V3DfgSynthesisContext m_synthContext; // CONSTRUCTOR V3DfgContext() = default; ~V3DfgContext() = default; }; #endif //VERILATOR_V3DFGCONTEXT_H_