Commit 42c63313 by David Malcolm

analyzer: add new supergraph visualization

This patch extends -fdump-analyzer-supergraph so that rather than just
dumping a DUMP_BASE_NAME.supergraph.dot at the start of analysis, it
also dumps a DUMP_BASE_NAME.supergraph-eg.dot at the end.

The new dump file contains a concise dump of the exploded_graph,
organized with respect to the supergraph and its statements.  The
exploded nodes are colorized to show sm-state, but no other state
is shown.  Per exploded_node saved_diagnostics are also shown,
along with feasibility of the paths to reach them.

I've been finding this a useful way of tracking down issues in
exploded_graphs that are sufficiently large that the output of
-fdump-analyzer-exploded-graph becomes unwieldy.

The patch extends feasiblity-testing so that if the exploded_path
for a saved_diagnostic is found to be infeasible, the reason is
saved and written into the saved_diagnostic, so it can be shown in the
dump.  I've found this very useful when tracking down feasibility
issues.

I'm keeping the initial dump file as it's useful when tracking down
ICEs within the analyzer (which would stop the second dump file being
written).

gcc/analyzer/ChangeLog:
	* 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.

gcc/ChangeLog:
	* 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.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/dot-output.c: Check that
	dot-output.c.supergraph-eg.dot is valid.
