// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Persistent context for DFG algorithms // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2025 by Wilson Snyder. 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-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 "V3DfgPatternStats.h" #include "V3DfgPeepholePatterns.h" #include "V3File.h" #include "V3Stats.h" #include "V3String.h" class V3DfgContext; ////////////////////////////////////////////////////////////////////////////// // Base class for all context objects ////////////////////////////////////////////////////////////////////////////// class V3DfgSubContext VL_NOT_FINAL { V3DfgContext& m_ctx; // The whole context const std::string m_label; // Label to add to stats, etc. const std::string m_name; // Pass/algorithm name, to be used in statistics protected: VL_DEFINE_DEBUG_FUNCTIONS; V3DfgSubContext(V3DfgContext& ctx, const std::string& label, const char* name) : m_ctx{ctx} , m_label{label} , m_name{name} {} void addStat(const std::string& what, double value) { V3Stats::addStat("Optimizations, DFG " + m_label + " " + m_name + ", " + what, value); } public: inline const std::string& prefix() const; const std::string& label() const { return m_label; } }; ////////////////////////////////////////////////////////////////////////////// // 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(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "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(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "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(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "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: V3DfgCseContext(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "CSE"} {} ~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 private: V3DfgDfgToAstContext(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "DfgToAst"} {} ~V3DfgDfgToAstContext() { addStat("output variables", m_outputVariables); addStat("output variables with default driver", m_outputVariablesWithDefault); 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; public: // STATE // Enable flags for each optimization std::array m_enabled; // Count of applications for each optimization (for statistics) std::array m_count; private: V3DfgPeepholeContext(V3DfgContext& ctx, const std::string& label) VL_MT_DISABLED; ~V3DfgPeepholeContext() VL_MT_DISABLED; }; class V3DfgRegularizeContext final : public V3DfgSubContext { // Only V3DfgContext can create an instance friend class V3DfgContext; public: // STATE VDouble0 m_temporariesIntroduced; // Number of temporaries introduced private: V3DfgRegularizeContext(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "Regularize"} {} ~V3DfgRegularizeContext() { addStat("temporaries introduced", m_temporariesIntroduced); } }; 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 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 } m_synt; private: V3DfgSynthesisContext(V3DfgContext& ctx, const std::string& label) : V3DfgSubContext{ctx, label, "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 (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.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"); } }; ////////////////////////////////////////////////////////////////////////////// // Top level V3DfgContext ////////////////////////////////////////////////////////////////////////////// class V3DfgContext final { const std::string m_label; // Label to add to stats, etc. const std::string m_prefix; // Prefix to add to file dumps (derived from label) public: // STATE // Global statistics VDouble0 m_modules; // Number of modules optimized // Sub contexts - keep sorted by type V3DfgAstToDfgContext m_ast2DfgContext{*this, m_label}; V3DfgBinToOneHotContext m_binToOneHotContext{*this, m_label}; V3DfgBreakCyclesContext m_breakCyclesContext{*this, m_label}; 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}; // Node pattern collector V3DfgPatternStats m_patternStats; // CONSTRUCTOR explicit V3DfgContext(const std::string& label) : m_label{label} , m_prefix{VString::removeWhitespace(label) + "-"} {} ~V3DfgContext() { const string front = "Optimizations, DFG " + label() + " General, "; V3Stats::addStat(front + "modules", m_modules); // Print the collected patterns if (v3Global.opt.stats()) { // Label to lowercase, without spaces std::string ident = label(); std::transform(ident.begin(), ident.end(), ident.begin(), [](unsigned char c) { // return c == ' ' ? '_' : std::tolower(c); }); // File to dump to const std::string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() + "__stats_dfg_patterns__" + ident + ".txt"; // Open, write, close const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write file: " << filename); m_patternStats.dump(label(), *ofp); } } // ACCESSORS const std::string& label() const { return m_label; } const std::string& prefix() const { return m_prefix; } }; const std::string& V3DfgSubContext::prefix() const { return m_ctx.prefix(); } #endif //VERILATOR_V3DFGCONTEXT_H_