analyzer: fix ICE in call summarization [PR114159]

PR analyzer/114159 reports an ICE inside playback of call summaries
for very low values of --param=analyzer-max-svalue-depth=VAL.

Root cause is that call_summary_edge_info's ctor tries to evaluate
the function ptr of a gimple call stmt and assumes it gets a function *,
but with low values of --param=analyzer-max-svalue-depth=VAL we get
back an UNKNOWN svalue, rather than a pointer to a specific function.

Fix by adding a new call_info ctor that passes a specific
const function & from the call_summary_edge_info, rather than trying
to compute the function.

In doing so, I noticed that the analyzer was using "function *" despite
not modifying functions, and was sloppy about can-be-null versus
must-be-non-null function pointers, so I "constified" the function, and
converted the many places where the function must be non-null to be
"const function &".

gcc/analyzer/ChangeLog:
	PR analyzer/114159
	* analyzer.cc: Include "tree-dfa.h".
	(get_ssa_default_def): New decl.
	* analyzer.h (get_ssa_default_def): New.
	* call-info.cc (call_info::call_info): New ctor taking an explicit
	called_fn.
	* call-info.h (call_info::call_info): Likewise.
	* call-summary.cc (call_summary_replay::call_summary_replay):
	Convert param from function * to const function &.
	* call-summary.h (call_summary_replay::call_summary_replay):
	Likewise.
	* checker-event.h (state_change_event::get_dest_function):
	Constify return value.
	* engine.cc (point_and_state::validate): Update for conversion to
	const function &.
	(exploded_node::on_stmt): Likewise.
	(call_summary_edge_info::call_summary_edge_info): Likewise.
	Pass in called_fn to call_info ctor.
	(exploded_node::replay_call_summaries): Update for conversion to
	const function &.  Convert per_function_data from * to &.
	(exploded_node::replay_call_summary): Update for conversion to
	const function &.
	(exploded_graph::add_function_entry): Likewise.
	(toplevel_function_p): Likewise.
	(add_tainted_args_callback): Likewise.
	(exploded_graph::build_initial_worklist): Likewise.
	(exploded_graph::maybe_create_dynamic_call): Likewise.
	(maybe_update_for_edge): Likewise.
	(exploded_graph::on_escaped_function): Likewise.
	* exploded-graph.h (exploded_node::replay_call_summaries):
	Likewise.
	(exploded_node::replay_call_summary): Likewise.
	(exploded_graph::add_function_entry): Likewise.
	* program-point.cc (function_point::from_function_entry):
	Likewise.
	(program_point::from_function_entry): Likewise.
	* program-point.h (function_point::from_function_entry): Likewise.
	(program_point::from_function_entry): Likewise.
	* program-state.cc (program_state::push_frame): Likewise.
	(program_state::get_current_function): Constify return type.
	* program-state.h (program_state::push_frame): Update for
	conversion to const function &.
	(program_state::get_current_function): Likewise.
	* region-model-manager.cc
	(region_model_manager::get_frame_region): Likewise.
	* region-model-manager.h
	(region_model_manager::get_frame_region): Likewise.
	* region-model.cc (region_model::called_from_main_p): Likewise.
	(region_model::update_for_gcall): Likewise.
	(region_model::push_frame): Likewise.
	(region_model::get_current_function): Constify return type.
	(region_model::pop_frame): Update for conversion to
	const function &.
	(selftest::test_stack_frames): Likewise.
	(selftest::test_get_representative_path_var): Likewise.
	(selftest::test_state_merging): Likewise.
	(selftest::test_alloca): Likewise.
	* region-model.h (region_model::push_frame): Likewise.
	(region_model::get_current_function): Likewise.
	* region.cc (frame_region::dump_to_pp): Likewise.
	(frame_region::get_region_for_local): Likewise.
	* region.h (class frame_region): Likewise.
	* sm-signal.cc (signal_unsafe_call::describe_state_change):
	Likewise.
	(update_model_for_signal_handler): Likewise.
	(signal_delivery_edge_info_t::update_model): Likewise.
	(register_signal_handler::impl_transition): Likewise.
	* state-purge.cc (class gimple_op_visitor): Likewise.
	(state_purge_map::state_purge_map): Likewise.
	(state_purge_map::get_or_create_data_for_decl): Likewise.
	(state_purge_per_ssa_name::state_purge_per_ssa_name): Likewise.
	(state_purge_per_ssa_name::add_to_worklist): Likewise.
	(state_purge_per_ssa_name::process_point): Likewise.
	(state_purge_per_decl::add_to_worklist): Likewise.
	(state_purge_annotator::print_needed): Likewise.
	* state-purge.h
	(state_purge_map::get_or_create_data_for_decl): Likewise.
	(class state_purge_per_tree): Likewise.
	(class state_purge_per_ssa_name): Likewise.
	(class state_purge_per_decl): Likewise.
	* supergraph.cc (supergraph::dump_dot_to_pp): Likewise.
	* supergraph.h
	(supergraph::get_node_for_function_entry): Likewise.
	(supergraph::get_node_for_function_exit): Likewise.

