blob: 43ac3fc01fa5062cc3bad4c10bdaa45442a9a7dc [file] [log] [blame]
/* "Supergraph" classes that combine CFGs and callgraph into one digraph.
Copyright (C) 2019-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/>. */
#ifndef GCC_ANALYZER_SUPERGRAPH_H
#define GCC_ANALYZER_SUPERGRAPH_H
#include "ordered-hash-map.h"
#include "cfg.h"
#include "basic-block.h"
#include "cfgloop.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "digraph.h"
#include "except.h"
#include "analyzer/ops.h"
using namespace ana;
namespace ana {
/* Forward decls, using indentation to show inheritance. */
class supergraph;
class supernode;
class superedge;
class supercluster;
class dot_annotator;
class logger;
/* Flags for controlling the appearance of .dot dumps. */
enum supergraph_dot_flags
{
SUPERGRAPH_DOT_SHOW_BBS = (1 << 0)
};
/* A traits struct describing the family of node, edge and digraph
classes for supergraphs. */
struct supergraph_traits
{
typedef supernode node_t;
typedef superedge edge_t;
typedef supergraph graph_t;
struct dump_args_t
{
dump_args_t (enum supergraph_dot_flags flags,
const dot_annotator *node_annotator,
const exploded_graph *eg)
: m_flags (flags),
m_node_annotator (node_annotator),
m_eg (eg)
{}
enum supergraph_dot_flags m_flags;
const dot_annotator *m_node_annotator;
const exploded_graph *m_eg;
};
typedef supercluster cluster_t;
};
/* A class to manage the setting and restoring of statement uids. */
class saved_uids
{
public:
void make_uid_unique (gimple *stmt);
void restore_uids () const;
private:
auto_vec<std::pair<gimple *, unsigned> > m_old_stmt_uids;
};
/* A directed graph class representing the users's code,
with nodes representing locations within functions, and
edges representing transitions between them.
For historical reasons we call this the "supergraph", although
this is now a misnomer as we no longer add callgraph edges to this
graph: the edges within the supergraph are purely intraprocedural:
either linking consecutive stmts in a basic block, or linking
basic blocks (corresponding to CFG edges). However, all functions
are within the same graph. */
class supergraph : public digraph<supergraph_traits>
{
public:
supergraph (region_model_manager &mgr, logger *logger);
~supergraph ();
supernode *get_node_for_function_entry (const function &fun) const
{
return get_initial_node_for_block (ENTRY_BLOCK_PTR_FOR_FN (&fun));
}
supernode *get_node_for_function_exit (const function &fun) const
{
return get_final_node_for_block (EXIT_BLOCK_PTR_FOR_FN (&fun));
}
supernode *get_initial_node_for_block (basic_block bb) const
{
return *const_cast <bb_to_node_t &> (m_bb_to_initial_node).get (bb);
}
supernode *get_final_node_for_block (basic_block bb) const
{
return *const_cast <bb_to_node_t &> (m_bb_to_final_node).get (bb);
}
supernode *get_supernode_for_stmt (const gimple *stmt) const
{
auto iter = m_node_for_stmt.find (stmt);
gcc_assert (iter != m_node_for_stmt.end ());
return iter->second;
}
superedge *get_superedge_for_phis (::edge cfg_edge) const
{
auto iter = m_edges_for_phis.find (cfg_edge);
if (iter != m_edges_for_phis.end ())
return iter->second;
return nullptr;
}
void dump_dot_to_pp (pretty_printer *pp, const dump_args_t &) const;
void dump_dot_to_file (FILE *fp, const dump_args_t &) const;
void dump_dot (const char *path, const dump_args_t &) const;
std::unique_ptr<json::object> to_json () const;
int num_nodes () const { return m_nodes.length (); }
int num_edges () const { return m_edges.length (); }
unsigned get_num_snodes (const function *fun) const
{
function_to_num_snodes_t &map
= const_cast <function_to_num_snodes_t &>(m_function_to_num_snodes);
return *map.get (fun);
}
void log_stats (logger *logger) const;
void delete_nodes (const std::set<supernode *> &snodes);
/* Implemented in supergraph-fixup-locations.cc. */
void fixup_locations (logger *);
/* Implemented in supergraph-simplify.cc. */
void simplify (logger *);
/* Implemented in supergraph-sorting.cc. */
void sort_nodes (logger *logger);
supernode *add_node (function *fun, basic_block bb, logger *logger);
private:
gimple *
populate_for_basic_block (basic_block bb,
function *fun,
logger *logger);
void
add_sedges_for_cfg_edge (supernode *src,
supernode *dest,
::edge e,
gimple *control_stmt,
region_model_manager &mgr,
logger *logger);
void dump_dot_to_gv_for_loop (graphviz_out &gv, const dump_args_t &,
class loop *, function *) const;
void dump_dot_to_gv_for_bb (graphviz_out &gv, const dump_args_t &,
basic_block, function *) const;
/* Implemented in supergraph-sorting.cc. */
void
reorder_nodes_and_ids (const std::vector<supernode *> &ordering,
logger *logger);
/* Data. */
typedef ordered_hash_map<basic_block, supernode *> bb_to_node_t;
bb_to_node_t m_bb_to_initial_node;
bb_to_node_t m_bb_to_final_node;
std::map<const gimple *, supernode *> m_node_for_stmt;
std::map<::edge, superedge *> m_edges_for_phis;
typedef hash_map<const function *, unsigned> function_to_num_snodes_t;
function_to_num_snodes_t m_function_to_num_snodes;
saved_uids m_stmt_uids;
/* Hand out unique IDs to supernodes, to make it easier
to track them when deleting/splitting etc (they're easier to
think about when debugging than pointer values). */
int m_next_snode_id;
std::vector<supernode *> m_snode_by_id;
};
/* A node within a supergraph. */
class supernode : public dnode<supergraph_traits>
{
public:
supernode (function *fun, basic_block bb, int id)
: m_fun (fun), m_bb (bb),
m_loc (UNKNOWN_LOCATION),
m_stmt_loc (UNKNOWN_LOCATION),
m_id (id),
m_original_id (id),
m_label (NULL_TREE),
m_preserve_p (false),
m_state_merger_node (false)
{}
function *get_function () const { return m_fun; }
bool entry_p () const
{
return m_bb == ENTRY_BLOCK_PTR_FOR_FN (m_fun);
}
bool exit_p () const
{
return m_bb == EXIT_BLOCK_PTR_FOR_FN (m_fun);
}
void dump_dot (graphviz_out *gv, const dump_args_t &args) const override;
void dump_dot_id (pretty_printer *pp) const;
void print (pretty_printer *pp) const
{
pp_printf (pp, "SN %i", m_id);
}
std::unique_ptr<json::object> to_json () const;
location_t get_location () const { return m_loc; }
tree get_label () const { return m_label; }
bool preserve_p () const { return m_preserve_p; }
function * const m_fun;
const basic_block m_bb;
location_t m_loc;
location_t m_stmt_loc; // for debugging
int m_id; /* unique within the supergraph as a whole. */
const int m_original_id;
tree m_label;
bool m_preserve_p;
bool m_state_merger_node;
};
/* An edge within the supergraph, with an optional operation.
Edges can be CFG edges or edges between statements, or persist
in order to give more opportunities for state-merging when
building the exploded graph. */
class superedge : public dedge<supergraph_traits>
{
public:
superedge (supernode *src, supernode *dest,
std::unique_ptr<operation> op,
::edge cfg_edge)
: dedge<supergraph_traits> (src, dest),
m_op (std::move (op)),
m_cfg_edge (cfg_edge)
{
/* All edges are intraprocedural. */
gcc_assert (m_src->get_function ()
== m_dest->get_function ());
}
virtual ~superedge () {}
void dump (pretty_printer *pp) const;
void dump () const;
void dump_dot (graphviz_out *gv, const dump_args_t &args)
const final override;
const operation *get_op () const { return m_op.get (); }
void set_op (std::unique_ptr<operation> op) { m_op = std::move (op); }
void dump_label_to_pp (pretty_printer *pp,
bool user_facing) const;
std::unique_ptr<json::object> to_json () const;
const supernode *get_dest_snode () const { return m_dest; }
::edge get_any_cfg_edge () const { return m_cfg_edge; }
bool preserve_p () const;
label_text get_description (bool user_facing) const;
bool
supports_bulk_merge_p () const;
private:
std::unique_ptr<operation> m_op;
::edge m_cfg_edge;
};
/* An ID representing an expression at a callsite:
either a parameter index, or the return value (or unknown). */
class callsite_expr
{
public:
callsite_expr () : m_val (-1) {}
static callsite_expr from_zero_based_param (int idx)
{
return callsite_expr (idx + 1);
}
static callsite_expr from_return_value ()
{
return callsite_expr (0);
}
bool param_p () const
{
return m_val > 0;
}
bool return_value_p () const
{
return m_val == 0;
}
private:
callsite_expr (int val) : m_val (val) {}
int m_val; /* 1-based parm, 0 for return value, or -1 for "unknown". */
};
/* Base class for adding additional content to the .dot output
for a supergraph. */
class dot_annotator
{
public:
virtual ~dot_annotator () = default;
virtual void
add_node_annotations (graphviz_out *gv ATTRIBUTE_UNUSED,
const supernode &n ATTRIBUTE_UNUSED) const
{
// no-op
}
virtual void
add_extra_objects (graphviz_out *gv ATTRIBUTE_UNUSED) const
{
// no-op
}
};
} // namespace ana
#endif /* GCC_ANALYZER_SUPERGRAPH_H */