2025-08-05 14:03:30 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Cycle finding algorithm for DfgGraph
|
|
|
|
|
//
|
|
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Implements Pearce's algorithm to color the strongly connected components. For reference
|
|
|
|
|
// see "An Improved Algorithm for Finding the Strongly Connected Components of a Directed
|
|
|
|
|
// Graph", David J.Pearce, 2005.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "V3Dfg.h"
|
|
|
|
|
#include "V3DfgPasses.h"
|
|
|
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
// Similar algorithm used in ExtractCyclicComponents.
|
|
|
|
|
// This one sets DfgVertex::user(). See the static 'apply' method below.
|
|
|
|
|
class ColorStronglyConnectedComponents final {
|
|
|
|
|
static constexpr uint32_t UNASSIGNED = std::numeric_limits<uint32_t>::max();
|
|
|
|
|
|
|
|
|
|
// TYPES
|
|
|
|
|
struct VertexState final {
|
|
|
|
|
uint32_t component = UNASSIGNED; // Result component number (0 means not in SCC)
|
|
|
|
|
uint32_t index = UNASSIGNED; // Used by Pearce's algorithm for detecting SCCs
|
|
|
|
|
VertexState() = default;
|
|
|
|
|
VertexState(uint32_t i, uint32_t n)
|
|
|
|
|
: component{n}
|
|
|
|
|
, index{i} {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
DfgGraph& m_dfg; // The input graph
|
|
|
|
|
uint32_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph
|
|
|
|
|
uint32_t m_index = 0; // Visitation index counter
|
|
|
|
|
std::vector<DfgVertex*> m_stack; // The stack used by the algorithm
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void visitColorSCCs(DfgVertex& vtx, VertexState& vtxState) {
|
|
|
|
|
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED, &vtx, "Already visited vertex"););
|
|
|
|
|
|
|
|
|
|
// Visiting vertex
|
|
|
|
|
const size_t rootIndex = vtxState.index = ++m_index;
|
|
|
|
|
|
|
|
|
|
// Visit children
|
2025-09-02 17:50:40 +02:00
|
|
|
vtx.foreachSink([&](DfgVertex& child) {
|
2025-08-05 14:03:30 +02:00
|
|
|
VertexState& childSatate = child.user<VertexState>();
|
|
|
|
|
// If the child has not yet been visited, then continue traversal
|
|
|
|
|
if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate);
|
|
|
|
|
// If the child is not in an SCC
|
|
|
|
|
if (childSatate.component == UNASSIGNED) {
|
|
|
|
|
if (vtxState.index > childSatate.index) vtxState.index = childSatate.index;
|
|
|
|
|
}
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2025-08-05 14:03:30 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (vtxState.index == rootIndex) {
|
|
|
|
|
// This is the 'root' of an SCC
|
|
|
|
|
|
|
|
|
|
// A trivial SCC contains only a single vertex
|
|
|
|
|
const bool isTrivial = m_stack.empty() //
|
|
|
|
|
|| m_stack.back()->getUser<VertexState>().index < rootIndex;
|
|
|
|
|
// We also need a separate component for vertices that drive themselves (which can
|
|
|
|
|
// happen for input like 'assign a = a'), as we want to extract them (they are cyclic).
|
2025-09-02 17:50:40 +02:00
|
|
|
const bool drivesSelf = vtx.foreachSink([&](const DfgVertex& sink) { //
|
2025-08-05 14:03:30 +02:00
|
|
|
return &vtx == &sink;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!isTrivial || drivesSelf) {
|
|
|
|
|
// Allocate new component
|
|
|
|
|
++m_nonTrivialSCCs;
|
|
|
|
|
vtxState.component = m_nonTrivialSCCs;
|
|
|
|
|
while (!m_stack.empty()) {
|
|
|
|
|
VertexState& topState = m_stack.back()->getUser<VertexState>();
|
|
|
|
|
// Only higher nodes belong to the same SCC
|
|
|
|
|
if (topState.index < rootIndex) break;
|
|
|
|
|
m_stack.pop_back();
|
|
|
|
|
topState.component = m_nonTrivialSCCs;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph.
|
|
|
|
|
vtxState.component = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Not the root of an SCC
|
|
|
|
|
m_stack.push_back(&vtx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void colorSCCs() {
|
|
|
|
|
// We know constant nodes have no input edges, so they cannot be part
|
|
|
|
|
// of a non-trivial SCC. Mark them as such without any real traversals.
|
|
|
|
|
for (DfgConst& vtx : m_dfg.constVertices()) vtx.setUser(VertexState{0, 0});
|
|
|
|
|
|
|
|
|
|
// Start traversals through variables
|
|
|
|
|
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
|
|
|
|
|
VertexState& vtxState = vtx.user<VertexState>();
|
|
|
|
|
// If it has no input or no outputs, it cannot be part of a non-trivial SCC.
|
2025-09-02 17:50:40 +02:00
|
|
|
if ((!vtx.srcp() && !vtx.defaultp()) || !vtx.hasSinks()) {
|
2025-08-05 14:03:30 +02:00
|
|
|
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0,
|
|
|
|
|
&vtx, "Non circular variable must be in a trivial SCC"););
|
|
|
|
|
vtxState.index = 0;
|
|
|
|
|
vtxState.component = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// If not yet visited, start a traversal
|
|
|
|
|
if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start traversals through operations
|
|
|
|
|
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
|
|
|
|
VertexState& vtxState = vtx.user<VertexState>();
|
|
|
|
|
// If not yet visited, start a traversal
|
|
|
|
|
if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-20 19:21:24 +02:00
|
|
|
explicit ColorStronglyConnectedComponents(DfgGraph& dfg)
|
2025-08-05 14:03:30 +02:00
|
|
|
: m_dfg{dfg} {
|
|
|
|
|
UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name());
|
|
|
|
|
// Yet another implementation of Pearce's algorithm.
|
|
|
|
|
colorSCCs();
|
|
|
|
|
// Re-assign user values
|
|
|
|
|
m_dfg.forEachVertex([](DfgVertex& vtx) {
|
|
|
|
|
const uint64_t component = vtx.getUser<VertexState>().component;
|
|
|
|
|
vtx.setUser<uint64_t>(component);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Sets DfgVertex::user<uint64_t>() 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)
|
|
|
|
|
static uint32_t apply(DfgGraph& dfg) {
|
|
|
|
|
return ColorStronglyConnectedComponents{dfg}.m_nonTrivialSCCs;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
uint32_t V3DfgPasses::colorStronglyConnectedComponents(DfgGraph& dfg) {
|
|
|
|
|
return ColorStronglyConnectedComponents::apply(dfg);
|
|
|
|
|
}
|