gcc/ChangeLog:
	PR analyzer/114159
	* function.cc (function_name): Make param const.
	* function.h (function_name): Likewise.

gcc/testsuite/ChangeLog:
	PR analyzer/114159
	* c-c++-common/analyzer/call-summaries-pr114159.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2024-02-29 17:57:08 -05:00
parent cda3836161
commit c0d8a64e72
27 changed files with 181 additions and 129 deletions

View file

@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/analyzer.h"
#include "tree-pretty-print.h"
#include "diagnostic-event-id.h"
#include "tree-dfa.h"
#if ENABLE_ANALYZER
@ -275,6 +276,14 @@ byte_offset_to_json (const byte_offset_t &offset)
return new json::string (pp_formatted_text (&pp));
}
/* Workaround for lack of const-correctness of ssa_default_def. */
tree
get_ssa_default_def (const function &fun, tree var)
{
return ssa_default_def (const_cast <function *> (&fun), var);
}
} // namespace ana
/* Helper function for checkers. Is the CALL to the given function name,

View file

@ -433,6 +433,9 @@ compare_constants (tree lhs_const, enum tree_code op, tree rhs_const);
extern tree
get_string_cst_size (const_tree string_cst);
extern tree
get_ssa_default_def (const function &fun, tree var);
} // namespace ana
extern bool is_special_named_call_p (const gcall *call, const char *funcname,

View file

@ -143,6 +143,14 @@ call_info::call_info (const call_details &cd)
gcc_assert (m_fndecl);
}
call_info::call_info (const call_details &cd,
const function &called_fn)
: m_call_stmt (cd.get_call_stmt ()),
m_fndecl (called_fn.decl)
{
gcc_assert (m_fndecl);
}
/* class succeed_or_fail_call_info : public call_info. */
label_text

View file

@ -44,6 +44,7 @@ public:
protected:
call_info (const call_details &cd);
call_info (const call_details &cd, const function &called_fn);
private:
const gcall *m_call_stmt;

View file

@ -167,7 +167,7 @@ call_summary::dump (const extrinsic_state &ext_state, bool simple) const
arguments at the caller. */
call_summary_replay::call_summary_replay (const call_details &cd,
function *called_fn,
const function &called_fn,
call_summary *summary,
const extrinsic_state &ext_state)
: m_cd (cd),
@ -177,7 +177,7 @@ call_summary_replay::call_summary_replay (const call_details &cd,
region_model_manager *mgr = cd.get_manager ();
// populate params based on args
tree fndecl = called_fn->decl;
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
@ -196,7 +196,7 @@ call_summary_replay::call_summary_replay (const call_details &cd,
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))
if (tree parm_default_ssa = get_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 ());

View file

