This patch generalizes the optional rewind_info_t associated with an exploded_edge (for handling longjmp) into a subclass of a new exploded_edge::custom_info_t abstract base class, so that other kinds of custom edges can be supported (e.g. to support signal handlers being called, or possibly to support C++ exceptions, etc)
gcc/ChangeLog: * analyzer/analyzer.h (class rewind_info_t): New forward decl. * analyzer/checker-path.cc (rewind_event::rewind_event): Update assertion. (rewind_to_setjmp_event::prepare_for_emission): Update call to get_enode_origin. * analyzer/checker-path.h (rewind_to_setjmp_event::rewind_to_setjmp_event): Add rewind_info param and use it to initializer m_rewind_event. (rewind_to_setjmp_event::clone): Update for new field. (rewind_to_setjmp_event::m_rewind_info): New field. * analyzer/diagnostic-manager.cc (diagnostic_manager::add_events_for_eedge): Drop src_stack_depth local. Move injection of rewind_from/to_setjmp_event from here to rewind_info_t::add_events_to_path, calling it via a vfunc. * analyzer/engine.cc: Include "analyzer/checker-path.h". (rewind_info_t::update_model): New vfunc. (rewind_info_t::add_events_to_path): New vfunc. (exploded_edge::exploded_edge): Generalize final param from rewind_info_t * to custom_info_t *. (exploded_edge::~exploded_edge): Update for renaming of m_rewind_info to m_custom_info. (exploded_edge::dump_dot): Likewise, replacing hardcode print of "rewind" with a call to the custom_info_t::print vfunc. (exploded_graph::add_edge): Update final param from rewind_info_t * to exploded_edge::custom_info_t *. (exploded_path::feasible_p): When logging a rejection due to the region model, dump the model. Move update due to rewind info to rewind_info_t::update_model and call it via a vfunc. * analyzer/exploded-graph.h (class exploded_edge::custom_info_t): New class. (exploded_edge::m_rewind_info): Rename to... (exploded_edge::m_custom_info): ...this, converting from a rewind_info_t * to a custom_info_t *. (class rewind_info_t): Make a subclass of exploded_edge::custom_info_t. (rewind_info_t::print): New vfunc. (rewind_info_t::update_model): New vfunc. (rewind_info_t::add_events_to_path): New vfunc. (exploded_graph::add_edge): Convert final param from rewind_info_t * to exploded_edge::custom_info_t *. --- gcc/analyzer/analyzer.h | 1 + gcc/analyzer/checker-path.cc | 4 +- gcc/analyzer/checker-path.h | 10 ++- gcc/analyzer/diagnostic-manager.cc | 19 ++---- gcc/analyzer/engine.cc | 104 +++++++++++++++++++++-------- gcc/analyzer/exploded-graph.h | 85 +++++++++++++++-------- 6 files changed, 148 insertions(+), 75 deletions(-) diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 90da44b1a00a..19bc2c6d8252 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -65,6 +65,7 @@ class analysis_plan; class state_purge_map; class state_purge_per_ssa_name; class state_change; +class rewind_info_t; //////////////////////////////////////////////////////////////////////////// diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index 0a1840682cd5..d277c2806308 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -705,7 +705,7 @@ rewind_event::rewind_event (const exploded_edge *eedge, : checker_event (kind, loc, fndecl, depth), m_eedge (eedge) { - gcc_assert (m_eedge->m_rewind_info); + gcc_assert (m_eedge->m_custom_info); // a rewind_info_t } //////////////////////////////////////////////////////////////////////////// @@ -789,7 +789,7 @@ rewind_to_setjmp_event::prepare_for_emission (checker_path *path, diagnostic_event_id_t emission_id) { checker_event::prepare_for_emission (path, pd, emission_id); - path->get_setjmp_event (get_eedge ()->m_rewind_info->get_enode_origin (), + path->get_setjmp_event (m_rewind_info->get_enode_origin (), &m_original_setjmp_event_id); } diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h index 916e5a736e3e..ccff8f2ea0bc 100644 --- a/gcc/analyzer/checker-path.h +++ b/gcc/analyzer/checker-path.h @@ -428,8 +428,10 @@ class rewind_to_setjmp_event : public rewind_event { public: rewind_to_setjmp_event (const exploded_edge *eedge, - location_t loc, tree fndecl, int depth) - : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth) + location_t loc, tree fndecl, int depth, + const rewind_info_t *rewind_info) + : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth), + m_rewind_info (rewind_info) { } @@ -438,7 +440,8 @@ public: rewind_to_setjmp_event *clone () const FINAL OVERRIDE { return new rewind_to_setjmp_event (get_eedge (), - m_loc, m_fndecl, m_depth); + m_loc, m_fndecl, m_depth, + m_rewind_info); } void prepare_for_emission (checker_path *path, @@ -447,6 +450,7 @@ public: private: diagnostic_event_id_t m_original_setjmp_event_id; + const rewind_info_t *m_rewind_info; }; /* Concrete subclass of checker_event for use at the end of a path: diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 8cd4507bc6e5..461cc3318b7f 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -655,7 +655,6 @@ diagnostic_manager::add_events_for_eedge (const exploded_edge &eedge, { const exploded_node *src_node = eedge.m_src; const program_point &src_point = src_node->get_point (); - const int src_stack_depth = src_point.get_stack_depth (); const exploded_node *dst_node = eedge.m_dest; const program_point &dst_point = dst_node->get_point (); const int dst_stack_depth = dst_point.get_stack_depth (); @@ -693,20 +692,10 @@ diagnostic_manager::add_events_for_eedge (const exploded_edge &eedge, for_each_state_change (src_state, dst_state, ext_state, &visitor); - /* Add events for rewinding from a longjmp to a setjmp. */ - if (eedge.m_rewind_info) - { - emission_path->add_event - (new rewind_from_longjmp_event - (&eedge, src_point.get_supernode ()->get_end_location (), - src_point.get_fndecl (), - src_stack_depth)); - emission_path->add_event - (new rewind_to_setjmp_event - (&eedge, eedge.m_rewind_info->get_setjmp_call ()->location, - dst_point.get_fndecl (), - dst_stack_depth)); - } + /* Allow non-standard edges to add events, e.g. when rewinding from + longjmp to a setjmp. */ + if (eedge.m_custom_info) + eedge.m_custom_info->add_events_to_path (emission_path, eedge); /* Add events for superedges, function entries, and for statements. */ switch (dst_point.get_kind ()) diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index eed2be091c93..d327340c8c28 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "gcc-rich-location.h" #include "analyzer/exploded-graph.h" #include "analyzer/analysis-plan.h" +#include "analyzer/checker-path.h" #include "analyzer/state-purge.h" /* For an overview, see gcc/doc/analyzer.texi. */ @@ -1247,6 +1248,66 @@ exploded_node::dump_succs_and_preds (FILE *outf) const //////////////////////////////////////////////////////////////////////////// +/* class rewind_info_t : public exploded_edge::custom_info_t. */ + +/* Implementation of exploded_edge::custom_info_t::update_model vfunc + for rewind_info_t. + + Update state for the special-case of a rewind of a longjmp + to a setjmp (which doesn't have a superedge, but does affect + state). */ + +void +rewind_info_t::update_model (region_model *model, + const exploded_edge &eedge) +{ + const exploded_node &src_enode = *eedge.m_src; + const program_point &src_point = src_enode.get_point (); + + const gimple *last_stmt + = src_point.get_supernode ()->get_last_stmt (); + gcc_assert (last_stmt); + const gcall *longjmp_call = as_a <const gcall *> (last_stmt); + + const program_point &longjmp_point = eedge.m_src->get_point (); + const program_point &setjmp_point = eedge.m_dest->get_point (); + + gcc_assert (longjmp_point.get_stack_depth () + >= setjmp_point.get_stack_depth ()); + + model->on_longjmp (longjmp_call, + get_setjmp_call (), + setjmp_point.get_stack_depth (), NULL); +} + +/* Implementation of exploded_edge::custom_info_t::add_events_to_path vfunc + for rewind_info_t. */ + +void +rewind_info_t::add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) +{ + const exploded_node *src_node = eedge.m_src; + const program_point &src_point = src_node->get_point (); + const int src_stack_depth = src_point.get_stack_depth (); + const exploded_node *dst_node = eedge.m_dest; + const program_point &dst_point = dst_node->get_point (); + const int dst_stack_depth = dst_point.get_stack_depth (); + + emission_path->add_event + (new rewind_from_longjmp_event + (&eedge, src_point.get_supernode ()->get_end_location (), + src_point.get_fndecl (), + src_stack_depth)); + emission_path->add_event + (new rewind_to_setjmp_event + (&eedge, get_setjmp_call ()->location, + dst_point.get_fndecl (), + dst_stack_depth, this)); +} + +//////////////////////////////////////////////////////////////////////////// + /* class exploded_edge : public dedge. */ /* exploded_edge's ctor. */ @@ -1254,9 +1315,9 @@ exploded_node::dump_succs_and_preds (FILE *outf) const exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest, const superedge *sedge, const state_change &change, - rewind_info_t *rewind_info) + custom_info_t *custom_info) : dedge (src, dest), m_sedge (sedge), m_change (change), - m_rewind_info (rewind_info) + m_custom_info (custom_info) { change.validate (dest->get_state ()); } @@ -1265,7 +1326,7 @@ exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest, exploded_edge::~exploded_edge () { - delete m_rewind_info; + delete m_custom_info; } /* Implementation of dedge::dump_dot vfunc for exploded_edge. @@ -1300,7 +1361,7 @@ exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &args) const style = "\"dotted\""; break; } - if (m_rewind_info) + if (m_custom_info) { color = "red"; style = "\"dotted\""; @@ -1316,8 +1377,8 @@ exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &args) const if (m_sedge) m_sedge->dump_label_to_pp (pp, false); - else if (m_rewind_info) - pp_string (pp, "rewind"); + else if (m_custom_info) + m_custom_info->print (pp); m_change.dump (pp, args.m_eg.get_ext_state ()); //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false); @@ -1859,9 +1920,9 @@ exploded_edge * exploded_graph::add_edge (exploded_node *src, exploded_node *dest, const superedge *sedge, const state_change &change, - rewind_info_t *rewind_info) + exploded_edge::custom_info_t *custom_info) { - exploded_edge *e = new exploded_edge (src, dest, sedge, change, rewind_info); + exploded_edge *e = new exploded_edge (src, dest, sedge, change, custom_info); digraph::add_edge (e); return e; } @@ -2569,7 +2630,10 @@ exploded_path::feasible_p (logger *logger) const if (!model.maybe_update_for_edge (*sedge, last_stmt, NULL)) { if (logger) - logger->log ("rejecting due to region model"); + { + logger->log ("rejecting due to region model"); + model.dump_to_pp (logger->get_printer (), false); + } return false; } } @@ -2589,26 +2653,8 @@ exploded_path::feasible_p (logger *logger) const if (logger) logger->log (" pushing frame for %qD", fun->decl); } - else if (eedge->m_rewind_info) - { - /* Update state for the special-case of a rewind of a longjmp - to a setjmp (which doesn't have a superedge, but does affect - state). */ - const gimple *last_stmt - = src_point.get_supernode ()->get_last_stmt (); - gcc_assert (last_stmt); - const gcall *longjmp_call = as_a <const gcall *> (last_stmt); - - const program_point &longjmp_point = eedge->m_src->get_point (); - const program_point &setjmp_point = eedge->m_dest->get_point (); - - gcc_assert (longjmp_point.get_stack_depth () - >= setjmp_point.get_stack_depth ()); - - model.on_longjmp (longjmp_call, - eedge->m_rewind_info->get_setjmp_call (), - setjmp_point.get_stack_depth (), NULL); - } + else if (eedge->m_custom_info) + eedge->m_custom_info->update_model (&model, *eedge); } /* Handle phi nodes on an edge leaving a PK_BEFORE_SUPERNODE (to diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index c8e36f25a575..26ae109628f4 100644 --- a/gcc/analyzer/exploded-graph.h +++ b/gcc/analyzer/exploded-graph.h @@ -252,16 +252,73 @@ public: const int m_index; }; +/* An edge within the exploded graph. + Some exploded_edges have an underlying superedge; others don't. */ + +class exploded_edge : public dedge<eg_traits> +{ + public: + /* Abstract base class for associating custom data with an + exploded_edge, for handling non-standard edges such as + rewinding from a longjmp, signal handlers, etc. */ + class custom_info_t + { + public: + virtual ~custom_info_t () {} + + /* Hook for making .dot label more readable . */ + virtual void print (pretty_printer *pp) = 0; + + /* Hook for updating MODEL within exploded_path::feasible_p. */ + virtual void update_model (region_model *model, + const exploded_edge &eedge) = 0; + + virtual void add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) = 0; + }; + + exploded_edge (exploded_node *src, exploded_node *dest, + const superedge *sedge, + const state_change &change, + custom_info_t *custom_info); + ~exploded_edge (); + void dump_dot (graphviz_out *gv, const dump_args_t &args) + const FINAL OVERRIDE; + + //private: + const superedge *const m_sedge; + + const state_change m_change; + + /* NULL for most edges; will be non-NULL for special cases + such as an unwind from a longjmp to a setjmp, or when + a signal is delivered to a signal-handler. + + Owned by this class. */ + custom_info_t *m_custom_info; +}; + /* Extra data for an exploded_edge that represents a rewind from a longjmp to a setjmp. */ -class rewind_info_t +class rewind_info_t : public exploded_edge::custom_info_t { public: rewind_info_t (const exploded_node *enode_origin) : m_enode_origin (enode_origin) {} + void print (pretty_printer *pp) FINAL OVERRIDE + { + pp_string (pp, "rewind"); + } + + void update_model (region_model *model, + const exploded_edge &eedge) FINAL OVERRIDE; + + void add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) FINAL OVERRIDE; + const program_point &get_setjmp_point () const { const program_point &origin_point = m_enode_origin->get_point (); @@ -285,30 +342,6 @@ private: const exploded_node *m_enode_origin; }; -/* An edge within the exploded graph. - Some exploded_edges have an underlying superedge; others don't. */ - -class exploded_edge : public dedge<eg_traits> -{ - public: - exploded_edge (exploded_node *src, exploded_node *dest, - const superedge *sedge, - const state_change &change, - rewind_info_t *rewind_info); - ~exploded_edge (); - void dump_dot (graphviz_out *gv, const dump_args_t &args) - const FINAL OVERRIDE; - - //private: - const superedge *const m_sedge; - - const state_change m_change; - - /* NULL for most edges; will be non-NULL for an unwind from a longjmp - to a setjmp (owned by this class). */ - rewind_info_t *m_rewind_info; -}; - /* Statistics about aspects of an exploded_graph. */ struct stats @@ -665,7 +698,7 @@ public: exploded_edge *add_edge (exploded_node *src, exploded_node *dest, const superedge *sedge, const state_change &change, - rewind_info_t *rewind_info = NULL); + exploded_edge::custom_info_t *custom = NULL); per_program_point_data * get_or_create_per_program_point_data (const program_point &); -- 2.21.0