gcc/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_grap...

283 lines
7.1 KiB
C++

/* This plugin exercises diagnostic graphs.
We emit an error with a pair of digraphs associated with it,
and a global digraph showing the optimization passes. */
#define INCLUDE_MAP
#define INCLUDE_STRING
#define INCLUDE_VECTOR
#include "gcc-plugin.h"
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "stringpool.h"
#include "toplev.h"
#include "basic-block.h"
#include "hash-table.h"
#include "vec.h"
#include "ggc.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
#include "is-a.h"
#include "tree.h"
#include "tree-pass.h"
#include "intl.h"
#include "plugin-version.h"
#include "diagnostic.h"
#include "context.h"
#include "gcc-rich-location.h"
#include "diagnostics/metadata.h"
#include "diagnostics/digraphs.h"
#include "pass_manager.h"
int plugin_is_GPL_compatible;
const pass_data pass_data_test_graph_emission =
{
GIMPLE_PASS, /* type */
"test_graph_emission", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_ssa, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_test_graph_emission : public gimple_opt_pass
{
public:
pass_test_graph_emission(gcc::context *ctxt)
: gimple_opt_pass(pass_data_test_graph_emission, ctxt)
{}
/* opt_pass methods: */
bool gate (function *) { return true; }
virtual unsigned int execute (function *);
}; // class pass_test_graph_emission
/* Determine if STMT is a call with NUM_ARGS arguments to a function
named FUNCNAME.
If so, return STMT as a gcall *. Otherwise return NULL. */
static gcall *
check_for_named_call (gimple *stmt,
const char *funcname, unsigned int num_args)
{
gcc_assert (funcname);
gcall *call = dyn_cast <gcall *> (stmt);
if (!call)
return NULL;
tree fndecl = gimple_call_fndecl (call);
if (!fndecl)
return NULL;
if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
return NULL;
if (gimple_call_num_args (call) != num_args)
{
error_at (stmt->location, "expected number of args: %i (got %i)",
num_args, gimple_call_num_args (call));
return NULL;
}
return call;
}
class lazy_passes_graph : public lazily_created<diagnostics::digraphs::digraph>
{
public:
lazy_passes_graph (const ::gcc::pass_manager &pass_manager_)
: m_pass_manager (pass_manager_)
{
}
private:
std::unique_ptr<diagnostics::digraphs::digraph>
create_object () const final override
{
auto g = std::make_unique<diagnostics::digraphs::digraph> ();
g->set_description ("Optimization Passes");
#define DEF_PASS_LIST(NAME) \
add_top_level_pass_list (*g, #NAME, m_pass_manager.NAME);
GCC_PASS_LISTS
#undef DEF_PASS_LIST
return g;
}
void
add_top_level_pass_list (diagnostics::digraphs::digraph &g,
const char *pass_list_name,
const opt_pass *p) const
{
gcc_assert (p);
auto n = std::make_unique<diagnostics::digraphs::node> (g, pass_list_name);
n->set_label (pass_list_name);
add_child_pass (g, *n, *p);
g.add_node (std::move (n));
}
diagnostics::digraphs::node &
add_child_pass (diagnostics::digraphs::digraph &g,
diagnostics::digraphs::node &parent_node,
const opt_pass &p) const
{
std::string node_label;
std::string node_id;
if (p.static_pass_number > 0 )
{
node_label = std::to_string (p.static_pass_number) + "_" + p.name;
node_id = node_label;
}
else
{
node_label = std::string (p.name);
pretty_printer pp;
pp_printf (&pp, "%s_%p", p.name, &p);
node_id = pp_formatted_text (&pp);
}
auto n
= std::make_unique<diagnostics::digraphs::node> (g,
std::move (node_id));
n->set_label (node_label.c_str ());
diagnostics::digraphs::node &result = *n;
parent_node.add_child (std::move (n));
// TODO: add attrs for things like type, properties_*, etc
if (p.sub)
{
auto &other_node = add_child_pass (g, parent_node, *p.sub);
g.add_edge (nullptr, result, other_node, "sub");
}
if (p.next)
{
auto &other_node = add_child_pass (g, parent_node, *p.next);
g.add_edge (nullptr, result, other_node, "next");
}
return result;
}
const ::gcc::pass_manager &m_pass_manager;
};
static void
report_diag_with_graphs (location_t loc)
{
class my_lazy_digraphs : public diagnostics::metadata::lazy_digraphs
{
public:
using diagnostic_graph = diagnostics::digraphs::digraph;
using diagnostic_node = diagnostics::digraphs::node;
using diagnostic_edge = diagnostics::digraphs::edge;
std::unique_ptr<std::vector<std::unique_ptr<diagnostic_graph>>>
create_object () const final override
{
auto graphs
= std::make_unique<std::vector<std::unique_ptr<diagnostic_graph>>> ();
graphs->push_back (make_test_graph ("foo"));
graphs->push_back (make_test_graph ("bar"));
return graphs;
}
private:
std::unique_ptr<diagnostic_graph>
make_test_graph (const char *desc) const
{
auto g = std::make_unique<diagnostic_graph> ();
g->set_description (desc);
auto a = std::make_unique<diagnostic_node> (*g, "a");
auto b = std::make_unique<diagnostic_node> (*g, "b");
const json::string_property color ("/placeholder-prefix/color");
b->set_property (color, "red");
auto c = std::make_unique<diagnostic_node> (*g, "c");
c->set_label ("I am a node label");
auto e = std::make_unique<diagnostic_edge> (*g, "my-edge", *a, *c);
e->set_label ("I am an edge label");
g->add_edge (std::move (e));
g->add_node (std::move (a));
b->add_child (std::move (c));
g->add_node (std::move (b));
return g;
}
};
gcc_rich_location rich_loc (loc);
diagnostics::metadata meta;
my_lazy_digraphs ldg;
meta.set_lazy_digraphs (&ldg);
error_meta (&rich_loc, meta,
"this is a placeholder error, with graphs");
}
/* Exercise diagnostic graph emission. */
unsigned int
pass_test_graph_emission::execute (function *fun)
{
gimple_stmt_iterator gsi;
basic_block bb;
FOR_EACH_BB_FN (bb, fun)
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
if (gcall *call = check_for_named_call (stmt, "here", 0))
report_diag_with_graphs (gimple_location (call));
}
return 0;
}
int
plugin_init (struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version)
{
struct register_pass_info pass_info;
const char *plugin_name = plugin_info->base_name;
int argc = plugin_info->argc;
struct plugin_argument *argv = plugin_info->argv;
if (!plugin_default_version_check (version, &gcc_version))
return 1;
pass_info.pass = new pass_test_graph_emission (g);
pass_info.reference_pass_name = "ssa";
pass_info.ref_pass_instance_number = 1;
pass_info.pos_op = PASS_POS_INSERT_AFTER;
register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
&pass_info);
gcc_assert (::g->get_passes ());
global_dc->report_global_digraph (lazy_passes_graph (*::g->get_passes ()));
return 0;
}