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>
|
|
|
|
|
|
|
|
|
|
class ColorStronglyConnectedComponents final {
|
2025-09-02 23:21:24 +02:00
|
|
|
static_assert(sizeof(uint32_t[2]) == sizeof(uint64_t), "Incorrect ovverlay size");
|
2025-08-05 14:03:30 +02:00
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
// CONSTANTS
|
|
|
|
|
static constexpr uint32_t UNASSIGNED = std::numeric_limits<uint32_t>::max();
|
2025-08-05 14:03:30 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2025-09-02 23:21:24 +02:00
|
|
|
const DfgGraph& m_dfg; // The input graph
|
|
|
|
|
DfgUserMap<uint64_t>& m_map; // The result map we are computing - also used for traversal
|
2025-08-05 14:03:30 +02:00
|
|
|
uint32_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph
|
|
|
|
|
uint32_t m_index = 0; // Visitation index counter
|
2025-09-02 23:21:24 +02:00
|
|
|
std::vector<const DfgVertex*> m_stack; // The stack used by the algorithm
|
2025-08-05 14:03:30 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2025-09-02 23:21:24 +02:00
|
|
|
// Use the bottom 32-bit word as the component number
|
|
|
|
|
uint32_t& component(const DfgVertex& vtx) { //
|
|
|
|
|
return reinterpret_cast<uint32_t(&)[2]>(m_map[vtx])[0];
|
|
|
|
|
}
|
|
|
|
|
// Use the top 32-bit word as the visitation index
|
|
|
|
|
uint32_t& index(const DfgVertex& vtx) { //
|
|
|
|
|
return reinterpret_cast<uint32_t(&)[2]>(m_map[vtx])[1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visitColorSCCs(const DfgVertex& vtx) {
|
|
|
|
|
UDEBUGONLY(UASSERT_OBJ(index(vtx) == UNASSIGNED, &vtx, "Already visited vertex"););
|
2025-08-05 14:03:30 +02:00
|
|
|
|
|
|
|
|
// Visiting vertex
|
2025-09-02 23:21:24 +02:00
|
|
|
const size_t rootIndex = index(vtx) = ++m_index;
|
2025-08-05 14:03:30 +02:00
|
|
|
|
|
|
|
|
// Visit children
|
2025-09-02 23:21:24 +02:00
|
|
|
vtx.foreachSink([&](const DfgVertex& child) {
|
2025-08-05 14:03:30 +02:00
|
|
|
// If the child has not yet been visited, then continue traversal
|
2025-09-02 23:21:24 +02:00
|
|
|
if (index(child) == UNASSIGNED) visitColorSCCs(child);
|
2025-08-05 14:03:30 +02:00
|
|
|
// If the child is not in an SCC
|
2025-09-02 23:21:24 +02:00
|
|
|
if (component(child) == UNASSIGNED) {
|
|
|
|
|
if (index(vtx) > index(child)) index(vtx) = index(child);
|
2025-08-05 14:03:30 +02:00
|
|
|
}
|
2025-09-02 17:50:40 +02:00
|
|
|
return false;
|
2025-08-05 14:03:30 +02:00
|
|
|
});
|
|
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
if (index(vtx) == rootIndex) {
|
2025-08-05 14:03:30 +02:00
|
|
|
// This is the 'root' of an SCC
|
|
|
|
|
|
|
|
|
|
// A trivial SCC contains only a single vertex
|
2025-09-02 23:21:24 +02:00
|
|
|
const bool isTrivial = m_stack.empty() || index(*m_stack.back()) < rootIndex;
|
2025-08-05 14:03:30 +02:00
|
|
|
// 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;
|
2025-09-02 23:21:24 +02:00
|
|
|
component(vtx) = m_nonTrivialSCCs;
|
2025-08-05 14:03:30 +02:00
|
|
|
while (!m_stack.empty()) {
|
|
|
|
|
// Only higher nodes belong to the same SCC
|
2025-09-02 23:21:24 +02:00
|
|
|
if (index(*m_stack.back()) < rootIndex) break;
|
|
|
|
|
component(*m_stack.back()) = m_nonTrivialSCCs;
|
2025-08-05 14:03:30 +02:00
|
|
|
m_stack.pop_back();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph.
|
2025-09-02 23:21:24 +02:00
|
|
|
component(vtx) = 0;
|
2025-08-05 14:03:30 +02:00
|
|
|
}
|
|
|
|
|
} 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.
|
2025-09-02 23:21:24 +02:00
|
|
|
for (const DfgConst& vtx : m_dfg.constVertices()) {
|
|
|
|
|
index(vtx) = 0;
|
|
|
|
|
component(vtx) = 0;
|
|
|
|
|
}
|
2025-08-05 14:03:30 +02:00
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
// Initialize state of variable vertices
|
|
|
|
|
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
|
|
|
|
|
// If it has no inputs 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-09-02 23:21:24 +02:00
|
|
|
index(vtx) = 0;
|
|
|
|
|
component(vtx) = 0;
|
2025-08-05 14:03:30 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2025-09-02 23:21:24 +02:00
|
|
|
index(vtx) = UNASSIGNED;
|
|
|
|
|
component(vtx) = UNASSIGNED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize state of operation vertices
|
|
|
|
|
for (const DfgVertex& vtx : m_dfg.opVertices()) {
|
|
|
|
|
index(vtx) = UNASSIGNED;
|
|
|
|
|
component(vtx) = UNASSIGNED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start traversals through not yet visited variables
|
|
|
|
|
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
|
|
|
|
|
if (index(vtx) == UNASSIGNED) visitColorSCCs(vtx);
|
2025-08-05 14:03:30 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
// Start traversals through not yet visited operations
|
|
|
|
|
for (const DfgVertex& vtx : m_dfg.opVertices()) {
|
|
|
|
|
if (index(vtx) == UNASSIGNED) visitColorSCCs(vtx);
|
2025-08-05 14:03:30 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
explicit ColorStronglyConnectedComponents(const DfgGraph& dfg, DfgUserMap<uint64_t>& map)
|
|
|
|
|
: m_dfg{dfg}
|
|
|
|
|
, m_map{map} {
|
2025-08-05 14:03:30 +02:00
|
|
|
UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name());
|
|
|
|
|
// Yet another implementation of Pearce's algorithm.
|
|
|
|
|
colorSCCs();
|
2025-09-02 23:21:24 +02:00
|
|
|
// Re-assign mapped values
|
|
|
|
|
m_dfg.forEachVertex([&](const DfgVertex& vtx) {
|
|
|
|
|
const uint64_t c = component(vtx);
|
|
|
|
|
map[vtx] = c;
|
2025-08-05 14:03:30 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2025-09-02 23:21:24 +02:00
|
|
|
// See declaration of V3DfgPasses::colorStronglyConnectedComponents
|
|
|
|
|
static uint32_t apply(const DfgGraph& dfg, DfgUserMap<uint64_t>& map) {
|
|
|
|
|
return ColorStronglyConnectedComponents{dfg, map}.m_nonTrivialSCCs;
|
2025-08-05 14:03:30 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-02 23:21:24 +02:00
|
|
|
uint32_t V3DfgPasses::colorStronglyConnectedComponents(const DfgGraph& dfg,
|
|
|
|
|
DfgUserMap<uint64_t>& map) {
|
|
|
|
|
return ColorStronglyConnectedComponents::apply(dfg, map);
|
2025-08-05 14:03:30 +02:00
|
|
|
}
|