/* Sorting the nodes in the supergraph. Copyright (C) 2025-2026 Free Software Foundation, Inc. Contributed by David Malcolm . This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #define INCLUDE_SET #include "analyzer/common.h" #include "cgraph.h" #include "alloc-pool.h" #include "fibonacci_heap.h" #include "timevar.h" #include "analyzer/supergraph.h" #include "analyzer/analyzer-logging.h" #if ENABLE_ANALYZER namespace ana { /* Update m_nodes to be ORDERING. Update the m_id of all nodes to reflect the new orderding. This assumes that all nodes are in ORDERING; does not delete any underlying nodes. */ void supergraph::reorder_nodes_and_ids (const std::vector &ordering, logger *logger) { LOG_SCOPE (logger); m_nodes.truncate (0); for (size_t new_id = 0; new_id < ordering.size (); ++new_id) { supernode *snode = ordering[new_id]; if (logger) { if ((size_t)snode->m_id == new_id) logger->log ("SN %i is unchanged", snode->m_id); else logger->log ("old SN %i is now SN %li", snode->m_id, new_id); } m_nodes.safe_push (snode); snode->m_id = new_id; } m_next_snode_id = m_nodes.length (); } /* std::set::contains is C++20 onwards. */ template static bool set_contains_p (const std::set &s, T v) { return s.find (v) != s.end (); } namespace { class sorting_worklist { public: sorting_worklist () : m_queue (key_t (nullptr)) { } void add_node (supernode *n); supernode *take_next (logger *logger); bool contains_p (supernode *n) const; private: class key_t { public: key_t (supernode *snode) : m_snode (snode) {} bool operator< (const key_t &other) const { return cmp (*this, other) < 0; } bool operator== (const key_t &other) const { return cmp (*this, other) == 0; } bool operator> (const key_t &other) const { return !(*this == other || *this < other); } private: static int cmp (const key_t &ka, const key_t &kb); supernode *m_snode; }; bool already_seen_all_predecessors_p (const supernode *n, logger *logger) const; std::set m_set_of_ordered_nodes; std::set m_set_of_queued_nodes; typedef fibonacci_heap queue_t; queue_t m_queue; }; void sorting_worklist::add_node (supernode *n) { m_queue.insert ({n}, n); m_set_of_queued_nodes.insert (n); } supernode * sorting_worklist::take_next (logger *logger) { if (m_queue.empty ()) return nullptr; std::vector rejected; /* First, try to find a node for which all predecessors have been ordered. */ while (!m_queue.empty ()) { supernode *candidate = m_queue.extract_min (); // n shouldn't be already within the ordering gcc_assert (!set_contains_p (m_set_of_ordered_nodes, candidate)); if (logger) logger->log ("consider SN %i from worklist", candidate->m_id); if (already_seen_all_predecessors_p (candidate, logger)) { if (logger) logger->log ("all predecessors of SN %i seen; using it", candidate->m_id); for (auto r : rejected) add_node (r); m_set_of_ordered_nodes.insert (candidate); m_set_of_queued_nodes.erase (candidate); return candidate; } else rejected.push_back (candidate); } /* Otherwise, simply use the first node. */ for (auto r : rejected) add_node (r); supernode *n = m_queue.extract_min (); if (logger) logger->log ("using first in queue: SN %i", n->m_id); m_set_of_ordered_nodes.insert (n); m_set_of_queued_nodes.erase (n); return n; } bool sorting_worklist::contains_p (supernode *n) const { return (m_set_of_queued_nodes.find (n) != m_set_of_queued_nodes.end () || m_set_of_ordered_nodes.find (n) != m_set_of_ordered_nodes.end ()); } int sorting_worklist::key_t::cmp (const key_t &ka, const key_t &kb) { const supernode *snode_a = ka.m_snode; const supernode *snode_b = kb.m_snode; /* Sort by BB. */ if (int bb_cmp = snode_a->m_bb->index - snode_b->m_bb->index) return bb_cmp; /* Sort by existing id. */ return snode_a->m_id - snode_b->m_id; } bool sorting_worklist::already_seen_all_predecessors_p (const supernode *n, logger *logger) const { for (auto e : n->m_preds) if (!set_contains_p (m_set_of_ordered_nodes, e->m_src)) { if (logger) logger->log ("not yet ordered predecessor SN %i", e->m_src->m_id); return false; } return true; } static std::vector get_node_ordering (const supergraph &sg, logger *logger) { LOG_SCOPE (logger); std::vector ordering_vec; cgraph_node *cgnode; FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode) { function *fun = cgnode->get_fun (); sorting_worklist worklist; supernode *start_node = sg.get_node_for_function_entry (*fun); worklist.add_node (start_node); // Find the best node to add next in the ordering while (supernode *next = worklist.take_next (logger)) { gcc_assert (next); if (logger) logger->log ("next: SN: %i", next->m_id); ordering_vec.push_back (next); for (auto out_edge : next->m_succs) { supernode *dest_node = out_edge->m_dest; if (!worklist.contains_p (dest_node)) worklist.add_node (dest_node); } } } return ordering_vec; } } // anonymous namespace void supergraph::sort_nodes (logger *logger) { auto_timevar tv (TV_ANALYZER_SUPERGRAPH_SORTING); LOG_SCOPE (logger); const std::vector ordering = get_node_ordering (*this, logger); reorder_nodes_and_ids (ordering, logger); } } // namespace ana #endif /* #if ENABLE_ANALYZER */