+2020-03-27 David Malcolm <dmalcolm@redhat.com>
+
+ * doc/invoke.texi (-fdump-analyzer-supergraph): Document that this
+ now emits two .dot files.
+ * graphviz.cc (graphviz_out::begin_tr): Only emit a TR, not a TD.
+ (graphviz_out::end_tr): Only close a TR, not a TD.
+ (graphviz_out::begin_td): New.
+ (graphviz_out::end_td): New.
+ (graphviz_out::begin_trtd): New, replacing the old implementation
+ of graphviz_out::begin_tr.
+ (graphviz_out::end_tdtr): New, replacing the old implementation
+ of graphviz_out::end_tr.
+ * graphviz.h (graphviz_out::begin_td): New decl.
+ (graphviz_out::end_td): New decl.
+ (graphviz_out::begin_trtd): New decl.
+ (graphviz_out::end_tdtr): New decl.
+
2020-03-27 Richard Biener <rguenther@suse.de>
PR debug/94273
+2020-03-27 David Malcolm <dmalcolm@redhat.com>
+
+ * analyzer.h (class feasibility_problem): New forward decl.
+ * diagnostic-manager.cc (saved_diagnostic::saved_diagnostic):
+ Initialize new fields m_status, m_epath_length, and m_problem.
+ (saved_diagnostic::~saved_diagnostic): Delete m_problem.
+ (dedupe_candidate::dedupe_candidate): Convert "sd" param from a
+ const ref to a mutable ptr.
+ (dedupe_winners::add): Convert "sd" param from a const ref to a
+ mutable ptr. Record the length of the exploded_path. Record the
+ feasibility/infeasibility of sd into sd, capturing a
+ feasibility_problem when feasible_p fails, and storing it in sd.
+ (diagnostic_manager::emit_saved_diagnostics): Update for pass by
+ ptr rather than by const ref.
+ * diagnostic-manager.h (class saved_diagnostic): Add new enum
+ status. Add fields m_status, m_epath_length and m_problem.
+ (saved_diagnostic::set_feasible): New member function.
+ (saved_diagnostic::set_infeasible): New member function.
+ (saved_diagnostic::get_feasibility_problem): New accessor.
+ (saved_diagnostic::get_status): New accessor.
+ (saved_diagnostic::set_epath_length): New member function.
+ (saved_diagnostic::get_epath_length): New accessor.
+ * engine.cc: Include "gimple-pretty-print.h".
+ (exploded_path::feasible_p): Add OUT param and, if non-NULL, write
+ a new feasibility_problem to it on failure.
+ (viz_callgraph_node::dump_dot): Convert begin_tr calls to
+ begin_trtd. Convert end_tr calls to end_tdtr.
+ (class exploded_graph_annotator): New subclass of dot_annotator.
+ (impl_run_checkers): Add a second -fdump-analyzer-supergraph dump
+ after the analysis runs, using exploded_graph_annotator. dumping
+ to DUMP_BASE_NAME.supergraph-eg.dot.
+ * exploded-graph.h (exploded_node::get_dot_fillcolor): Make
+ public.
+ (exploded_path::feasible_p): Add OUT param.
+ (class feasibility_problem): New class.
+ * state-purge.cc (state_purge_annotator::add_node_annotations):
+ Return a bool, add a "within_table" param.
+ (print_vec_of_names): Convert begin_tr calls to begin_trtd.
+ Convert end_tr calls to end_tdtr.
+ (state_purge_annotator::add_stmt_annotations): Add "within_row"
+ param.
+ * state-purge.h ((state_purge_annotator::add_node_annotations):
+ Return a bool, add a "within_table" param.
+ (state_purge_annotator::add_stmt_annotations): Add "within_row"
+ param.
+ * supergraph.cc (supernode::dump_dot): Call add_node_annotations
+ twice: as before, passing false for "within_table", then again
+ with true when within the TABLE element. Convert some begin_tr
+ calls to begin_trtd, and some end_tr calls to end_tdtr.
+ Repeat each add_stmt_annotations call, distinguishing between
+ calls that add TRs and those that add TDs to an existing TR.
+ Add a call to add_after_node_annotations.
+ * supergraph.h (dot_annotator::add_node_annotations): Add a
+ "within_table" param.
+ (dot_annotator::add_stmt_annotations): Add a "within_row" param.
+ (dot_annotator::add_after_node_annotations): New vfunc.
+
2020-03-27 David Malcolm <dmalcolm@redhat.com>
* diagnostic-manager.cc (dedupe_winners::add): Show the
class exploded_graph;
class exploded_node;
class exploded_edge;
+class feasibility_problem;
class exploded_cluster;
class exploded_path;
class analysis_plan;
outlive that. */
m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL),
m_var (var), m_state (state),
- m_d (d), m_trailing_eedge (NULL)
+ m_d (d), m_trailing_eedge (NULL),
+ m_status (STATUS_NEW), m_epath_length (0), m_problem (NULL)
{
gcc_assert (m_stmt || m_stmt_finder);
{
delete m_stmt_finder;
delete m_d;
+ delete m_problem;
}
bool
public:
// has the exploded_path
dedupe_candidate (const shortest_exploded_paths &sp,
- const saved_diagnostic &sd)
- : m_epath (sp.get_shortest_path (sd.m_enode)),
+ saved_diagnostic *sd)
+ : m_epath (sp.get_shortest_path (sd->m_enode)),
m_num_dupes (0)
{
}
void add (logger *logger,
const shortest_exploded_paths &sp,
- const saved_diagnostic &sd)
+ saved_diagnostic *sd)
{
/* Build a dedupe_candidate for SD.
This uses SP to build an exploded_path. */
dedupe_candidate *dc = new dedupe_candidate (sp, sd);
+ sd->set_epath_length (dc->length ());
+
/* Verify that the epath is feasible.
State-merging means that not every path in the epath corresponds
to a feasible one w.r.t. states.
feasible paths within the egraph. */
if (logger)
logger->log ("considering %qs at EN: %i, SN: %i",
- sd.m_d->get_kind (), sd.m_enode->m_index,
- sd.m_snode->m_index);
+ sd->m_d->get_kind (), sd->m_enode->m_index,
+ sd->m_snode->m_index);
- if (!dc->get_path ().feasible_p (logger))
+ feasibility_problem *p = NULL;
+ if (!dc->get_path ().feasible_p (logger, &p))
{
if (logger)
logger->log ("rejecting %qs at EN: %i, SN: %i"
" due to infeasible path",
- sd.m_d->get_kind (), sd.m_enode->m_index,
- sd.m_snode->m_index);
+ sd->m_d->get_kind (), sd->m_enode->m_index,
+ sd->m_snode->m_index);
+ sd->set_infeasible (p);
delete dc;
return;
}
else
if (logger)
logger->log ("accepting %qs at EN: %i, SN: %i with feasible path",
- sd.m_d->get_kind (), sd.m_enode->m_index,
- sd.m_snode->m_index);
+ sd->m_d->get_kind (), sd->m_enode->m_index,
+ sd->m_snode->m_index);
+
+ sd->set_feasible ();
- dedupe_key *key = new dedupe_key (sd, dc->get_path ());
+ dedupe_key *key = new dedupe_key (*sd, dc->get_path ());
if (dedupe_candidate **slot = m_map.get (key))
{
if (logger)
int i;
saved_diagnostic *sd;
FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
- best_candidates.add (get_logger (), sp, *sd);
+ best_candidates.add (get_logger (), sp, sd);
/* For each dedupe-key, call emit_saved_diagnostic on the "best"
saved_diagnostic. */
class saved_diagnostic
{
public:
+ enum status
+ {
+ STATUS_NEW,
+ STATUS_INFEASIBLE_PATH,
+ STATUS_FEASIBLE_PATH
+ };
+
saved_diagnostic (const state_machine *sm,
const exploded_node *enode,
const supernode *snode, const gimple *stmt,
bool operator== (const saved_diagnostic &other) const;
+ void set_feasible ()
+ {
+ gcc_assert (m_status == STATUS_NEW);
+ m_status = STATUS_FEASIBLE_PATH;
+ }
+ void set_infeasible (feasibility_problem *p)
+ {
+ gcc_assert (m_status == STATUS_NEW);
+ m_status = STATUS_INFEASIBLE_PATH;
+ m_problem = p; // take ownership
+ }
+ const feasibility_problem *get_feasibility_problem () const
+ {
+ return m_problem;
+ }
+
+ enum status get_status () const { return m_status; }
+
+ void set_epath_length (unsigned length) { m_epath_length = length; }
+ unsigned get_epath_length () const { return m_epath_length; }
+
//private:
const state_machine *m_sm;
const exploded_node *m_enode;
private:
DISABLE_COPY_AND_ASSIGN (saved_diagnostic);
+
+ enum status m_status;
+ unsigned m_epath_length;
+ feasibility_problem *m_problem;
};
class path_builder;
#include "basic-block.h"
#include "gimple.h"
#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
#include "cgraph.h"
#include "digraph.h"
#include "analyzer/supergraph.h"
return m_edges[m_edges.length () - 1]->m_dest;
}
-/* Check state along this path, returning true if it is feasible. */
+/* Check state along this path, returning true if it is feasible.
+ If OUT is non-NULL, and the path is infeasible, write a new
+ feasibility_problem to *OUT. */
bool
-exploded_path::feasible_p (logger *logger) const
+exploded_path::feasible_p (logger *logger, feasibility_problem **out) const
{
LOG_SCOPE (logger);
logger->log ("rejecting due to region model");
model.dump_to_pp (logger->get_printer (), false);
}
+ if (out)
+ *out = new feasibility_problem (i, model, *eedge, last_stmt);
return false;
}
}
pp_string (pp, "<TABLE BORDER=\"0\">");
pp_write_text_to_stream (pp);
- gv->begin_tr ();
+ gv->begin_trtd ();
pp_printf (pp, "VCG: %i: %s", m_index, function_name (m_fun));
- gv->end_tr ();
+ gv->end_tdtr ();
pp_newline (pp);
- gv->begin_tr ();
+ gv->begin_trtd ();
pp_printf (pp, "supernodes: %i\n", m_num_supernodes);
- gv->end_tr ();
+ gv->end_tdtr ();
pp_newline (pp);
- gv->begin_tr ();
+ gv->begin_trtd ();
pp_printf (pp, "superedges: %i\n", m_num_superedges);
- gv->end_tr ();
+ gv->end_tdtr ();
pp_newline (pp);
if (args.m_eg)
if (enode->get_point ().get_function () == m_fun)
num_enodes++;
}
- gv->begin_tr ();
+ gv->begin_trtd ();
pp_printf (pp, "enodes: %i\n", num_enodes);
- gv->end_tr ();
+ gv->end_tdtr ();
pp_newline (pp);
// TODO: also show the per-callstring breakdown
}
if (num_enodes > 0)
{
- gv->begin_tr ();
+ gv->begin_trtd ();
cs->print (pp);
pp_printf (pp, ": %i\n", num_enodes);
pp_write_text_as_html_like_dot_to_stream (pp);
- gv->end_tr ();
+ gv->end_tdtr ();
}
}
if (data)
{
pp_newline (pp);
- gv->begin_tr ();
+ gv->begin_trtd ();
pp_printf (pp, "summaries: %i\n", data->m_summaries.length ());
pp_write_text_as_html_like_dot_to_stream (pp);
- gv->end_tr ();
+ gv->end_tdtr ();
}
}
free (filename);
}
+/* Subclass of dot_annotator for implementing
+ DUMP_BASE_NAME.supergraph-eg.dot, a post-analysis dump of the supergraph.
+
+ Annotate the supergraph nodes by printing the exploded nodes in concise
+ form within them, next to their pertinent statements where appropriate,
+ colorizing the exploded nodes based on sm-state.
+ Also show saved diagnostics within the exploded nodes, giving information
+ on whether they were feasible, and, if infeasible, where the problem
+ was. */
+
+class exploded_graph_annotator : public dot_annotator
+{
+public:
+ exploded_graph_annotator (const exploded_graph &eg)
+ : m_eg (eg)
+ {
+ /* Avoid O(N^2) by prepopulating m_enodes_per_snodes. */
+ unsigned i;
+ supernode *snode;
+ FOR_EACH_VEC_ELT (eg.get_supergraph ().m_nodes, i, snode)
+ m_enodes_per_snodes.safe_push (new auto_vec <exploded_node *> ());
+ exploded_node *enode;
+ FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode)
+ if (enode->get_supernode ())
+ m_enodes_per_snodes[enode->get_supernode ()->m_index]->safe_push (enode);
+ }
+
+ /* Show exploded nodes for BEFORE_SUPERNODE points before N. */
+ bool add_node_annotations (graphviz_out *gv, const supernode &n,
+ bool within_table)
+ const FINAL OVERRIDE
+ {
+ if (!within_table)
+ return false;
+ gv->begin_tr ();
+ pretty_printer *pp = gv->get_pp ();
+
+ gv->begin_td ();
+ pp_string (pp, "BEFORE");
+ gv->end_td ();
+
+ unsigned i;
+ exploded_node *enode;
+ bool had_enode = false;
+ FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
+ {
+ gcc_assert (enode->get_supernode () == &n);
+ const program_point &point = enode->get_point ();
+ if (point.get_kind () != PK_BEFORE_SUPERNODE)
+ continue;
+ print_enode (gv, enode);
+ had_enode = true;
+ }
+ if (!had_enode)
+ pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
+ pp_flush (pp);
+ gv->end_tr ();
+ return true;
+ }
+
+ /* Show exploded nodes for STMT. */
+ void add_stmt_annotations (graphviz_out *gv, const gimple *stmt,
+ bool within_row)
+ const FINAL OVERRIDE
+ {
+ if (!within_row)
+ return;
+ pretty_printer *pp = gv->get_pp ();
+
+ const supernode *snode
+ = m_eg.get_supergraph ().get_supernode_for_stmt (stmt);
+ unsigned i;
+ exploded_node *enode;
+ bool had_td = false;
+ FOR_EACH_VEC_ELT (*m_enodes_per_snodes[snode->m_index], i, enode)
+ {
+ const program_point &point = enode->get_point ();
+ if (point.get_kind () != PK_BEFORE_STMT)
+ continue;
+ if (point.get_stmt () != stmt)
+ continue;
+ print_enode (gv, enode);
+ had_td = true;
+ }
+ pp_flush (pp);
+ if (!had_td)
+ {
+ gv->begin_td ();
+ gv->end_td ();
+ }
+ }
+
+ /* Show exploded nodes for AFTER_SUPERNODE points after N. */
+ bool add_after_node_annotations (graphviz_out *gv, const supernode &n)
+ const FINAL OVERRIDE
+ {
+ gv->begin_tr ();
+ pretty_printer *pp = gv->get_pp ();
+
+ gv->begin_td ();
+ pp_string (pp, "AFTER");
+ gv->end_td ();
+
+ unsigned i;
+ exploded_node *enode;
+ FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
+ {
+ gcc_assert (enode->get_supernode () == &n);
+ const program_point &point = enode->get_point ();
+ if (point.get_kind () != PK_AFTER_SUPERNODE)
+ continue;
+ print_enode (gv, enode);
+ }
+ pp_flush (pp);
+ gv->end_tr ();
+ return true;
+ }
+
+private:
+ /* Concisely print a TD element for ENODE, showing the index, status,
+ and any saved_diagnostics at the enode. Colorize it to show sm-state.
+
+ Ideally we'd dump ENODE's state here, hidden behind some kind of
+ interactive disclosure method like a tooltip, so that the states
+ can be explored without overwhelming the graph.
+ However, I wasn't able to get graphviz/xdot to show tooltips on
+ individual elements within a HTML-like label. */
+ void print_enode (graphviz_out *gv, const exploded_node *enode) const
+ {
+ pretty_printer *pp = gv->get_pp ();
+ pp_printf (pp, "<TD BGCOLOR=\"%s\">",
+ enode->get_dot_fillcolor ());
+ pp_printf (pp, "<TABLE BORDER=\"0\">");
+ gv->begin_trtd ();
+ pp_printf (pp, "EN: %i", enode->m_index);
+ switch (enode->get_status ())
+ {
+ default:
+ gcc_unreachable ();
+ case exploded_node::STATUS_WORKLIST:
+ pp_string (pp, "(W)");
+ break;
+ case exploded_node::STATUS_PROCESSED:
+ break;
+ case exploded_node::STATUS_MERGER:
+ pp_string (pp, "(M)");
+ break;
+ }
+ gv->end_tdtr ();
+ /* Dump any saved_diagnostics at this enode. */
+ {
+ const diagnostic_manager &dm = m_eg.get_diagnostic_manager ();
+ for (unsigned i = 0; i < dm.get_num_diagnostics (); i++)
+ {
+ const saved_diagnostic *sd = dm.get_saved_diagnostic (i);
+ if (sd->m_enode == enode)
+ print_saved_diagnostic (gv, sd);
+ }
+ }
+ pp_printf (pp, "</TABLE>");
+ pp_printf (pp, "</TD>");
+ }
+
+ /* Print a TABLE element for SD, showing the kind, the length of the
+ exploded_path, whether the path was feasible, and if infeasible,
+ what the problem was. */
+ void print_saved_diagnostic (graphviz_out *gv,
+ const saved_diagnostic *sd) const
+ {
+ pretty_printer *pp = gv->get_pp ();
+ gv->begin_trtd ();
+ pp_printf (pp, "<TABLE BORDER=\"0\">");
+ gv->begin_tr ();
+ pp_string (pp, "<TD BGCOLOR=\"green\">");
+ pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
+ gv->end_tdtr ();
+ gv->begin_trtd ();
+ pp_printf (pp, "epath length: %i", sd->get_epath_length ());
+ gv->end_tdtr ();
+ switch (sd->get_status ())
+ {
+ default:
+ case saved_diagnostic::STATUS_NEW:
+ gcc_unreachable ();
+ break;
+ case saved_diagnostic::STATUS_INFEASIBLE_PATH:
+ {
+ gv->begin_trtd ();
+ pp_printf (pp, "INFEASIBLE");
+ gv->end_tdtr ();
+ const feasibility_problem *p = sd->get_feasibility_problem ();
+ gcc_assert (p);
+ gv->begin_trtd ();
+ pp_printf (pp, "at eedge %i: EN:%i -> EN:%i",
+ p->m_eedge_idx,
+ p->m_eedge.m_src->m_index,
+ p->m_eedge.m_dest->m_index);
+ pp_write_text_as_html_like_dot_to_stream (pp);
+ gv->end_tdtr ();
+ gv->begin_trtd ();
+ p->m_eedge.m_sedge->dump (pp);
+ pp_write_text_as_html_like_dot_to_stream (pp);
+ gv->end_tdtr ();
+ gv->begin_trtd ();
+ pp_gimple_stmt_1 (pp, p->m_last_stmt, 0, (dump_flags_t)0);
+ pp_write_text_as_html_like_dot_to_stream (pp);
+ gv->end_tdtr ();
+ /* Ideally we'd print p->m_model here; see the notes above about
+ tooltips. */
+ }
+ break;
+ case saved_diagnostic::STATUS_FEASIBLE_PATH:
+ gv->begin_trtd ();
+ pp_printf (pp, "FEASIBLE");
+ gv->end_tdtr ();
+ break;
+ }
+ pp_printf (pp, "</TABLE>");
+ gv->end_tdtr ();
+ }
+
+ const exploded_graph &m_eg;
+ auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes;
+};
+
/* Run the analysis "engine". */
void
if (flag_dump_analyzer_supergraph)
{
+ /* Dump supergraph pre-analysis. */
auto_timevar tv (TV_ANALYZER_DUMP);
char *filename = concat (dump_base_name, ".supergraph.dot", NULL);
supergraph::dump_args_t args ((enum supergraph_dot_flags)0, NULL);
if (flag_dump_analyzer_callgraph)
dump_callgraph (sg, &eg);
+ if (flag_dump_analyzer_supergraph)
+ {
+ /* Dump post-analysis form of supergraph. */
+ auto_timevar tv (TV_ANALYZER_DUMP);
+ char *filename = concat (dump_base_name, ".supergraph-eg.dot", NULL);
+ exploded_graph_annotator a (eg);
+ supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a);
+ sg.dump_dot (filename, args);
+ free (filename);
+ }
+
delete purge_map;
}
hashval_t hash () const { return m_ps.hash (); }
+ const char * get_dot_fillcolor () const;
void dump_dot (graphviz_out *gv, const dump_args_t &args)
const FINAL OVERRIDE;
void dump_dot_id (pretty_printer *pp) const;
private:
DISABLE_COPY_AND_ASSIGN (exploded_node);
- const char * get_dot_fillcolor () const;
-
/* The <program_point, program_state> pair. This is const, as it
is immutable once the exploded_node has been created. */
const point_and_state m_ps;
void dump (FILE *fp) const;
void dump () const;
- bool feasible_p (logger *logger) const;
+ bool feasible_p (logger *logger, feasibility_problem **out) const;
auto_vec<const exploded_edge *> m_edges;
};
+/* A reason why a particular exploded_path is infeasible. */
+
+class feasibility_problem
+{
+public:
+ feasibility_problem (unsigned eedge_idx,
+ const region_model &model,
+ const exploded_edge &eedge,
+ const gimple *last_stmt)
+ : m_eedge_idx (eedge_idx), m_model (model), m_eedge (eedge),
+ m_last_stmt (last_stmt)
+ {}
+
+ unsigned m_eedge_idx;
+ region_model m_model;
+ const exploded_edge &m_eedge;
+ const gimple *m_last_stmt;
+};
+
/* Finding the shortest exploded_path within an exploded_graph. */
typedef shortest_paths<eg_traits, exploded_path> shortest_exploded_paths;
Add an additional record showing which names are purged on entry
to the supernode N. */
-void
+bool
state_purge_annotator::add_node_annotations (graphviz_out *gv,
- const supernode &n) const
+ const supernode &n,
+ bool within_table) const
{
if (m_map == NULL)
- return;
+ return false;
+
+ if (within_table)
+ return false;
pretty_printer *pp = gv->get_pp ();
pp_string (pp, "\"];\n\n");
pp_flush (pp);
+ return false;
}
/* Print V to GV as a comma-separated list in braces within a <TR>,
pretty_printer *pp = gv->get_pp ();
tree name;
unsigned i;
- gv->begin_tr ();
+ gv->begin_trtd ();
pp_printf (pp, "%s: {", title);
FOR_EACH_VEC_ELT (v, i, name)
{
}
pp_printf (pp, "}");
pp_write_text_as_html_like_dot_to_stream (pp);
- gv->end_tr ();
+ gv->end_tdtr ();
pp_newline (pp);
}
void
state_purge_annotator::add_stmt_annotations (graphviz_out *gv,
- const gimple *stmt) const
+ const gimple *stmt,
+ bool within_row) const
{
+ if (within_row)
+ return;
+
if (m_map == NULL)
return;
public:
state_purge_annotator (const state_purge_map *map) : m_map (map) {}
- void add_node_annotations (graphviz_out *gv, const supernode &n)
+ bool add_node_annotations (graphviz_out *gv, const supernode &n, bool)
const FINAL OVERRIDE;
- void add_stmt_annotations (graphviz_out *gv, const gimple *stmt)
+ void add_stmt_annotations (graphviz_out *gv, const gimple *stmt,
+ bool within_row)
const FINAL OVERRIDE;
private:
pretty_printer *pp = gv->get_pp ();
if (args.m_node_annotator)
- args.m_node_annotator->add_node_annotations (gv, *this);
+ args.m_node_annotator->add_node_annotations (gv, *this, false);
gv->write_indent ();
dump_dot_id (pp);
bool had_row = false;
+ /* Give any annotator the chance to add its own per-node TR elements. */
+ if (args.m_node_annotator)
+ if (args.m_node_annotator->add_node_annotations (gv, *this, true))
+ had_row = true;
+
if (m_returning_call)
{
- gv->begin_tr ();
+ gv->begin_trtd ();
pp_string (pp, "returning call: ");
- gv->end_tr ();
+ gv->end_tdtr ();
gv->begin_tr ();
+ gv->begin_td ();
pp_gimple_stmt_1 (pp, m_returning_call, 0, (dump_flags_t)0);
pp_write_text_as_html_like_dot_to_stream (pp);
+ gv->end_td ();
+ /* Give any annotator the chance to add per-stmt TD elements to
+ this row. */
+ if (args.m_node_annotator)
+ args.m_node_annotator->add_stmt_annotations (gv, m_returning_call,
+ true);
gv->end_tr ();
+ /* Give any annotator the chance to add per-stmt TR elements. */
if (args.m_node_annotator)
- args.m_node_annotator->add_stmt_annotations (gv, m_returning_call);
+ args.m_node_annotator->add_stmt_annotations (gv, m_returning_call,
+ false);
pp_newline (pp);
had_row = true;
{
const gimple *stmt = gsi_stmt (gpi);
gv->begin_tr ();
+ gv->begin_td ();
pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
pp_write_text_as_html_like_dot_to_stream (pp);
+ gv->end_td ();
+ /* Give any annotator the chance to add per-phi TD elements to
+ this row. */
+ if (args.m_node_annotator)
+ args.m_node_annotator->add_stmt_annotations (gv, stmt, true);
gv->end_tr ();
+ /* Give any annotator the chance to add per-phi TR elements. */
if (args.m_node_annotator)
- args.m_node_annotator->add_stmt_annotations (gv, stmt);
+ args.m_node_annotator->add_stmt_annotations (gv, stmt, false);
pp_newline (pp);
had_row = true;
FOR_EACH_VEC_ELT (m_stmts, i, stmt)
{
gv->begin_tr ();
+ gv->begin_td ();
pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
pp_write_text_as_html_like_dot_to_stream (pp);
+ gv->end_td ();
+ /* Give any annotator the chance to add per-stmt TD elements to
+ this row. */
+ if (args.m_node_annotator)
+ args.m_node_annotator->add_stmt_annotations (gv, stmt, true);
gv->end_tr ();
+ /* Give any annotator the chance to add per-stmt TR elements. */
if (args.m_node_annotator)
- args.m_node_annotator->add_stmt_annotations (gv, stmt);
+ args.m_node_annotator->add_stmt_annotations (gv, stmt, false);
pp_newline (pp);
had_row = true;
}
+ /* Give any annotator the chance to add additional per-node TR elements
+ to the end of the TABLE. */
+ if (args.m_node_annotator)
+ if (args.m_node_annotator->add_after_node_annotations (gv, *this))
+ had_row = true;
+
/* Graphviz requires a TABLE element to have at least one TR
(and each TR to have at least one TD). */
if (!had_row)
{
public:
virtual ~dot_annotator () {}
- virtual void add_node_annotations (graphviz_out *gv ATTRIBUTE_UNUSED,
- const supernode &n ATTRIBUTE_UNUSED)
- const {}
+ virtual bool add_node_annotations (graphviz_out *gv ATTRIBUTE_UNUSED,
+ const supernode &n ATTRIBUTE_UNUSED,
+ bool within_table ATTRIBUTE_UNUSED)
+ const
+ {
+ return false;
+ }
virtual void add_stmt_annotations (graphviz_out *gv ATTRIBUTE_UNUSED,
- const gimple *stmt ATTRIBUTE_UNUSED)
+ const gimple *stmt ATTRIBUTE_UNUSED,
+ bool within_row ATTRIBUTE_UNUSED)
const {}
+ virtual bool add_after_node_annotations (graphviz_out *gv ATTRIBUTE_UNUSED,
+ const supernode &n ATTRIBUTE_UNUSED)
+ const
+ {
+ return false;
+ }
};
extern cgraph_edge *supergraph_call_edge (function *fun, gimple *stmt);
@item -fdump-analyzer-supergraph
@opindex fdump-analyzer-supergraph
-Dump a representation of the ``supergraph'' suitable for viewing with
-GraphViz to @file{@var{file}.supergraph.dot}. This shows all of the
+Dump representations of the ``supergraph'' suitable for viewing with
+GraphViz to @file{@var{file}.supergraph.dot} and to
+@file{@var{file}.supergraph-eg.dot}. These show all of the
control flow graphs in the program, with interprocedural edges for
-calls and returns.
+calls and returns. The second dump contains annotations showing nodes
+in the ``exploded graph'' and diagnostics associated with them.
@end table
pp_space (m_pp);
}
-/* Write the start of an HTML-like row via <TR><TD>, writing to the stream
+/* Write the start of an HTML-like row via <TR>, writing to the stream
so that followup text can be escaped. */
void
graphviz_out::begin_tr ()
+{
+ pp_string (m_pp, "<TR>");
+ pp_write_text_to_stream (m_pp);
+}
+
+/* Write the end of an HTML-like row via </TR>, writing to the stream
+ so that followup text can be escaped. */
+
+void
+graphviz_out::end_tr ()
+{
+ pp_string (m_pp, "</TR>");
+ pp_write_text_to_stream (m_pp);
+}
+
+/* Write the start of an HTML-like <TD>, writing to the stream
+ so that followup text can be escaped. */
+
+void
+graphviz_out::begin_td ()
+{
+ pp_string (m_pp, "<TD ALIGN=\"LEFT\">");
+ pp_write_text_to_stream (m_pp);
+}
+
+/* Write the end of an HTML-like </TD>, writing to the stream
+ so that followup text can be escaped. */
+
+void
+graphviz_out::end_td ()
+{
+ pp_string (m_pp, "</TD>");
+ pp_write_text_to_stream (m_pp);
+}
+
+/* Write the start of an HTML-like row via <TR><TD>, writing to the stream
+ so that followup text can be escaped. */
+
+void
+graphviz_out::begin_trtd ()
{
pp_string (m_pp, "<TR><TD ALIGN=\"LEFT\">");
pp_write_text_to_stream (m_pp);
so that followup text can be escaped. */
void
-graphviz_out::end_tr ()
+graphviz_out::end_tdtr ()
{
pp_string (m_pp, "</TD></TR>");
pp_write_text_to_stream (m_pp);
void begin_tr ();
void end_tr ();
+ void begin_td ();
+ void end_td ();
+
+ void begin_trtd ();
+ void end_tdtr ();
+
pretty_printer *get_pp () const { return m_pp; }
private:
+2020-03-27 David Malcolm <dmalcolm@redhat.com>
+
+ * gcc.dg/analyzer/dot-output.c: Check that
+ dot-output.c.supergraph-eg.dot is valid.
+
2020-03-27 Richard Biener <rguenther@suse.de>
PR debug/94273
/* { dg-final { dg-check-dot "dot-output.c.eg.dot" } } */
/* { dg-final { dg-check-dot "dot-output.c.state-purge.dot" } } */
/* { dg-final { dg-check-dot "dot-output.c.supergraph.dot" } } */
+/* { dg-final { dg-check-dot "dot-output.c.supergraph-eg.dot" } } */