393 lines
16 KiB
C++
393 lines
16 KiB
C++
// -*- 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 V3DfgPeepholeContext final : public V3DfgSubContext {
|
|
// Only V3DfgContext can create an instance
|
|
friend class V3DfgContext;
|
|
|
|
public:
|
|
// STATE
|
|
// Enable flags for each optimization
|
|
std::array<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
|
|
// Count of applications for each optimization (for statistics)
|
|
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> 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_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() {
|
|
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
|
|
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(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 (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 {
|
|
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};
|
|
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<std::ofstream> 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_
|