blob: 4c4694b53815f45f65b7453d7bc900d1dfa00c2c [file] [log] [blame]
/* 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"
#define INCLUDE_MEMORY
#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_THREAD_LOCAL:
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_ERRNO:
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 */