Internals: Run Dfg passes on all components at once
This is just a reordering of pass applications to make --stats more useful. No functional change.
This commit is contained in:
parent
cf6a2aec19
commit
fb66174d80
|
|
@ -401,24 +401,9 @@ public:
|
|||
V3DfgRemoveUnobservableContext m_removeUnobservableContext;
|
||||
V3DfgSynthesisContext m_synthContext;
|
||||
|
||||
// Node pattern collector
|
||||
V3DfgPatternStats m_patternStats;
|
||||
|
||||
// CONSTRUCTOR
|
||||
V3DfgContext() = default;
|
||||
|
||||
~V3DfgContext() {
|
||||
// Print the collected patterns
|
||||
if (v3Global.opt.stats()) {
|
||||
// File to dump to
|
||||
const std::string filename = v3Global.opt.hierTopDataDir() + "/"
|
||||
+ v3Global.opt.prefix() + "__stats_dfg_patterns.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(*ofp);
|
||||
}
|
||||
}
|
||||
~V3DfgContext() = default;
|
||||
};
|
||||
|
||||
#endif //VERILATOR_V3DFGCONTEXT_H_
|
||||
|
|
|
|||
|
|
@ -257,13 +257,30 @@ class DataflowOptimize final {
|
|||
// STATE
|
||||
V3DfgContext m_ctx; // The context holding values that need to persist across multiple graphs
|
||||
|
||||
void endOfStage(const std::string& name, const DfgGraph* dfgp = nullptr) {
|
||||
// Dump the graph for debugging if given one
|
||||
if (VL_UNLIKELY(dumpDfgLevel() >= 8 && dfgp)) dfgp->dumpDotFilePrefixed(name);
|
||||
// Dump stage stats only in scoped mode when running on the whole netlist
|
||||
void endOfStage(const std::string& name) {
|
||||
if (VL_UNLIKELY(v3Global.opt.stats())) V3Stats::statsStage("dfg-optimize-" + name);
|
||||
}
|
||||
|
||||
void endOfStage(const std::string& name, const DfgGraph& dfg,
|
||||
const std::vector<std::unique_ptr<DfgGraph>>& componentps) {
|
||||
// Dump the graphs for debugging
|
||||
if (VL_UNLIKELY(dumpDfgLevel() >= 5)) {
|
||||
if (dfg.size() > 0) dfg.dumpDotFilePrefixed(name);
|
||||
for (const std::unique_ptr<DfgGraph>& componentp : componentps) {
|
||||
if (componentp->size() > 0) componentp->dumpDotFilePrefixed(name);
|
||||
}
|
||||
}
|
||||
// Type check the graphs
|
||||
if (VL_UNLIKELY(v3Global.opt.debugCheck())) {
|
||||
V3DfgPasses::typeCheck(dfg);
|
||||
for (const std::unique_ptr<DfgGraph>& componentp : componentps) {
|
||||
V3DfgPasses::typeCheck(*componentp);
|
||||
}
|
||||
}
|
||||
// Dump stage stats
|
||||
endOfStage(name);
|
||||
}
|
||||
|
||||
// Mark variables with external references
|
||||
void markExternallyReferencedVariables(AstNetlist* netlistp) {
|
||||
netlistp->foreach([](AstNode* nodep) {
|
||||
|
|
@ -297,17 +314,17 @@ class DataflowOptimize final {
|
|||
void optimize(DfgGraph& dfg) {
|
||||
// Remove unobservable variabels and logic that drives only such variables
|
||||
V3DfgPasses::removeUnobservable(dfg, m_ctx);
|
||||
endOfStage("remove-unobservable", &dfg);
|
||||
endOfStage("removeUnobservable", dfg, {});
|
||||
|
||||
// Synthesize DfgLogic vertices
|
||||
V3DfgPasses::synthesize(dfg, m_ctx);
|
||||
endOfStage("synthesize", &dfg);
|
||||
endOfStage("synthesize", dfg, {});
|
||||
|
||||
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
|
||||
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
|
||||
// small cycles.
|
||||
std::vector<std::unique_ptr<DfgGraph>> cyclicComps = dfg.extractCyclicComponents("cyclic");
|
||||
endOfStage("extract-cyclic");
|
||||
endOfStage("extractCyclic", dfg, cyclicComps);
|
||||
|
||||
// Attempt to convert cyclic components into acyclic ones
|
||||
std::vector<std::unique_ptr<DfgGraph>> madeAcyclicComponents;
|
||||
|
|
@ -330,27 +347,46 @@ class DataflowOptimize final {
|
|||
}
|
||||
// Merge those that were made acyclic back to the graph, this enables optimizing more
|
||||
dfg.mergeGraphs(std::move(madeAcyclicComponents));
|
||||
endOfStage("break-cycles");
|
||||
endOfStage("breakCycles", dfg, cyclicComps);
|
||||
|
||||
// Split the acyclic DFG into [weakly] connected components
|
||||
std::vector<std::unique_ptr<DfgGraph>> acyclicComps = dfg.splitIntoComponents("acyclic");
|
||||
UASSERT(dfg.size() == 0, "DfgGraph should have become empty");
|
||||
endOfStage("split-acyclic");
|
||||
endOfStage("splitAcyclic", dfg, acyclicComps);
|
||||
|
||||
// Optimize each acyclic component
|
||||
for (const std::unique_ptr<DfgGraph>& component : acyclicComps) {
|
||||
V3DfgPasses::optimize(*component, m_ctx);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::inlineVars(*cp);
|
||||
endOfStage("inlineVars", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext0);
|
||||
endOfStage("cse0", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::binToOneHot(*cp, m_ctx.m_binToOneHotContext);
|
||||
endOfStage("binToOneHot", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::peephole(*cp, m_ctx.m_peepholeContext);
|
||||
endOfStage("peephole", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::pushDownSels(*cp, m_ctx.m_pushDownSelsContext);
|
||||
endOfStage("pushDownSels", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext1);
|
||||
endOfStage("cse1", dfg, acyclicComps);
|
||||
|
||||
// Accumulate patterns for reporting
|
||||
if (v3Global.opt.stats()) {
|
||||
{
|
||||
V3DfgPatternStats patternStats;
|
||||
for (auto& cp : acyclicComps) patternStats.accumulate(*cp);
|
||||
}
|
||||
endOfStage("patterns");
|
||||
}
|
||||
|
||||
// Merge everything back under the main DFG
|
||||
dfg.mergeGraphs(std::move(acyclicComps));
|
||||
dfg.mergeGraphs(std::move(cyclicComps));
|
||||
endOfStage("optimize", &dfg);
|
||||
endOfStage("optimized", dfg, {});
|
||||
|
||||
// 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);
|
||||
endOfStage("regularize", &dfg);
|
||||
endOfStage("regularize", dfg, {});
|
||||
}
|
||||
|
||||
void removeNeverActives(AstNetlist* netlistp) {
|
||||
|
|
@ -372,7 +408,6 @@ class DataflowOptimize final {
|
|||
}
|
||||
|
||||
DataflowOptimize(AstNetlist* netlistp) {
|
||||
|
||||
// Mark interfaces that might be referenced by a virtual interface
|
||||
if (v3Global.hasVirtIfaces()) {
|
||||
netlistp->typeTablep()->foreach([](const AstIfaceRefDType* nodep) {
|
||||
|
|
@ -380,25 +415,26 @@ class DataflowOptimize final {
|
|||
nodep->ifaceViaCellp()->setHasVirtualRef();
|
||||
});
|
||||
}
|
||||
|
||||
// Mark variables with external references
|
||||
markExternallyReferencedVariables(netlistp);
|
||||
|
||||
// Dump stage stats
|
||||
endOfStage("init");
|
||||
// Post V3Scope application. Run on whole netlist.
|
||||
UINFO(4, "Applying DFG optimization to entire netlist");
|
||||
// Build the DFG of the entire netlist
|
||||
const std::unique_ptr<DfgGraph> dfgp = V3DfgPasses::astToDfg(*netlistp, m_ctx);
|
||||
endOfStage("ast-to-dfg", dfgp.get());
|
||||
endOfStage("astToDfg", *dfgp, {});
|
||||
// Actually process the graph
|
||||
optimize(*dfgp);
|
||||
// Convert back to Ast
|
||||
V3DfgPasses::dfgToAst(*dfgp, m_ctx);
|
||||
endOfStage("dfg-to-ast", dfgp.get());
|
||||
endOfStage("dfgToAst", *dfgp, {});
|
||||
// Some sentrees might have become constant, remove them
|
||||
removeNeverActives(netlistp);
|
||||
|
||||
// Reset interned types so the corresponding Ast types can be garbage collected
|
||||
DfgDataType::reset();
|
||||
// Dump stage stats
|
||||
endOfStage("fini");
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -466,33 +466,3 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const auto run = [&](const std::string& name, bool dump, std::function<void()> pass) {
|
||||
// Apply the pass
|
||||
pass();
|
||||
// Debug dump
|
||||
if (dump) dfg.dumpDotFilePrefixed("opt-" + VString::removeWhitespace(name));
|
||||
// Internal type check
|
||||
if (v3Global.opt.debugCheck()) V3DfgPasses::typeCheck(dfg);
|
||||
};
|
||||
|
||||
// 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("pushDownSels", dumpLvl >= 4, [&]() { pushDownSels(dfg, ctx.m_pushDownSelsContext); });
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,20 +34,11 @@ namespace V3DfgPasses {
|
|||
// The logic that is represented by the graph is removed from the netlist.
|
||||
// Returns the constructed DfgGraph.
|
||||
std::unique_ptr<DfgGraph> astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Add DfgVertexAst to the given DfgGraph for all references in the given AstNode.
|
||||
// The function 'getVarVertex' is used to get the DfgVertexVar for an AstVarScope.
|
||||
// If it returns nullptr, the reference will be ignored.
|
||||
void addAstRefs(DfgGraph& dfg, AstNode* nodep,
|
||||
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex) VL_MT_DISABLED;
|
||||
|
||||
// Remove unobservable variabels and logic that drives only such variables
|
||||
void removeUnobservable(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Synthesize DfgLogic vertices into primitive operations.
|
||||
// Removes all DfgLogic (even those that were not synthesized).
|
||||
void synthesize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Attempt to make the given cyclic graph into an acyclic, or "less cyclic"
|
||||
// equivalent. If the returned pointer is null, then no improvement was
|
||||
// possible on the input graph. Otherwise the returned graph is an improvement
|
||||
|
|
@ -57,27 +48,8 @@ void synthesize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
|||
// acyclic (flag 'true'), or still cyclic (flag 'false').
|
||||
std::pair<std::unique_ptr<DfgGraph>, bool> //
|
||||
breakCycles(const DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Optimize the given DfgGraph
|
||||
void optimize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
// Convert DfgGraph back into Ast, and insert converted graph back into the Ast.
|
||||
void dfgToAst(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
//===========================================================================
|
||||
// Intermediate/internal operations
|
||||
//===========================================================================
|
||||
|
||||
// Construct binary to oneHot decoders
|
||||
void binToOneHot(DfgGraph&, V3DfgBinToOneHotContext&) VL_MT_DISABLED;
|
||||
// Populates the given DfgUserMap for all vertext to:
|
||||
// - 0, if the vertex is not part of a non-trivial strongly connected component
|
||||
// and is not part of a self-loop. That is: the Vertex is not part of any cycle.
|
||||
// - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N.
|
||||
// That is: each set of vertices that are reachable from each other will have the same
|
||||
// non-zero value assigned.
|
||||
// Returns the number of non-trivial SCCs (distinct cycles)
|
||||
uint32_t colorStronglyConnectedComponents(const DfgGraph&, DfgUserMap<uint64_t>&) VL_MT_DISABLED;
|
||||
// Common subexpression elimination
|
||||
void cse(DfgGraph&, V3DfgCseContext&) VL_MT_DISABLED;
|
||||
// Inline fully driven variables
|
||||
|
|
@ -88,6 +60,26 @@ void pushDownSels(DfgGraph& dfg, V3DfgPushDownSelsContext& ctx) VL_MT_DISABLED;
|
|||
void peephole(DfgGraph&, V3DfgPeepholeContext&) VL_MT_DISABLED;
|
||||
// Regularize graph. This must be run before converting back to Ast.
|
||||
void regularize(DfgGraph&, V3DfgRegularizeContext&) VL_MT_DISABLED;
|
||||
// Convert DfgGraph back into Ast, and insert converted graph back into the Ast.
|
||||
void dfgToAst(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
|
||||
//===========================================================================
|
||||
// Intermediate/internal operations
|
||||
//===========================================================================
|
||||
|
||||
// Add DfgVertexAst to the given DfgGraph for all references in the given AstNode.
|
||||
// The function 'getVarVertex' is used to get the DfgVertexVar for an AstVarScope.
|
||||
// If it returns nullptr, the reference will be ignored.
|
||||
void addAstRefs(DfgGraph& dfg, AstNode* nodep,
|
||||
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex) VL_MT_DISABLED;
|
||||
// Populates the given DfgUserMap for all vertext to:
|
||||
// - 0, if the vertex is not part of a non-trivial strongly connected component
|
||||
// and is not part of a self-loop. That is: the Vertex is not part of any cycle.
|
||||
// - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N.
|
||||
// That is: each set of vertices that are reachable from each other will have the same
|
||||
// non-zero value assigned.
|
||||
// Returns the number of non-trivial SCCs (distinct cycles)
|
||||
uint32_t colorStronglyConnectedComponents(const DfgGraph&, DfgUserMap<uint64_t>&) VL_MT_DISABLED;
|
||||
// Remove unused nodes
|
||||
void removeUnused(DfgGraph&) VL_MT_DISABLED;
|
||||
// Check all types are consistent. This will not return if there is a type error.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#define VERILATOR_V3DFGPATTERNSTATS_H_
|
||||
|
||||
#include "V3Dfg.h"
|
||||
#include "V3File.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
|
@ -149,24 +150,6 @@ class V3DfgPatternStats final {
|
|||
return deep;
|
||||
}
|
||||
|
||||
public:
|
||||
V3DfgPatternStats() = default;
|
||||
|
||||
void accumulate(const DfgGraph& dfg) {
|
||||
dfg.forEachVertex([&](const DfgVertex& vtx) {
|
||||
for (uint32_t i = MIN_PATTERN_DEPTH; i <= MAX_PATTERN_DEPTH; ++i) {
|
||||
std::ostringstream ss;
|
||||
if (render(ss, vtx, i)) m_patterCounts[i][ss.str()] += 1;
|
||||
m_internedConsts.clear();
|
||||
m_internedVars.clear();
|
||||
m_internedSelLsbs.clear();
|
||||
m_internedWordWidths.clear();
|
||||
m_internedWideWidths.clear();
|
||||
m_internedVertices.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void dump(std::ostream& os) {
|
||||
using Line = std::pair<std::string, size_t>;
|
||||
for (uint32_t i = MIN_PATTERN_DEPTH; i <= MAX_PATTERN_DEPTH; ++i) {
|
||||
|
|
@ -194,6 +177,33 @@ public:
|
|||
os << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
V3DfgPatternStats() = default;
|
||||
~V3DfgPatternStats() {
|
||||
// File to dump to
|
||||
const std::string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix()
|
||||
+ "__stats_dfg_patterns.txt";
|
||||
// Open, write, close
|
||||
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
||||
if (ofp->fail()) v3fatal("Can't write file: " << filename);
|
||||
dump(*ofp);
|
||||
}
|
||||
|
||||
void accumulate(const DfgGraph& dfg) {
|
||||
dfg.forEachVertex([&](const DfgVertex& vtx) {
|
||||
for (uint32_t i = MIN_PATTERN_DEPTH; i <= MAX_PATTERN_DEPTH; ++i) {
|
||||
std::ostringstream ss;
|
||||
if (render(ss, vtx, i)) m_patterCounts[i][ss.str()] += 1;
|
||||
m_internedConsts.clear();
|
||||
m_internedVars.clear();
|
||||
m_internedSelLsbs.clear();
|
||||
m_internedWordWidths.clear();
|
||||
m_internedWideWidths.clear();
|
||||
m_internedVertices.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue