mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			357 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *  yosys -- Yosys Open SYnthesis Suite
 | 
						|
 *
 | 
						|
 *  Copyright (C) 2024  Jannis Harder <jix@yosyshq.com> <me@jix.one>
 | 
						|
 *
 | 
						|
 *  Permission to use, copy, modify, and/or distribute this software for any
 | 
						|
 *  purpose with or without fee is hereby granted, provided that the above
 | 
						|
 *  copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
						|
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
						|
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#ifndef TOPO_SCC_H
 | 
						|
#define TOPO_SCC_H
 | 
						|
 | 
						|
#include "kernel/yosys.h"
 | 
						|
 | 
						|
YOSYS_NAMESPACE_BEGIN
 | 
						|
 | 
						|
class SigCellGraph {
 | 
						|
public:
 | 
						|
    typedef int node_type;
 | 
						|
 | 
						|
    struct successor_enumerator {
 | 
						|
        std::vector<std::pair<int, int>>::const_iterator current, end;
 | 
						|
        bool finished() const { return current == end; }
 | 
						|
        node_type next() {
 | 
						|
            log_assert(!finished());
 | 
						|
            node_type result = current->second;
 | 
						|
            ++current;
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    struct node_enumerator {
 | 
						|
        int current, end;
 | 
						|
        bool finished() const { return current == end; }
 | 
						|
        node_type next() {
 | 
						|
            log_assert(!finished());
 | 
						|
            node_type result = current;
 | 
						|
            ++current;
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
private:
 | 
						|
    idict<RTLIL::Cell *> cell_ids;
 | 
						|
    idict<RTLIL::SigBit> sig_ids;
 | 
						|
    std::vector<std::pair<int, int>> edges;
 | 
						|
    std::vector<std::pair<int, int>> edge_ranges;
 | 
						|
    std::vector<int> indices_;
 | 
						|
    int offset;
 | 
						|
    bool computed = false;
 | 
						|
 | 
						|
    void compute() {
 | 
						|
        offset = GetSize(sig_ids);
 | 
						|
        edge_ranges.clear();
 | 
						|
        indices_.clear();
 | 
						|
        indices_.resize(GetSize(sig_ids) + GetSize(cell_ids), -1);
 | 
						|
 | 
						|
        std::sort(edges.begin(), edges.end());
 | 
						|
        auto last = std::unique(edges.begin(), edges.end());
 | 
						|
        edges.erase(last, edges.end());
 | 
						|
        auto edge = edges.begin();
 | 
						|
        auto edge_end = edges.end();
 | 
						|
        int range_begin = 0;
 | 
						|
        for (int node = -offset, node_end = GetSize(cell_ids); node != node_end; ++node) {
 | 
						|
            while (edge != edge_end && edge->first <= node)
 | 
						|
                ++edge;
 | 
						|
            int range_end = edge - edges.begin();
 | 
						|
            edge_ranges.emplace_back(std::make_pair(range_begin, range_end));
 | 
						|
            range_begin = range_end;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
public:
 | 
						|
    node_type node(RTLIL::Cell *cell) { return cell_ids(cell); }
 | 
						|
    node_type node(SigBit const &bit) { return ~sig_ids(bit); }
 | 
						|
 | 
						|
    bool is_cell(node_type node) { return node >= 0; }
 | 
						|
    bool is_sig(node_type node) { return node < 0; }
 | 
						|
 | 
						|
    Cell *cell(node_type node) { return node >= 0 ? cell_ids[node] : nullptr; }
 | 
						|
    SigBit sig(node_type node) { return node < 0 ? sig_ids[~node] : SigBit(); }
 | 
						|
 | 
						|
    template<typename Src, typename Dst>
 | 
						|
    void add_edge(Src &&src, Dst &&dst) {
 | 
						|
        computed = false;
 | 
						|
        node_type src_node = node(std::forward<Src>(src));
 | 
						|
        node_type dst_node = node(std::forward<Dst>(dst));
 | 
						|
        edges.emplace_back(std::make_pair(src_node, dst_node));
 | 
						|
    }
 | 
						|
 | 
						|
    node_enumerator enumerate_nodes() {
 | 
						|
        if (!computed) compute();
 | 
						|
        return {-GetSize(sig_ids), GetSize(cell_ids)};
 | 
						|
    }
 | 
						|
 | 
						|
    successor_enumerator enumerate_successors(node_type const &node) const {
 | 
						|
        auto range = edge_ranges[node + offset];
 | 
						|
        return {edges.begin() + range.first, edges.begin() + range.second};
 | 
						|
    }
 | 
						|
 | 
						|
    int &dfs_index(node_type const &node) {
 | 
						|
        return indices_[node + offset];
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
class IntGraph {
 | 
						|
public:
 | 
						|
    typedef int node_type;
 | 
						|
 | 
						|
    struct successor_enumerator {
 | 
						|
        std::vector<std::pair<int, int>>::const_iterator current, end;
 | 
						|
        bool finished() const { return current == end; }
 | 
						|
        node_type next() {
 | 
						|
            log_assert(!finished());
 | 
						|
            node_type result = current->second;
 | 
						|
            ++current;
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    struct node_enumerator {
 | 
						|
        int current, end;
 | 
						|
        bool finished() const { return current == end; }
 | 
						|
        node_type next() {
 | 
						|
            log_assert(!finished());
 | 
						|
            node_type result = current;
 | 
						|
            ++current;
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
private:
 | 
						|
    std::vector<std::pair<int, int>> edges;
 | 
						|
    std::vector<std::pair<int, int>> edge_ranges;
 | 
						|
    std::vector<int> indices_;
 | 
						|
    bool computed = false;
 | 
						|
 | 
						|
    void compute() {
 | 
						|
        edge_ranges.clear();
 | 
						|
 | 
						|
        int node_end = 0;
 | 
						|
        for (auto const &edge : edges)
 | 
						|
            node_end = std::max(node_end, std::max(edge.first, edge.second) + 1);
 | 
						|
        indices_.clear();
 | 
						|
        indices_.resize(node_end, -1);
 | 
						|
 | 
						|
        std::sort(edges.begin(), edges.end());
 | 
						|
        auto last = std::unique(edges.begin(), edges.end());
 | 
						|
        edges.erase(last, edges.end());
 | 
						|
        auto edge = edges.begin();
 | 
						|
        auto edge_end = edges.end();
 | 
						|
        int range_begin = 0;
 | 
						|
        for (int node = 0; node != node_end; ++node) {
 | 
						|
            while (edge != edge_end && edge->first <= node)
 | 
						|
                ++edge;
 | 
						|
            int range_end = edge - edges.begin();
 | 
						|
            edge_ranges.emplace_back(std::make_pair(range_begin, range_end));
 | 
						|
            range_begin = range_end;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
public:
 | 
						|
    void add_edge(int src, int dst) {
 | 
						|
        log_assert(src >= 0);
 | 
						|
        log_assert(dst >= 0);
 | 
						|
        computed = false;
 | 
						|
        edges.emplace_back(std::make_pair(src, dst));
 | 
						|
    }
 | 
						|
 | 
						|
    node_enumerator enumerate_nodes() {
 | 
						|
        if (!computed) compute();
 | 
						|
        return {0, GetSize(indices_)};
 | 
						|
    }
 | 
						|
 | 
						|
    successor_enumerator enumerate_successors(int node) const {
 | 
						|
        auto range = edge_ranges[node];
 | 
						|
        return {edges.begin() + range.first, edges.begin() + range.second};
 | 
						|
    }
 | 
						|
 | 
						|
    int &dfs_index(node_type const &node) {
 | 
						|
        return indices_[node];
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template<typename G, typename ComponentCallback>
 | 
						|
class TopoSortedSccs
 | 
						|
{
 | 
						|
    typedef typename G::node_enumerator node_enumerator;
 | 
						|
    typedef typename G::successor_enumerator successor_enumerator;
 | 
						|
    typedef typename G::node_type node_type;
 | 
						|
 | 
						|
    struct dfs_entry {
 | 
						|
        node_type node;
 | 
						|
        successor_enumerator successors;
 | 
						|
        int lowlink;
 | 
						|
 | 
						|
        dfs_entry(node_type node, successor_enumerator successors, int lowlink) :
 | 
						|
            node(node), successors(successors), lowlink(lowlink)
 | 
						|
        {}
 | 
						|
    };
 | 
						|
 | 
						|
    G &graph;
 | 
						|
    ComponentCallback component;
 | 
						|
 | 
						|
    std::vector<dfs_entry> dfs_stack;
 | 
						|
    std::vector<node_type> component_stack;
 | 
						|
    int next_index = 0;
 | 
						|
 | 
						|
public:
 | 
						|
    TopoSortedSccs(G &graph, ComponentCallback component)
 | 
						|
    : graph(graph), component(component) {}
 | 
						|
 | 
						|
    // process all sources (nodes without a successor)
 | 
						|
    TopoSortedSccs &process_sources() {
 | 
						|
        node_enumerator nodes = graph.enumerate_nodes();
 | 
						|
        while (!nodes.finished()) {
 | 
						|
            node_type node = nodes.next();
 | 
						|
            successor_enumerator successors = graph.enumerate_successors(node);
 | 
						|
            if (successors.finished())
 | 
						|
            {
 | 
						|
                graph.dfs_index(node) = next_index;
 | 
						|
                next_index++;
 | 
						|
                component_stack.push_back(node);
 | 
						|
                component(component_stack.data(), component_stack.data() + 1);
 | 
						|
                component_stack.clear();
 | 
						|
                graph.dfs_index(node) = INT_MAX;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return *this;
 | 
						|
    }
 | 
						|
 | 
						|
    // process all remaining nodes in the graph
 | 
						|
    TopoSortedSccs &process_all() {   
 | 
						|
        node_enumerator nodes = graph.enumerate_nodes();
 | 
						|
        // iterate over all nodes to ensure we process the whole graph
 | 
						|
        while (!nodes.finished())
 | 
						|
            process(nodes.next());
 | 
						|
        return *this;
 | 
						|
    }
 | 
						|
 | 
						|
    // process all nodes that are reachable from a given start node
 | 
						|
    TopoSortedSccs &process(node_type node) {
 | 
						|
        // only start a new search if the node wasn't visited yet
 | 
						|
        if (graph.dfs_index(node) >= 0)
 | 
						|
            return *this;
 | 
						|
        while (true) {
 | 
						|
            // at this point we're visiting the node for the first time during
 | 
						|
            // the DFS search
 | 
						|
 | 
						|
            // we record the timestamp of when we first visited the node as the
 | 
						|
            // dfs_index
 | 
						|
            int lowlink = next_index;
 | 
						|
            next_index++;
 | 
						|
            graph.dfs_index(node) = lowlink;
 | 
						|
 | 
						|
            // and we add the node to the component stack where it will remain
 | 
						|
            // until all nodes of the component containing this node are popped
 | 
						|
            component_stack.push_back(node);
 | 
						|
 | 
						|
            // then we start iterating over the successors of this node
 | 
						|
            successor_enumerator successors = graph.enumerate_successors(node);
 | 
						|
            while (true) {
 | 
						|
                if (successors.finished()) {
 | 
						|
                    // when we processed all successors, i.e. when we visited
 | 
						|
                    // the complete DFS subtree rooted at the current node, we
 | 
						|
                    // first check whether the current node is a SCC root
 | 
						|
                    //
 | 
						|
                    // (why this check identifies SCC roots is out of scope for
 | 
						|
                    // this comment, see other material on Tarjan's SCC
 | 
						|
                    // algorithm)
 | 
						|
                    if (lowlink == graph.dfs_index(node)) {
 | 
						|
                        // the SCC containing the current node is at the top of
 | 
						|
                        // the component stack, with the current node at the bottom
 | 
						|
                        int current = GetSize(component_stack);
 | 
						|
                        do {
 | 
						|
                            --current;
 | 
						|
                        } while (component_stack[current] != node);
 | 
						|
 | 
						|
                        // we invoke the callback with a pointer range of the
 | 
						|
                        // nodes in the SCC
 | 
						|
 | 
						|
                        node_type *stack_ptr = component_stack.data();
 | 
						|
                        node_type *component_begin = stack_ptr + current;
 | 
						|
                        node_type *component_end = stack_ptr + component_stack.size();
 | 
						|
 | 
						|
                        // note that we allow the callback to permute the nodes
 | 
						|
                        // in this range as well as to modify dfs_index of the
 | 
						|
                        // nodes in the SCC.
 | 
						|
                        component(component_begin, component_end);
 | 
						|
 | 
						|
                        // by setting the dfs_index of all already emitted
 | 
						|
                        // nodes to INT_MAX, we don't need a separate check for
 | 
						|
                        // whether successor nodes are still on the component
 | 
						|
                        // stack before updating the lowlink value
 | 
						|
                        for (; component_begin != component_end; ++component_begin)
 | 
						|
                            graph.dfs_index(*component_begin) = INT_MAX;
 | 
						|
                        component_stack.resize(current);
 | 
						|
                    }
 | 
						|
 | 
						|
                    // after checking for a completed SCC the DFS either
 | 
						|
                    // continues the search at the parent node or returns to
 | 
						|
                    // the outer loop if we already are at the root node.
 | 
						|
                    if (dfs_stack.empty())
 | 
						|
                        return *this;
 | 
						|
                    auto &dfs_top = dfs_stack.back();
 | 
						|
 | 
						|
                    node = dfs_top.node;
 | 
						|
                    successors = std::move(dfs_top.successors);
 | 
						|
 | 
						|
                    // the parent's lowlink is updated when returning
 | 
						|
                    lowlink = min(lowlink, dfs_top.lowlink);
 | 
						|
                    dfs_stack.pop_back();
 | 
						|
                    // continue checking the remaining successors of the parent node.
 | 
						|
                } else {
 | 
						|
                    node_type succ = successors.next();
 | 
						|
                    if (graph.dfs_index(succ) < 0) {
 | 
						|
                        // if the successor wasn't visted yet, the DFS recurses
 | 
						|
                        // into the successor
 | 
						|
 | 
						|
                        // we save the state for this node and make the
 | 
						|
                        // successor the current node.
 | 
						|
                        dfs_stack.emplace_back(node, std::move(successors), lowlink);
 | 
						|
                        node = succ;
 | 
						|
 | 
						|
                        // this break gets us to the section corresponding to
 | 
						|
                        // the function entry in the recursive version
 | 
						|
                        break;
 | 
						|
                    } else {
 | 
						|
                        // the textbook version guards this update with a check
 | 
						|
                        // whether the successor is still on the component
 | 
						|
                        // stack. If the successor node was already visisted
 | 
						|
                        // but is not on the component stack, it must be part
 | 
						|
                        // of an already emitted SCC. We can avoid this check
 | 
						|
                        // by setting the DFS index of all nodes in a SCC to
 | 
						|
                        // INT_MAX when the SCC is emitted.
 | 
						|
                        lowlink = min(lowlink, graph.dfs_index(succ));
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
YOSYS_NAMESPACE_END
 | 
						|
 | 
						|
#endif
 |