/* 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 "diagnostic-metadata.h" #include "diagnostic-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 (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 diagnostics::digraphs::lazy_digraph { public: lazy_passes_graph (const ::gcc::pass_manager &pass_manager_) : m_pass_manager (pass_manager_) { } std::unique_ptr create_digraph () const final override { auto g = std::make_unique (); 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 (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 (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; } private: const ::gcc::pass_manager &m_pass_manager; }; static void report_diag_with_graphs (location_t loc) { class my_lazy_digraphs : public diagnostics::digraphs::lazy_digraphs { public: using diagnostic_graph = diagnostics::digraphs::digraph; using diagnostic_node = diagnostics::digraphs::node; using diagnostic_edge = diagnostics::digraphs::edge; std::unique_ptr>> create_digraphs () const final override { auto graphs = std::make_unique>> (); graphs->push_back (make_test_graph ("foo")); graphs->push_back (make_test_graph ("bar")); return graphs; } private: std::unique_ptr make_test_graph (const char *desc) const { auto g = std::make_unique (); g->set_description (desc); auto a = std::make_unique (*g, "a"); auto b = std::make_unique (*g, "b"); #define KEY_PREFIX "/placeholder-prefix/" b->set_attr (KEY_PREFIX, "color", "red"); #undef KEY_PREFIX auto c = std::make_unique (*g, "c"); c->set_label ("I am a node label"); auto e = std::make_unique (*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); diagnostic_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; }