mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Introduce a new DfgPrev vertex, representing the value of a variable before any in-graph assignments. This can be used to break all remaining cycles in the graph, so all Dfgs become acyclic after V3DfgBreakCycles. The circular dataflow is still represented, and is taken care of by the scheduler, it is just the DfgGraph that represents the logic that becomes acyclic. This makes V3DfgBreakCycles a mandatory transform, so drop the disabling -fno-dfg-break-cycles option (still parsed, but has no effect). Note the effect of this is small, as most cycles can be fixed up by driver tracing, which is unchanged, but this is required for some upcoming work.
432 lines
18 KiB
C++
432 lines
18 KiB
C++
// -*- 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 improved but still cyclic
|
|
VDouble0 m_nUnchanged; // Number of graphs that were left unchanged
|
|
VDouble0 m_nImprovements; // Number of changes made to graphs
|
|
VDouble0 m_nPrevInserted; // Number of Prev vertices inserted
|
|
|
|
private:
|
|
V3DfgBreakCyclesContext()
|
|
: V3DfgSubContext{"BreakCycles"} {}
|
|
~V3DfgBreakCyclesContext() {
|
|
addStat("made acyclic", m_nFixed);
|
|
addStat("improved", m_nImproved);
|
|
addStat("left unchanged", m_nUnchanged);
|
|
addStat("changes applied", m_nImprovements);
|
|
addStat("prev vertices inserted", m_nPrevInserted);
|
|
}
|
|
};
|
|
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<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
|
|
// Count of applications for each optimization (for statistics)
|
|
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> m_count;
|
|
std::vector<AstNode*> 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<AstNode*> 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 V3DfgRemoveSelectsContext final : public V3DfgSubContext {
|
|
// Only V3DfgContext can create an instance
|
|
friend class V3DfgContext;
|
|
|
|
public:
|
|
// STATE
|
|
VDouble0 m_removedFullWidth; // Number of full width selects removed
|
|
VDouble0 m_replacedWithSelFromFull; // Number of selects replaced with sel from full driver
|
|
VDouble0 m_replacedWithSelFromPart; // Number of selects replaced with sel from partial driver
|
|
VDouble0 m_replacedWithPart; // Number of selects replaced with part of driver
|
|
private:
|
|
V3DfgRemoveSelectsContext()
|
|
: V3DfgSubContext{"RemoveSelects"} {}
|
|
~V3DfgRemoveSelectsContext() {
|
|
addStat("full width selects removed", m_removedFullWidth);
|
|
addStat("replaced with sel from full driver", m_replacedWithSelFromFull);
|
|
addStat("replaced with sel from partial driver", m_replacedWithSelFromPart);
|
|
addStat("replaced with partial driver", m_replacedWithPart);
|
|
}
|
|
};
|
|
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;
|
|
V3DfgRemoveSelectsContext m_removeSelectsContext;
|
|
V3DfgRemoveUnobservableContext m_removeUnobservableContext;
|
|
V3DfgSynthesisContext m_synthContext;
|
|
|
|
// CONSTRUCTOR
|
|
V3DfgContext() = default;
|
|
~V3DfgContext() = default;
|
|
};
|
|
|
|
#endif //VERILATOR_V3DFGCONTEXT_H_
|