parent 8f023575
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> 2020-03-27 Richard Biener <rguenther@suse.de>
PR debug/94273 PR debug/94273
......
2020-03-27 David Malcolm <dmalcolm@redhat.com> 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 * diagnostic-manager.cc (dedupe_winners::add): Show the
exploded_node index in the log messages. exploded_node index in the log messages.
(diagnostic_manager::emit_saved_diagnostics): Log a summary of (diagnostic_manager::emit_saved_diagnostics): Log a summary of
......
...@@ -64,6 +64,7 @@ class program_state; ...@@ -64,6 +64,7 @@ class program_state;
class exploded_graph; class exploded_graph;
class exploded_node; class exploded_node;
class exploded_edge; class exploded_edge;
class feasibility_problem;
class exploded_cluster; class exploded_cluster;
class exploded_path; class exploded_path;
class analysis_plan; class analysis_plan;
......
...@@ -77,7 +77,8 @@ saved_diagnostic::saved_diagnostic (const state_machine *sm, ...@@ -77,7 +77,8 @@ saved_diagnostic::saved_diagnostic (const state_machine *sm,
outlive that. */ outlive that. */
m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL), m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL),
m_var (var), m_state (state), 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); gcc_assert (m_stmt || m_stmt_finder);
...@@ -92,6 +93,7 @@ saved_diagnostic::~saved_diagnostic () ...@@ -92,6 +93,7 @@ saved_diagnostic::~saved_diagnostic ()
{ {
delete m_stmt_finder; delete m_stmt_finder;
delete m_d; delete m_d;
delete m_problem;
} }
bool bool
...@@ -257,8 +259,8 @@ class dedupe_candidate ...@@ -257,8 +259,8 @@ class dedupe_candidate
public: public:
// has the exploded_path // has the exploded_path
dedupe_candidate (const shortest_exploded_paths &sp, dedupe_candidate (const shortest_exploded_paths &sp,
const saved_diagnostic &sd) saved_diagnostic *sd)
: m_epath (sp.get_shortest_path (sd.m_enode)), : m_epath (sp.get_shortest_path (sd->m_enode)),
m_num_dupes (0) m_num_dupes (0)
{ {
} }
...@@ -344,12 +346,14 @@ public: ...@@ -344,12 +346,14 @@ public:
void add (logger *logger, void add (logger *logger,
const shortest_exploded_paths &sp, const shortest_exploded_paths &sp,
const saved_diagnostic &sd) saved_diagnostic *sd)
{ {
/* Build a dedupe_candidate for SD. /* Build a dedupe_candidate for SD.
This uses SP to build an exploded_path. */ This uses SP to build an exploded_path. */
dedupe_candidate *dc = new dedupe_candidate (sp, sd); dedupe_candidate *dc = new dedupe_candidate (sp, sd);
sd->set_epath_length (dc->length ());
/* Verify that the epath is feasible. /* Verify that the epath is feasible.
State-merging means that not every path in the epath corresponds State-merging means that not every path in the epath corresponds
to a feasible one w.r.t. states. to a feasible one w.r.t. states.
...@@ -359,26 +363,30 @@ public: ...@@ -359,26 +363,30 @@ public:
feasible paths within the egraph. */ feasible paths within the egraph. */
if (logger) if (logger)
logger->log ("considering %qs at EN: %i, SN: %i", logger->log ("considering %qs at EN: %i, SN: %i",
sd.m_d->get_kind (), sd.m_enode->m_index, sd->m_d->get_kind (), sd->m_enode->m_index,
sd.m_snode->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) if (logger)
logger->log ("rejecting %qs at EN: %i, SN: %i" logger->log ("rejecting %qs at EN: %i, SN: %i"
" due to infeasible path", " due to infeasible path",
sd.m_d->get_kind (), sd.m_enode->m_index, sd->m_d->get_kind (), sd->m_enode->m_index,
sd.m_snode->m_index); sd->m_snode->m_index);
sd->set_infeasible (p);
delete dc; delete dc;
return; return;
} }
else else
if (logger) if (logger)
logger->log ("accepting %qs at EN: %i, SN: %i with feasible path", logger->log ("accepting %qs at EN: %i, SN: %i with feasible path",
sd.m_d->get_kind (), sd.m_enode->m_index, sd->m_d->get_kind (), sd->m_enode->m_index,
sd.m_snode->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 (dedupe_candidate **slot = m_map.get (key))
{ {
if (logger) if (logger)
...@@ -495,7 +503,7 @@ diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg) ...@@ -495,7 +503,7 @@ diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
int i; int i;
saved_diagnostic *sd; saved_diagnostic *sd;
FOR_EACH_VEC_ELT (m_saved_diagnostics, i, 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" /* For each dedupe-key, call emit_saved_diagnostic on the "best"
saved_diagnostic. */ saved_diagnostic. */
......
...@@ -28,6 +28,13 @@ namespace ana { ...@@ -28,6 +28,13 @@ namespace ana {
class saved_diagnostic class saved_diagnostic
{ {
public: public:
enum status
{
STATUS_NEW,
STATUS_INFEASIBLE_PATH,
STATUS_FEASIBLE_PATH
};
saved_diagnostic (const state_machine *sm, saved_diagnostic (const state_machine *sm,
const exploded_node *enode, const exploded_node *enode,
const supernode *snode, const gimple *stmt, const supernode *snode, const gimple *stmt,
...@@ -38,6 +45,27 @@ public: ...@@ -38,6 +45,27 @@ public:
bool operator== (const saved_diagnostic &other) const; 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: //private:
const state_machine *m_sm; const state_machine *m_sm;
const exploded_node *m_enode; const exploded_node *m_enode;
...@@ -51,6 +79,10 @@ public: ...@@ -51,6 +79,10 @@ public:
private: private:
DISABLE_COPY_AND_ASSIGN (saved_diagnostic); DISABLE_COPY_AND_ASSIGN (saved_diagnostic);
enum status m_status;
unsigned m_epath_length;
feasibility_problem *m_problem;
}; };
class path_builder; class path_builder;
......
...@@ -179,6 +179,7 @@ class exploded_node : public dnode<eg_traits> ...@@ -179,6 +179,7 @@ class exploded_node : public dnode<eg_traits>
hashval_t hash () const { return m_ps.hash (); } 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) void dump_dot (graphviz_out *gv, const dump_args_t &args)
const FINAL OVERRIDE; const FINAL OVERRIDE;
void dump_dot_id (pretty_printer *pp) const; void dump_dot_id (pretty_printer *pp) const;
...@@ -269,8 +270,6 @@ class exploded_node : public dnode<eg_traits> ...@@ -269,8 +270,6 @@ class exploded_node : public dnode<eg_traits>
private: private:
DISABLE_COPY_AND_ASSIGN (exploded_node); DISABLE_COPY_AND_ASSIGN (exploded_node);
const char * get_dot_fillcolor () const;
/* The <program_point, program_state> pair. This is const, as it /* The <program_point, program_state> pair. This is const, as it
is immutable once the exploded_node has been created. */ is immutable once the exploded_node has been created. */
const point_and_state m_ps; const point_and_state m_ps;
...@@ -857,11 +856,30 @@ public: ...@@ -857,11 +856,30 @@ public:
void dump (FILE *fp) const; void dump (FILE *fp) const;
void dump () 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; 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. */ /* Finding the shortest exploded_path within an exploded_graph. */
typedef shortest_paths<eg_traits, exploded_path> shortest_exploded_paths; typedef shortest_paths<eg_traits, exploded_path> shortest_exploded_paths;
......
...@@ -419,12 +419,16 @@ state_purge_per_ssa_name::process_point (const function_point &point, ...@@ -419,12 +419,16 @@ state_purge_per_ssa_name::process_point (const function_point &point,
Add an additional record showing which names are purged on entry Add an additional record showing which names are purged on entry
to the supernode N. */ to the supernode N. */
void bool
state_purge_annotator::add_node_annotations (graphviz_out *gv, state_purge_annotator::add_node_annotations (graphviz_out *gv,
const supernode &n) const const supernode &n,
bool within_table) const
{ {
if (m_map == NULL) if (m_map == NULL)
return; return false;
if (within_table)
return false;
pretty_printer *pp = gv->get_pp (); pretty_printer *pp = gv->get_pp ();
...@@ -455,6 +459,7 @@ state_purge_annotator::add_node_annotations (graphviz_out *gv, ...@@ -455,6 +459,7 @@ state_purge_annotator::add_node_annotations (graphviz_out *gv,
pp_string (pp, "\"];\n\n"); pp_string (pp, "\"];\n\n");
pp_flush (pp); pp_flush (pp);
return false;
} }
/* Print V to GV as a comma-separated list in braces within a <TR>, /* Print V to GV as a comma-separated list in braces within a <TR>,
...@@ -469,7 +474,7 @@ print_vec_of_names (graphviz_out *gv, const char *title, ...@@ -469,7 +474,7 @@ print_vec_of_names (graphviz_out *gv, const char *title,
pretty_printer *pp = gv->get_pp (); pretty_printer *pp = gv->get_pp ();
tree name; tree name;
unsigned i; unsigned i;
gv->begin_tr (); gv->begin_trtd ();
pp_printf (pp, "%s: {", title); pp_printf (pp, "%s: {", title);
FOR_EACH_VEC_ELT (v, i, name) FOR_EACH_VEC_ELT (v, i, name)
{ {
...@@ -479,7 +484,7 @@ print_vec_of_names (graphviz_out *gv, const char *title, ...@@ -479,7 +484,7 @@ print_vec_of_names (graphviz_out *gv, const char *title,
} }
pp_printf (pp, "}"); pp_printf (pp, "}");
pp_write_text_as_html_like_dot_to_stream (pp); pp_write_text_as_html_like_dot_to_stream (pp);
gv->end_tr (); gv->end_tdtr ();
pp_newline (pp); pp_newline (pp);
} }
...@@ -490,8 +495,12 @@ print_vec_of_names (graphviz_out *gv, const char *title, ...@@ -490,8 +495,12 @@ print_vec_of_names (graphviz_out *gv, const char *title,
void void
state_purge_annotator::add_stmt_annotations (graphviz_out *gv, 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) if (m_map == NULL)
return; return;
......
...@@ -151,10 +151,11 @@ class state_purge_annotator : public dot_annotator ...@@ -151,10 +151,11 @@ class state_purge_annotator : public dot_annotator
public: public:
state_purge_annotator (const state_purge_map *map) : m_map (map) {} 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; 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; const FINAL OVERRIDE;
private: private:
......
...@@ -458,7 +458,7 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const ...@@ -458,7 +458,7 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const
pretty_printer *pp = gv->get_pp (); pretty_printer *pp = gv->get_pp ();
if (args.m_node_annotator) 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 (); gv->write_indent ();
dump_dot_id (pp); dump_dot_id (pp);
...@@ -470,19 +470,33 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const ...@@ -470,19 +470,33 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const
bool had_row = false; 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) if (m_returning_call)
{ {
gv->begin_tr (); gv->begin_trtd ();
pp_string (pp, "returning call: "); pp_string (pp, "returning call: ");
gv->end_tr (); gv->end_tdtr ();
gv->begin_tr (); gv->begin_tr ();
gv->begin_td ();
pp_gimple_stmt_1 (pp, m_returning_call, 0, (dump_flags_t)0); pp_gimple_stmt_1 (pp, m_returning_call, 0, (dump_flags_t)0);
pp_write_text_as_html_like_dot_to_stream (pp); 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 (); gv->end_tr ();
/* Give any annotator the chance to add per-stmt TR elements. */
if (args.m_node_annotator) 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); pp_newline (pp);
had_row = true; had_row = true;
...@@ -508,12 +522,19 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const ...@@ -508,12 +522,19 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const
{ {
const gimple *stmt = gsi_stmt (gpi); const gimple *stmt = gsi_stmt (gpi);
gv->begin_tr (); gv->begin_tr ();
gv->begin_td ();
pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0); pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
pp_write_text_as_html_like_dot_to_stream (pp); 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 (); gv->end_tr ();
/* Give any annotator the chance to add per-phi TR elements. */
if (args.m_node_annotator) 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); pp_newline (pp);
had_row = true; had_row = true;
...@@ -525,17 +546,30 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const ...@@ -525,17 +546,30 @@ supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const
FOR_EACH_VEC_ELT (m_stmts, i, stmt) FOR_EACH_VEC_ELT (m_stmts, i, stmt)
{ {
gv->begin_tr (); gv->begin_tr ();
gv->begin_td ();
pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0); pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
pp_write_text_as_html_like_dot_to_stream (pp); 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 (); gv->end_tr ();
/* Give any annotator the chance to add per-stmt TR elements. */
if (args.m_node_annotator) 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); pp_newline (pp);
had_row = true; 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 /* Graphviz requires a TABLE element to have at least one TR
(and each TR to have at least one TD). */ (and each TR to have at least one TD). */
if (!had_row) if (!had_row)
......
...@@ -569,12 +569,23 @@ class dot_annotator ...@@ -569,12 +569,23 @@ class dot_annotator
{ {
public: public:
virtual ~dot_annotator () {} virtual ~dot_annotator () {}
virtual void add_node_annotations (graphviz_out *gv ATTRIBUTE_UNUSED, virtual bool add_node_annotations (graphviz_out *gv ATTRIBUTE_UNUSED,
const supernode &n ATTRIBUTE_UNUSED) const supernode &n ATTRIBUTE_UNUSED,
const {} bool within_table ATTRIBUTE_UNUSED)
const
{
return false;
}
virtual void add_stmt_annotations (graphviz_out *gv ATTRIBUTE_UNUSED, 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 {} 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); extern cgraph_edge *supergraph_call_edge (function *fun, gimple *stmt);
......
...@@ -8607,10 +8607,12 @@ The graph is written to @file{@var{file}.state-purge.dot}. ...@@ -8607,10 +8607,12 @@ The graph is written to @file{@var{file}.state-purge.dot}.
@item -fdump-analyzer-supergraph @item -fdump-analyzer-supergraph
@opindex fdump-analyzer-supergraph @opindex fdump-analyzer-supergraph
Dump a representation of the ``supergraph'' suitable for viewing with Dump representations of the ``supergraph'' suitable for viewing with
GraphViz to @file{@var{file}.supergraph.dot}. This shows all of the 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 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 @end table
...@@ -79,12 +79,52 @@ graphviz_out::write_indent () ...@@ -79,12 +79,52 @@ graphviz_out::write_indent ()
pp_space (m_pp); 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. */ so that followup text can be escaped. */
void void
graphviz_out::begin_tr () 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_string (m_pp, "<TR><TD ALIGN=\"LEFT\">");
pp_write_text_to_stream (m_pp); pp_write_text_to_stream (m_pp);
} }
...@@ -93,7 +133,7 @@ graphviz_out::begin_tr () ...@@ -93,7 +133,7 @@ graphviz_out::begin_tr ()
so that followup text can be escaped. */ so that followup text can be escaped. */
void void
graphviz_out::end_tr () graphviz_out::end_tdtr ()
{ {
pp_string (m_pp, "</TD></TR>"); pp_string (m_pp, "</TD></TR>");
pp_write_text_to_stream (m_pp); pp_write_text_to_stream (m_pp);
......
...@@ -43,6 +43,12 @@ class graphviz_out { ...@@ -43,6 +43,12 @@ class graphviz_out {
void begin_tr (); void begin_tr ();
void end_tr (); void end_tr ();
void begin_td ();
void end_td ();
void begin_trtd ();
void end_tdtr ();
pretty_printer *get_pp () const { return m_pp; } pretty_printer *get_pp () const { return m_pp; }
private: 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> 2020-03-27 Richard Biener <rguenther@suse.de>
PR debug/94273 PR debug/94273
......
...@@ -47,3 +47,4 @@ int test_2 (void) ...@@ -47,3 +47,4 @@ int test_2 (void)
/* { dg-final { dg-check-dot "dot-output.c.eg.dot" } } */ /* { 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.state-purge.dot" } } */
/* { dg-final { dg-check-dot "dot-output.c.supergraph.dot" } } */ /* { dg-final { dg-check-dot "dot-output.c.supergraph.dot" } } */
/* { dg-final { dg-check-dot "dot-output.c.supergraph-eg.dot" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment