analyzer: eliminate dedupe_candidate [PR96374]

In gcc/analyzer/diagnostic-manager.cc the code partitions
saved_diagnostic instances by dedupe_key, and tries to find the "best"
saved_diagnostic for each dedupe_key.

Ideally we would find the shortest feasible path for each
saved_diagnostic and pick the winner in each deduplication set.

Currently we merely approximate that by finding the shortest path for
each saved_diagnostic, and checking to see if it feasible, rejecting
the saved_diagnostic if it is not.  The "shortest path, or nothing if
it's infeasible" is not the same as the "shortest feasible path", and
this leads to false negatives, where we reject valid diagnostics,
tracked as PR analyzer/96374.

I have been attempting various fixes for this, but in doing so I
found that the existing structure of the code makes things unnecessarily
awkward: each dedupe_set had a a dedupe_candidate which stored the
best epath for that set, creating it from the shortest path when that
dedupe_candidate was constructed.

This patch eliminates the dedupe_candidate, instead storing the best
epath for each saved_diagnostic within the saved_diagnostic itself,
along with any feasibility_problem, and eliminating a redundant "status"
field.  The logic for finding the best epath is moved to a new
epath_finder::get_best_epath subroutine, introducing an epath_finder
class to give a place to cache state.

This patch merely copies over the existing logic to
epath_finder::get_best_epath, so no functional change is intended,
but the patch simplifies the logic and makes it much easier to
experiment with alternate implementations as I try to fix
PR analyzer/96374.

I attempted another version of this patch in which I added a dedupe_set
class and partitioned saved_diagnostics into them as the diagnostics were
added, but in this earlier iteration of the patch there were regressions
e.g. from gcc.dg/analyzer/zlib-4.c where 4 deduplication sets became 3.
The issue was that the deduplication logic needs source locations, which
need gimple statements, and the stmt_finder needs epaths to run.  Finding
the epaths needs the full egraph (as opposed to the egraph in its state
at the time when the diagnostic is saved).  Hence the partitioning needs to
happen after the egraph is fully explored.  I backed up the earlier patch
kit to:
  https://dmalcolm.fedorapeople.org/gcc/2021-02-23/feasibility-v0.3-relative-to-72d78655a91bb2f89ac4432cfd6374380d6f9987/

gcc/analyzer/ChangeLog:
	PR analyzer/96374
	* diagnostic-manager.cc (class epath_finder): New.
	(epath_finder::get_best_epath): New.
	(saved_diagnostic::saved_diagnostic): Update for replacement of
	m_state and m_epath_length with m_best_epath.
	(saved_diagnostic::~saved_diagnostic): Delete m_best_epath.
	(saved_diagnostic::to_json): Update "path_length" to be optional.
	(saved_diagnostic::calc_best_epath): New, based on
	dedupe_winners::add and parts of dedupe_key::dedupe_key.
	(saved_diagnostic::get_epath_length): New.
	(saved_diagnostic::add_duplicate): New.
	(dedupe_key::dedupe_key): Drop epath param.  Move invocation of
	stmt_finder to saved_diagnostic::calc_best_epath.
	(class dedupe_candidate): Delete.
	(class dedupe_hash_map_traits): Update to use saved_diagnotic *
	rather than dedupe_candidate * as the value_type/compare_type.
	(dedupe_winners::~dedupe_winners): Don't delete the values.
	(dedupe_winners::add): Convert param from shortest_exploded_paths to
	epath_finder.  Drop "eg" param.  Drop dedupe_candidate, moving
	path generation and feasiblity checking to
	epath_finder::get_best_epath.  Update winner-selection for move
	of epaths from dedupe_candidate to saved_diagnostic.
	(dedupe_winners::emit_best):  Update for removal of class
	dedupe_candidate.
	(dedupe_winners::map_t): Update to use saved_diagnotic * rather
	than dedupe_candidate * as the value_type/compare_type.
	(diagnostic_manager::emit_saved_diagnostics): Move
	shortest_exploded_paths instance into epath_finder and pass that
	around instead.
	(diagnostic_manager::emit_saved_diagnostic): Drop epath, stmt
	and num_dupes params, instead getting these from the
	saved_diagnostic.  Use correct location in inform_n call.
	* diagnostic-manager.h (class epath_finder): New forward decl.
	(saved_diagnostic::status): Drop enum.
	(saved_diagnostic::set_feasible): Drop.
	(saved_diagnostic::set_infeasible): Drop.
	(saved_diagnostic::get_status): Drop.
	(saved_diagnostic::calc_best_epath): New decl.
	(saved_diagnostic::get_best_epath): New decl.
	(saved_diagnostic::get_epath_length): New decl.
	(saved_diagnostic::set_epath_length): Drop.
	(saved_diagnostic::get_epath_length): Drop inline implementation.
	(saved_diagnostic::add_duplicate): New.
	(saved_diagnostic::get_num_dupes): New.
	(saved_diagnostic::m_d): Document ownership.
	(saved_diagnostic::m_trailing_eedge): Make const.
	(saved_diagnostic::m_status): Drop field.
	(saved_diagnostic::m_epath_length): Drop field.
	(saved_diagnostic::m_best_epath): New field.
	(saved_diagnostic::m_problem): Document ownership.
	(saved_diagnostic::m_duplicates): New field.
	(diagnostic_manager::emit_saved_diagnostic): Drop params epath,
	stmt, and num_dupes.
	* engine.cc (exploded_graph_annotator::print_saved_diagnostic):
	Update for changes to saved_diagnostic class.
	* exploded-graph.h (exploded_path::feasible_p): Drop unused
	overloaded decl.
