blob: fb0b5763e68c1b7e0f7a7b38a94bb279b6363245 [file] [log] [blame]
/* Sorting the nodes in the supergraph.
Copyright (C) 2025 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
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
<http://www.gnu.org/licenses/>. */
#define INCLUDE_SET
#include "analyzer/common.h"
#include "cgraph.h"
#include "alloc-pool.h"
#include "fibonacci_heap.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<supernode *> &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 <typename T>
static bool
set_contains_p (const std::set<T> &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<supernode *> m_set_of_ordered_nodes;
std::set<supernode *> m_set_of_queued_nodes;
typedef fibonacci_heap<key_t, supernode> 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<supernode *> 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<supernode *>
get_node_ordering (const supergraph &sg,
logger *logger)
{
LOG_SCOPE (logger);
std::vector<supernode *> 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)
{
LOG_SCOPE (logger);
const std::vector<supernode *> ordering = get_node_ordering (*this, logger);
reorder_nodes_and_ids (ordering, logger);
}
} // namespace ana
#endif /* #if ENABLE_ANALYZER */