@ -68,7 +68,7 @@ class call_summary_replay
{
public:
call_summary_replay (const call_details &cd,
function *called_fn,
const function &called_fn,
call_summary *m_summary,
const extrinsic_state &ext_state);

View file

@ -370,7 +370,7 @@ public:
label_text get_desc (bool can_colorize) const final override;
meaning get_meaning () const override;
function *get_dest_function () const
const function *get_dest_function () const
{
return m_dst_state.get_current_function ();
}

View file

@ -1072,7 +1072,7 @@ point_and_state::validate (const extrinsic_state &ext_state) const
{
int index = iter_frame->get_index ();
gcc_assert (m_point.get_function_at_depth (index)
== iter_frame->get_function ());
== &iter_frame->get_function ());
}
}
@ -1496,14 +1496,17 @@ exploded_node::on_stmt (exploded_graph &eg,
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);
{
gcc_assert (called_fn);
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;
@ -1610,10 +1613,10 @@ class call_summary_edge_info : public call_info
{
public:
call_summary_edge_info (const call_details &cd,
function *called_fn,
const function &called_fn,
call_summary *summary,
const extrinsic_state &ext_state)
: call_info (cd),
: call_info (cd, called_fn),
m_called_fn (called_fn),
m_summary (summary),
m_ext_state (ext_state)
@ -1648,7 +1651,7 @@ public:
}
private:
function *m_called_fn;
const function &m_called_fn;
call_summary *m_summary;
const extrinsic_state &m_ext_state;
};
@ -1662,18 +1665,15 @@ exploded_node::replay_call_summaries (exploded_graph &eg,
const gcall *call_stmt,
program_state *state,
path_context *path_ctxt,
function *called_fn,
per_function_data *called_fn_data,
const 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)
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 ();
@ -1691,7 +1691,7 @@ exploded_node::replay_call_summary (exploded_graph &eg,
const gcall *call_stmt,
program_state *old_state,
path_context *path_ctxt,
function *called_fn,
const function &called_fn,
call_summary *summary,
region_model_context *ctxt)
{
@ -1700,13 +1700,12 @@ exploded_node::replay_call_summary (exploded_graph &eg,
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,
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 ();
@ -2784,16 +2783,17 @@ private:
Return the exploded_node for the entrypoint to the function. */
exploded_node *
exploded_graph::add_function_entry (function *fun)
exploded_graph::add_function_entry (const function &fun)
{
gcc_assert (gimple_has_body_p (fun->decl));
gcc_assert (gimple_has_body_p (fun.decl));
/* Be idempotent. */
if (m_functions_with_enodes.contains (fun))
function *key = const_cast<function *> (&fun);
if (m_functions_with_enodes.contains (key))
{
logger * const logger = get_logger ();
if (logger)
logger->log ("entrypoint for %qE already exists", fun->decl);
logger->log ("entrypoint for %qE already exists", fun.decl);
return NULL;
}
@ -2805,10 +2805,10 @@ exploded_graph::add_function_entry (function *fun)
std::unique_ptr<custom_edge_info> edge_info = NULL;
if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun->decl)))
if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl)))
{
if (mark_params_as_tainted (&state, fun->decl, m_ext_state))
edge_info = make_unique<tainted_args_function_info> (fun->decl);
if (mark_params_as_tainted (&state, fun.decl, m_ext_state))
edge_info = make_unique<tainted_args_function_info> (fun.decl);
}
if (!state.m_valid)
@ -2820,7 +2820,7 @@ exploded_graph::add_function_entry (function *fun)
add_edge (m_origin, enode, NULL, false, std::move (edge_info));
m_functions_with_enodes.add (fun);
m_functions_with_enodes.add (key);
return enode;
}
@ -3108,7 +3108,7 @@ exploded_graph::get_per_function_data (function *fun) const
called via other functions. */
static bool
toplevel_function_p (function *fun, logger *logger)
toplevel_function_p (const function &fun, logger *logger)
{
/* Don't directly traverse into functions that have an "__analyzer_"
prefix. Doing so is useful for the analyzer test suite, allowing
@ -3119,17 +3119,17 @@ toplevel_function_p (function *fun, logger *logger)
excess messages from the case of the first function being traversed
directly. */
#define ANALYZER_PREFIX "__analyzer_"
if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun->decl)), ANALYZER_PREFIX,
if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun.decl)), ANALYZER_PREFIX,
strlen (ANALYZER_PREFIX)))
{
if (logger)
logger->log ("not traversing %qE (starts with %qs)",
fun->decl, ANALYZER_PREFIX);
fun.decl, ANALYZER_PREFIX);
return false;
}
if (logger)
logger->log ("traversing %qE (all checks passed)", fun->decl);
logger->log ("traversing %qE (all checks passed)", fun.decl);
return true;
}
@ -3254,9 +3254,9 @@ add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl,
program_point point
= program_point::from_function_entry (*ext_state.get_model_manager (),
eg->get_supergraph (), fun);
eg->get_supergraph (), *fun);
program_state state (ext_state);
state.push_frame (ext_state, fun);
state.push_frame (ext_state, *fun);
if (!mark_params_as_tainted (&state, fndecl, ext_state))
return;
@ -3330,9 +3330,10 @@ exploded_graph::build_initial_worklist ()
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fun = node->get_fun ();
if (!toplevel_function_p (fun, logger))
gcc_assert (fun);
if (!toplevel_function_p (*fun, logger))
continue;
exploded_node *enode = add_function_entry (fun);
exploded_node *enode = add_function_entry (*fun);
if (logger)
{
if (enode)
@ -3838,8 +3839,8 @@ exploded_graph::maybe_create_dynamic_call (const gcall *call,
if (fun)
{
const supergraph &sg = this->get_supergraph ();
supernode *sn_entry = sg.get_node_for_function_entry (fun);
supernode *sn_exit = sg.get_node_for_function_exit (fun);
supernode *sn_entry = sg.get_node_for_function_entry (*fun);
supernode *sn_exit = sg.get_node_for_function_exit (*fun);
program_point new_point
= program_point::before_supernode (sn_entry,
@ -4962,7 +4963,7 @@ maybe_update_for_edge (logger *logger,
== PK_BEFORE_SUPERNODE);
function *fun = eedge->m_dest->get_function ();
gcc_assert (fun);
m_model.push_frame (fun, NULL, ctxt);
m_model.push_frame (*fun, NULL, ctxt);
if (logger)
logger->log (" pushing frame for %qD", fun->decl);
}
@ -5582,7 +5583,7 @@ exploded_graph::on_escaped_function (tree fndecl)
if (!gimple_has_body_p (fndecl))
return;
exploded_node *enode = add_function_entry (fun);
exploded_node *enode = add_function_entry (*fun);
if (logger)
{
if (enode)

View file

@ -285,15 +285,15 @@ class exploded_node : public dnode<eg_traits>
const gcall *call_stmt,
program_state *state,
path_context *path_ctxt,
function *called_fn,
per_function_data *called_fn_data,
const 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,
const function &called_fn,
call_summary *summary,
region_model_context *ctxt);
@ -810,7 +810,7 @@ public:
exploded_node *get_origin () const { return m_origin; }
exploded_node *add_function_entry (function *fun);
exploded_node *add_function_entry (const function &fun);
void build_initial_worklist ();
void process_worklist ();

View file

@ -230,7 +230,7 @@ function_point::final_stmt_p () const
/* Create a function_point representing the entrypoint of function FUN. */
function_point
function_point::from_function_entry (const supergraph &sg, function *fun)
function_point::from_function_entry (const supergraph &sg, const function &fun)
{
return before_supernode (sg.get_node_for_function_entry (fun), NULL);
}
@ -698,7 +698,7 @@ program_point::origin (const region_model_manager &mgr)
program_point
program_point::from_function_entry (const region_model_manager &mgr,
const supergraph &sg,
function *fun)
const function &fun)
{
return program_point (function_point::from_function_entry (sg, fun),
mgr.get_empty_call_string ());

View file

@ -112,7 +112,7 @@ public:
/* Factory functions for making various kinds of program_point. */
static function_point from_function_entry (const supergraph &sg,
function *fun);
const function &fun);
static function_point before_supernode (const supernode *supernode,
const superedge *from_edge);
@ -252,7 +252,7 @@ public:
static program_point origin (const region_model_manager &mgr);
static program_point from_function_entry (const region_model_manager &mgr,
const supergraph &sg,
function *fun);
const function &fun);
static program_point before_supernode (const supernode *supernode,
const superedge *from_edge,

View file

@ -1143,14 +1143,14 @@ program_state::to_json (const extrinsic_state &ext_state) const
void
program_state::push_frame (const extrinsic_state &ext_state ATTRIBUTE_UNUSED,
function *fun)
const function &fun)
{
m_region_model->push_frame (fun, NULL, NULL);
}
/* Get the current function of this state. */
function *
const function *
program_state::get_current_function () const
{
return m_region_model->get_current_function ();

View file

@ -226,8 +226,8 @@ public:
json::object *to_json (const extrinsic_state &ext_state) const;
void push_frame (const extrinsic_state &ext_state, function *fun);
function * get_current_function () const;
void push_frame (const extrinsic_state &ext_state, const function &fun);
const function * get_current_function () const;
void push_call (exploded_graph &eg,
exploded_node *enode,

View file

@ -1676,7 +1676,7 @@ region_model_manager::get_cast_region (const region *original_region,
const frame_region *
region_model_manager::get_frame_region (const frame_region *calling_frame,
function *fun)
const function &fun)
{
int index = calling_frame ? calling_frame->get_index () + 1 : 0;

View file

@ -131,7 +131,7 @@ public:
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 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,

View file

@ -2619,7 +2619,7 @@ region_model::called_from_main_p () const
/* Determine if the oldest stack frame in this model is for "main". */
const frame_region *frame0 = get_frame_at_index (0);
gcc_assert (frame0);
return id_equal (DECL_NAME (frame0->get_function ()->decl), "main");
return id_equal (DECL_NAME (frame0->get_function ().decl), "main");
}
/* Subroutine of region_model::get_store_value for when REG is (or is within)
@ -5552,7 +5552,8 @@ region_model::update_for_gcall (const gcall *call_stmt,
callee = DECL_STRUCT_FUNCTION (fn_decl);
}
push_frame (callee, &arg_svals, ctxt);
gcc_assert (callee);
push_frame (*callee, &arg_svals, ctxt);
}
/* Pop the top-most frame_region from the stack, and copy the return
@ -5896,14 +5897,15 @@ region_model::on_top_level_param (tree param,
Return the frame_region for the new frame. */
const region *
region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
region_model::push_frame (const function &fun,
const vec<const svalue *> *arg_svals,
region_model_context *ctxt)
{
m_current_frame = m_mgr->get_frame_region (m_current_frame, fun);
if (arg_svals)
{
/* Arguments supplied from a caller frame. */
tree fndecl = fun->decl;
tree fndecl = fun.decl;
unsigned idx = 0;
for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
iter_parm = DECL_CHAIN (iter_parm), ++idx)
@ -5914,7 +5916,7 @@ region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
if (idx >= arg_svals->length ())
break;
tree parm_lval = iter_parm;
if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
if (tree parm_default_ssa = get_ssa_default_def (fun, iter_parm))
parm_lval = parm_default_ssa;
const region *parm_reg = get_lvalue (parm_lval, ctxt);
const svalue *arg_sval = (*arg_svals)[idx];
@ -5937,7 +5939,7 @@ region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
/* Otherwise we have a top-level call within the analysis. The params
have defined but unknown initial values.
Anything they point to has escaped. */
tree fndecl = fun->decl;
tree fndecl = fun.decl;
/* Handle "__attribute__((nonnull))". */
tree fntype = TREE_TYPE (fndecl);
@ -5951,7 +5953,7 @@ region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
? (bitmap_empty_p (nonnull_args)
|| bitmap_bit_p (nonnull_args, parm_idx))
: false);
if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
if (tree parm_default_ssa = get_ssa_default_def (fun, iter_parm))
on_top_level_param (parm_default_ssa, non_null, ctxt);
else
on_top_level_param (iter_parm, non_null, ctxt);
@ -5967,12 +5969,12 @@ region_model::push_frame (function *fun, const vec<const svalue *> *arg_svals,
/* Get the function of the top-most frame in this region_model's stack.
There must be such a frame. */
function *
const function *
region_model::get_current_function () const
{
const frame_region *frame = get_current_frame ();
gcc_assert (frame);
return frame->get_function ();
return &frame->get_function ();
}
/* Pop the topmost frame_region from this region_model's stack;
@ -6007,7 +6009,7 @@ region_model::pop_frame (tree result_lvalue,
ctxt->on_pop_frame (frame_reg);
/* Evaluate the result, within the callee frame. */
tree fndecl = m_current_frame->get_function ()->decl;
tree fndecl = m_current_frame->get_function ().decl;
tree result = DECL_RESULT (fndecl);
const svalue *retval = NULL;
if (result
@ -7966,7 +7968,7 @@ test_stack_frames ()
/* Push stack frame for "parent_fn". */
const region *parent_frame_reg
= model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl),
= model.push_frame (*DECL_STRUCT_FUNCTION (parent_fndecl),
NULL, &ctxt);
ASSERT_EQ (model.get_current_frame (), parent_frame_reg);
ASSERT_TRUE (model.region_exists_p (parent_frame_reg));
@ -7982,7 +7984,7 @@ test_stack_frames ()
/* Push stack frame for "child_fn". */
const region *child_frame_reg
= model.push_frame (DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt);
= model.push_frame (*DECL_STRUCT_FUNCTION (child_fndecl), NULL, &ctxt);
ASSERT_EQ (model.get_current_frame (), child_frame_reg);
ASSERT_TRUE (model.region_exists_p (child_frame_reg));
const region *x_in_child_reg = model.get_lvalue (x, &ctxt);
@ -8075,7 +8077,7 @@ test_get_representative_path_var ()
for (int depth = 0; depth < 5; depth++)
{
const region *frame_n_reg
= model.push_frame (DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt);
= model.push_frame (*DECL_STRUCT_FUNCTION (fndecl), NULL, &ctxt);
const region *parm_n_reg = model.get_lvalue (path_var (n, depth), &ctxt);
parm_regs.safe_push (parm_n_reg);
@ -8319,9 +8321,9 @@ test_state_merging ()
region_model model0 (&mgr);
region_model model1 (&mgr);
ASSERT_EQ (model0.get_stack_depth (), 0);
model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
ASSERT_EQ (model0.get_stack_depth (), 1);
model1.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
model1.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, &ctxt);
placeholder_svalue test_sval (mgr.alloc_symbol_id (),
integer_type_node, "test sval");
@ -8413,7 +8415,7 @@ test_state_merging ()
/* Pointers: non-NULL and non-NULL: ptr to a local. */
{
region_model model0 (&mgr);
model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
model0.set_value (model0.get_lvalue (p, NULL),
model0.get_rvalue (addr_of_a, NULL), NULL);
@ -8552,12 +8554,12 @@ test_state_merging ()
frame points to a local in a more recent stack frame. */
{
region_model model0 (&mgr);
model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
const region *q_in_first_frame = model0.get_lvalue (q, NULL);
/* Push a second frame. */
const region *reg_2nd_frame
= model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
= model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
/* Have a pointer in the older frame point to a local in the
more recent frame. */
@ -8584,7 +8586,7 @@ test_state_merging ()
/* Verify that we can merge a model in which a local points to a global. */
{
region_model model0 (&mgr);
model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
model0.push_frame (*DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL);
model0.set_value (model0.get_lvalue (q, NULL),
model0.get_rvalue (addr_of_y, NULL), NULL);
@ -9110,7 +9112,7 @@ test_alloca ()
/* Push stack frame. */
const region *frame_reg
= model.push_frame (DECL_STRUCT_FUNCTION (fndecl),
= model.push_frame (*DECL_STRUCT_FUNCTION (fndecl),
NULL, &ctxt);
/* "p = alloca (n * 4);". */
const svalue *size_sval = model.get_rvalue (n_times_4, &ctxt);

View file

@ -347,10 +347,10 @@ class region_model
void update_for_return_gcall (const gcall *call_stmt,
region_model_context *ctxt);
const region *push_frame (function *fun, const vec<const svalue *> *arg_sids,
const region *push_frame (const function &fun, const vec<const svalue *> *arg_sids,
region_model_context *ctxt);
const frame_region *get_current_frame () const { return m_current_frame; }
function * get_current_function () const;
const function *get_current_function () const;
void pop_frame (tree result_lvalue,
const svalue **out_result,
region_model_context *ctxt,

View file

@ -1306,10 +1306,10 @@ void
frame_region::dump_to_pp (pretty_printer *pp, bool simple) const
{
if (simple)
pp_printf (pp, "frame: %qs@%i", function_name (m_fun), get_stack_depth ());
pp_printf (pp, "frame: %qs@%i", function_name (&m_fun), get_stack_depth ());
else
pp_printf (pp, "frame_region(%qs, index: %i, depth: %i)",
function_name (m_fun), m_index, get_stack_depth ());
function_name (&m_fun), m_index, get_stack_depth ());
}
const decl_region *
@ -1334,14 +1334,14 @@ frame_region::get_region_for_local (region_model_manager *mgr,
/* Fall through. */
case PARM_DECL:
case RESULT_DECL:
gcc_assert (DECL_CONTEXT (expr) == m_fun->decl);
gcc_assert (DECL_CONTEXT (expr) == m_fun.decl);
break;
case SSA_NAME:
{
if (tree var = SSA_NAME_VAR (expr))
{
if (DECL_P (var))
gcc_assert (DECL_CONTEXT (var) == m_fun->decl);
gcc_assert (DECL_CONTEXT (var) == m_fun.decl);
}
else if (ctxt)
if (const extrinsic_state *ext_state = ctxt->get_ext_state ())
@ -1351,7 +1351,7 @@ frame_region::get_region_for_local (region_model_manager *mgr,
const gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
const supernode *snode
= sg->get_supernode_for_stmt (def_stmt);
gcc_assert (snode->get_function () == m_fun);
gcc_assert (snode->get_function () == &m_fun);
}
}
break;

View file

@ -305,11 +305,10 @@ public:
/* A support class for uniquifying instances of frame_region. */
struct key_t
{
key_t (const frame_region *calling_frame, function *fun)
: m_calling_frame (calling_frame), m_fun (fun)
key_t (const frame_region *calling_frame, const function &fun)
: m_calling_frame (calling_frame), m_fun (&fun)
{
/* calling_frame can be NULL. */
gcc_assert (fun);
}
hashval_t hash () const
@ -322,7 +321,8 @@ public:
bool operator== (const key_t &other) const
{
return (m_calling_frame == other.m_calling_frame && m_fun == other.m_fun);
return (m_calling_frame == other.m_calling_frame
&& m_fun == other.m_fun);
}
void mark_deleted () { m_fun = reinterpret_cast<function *> (1); }
@ -334,12 +334,12 @@ public:
bool is_empty () const { return m_fun == NULL; }
const frame_region *m_calling_frame;
function *m_fun;
const function *m_fun;
};
frame_region (symbol::id_t id, const region *parent,
const frame_region *calling_frame,
function *fun, int index)
const function &fun, int index)
: space_region (id, parent), m_calling_frame (calling_frame),
m_fun (fun), m_index (index)
{}
@ -356,8 +356,8 @@ public:
/* Accessors. */
const frame_region *get_calling_frame () const { return m_calling_frame; }
function *get_function () const { return m_fun; }
tree get_fndecl () const { return get_function ()->decl; }
const function &get_function () const { return m_fun; }
tree get_fndecl () const { return get_function ().decl; }
int get_index () const { return m_index; }
int get_stack_depth () const { return m_index + 1; }
@ -373,7 +373,7 @@ public:
private:
const frame_region *m_calling_frame;
function *m_fun;
const function &m_fun;
int m_index;
/* The regions for the decls within this frame are managed by this

View file

@ -146,7 +146,8 @@ public:
if (change.is_global_p ()
&& change.m_new_state == m_sm.m_in_signal_handler)
{
function *handler = change.m_event.get_dest_function ();
const function *handler = change.m_event.get_dest_function ();
gcc_assert (handler);
return change.formatted_print ("registering %qD as signal handler",
handler->decl);
}
@ -193,7 +194,7 @@ signal_state_machine::signal_state_machine (logger *logger)
static void
update_model_for_signal_handler (region_model *model,
function *handler_fun)
const function &handler_fun)
{
gcc_assert (model);
/* Purge all state within MODEL. */
@ -222,7 +223,9 @@ public:
region_model_context *) const final override
{
gcc_assert (eedge);
update_model_for_signal_handler (model, eedge->m_dest->get_function ());
gcc_assert (eedge->m_dest->get_function ());
update_model_for_signal_handler (model,
*eedge->m_dest->get_function ());
return true;
}
@ -263,11 +266,11 @@ public:
program_point entering_handler
= program_point::from_function_entry (*ext_state.get_model_manager (),
eg->get_supergraph (),
handler_fun);
*handler_fun);
program_state state_entering_handler (ext_state);
update_model_for_signal_handler (state_entering_handler.m_region_model,
handler_fun);
*handler_fun);
state_entering_handler.m_checker_states[sm_idx]->set_global_state
(m_sm.m_in_signal_handler);

View file

@ -89,7 +89,7 @@ class gimple_op_visitor : public log_user
public:
gimple_op_visitor (state_purge_map *map,
const function_point &point,
function *fun)
const function &fun)
: log_user (map->get_logger ()),
m_map (map),
m_point (point),
@ -172,7 +172,7 @@ private:
state_purge_map *m_map;
const function_point &m_point;
function *m_fun;
const function &m_fun;
};
static bool
@ -214,6 +214,7 @@ state_purge_map::state_purge_map (const supergraph &sg,
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fun = node->get_fun ();
gcc_assert (fun);
if (logger)
log ("function: %s", function_name (fun));
tree name;
@ -225,7 +226,7 @@ state_purge_map::state_purge_map (const supergraph &sg,
if (TREE_CODE (var) == VAR_DECL)
if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
continue;
m_ssa_map.put (name, new state_purge_per_ssa_name (*this, name, fun));
m_ssa_map.put (name, new state_purge_per_ssa_name (*this, name, *fun));
}
}
@ -241,8 +242,10 @@ state_purge_map::state_purge_map (const supergraph &sg,
unsigned i;
FOR_EACH_VEC_ELT (snode->m_stmts, i, stmt)
{
function *fun = snode->get_function ();
gcc_assert (fun);
function_point point (function_point::before_stmt (snode, i));
gimple_op_visitor v (this, point, snode->get_function ());
gimple_op_visitor v (this, point, *fun);
walk_stmt_load_store_addr_ops (stmt, &v,
my_load_cb, my_store_cb, my_addr_cb);
}
@ -272,7 +275,7 @@ state_purge_map::~state_purge_map ()
if necessary. */
state_purge_per_decl &
state_purge_map::get_or_create_data_for_decl (function *fun, tree decl)
state_purge_map::get_or_create_data_for_decl (const function &fun, tree decl)
{
if (state_purge_per_decl **slot
= const_cast <decl_map_t&> (m_decl_map).get (decl))
@ -295,14 +298,14 @@ state_purge_map::get_or_create_data_for_decl (function *fun, tree decl)
state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
tree name,
function *fun)
const function &fun)
: state_purge_per_tree (fun), m_points_needing_name (), m_name (name)
{
LOG_FUNC (map.get_logger ());
if (map.get_logger ())
{
map.log ("SSA name: %qE within %qD", name, fun->decl);
map.log ("SSA name: %qE within %qD", name, fun.decl);
/* Show def stmt. */
const gimple *def_stmt = SSA_NAME_DEF_STMT (name);
@ -410,7 +413,7 @@ state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
if (map.get_logger ())
{
map.log ("%qE in %qD is needed to process:", name, fun->decl);
map.log ("%qE in %qD is needed to process:", name, fun.decl);
/* Log m_points_needing_name, sorting it to avoid churn when comparing
dumps. */
auto_vec<function_point> points;
@ -472,7 +475,7 @@ state_purge_per_ssa_name::add_to_worklist (const function_point &point,
logger->end_log_line ();
}
gcc_assert (point.get_function () == get_function ());
gcc_assert (point.get_function () == &get_function ());
if (point.get_from_edge ())
gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
@ -678,7 +681,7 @@ state_purge_per_ssa_name::process_point (const function_point &point,
state_purge_per_decl::state_purge_per_decl (const state_purge_map &map,
tree decl,
function *fun)
const function &fun)
: state_purge_per_tree (fun),
m_decl (decl)
{
@ -794,7 +797,7 @@ state_purge_per_decl::add_to_worklist (const function_point &point,
logger->end_log_line ();
}
gcc_assert (point.get_function () == get_function ());
gcc_assert (point.get_function () == &get_function ());
if (point.get_from_edge ())
gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
@ -1192,7 +1195,7 @@ state_purge_annotator::print_needed (graphviz_out *gv,
{
tree name = (*iter).first;
state_purge_per_ssa_name *per_name_data = (*iter).second;
if (per_name_data->get_function () == point.get_function ())
if (&per_name_data->get_function () == point.get_function ())
{
if (per_name_data->needed_at_point_p (point))
needed.safe_push (name);
@ -1206,7 +1209,7 @@ state_purge_annotator::print_needed (graphviz_out *gv,
{
tree decl = (*iter).first;
state_purge_per_decl *per_decl_data = (*iter).second;
if (per_decl_data->get_function () == point.get_function ())
if (&per_decl_data->get_function () == point.get_function ())
{
if (per_decl_data->needed_at_point_p (point))
needed.safe_push (decl);

View file

@ -112,7 +112,8 @@ public:
return NULL;
}
state_purge_per_decl &get_or_create_data_for_decl (function *fun, tree decl);
state_purge_per_decl &
get_or_create_data_for_decl (const function &fun, tree decl);
const supergraph &get_sg () const { return m_sg; }
@ -135,19 +136,19 @@ private:
class state_purge_per_tree
{
public:
function *get_function () const { return m_fun; }
tree get_fndecl () const { return m_fun->decl; }
const function &get_function () const { return m_fun; }
tree get_fndecl () const { return m_fun.decl; }
protected:
typedef hash_set<function_point> point_set_t;
state_purge_per_tree (function *fun)
state_purge_per_tree (const function &fun)
: m_fun (fun)
{
}
private:
function *m_fun;
const function &m_fun;
};
/* The part of a state_purge_map relating to a specific SSA name.
@ -162,7 +163,7 @@ class state_purge_per_ssa_name : public state_purge_per_tree
public:
state_purge_per_ssa_name (const state_purge_map &map,
tree name,
function *fun);
const function &fun);
bool needed_at_point_p (const function_point &point) const;
@ -194,7 +195,7 @@ class state_purge_per_decl : public state_purge_per_tree
public:
state_purge_per_decl (const state_purge_map &map,
tree decl,
function *fun);
const function &fun);
bool needed_at_point_p (const function_point &point) const;

View file

@ -364,6 +364,7 @@ supergraph::dump_dot_to_pp (pretty_printer *pp,
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fun = node->get_fun ();
gcc_assert (fun);
const char *funcname = function_name (fun);
gv.println ("subgraph \"cluster_%s\" {",
funcname);
@ -409,9 +410,9 @@ supergraph::dump_dot_to_pp (pretty_printer *pp,
/* Add an invisible edge from ENTRY to EXIT, to improve the graph layout. */
pp_string (pp, "\t");
get_node_for_function_entry (fun)->dump_dot_id (pp);
get_node_for_function_entry (*fun)->dump_dot_id (pp);
pp_string (pp, ":s -> ");
get_node_for_function_exit (fun)->dump_dot_id (pp);
get_node_for_function_exit (*fun)->dump_dot_id (pp);
pp_string (pp, ":n [style=\"invis\",constraint=true];\n");
/* Terminate per-function "subgraph" */

View file

@ -111,14 +111,14 @@ public:
supergraph (logger *logger);
~supergraph ();
supernode *get_node_for_function_entry (function *fun) const
supernode *get_node_for_function_entry (const function &fun) const
{
return get_node_for_block (ENTRY_BLOCK_PTR_FOR_FN (fun));
return get_node_for_block (ENTRY_BLOCK_PTR_FOR_FN (&fun));
}
supernode *get_node_for_function_exit (function *fun) const
supernode *get_node_for_function_exit (const function &fun) const
{
return get_node_for_block (EXIT_BLOCK_PTR_FOR_FN (fun));
return get_node_for_block (EXIT_BLOCK_PTR_FOR_FN (&fun));
}
supernode *get_node_for_block (basic_block bb) const

View file

@ -6391,7 +6391,7 @@ fndecl_name (tree fndecl)
/* Returns the name of function FN. */
const char *
function_name (struct function *fn)
function_name (const function *fn)
{
tree fndecl = (fn == NULL) ? NULL : fn->decl;
return fndecl_name (fndecl);

View file

@ -730,7 +730,7 @@ extern poly_int64 get_stack_dynamic_offset ();
/* Returns the name of the current function. */
extern const char *fndecl_name (tree);
extern const char *function_name (struct function *);
extern const char *function_name (const function *);
extern const char *current_function_name (void);
extern void used_types_insert (tree);

View file

@ -0,0 +1,20 @@
/* Verify we don't ICE on this case with these options. */
/* { dg-additional-options "-fanalyzer-call-summaries --param=analyzer-max-svalue-depth=0 -Wno-analyzer-symbol-too-complex" } */
int foo_i;
void bar() {}
void foo() {
if (foo_i)
bar();
else
goto f1;
bar();
f1:
bar();
}
int main() {
foo();
foo();
return 0;
}