2022-10-08 15:34:07 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: DfgGraph decomposition algorithms
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2022-10-08 15:34:07 +02:00
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Algorithms that take a DfgGraph and decompose it into multiple DfgGraphs.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2022-10-08 15:34:07 +02:00
|
|
|
#include "V3Dfg.h"
|
2025-08-05 14:03:30 +02:00
|
|
|
#include "V3DfgPasses.h"
|
2022-10-08 15:34:07 +02:00
|
|
|
#include "V3File.h"
|
|
|
|
|
|
|
|
|
|
#include <deque>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
|
|
|
|
class SplitIntoComponents final {
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
DfgGraph& m_dfg; // The input graph
|
|
|
|
|
const std::string m_prefix; // Component name prefix
|
|
|
|
|
std::vector<std::unique_ptr<DfgGraph>> m_components; // The extracted components
|
|
|
|
|
// Component counter - starting from 1 as 0 is the default value used as a marker
|
|
|
|
|
size_t m_componentCounter = 1;
|
|
|
|
|
|
|
|
|
|
void colorComponents() {
|
|
|
|
|
// Work queue for depth first traversal starting from this vertex
|
|
|
|
|
std::vector<DfgVertex*> queue;
|
|
|
|
|
queue.reserve(m_dfg.size());
|
|
|
|
|
|
|
|
|
|
// any sort of interesting logic must involve a variable, so we only need to iterate them
|
2024-03-26 00:06:25 +01:00
|
|
|
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
2022-10-08 15:34:07 +02:00
|
|
|
// If already assigned this vertex to a component, then continue
|
2024-03-26 00:06:25 +01:00
|
|
|
if (vtx.user<size_t>()) continue;
|
2022-10-08 15:34:07 +02:00
|
|
|
|
|
|
|
|
// Start depth first traversal at this vertex
|
2024-03-26 00:06:25 +01:00
|
|
|
queue.push_back(&vtx);
|
2022-10-08 15:34:07 +02:00
|
|
|
|
|
|
|
|
// Depth first traversal
|
|
|
|
|
do {
|
|
|
|
|
// Pop next work item
|
|
|
|
|
DfgVertex& item = *queue.back();
|
|
|
|
|
queue.pop_back();
|
|
|
|
|
|
|
|
|
|
// Move on if already visited
|
|
|
|
|
if (item.user<size_t>()) continue;
|
|
|
|
|
|
|
|
|
|
// Assign to current component
|
|
|
|
|
item.user<size_t>() = m_componentCounter;
|
|
|
|
|
|
|
|
|
|
// Enqueue all sources and sinks of this vertex.
|
|
|
|
|
item.forEachSource([&](DfgVertex& src) { queue.push_back(&src); });
|
|
|
|
|
item.forEachSink([&](DfgVertex& dst) { queue.push_back(&dst); });
|
|
|
|
|
} while (!queue.empty());
|
|
|
|
|
|
|
|
|
|
// Done with this component
|
|
|
|
|
++m_componentCounter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-26 00:06:25 +01:00
|
|
|
template <typename Vertex>
|
|
|
|
|
void moveVertices(DfgVertex::List<Vertex>& list) {
|
|
|
|
|
for (DfgVertex* const vtxp : list.unlinkable()) {
|
|
|
|
|
if (const size_t component = vtxp->user<size_t>()) {
|
|
|
|
|
m_dfg.removeVertex(*vtxp);
|
|
|
|
|
m_components[component - 1]->addVertex(*vtxp);
|
2022-10-08 15:34:07 +02:00
|
|
|
} else {
|
|
|
|
|
// This vertex is not connected to a variable and is hence unused, remove here
|
2024-03-26 00:06:25 +01:00
|
|
|
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 11:52:40 +01:00
|
|
|
SplitIntoComponents(DfgGraph& dfg, const std::string& label)
|
2022-10-08 15:34:07 +02:00
|
|
|
: m_dfg{dfg}
|
|
|
|
|
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
|
|
|
|
|
// Component number is stored as DfgVertex::user<size_t>()
|
|
|
|
|
const auto userDataInUse = m_dfg.userDataInUse();
|
|
|
|
|
// Color each component of the graph
|
|
|
|
|
colorComponents();
|
|
|
|
|
// Allocate the component graphs
|
|
|
|
|
m_components.resize(m_componentCounter - 1);
|
|
|
|
|
for (size_t i = 1; i < m_componentCounter; ++i) {
|
2025-07-01 23:55:08 +02:00
|
|
|
m_components[i - 1].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i - 1)});
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
// Move the vertices to the component graphs
|
2024-03-26 00:06:25 +01:00
|
|
|
moveVertices(m_dfg.varVertices());
|
|
|
|
|
moveVertices(m_dfg.constVertices());
|
|
|
|
|
moveVertices(m_dfg.opVertices());
|
2022-10-08 15:34:07 +02:00
|
|
|
//
|
|
|
|
|
UASSERT(m_dfg.size() == 0, "'this' DfgGraph should have been emptied");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static std::vector<std::unique_ptr<DfgGraph>> apply(DfgGraph& dfg, const std::string& label) {
|
|
|
|
|
return std::move(SplitIntoComponents{dfg, label}.m_components);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-20 19:21:24 +02:00
|
|
|
std::vector<std::unique_ptr<DfgGraph>> DfgGraph::splitIntoComponents(const std::string& label) {
|
2022-10-08 15:34:07 +02:00
|
|
|
return SplitIntoComponents::apply(*this, label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ExtractCyclicComponents final {
|
|
|
|
|
// TYPES
|
2025-08-05 14:03:30 +02:00
|
|
|
// We reuse the DfgVertex::user state set by V3DfgPasses::colorStronglyConnectedComponents.
|
|
|
|
|
// We sneak an extra flag into the MSB to indicate the vertex was merged already.
|
|
|
|
|
class VertexState final {
|
|
|
|
|
uint64_t& m_userr;
|
|
|
|
|
|
|
|
|
|
public:
|
2025-08-20 19:21:24 +02:00
|
|
|
explicit VertexState(DfgVertex& vtx)
|
2025-08-05 14:03:30 +02:00
|
|
|
: m_userr{vtx.getUser<uint64_t>()} {}
|
|
|
|
|
bool merged() const { return m_userr >> 63; }
|
|
|
|
|
void setMerged() { m_userr |= 1ULL << 63; }
|
|
|
|
|
uint64_t component() const { return m_userr & ~(1ULL << 63); }
|
|
|
|
|
void component(uint64_t value) { m_userr = (m_userr & (1ULL << 63)) | value; }
|
2022-10-08 15:34:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
DfgGraph& m_dfg; // The input graph
|
|
|
|
|
const std::string m_prefix; // Component name prefix
|
|
|
|
|
const bool m_doExpensiveChecks = v3Global.opt.debugCheck();
|
|
|
|
|
// The extracted cyclic components
|
|
|
|
|
std::vector<std::unique_ptr<DfgGraph>> m_components;
|
|
|
|
|
// Map from 'variable vertex' -> 'component index' -> 'clone in that component'
|
2025-08-05 14:03:30 +02:00
|
|
|
std::unordered_map<const DfgVertexVar*, std::unordered_map<uint64_t, DfgVertexVar*>> m_clones;
|
2022-10-08 15:34:07 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2025-08-05 14:03:30 +02:00
|
|
|
void visitMergeSCCs(DfgVertex& vtx, uint64_t targetComponent) {
|
|
|
|
|
VertexState vtxState{vtx};
|
2022-10-08 15:34:07 +02:00
|
|
|
|
|
|
|
|
// Move on if already visited
|
2025-08-05 14:03:30 +02:00
|
|
|
if (vtxState.merged()) return;
|
2022-10-08 15:34:07 +02:00
|
|
|
|
|
|
|
|
// Visiting vertex
|
2025-08-05 14:03:30 +02:00
|
|
|
vtxState.setMerged();
|
2022-10-08 15:34:07 +02:00
|
|
|
|
|
|
|
|
// Assign vertex to the target component
|
2025-08-05 14:03:30 +02:00
|
|
|
vtxState.component(targetComponent);
|
2022-10-08 15:34:07 +02:00
|
|
|
|
2022-12-23 17:32:38 +01:00
|
|
|
// Visit all neighbors. We stop at variable boundaries,
|
2022-10-08 15:34:07 +02:00
|
|
|
// which is where we will split the graphs
|
2023-11-06 13:13:31 +01:00
|
|
|
vtx.forEachSource([this, targetComponent](DfgVertex& other) {
|
2022-10-08 15:34:07 +02:00
|
|
|
if (other.is<DfgVertexVar>()) return;
|
|
|
|
|
visitMergeSCCs(other, targetComponent);
|
|
|
|
|
});
|
2023-11-06 13:13:31 +01:00
|
|
|
vtx.forEachSink([this, targetComponent](DfgVertex& other) {
|
2022-10-08 15:34:07 +02:00
|
|
|
if (other.is<DfgVertexVar>()) return;
|
|
|
|
|
visitMergeSCCs(other, targetComponent);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mergeSCCs() {
|
|
|
|
|
// Ensure that component boundaries are always at variables, by merging SCCs. Merging stops
|
|
|
|
|
// at variable boundaries, so we don't need to iterate variables. Constants are reachable
|
2022-12-03 00:46:38 +01:00
|
|
|
// from their sinks, or are unused, so we don't need to iterate them either.
|
2024-03-26 00:06:25 +01:00
|
|
|
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
2022-10-08 15:34:07 +02:00
|
|
|
// Start DFS from each vertex that is in a non-trivial SCC, and merge everything
|
|
|
|
|
// that is reachable from it into this component.
|
2025-08-05 14:03:30 +02:00
|
|
|
if (const uint64_t target = VertexState{vtx}.component()) visitMergeSCCs(vtx, target);
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retrieve clone of vertex in the given component
|
2025-08-05 14:03:30 +02:00
|
|
|
DfgVertexVar& getClone(DfgVertexVar& vtx, uint64_t component) {
|
|
|
|
|
UASSERT_OBJ(VertexState{vtx}.component() != component, &vtx,
|
|
|
|
|
"Vertex is in that component");
|
2022-10-08 15:34:07 +02:00
|
|
|
DfgVertexVar*& clonep = m_clones[&vtx][component];
|
|
|
|
|
if (!clonep) {
|
|
|
|
|
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
|
2025-07-01 23:55:08 +02:00
|
|
|
if (AstVarScope* const vscp = pVtxp->varScopep()) {
|
|
|
|
|
clonep = new DfgVarPacked{m_dfg, vscp};
|
|
|
|
|
} else {
|
|
|
|
|
clonep = new DfgVarPacked{m_dfg, pVtxp->varp()};
|
|
|
|
|
}
|
2022-10-08 15:34:07 +02:00
|
|
|
} else if (DfgVarArray* const aVtxp = vtx.cast<DfgVarArray>()) {
|
2025-07-01 23:55:08 +02:00
|
|
|
if (AstVarScope* const vscp = aVtxp->varScopep()) {
|
|
|
|
|
clonep = new DfgVarArray{m_dfg, vscp};
|
|
|
|
|
} else {
|
|
|
|
|
clonep = new DfgVarArray{m_dfg, aVtxp->varp()};
|
|
|
|
|
}
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type");
|
2025-08-05 14:03:30 +02:00
|
|
|
clonep->setUser<uint64_t>(component);
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
clonep->tmpForp(vtx.tmpForp());
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
return *clonep;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:09:34 +02:00
|
|
|
// Fix edges that cross components
|
|
|
|
|
void fixEdges(DfgVertexVar& vtx) {
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t component = VertexState{vtx}.component();
|
2022-10-08 15:34:07 +02:00
|
|
|
|
2025-07-22 15:03:35 +02:00
|
|
|
// Fix up sources in a different component
|
|
|
|
|
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) {
|
|
|
|
|
DfgVertex* const srcp = edge.sourcep();
|
|
|
|
|
if (!srcp) return;
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t sourceComponent = VertexState{*srcp}.component();
|
2025-07-22 15:03:35 +02:00
|
|
|
// Same component is OK
|
|
|
|
|
if (sourceComponent == component) return;
|
|
|
|
|
// Relink the source to write the clone
|
|
|
|
|
edge.unlinkSource();
|
|
|
|
|
getClone(vtx, sourceComponent).srcp(srcp);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Fix up sinks in a different component
|
2022-10-08 15:34:07 +02:00
|
|
|
vtx.forEachSinkEdge([&](DfgEdge& edge) {
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t sinkComponent = VertexState{*edge.sinkp()}.component();
|
2022-10-08 15:34:07 +02:00
|
|
|
// Same component is OK
|
|
|
|
|
if (sinkComponent == component) return;
|
|
|
|
|
// Relink the sink to read the clone
|
|
|
|
|
edge.relinkSource(&getClone(vtx, sinkComponent));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-26 00:06:25 +01:00
|
|
|
template <typename Vertex>
|
|
|
|
|
void moveVertices(DfgVertex::List<Vertex>& list) {
|
|
|
|
|
for (DfgVertex* const vtxp : list.unlinkable()) {
|
2022-10-08 15:34:07 +02:00
|
|
|
DfgVertex& vtx = *vtxp;
|
2025-08-05 14:03:30 +02:00
|
|
|
if (const uint64_t component = VertexState{vtx}.component()) {
|
2022-10-08 15:34:07 +02:00
|
|
|
m_dfg.removeVertex(vtx);
|
|
|
|
|
m_components[component - 1]->addVertex(vtx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkEdges(DfgGraph& dfg) const {
|
|
|
|
|
// Check that:
|
|
|
|
|
// - Edges only cross components at variable boundaries
|
|
|
|
|
// - Variable vertex sources are all connected.
|
|
|
|
|
dfg.forEachVertex([&](DfgVertex& vtx) {
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint64_t component = VertexState{vtx}.component();
|
2022-10-08 15:34:07 +02:00
|
|
|
vtx.forEachSource([&](DfgVertex& src) {
|
|
|
|
|
if (src.is<DfgVertexVar>()) return; // OK to cross at variables
|
2025-08-05 14:03:30 +02:00
|
|
|
UASSERT_OBJ(component == VertexState{src}.component(), &vtx,
|
2022-10-08 15:34:07 +02:00
|
|
|
"Edge crossing components without variable involvement");
|
|
|
|
|
});
|
|
|
|
|
vtx.forEachSink([&](DfgVertex& snk) {
|
|
|
|
|
if (snk.is<DfgVertexVar>()) return; // OK to cross at variables
|
2025-08-05 14:03:30 +02:00
|
|
|
UASSERT_OBJ(component == VertexState{snk}.component(), &vtx,
|
2022-10-08 15:34:07 +02:00
|
|
|
"Edge crossing components without variable involvement");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkGraph(DfgGraph& dfg) const {
|
|
|
|
|
// Build set of vertices
|
|
|
|
|
std::unordered_set<const DfgVertex*> vertices{dfg.size()};
|
|
|
|
|
dfg.forEachVertex([&](const DfgVertex& vtx) { vertices.insert(&vtx); });
|
|
|
|
|
|
|
|
|
|
// Check that each edge connects to a vertex that is within the same graph
|
2025-08-20 19:21:24 +02:00
|
|
|
dfg.forEachVertex([&](const DfgVertex& vtx) {
|
|
|
|
|
vtx.forEachSource([&](const DfgVertex& src) {
|
2022-10-08 15:34:07 +02:00
|
|
|
UASSERT_OBJ(vertices.count(&src), &vtx, "Source vertex not in graph");
|
|
|
|
|
});
|
2025-08-20 19:21:24 +02:00
|
|
|
vtx.forEachSink([&](const DfgVertex& snk) {
|
2022-10-08 15:34:07 +02:00
|
|
|
UASSERT_OBJ(vertices.count(&snk), &snk, "Sink vertex not in graph");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-05 14:03:30 +02:00
|
|
|
void extractComponents(uint32_t numNonTrivialSCCs) {
|
2022-10-08 15:34:07 +02:00
|
|
|
// Allocate result graphs
|
2025-08-05 14:03:30 +02:00
|
|
|
m_components.resize(numNonTrivialSCCs);
|
|
|
|
|
for (uint32_t i = 0; i < numNonTrivialSCCs; ++i) {
|
2025-07-01 23:55:08 +02:00
|
|
|
m_components[i].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i)});
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fix up edges crossing components (we can only do this at variable boundaries, and the
|
|
|
|
|
// earlier merging of components ensured crossing in fact only happen at variable
|
|
|
|
|
// boundaries). Note that fixing up the edges can create clones of variables. Clones do
|
|
|
|
|
// not need fixing up, so we do not need to iterate them.
|
2025-08-20 19:21:24 +02:00
|
|
|
const DfgVertex* const lastp = m_dfg.varVertices().backp();
|
2024-03-26 00:06:25 +01:00
|
|
|
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
2022-10-08 15:34:07 +02:00
|
|
|
// Fix up the edges crossing components
|
|
|
|
|
fixEdges(vtx);
|
|
|
|
|
// Don't iterate clones added during this loop
|
2024-03-26 00:06:25 +01:00
|
|
|
if (&vtx == lastp) break;
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check results for consistency
|
|
|
|
|
if (VL_UNLIKELY(m_doExpensiveChecks)) {
|
|
|
|
|
checkEdges(m_dfg);
|
|
|
|
|
for (const auto& dfgp : m_components) checkEdges(*dfgp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move other vertices to their component graphs
|
|
|
|
|
// After this, vertex states are invalid as we moved the vertices
|
2024-03-26 00:06:25 +01:00
|
|
|
moveVertices(m_dfg.varVertices());
|
|
|
|
|
moveVertices(m_dfg.constVertices());
|
|
|
|
|
moveVertices(m_dfg.opVertices());
|
2022-10-08 15:34:07 +02:00
|
|
|
|
|
|
|
|
// Check results for consistency
|
|
|
|
|
if (VL_UNLIKELY(m_doExpensiveChecks)) {
|
|
|
|
|
checkGraph(m_dfg);
|
|
|
|
|
for (const auto& dfgp : m_components) checkGraph(*dfgp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CONSTRUCTOR - entry point
|
2022-11-27 11:52:40 +01:00
|
|
|
explicit ExtractCyclicComponents(DfgGraph& dfg, const std::string& label)
|
2022-10-08 15:34:07 +02:00
|
|
|
: m_dfg{dfg}
|
|
|
|
|
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
|
2025-08-05 14:03:30 +02:00
|
|
|
// DfgVertex::user<uint64_t> is set to the SCC number by colorStronglyConnectedComponents,
|
|
|
|
|
// Then we use VertexState to handle the MSB as an extra flag.
|
2022-10-08 15:34:07 +02:00
|
|
|
const auto userDataInUse = dfg.userDataInUse();
|
|
|
|
|
// Find all the non-trivial SCCs (and trivial cycles) in the graph
|
2025-08-05 14:03:30 +02:00
|
|
|
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg);
|
|
|
|
|
// If the graph was acyclic (which should be the common case), then we are done.
|
|
|
|
|
if (!numNonTrivialSCCs) return;
|
2022-10-08 15:34:07 +02:00
|
|
|
// Ensure that component boundaries are always at variables, by merging SCCs
|
|
|
|
|
mergeSCCs();
|
|
|
|
|
// Extract the components
|
2025-08-05 14:03:30 +02:00
|
|
|
extractComponents(numNonTrivialSCCs);
|
2022-10-08 15:34:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static std::vector<std::unique_ptr<DfgGraph>> apply(DfgGraph& dfg, const std::string& label) {
|
|
|
|
|
return std::move(ExtractCyclicComponents{dfg, label}.m_components);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-20 19:21:24 +02:00
|
|
|
std::vector<std::unique_ptr<DfgGraph>>
|
|
|
|
|
DfgGraph::extractCyclicComponents(const std::string& label) {
|
2022-10-08 15:34:07 +02:00
|
|
|
return ExtractCyclicComponents::apply(*this, label);
|
|
|
|
|
}
|