This commit is contained in:
David Malcolm 2021-02-25 20:00:12 -05:00
parent 6bd409cfc8
commit a505fad4dd
4 changed files with 244 additions and 206 deletions

View File

@ -64,6 +64,104 @@ along with GCC; see the file COPYING3. If not see
namespace ana { namespace ana {
/* State for finding the shortest feasible exploded_path for a
saved_diagnostic.
This is shared between all diagnostics, so that we avoid repeating work. */
class epath_finder
{
public:
epath_finder (const exploded_graph &eg)
: m_eg (eg),
m_sep (eg, eg.get_origin ())
{
}
logger *get_logger () const { return m_eg.get_logger (); }
exploded_path *get_best_epath (const exploded_node *enode,
const char *desc,
feasibility_problem **out_problem);
private:
const exploded_graph &m_eg;
shortest_exploded_paths m_sep;
};
/* class epath_finder. */
/* Get the "best" exploded_path for reaching ENODE from the origin,
returning ownership of it to the caller.
Ideally we want to report the shortest feasible path.
Return NULL if we could not find a feasible path
(when flag_analyzer_feasibility is true).
If flag_analyzer_feasibility is false, then simply return the
shortest path.
Use DESC when logging.
Write any feasiblity_problem to *OUT_PROBLEM. */
exploded_path *
epath_finder::get_best_epath (const exploded_node *enode,
const char *desc,
feasibility_problem **out_problem)
{
logger *logger = get_logger ();
LOG_SCOPE (logger);
unsigned snode_idx = enode->get_supernode ()->m_index;
if (logger)
logger->log ("considering %qs at EN: %i, SN: %i",
desc, enode->m_index, snode_idx);
/* State-merging means that not every path in the egraph corresponds
to a feasible one w.r.t. states.
We want to find the shortest feasible path from the origin to ENODE
in the egraph.
As a crude approximation to this, we find the shortest path, and
determine if it is feasible. This could introduce false negatives,
as there could be longer feasible paths within the egraph.
(PR analyzer/96374). */
exploded_path *epath = new exploded_path (m_sep.get_shortest_path (enode));
if (epath->feasible_p (logger, out_problem, m_eg.get_engine (), &m_eg))
{
if (logger)
logger->log ("accepting %qs at EN: %i, SN: %i with feasible path",
desc, enode->m_index,
snode_idx);
}
else
{
if (flag_analyzer_feasibility)
{
if (logger)
logger->log ("rejecting %qs at EN: %i, SN: %i"
" due to infeasible path",
desc, enode->m_index,
snode_idx);
delete epath;
return NULL;
}
else
{
if (logger)
logger->log ("accepting %qs at EN: %i, SN: %i"
" despite infeasible path (due to %qs)",
desc, enode->m_index,
snode_idx,
"-fno-analyzer-feasibility");
}
}
return epath;
}
/* class saved_diagnostic. */ /* class saved_diagnostic. */
/* saved_diagnostic's ctor. /* saved_diagnostic's ctor.
@ -83,7 +181,7 @@ saved_diagnostic::saved_diagnostic (const state_machine *sm,
m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL), m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL),
m_var (var), m_sval (sval), m_state (state), m_var (var), m_sval (sval), 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) m_best_epath (NULL), m_problem (NULL)
{ {
gcc_assert (m_stmt || m_stmt_finder); gcc_assert (m_stmt || m_stmt_finder);
@ -98,6 +196,7 @@ saved_diagnostic::~saved_diagnostic ()
{ {
delete m_stmt_finder; delete m_stmt_finder;
delete m_d; delete m_d;
delete m_best_epath;
delete m_problem; delete m_problem;
} }
@ -121,7 +220,7 @@ saved_diagnostic::operator== (const saved_diagnostic &other) const
"snode": int, "snode": int,
"sval": optional str, "sval": optional str,
"state": optional str, "state": optional str,
"path_length": int, "path_length": optional int,
"pending_diagnostic": str}. */ "pending_diagnostic": str}. */
json::object * json::object *
@ -137,7 +236,8 @@ saved_diagnostic::to_json () const
sd_obj->set ("sval", m_sval->to_json ()); sd_obj->set ("sval", m_sval->to_json ());
if (m_state) if (m_state)
sd_obj->set ("state", m_state->to_json ()); sd_obj->set ("state", m_state->to_json ());
sd_obj->set ("path_length", new json::integer_number (m_epath_length)); if (m_best_epath)
sd_obj->set ("path_length", new json::integer_number (get_epath_length ()));
sd_obj->set ("pending_diagnostic", new json::string (m_d->get_kind ())); sd_obj->set ("pending_diagnostic", new json::string (m_d->get_kind ()));
/* We're not yet JSONifying the following fields: /* We're not yet JSONifying the following fields:
@ -152,6 +252,64 @@ saved_diagnostic::to_json () const
return sd_obj; return sd_obj;
} }
/* Use PF to find the best exploded_path for this saved_diagnostic,
and store it in m_best_epath.
If m_stmt is still NULL, use m_stmt_finder on the epath to populate
m_stmt.
Return true if a best path was found. */
bool
saved_diagnostic::calc_best_epath (epath_finder *pf)
{
logger *logger = pf->get_logger ();
LOG_SCOPE (logger);
delete m_best_epath;
delete m_problem;
m_problem = NULL;
m_best_epath = pf->get_best_epath (m_enode, m_d->get_kind (),
&m_problem);
/* Handle failure to find a feasible path. */
if (m_best_epath == NULL)
{
gcc_assert (m_problem);
return false;
}
gcc_assert (m_best_epath);
if (m_stmt == NULL)
{
gcc_assert (m_stmt_finder);
m_stmt = m_stmt_finder->find_stmt (*m_best_epath);
}
gcc_assert (m_stmt);
return true;
}
unsigned
saved_diagnostic::get_epath_length () const
{
gcc_assert (m_best_epath);
return m_best_epath->length ();
}
/* Record that OTHER (and its duplicates) are duplicates
of this saved_diagnostic. */
void
saved_diagnostic::add_duplicate (saved_diagnostic *other)
{
gcc_assert (other);
m_duplicates.reserve (m_duplicates.length ()
+ other->m_duplicates.length ()
+ 1);
m_duplicates.splice (other->m_duplicates);
other->m_duplicates.truncate (0);
m_duplicates.safe_push (other);
}
/* State for building a checker_path from a particular exploded_path. /* State for building a checker_path from a particular exploded_path.
In particular, this precomputes reachability information: the set of In particular, this precomputes reachability information: the set of
source enodes for which a path be found to the diagnostic enode. */ source enodes for which a path be found to the diagnostic enode. */
@ -279,23 +437,15 @@ diagnostic_manager::to_json () const
/* A class for identifying sets of duplicated pending_diagnostic. /* A class for identifying sets of duplicated pending_diagnostic.
We want to find the simplest dedupe_candidate amongst those that share a We want to find the simplest saved_diagnostic amongst those that share a
dedupe_key. */ dedupe_key. */
class dedupe_key class dedupe_key
{ {
public: public:
dedupe_key (const saved_diagnostic &sd, dedupe_key (const saved_diagnostic &sd)
const exploded_path &epath)
: m_sd (sd), m_stmt (sd.m_stmt) : m_sd (sd), m_stmt (sd.m_stmt)
{ {
/* Support deferring the choice of stmt until after an emission path has
been built, using an optional stmt_finder. */
if (m_stmt == NULL)
{
gcc_assert (sd.m_stmt_finder);
m_stmt = sd.m_stmt_finder->find_stmt (epath);
}
gcc_assert (m_stmt); gcc_assert (m_stmt);
} }
@ -344,41 +494,14 @@ public:
const gimple *m_stmt; const gimple *m_stmt;
}; };
/* The value of a slot for a dedupe_key within dedupe_winners:
the exploded_path for the best candidate for that key, and the
number of duplicates seen so far. */
class dedupe_candidate
{
public:
// has the exploded_path
dedupe_candidate (const shortest_exploded_paths &sp,
saved_diagnostic *sd)
: m_epath (sp.get_shortest_path (sd->m_enode)),
m_num_dupes (0)
{
}
unsigned length () const { return m_epath.length (); }
const exploded_path &get_path () const { return m_epath; }
void add_duplicate () { m_num_dupes++; }
int get_num_dupes () const { return m_num_dupes; }
private:
exploded_path m_epath;
public:
int m_num_dupes;
};
/* Traits for use by dedupe_winners. */ /* Traits for use by dedupe_winners. */
class dedupe_hash_map_traits class dedupe_hash_map_traits
{ {
public: public:
typedef const dedupe_key *key_type; typedef const dedupe_key *key_type;
typedef dedupe_candidate *value_type; typedef saved_diagnostic *value_type;
typedef dedupe_candidate *compare_type; typedef saved_diagnostic *compare_type;
static inline hashval_t hash (const key_type &v) static inline hashval_t hash (const key_type &v)
{ {
@ -417,7 +540,7 @@ public:
}; };
/* A class for deduplicating diagnostics and finding (and emitting) the /* A class for deduplicating diagnostics and finding (and emitting) the
best diagnostic within each partition. */ best saved_diagnostic within each partition. */
class dedupe_winners class dedupe_winners
{ {
@ -426,113 +549,62 @@ public:
~dedupe_winners () ~dedupe_winners ()
{ {
/* Delete all keys and candidates. */ /* Delete all keys, but not the saved_diagnostics. */
for (map_t::iterator iter = m_map.begin (); for (map_t::iterator iter = m_map.begin ();
iter != m_map.end (); iter != m_map.end ();
++iter) ++iter)
{
delete (*iter).first; delete (*iter).first;
delete (*iter).second;
}
} }
/* Determine an exploded_path for SD using SP and, if it's feasible, /* Determine an exploded_path for SD using PF and, if it's feasible,
determine if it's the best seen so far for its dedupe_key. determine if SD is the best seen so far for its dedupe_key.
Retain the winner for each dedupe_key, and discard the rest. */ Record the winning SD for each dedupe_key. */
void add (logger *logger, void add (logger *logger,
const shortest_exploded_paths &sp, epath_finder *pf,
const exploded_graph *eg,
saved_diagnostic *sd) saved_diagnostic *sd)
{ {
/* Build a dedupe_candidate for SD. /* Determine best epath for SD. */
This uses SP to build an exploded_path. */ if (!sd->calc_best_epath (pf))
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.
Here we simply check each duplicate saved_diagnostic's
shortest_path, and reject any that aren't feasible.
This could introduce false negatives, as there could be longer
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);
feasibility_problem *p = NULL;
if (dc->get_path ().feasible_p (logger, &p, m_engine, eg))
{
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->set_feasible ();
}
else
{
if (flag_analyzer_feasibility)
{
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->set_infeasible (p);
delete dc;
return; return;
}
else
{
if (logger)
logger->log ("accepting %qs at EN: %i, SN: %i"
" despite infeasible path (due to %qs)",
sd->m_d->get_kind (), sd->m_enode->m_index,
sd->m_snode->m_index,
"-fno-analyzer-feasibility");
sd->set_infeasible (p);
}
}
dedupe_key *key = new dedupe_key (*sd, dc->get_path ()); dedupe_key *key = new dedupe_key (*sd);
if (dedupe_candidate **slot = m_map.get (key)) if (saved_diagnostic **slot = m_map.get (key))
{ {
if (logger) if (logger)
logger->log ("already have this dedupe_key"); logger->log ("already have this dedupe_key");
(*slot)->add_duplicate (); saved_diagnostic *cur_best_sd = *slot;
if (dc->length () < (*slot)->length ()) if (sd->get_epath_length () < cur_best_sd->get_epath_length ())
{ {
/* We've got a shorter path for the key; replace /* We've got a shorter path for the key; replace
the current candidate. */ the current candidate, marking it as a duplicate of SD. */
if (logger) if (logger)
logger->log ("length %i is better than existing length %i;" logger->log ("length %i is better than existing length %i;"
" taking over this dedupe_key", " taking over this dedupe_key",
dc->length (), (*slot)->length ()); sd->get_epath_length (),
dc->m_num_dupes = (*slot)->get_num_dupes (); cur_best_sd->get_epath_length ());
delete *slot; sd->add_duplicate (cur_best_sd);
*slot = dc; *slot = sd;
} }
else else
/* We haven't beaten the current best candidate; /* We haven't beaten the current best candidate; add SD
drop the new candidate. */ as a duplicate of it. */
{ {
if (logger) if (logger)
logger->log ("length %i isn't better than existing length %i;" logger->log ("length %i isn't better than existing length %i;"
" dropping this candidate", " dropping this candidate",
dc->length (), (*slot)->length ()); sd->get_epath_length (),
delete dc; cur_best_sd->get_epath_length ());
cur_best_sd->add_duplicate (sd);
} }
delete key; delete key;
} }
else else
{ {
/* This is the first candidate for this key. */ /* This is the first candidate for this key. */
m_map.put (key, dc); m_map.put (key, sd);
if (logger) if (logger)
logger->log ("first candidate for this dedupe_key"); logger->log ("first candidate for this dedupe_key");
} }
@ -557,27 +629,24 @@ public:
/* Sort into a good emission order. */ /* Sort into a good emission order. */
keys.qsort (dedupe_key::comparator); keys.qsort (dedupe_key::comparator);
/* Emit the best candidate for each key. */ /* Emit the best saved_diagnostics for each key. */
int i; int i;
const dedupe_key *key; const dedupe_key *key;
FOR_EACH_VEC_ELT (keys, i, key) FOR_EACH_VEC_ELT (keys, i, key)
{ {
dedupe_candidate **slot = m_map.get (key); saved_diagnostic **slot = m_map.get (key);
gcc_assert (*slot); gcc_assert (*slot);
const dedupe_candidate &dc = **slot; const saved_diagnostic *sd = *slot;
dm->emit_saved_diagnostic (eg, *sd);
dm->emit_saved_diagnostic (eg, key->m_sd,
dc.get_path (), key->m_stmt,
dc.get_num_dupes ());
} }
} }
private: private:
engine *m_engine; engine *m_engine;
/* This maps from each dedupe_key to a current best dedupe_candidate. */ /* This maps from each dedupe_key to a current best saved_diagnostic. */
typedef hash_map<const dedupe_key *, dedupe_candidate *, typedef hash_map<const dedupe_key *, saved_diagnostic *,
dedupe_hash_map_traits> map_t; dedupe_hash_map_traits> map_t;
map_t m_map; map_t m_map;
}; };
@ -604,7 +673,7 @@ diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
return; return;
/* Compute the shortest_paths once, sharing it between all diagnostics. */ /* Compute the shortest_paths once, sharing it between all diagnostics. */
shortest_exploded_paths sp (eg, eg.get_origin ()); epath_finder pf (eg);
/* Iterate through all saved diagnostics, adding them to a dedupe_winners /* Iterate through all saved diagnostics, adding them to a dedupe_winners
instance. This partitions the saved diagnostics by dedupe_key, instance. This partitions the saved diagnostics by dedupe_key,
@ -615,39 +684,39 @@ 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, &eg, sd); best_candidates.add (get_logger (), &pf, 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. */
best_candidates.emit_best (this, eg); best_candidates.emit_best (this, eg);
} }
/* Given a saved_diagnostic SD at STMT with feasible path EPATH through EG, /* Given a saved_diagnostic SD with m_best_epath through EG,
create an checker_path of suitable events and use it to call create an checker_path of suitable events and use it to call
SD's underlying pending_diagnostic "emit" vfunc to emit a diagnostic. */ SD's underlying pending_diagnostic "emit" vfunc to emit a diagnostic. */
void void
diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg, diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
const saved_diagnostic &sd, const saved_diagnostic &sd)
const exploded_path &epath,
const gimple *stmt,
int num_dupes)
{ {
LOG_SCOPE (get_logger ()); LOG_SCOPE (get_logger ());
log ("sd: %qs at SN: %i", sd.m_d->get_kind (), sd.m_snode->m_index); log ("sd: %qs at SN: %i", sd.m_d->get_kind (), sd.m_snode->m_index);
log ("num dupes: %i", num_dupes); log ("num dupes: %i", sd.get_num_dupes ());
pretty_printer *pp = global_dc->printer->clone (); pretty_printer *pp = global_dc->printer->clone ();
const exploded_path *epath = sd.get_best_epath ();
gcc_assert (epath);
/* Precompute all enodes from which the diagnostic is reachable. */ /* Precompute all enodes from which the diagnostic is reachable. */
path_builder pb (eg, epath, sd.get_feasibility_problem (), sd); path_builder pb (eg, *epath, sd.get_feasibility_problem (), sd);
/* This is the diagnostic_path subclass that will be built for /* This is the diagnostic_path subclass that will be built for
the diagnostic. */ the diagnostic. */
checker_path emission_path; checker_path emission_path;
/* Populate emission_path with a full description of EPATH. */ /* Populate emission_path with a full description of EPATH. */
build_emission_path (pb, epath, &emission_path); build_emission_path (pb, *epath, &emission_path);
/* Now prune it to just cover the most pertinent events. */ /* Now prune it to just cover the most pertinent events. */
prune_path (&emission_path, sd.m_sm, sd.m_sval, sd.m_state); prune_path (&emission_path, sd.m_sm, sd.m_sval, sd.m_state);
@ -656,7 +725,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
We use the final enode from the epath, which might be different from We use the final enode from the epath, which might be different from
the sd.m_enode, as the dedupe code doesn't care about enodes, just the sd.m_enode, as the dedupe code doesn't care about enodes, just
snodes. */ snodes. */
emission_path.add_final_event (sd.m_sm, epath.get_final_enode (), stmt, emission_path.add_final_event (sd.m_sm, epath->get_final_enode (), sd.m_stmt,
sd.m_var, sd.m_state); sd.m_var, sd.m_state);
/* The "final" event might not be final; if the saved_diagnostic has a /* The "final" event might not be final; if the saved_diagnostic has a
@ -667,7 +736,7 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
emission_path.prepare_for_emission (sd.m_d); emission_path.prepare_for_emission (sd.m_d);
location_t loc = get_stmt_location (stmt, sd.m_snode->m_fun); location_t loc = get_stmt_location (sd.m_stmt, sd.m_snode->m_fun);
/* Allow the pending_diagnostic to fix up the primary location /* Allow the pending_diagnostic to fix up the primary location
and any locations for events. */ and any locations for events. */
@ -681,8 +750,9 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
auto_cfun sentinel (sd.m_snode->m_fun); auto_cfun sentinel (sd.m_snode->m_fun);
if (sd.m_d->emit (&rich_loc)) if (sd.m_d->emit (&rich_loc))
{ {
unsigned num_dupes = sd.get_num_dupes ();
if (flag_analyzer_show_duplicate_count && num_dupes > 0) if (flag_analyzer_show_duplicate_count && num_dupes > 0)
inform_n (stmt->location, num_dupes, inform_n (loc, num_dupes,
"%i duplicate", "%i duplicates", "%i duplicate", "%i duplicates",
num_dupes); num_dupes);
} }

View File

@ -23,18 +23,13 @@ along with GCC; see the file COPYING3. If not see
namespace ana { namespace ana {
class epath_finder;
/* A to-be-emitted diagnostic stored within diagnostic_manager. */ /* A to-be-emitted diagnostic stored within diagnostic_manager. */
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,
@ -48,26 +43,17 @@ public:
json::object *to_json () const; json::object *to_json () 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 const feasibility_problem *get_feasibility_problem () const
{ {
return m_problem; return m_problem;
} }
enum status get_status () const { return m_status; } bool calc_best_epath (epath_finder *pf);
const exploded_path *get_best_epath () const { return m_best_epath; }
unsigned get_epath_length () const;
void set_epath_length (unsigned length) { m_epath_length = length; } void add_duplicate (saved_diagnostic *other);
unsigned get_epath_length () const { return m_epath_length; } unsigned get_num_dupes () const { return m_duplicates.length (); }
//private: //private:
const state_machine *m_sm; const state_machine *m_sm;
@ -78,15 +64,16 @@ public:
tree m_var; tree m_var;
const svalue *m_sval; const svalue *m_sval;
state_machine::state_t m_state; state_machine::state_t m_state;
pending_diagnostic *m_d; pending_diagnostic *m_d; // owned
exploded_edge *m_trailing_eedge; const exploded_edge *m_trailing_eedge;
private: private:
DISABLE_COPY_AND_ASSIGN (saved_diagnostic); DISABLE_COPY_AND_ASSIGN (saved_diagnostic);
enum status m_status; exploded_path *m_best_epath; // owned
unsigned m_epath_length; feasibility_problem *m_problem; // owned
feasibility_problem *m_problem;
auto_vec<const saved_diagnostic *> m_duplicates;
}; };
class path_builder; class path_builder;
@ -126,10 +113,7 @@ public:
void emit_saved_diagnostics (const exploded_graph &eg); void emit_saved_diagnostics (const exploded_graph &eg);
void emit_saved_diagnostic (const exploded_graph &eg, void emit_saved_diagnostic (const exploded_graph &eg,
const saved_diagnostic &sd, const saved_diagnostic &sd);
const exploded_path &epath,
const gimple *stmt,
int num_dupes);
unsigned get_num_diagnostics () const unsigned get_num_diagnostics () const
{ {

View File

@ -4581,23 +4581,15 @@ private:
pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ()); pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
gv->end_tdtr (); gv->end_tdtr ();
gv->begin_trtd (); gv->begin_trtd ();
if (sd->get_best_epath ())
pp_printf (pp, "epath length: %i", sd->get_epath_length ()); pp_printf (pp, "epath length: %i", sd->get_epath_length ());
else
pp_printf (pp, "no best epath");
gv->end_tdtr (); gv->end_tdtr ();
switch (sd->get_status ()) if (const feasibility_problem *p = sd->get_feasibility_problem ())
{
default:
case saved_diagnostic::STATUS_NEW:
gcc_unreachable ();
break;
case saved_diagnostic::STATUS_INFEASIBLE_PATH:
{ {
gv->begin_trtd (); gv->begin_trtd ();
pp_printf (pp, "INFEASIBLE"); pp_printf (pp, "INFEASIBLE at eedge %i: EN:%i -> EN:%i",
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_idx,
p->m_eedge.m_src->m_index, p->m_eedge.m_src->m_index,
p->m_eedge.m_dest->m_index); p->m_eedge.m_dest->m_index);
@ -4614,13 +4606,6 @@ private:
/* Ideally we'd print p->m_model here; see the notes above about /* Ideally we'd print p->m_model here; see the notes above about
tooltips. */ tooltips. */
} }
break;
case saved_diagnostic::STATUS_FEASIBLE_PATH:
gv->begin_trtd ();
pp_printf (pp, "FEASIBLE");
gv->end_tdtr ();
break;
}
pp_printf (pp, "</TABLE>"); pp_printf (pp, "</TABLE>");
gv->end_tdtr (); gv->end_tdtr ();
} }

View File

@ -879,7 +879,6 @@ public:
void dump (FILE *fp) const; void dump (FILE *fp) const;
void dump () const; void dump () const;
bool feasible_p (logger *logger, feasibility_problem **out) const;
bool feasible_p (logger *logger, feasibility_problem **out, bool feasible_p (logger *logger, feasibility_problem **out,
engine *eng, const exploded_graph *eg) const; engine *eng, const exploded_graph *eg) const;