diff options
author | Martin Liska <mliska@suse.cz> | 2022-10-08 10:19:23 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2022-10-08 10:19:23 +0200 |
commit | d9e7934d25da4a78ffef1f738206aa1d897911df (patch) | |
tree | 1bd1697c14259e095f4b4790946eae7df0c5a2e3 /gcc/analyzer | |
parent | da0970e441345f8349522ff1abac5c223044ebb1 (diff) | |
parent | 6ffbf87ca66f4ed9cd79cff675fabe2109e46e85 (diff) | |
download | gcc-d9e7934d25da4a78ffef1f738206aa1d897911df.zip gcc-d9e7934d25da4a78ffef1f738206aa1d897911df.tar.gz gcc-d9e7934d25da4a78ffef1f738206aa1d897911df.tar.bz2 |
Merge branch 'master' into devel/sphinx
Diffstat (limited to 'gcc/analyzer')
51 files changed, 2237 insertions, 530 deletions
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index 3af1a38..33477da 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,205 @@ +2022-10-07 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/105783 + * region-model.cc (selftest::get_bit): New function. + (selftest::test_bits_within_svalue_folding): New. + (selfftest::analyzer_region_model_cc_tests): Call it. + * svalue.cc (constant_svalue::maybe_fold_bits_within): Handle the + case of extracting a single bit. + +2022-10-06 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/107158 + * store.cc (store::replay_call_summary_cluster): Eliminate + special-casing of RK_HEAP_ALLOCATED in favor of sharing code with + RK_DECL, avoiding an ICE due to attempting to bind a + compound_svalue into a binding_cluster when an svalue in the + summary cluster converts to a compound_svalue in the caller. + +2022-10-06 David Malcolm <dmalcolm@redhat.com> + + * call-summary.cc (call_summary_replay::dump_to_pp): Bulletproof + against NULL caller regions/svalues. + +2022-10-05 David Malcolm <dmalcolm@redhat.com> + + * analysis-plan.cc: Simplify includes. + * analyzer-pass.cc: Likewise. + * analyzer-selftests.cc: Likewise. + * analyzer.cc: Likewise. + * analyzer.h: Add includes of "json.h" and "tristate.h". + * call-info.cc: Simplify includes. + * call-string.cc: Likewise. + * call-summary.cc: Likewise. + * checker-path.cc: Likewise. + * complexity.cc: Likewise. + * constraint-manager.cc: Likewise. + * diagnostic-manager.cc: Likewise. + * engine.cc: Likewise. + * feasible-graph.cc: Likewise. + * known-function-manager.cc: Likewise. + * pending-diagnostic.cc: Likewise. + * program-point.cc: Likewise. + * program-state.cc: Likewise. + * region-model-asm.cc: Likewise. + * region-model-impl-calls.cc: Likewise. + * region-model-manager.cc: Likewise. + * region-model-reachability.cc: Likewise. + * region-model.cc: Likewise. + * region-model.h: Include "selftest.h". + * region.cc: Simplify includes. + * sm-fd.cc: Likewise. + * sm-file.cc: Likewise. + * sm-malloc.cc: Likewise. + * sm-pattern-test.cc: Likewise. + * sm-sensitive.cc: Likewise. + * sm-signal.cc: Likewise. + * sm-taint.cc: Likewise. + * sm.cc: Likewise. + * state-purge.cc: Likewise. + * store.cc: Likewise. + * store.h: Likewise. + * supergraph.cc: Likewise. + * svalue.cc: Likewise. + * svalue.h: Likewise. + * trimmed-graph.cc: Likewise. + * varargs.cc: Likewise. + +2022-10-05 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/107060 + * call-summary.cc + (call_summary_replay::convert_svalue_from_summary_1): Handle NULL + results from convert_svalue_from_summary in SK_UNARY_OP and + SK_BIN_OP. + * engine.cc (impl_region_model_context::on_unknown_change): Bail + out on svalues that can't have associated state. + * region-model-impl-calls.cc + (region_model::impl_call_analyzer_get_unknown_ptr): New. + * region-model.cc (region_model::on_stmt_pre): Handle + "__analyzer_get_unknown_ptr". + * region-model.h + (region_model::impl_call_analyzer_get_unknown_ptr): New decl. + * store.cc (store::replay_call_summary_cluster): Avoid trying to + create binding clusters for base regions that shouldn't have them. + +2022-10-05 Martin Liska <mliska@suse.cz> + + * call-summary.cc (call_summary_replay::call_summary_replay): + Remove unused variable and arguments. + * call-summary.h: Likewise. + * engine.cc (exploded_node::on_stmt): Likewise. + (exploded_node::replay_call_summaries): Likewise. + (exploded_node::replay_call_summary): Likewise. + * exploded-graph.h (class exploded_node): Likewise. + +2022-10-05 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/107072 + * analyzer-logging.h: Include "diagnostic-core.h". + * analyzer.h: Include "function.h". + (class call_summary): New forward decl. + (class call_summary_replay): New forward decl. + (struct per_function_data): New forward decl. + (struct interesting_t): New forward decl. + (custom_edge_info::update_state): New vfunc. + * call-info.cc (custom_edge_info::update_state): New. + * call-summary.cc: New file. + * call-summary.h: New file. + * constraint-manager.cc: Include "analyzer/call-summary.h". + (class replay_fact_visitor): New. + (constraint_manager::replay_call_summary): New. + * constraint-manager.h (constraint_manager::replay_call_summary): + New. + * engine.cc: Include "analyzer/call-summary.h". + (exploded_node::on_stmt): Handle call summaries. + (class call_summary_edge_info): New. + (exploded_node::replay_call_summaries): New. + (exploded_node::replay_call_summary): New. + (per_function_data::~per_function_data): New. + (per_function_data::add_call_summary): Move here from header and + reimplement. + (exploded_graph::process_node): Call update_state rather than + update_model when handling bifurcation + (viz_callgraph_node::dump_dot): Use a regular label rather + than an HTML table; add summaries to dump. + * exploded-graph.h: Include "alloc-pool.h", "fibonacci_heap.h", + "supergraph.h", "sbitmap.h", "shortest-paths.h", "analyzer/sm.h", + "analyzer/program-state.h", and "analyzer/diagnostic-manager.h". + (exploded_node::replay_call_summaries): New decl. + (exploded_node::replay_call_summary): New decl. + (per_function_data::~per_function_data): New decl. + (per_function_data::add_call_summary): Move implemention from + header. + (per_function_data::m_summaries): Update type of element. + * known-function-manager.h: Include "analyzer/analyzer-logging.h". + * program-point.h: Include "pretty-print.h" and + "analyzer/call-string.h". + * program-state.cc: Include "analyzer/call-summary.h". + (sm_state_map::replay_call_summary): New. + (program_state::replay_call_summary): New. + * program-state.h (sm_state_map::replay_call_summary): New decl. + (program_state::replay_call_summary): New decl. + * region-model-manager.cc + (region_model_manager::get_or_create_asm_output_svalue): New + overload. + * region-model-manager.h + (region_model_manager::get_or_create_asm_output_svalue): New + overload decl. + * region-model.cc: Include "analyzer/call-summary.h". + (region_model::maybe_update_for_edge): Remove call to + region_model::update_for_call_summary on + SUPEREDGE_INTRAPROCEDURAL_CALL. + (region_model::update_for_call_summary): Delete. + (region_model::replay_call_summary): New. + * region-model.h (region_model::replay_call_summary): New decl. + (region_model::update_for_call_summary): Delete decl. + * store.cc: Include "analyzer/call-summary.h". + (store::replay_call_summary): New. + (store::replay_call_summary_cluster): New. + * store.h: Include "tristate.h". + (is_a_helper <const ana::concrete_binding *>::test): New. + (store::replay_call_summary): New decl. + (store::replay_call_summary_cluster): New decl. + * supergraph.cc (get_ultimate_function_for_cgraph_edge): Remove + "static" from decl. + (supergraph_call_edge): Make stmt param const. + * supergraph.h: Include "ordered-hash-map.h", "cfg.h", + "basic-block.h", "gimple.h", "gimple-iterator.h", and "digraph.h". + (supergraph_call_edge): Make stmt param const. + (get_ultimate_function_for_cgraph_edge): New decl. + * svalue.cc (compound_svalue::compound_svalue): Assert that we're + not nesting compound_svalues. + * svalue.h: Include "json.h", "analyzer/store.h", and + "analyzer/program-point.h". + (asm_output_svalue::get_num_outputs): New accessor. + +2022-10-05 David Malcolm <dmalcolm@redhat.com> + + * region-model.h: Include "analyzer/region-model-manager.h" + (class region_model_manager): Move decl to... + * region-model-manager.h: ...this new file. + +2022-10-05 David Malcolm <dmalcolm@redhat.com> + + * region-model-manager.cc + (region_model_manager::maybe_fold_unaryop): Fold -(-(VAL)) to VAL. + +2022-10-05 David Malcolm <dmalcolm@redhat.com> + + * region-model-manager.cc + (region_model_manager::get_or_create_widening_svalue): Use a + function_point rather than a program_point. + * region-model.cc (selftest::test_widening_constraints): Likewise. + * region-model.h + (region_model_manager::get_or_create_widening_svalue): Likewise. + (model_merger::get_function_point): New. + * svalue.cc (svalue::can_merge_p): Use a function_point rather + than a program_point. + (svalue::can_merge_p): Likewise. + * svalue.h (widening_svalue::key_t): Likewise. + (widening_svalue::widening_svalue): Likewise. + 2022-09-12 Martin Liska <mliska@suse.cz> * region-model.cc (region_model::maybe_complain_about_infoleak): diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc index c488f37..a4a42c5 100644 --- a/gcc/analyzer/analysis-plan.cc +++ b/gcc/analyzer/analysis-plan.cc @@ -27,7 +27,6 @@ along with GCC; see the file COPYING3. If not see #include "timevar.h" #include "ipa-utils.h" #include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-core.h" #include "analyzer/analyzer-logging.h" @@ -35,7 +34,6 @@ along with GCC; see the file COPYING3. If not see #include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" -#include "function.h" #include "cfg.h" #include "basic-block.h" #include "gimple.h" diff --git a/gcc/analyzer/analyzer-logging.h b/gcc/analyzer/analyzer-logging.h index ef14b29..71b540c 100644 --- a/gcc/analyzer/analyzer-logging.h +++ b/gcc/analyzer/analyzer-logging.h @@ -23,6 +23,8 @@ along with GCC; see the file COPYING3. If not see #ifndef ANALYZER_LOGGING_H #define ANALYZER_LOGGING_H +#include "diagnostic-core.h" + namespace ana { /* A logger encapsulates a logging stream: a way to send diff --git a/gcc/analyzer/analyzer-pass.cc b/gcc/analyzer/analyzer-pass.cc index f6cef58..fc7098d 100644 --- a/gcc/analyzer/analyzer-pass.cc +++ b/gcc/analyzer/analyzer-pass.cc @@ -26,7 +26,6 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "options.h" #include "tree.h" -#include "function.h" #include "analyzer/analyzer.h" #include "analyzer/engine.h" diff --git a/gcc/analyzer/analyzer-selftests.cc b/gcc/analyzer/analyzer-selftests.cc index 3765e70..278c245 100644 --- a/gcc/analyzer/analyzer-selftests.cc +++ b/gcc/analyzer/analyzer-selftests.cc @@ -23,7 +23,6 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "tree.h" #include "stringpool.h" -#include "function.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-selftests.h" diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index c85dbf3..8a2a773 100644 --- a/gcc/analyzer/analyzer.cc +++ b/gcc/analyzer/analyzer.cc @@ -27,7 +27,6 @@ along with GCC; see the file COPYING3. If not see #include "gimple.h" #include "diagnostic.h" #include "intl.h" -#include "function.h" #include "analyzer/analyzer.h" #if ENABLE_ANALYZER diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index b325aee..a2d79e4 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -21,6 +21,10 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_ANALYZER_H #define GCC_ANALYZER_ANALYZER_H +#include "function.h" +#include "json.h" +#include "tristate.h" + class graphviz_out; namespace ana { @@ -114,6 +118,10 @@ class state_machine; class logger; class visitor; class known_function_manager; +class call_summary; +class call_summary_replay; +struct per_function_data; +struct interesting_t; /* Forward decls of functions. */ @@ -263,6 +271,11 @@ public: /* Hook for making .dot label more readable. */ virtual void print (pretty_printer *pp) const = 0; + /* Hook for updating STATE when handling bifurcation. */ + virtual bool update_state (program_state *state, + const exploded_edge *eedge, + region_model_context *ctxt) const; + /* Hook for updating MODEL within exploded_path::feasible_p and when handling bifurcation. */ virtual bool update_model (region_model *model, diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc index efc070b..56059ac 100644 --- a/gcc/analyzer/call-info.cc +++ b/gcc/analyzer/call-info.cc @@ -30,11 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "options.h" #include "cgraph.h" #include "tree-pretty-print.h" -#include "tristate.h" #include "bitmap.h" -#include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" @@ -56,9 +52,6 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-path.h" #include "analyzer/checker-path.h" #include "analyzer/diagnostic-manager.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" #include "analyzer/exploded-graph.h" #include "analyzer/call-info.h" @@ -66,7 +59,17 @@ along with GCC; see the file COPYING3. If not see namespace ana { -/* class call_info : public custom_eedge_info_t. */ +/* class custom_edge_info. */ + +bool +custom_edge_info::update_state (program_state *state, + const exploded_edge *eedge, + region_model_context *ctxt) const +{ + return update_model (state->m_region_model, eedge, ctxt); +} + +/* class call_info : public custom_edge_info. */ /* Implementation of custom_edge_info::print vfunc for call_info: use get_desc to get a label_text, and print it to PP. */ diff --git a/gcc/analyzer/call-string.cc b/gcc/analyzer/call-string.cc index a09f569..f0a30d9 100644 --- a/gcc/analyzer/call-string.cc +++ b/gcc/analyzer/call-string.cc @@ -24,7 +24,6 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print.h" #include "tree.h" #include "options.h" -#include "json.h" #include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" diff --git a/gcc/analyzer/call-summary.cc b/gcc/analyzer/call-summary.cc new file mode 100644 index 0000000..12ef82d --- /dev/null +++ b/gcc/analyzer/call-summary.cc @@ -0,0 +1,889 @@ +/* Classes for working with summaries of function calls. + Copyright (C) 2022 David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "tree-dfa.h" +#include "diagnostic.h" +#include "tree-diagnostic.h" +#include "analyzer/analyzer.h" +#include "analyzer/region-model.h" +#include "analyzer/call-summary.h" +#include "analyzer/exploded-graph.h" + +#if ENABLE_ANALYZER + +namespace ana { + +/* class call_summary. */ + +const program_state & +call_summary::get_state () const +{ + return m_enode->get_state (); +} + +tree +call_summary::get_fndecl () const +{ + return m_enode->get_point ().get_fndecl (); +} + +label_text +call_summary::get_desc () const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + + get_user_facing_desc (&pp); + if (flag_analyzer_verbose_edges) + pp_printf (&pp, " (call summary; EN: %i)", m_enode->m_index); + + return label_text::take (xstrdup (pp_formatted_text (&pp))); +} + +/* Generate a user-facing description of this call summary.c + This has various heuristics for distinguishing between different + summaries. + This will help with debugging, too. */ + +void +call_summary::get_user_facing_desc (pretty_printer *pp) const +{ + tree fndecl = get_fndecl (); + + /* If there are multiple summaries, try to use the return value to + distinguish between them. */ + if (m_per_fn_data->m_summaries.length () > 1) + { + if (tree result = DECL_RESULT (fndecl)) + { + const region *result_reg + = get_state ().m_region_model->get_lvalue (result, NULL); + const svalue *result_sval + = get_state ().m_region_model->get_store_value (result_reg, NULL); + switch (result_sval->get_kind ()) + { + default: + break; + case SK_REGION: + { + const region_svalue *region_sval + = as_a <const region_svalue *> (result_sval); + const region *pointee_reg = region_sval->get_pointee (); + switch (pointee_reg->get_kind ()) + { + default: + break; + case RK_HEAP_ALLOCATED: + pp_printf (pp, + "when %qE returns pointer" + " to heap-allocated buffer", + fndecl); + return; + } + } + break; + case SK_CONSTANT: + { + const constant_svalue *constant_sval + = as_a <const constant_svalue *> (result_sval); + tree cst = constant_sval->get_constant (); + if (POINTER_TYPE_P (TREE_TYPE (result)) + && zerop (cst)) + pp_printf (pp, "when %qE returns NULL", fndecl); + else + pp_printf (pp, "when %qE returns %qE", fndecl, cst); + return; + } + } + } + } + + /* Fallback. */ + pp_printf (pp, "when %qE returns", fndecl); +} + +/* Dump a multiline representation of this object to PP. */ + +void +call_summary::dump_to_pp (const extrinsic_state &ext_state, + pretty_printer *pp, + bool simple) const +{ + label_text desc = get_desc (); + pp_printf (pp, "desc: %qs", desc.get ()); + pp_newline (pp); + + get_state ().dump_to_pp (ext_state, simple, true, pp); +} + +/* Dump a multiline representation of this object to FILE. */ + +void +call_summary::dump (const extrinsic_state &ext_state, + FILE *fp, + bool simple) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = fp; + dump_to_pp (ext_state, &pp, simple); + pp_flush (&pp); +} + +/* Dump a multiline representation of this object to stderr. */ + +DEBUG_FUNCTION void +call_summary::dump (const extrinsic_state &ext_state, bool simple) const +{ + dump (ext_state, stderr, simple); +} + +/* class call_summary_replay. */ + +/* call_summary_replay's ctor. + Populate the cache with params for the summary based on + arguments at the caller. */ + +call_summary_replay::call_summary_replay (const call_details &cd, + function *called_fn, + call_summary *summary, + const extrinsic_state &ext_state) +: m_cd (cd), + m_summary (summary), + m_ext_state (ext_state) +{ + region_model_manager *mgr = cd.get_manager (); + + // populate params based on args + tree fndecl = called_fn->decl; + + /* Get a frame_region for use with respect to the summary. + This will be a top-level frame, since that's what's in + the summary. */ + const frame_region *summary_frame + = mgr->get_frame_region (NULL, called_fn); + + unsigned idx = 0; + for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm; + iter_parm = DECL_CHAIN (iter_parm), ++idx) + { + /* If there's a mismatching declaration, the call stmt might + not have enough args. Handle this case by leaving the + rest of the params as uninitialized. */ + if (idx >= cd.num_args ()) + break; + const svalue *caller_arg_sval = cd.get_arg_svalue (idx); + tree parm_lval = iter_parm; + if (tree parm_default_ssa = ssa_default_def (called_fn, iter_parm)) + parm_lval = parm_default_ssa; + const region *summary_parm_reg + = summary_frame->get_region_for_local (mgr, parm_lval, cd.get_ctxt ()); + const svalue *summary_initial_parm_reg + = mgr->get_or_create_initial_value (summary_parm_reg); + add_svalue_mapping (summary_initial_parm_reg, caller_arg_sval); + } + + /* Handle any variadic args. */ + unsigned va_arg_idx = 0; + for (; idx < cd.num_args (); idx++, va_arg_idx++) + { + const svalue *caller_arg_sval = cd.get_arg_svalue (idx); + const region *summary_var_arg_reg + = mgr->get_var_arg_region (summary_frame, va_arg_idx); + const svalue *summary_initial_var_arg_reg + = mgr->get_or_create_initial_value (summary_var_arg_reg); + add_svalue_mapping (summary_initial_var_arg_reg, caller_arg_sval); + } +} + +/* Try to convert SUMMARY_SVAL in the summary to a corresponding svalue + in the caller, caching the result. + + Return NULL if the conversion is not possible. */ + +const svalue * +call_summary_replay::convert_svalue_from_summary (const svalue *summary_sval) +{ + gcc_assert (summary_sval); + + if (const svalue **slot + = m_map_svalue_from_summary_to_caller.get (summary_sval)) + return *slot; + + const svalue *caller_sval = convert_svalue_from_summary_1 (summary_sval); + + /* Add to cache. */ + add_svalue_mapping (summary_sval, caller_sval); + + return caller_sval; +} + +/* Implementation of call_summary_replay::convert_svalue_from_summary. */ + +const svalue * +call_summary_replay::convert_svalue_from_summary_1 (const svalue *summary_sval) +{ + gcc_assert (summary_sval); + + switch (summary_sval->get_kind ()) + { + default: + gcc_unreachable (); + case SK_REGION: + { + const region_svalue *region_summary_sval + = as_a <const region_svalue *> (summary_sval); + const region *summary_reg = region_summary_sval->get_pointee (); + const region *caller_reg = convert_region_from_summary (summary_reg); + if (!caller_reg) + return NULL; + region_model_manager *mgr = get_manager (); + const svalue *caller_ptr + = mgr->get_ptr_svalue (summary_sval->get_type (), + caller_reg); + return caller_ptr; + } + break; + + case SK_CONSTANT: + case SK_PLACEHOLDER: + case SK_POISONED: + case SK_UNKNOWN: + return summary_sval; + + case SK_SETJMP: + return NULL; // TODO + + case SK_INITIAL: + { + const initial_svalue *initial_summary_sval + = as_a <const initial_svalue *> (summary_sval); + /* Params should already be in the cache, courtesy of the ctor. */ + gcc_assert (!initial_summary_sval->initial_value_of_param_p ()); + + /* Initial value of region within the summary is the value of the + region at the point of the call. */ + const region *summary_reg = initial_summary_sval->get_region (); + const region *caller_reg = convert_region_from_summary (summary_reg); + if (!caller_reg) + return NULL; + const svalue *caller_sval + = m_cd.get_model ()->get_store_value (caller_reg, m_cd.get_ctxt ()); + return caller_sval; + } + break; + case SK_UNARYOP: + { + const unaryop_svalue *unaryop_summary_sval + = as_a <const unaryop_svalue *> (summary_sval); + const svalue *summary_arg = unaryop_summary_sval->get_arg (); + const svalue *caller_arg = convert_svalue_from_summary (summary_arg); + if (!caller_arg) + return NULL; + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_unaryop (summary_sval->get_type (), + unaryop_summary_sval->get_op (), + caller_arg); + } + break; + case SK_BINOP: + { + const binop_svalue *binop_summary_sval + = as_a <const binop_svalue *> (summary_sval); + const svalue *summary_arg0 = binop_summary_sval->get_arg0 (); + const svalue *caller_arg0 = convert_svalue_from_summary (summary_arg0); + if (!caller_arg0) + return NULL; + const svalue *summary_arg1 = binop_summary_sval->get_arg1 (); + const svalue *caller_arg1 = convert_svalue_from_summary (summary_arg1); + if (!caller_arg1) + return NULL; + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_binop (summary_sval->get_type (), + binop_summary_sval->get_op (), + caller_arg0, + caller_arg1); + } + break; + case SK_SUB: + { + const sub_svalue *sub_summary_sval + = as_a <const sub_svalue *> (summary_sval); + region_model_manager *mgr = get_manager (); + const svalue *summary_parent_sval = sub_summary_sval->get_parent (); + if (!summary_parent_sval) + return NULL; + const region *summary_subregion = sub_summary_sval->get_subregion (); + if (!summary_subregion) + return NULL; + return mgr->get_or_create_sub_svalue (summary_sval->get_type (), + summary_parent_sval, + summary_subregion); + } + break; + case SK_REPEATED: + { + const repeated_svalue *repeated_summary_sval + = as_a <const repeated_svalue *> (summary_sval); + const svalue *summary_outer_size + = repeated_summary_sval->get_outer_size (); + const svalue *caller_outer_size + = convert_svalue_from_summary (summary_outer_size); + if (!caller_outer_size) + return NULL; + const svalue *summary_inner_sval + = repeated_summary_sval->get_inner_svalue (); + const svalue *caller_inner_sval + = convert_svalue_from_summary (summary_inner_sval); + if (!caller_inner_sval) + return NULL; + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_repeated_svalue (summary_sval->get_type (), + caller_outer_size, + caller_inner_sval); + } + break; + case SK_BITS_WITHIN: + { + const bits_within_svalue *bits_within_summary_sval + = as_a <const bits_within_svalue *> (summary_sval); + const bit_range &bits = bits_within_summary_sval->get_bits (); + const svalue *summary_inner_sval + = bits_within_summary_sval->get_inner_svalue (); + const svalue *caller_inner_sval + = convert_svalue_from_summary (summary_inner_sval); + if (!caller_inner_sval) + return NULL; + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_bits_within (summary_sval->get_type (), + bits, + caller_inner_sval); + } + break; + case SK_UNMERGEABLE: + { + const unmergeable_svalue *unmergeable_summary_sval + = as_a <const unmergeable_svalue *> (summary_sval); + const svalue *summary_arg_sval = unmergeable_summary_sval->get_arg (); + const svalue *caller_arg_sval + = convert_svalue_from_summary (summary_arg_sval); + if (!caller_arg_sval) + return NULL; + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_unmergeable (caller_arg_sval); + } + break; + case SK_WIDENING: + { + const widening_svalue *widening_summary_sval + = as_a <const widening_svalue *> (summary_sval); + const function_point &point = widening_summary_sval->get_point (); + const svalue *summary_base_sval + = widening_summary_sval->get_base_svalue (); + const svalue *caller_base_sval + = convert_svalue_from_summary (summary_base_sval); + if (!(caller_base_sval + && caller_base_sval->can_have_associated_state_p ())) + return NULL; + const svalue *summary_iter_sval + = widening_summary_sval->get_iter_svalue (); + const svalue *caller_iter_sval + = convert_svalue_from_summary (summary_iter_sval); + if (!(caller_iter_sval + && caller_iter_sval->can_have_associated_state_p ())) + return NULL; + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_widening_svalue + (summary_iter_sval->get_type (), + point, + caller_base_sval, + caller_iter_sval); + } + break; + case SK_COMPOUND: + { + const compound_svalue *compound_summary_sval + = as_a <const compound_svalue *> (summary_sval); + region_model_manager *mgr = get_manager (); + store_manager *store_mgr = mgr->get_store_manager (); + binding_map caller_map; + auto_vec <const binding_key *> summary_keys; + for (auto kv : *compound_summary_sval) + summary_keys.safe_push (kv.first); + summary_keys.qsort (binding_key::cmp_ptrs); + for (auto key : summary_keys) + { + gcc_assert (key->concrete_p ()); + /* No remapping is needed for concrete binding keys. */ + + const svalue *bound_summary_sval + = compound_summary_sval->get_map ().get (key); + const svalue *caller_sval + = convert_svalue_from_summary (bound_summary_sval); + if (!caller_sval) + caller_sval = mgr->get_or_create_unknown_svalue (NULL_TREE); + + if (const compound_svalue *inner_compound_sval + = caller_sval->dyn_cast_compound_svalue ()) + { + const concrete_binding *outer_key + = as_a <const concrete_binding *> (key); + for (auto inner_kv : *inner_compound_sval) + { + // These should already be mapped to the caller. + const binding_key *inner_key = inner_kv.first; + const svalue *inner_sval = inner_kv.second; + gcc_assert (inner_key->concrete_p ()); + const concrete_binding *concrete_key + = as_a <const concrete_binding *> (inner_key); + bit_offset_t effective_start + = (concrete_key->get_start_bit_offset () + + outer_key->get_start_bit_offset ()); + const concrete_binding *effective_concrete_key + = store_mgr->get_concrete_binding + (effective_start, + concrete_key->get_size_in_bits ()); + caller_map.put (effective_concrete_key, inner_sval); + } + } + else + caller_map.put (key, caller_sval); + } + return mgr->get_or_create_compound_svalue (summary_sval->get_type (), + caller_map); + } + break; + case SK_CONJURED: + { + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_unknown_svalue (summary_sval->get_type ()); + } + break; + case SK_ASM_OUTPUT: + { + const asm_output_svalue *asm_output_summary_sval + = as_a <const asm_output_svalue *> (summary_sval); + const char *asm_string = asm_output_summary_sval->get_asm_string (); + unsigned output_idx = asm_output_summary_sval->get_output_idx (); + unsigned num_inputs = asm_output_summary_sval->get_num_inputs (); + unsigned num_outputs = asm_output_summary_sval->get_num_outputs (); + auto_vec<const svalue *> inputs (num_inputs); + for (unsigned idx = 0; idx < num_inputs; idx++) + { + const svalue *summary_input + = asm_output_summary_sval->get_input (idx); + const svalue *caller_input + = convert_svalue_from_summary (summary_input); + if (!caller_input) + return NULL; + inputs.safe_push (caller_input); + } + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_asm_output_svalue (summary_sval->get_type (), + asm_string, + output_idx, + num_outputs, + inputs); + } + break; + case SK_CONST_FN_RESULT: + { + const const_fn_result_svalue *const_fn_result_summary_sval + = as_a <const const_fn_result_svalue *> (summary_sval); + tree fndecl = const_fn_result_summary_sval->get_fndecl (); + unsigned num_inputs = const_fn_result_summary_sval->get_num_inputs (); + auto_vec<const svalue *> inputs (num_inputs); + for (unsigned idx = 0; idx < num_inputs; idx++) + { + const svalue *summary_input + = const_fn_result_summary_sval->get_input (idx); + const svalue *caller_input + = convert_svalue_from_summary (summary_input); + if (!caller_input) + return NULL; + inputs.safe_push (caller_input); + } + region_model_manager *mgr = get_manager (); + return mgr->get_or_create_const_fn_result_svalue + (summary_sval->get_type (), + fndecl, + inputs); + } + break; + } +} + +/* Try to convert SUMMARY_REG in the summary to a corresponding region + in the caller, caching the result. + + Return NULL if the conversion is not possible. */ + +const region * +call_summary_replay::convert_region_from_summary (const region *summary_reg) +{ + gcc_assert (summary_reg); + + if (const region **slot + = m_map_region_from_summary_to_caller.get (summary_reg)) + return *slot; + + const region *caller_reg = convert_region_from_summary_1 (summary_reg); + + /* Add to cache. */ + add_region_mapping (summary_reg, caller_reg); + + return caller_reg; +} + +/* Implementation of call_summary_replay::convert_region_from_summary. */ + +const region * +call_summary_replay::convert_region_from_summary_1 (const region *summary_reg) +{ + gcc_assert (summary_reg); + + region_model_manager *mgr = get_manager (); + switch (summary_reg->get_kind ()) + { + default: + gcc_unreachable (); + /* Top-level regions. */ + case RK_FRAME: + case RK_GLOBALS: + case RK_CODE: + case RK_STACK: + case RK_HEAP: + case RK_ROOT: + /* These should never be pointed to by a region_svalue. */ + gcc_unreachable (); + + case RK_FUNCTION: + case RK_LABEL: + case RK_STRING: + case RK_UNKNOWN: + /* We can reuse these regions directly. */ + return summary_reg; + + case RK_SYMBOLIC: + { + const symbolic_region *summary_symbolic_reg + = as_a <const symbolic_region *> (summary_reg); + const svalue *summary_ptr_sval = summary_symbolic_reg->get_pointer (); + const svalue *caller_ptr_sval + = convert_svalue_from_summary (summary_ptr_sval); + if (!caller_ptr_sval) + return NULL; + const region *caller_reg + = get_caller_model ()->deref_rvalue (caller_ptr_sval, + NULL_TREE, + get_ctxt ()); + return caller_reg; + } + break; + + case RK_DECL: + { + const decl_region *summary_decl_reg + = as_a <const decl_region *> (summary_reg); + tree decl = summary_decl_reg->get_decl (); + switch (TREE_CODE (decl)) + { + default: + gcc_unreachable (); + case SSA_NAME: + /* We don't care about writes to locals within + the summary. */ + return NULL; + case VAR_DECL: + /* We don't care about writes to locals within + the summary. */ + if (is_global_var (decl)) + /* If it's a global, we can reuse the region directly. */ + return summary_reg; + else + /* Otherwise, we don't care about locals. */ + return NULL; + case RESULT_DECL: + return m_cd.get_lhs_region (); + case PARM_DECL: + /* Writes (by value) to parms should be visible to the caller. */ + return NULL; + } + } + break; + case RK_FIELD: + { + const field_region *summary_field_reg + = as_a <const field_region *> (summary_reg); + const region *summary_parent_reg = summary_reg->get_parent_region (); + const region *caller_parent_reg + = convert_region_from_summary (summary_parent_reg); + if (!caller_parent_reg) + return NULL; + tree field = summary_field_reg->get_field (); + return mgr->get_field_region (caller_parent_reg, field); + } + break; + case RK_ELEMENT: + { + const element_region *summary_element_reg + = as_a <const element_region *> (summary_reg); + const region *summary_parent_reg = summary_reg->get_parent_region (); + const region *caller_parent_reg + = convert_region_from_summary (summary_parent_reg); + if (!caller_parent_reg) + return NULL; + const svalue *summary_index = summary_element_reg->get_index (); + const svalue *caller_index + = convert_svalue_from_summary (summary_index); + if (!caller_index) + return NULL; + return mgr->get_element_region (caller_parent_reg, + summary_reg->get_type (), + caller_index); + } + break; + case RK_OFFSET: + { + const offset_region *summary_offset_reg + = as_a <const offset_region *> (summary_reg); + const region *summary_parent_reg = summary_reg->get_parent_region (); + const region *caller_parent_reg + = convert_region_from_summary (summary_parent_reg); + if (!caller_parent_reg) + return NULL; + const svalue *summary_byte_offset + = summary_offset_reg->get_byte_offset (); + const svalue *caller_byte_offset + = convert_svalue_from_summary (summary_byte_offset); + if (!caller_byte_offset) + return NULL; + return mgr->get_offset_region (caller_parent_reg, + summary_reg->get_type (), + caller_byte_offset); + } + break; + case RK_SIZED: + { + const sized_region *summary_sized_reg + = as_a <const sized_region *> (summary_reg); + const region *summary_parent_reg = summary_reg->get_parent_region (); + const region *caller_parent_reg + = convert_region_from_summary (summary_parent_reg); + if (!caller_parent_reg) + return NULL; + const svalue *summary_byte_size + = summary_sized_reg->get_byte_size_sval (mgr); + const svalue *caller_byte_size + = convert_svalue_from_summary (summary_byte_size); + if (!caller_byte_size) + return NULL; + return mgr->get_sized_region (caller_parent_reg, + summary_reg->get_type (), + caller_byte_size); + } + break; + case RK_CAST: + { + const cast_region *summary_cast_reg + = as_a <const cast_region *> (summary_reg); + const region *summary_original_reg + = summary_cast_reg->get_original_region (); + const region *caller_original_reg + = convert_region_from_summary (summary_original_reg); + if (!caller_original_reg) + return NULL; + return mgr->get_cast_region (caller_original_reg, + summary_reg->get_type ()); + } + break; + case RK_HEAP_ALLOCATED: + { + /* If we have a heap-allocated region in the summary, then + it was allocated within the callee. + Create a new heap-allocated region to summarize this. */ + return mgr->create_region_for_heap_alloc (); + } + break; + case RK_ALLOCA: + return NULL; + case RK_BIT_RANGE: + { + const bit_range_region *summary_bit_range_reg + = as_a <const bit_range_region *> (summary_reg); + const region *summary_parent_reg = summary_reg->get_parent_region (); + const region *caller_parent_reg + = convert_region_from_summary (summary_parent_reg); + if (!caller_parent_reg) + return NULL; + const bit_range &bits = summary_bit_range_reg->get_bits (); + return mgr->get_bit_range (caller_parent_reg, + summary_reg->get_type (), + bits); + } + break; + case RK_VAR_ARG: + return NULL; + } +} + +/* Try to convert SUMMARY_KEY in the summary to a corresponding binding key + in the caller. + + Return NULL if the conversion is not possible. */ + +const binding_key * +call_summary_replay::convert_key_from_summary (const binding_key *summary_key) +{ + if (summary_key->concrete_p ()) + return summary_key; + + const symbolic_binding *symbolic_key = (const symbolic_binding *)summary_key; + const region *summary_reg = symbolic_key->get_region (); + const region *caller_reg = convert_region_from_summary (summary_reg); + if (!caller_reg) + return NULL; + region_model_manager *mgr = get_manager (); + store_manager *store_mgr = mgr->get_store_manager (); + return store_mgr->get_symbolic_binding (caller_reg); +} + +/* Record that SUMMARY_SVAL maps to CALLER_SVAL for this replay. */ + +void +call_summary_replay::add_svalue_mapping (const svalue *summary_sval, + const svalue *caller_sval) +{ + gcc_assert (summary_sval); + // CALLER_SVAL can be NULL + m_map_svalue_from_summary_to_caller.put (summary_sval, caller_sval); +} + +/* Record that SUMMARY_REG maps to CALLER_REG for this replay. */ + +void +call_summary_replay::add_region_mapping (const region *summary_reg, + const region *caller_reg) +{ + gcc_assert (summary_reg); + // CALLER_REG can be NULL + m_map_region_from_summary_to_caller.put (summary_reg, caller_reg); +} + +/* Dump a multiline representation of this object to PP. */ + +void +call_summary_replay::dump_to_pp (pretty_printer *pp, bool simple) const +{ + pp_newline (pp); + pp_string (pp, "CALL DETAILS:"); + pp_newline (pp); + m_cd.dump_to_pp (pp, simple); + + pp_newline (pp); + pp_string (pp, "CALLEE SUMMARY:"); + pp_newline (pp); + m_summary->dump_to_pp (m_ext_state, pp, simple); + + /* Current state of caller (could be in mid-update). */ + pp_newline (pp); + pp_string (pp, "CALLER:"); + pp_newline (pp); + m_cd.get_model ()->dump_to_pp (pp, simple, true); + + pp_newline (pp); + pp_string (pp, "REPLAY STATE:"); + pp_newline (pp); + pp_string (pp, "svalue mappings from summary to caller:"); + pp_newline (pp); + auto_vec <const svalue *> summary_svals; + for (auto kv : m_map_svalue_from_summary_to_caller) + summary_svals.safe_push (kv.first); + summary_svals.qsort (svalue::cmp_ptr_ptr); + for (auto summary_sval : summary_svals) + { + pp_string (pp, "sval in summary: "); + summary_sval->dump_to_pp (pp, simple); + pp_newline (pp); + + const svalue *caller_sval + = *((const_cast<svalue_map_t &> + (m_map_svalue_from_summary_to_caller)).get (summary_sval)); + pp_string (pp, " sval in caller: "); + caller_sval->dump_to_pp (pp, simple); + pp_newline (pp); + } + + pp_newline (pp); + pp_string (pp, "region mappings from summary to caller:"); + pp_newline (pp); + auto_vec <const region *> summary_regs; + for (auto kv : m_map_region_from_summary_to_caller) + summary_regs.safe_push (kv.first); + summary_regs.qsort (region::cmp_ptr_ptr); + for (auto summary_reg : summary_regs) + { + pp_string (pp, "reg in summary: "); + if (summary_reg) + summary_reg->dump_to_pp (pp, simple); + else + pp_string (pp, "(null)"); + pp_newline (pp); + + const region *caller_reg + = *((const_cast<region_map_t &> + (m_map_region_from_summary_to_caller)).get (summary_reg)); + pp_string (pp, " reg in caller: "); + if (caller_reg) + caller_reg->dump_to_pp (pp, simple); + else + pp_string (pp, "(null)"); + pp_newline (pp); + } +} + +/* Dump a multiline representation of this object to FILE. */ + +void +call_summary_replay::dump (FILE *fp, bool simple) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = fp; + dump_to_pp (&pp, simple); + pp_flush (&pp); +} + +/* Dump a multiline representation of this object to stderr. */ + +DEBUG_FUNCTION void +call_summary_replay::dump (bool simple) const +{ + dump (stderr, simple); +} + +} // namespace ana + +#endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/call-summary.h b/gcc/analyzer/call-summary.h new file mode 100644 index 0000000..07cd3f5 --- /dev/null +++ b/gcc/analyzer/call-summary.h @@ -0,0 +1,117 @@ +/* Classes for working with summaries of function calls. + Copyright (C) 2022 David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_ANALYZER_CALL_SUMMARY_H +#define GCC_ANALYZER_CALL_SUMMARY_H + +namespace ana { + +/* A class summarizing one particular outcome of a function that + we've already analyzed. + This lets us efficiently replay the analysis when we see calls + to the function, providing an approximation of the behavior of + the function without having to execute within the function itself. */ + +class call_summary +{ +public: + call_summary (per_function_data *per_fn_data, + const exploded_node *enode) + : m_per_fn_data (per_fn_data), + m_enode (enode) + {} + const program_state &get_state () const; + tree get_fndecl () const; + + label_text get_desc () const; + + void dump_to_pp (const extrinsic_state &ext_state, + pretty_printer *pp, + bool simple) const; + void dump (const extrinsic_state &ext_state, FILE *fp, bool simple) const; + void dump (const extrinsic_state &ext_state, bool simple) const; + +private: + void get_user_facing_desc (pretty_printer *pp) const; + + per_function_data *const m_per_fn_data; + const exploded_node *const m_enode; +}; + +/* A class for handling replaying a specific call summary at + a specific call site. + + Supports remapping svalues and regions, e.g. remapping + INIT_VAL(param of callee) + to: + whatever that argument is at the call site. */ + +class call_summary_replay +{ +public: + call_summary_replay (const call_details &cd, + function *called_fn, + call_summary *m_summary, + const extrinsic_state &ext_state); + + const call_details &get_call_details () const { return m_cd; } + const gcall *get_call_stmt () const { return m_cd.get_call_stmt (); } + region_model_manager *get_manager () const { return m_cd.get_manager (); } + store_manager *get_store_manager () const + { + return get_manager ()->get_store_manager (); + } + region_model_context *get_ctxt () const { return m_cd.get_ctxt (); } + region_model *get_caller_model () const { return m_cd.get_model (); } + + const svalue *convert_svalue_from_summary (const svalue *); + const region *convert_region_from_summary (const region *); + const binding_key *convert_key_from_summary (const binding_key *); + + void add_svalue_mapping (const svalue *summary_sval, + const svalue *caller_sval); + void add_region_mapping (const region *summary_sval, + const region *caller_sval); + + void dump_to_pp (pretty_printer *pp, bool simple) const; + void dump (FILE *fp, bool simple) const; + void dump (bool simple) const; + +private: + DISABLE_COPY_AND_ASSIGN (call_summary_replay); + + const svalue *convert_svalue_from_summary_1 (const svalue *); + const region *convert_region_from_summary_1 (const region *); + + const call_details &m_cd; + call_summary *m_summary; + const extrinsic_state &m_ext_state; + + // Mapping from svalues in summary to svalues for callsite: + typedef hash_map <const svalue *, const svalue *> svalue_map_t; + svalue_map_t m_map_svalue_from_summary_to_caller; + + // Mapping from regions in summary to regions for callsite: + typedef hash_map <const region *, const region *> region_map_t; + region_map_t m_map_region_from_summary_to_caller; +}; + +} // namespace ana + +#endif /* GCC_ANALYZER_CALL_SUMMARY_H */ diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index 22bae2f..371111b 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -28,26 +28,18 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "gimple-pretty-print.h" #include "fold-const.h" -#include "function.h" #include "diagnostic-path.h" #include "options.h" #include "cgraph.h" -#include "function.h" #include "cfg.h" #include "digraph.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" #include "diagnostic-event-id.h" -#include "shortest-paths.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" #include "ordered-hash-map.h" -#include "selftest.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" diff --git a/gcc/analyzer/complexity.cc b/gcc/analyzer/complexity.cc index 40ae8f7..39fbbc1 100644 --- a/gcc/analyzer/complexity.cc +++ b/gcc/analyzer/complexity.cc @@ -38,11 +38,7 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "fold-const.h" #include "tree-pretty-print.h" -#include "tristate.h" #include "bitmap.h" -#include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "options.h" diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc index 4133a13..96ae073 100644 --- a/gcc/analyzer/constraint-manager.cc +++ b/gcc/analyzer/constraint-manager.cc @@ -30,8 +30,6 @@ along with GCC; see the file COPYING3. If not see #include "selftest.h" #include "diagnostic-core.h" #include "graphviz.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "ordered-hash-map.h" #include "options.h" @@ -41,13 +39,13 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/supergraph.h" #include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" +#include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" #include "tree-pretty-print.h" @@ -3043,6 +3041,60 @@ constraint_manager::for_each_fact (fact_visitor *visitor) const } } +/* Subclass of fact_visitor for use by + constraint_manager::replay_call_summary. */ + +class replay_fact_visitor : public fact_visitor +{ +public: + replay_fact_visitor (call_summary_replay &r, + constraint_manager *out) + : m_r (r), m_out (out), m_feasible (true) + {} + + bool feasible_p () const { return m_feasible; } + + void on_fact (const svalue *lhs, enum tree_code code, const svalue *rhs) + final override + { + const svalue *caller_lhs = m_r.convert_svalue_from_summary (lhs); + if (!caller_lhs) + return; + const svalue *caller_rhs = m_r.convert_svalue_from_summary (rhs); + if (!caller_rhs) + return; + if (!m_out->add_constraint (caller_lhs, code, caller_rhs)) + m_feasible = false; + } + + void on_ranges (const svalue *lhs_sval, + const bounded_ranges *ranges) final override + { + const svalue *caller_lhs = m_r.convert_svalue_from_summary (lhs_sval); + if (!caller_lhs) + return; + if (!m_out->add_bounded_ranges (caller_lhs, ranges)) + m_feasible = false; + } + +private: + call_summary_replay &m_r; + constraint_manager *m_out; + bool m_feasible; +}; + +/* Attempt to use R to replay the constraints from SUMMARY into this object. + Return true if it is feasible. */ + +bool +constraint_manager::replay_call_summary (call_summary_replay &r, + const constraint_manager &summary) +{ + replay_fact_visitor v (r, this); + summary.for_each_fact (&v); + return v.feasible_p (); +} + /* Assert that this object is valid. */ void diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h index 1271f18..daacaa3 100644 --- a/gcc/analyzer/constraint-manager.h +++ b/gcc/analyzer/constraint-manager.h @@ -487,6 +487,9 @@ public: bounded_ranges_manager *get_range_manager () const; + bool replay_call_summary (call_summary_replay &r, + const constraint_manager &summary); + auto_delete_vec<equiv_class> m_equiv_classes; auto_vec<constraint> m_constraints; auto_vec<bounded_ranges_constraint> m_bounded_ranges_constraints; diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 2d185a1..695bde5 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -29,15 +29,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "diagnostic-event-id.h" #include "diagnostic-path.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" -#include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" -#include "selftest.h" #include "ordered-hash-map.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 742ac02..46bcaed 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -25,9 +25,6 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "fold-const.h" #include "gcc-rich-location.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" #include "diagnostic-core.h" #include "diagnostic-event-id.h" #include "diagnostic-path.h" @@ -35,10 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print.h" #include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" #include "ordered-hash-map.h" -#include "selftest.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" @@ -72,6 +66,7 @@ along with GCC; see the file COPYING3. If not see #include "attribs.h" #include "tree-dfa.h" #include "analyzer/known-function-manager.h" +#include "analyzer/call-summary.h" /* For an overview, see gcc/doc/analyzer.texi. */ @@ -171,6 +166,8 @@ void impl_region_model_context::on_unknown_change (const svalue *sval, bool is_mutable) { + if (!sval->can_have_associated_state_p ()) + return; for (sm_state_map *smap : m_new_state->m_checker_states) smap->on_unknown_change (sval, is_mutable, m_ext_state); } @@ -1425,6 +1422,25 @@ exploded_node::on_stmt (exploded_graph &eg, &old_state, state, uncertainty, path_ctxt, stmt); + /* Handle call summaries here. */ + if (cgraph_edge *cgedge + = supergraph_call_edge (snode->get_function (), stmt)) + if (eg.get_analysis_plan ().use_summary_p (cgedge)) + { + function *called_fn = get_ultimate_function_for_cgraph_edge (cgedge); + per_function_data *called_fn_data + = eg.get_per_function_data (called_fn); + if (called_fn_data) + return replay_call_summaries (eg, + snode, + as_a <const gcall *> (stmt), + state, + path_ctxt, + called_fn, + called_fn_data, + &ctxt); + } + bool unknown_side_effects = false; bool terminate_path = false; @@ -1520,6 +1536,140 @@ exploded_node::on_stmt_post (const gimple *stmt, state->m_region_model->on_call_post (call, unknown_side_effects, ctxt); } +/* A concrete call_info subclass representing a replay of a call summary. */ + +class call_summary_edge_info : public call_info +{ +public: + call_summary_edge_info (const call_details &cd, + function *called_fn, + call_summary *summary, + const extrinsic_state &ext_state) + : call_info (cd), + m_called_fn (called_fn), + m_summary (summary), + m_ext_state (ext_state) + {} + + bool update_state (program_state *state, + const exploded_edge *, + region_model_context *ctxt) const final override + { + /* Update STATE based on summary_end_state. */ + call_details cd (get_call_details (state->m_region_model, ctxt)); + call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state); + const program_state &summary_end_state = m_summary->get_state (); + return state->replay_call_summary (r, summary_end_state); + } + + bool update_model (region_model *model, + const exploded_edge *, + region_model_context *ctxt) const final override + { + /* Update STATE based on summary_end_state. */ + call_details cd (get_call_details (model, ctxt)); + call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state); + const program_state &summary_end_state = m_summary->get_state (); + model->replay_call_summary (r, *summary_end_state.m_region_model); + return true; + } + + label_text get_desc (bool /*can_colorize*/) const final override + { + return m_summary->get_desc (); + } + +private: + function *m_called_fn; + call_summary *m_summary; + const extrinsic_state &m_ext_state; +}; + +/* Use PATH_CTXT to bifurcate, which when handled will add custom edges + for a replay of the various feasible summaries in CALLED_FN_DATA. */ + +exploded_node::on_stmt_flags +exploded_node::replay_call_summaries (exploded_graph &eg, + const supernode *snode, + const gcall *call_stmt, + program_state *state, + path_context *path_ctxt, + function *called_fn, + per_function_data *called_fn_data, + region_model_context *ctxt) +{ + logger *logger = eg.get_logger (); + LOG_SCOPE (logger); + + gcc_assert (called_fn); + gcc_assert (called_fn_data); + + /* Each summary will call bifurcate on the PATH_CTXT. */ + for (auto summary : called_fn_data->m_summaries) + replay_call_summary (eg, snode, call_stmt, state, + path_ctxt, called_fn, summary, ctxt); + path_ctxt->terminate_path (); + + return on_stmt_flags (); +} + +/* Use PATH_CTXT to bifurcate, which when handled will add a + custom edge for a replay of SUMMARY, if the summary's + conditions are feasible based on the current state. */ + +void +exploded_node::replay_call_summary (exploded_graph &eg, + const supernode *snode, + const gcall *call_stmt, + program_state *old_state, + path_context *path_ctxt, + function *called_fn, + call_summary *summary, + region_model_context *ctxt) +{ + logger *logger = eg.get_logger (); + LOG_SCOPE (logger); + gcc_assert (snode); + gcc_assert (call_stmt); + gcc_assert (old_state); + gcc_assert (called_fn); + gcc_assert (summary); + + if (logger) + logger->log ("using %s as summary for call to %qE from %qE", + summary->get_desc ().get (), + called_fn->decl, + snode->get_function ()->decl); + const extrinsic_state &ext_state = eg.get_ext_state (); + const program_state &summary_end_state = summary->get_state (); + if (logger) + { + pretty_printer *pp = logger->get_printer (); + + logger->start_log_line (); + pp_string (pp, "callsite state: "); + old_state->dump_to_pp (ext_state, true, false, pp); + logger->end_log_line (); + + logger->start_log_line (); + pp_string (pp, "summary end state: "); + summary_end_state.dump_to_pp (ext_state, true, false, pp); + logger->end_log_line (); + } + + program_state new_state (*old_state); + + call_details cd (call_stmt, new_state.m_region_model, ctxt); + call_summary_replay r (cd, called_fn, summary, ext_state); + + if (path_ctxt) + path_ctxt->bifurcate (new call_summary_edge_info (cd, + called_fn, + summary, + ext_state)); +} + + /* Consider the effect of following superedge SUCC from this node. Return true if it's feasible to follow the edge, or false @@ -2115,6 +2265,20 @@ stats::get_total_enodes () const return result; } +/* struct per_function_data. */ + +per_function_data::~per_function_data () +{ + for (auto iter : m_summaries) + delete iter; +} + +void +per_function_data::add_call_summary (exploded_node *node) +{ + m_summaries.safe_push (new call_summary (this, node)); +} + /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */ strongly_connected_components:: @@ -3980,7 +4144,7 @@ exploded_graph::process_node (exploded_node *node) NULL, // uncertainty_t *uncertainty NULL, // path_context *path_ctxt stmt); - if (edge_info->update_model (bifurcated_new_state.m_region_model, + if (edge_info->update_state (&bifurcated_new_state, NULL, /* no exploded_edge yet. */ &bifurcation_ctxt)) { @@ -5350,24 +5514,17 @@ public: pretty_printer *pp = gv->get_pp (); dump_dot_id (pp); - pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=<", + pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"", "lightgrey"); - pp_string (pp, "<TABLE BORDER=\"0\">"); pp_write_text_to_stream (pp); - gv->begin_trtd (); pp_printf (pp, "VCG: %i: %s", m_index, function_name (m_fun)); - gv->end_tdtr (); pp_newline (pp); - gv->begin_trtd (); pp_printf (pp, "supernodes: %i\n", m_num_supernodes); - gv->end_tdtr (); pp_newline (pp); - gv->begin_trtd (); pp_printf (pp, "superedges: %i\n", m_num_superedges); - gv->end_tdtr (); pp_newline (pp); if (args.m_eg) @@ -5380,9 +5537,7 @@ public: if (enode->get_point ().get_function () == m_fun) num_enodes++; } - gv->begin_trtd (); pp_printf (pp, "enodes: %i\n", num_enodes); - gv->end_tdtr (); pp_newline (pp); // TODO: also show the per-callstring breakdown @@ -5404,11 +5559,8 @@ public: } if (num_enodes > 0) { - 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_tdtr (); } } @@ -5417,14 +5569,20 @@ public: if (data) { pp_newline (pp); - 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_tdtr (); + for (auto summary : data->m_summaries) + { + pp_printf (pp, "\nsummary: %s:\n", summary->get_desc ().get ()); + const extrinsic_state &ext_state = args.m_eg->get_ext_state (); + const program_state &state = summary->get_state (); + state.dump_to_pp (ext_state, false, true, pp); + pp_newline (pp); + } } } - pp_string (pp, "</TABLE>>];\n\n"); + pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true); + pp_string (pp, "\"];\n\n"); pp_flush (pp); } diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index f957568..11e46ca 100644 --- a/gcc/analyzer/exploded-graph.h +++ b/gcc/analyzer/exploded-graph.h @@ -21,6 +21,15 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_EXPLODED_GRAPH_H #define GCC_ANALYZER_EXPLODED_GRAPH_H +#include "alloc-pool.h" +#include "fibonacci_heap.h" +#include "supergraph.h" +#include "sbitmap.h" +#include "shortest-paths.h" +#include "analyzer/sm.h" +#include "analyzer/program-state.h" +#include "analyzer/diagnostic-manager.h" + namespace ana { /* Concrete implementation of region_model_context, wiring it up to the @@ -258,6 +267,23 @@ class exploded_node : public dnode<eg_traits> bool unknown_side_effects, region_model_context *ctxt); + on_stmt_flags replay_call_summaries (exploded_graph &eg, + const supernode *snode, + const gcall *call_stmt, + program_state *state, + path_context *path_ctxt, + function *called_fn, + per_function_data *called_fn_data, + region_model_context *ctxt); + void replay_call_summary (exploded_graph &eg, + const supernode *snode, + const gcall *call_stmt, + program_state *state, + path_context *path_ctxt, + function *called_fn, + call_summary *summary, + region_model_context *ctxt); + bool on_edge (exploded_graph &eg, const superedge *succ, program_point *next_point, @@ -611,13 +637,11 @@ struct per_call_string_data struct per_function_data { per_function_data () {} + ~per_function_data (); - void add_call_summary (exploded_node *node) - { - m_summaries.safe_push (node); - } + void add_call_summary (exploded_node *node); - auto_vec<exploded_node *> m_summaries; + auto_vec<call_summary *> m_summaries; }; diff --git a/gcc/analyzer/feasible-graph.cc b/gcc/analyzer/feasible-graph.cc index fe7e79f..a946e4c 100644 --- a/gcc/analyzer/feasible-graph.cc +++ b/gcc/analyzer/feasible-graph.cc @@ -29,15 +29,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "diagnostic-event-id.h" #include "diagnostic-path.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" -#include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" -#include "selftest.h" #include "ordered-hash-map.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc index f0fd4fc..48fb005 100644 --- a/gcc/analyzer/known-function-manager.cc +++ b/gcc/analyzer/known-function-manager.cc @@ -22,7 +22,6 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "tree.h" -#include "function.h" #include "analyzer/analyzer.h" #include "diagnostic-core.h" #include "analyzer/analyzer-logging.h" diff --git a/gcc/analyzer/known-function-manager.h b/gcc/analyzer/known-function-manager.h index fbde853..2b95b7e 100644 --- a/gcc/analyzer/known-function-manager.h +++ b/gcc/analyzer/known-function-manager.h @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_KNOWN_FUNCTION_MANAGER_H #define GCC_ANALYZER_KNOWN_FUNCTION_MANAGER_H +#include "analyzer/analyzer-logging.h" + namespace ana { class known_function_manager : public log_user diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc index eff050f..50a8afc 100644 --- a/gcc/analyzer/pending-diagnostic.cc +++ b/gcc/analyzer/pending-diagnostic.cc @@ -24,8 +24,6 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "intl.h" #include "diagnostic.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-event-id.h" #include "analyzer/analyzer-logging.h" @@ -34,8 +32,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" -#include "selftest.h" -#include "tristate.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" @@ -50,10 +46,6 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" -#include "sbitmap.h" #include "analyzer/exploded-graph.h" #include "diagnostic-path.h" #include "analyzer/checker-path.h" diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc index 6c296d5..cfa4dda 100644 --- a/gcc/analyzer/program-point.cc +++ b/gcc/analyzer/program-point.cc @@ -24,7 +24,6 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "gimple-pretty-print.h" #include "gcc-rich-location.h" -#include "json.h" #include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" @@ -41,14 +40,11 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/program-point.h" #include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" #include "selftest.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/sm.h" #include "analyzer/program-state.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" #include "diagnostic-event-id.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h index 63f7224..f72b86d 100644 --- a/gcc/analyzer/program-point.h +++ b/gcc/analyzer/program-point.h @@ -21,6 +21,9 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_PROGRAM_POINT_H #define GCC_ANALYZER_PROGRAM_POINT_H +#include "pretty-print.h" +#include "analyzer/call-string.h" + namespace ana { class exploded_graph; diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index f0f4046..7537dc0 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -24,14 +24,11 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "diagnostic-core.h" #include "diagnostic.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" #include "ordered-hash-map.h" #include "selftest.h" #include "analyzer/call-string.h" @@ -40,9 +37,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/region-model.h" #include "analyzer/program-state.h" #include "analyzer/constraint-manager.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" #include "diagnostic-event-id.h" #include "analyzer/pending-diagnostic.h" #include "analyzer/diagnostic-manager.h" @@ -56,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" #include "analyzer/state-purge.h" +#include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" #if ENABLE_ANALYZER @@ -743,6 +738,31 @@ program_state::program_state (const extrinsic_state &ext_state) } } +/* Attempt to to use R to replay SUMMARY into this object. + Return true if it is possible. */ + +bool +sm_state_map::replay_call_summary (call_summary_replay &r, + const sm_state_map &summary) +{ + for (auto kv : summary.m_map) + { + const svalue *summary_sval = kv.first; + const svalue *caller_sval = r.convert_svalue_from_summary (summary_sval); + if (!caller_sval) + continue; + const svalue *summary_origin = kv.second.m_origin; + const svalue *caller_origin + = (summary_origin + ? r.convert_svalue_from_summary (summary_origin) + : NULL); + // caller_origin can be NULL. + m_map.put (caller_sval, entry_t (kv.second.m_state, caller_origin)); + } + m_global_state = summary.m_global_state; + return true; +} + /* program_state's copy ctor. */ program_state::program_state (const program_state &other) @@ -1437,6 +1457,28 @@ program_state::detect_leaks (const program_state &src_state, dest_state.m_region_model->unset_dynamic_extents (reg); } +/* Attempt to to use R to replay SUMMARY into this object. + Return true if it is possible. */ + +bool +program_state::replay_call_summary (call_summary_replay &r, + const program_state &summary) +{ + if (!m_region_model->replay_call_summary (r, *summary.m_region_model)) + return false; + + for (unsigned sm_idx = 0; sm_idx < m_checker_states.length (); sm_idx++) + { + const sm_state_map *summary_sm_map = summary.m_checker_states[sm_idx]; + m_checker_states[sm_idx]->replay_call_summary (r, *summary_sm_map); + } + + if (!summary.m_valid) + m_valid = false; + + return true; +} + /* Handle calls to "__analyzer_dump_state". */ void diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index baab787..ad40578 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -171,6 +171,9 @@ public: static const svalue * canonicalize_svalue (const svalue *sval, const extrinsic_state &ext_state); + bool replay_call_summary (call_summary_replay &r, + const sm_state_map &summary); + private: const state_machine &m_sm; map_t m_map; @@ -273,6 +276,9 @@ public: const extrinsic_state &ext_state, region_model_context *ctxt); + bool replay_call_summary (call_summary_replay &r, + const program_state &summary); + void impl_call_analyzer_dump_state (const gcall *call, const extrinsic_state &ext_state, region_model_context *ctxt); diff --git a/gcc/analyzer/region-model-asm.cc b/gcc/analyzer/region-model-asm.cc index bb73e6e..b4c1f91 100644 --- a/gcc/analyzer/region-model-asm.cc +++ b/gcc/analyzer/region-model-asm.cc @@ -28,9 +28,6 @@ along with GCC; see the file COPYING3. If not see #include "gimple-iterator.h" #include "diagnostic-core.h" #include "pretty-print.h" -#include "tristate.h" -#include "selftest.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "options.h" diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 71fb277..8f4940a 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -38,18 +38,11 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "diagnostic-color.h" #include "diagnostic-metadata.h" -#include "tristate.h" #include "bitmap.h" -#include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" #include "options.h" -#include "cgraph.h" -#include "cfg.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "sbitmap.h" #include "analyzer/call-string.h" @@ -374,6 +367,16 @@ region_model::impl_call_analyzer_eval (const gcall *call, warning_at (call->location, 0, "%s", t.as_string ()); } +/* Handle the on_call_pre part of "__analyzer_get_unknown_ptr". */ + +void +region_model::impl_call_analyzer_get_unknown_ptr (const call_details &cd) +{ + const svalue *ptr_sval + = m_mgr->get_or_create_unknown_svalue (cd.get_lhs_type ()); + cd.maybe_set_lhs (ptr_sval); +} + /* Handle the on_call_pre part of "__builtin_expect" etc. */ void diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index cbda77f..f5999e6 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -38,18 +38,11 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "fold-const.h" #include "tree-pretty-print.h" -#include "tristate.h" #include "bitmap.h" -#include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" #include "options.h" -#include "cgraph.h" -#include "cfg.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "sbitmap.h" #include "analyzer/call-string.h" @@ -432,6 +425,17 @@ region_model_manager::maybe_fold_unaryop (tree type, enum tree_code op, } } break; + case NEGATE_EXPR: + { + /* -(-(VAL)) is VAL, for integer types. */ + if (const unaryop_svalue *unaryop = arg->dyn_cast_unaryop_svalue ()) + if (unaryop->get_op () == NEGATE_EXPR + && type == unaryop->get_type () + && type + && INTEGRAL_TYPE_P (type)) + return unaryop->get_arg (); + } + break; } /* Constants. */ @@ -1143,10 +1147,11 @@ region_model_manager::get_or_create_unmergeable (const svalue *arg) and ITER_SVAL at POINT, creating it if necessary. */ const svalue * -region_model_manager::get_or_create_widening_svalue (tree type, - const program_point &point, - const svalue *base_sval, - const svalue *iter_sval) +region_model_manager:: +get_or_create_widening_svalue (tree type, + const function_point &point, + const svalue *base_sval, + const svalue *iter_sval) { gcc_assert (base_sval->get_kind () != SK_WIDENING); gcc_assert (iter_sval->get_kind () != SK_WIDENING); @@ -1263,6 +1268,33 @@ get_or_create_asm_output_svalue (tree type, return asm_output_sval; } +/* Return the svalue * of type TYPE for OUTPUT_IDX of a deterministic + asm stmt with string ASM_STRING with NUM_OUTPUTS outputs, given + INPUTS as inputs. */ + +const svalue * +region_model_manager:: +get_or_create_asm_output_svalue (tree type, + const char *asm_string, + unsigned output_idx, + unsigned num_outputs, + const vec<const svalue *> &inputs) +{ + gcc_assert (inputs.length () <= asm_output_svalue::MAX_INPUTS); + + if (const svalue *folded + = maybe_fold_asm_output_svalue (type, inputs)) + return folded; + + asm_output_svalue::key_t key (type, asm_string, output_idx, inputs); + if (asm_output_svalue **slot = m_asm_output_values_map.get (key)) + return *slot; + asm_output_svalue *asm_output_sval + = new asm_output_svalue (type, asm_string, output_idx, num_outputs, inputs); + RETURN_UNKNOWN_IF_TOO_COMPLEX (asm_output_sval); + m_asm_output_values_map.put (key, asm_output_sval); + return asm_output_sval; +} /* Return the svalue * of type TYPE for the result of a call to FNDECL with __attribute__((const)), given INPUTS as inputs. */ diff --git a/gcc/analyzer/region-model-manager.h b/gcc/analyzer/region-model-manager.h new file mode 100644 index 0000000..3d8f76e --- /dev/null +++ b/gcc/analyzer/region-model-manager.h @@ -0,0 +1,318 @@ +/* Consolidation of svalues and regions. + Copyright (C) 2020-2022 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_ANALYZER_REGION_MODEL_MANAGER_H +#define GCC_ANALYZER_REGION_MODEL_MANAGER_H + +namespace ana { + +/* A class responsible for owning and consolidating region and svalue + instances. + region and svalue instances are immutable as far as clients are + concerned, so they are provided as "const" ptrs. */ + +class region_model_manager +{ +public: + region_model_manager (logger *logger = NULL); + ~region_model_manager (); + + /* call_string consolidation. */ + const call_string &get_empty_call_string () const + { + return m_empty_call_string; + } + + /* svalue consolidation. */ + const svalue *get_or_create_constant_svalue (tree cst_expr); + const svalue *get_or_create_int_cst (tree type, poly_int64); + const svalue *get_or_create_unknown_svalue (tree type); + const svalue *get_or_create_setjmp_svalue (const setjmp_record &r, + tree type); + const svalue *get_or_create_poisoned_svalue (enum poison_kind kind, + tree type); + const svalue *get_or_create_initial_value (const region *reg); + const svalue *get_ptr_svalue (tree ptr_type, const region *pointee); + const svalue *get_or_create_unaryop (tree type, enum tree_code op, + const svalue *arg); + const svalue *get_or_create_cast (tree type, const svalue *arg); + const svalue *get_or_create_binop (tree type, + enum tree_code op, + const svalue *arg0, const svalue *arg1); + const svalue *get_or_create_sub_svalue (tree type, + const svalue *parent_svalue, + const region *subregion); + const svalue *get_or_create_repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue); + const svalue *get_or_create_bits_within (tree type, + const bit_range &bits, + const svalue *inner_svalue); + const svalue *get_or_create_unmergeable (const svalue *arg); + const svalue *get_or_create_widening_svalue (tree type, + const function_point &point, + const svalue *base_svalue, + const svalue *iter_svalue); + const svalue *get_or_create_compound_svalue (tree type, + const binding_map &map); + const svalue *get_or_create_conjured_svalue (tree type, const gimple *stmt, + const region *id_reg, + const conjured_purge &p); + const svalue * + get_or_create_asm_output_svalue (tree type, + const gasm *asm_stmt, + unsigned output_idx, + const vec<const svalue *> &inputs); + const svalue * + get_or_create_asm_output_svalue (tree type, + const char *asm_string, + unsigned output_idx, + unsigned num_outputs, + const vec<const svalue *> &inputs); + const svalue * + get_or_create_const_fn_result_svalue (tree type, + tree fndecl, + const vec<const svalue *> &inputs); + + const svalue *maybe_get_char_from_string_cst (tree string_cst, + tree byte_offset_cst); + + /* Dynamically-allocated svalue instances. + The number of these within the analysis can grow arbitrarily. + They are still owned by the manager. */ + const svalue *create_unique_svalue (tree type); + + /* region consolidation. */ + const stack_region * get_stack_region () const { return &m_stack_region; } + const heap_region *get_heap_region () const { return &m_heap_region; } + const code_region *get_code_region () const { return &m_code_region; } + const globals_region *get_globals_region () const + { + return &m_globals_region; + } + const function_region *get_region_for_fndecl (tree fndecl); + const label_region *get_region_for_label (tree label); + const decl_region *get_region_for_global (tree expr); + const region *get_field_region (const region *parent, tree field); + const region *get_element_region (const region *parent, + tree element_type, + const svalue *index); + const region *get_offset_region (const region *parent, + tree type, + const svalue *byte_offset); + const region *get_sized_region (const region *parent, + tree type, + const svalue *byte_size_sval); + const region *get_cast_region (const region *original_region, + tree type); + const frame_region *get_frame_region (const frame_region *calling_frame, + function *fun); + const region *get_symbolic_region (const svalue *sval); + const string_region *get_region_for_string (tree string_cst); + const region *get_bit_range (const region *parent, tree type, + const bit_range &bits); + const var_arg_region *get_var_arg_region (const frame_region *parent, + unsigned idx); + + const region *get_unknown_symbolic_region (tree region_type); + + const region * + get_region_for_unexpected_tree_code (region_model_context *ctxt, + tree t, + const dump_location_t &loc); + + unsigned alloc_region_id () { return m_next_region_id++; } + + store_manager *get_store_manager () { return &m_store_mgr; } + bounded_ranges_manager *get_range_manager () const { return m_range_mgr; } + + known_function_manager *get_known_function_manager () + { + return &m_known_fn_mgr; + } + + /* Dynamically-allocated region instances. + The number of these within the analysis can grow arbitrarily. + They are still owned by the manager. */ + const region *create_region_for_heap_alloc (); + const region *create_region_for_alloca (const frame_region *frame); + + void log_stats (logger *logger, bool show_objs) const; + + void begin_checking_feasibility (void) { m_checking_feasibility = true; } + void end_checking_feasibility (void) { m_checking_feasibility = false; } + + logger *get_logger () const { return m_logger; } + + void dump_untracked_regions () const; + +private: + bool too_complex_p (const complexity &c) const; + bool reject_if_too_complex (svalue *sval); + + const svalue *maybe_fold_unaryop (tree type, enum tree_code op, + const svalue *arg); + const svalue *maybe_fold_binop (tree type, enum tree_code op, + const svalue *arg0, const svalue *arg1); + const svalue *maybe_fold_sub_svalue (tree type, + const svalue *parent_svalue, + const region *subregion); + const svalue *maybe_fold_repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue); + const svalue *maybe_fold_bits_within_svalue (tree type, + const bit_range &bits, + const svalue *inner_svalue); + const svalue *maybe_undo_optimize_bit_field_compare (tree type, + const compound_svalue *compound_sval, + tree cst, const svalue *arg1); + const svalue *maybe_fold_asm_output_svalue (tree type, + const vec<const svalue *> &inputs); + + logger *m_logger; + + const call_string m_empty_call_string; + + unsigned m_next_region_id; + root_region m_root_region; + stack_region m_stack_region; + heap_region m_heap_region; + + /* svalue consolidation. */ + typedef hash_map<tree, constant_svalue *> constants_map_t; + constants_map_t m_constants_map; + + typedef hash_map<tree, unknown_svalue *> unknowns_map_t; + unknowns_map_t m_unknowns_map; + const unknown_svalue *m_unknown_NULL; + + typedef hash_map<poisoned_svalue::key_t, + poisoned_svalue *> poisoned_values_map_t; + poisoned_values_map_t m_poisoned_values_map; + + typedef hash_map<setjmp_svalue::key_t, + setjmp_svalue *> setjmp_values_map_t; + setjmp_values_map_t m_setjmp_values_map; + + typedef hash_map<const region *, initial_svalue *> initial_values_map_t; + initial_values_map_t m_initial_values_map; + + typedef hash_map<region_svalue::key_t, region_svalue *> pointer_values_map_t; + pointer_values_map_t m_pointer_values_map; + + typedef hash_map<unaryop_svalue::key_t, + unaryop_svalue *> unaryop_values_map_t; + unaryop_values_map_t m_unaryop_values_map; + + typedef hash_map<binop_svalue::key_t, binop_svalue *> binop_values_map_t; + binop_values_map_t m_binop_values_map; + + typedef hash_map<sub_svalue::key_t, sub_svalue *> sub_values_map_t; + sub_values_map_t m_sub_values_map; + + typedef hash_map<repeated_svalue::key_t, + repeated_svalue *> repeated_values_map_t; + repeated_values_map_t m_repeated_values_map; + + typedef hash_map<bits_within_svalue::key_t, + bits_within_svalue *> bits_within_values_map_t; + bits_within_values_map_t m_bits_within_values_map; + + typedef hash_map<const svalue *, + unmergeable_svalue *> unmergeable_values_map_t; + unmergeable_values_map_t m_unmergeable_values_map; + + typedef hash_map<widening_svalue::key_t, + widening_svalue */*, + widening_svalue::key_t::hash_map_traits*/> + widening_values_map_t; + widening_values_map_t m_widening_values_map; + + typedef hash_map<compound_svalue::key_t, + compound_svalue *> compound_values_map_t; + compound_values_map_t m_compound_values_map; + + typedef hash_map<conjured_svalue::key_t, + conjured_svalue *> conjured_values_map_t; + conjured_values_map_t m_conjured_values_map; + + typedef hash_map<asm_output_svalue::key_t, + asm_output_svalue *> asm_output_values_map_t; + asm_output_values_map_t m_asm_output_values_map; + + typedef hash_map<const_fn_result_svalue::key_t, + const_fn_result_svalue *> const_fn_result_values_map_t; + const_fn_result_values_map_t m_const_fn_result_values_map; + + bool m_checking_feasibility; + + /* "Dynamically-allocated" svalue instances. + The number of these within the analysis can grow arbitrarily. + They are still owned by the manager. */ + auto_delete_vec<svalue> m_managed_dynamic_svalues; + + /* Maximum complexity of svalues that weren't rejected. */ + complexity m_max_complexity; + + /* region consolidation. */ + + code_region m_code_region; + typedef hash_map<tree, function_region *> fndecls_map_t; + typedef fndecls_map_t::iterator fndecls_iterator_t; + fndecls_map_t m_fndecls_map; + + typedef hash_map<tree, label_region *> labels_map_t; + typedef labels_map_t::iterator labels_iterator_t; + labels_map_t m_labels_map; + + globals_region m_globals_region; + typedef hash_map<tree, decl_region *> globals_map_t; + typedef globals_map_t::iterator globals_iterator_t; + globals_map_t m_globals_map; + + consolidation_map<field_region> m_field_regions; + consolidation_map<element_region> m_element_regions; + consolidation_map<offset_region> m_offset_regions; + consolidation_map<sized_region> m_sized_regions; + consolidation_map<cast_region> m_cast_regions; + consolidation_map<frame_region> m_frame_regions; + consolidation_map<symbolic_region> m_symbolic_regions; + + typedef hash_map<tree, string_region *> string_map_t; + string_map_t m_string_map; + + consolidation_map<bit_range_region> m_bit_range_regions; + consolidation_map<var_arg_region> m_var_arg_regions; + + store_manager m_store_mgr; + + bounded_ranges_manager *m_range_mgr; + + known_function_manager m_known_fn_mgr; + + /* "Dynamically-allocated" region instances. + The number of these within the analysis can grow arbitrarily. + They are still owned by the manager. */ + auto_delete_vec<region> m_managed_dynamic_regions; +}; + +} // namespace ana + +#endif /* GCC_ANALYZER_REGION_MODEL_MANAGER_H */ diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc index 12d09c3..be1372c 100644 --- a/gcc/analyzer/region-model-reachability.cc +++ b/gcc/analyzer/region-model-reachability.cc @@ -36,23 +36,18 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "fold-const.h" #include "tree-pretty-print.h" -#include "tristate.h" #include "bitmap.h" -#include "selftest.h" -#include "function.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" #include "options.h" -#include "cgraph.h" -#include "cfg.h" -#include "digraph.h" -#include "json.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/region-model-reachability.h" +#include "diagnostic.h" +#include "tree-diagnostic.h" #if ENABLE_ANALYZER diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 22c5287..81ef41e 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -39,18 +39,14 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "diagnostic-color.h" #include "diagnostic-metadata.h" -#include "tristate.h" #include "bitmap.h" #include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" #include "options.h" #include "cgraph.h" #include "cfg.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "sbitmap.h" #include "analyzer/call-string.h" @@ -66,6 +62,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/region-model-reachability.h" #include "analyzer/analyzer-selftests.h" #include "analyzer/program-state.h" +#include "analyzer/call-summary.h" #include "stor-layout.h" #include "attribs.h" #include "tree-object-size.h" @@ -1255,6 +1252,12 @@ region_model::on_stmt_pre (const gimple *stmt, { /* This is handled elsewhere. */ } + else if (is_special_named_call_p (call, "__analyzer_get_unknown_ptr", + 0)) + { + call_details cd (call, this, ctxt); + impl_call_analyzer_get_unknown_ptr (cd); + } else *out_unknown_side_effects = on_call_pre (call, ctxt, out_terminate_path); @@ -5038,11 +5041,8 @@ region_model::maybe_update_for_edge (const superedge &edge, break; case SUPEREDGE_INTRAPROCEDURAL_CALL: - { - const callgraph_superedge *cg_sedge - = as_a <const callgraph_superedge *> (&edge); - update_for_call_summary (*cg_sedge, ctxt); - } + /* This is a no-op for call summaries; we should already + have handled the effect of the call summary at the call stmt. */ break; } @@ -5140,25 +5140,34 @@ region_model::update_for_return_superedge (const return_superedge &return_edge, update_for_return_gcall (call_stmt, ctxt); } -/* Update this region_model with a summary of the effect of calling - and returning from CG_SEDGE. +/* Attempt to to use R to replay SUMMARY into this object. + Return true if it is possible. */ - TODO: Currently this is extremely simplistic: we merely set the - return value to "unknown". A proper implementation would e.g. update - sm-state, and presumably be reworked to support multiple outcomes. */ - -void -region_model::update_for_call_summary (const callgraph_superedge &cg_sedge, - region_model_context *ctxt) +bool +region_model::replay_call_summary (call_summary_replay &r, + const region_model &summary) { - /* For now, set any return value to "unknown". */ - const gcall *call_stmt = cg_sedge.get_call_stmt (); - tree lhs = gimple_call_lhs (call_stmt); - if (lhs) - mark_region_as_unknown (get_lvalue (lhs, ctxt), - ctxt ? ctxt->get_uncertainty () : NULL); + gcc_assert (summary.get_stack_depth () == 1); + + m_store.replay_call_summary (r, summary.m_store); - // TODO: actually implement some kind of summary here + if (!m_constraints->replay_call_summary (r, *summary.m_constraints)) + return false; + + for (auto kv : summary.m_dynamic_extents) + { + const region *summary_reg = kv.first; + const region *caller_reg = r.convert_region_from_summary (summary_reg); + if (!caller_reg) + continue; + const svalue *summary_sval = kv.second; + const svalue *caller_sval = r.convert_svalue_from_summary (summary_sval); + if (!caller_sval) + continue; + m_dynamic_extents.put (caller_reg, caller_sval); + } + + return true; } /* Given a true or false edge guarded by conditional statement COND_STMT, @@ -7123,6 +7132,57 @@ test_sub_svalue_folding () ASSERT_EQ (sub->get_type (), TREE_TYPE (ct.m_x_field)); } +/* Get BIT within VAL as a symbolic value within MGR. */ + +static const svalue * +get_bit (region_model_manager *mgr, + bit_offset_t bit, + unsigned HOST_WIDE_INT val) +{ + const svalue *inner_svalue + = mgr->get_or_create_int_cst (unsigned_type_node, val); + return mgr->get_or_create_bits_within (boolean_type_node, + bit_range (bit, 1), + inner_svalue); +} + +/* Verify that bits_within_svalues are folded as expected. */ + +static void +test_bits_within_svalue_folding () +{ + region_model_manager mgr; + + const svalue *zero = mgr.get_or_create_int_cst (boolean_type_node, 0); + const svalue *one = mgr.get_or_create_int_cst (boolean_type_node, 1); + + { + const unsigned val = 0x0000; + for (unsigned bit = 0; bit < 16; bit++) + ASSERT_EQ (get_bit (&mgr, bit, val), zero); + } + + { + const unsigned val = 0x0001; + ASSERT_EQ (get_bit (&mgr, 0, val), one); + for (unsigned bit = 1; bit < 16; bit++) + ASSERT_EQ (get_bit (&mgr, bit, val), zero); + } + + { + const unsigned val = 0x8000; + for (unsigned bit = 0; bit < 15; bit++) + ASSERT_EQ (get_bit (&mgr, bit, val), zero); + ASSERT_EQ (get_bit (&mgr, 15, val), one); + } + + { + const unsigned val = 0xFFFF; + for (unsigned bit = 0; bit < 16; bit++) + ASSERT_EQ (get_bit (&mgr, bit, val), one); + } +} + /* Test that region::descendent_of_p works as expected. */ static void @@ -7956,7 +8016,7 @@ static void test_widening_constraints () { region_model_manager mgr; - program_point point (program_point::origin (mgr)); + function_point point (program_point::origin (mgr).get_function_point ()); tree int_0 = build_int_cst (integer_type_node, 0); tree int_m1 = build_int_cst (integer_type_node, -1); tree int_1 = build_int_cst (integer_type_node, 1); @@ -8479,6 +8539,7 @@ analyzer_region_model_cc_tests () test_unaryop_svalue_folding (); test_binop_svalue_folding (); test_sub_svalue_folding (); + test_bits_within_svalue_folding (); test_descendent_of_p (); test_bit_range_regions (); test_assignment (); diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index e86720a..635a0c2 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -26,9 +26,11 @@ along with GCC; see the file COPYING3. If not see (Zhongxing Xu, Ted Kremenek, and Jian Zhang) http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf */ +#include "selftest.h" #include "analyzer/svalue.h" #include "analyzer/region.h" #include "analyzer/known-function-manager.h" +#include "analyzer/region-model-manager.h" using namespace ana; @@ -230,294 +232,6 @@ public: virtual void visit_region (const region *) {} }; -} // namespace ana - -namespace ana { - -/* A class responsible for owning and consolidating region and svalue - instances. - region and svalue instances are immutable as far as clients are - concerned, so they are provided as "const" ptrs. */ - -class region_model_manager -{ -public: - region_model_manager (logger *logger = NULL); - ~region_model_manager (); - - /* call_string consolidation. */ - const call_string &get_empty_call_string () const - { - return m_empty_call_string; - } - - /* svalue consolidation. */ - const svalue *get_or_create_constant_svalue (tree cst_expr); - const svalue *get_or_create_int_cst (tree type, poly_int64); - const svalue *get_or_create_unknown_svalue (tree type); - const svalue *get_or_create_setjmp_svalue (const setjmp_record &r, - tree type); - const svalue *get_or_create_poisoned_svalue (enum poison_kind kind, - tree type); - const svalue *get_or_create_initial_value (const region *reg); - const svalue *get_ptr_svalue (tree ptr_type, const region *pointee); - const svalue *get_or_create_unaryop (tree type, enum tree_code op, - const svalue *arg); - const svalue *get_or_create_cast (tree type, const svalue *arg); - const svalue *get_or_create_binop (tree type, - enum tree_code op, - const svalue *arg0, const svalue *arg1); - const svalue *get_or_create_sub_svalue (tree type, - const svalue *parent_svalue, - const region *subregion); - const svalue *get_or_create_repeated_svalue (tree type, - const svalue *outer_size, - const svalue *inner_svalue); - const svalue *get_or_create_bits_within (tree type, - const bit_range &bits, - const svalue *inner_svalue); - const svalue *get_or_create_unmergeable (const svalue *arg); - const svalue *get_or_create_widening_svalue (tree type, - const program_point &point, - const svalue *base_svalue, - const svalue *iter_svalue); - const svalue *get_or_create_compound_svalue (tree type, - const binding_map &map); - const svalue *get_or_create_conjured_svalue (tree type, const gimple *stmt, - const region *id_reg, - const conjured_purge &p); - const svalue * - get_or_create_asm_output_svalue (tree type, - const gasm *asm_stmt, - unsigned output_idx, - const vec<const svalue *> &inputs); - const svalue * - get_or_create_const_fn_result_svalue (tree type, - tree fndecl, - const vec<const svalue *> &inputs); - - const svalue *maybe_get_char_from_string_cst (tree string_cst, - tree byte_offset_cst); - - /* Dynamically-allocated svalue instances. - The number of these within the analysis can grow arbitrarily. - They are still owned by the manager. */ - const svalue *create_unique_svalue (tree type); - - /* region consolidation. */ - const stack_region * get_stack_region () const { return &m_stack_region; } - const heap_region *get_heap_region () const { return &m_heap_region; } - const code_region *get_code_region () const { return &m_code_region; } - const globals_region *get_globals_region () const - { - return &m_globals_region; - } - const function_region *get_region_for_fndecl (tree fndecl); - const label_region *get_region_for_label (tree label); - const decl_region *get_region_for_global (tree expr); - const region *get_field_region (const region *parent, tree field); - const region *get_element_region (const region *parent, - tree element_type, - const svalue *index); - const region *get_offset_region (const region *parent, - tree type, - const svalue *byte_offset); - const region *get_sized_region (const region *parent, - tree type, - const svalue *byte_size_sval); - const region *get_cast_region (const region *original_region, - tree type); - const frame_region *get_frame_region (const frame_region *calling_frame, - function *fun); - const region *get_symbolic_region (const svalue *sval); - const string_region *get_region_for_string (tree string_cst); - const region *get_bit_range (const region *parent, tree type, - const bit_range &bits); - const var_arg_region *get_var_arg_region (const frame_region *parent, - unsigned idx); - - const region *get_unknown_symbolic_region (tree region_type); - - const region * - get_region_for_unexpected_tree_code (region_model_context *ctxt, - tree t, - const dump_location_t &loc); - - unsigned alloc_region_id () { return m_next_region_id++; } - - store_manager *get_store_manager () { return &m_store_mgr; } - bounded_ranges_manager *get_range_manager () const { return m_range_mgr; } - - known_function_manager *get_known_function_manager () - { - return &m_known_fn_mgr; - } - - /* Dynamically-allocated region instances. - The number of these within the analysis can grow arbitrarily. - They are still owned by the manager. */ - const region *create_region_for_heap_alloc (); - const region *create_region_for_alloca (const frame_region *frame); - - void log_stats (logger *logger, bool show_objs) const; - - void begin_checking_feasibility (void) { m_checking_feasibility = true; } - void end_checking_feasibility (void) { m_checking_feasibility = false; } - - logger *get_logger () const { return m_logger; } - - void dump_untracked_regions () const; - -private: - bool too_complex_p (const complexity &c) const; - bool reject_if_too_complex (svalue *sval); - - const svalue *maybe_fold_unaryop (tree type, enum tree_code op, - const svalue *arg); - const svalue *maybe_fold_binop (tree type, enum tree_code op, - const svalue *arg0, const svalue *arg1); - const svalue *maybe_fold_sub_svalue (tree type, - const svalue *parent_svalue, - const region *subregion); - const svalue *maybe_fold_repeated_svalue (tree type, - const svalue *outer_size, - const svalue *inner_svalue); - const svalue *maybe_fold_bits_within_svalue (tree type, - const bit_range &bits, - const svalue *inner_svalue); - const svalue *maybe_undo_optimize_bit_field_compare (tree type, - const compound_svalue *compound_sval, - tree cst, const svalue *arg1); - const svalue *maybe_fold_asm_output_svalue (tree type, - const vec<const svalue *> &inputs); - - logger *m_logger; - - const call_string m_empty_call_string; - - unsigned m_next_region_id; - root_region m_root_region; - stack_region m_stack_region; - heap_region m_heap_region; - - /* svalue consolidation. */ - typedef hash_map<tree, constant_svalue *> constants_map_t; - constants_map_t m_constants_map; - - typedef hash_map<tree, unknown_svalue *> unknowns_map_t; - unknowns_map_t m_unknowns_map; - const unknown_svalue *m_unknown_NULL; - - typedef hash_map<poisoned_svalue::key_t, - poisoned_svalue *> poisoned_values_map_t; - poisoned_values_map_t m_poisoned_values_map; - - typedef hash_map<setjmp_svalue::key_t, - setjmp_svalue *> setjmp_values_map_t; - setjmp_values_map_t m_setjmp_values_map; - - typedef hash_map<const region *, initial_svalue *> initial_values_map_t; - initial_values_map_t m_initial_values_map; - - typedef hash_map<region_svalue::key_t, region_svalue *> pointer_values_map_t; - pointer_values_map_t m_pointer_values_map; - - typedef hash_map<unaryop_svalue::key_t, - unaryop_svalue *> unaryop_values_map_t; - unaryop_values_map_t m_unaryop_values_map; - - typedef hash_map<binop_svalue::key_t, binop_svalue *> binop_values_map_t; - binop_values_map_t m_binop_values_map; - - typedef hash_map<sub_svalue::key_t, sub_svalue *> sub_values_map_t; - sub_values_map_t m_sub_values_map; - - typedef hash_map<repeated_svalue::key_t, - repeated_svalue *> repeated_values_map_t; - repeated_values_map_t m_repeated_values_map; - - typedef hash_map<bits_within_svalue::key_t, - bits_within_svalue *> bits_within_values_map_t; - bits_within_values_map_t m_bits_within_values_map; - - typedef hash_map<const svalue *, - unmergeable_svalue *> unmergeable_values_map_t; - unmergeable_values_map_t m_unmergeable_values_map; - - typedef hash_map<widening_svalue::key_t, - widening_svalue */*, - widening_svalue::key_t::hash_map_traits*/> - widening_values_map_t; - widening_values_map_t m_widening_values_map; - - typedef hash_map<compound_svalue::key_t, - compound_svalue *> compound_values_map_t; - compound_values_map_t m_compound_values_map; - - typedef hash_map<conjured_svalue::key_t, - conjured_svalue *> conjured_values_map_t; - conjured_values_map_t m_conjured_values_map; - - typedef hash_map<asm_output_svalue::key_t, - asm_output_svalue *> asm_output_values_map_t; - asm_output_values_map_t m_asm_output_values_map; - - typedef hash_map<const_fn_result_svalue::key_t, - const_fn_result_svalue *> const_fn_result_values_map_t; - const_fn_result_values_map_t m_const_fn_result_values_map; - - bool m_checking_feasibility; - - /* "Dynamically-allocated" svalue instances. - The number of these within the analysis can grow arbitrarily. - They are still owned by the manager. */ - auto_delete_vec<svalue> m_managed_dynamic_svalues; - - /* Maximum complexity of svalues that weren't rejected. */ - complexity m_max_complexity; - - /* region consolidation. */ - - code_region m_code_region; - typedef hash_map<tree, function_region *> fndecls_map_t; - typedef fndecls_map_t::iterator fndecls_iterator_t; - fndecls_map_t m_fndecls_map; - - typedef hash_map<tree, label_region *> labels_map_t; - typedef labels_map_t::iterator labels_iterator_t; - labels_map_t m_labels_map; - - globals_region m_globals_region; - typedef hash_map<tree, decl_region *> globals_map_t; - typedef globals_map_t::iterator globals_iterator_t; - globals_map_t m_globals_map; - - consolidation_map<field_region> m_field_regions; - consolidation_map<element_region> m_element_regions; - consolidation_map<offset_region> m_offset_regions; - consolidation_map<sized_region> m_sized_regions; - consolidation_map<cast_region> m_cast_regions; - consolidation_map<frame_region> m_frame_regions; - consolidation_map<symbolic_region> m_symbolic_regions; - - typedef hash_map<tree, string_region *> string_map_t; - string_map_t m_string_map; - - consolidation_map<bit_range_region> m_bit_range_regions; - consolidation_map<var_arg_region> m_var_arg_regions; - - store_manager m_store_mgr; - - bounded_ranges_manager *m_range_mgr; - - known_function_manager m_known_fn_mgr; - - /* "Dynamically-allocated" region instances. - The number of these within the analysis can grow arbitrarily. - They are still owned by the manager. */ - auto_delete_vec<region> m_managed_dynamic_regions; -}; - struct append_regions_cb_data; /* Helper class for handling calls to functions with known behavior. @@ -631,6 +345,7 @@ class region_model void impl_call_analyzer_dump_escaped (const gcall *call); void impl_call_analyzer_eval (const gcall *call, region_model_context *ctxt); + void impl_call_analyzer_get_unknown_ptr (const call_details &cd); void impl_call_builtin_expect (const call_details &cd); void impl_call_calloc (const call_details &cd); bool impl_call_error (const call_details &cd, unsigned min_args, @@ -816,6 +531,9 @@ class region_model const svalue *get_string_size (const svalue *sval) const; const svalue *get_string_size (const region *reg) const; + bool replay_call_summary (call_summary_replay &r, + const region_model &summary); + void maybe_complain_about_infoleak (const region *dst_reg, const svalue *copied_sval, const region *src_reg, @@ -857,8 +575,6 @@ class region_model region_model_context *ctxt); void update_for_return_superedge (const return_superedge &return_edge, region_model_context *ctxt); - void update_for_call_summary (const callgraph_superedge &cg_sedge, - region_model_context *ctxt); bool apply_constraints_for_gcond (const cfg_superedge &edge, const gcond *cond_stmt, region_model_context *ctxt, @@ -1282,6 +998,10 @@ struct model_merger } bool mergeable_svalue_p (const svalue *) const; + const function_point &get_function_point () const + { + return m_point.get_function_point (); + } const region_model *m_model_a; const region_model *m_model_b; diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index 09646bf..da5a13e 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -40,11 +40,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "diagnostic-color.h" #include "diagnostic-metadata.h" -#include "tristate.h" #include "bitmap.h" -#include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index 505d598..c4ad91c 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -28,8 +28,6 @@ along with GCC; see the file COPYING3. If not see #include "options.h" #include "diagnostic-path.h" #include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-event-id.h" #include "analyzer/analyzer-logging.h" @@ -37,8 +35,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/pending-diagnostic.h" #include "analyzer/function-set.h" #include "analyzer/analyzer-selftests.h" -#include "tristate.h" -#include "selftest.h" #include "stringpool.h" #include "attribs.h" #include "analyzer/call-string.h" diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index f6cb29c..13f2507 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -28,8 +28,6 @@ along with GCC; see the file COPYING3. If not see #include "options.h" #include "diagnostic-path.h" #include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-event-id.h" #include "analyzer/analyzer-logging.h" @@ -37,7 +35,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/pending-diagnostic.h" #include "analyzer/function-set.h" #include "analyzer/analyzer-selftests.h" -#include "tristate.h" #include "selftest.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 73c549f..fef6e63 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -29,15 +29,11 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "diagnostic-path.h" #include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-event-id.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" -#include "tristate.h" -#include "selftest.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc index 9b2ad68..3208132 100644 --- a/gcc/analyzer/sm-pattern-test.cc +++ b/gcc/analyzer/sm-pattern-test.cc @@ -30,15 +30,11 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "diagnostic-path.h" #include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-event-id.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" -#include "tristate.h" -#include "selftest.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc index 83c1906..cb5f859 100644 --- a/gcc/analyzer/sm-sensitive.cc +++ b/gcc/analyzer/sm-sensitive.cc @@ -24,14 +24,11 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "tree.h" #include "function.h" -#include "function.h" #include "basic-block.h" #include "gimple.h" #include "options.h" #include "diagnostic-path.h" #include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-event-id.h" #include "analyzer/analyzer-logging.h" diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc index b601f45..e3b08c3 100644 --- a/gcc/analyzer/sm-signal.cc +++ b/gcc/analyzer/sm-signal.cc @@ -31,15 +31,12 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "diagnostic-path.h" #include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "diagnostic-event-id.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" #include "sbitmap.h" -#include "tristate.h" #include "ordered-hash-map.h" #include "selftest.h" #include "analyzer/call-string.h" @@ -48,13 +45,10 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/region-model.h" #include "analyzer/program-state.h" #include "analyzer/checker-path.h" -#include "digraph.h" #include "cfg.h" #include "gimple-iterator.h" #include "cgraph.h" #include "analyzer/supergraph.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" #include "analyzer/diagnostic-manager.h" #include "shortest-paths.h" #include "analyzer/exploded-graph.h" diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc index f5c0cc1..bc27533 100644 --- a/gcc/analyzer/sm-taint.cc +++ b/gcc/analyzer/sm-taint.cc @@ -30,13 +30,9 @@ along with GCC; see the file COPYING3. If not see #include "options.h" #include "diagnostic-path.h" #include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "gimple-iterator.h" -#include "tristate.h" -#include "selftest.h" #include "ordered-hash-map.h" #include "cgraph.h" #include "cfg.h" diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc index d17d5c7..1ab4c21 100644 --- a/gcc/analyzer/sm.cc +++ b/gcc/analyzer/sm.cc @@ -31,11 +31,9 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print.h" #include "diagnostic.h" #include "tree-diagnostic.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" -#include "tristate.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc index 7a061a1..d3f516a 100644 --- a/gcc/analyzer/state-purge.cc +++ b/gcc/analyzer/state-purge.cc @@ -36,24 +36,16 @@ along with GCC; see the file COPYING3. If not see #include "ssa-iterators.h" #include "diagnostic-core.h" #include "gimple-pretty-print.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/call-string.h" -#include "digraph.h" -#include "ordered-hash-map.h" -#include "cfg.h" -#include "gimple-iterator.h" -#include "cgraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-point.h" #include "analyzer/analyzer-logging.h" #include "analyzer/state-purge.h" -#include "tristate.h" -#include "selftest.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "gimple-walk.h" +#include "cgraph.h" #if ENABLE_ANALYZER diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index 1857d95..2631ea2 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -38,24 +38,20 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "diagnostic-color.h" #include "diagnostic-metadata.h" -#include "tristate.h" #include "bitmap.h" #include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "ordered-hash-map.h" #include "options.h" -#include "cgraph.h" #include "cfg.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "sbitmap.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" +#include "analyzer/call-summary.h" #include "analyzer/analyzer-selftests.h" #include "stor-layout.h" @@ -3130,6 +3126,148 @@ store::loop_replay_fixup (const store *other_store, } } +/* Use R to replay the bindings from SUMMARY into this object. */ + +void +store::replay_call_summary (call_summary_replay &r, + const store &summary) +{ + if (summary.m_called_unknown_fn) + { + /* A call to an external function occurred in the summary. + Hence we need to invalidate our knownledge of globals, + escaped regions, etc. */ + on_unknown_fncall (r.get_call_stmt (), + r.get_store_manager (), + conjured_purge (r.get_caller_model (), + r.get_ctxt ())); + } + + auto_vec<const region *> keys (summary.m_cluster_map.elements ()); + for (auto kv : summary.m_cluster_map) + keys.quick_push (kv.first); + keys.qsort (region::cmp_ptr_ptr); + for (auto base_reg : keys) + replay_call_summary_cluster (r, summary, base_reg); +} + +/* Use R and SUMMARY to replay the bindings in SUMMARY_CLUSTER + into this object. */ + +void +store::replay_call_summary_cluster (call_summary_replay &r, + const store &summary, + const region *summary_base_reg) +{ + const call_details &cd = r.get_call_details (); + region_model_manager *reg_mgr = r.get_manager (); + store_manager *mgr = reg_mgr->get_store_manager (); + const binding_cluster *summary_cluster + = summary.get_cluster (summary_base_reg); + + /* Handle "ESCAPED" and "TOUCHED" flags. */ + if (summary_cluster->escaped_p () || summary_cluster->touched_p ()) + if (const region *caller_reg + = r.convert_region_from_summary (summary_base_reg)) + { + const region *caller_base_reg = caller_reg->get_base_region (); + if (caller_base_reg->tracked_p () + && !caller_base_reg->symbolic_for_unknown_ptr_p ()) + { + binding_cluster *caller_cluster + = get_or_create_cluster (caller_base_reg); + if (summary_cluster->escaped_p ()) + caller_cluster->mark_as_escaped (); + if (summary_cluster->touched_p ()) + caller_cluster->m_touched = true; + } + } + + switch (summary_base_reg->get_kind ()) + { + /* Top-level regions. */ + case RK_FRAME: + case RK_GLOBALS: + case RK_CODE: + case RK_STACK: + case RK_HEAP: + case RK_ROOT: + /* Child regions. */ + case RK_FIELD: + case RK_ELEMENT: + case RK_OFFSET: + case RK_SIZED: + case RK_CAST: + case RK_BIT_RANGE: + /* Other regions. */ + case RK_VAR_ARG: + case RK_UNKNOWN: + /* These should never be the base region of a binding cluster. */ + gcc_unreachable (); + break; + + case RK_FUNCTION: + case RK_LABEL: + case RK_STRING: + /* These can be marked as escaping. */ + break; + + case RK_SYMBOLIC: + { + const symbolic_region *summary_symbolic_reg + = as_a <const symbolic_region *> (summary_base_reg); + const svalue *summary_ptr_sval = summary_symbolic_reg->get_pointer (); + const svalue *caller_ptr_sval + = r.convert_svalue_from_summary (summary_ptr_sval); + if (!caller_ptr_sval) + return; + const region *caller_dest_reg + = cd.get_model ()->deref_rvalue (caller_ptr_sval, + NULL_TREE, + cd.get_ctxt ()); + const svalue *summary_sval + = summary.get_any_binding (mgr, summary_base_reg); + if (!summary_sval) + return; + const svalue *caller_sval + = r.convert_svalue_from_summary (summary_sval); + if (!caller_sval) + caller_sval = + reg_mgr->get_or_create_unknown_svalue (summary_sval->get_type ()); + set_value (mgr, caller_dest_reg, + caller_sval, NULL /* uncertainty_t * */); + } + break; + + case RK_HEAP_ALLOCATED: + case RK_DECL: + { + const region *caller_dest_reg + = r.convert_region_from_summary (summary_base_reg); + if (!caller_dest_reg) + return; + const svalue *summary_sval + = summary.get_any_binding (mgr, summary_base_reg); + if (!summary_sval) + summary_sval = reg_mgr->get_or_create_compound_svalue + (summary_base_reg->get_type (), + summary_cluster->get_map ()); + const svalue *caller_sval + = r.convert_svalue_from_summary (summary_sval); + if (!caller_sval) + caller_sval = + reg_mgr->get_or_create_unknown_svalue (summary_sval->get_type ()); + set_value (mgr, caller_dest_reg, + caller_sval, NULL /* uncertainty_t * */); + } + break; + + case RK_ALLOCA: + /* Ignore bindings of alloca regions in the summary. */ + break; + } +} + #if CHECKING_P namespace selftest { diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index d172ee7..1087782 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -418,6 +418,14 @@ private: } // namespace ana +template <> +template <> +inline bool +is_a_helper <const ana::concrete_binding *>::test (const ana::binding_key *key) +{ + return key->concrete_p (); +} + template <> struct default_hash_traits<ana::concrete_binding> : public member_function_hash_traits<ana::concrete_binding> { @@ -786,6 +794,12 @@ public: void loop_replay_fixup (const store *other_store, region_model_manager *mgr); + void replay_call_summary (call_summary_replay &r, + const store &summary); + void replay_call_summary_cluster (call_summary_replay &r, + const store &summary, + const region *base_reg); + private: void remove_overlapping_bindings (store_manager *mgr, const region *reg, uncertainty_t *uncertainty); diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc index 01e30f7..0e9a325 100644 --- a/gcc/analyzer/supergraph.cc +++ b/gcc/analyzer/supergraph.cc @@ -44,7 +44,6 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "cfganal.h" #include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "ordered-hash-map.h" #include "options.h" @@ -62,7 +61,7 @@ namespace ana { /* Get the function of the ultimate alias target being called at EDGE, if any. */ -static function * +function * get_ultimate_function_for_cgraph_edge (cgraph_edge *edge) { cgraph_node *ultimate_node = edge->callee->ultimate_alias_target (); @@ -74,12 +73,13 @@ get_ultimate_function_for_cgraph_edge (cgraph_edge *edge) /* Get the cgraph_edge, but only if there's an underlying function body. */ cgraph_edge * -supergraph_call_edge (function *fun, gimple *stmt) +supergraph_call_edge (function *fun, const gimple *stmt) { - gcall *call = dyn_cast<gcall *> (stmt); + const gcall *call = dyn_cast<const gcall *> (stmt); if (!call) return NULL; - cgraph_edge *edge = cgraph_node::get (fun->decl)->get_edge (stmt); + cgraph_edge *edge + = cgraph_node::get (fun->decl)->get_edge (const_cast <gimple *> (stmt)); if (!edge) return NULL; if (!edge->callee) diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h index e9a5be2..f66058c 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -21,6 +21,13 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ANALYZER_SUPERGRAPH_H #define GCC_ANALYZER_SUPERGRAPH_H +#include "ordered-hash-map.h" +#include "cfg.h" +#include "basic-block.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "digraph.h" + using namespace ana; namespace ana { @@ -605,7 +612,8 @@ class dot_annotator } }; -extern cgraph_edge *supergraph_call_edge (function *fun, gimple *stmt); +extern cgraph_edge *supergraph_call_edge (function *fun, const gimple *stmt); +extern function *get_ultimate_function_for_cgraph_edge (cgraph_edge *edge); } // namespace ana diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index f5a5f1c..a0838c0 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -38,22 +38,16 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "fold-const.h" #include "tree-pretty-print.h" -#include "tristate.h" #include "bitmap.h" -#include "selftest.h" -#include "function.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" -#include "options.h" -#include "cgraph.h" -#include "cfg.h" -#include "digraph.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/svalue.h" #include "analyzer/region-model.h" +#include "diagnostic.h" +#include "tree-diagnostic.h" #if ENABLE_ANALYZER @@ -207,7 +201,7 @@ svalue::can_merge_p (const svalue *other, if (maybe_get_constant () && other->maybe_get_constant ()) { return mgr->get_or_create_widening_svalue (other->get_type (), - merger->m_point, + merger->get_function_point (), other, this); } @@ -220,7 +214,7 @@ svalue::can_merge_p (const svalue *other, && binop_sval->get_arg1 ()->get_kind () == SK_CONSTANT && other->get_kind () != SK_WIDENING) return mgr->get_or_create_widening_svalue (other->get_type (), - merger->m_point, + merger->get_function_point (), other, this); /* Merge: (Widen(existing_val, V), existing_val) -> Widen (existing_val, V) @@ -874,7 +868,7 @@ constant_svalue::eval_condition (const constant_svalue *lhs, const svalue * constant_svalue::maybe_fold_bits_within (tree type, - const bit_range &, + const bit_range &bits, region_model_manager *mgr) const { /* Bits within an all-zero value are also all zero. */ @@ -885,6 +879,21 @@ constant_svalue::maybe_fold_bits_within (tree type, else return this; } + + /* Handle the case of extracting a single bit. */ + if (bits.m_size_in_bits == 1 + && TREE_CODE (m_cst_expr) == INTEGER_CST + && type + && INTEGRAL_TYPE_P (type)) + { + unsigned HOST_WIDE_INT bit = bits.m_start_bit_offset.to_uhwi (); + unsigned HOST_WIDE_INT mask = (1 << bit); + unsigned HOST_WIDE_INT val_as_hwi = tree_to_uhwi (m_cst_expr); + unsigned HOST_WIDE_INT masked_val = val_as_hwi & mask; + int result = masked_val ? 1 : 0; + return mgr->get_or_create_int_cst (type, result); + } + /* Otherwise, don't fold. */ return NULL; } @@ -1728,13 +1737,17 @@ unmergeable_svalue::implicitly_live_p (const svalue_set *live_svalues, compound_svalue::compound_svalue (tree type, const binding_map &map) : svalue (calc_complexity (map), type), m_map (map) { - /* All keys within the underlying binding_map are required to be concrete, - not symbolic. */ #if CHECKING_P for (iterator_t iter = begin (); iter != end (); ++iter) { + /* All keys within the underlying binding_map are required to be concrete, + not symbolic. */ const binding_key *key = (*iter).first; gcc_assert (key->concrete_p ()); + + /* We don't nest compound svalues. */ + const svalue *sval = (*iter).second; + gcc_assert (sval->get_kind () != SK_COMPOUND); } #endif } diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h index f4cab0d..382b083 100644 --- a/gcc/analyzer/svalue.h +++ b/gcc/analyzer/svalue.h @@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see #define GCC_ANALYZER_SVALUE_H #include "analyzer/complexity.h" +#include "analyzer/store.h" +#include "analyzer/program-point.h" using namespace ana; @@ -1113,9 +1115,9 @@ public: /* A support class for uniquifying instances of widening_svalue. */ struct key_t { - key_t (tree type, const program_point &point, + key_t (tree type, const function_point &point, const svalue *base_sval, const svalue *iter_sval) - : m_type (type), m_point (point.get_function_point ()), + : m_type (type), m_point (point), m_base_sval (base_sval), m_iter_sval (iter_sval) {} @@ -1153,12 +1155,12 @@ public: DIR_UNKNOWN }; - widening_svalue (tree type, const program_point &point, + widening_svalue (tree type, const function_point &point, const svalue *base_sval, const svalue *iter_sval) : svalue (complexity::from_pair (base_sval->get_complexity (), iter_sval->get_complexity ()), type), - m_point (point.get_function_point ()), + m_point (point), m_base_sval (base_sval), m_iter_sval (iter_sval) { gcc_assert (base_sval->can_have_associated_state_p ()); @@ -1527,6 +1529,7 @@ public: const char *get_asm_string () const { return m_asm_string; } unsigned get_output_idx () const { return m_output_idx; } + unsigned get_num_outputs () const { return m_num_outputs; } unsigned get_num_inputs () const { return m_num_inputs; } const svalue *get_input (unsigned idx) const { return m_input_arr[idx]; } diff --git a/gcc/analyzer/trimmed-graph.cc b/gcc/analyzer/trimmed-graph.cc index 6c85910..9fdb4a9 100644 --- a/gcc/analyzer/trimmed-graph.cc +++ b/gcc/analyzer/trimmed-graph.cc @@ -29,15 +29,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "diagnostic-event-id.h" #include "diagnostic-path.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" -#include "sbitmap.h" #include "bitmap.h" -#include "tristate.h" -#include "selftest.h" #include "ordered-hash-map.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" @@ -48,12 +41,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" -#include "cfg.h" -#include "basic-block.h" -#include "gimple.h" -#include "gimple-iterator.h" -#include "cgraph.h" -#include "digraph.h" #include "analyzer/supergraph.h" #include "analyzer/program-state.h" #include "analyzer/exploded-graph.h" diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc index 9400086..b2e6cd5 100644 --- a/gcc/analyzer/varargs.cc +++ b/gcc/analyzer/varargs.cc @@ -26,28 +26,17 @@ along with GCC; see the file COPYING3. If not see #include "basic-block.h" #include "gimple.h" #include "diagnostic-path.h" -#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" #include "analyzer/pending-diagnostic.h" -#include "tristate.h" -#include "selftest.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" #include "analyzer/checker-path.h" -#include "digraph.h" -#include "ordered-hash-map.h" -#include "cfg.h" -#include "gimple-iterator.h" #include "analyzer/supergraph.h" -#include "alloc-pool.h" -#include "fibonacci_heap.h" -#include "shortest-paths.h" -#include "sbitmap.h" #include "analyzer/diagnostic-manager.h" #include "analyzer/exploded-graph.h" #include "diagnostic-metadata.h" |