/* Tree inlining.
- Copyright (C) 2001-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2015 Free Software Foundation, Inc.
Contributed by Alexandre Oliva <aoliva@redhat.com>
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "diagnostic-core.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "stor-layout.h"
#include "calls.h"
#include "tree-inline.h"
#include "flags.h"
#include "params.h"
-#include "input.h"
#include "insn-config.h"
-#include "hashtab.h"
#include "langhooks.h"
#include "predict.h"
-#include "vec.h"
-#include "hash-set.h"
-#include "machmode.h"
#include "hard-reg-set.h"
#include "function.h"
#include "dominance.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "stringpool.h"
#include "tree-ssanames.h"
#include "tree-into-ssa.h"
+#include "rtl.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
#include "expr.h"
#include "tree-dfa.h"
#include "tree-ssa.h"
#include "tree-pretty-print.h"
#include "except.h"
#include "debug.h"
-#include "hash-map.h"
-#include "plugin-api.h"
-#include "ipa-ref.h"
#include "cgraph.h"
#include "alloc-pool.h"
+#include "symbol-summary.h"
#include "ipa-prop.h"
#include "value-prof.h"
#include "tree-pass.h"
#include "target.h"
#include "cfgloop.h"
#include "builtins.h"
+#include "tree-chkp.h"
#include "rtl.h" /* FIXME: For asm_str_count. */
/* Prototypes. */
-static tree declare_return_variable (copy_body_data *, tree, tree, basic_block);
+static tree declare_return_variable (copy_body_data *, tree, tree, tree,
+ basic_block);
static void remap_block (tree *, copy_body_data *);
static void copy_bind_expr (tree *, int *, copy_body_data *);
static void declare_inline_vars (tree, tree);
static tree copy_decl_to_var (tree, copy_body_data *);
static tree copy_result_decl_to_var (tree, copy_body_data *);
static tree copy_decl_maybe_to_var (tree, copy_body_data *);
-static gimple remap_gimple_stmt (gimple, copy_body_data *);
+static gimple_seq remap_gimple_stmt (gimple, copy_body_data *);
static bool delete_unreachable_blocks_update_callgraph (copy_body_data *id);
+static void insert_init_stmt (copy_body_data *, basic_block, gimple);
/* Insert a tree->tree mapping for ID. Despite the name suggests
that the trees should be variables, it is used for more than that. */
if (!gimple_in_ssa_p (id->src_cfun))
return;
- if (!MAY_HAVE_DEBUG_STMTS)
+ if (!opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
return;
if (!target_for_debug_bind (key))
&& !DECL_NAME (var)))
{
struct ptr_info_def *pi;
- new_tree = make_ssa_name (remap_type (TREE_TYPE (name), id), NULL);
+ new_tree = make_ssa_name (remap_type (TREE_TYPE (name), id));
if (!var && SSA_NAME_IDENTIFIER (name))
SET_SSA_NAME_VAR_OR_IDENTIFIER (new_tree, SSA_NAME_IDENTIFIER (name));
insert_decl_map (id, name, new_tree);
|| !id->transform_return_to_modify))
{
struct ptr_info_def *pi;
- new_tree = make_ssa_name (new_tree, NULL);
+ new_tree = make_ssa_name (new_tree);
insert_decl_map (id, name, new_tree);
SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_tree)
= SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name);
for (si = gsi_start (body); !gsi_end_p (si); gsi_next (&si))
{
- gimple new_stmt = remap_gimple_stmt (gsi_stmt (si), id);
- gimple_seq_add_stmt (&new_body, new_stmt);
+ gimple_seq new_stmts = remap_gimple_stmt (gsi_stmt (si), id);
+ gimple_seq_add_seq (&new_body, new_stmts);
}
return new_body;
block using the mapping information in ID. */
static gimple
-copy_gimple_bind (gimple stmt, copy_body_data *id)
+copy_gimple_bind (gbind *stmt, copy_body_data *id)
{
gimple new_bind;
tree new_block, new_vars;
return (TREE_CODE (decl) == PARM_DECL);
}
+/* Remap the dependence CLIQUE from the source to the destination function
+ as specified in ID. */
+
+static unsigned short
+remap_dependence_clique (copy_body_data *id, unsigned short clique)
+{
+ if (clique == 0)
+ return 0;
+ if (!id->dependence_map)
+ id->dependence_map = new hash_map<dependence_hash, unsigned short>;
+ bool existed;
+ unsigned short &newc = id->dependence_map->get_or_insert (clique, &existed);
+ if (!existed)
+ newc = ++cfun->last_clique;
+ return newc;
+}
+
/* Remap the GIMPLE operand pointed to by *TP. DATA is really a
'struct walk_stmt_info *'. DATA->INFO is a 'copy_body_data *'.
WALK_SUBTREES is used to indicate walk_gimple_op whether to keep
TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
TREE_SIDE_EFFECTS (*tp) = TREE_SIDE_EFFECTS (old);
TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old);
+ if (MR_DEPENDENCE_CLIQUE (old) != 0)
+ {
+ MR_DEPENDENCE_CLIQUE (*tp)
+ = remap_dependence_clique (id, MR_DEPENDENCE_CLIQUE (old));
+ MR_DEPENDENCE_BASE (*tp) = MR_DEPENDENCE_BASE (old);
+ }
/* We cannot propagate the TREE_THIS_NOTRAP flag if we have
remapped a parameter as the property might be valid only
for the parameter itself. */
TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
TREE_SIDE_EFFECTS (*tp) = TREE_SIDE_EFFECTS (old);
TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old);
+ if (MR_DEPENDENCE_CLIQUE (old) != 0)
+ {
+ MR_DEPENDENCE_CLIQUE (*tp)
+ = remap_dependence_clique (id, MR_DEPENDENCE_CLIQUE (old));
+ MR_DEPENDENCE_BASE (*tp) = MR_DEPENDENCE_BASE (old);
+ }
/* We cannot propagate the TREE_THIS_NOTRAP flag if we have
remapped a parameter as the property might be valid only
for the parameter itself. */
/* Helper for copy_bb. Remap statement STMT using the inlining
information in ID. Return the new statement copy. */
-static gimple
+static gimple_seq
remap_gimple_stmt (gimple stmt, copy_body_data *id)
{
gimple copy = NULL;
struct walk_stmt_info wi;
bool skip_first = false;
+ gimple_seq stmts = NULL;
+
+ if (is_gimple_debug (stmt)
+ && !opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
+ return stmts;
/* Begin by recognizing trees that we'll completely rewrite for the
inlining context. Our output for these trees is completely
statement. */
if (gimple_code (stmt) == GIMPLE_RETURN && id->transform_return_to_modify)
{
- tree retval = gimple_return_retval (stmt);
+ tree retval = gimple_return_retval (as_a <greturn *> (stmt));
+ tree retbnd = gimple_return_retbnd (stmt);
+ tree bndslot = id->retbnd;
+
+ if (retbnd && bndslot)
+ {
+ gimple bndcopy = gimple_build_assign (bndslot, retbnd);
+ memset (&wi, 0, sizeof (wi));
+ wi.info = id;
+ walk_gimple_op (bndcopy, remap_gimple_op_r, &wi);
+ gimple_seq_add_stmt (&stmts, bndcopy);
+ }
/* If we're returning something, just turn that into an
assignment into the equivalent of the original RESULT_DECL.
retval);
/* id->retvar is already substituted. Skip it on later remapping. */
skip_first = true;
+
+ /* We need to copy bounds if return structure with pointers into
+ instrumented function. */
+ if (chkp_function_instrumented_p (id->dst_fn)
+ && !bndslot
+ && !BOUNDED_P (id->retvar)
+ && chkp_type_has_pointer (TREE_TYPE (id->retvar)))
+ id->assign_stmts.safe_push (copy);
+
}
else
- return gimple_build_nop ();
+ return stmts;
}
else if (gimple_has_substatements (stmt))
{
switch (gimple_code (stmt))
{
case GIMPLE_BIND:
- copy = copy_gimple_bind (stmt, id);
+ copy = copy_gimple_bind (as_a <gbind *> (stmt), id);
break;
case GIMPLE_CATCH:
- s1 = remap_gimple_seq (gimple_catch_handler (stmt), id);
- copy = gimple_build_catch (gimple_catch_types (stmt), s1);
+ {
+ gcatch *catch_stmt = as_a <gcatch *> (stmt);
+ s1 = remap_gimple_seq (gimple_catch_handler (catch_stmt), id);
+ copy = gimple_build_catch (gimple_catch_types (catch_stmt), s1);
+ }
break;
case GIMPLE_EH_FILTER:
break;
case GIMPLE_OMP_PARALLEL:
- s1 = remap_gimple_seq (gimple_omp_body (stmt), id);
- copy = gimple_build_omp_parallel
- (s1,
- gimple_omp_parallel_clauses (stmt),
- gimple_omp_parallel_child_fn (stmt),
- gimple_omp_parallel_data_arg (stmt));
+ {
+ gomp_parallel *omp_par_stmt = as_a <gomp_parallel *> (stmt);
+ s1 = remap_gimple_seq (gimple_omp_body (omp_par_stmt), id);
+ copy = gimple_build_omp_parallel
+ (s1,
+ gimple_omp_parallel_clauses (omp_par_stmt),
+ gimple_omp_parallel_child_fn (omp_par_stmt),
+ gimple_omp_parallel_data_arg (omp_par_stmt));
+ }
break;
case GIMPLE_OMP_TASK:
case GIMPLE_OMP_CRITICAL:
s1 = remap_gimple_seq (gimple_omp_body (stmt), id);
- copy
- = gimple_build_omp_critical (s1, gimple_omp_critical_name (stmt));
+ copy = gimple_build_omp_critical (s1,
+ gimple_omp_critical_name (
+ as_a <gomp_critical *> (stmt)));
break;
case GIMPLE_TRANSACTION:
- s1 = remap_gimple_seq (gimple_transaction_body (stmt), id);
- copy = gimple_build_transaction (s1, gimple_transaction_label (stmt));
- gimple_transaction_set_subcode (copy, gimple_transaction_subcode (stmt));
+ {
+ gtransaction *old_trans_stmt = as_a <gtransaction *> (stmt);
+ gtransaction *new_trans_stmt;
+ s1 = remap_gimple_seq (gimple_transaction_body (old_trans_stmt),
+ id);
+ copy = new_trans_stmt
+ = gimple_build_transaction (
+ s1,
+ gimple_transaction_label (old_trans_stmt));
+ gimple_transaction_set_subcode (
+ new_trans_stmt,
+ gimple_transaction_subcode (old_trans_stmt));
+ }
break;
default:
value = *n;
STRIP_TYPE_NOPS (value);
if (TREE_CONSTANT (value) || TREE_READONLY (value))
- return gimple_build_nop ();
+ return NULL;
}
}
if (gimple_bb (def_stmt)
&& !bitmap_bit_p (id->blocks_to_copy,
gimple_bb (def_stmt)->index))
- return gimple_build_nop ();
+ return NULL;
}
}
if (gimple_debug_bind_p (stmt))
{
- copy = gimple_build_debug_bind (gimple_debug_bind_get_var (stmt),
- gimple_debug_bind_get_value (stmt),
- stmt);
+ gdebug *copy
+ = gimple_build_debug_bind (gimple_debug_bind_get_var (stmt),
+ gimple_debug_bind_get_value (stmt),
+ stmt);
id->debug_stmts.safe_push (copy);
- return copy;
+ gimple_seq_add_stmt (&stmts, copy);
+ return stmts;
}
if (gimple_debug_source_bind_p (stmt))
{
- copy = gimple_build_debug_source_bind
- (gimple_debug_source_bind_get_var (stmt),
- gimple_debug_source_bind_get_value (stmt), stmt);
+ gdebug *copy = gimple_build_debug_source_bind
+ (gimple_debug_source_bind_get_var (stmt),
+ gimple_debug_source_bind_get_value (stmt),
+ stmt);
id->debug_stmts.safe_push (copy);
- return copy;
+ gimple_seq_add_stmt (&stmts, copy);
+ return stmts;
}
/* Create a new deep copy of the statement. */
copy = gimple_copy (stmt);
/* Clear flags that need revisiting. */
- if (is_gimple_call (copy)
- && gimple_call_tail_p (copy))
- gimple_call_set_tail (copy, false);
+ if (gcall *call_stmt = dyn_cast <gcall *> (copy))
+ {
+ if (gimple_call_tail_p (call_stmt))
+ gimple_call_set_tail (call_stmt, false);
+ if (gimple_call_from_thunk_p (call_stmt))
+ gimple_call_set_from_thunk (call_stmt, false);
+ }
/* Remap the region numbers for __builtin_eh_{pointer,filter},
RESX and EH_DISPATCH. */
keep it valid over inlining by setting DECL_PT_UID. */
if (!id->src_cfun->gimple_df
|| !id->src_cfun->gimple_df->ipa_pta)
- gimple_call_reset_alias_info (copy);
+ gimple_call_reset_alias_info (as_a <gcall *> (copy));
}
break;
case GIMPLE_RESX:
{
- int r = gimple_resx_region (copy);
+ gresx *resx_stmt = as_a <gresx *> (copy);
+ int r = gimple_resx_region (resx_stmt);
r = remap_eh_region_nr (r, id);
- gimple_resx_set_region (copy, r);
+ gimple_resx_set_region (resx_stmt, r);
}
break;
case GIMPLE_EH_DISPATCH:
{
- int r = gimple_eh_dispatch_region (copy);
+ geh_dispatch *eh_dispatch = as_a <geh_dispatch *> (copy);
+ int r = gimple_eh_dispatch_region (eh_dispatch);
r = remap_eh_region_nr (r, id);
- gimple_eh_dispatch_set_region (copy, r);
+ gimple_eh_dispatch_set_region (eh_dispatch, r);
}
break;
}
if (gimple_debug_bind_p (copy) || gimple_debug_source_bind_p (copy))
- return copy;
+ {
+ gimple_seq_add_stmt (&stmts, copy);
+ return stmts;
+ }
/* Remap all the operands in COPY. */
memset (&wi, 0, sizeof (wi));
gimple_set_vuse (copy, NULL_TREE);
}
- return copy;
+ gimple_seq_add_stmt (&stmts, copy);
+ return stmts;
}
/* create_basic_block() will append every new block to
basic_block_info automatically. */
- copy_basic_block = create_basic_block (NULL, (void *) 0,
- (basic_block) prev->aux);
+ copy_basic_block = create_basic_block (NULL, (basic_block) prev->aux);
copy_basic_block->count = apply_scale (bb->count, count_scale);
/* We are going to rebuild frequencies from scratch. These values
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
+ gimple_seq stmts;
gimple stmt = gsi_stmt (gsi);
gimple orig_stmt = stmt;
+ gimple_stmt_iterator stmts_gsi;
+ bool stmt_added = false;
id->regimplify = false;
- stmt = remap_gimple_stmt (stmt, id);
- if (gimple_nop_p (stmt))
+ stmts = remap_gimple_stmt (stmt, id);
+
+ if (gimple_seq_empty_p (stmts))
continue;
- gimple_duplicate_stmt_histograms (cfun, stmt, id->src_cfun, orig_stmt);
seq_gsi = copy_gsi;
- /* With return slot optimization we can end up with
- non-gimple (foo *)&this->m, fix that here. */
- if (is_gimple_assign (stmt)
- && gimple_assign_rhs_code (stmt) == NOP_EXPR
- && !is_gimple_val (gimple_assign_rhs1 (stmt)))
+ for (stmts_gsi = gsi_start (stmts);
+ !gsi_end_p (stmts_gsi); )
{
- tree new_rhs;
- new_rhs = force_gimple_operand_gsi (&seq_gsi,
- gimple_assign_rhs1 (stmt),
- true, NULL, false,
- GSI_CONTINUE_LINKING);
- gimple_assign_set_rhs1 (stmt, new_rhs);
- id->regimplify = false;
- }
+ stmt = gsi_stmt (stmts_gsi);
+
+ /* Advance iterator now before stmt is moved to seq_gsi. */
+ gsi_next (&stmts_gsi);
+
+ if (gimple_nop_p (stmt))
+ continue;
+
+ gimple_duplicate_stmt_histograms (cfun, stmt, id->src_cfun,
+ orig_stmt);
- gsi_insert_after (&seq_gsi, stmt, GSI_NEW_STMT);
+ /* With return slot optimization we can end up with
+ non-gimple (foo *)&this->m, fix that here. */
+ if (is_gimple_assign (stmt)
+ && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
+ && !is_gimple_val (gimple_assign_rhs1 (stmt)))
+ {
+ tree new_rhs;
+ new_rhs = force_gimple_operand_gsi (&seq_gsi,
+ gimple_assign_rhs1 (stmt),
+ true, NULL, false,
+ GSI_CONTINUE_LINKING);
+ gimple_assign_set_rhs1 (stmt, new_rhs);
+ id->regimplify = false;
+ }
+
+ gsi_insert_after (&seq_gsi, stmt, GSI_NEW_STMT);
- if (id->regimplify)
- gimple_regimplify_operands (stmt, &seq_gsi);
+ if (id->regimplify)
+ gimple_regimplify_operands (stmt, &seq_gsi);
+
+ stmt_added = true;
+ }
+
+ if (!stmt_added)
+ continue;
/* If copy_basic_block has been empty at the start of this iteration,
call gsi_start_bb again to get at the newly added statements. */
do
{
tree fn;
+ gcall *call_stmt;
stmt = gsi_stmt (copy_gsi);
- if (is_gimple_call (stmt)
- && gimple_call_va_arg_pack_p (stmt)
- && id->gimple_call)
+ call_stmt = dyn_cast <gcall *> (stmt);
+ if (call_stmt
+ && gimple_call_va_arg_pack_p (call_stmt)
+ && id->call_stmt)
{
/* __builtin_va_arg_pack () should be replaced by
all arguments corresponding to ... in the caller. */
tree p;
- gimple new_call;
+ gcall *new_call;
vec<tree> argarray;
- size_t nargs = gimple_call_num_args (id->gimple_call);
- size_t n;
+ size_t nargs = gimple_call_num_args (id->call_stmt);
+ size_t n, i, nargs_to_copy;
+ bool remove_bounds = false;
for (p = DECL_ARGUMENTS (id->src_fn); p; p = DECL_CHAIN (p))
nargs--;
+ /* Bounds should be removed from arg pack in case
+ we handle not instrumented call in instrumented
+ function. */
+ nargs_to_copy = nargs;
+ if (gimple_call_with_bounds_p (id->call_stmt)
+ && !gimple_call_with_bounds_p (stmt))
+ {
+ for (i = gimple_call_num_args (id->call_stmt) - nargs;
+ i < gimple_call_num_args (id->call_stmt);
+ i++)
+ if (POINTER_BOUNDS_P (gimple_call_arg (id->call_stmt, i)))
+ nargs_to_copy--;
+ remove_bounds = true;
+ }
+
/* Create the new array of arguments. */
- n = nargs + gimple_call_num_args (stmt);
+ n = nargs_to_copy + gimple_call_num_args (call_stmt);
argarray.create (n);
argarray.safe_grow_cleared (n);
/* Copy all the arguments before '...' */
memcpy (argarray.address (),
- gimple_call_arg_ptr (stmt, 0),
- gimple_call_num_args (stmt) * sizeof (tree));
+ gimple_call_arg_ptr (call_stmt, 0),
+ gimple_call_num_args (call_stmt) * sizeof (tree));
- /* Append the arguments passed in '...' */
- memcpy (argarray.address () + gimple_call_num_args (stmt),
- gimple_call_arg_ptr (id->gimple_call, 0)
- + (gimple_call_num_args (id->gimple_call) - nargs),
- nargs * sizeof (tree));
+ if (remove_bounds)
+ {
+ /* Append the rest of arguments removing bounds. */
+ unsigned cur = gimple_call_num_args (call_stmt);
+ i = gimple_call_num_args (id->call_stmt) - nargs;
+ for (i = gimple_call_num_args (id->call_stmt) - nargs;
+ i < gimple_call_num_args (id->call_stmt);
+ i++)
+ if (!POINTER_BOUNDS_P (gimple_call_arg (id->call_stmt, i)))
+ argarray[cur++] = gimple_call_arg (id->call_stmt, i);
+ gcc_assert (cur == n);
+ }
+ else
+ {
+ /* Append the arguments passed in '...' */
+ memcpy (argarray.address () + gimple_call_num_args (call_stmt),
+ gimple_call_arg_ptr (id->call_stmt, 0)
+ + (gimple_call_num_args (id->call_stmt) - nargs),
+ nargs * sizeof (tree));
+ }
- new_call = gimple_build_call_vec (gimple_call_fn (stmt),
+ new_call = gimple_build_call_vec (gimple_call_fn (call_stmt),
argarray);
argarray.release ();
/* Copy all GIMPLE_CALL flags, location and block, except
GF_CALL_VA_ARG_PACK. */
- gimple_call_copy_flags (new_call, stmt);
+ gimple_call_copy_flags (new_call, call_stmt);
gimple_call_set_va_arg_pack (new_call, false);
gimple_set_location (new_call, gimple_location (stmt));
gimple_set_block (new_call, gimple_block (stmt));
- gimple_call_set_lhs (new_call, gimple_call_lhs (stmt));
+ gimple_call_set_lhs (new_call, gimple_call_lhs (call_stmt));
gsi_replace (©_gsi, new_call, false);
stmt = new_call;
}
- else if (is_gimple_call (stmt)
- && id->gimple_call
+ else if (call_stmt
+ && id->call_stmt
&& (decl = gimple_call_fndecl (stmt))
&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
&& DECL_FUNCTION_CODE (decl) == BUILT_IN_VA_ARG_PACK_LEN)
{
/* __builtin_va_arg_pack_len () should be replaced by
the number of anonymous arguments. */
- size_t nargs = gimple_call_num_args (id->gimple_call);
+ size_t nargs = gimple_call_num_args (id->call_stmt), i;
tree count, p;
gimple new_stmt;
for (p = DECL_ARGUMENTS (id->src_fn); p; p = DECL_CHAIN (p))
nargs--;
+ /* For instrumented calls we should ignore bounds. */
+ for (i = gimple_call_num_args (id->call_stmt) - nargs;
+ i < gimple_call_num_args (id->call_stmt);
+ i++)
+ if (POINTER_BOUNDS_P (gimple_call_arg (id->call_stmt, i)))
+ nargs--;
+
count = build_int_cst (integer_type_node, nargs);
new_stmt = gimple_build_assign (gimple_call_lhs (stmt), count);
gsi_replace (©_gsi, new_stmt, false);
stmt = new_stmt;
}
+ else if (call_stmt
+ && id->call_stmt
+ && gimple_call_internal_p (stmt)
+ && gimple_call_internal_fn (stmt) == IFN_TSAN_FUNC_EXIT)
+ {
+ /* Drop TSAN_FUNC_EXIT () internal calls during inlining. */
+ gsi_remove (©_gsi, false);
+ continue;
+ }
/* Statements produced by inlining can be unfolded, especially
when we constant propagated some operands. We can't fold
/* We're duplicating a CALL_EXPR. Find any corresponding
callgraph edges and update or duplicate them. */
- if (is_gimple_call (stmt))
+ if (gcall *call_stmt = dyn_cast <gcall *> (stmt))
{
struct cgraph_edge *edge;
int edge_freq = edge->frequency;
int new_freq;
struct cgraph_edge *old_edge = edge;
- edge = edge->clone (id->dst_node, stmt,
+ edge = edge->clone (id->dst_node, call_stmt,
gimple_uid (stmt),
REG_BR_PROB_BASE, CGRAPH_FREQ_BASE,
true);
gcc_assert (!edge->indirect_unknown_callee);
old_edge->speculative_call_info (direct, indirect, ref);
- indirect = indirect->clone (id->dst_node, stmt,
+ indirect = indirect->clone (id->dst_node, call_stmt,
gimple_uid (stmt),
REG_BR_PROB_BASE, CGRAPH_FREQ_BASE,
true);
case CB_CGE_MOVE_CLONES:
id->dst_node->set_call_stmt_including_clones (orig_stmt,
- stmt);
+ call_stmt);
edge = id->dst_node->get_edge (stmt);
break;
case CB_CGE_MOVE:
edge = id->dst_node->get_edge (orig_stmt);
if (edge)
- edge->set_call_stmt (stmt);
+ edge->set_call_stmt (call_stmt);
break;
default:
|| !id->dst_node->definition);
if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)
id->dst_node->create_edge_including_clones
- (dest, orig_stmt, stmt, bb->count,
+ (dest, orig_stmt, call_stmt, bb->count,
compute_call_stmt_bb_frequency (id->dst_node->decl,
copy_basic_block),
CIF_ORIGINALLY_INDIRECT_CALL);
else
- id->dst_node->create_edge (dest, stmt,
+ id->dst_node->create_edge (dest, call_stmt,
bb->count,
compute_call_stmt_bb_frequency
(id->dst_node->decl,
}
}
- notice_special_calls (stmt);
+ notice_special_calls (as_a <gcall *> (stmt));
}
maybe_duplicate_eh_stmt_fn (cfun, stmt, id->src_cfun, orig_stmt,
if (!e->dest->aux
|| ((basic_block)e->dest->aux)->index == ENTRY_BLOCK)
{
- gimple phi;
- gimple_stmt_iterator si;
+ gphi *phi;
+ gphi_iterator si;
if (!nonlocal_goto)
gcc_assert (e->flags & EDGE_EH);
{
edge re;
- phi = gsi_stmt (si);
+ phi = si.phi ();
/* For abnormal goto/call edges the receiver can be the
ENTRY_BLOCK. Do not assert this cannot happen. */
}
if (gimple_code (copy_stmt) == GIMPLE_EH_DISPATCH)
- make_eh_dispatch_edges (copy_stmt);
+ make_eh_dispatch_edges (as_a <geh_dispatch *> (copy_stmt));
else if (can_throw)
make_eh_edges (copy_stmt);
{
basic_block const new_bb = (basic_block) bb->aux;
edge_iterator ei;
- gimple phi;
- gimple_stmt_iterator si;
+ gphi *phi;
+ gphi_iterator si;
edge new_edge;
bool inserted = false;
for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
{
tree res, new_res;
- gimple new_phi;
+ gphi *new_phi;
- phi = gsi_stmt (si);
+ phi = si.phi ();
res = PHI_RESULT (phi);
new_res = res;
if (!virtual_operand_p (res))
gimple_stmt_iterator dsi = gsi_after_labels (e->dest);
while (is_gimple_debug (gsi_stmt (ssi)))
{
- gimple stmt = gsi_stmt (ssi), new_stmt;
+ gimple stmt = gsi_stmt (ssi);
+ gdebug *new_stmt;
tree var;
tree value;
redirect_all_calls (copy_body_data * id, basic_block bb)
{
gimple_stmt_iterator si;
+ gimple last = last_stmt (bb);
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
{
- if (is_gimple_call (gsi_stmt (si)))
+ gimple stmt = gsi_stmt (si);
+ if (is_gimple_call (stmt))
{
- struct cgraph_edge *edge = id->dst_node->get_edge (gsi_stmt (si));
+ struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
if (edge)
- edge->redirect_call_stmt_to_callee ();
+ {
+ edge->redirect_call_stmt_to_callee ();
+ if (stmt == last && id->call_stmt && maybe_clean_eh_stmt (stmt))
+ gimple_purge_dead_eh_edges (bb);
+ }
}
}
}
/* Now that we've duplicated the blocks, duplicate their edges. */
basic_block abnormal_goto_dest = NULL;
- if (id->gimple_call
- && stmt_can_make_abnormal_goto (id->gimple_call))
+ if (id->call_stmt
+ && stmt_can_make_abnormal_goto (id->call_stmt))
{
- gimple_stmt_iterator gsi = gsi_for_stmt (id->gimple_call);
+ gimple_stmt_iterator gsi = gsi_for_stmt (id->call_stmt);
- bb = gimple_bb (id->gimple_call);
+ bb = gimple_bb (id->call_stmt);
gsi_next (&gsi);
if (gsi_end_p (gsi))
abnormal_goto_dest = get_abnormal_succ_dispatcher (bb);
maybe_move_debug_stmts_to_successors (id, (basic_block) bb->aux);
/* Update call edge destinations. This can not be done before loop
info is updated, because we may split basic blocks. */
- if (id->transform_call_graph_edges == CB_CGE_DUPLICATE)
+ if (id->transform_call_graph_edges == CB_CGE_DUPLICATE
+ && bb->index != ENTRY_BLOCK
+ && bb->index != EXIT_BLOCK)
redirect_all_calls (id, (basic_block)bb->aux);
((basic_block)bb->aux)->aux = NULL;
bb->aux = NULL;
delete id->eh_map;
id->eh_map = NULL;
}
+ if (id->dependence_map)
+ {
+ delete id->dependence_map;
+ id->dependence_map = NULL;
+ }
return new_fndecl;
}
this arises, we drop the VALUE expression altogether. */
static void
-copy_debug_stmt (gimple stmt, copy_body_data *id)
+copy_debug_stmt (gdebug *stmt, copy_body_data *id)
{
tree t, *n;
struct walk_stmt_info wi;
t = gimple_debug_source_bind_get_value (stmt);
if (t != NULL_TREE
&& TREE_CODE (t) == PARM_DECL
- && id->gimple_call)
+ && id->call_stmt)
{
vec<tree, va_gc> **debug_args = decl_debug_args_lookup (id->src_fn);
unsigned int i;
copy_debug_stmts (copy_body_data *id)
{
size_t i;
- gimple stmt;
+ gdebug *stmt;
if (!id->debug_stmts.exists ())
return;
if (!gimple_in_ssa_p (id->src_cfun))
return NULL;
- if (!MAY_HAVE_DEBUG_STMTS)
+ if (!opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
return NULL;
tracked_var = target_for_debug_bind (var);
base_stmt = gsi_stmt (gsi);
}
- note = gimple_build_debug_bind (tracked_var, value, base_stmt);
+ note = gimple_build_debug_bind (tracked_var, unshare_expr (value), base_stmt);
if (bb)
{
gsi_insert_after (&si, init_stmt, GSI_NEW_STMT);
gimple_regimplify_operands (init_stmt, &si);
- if (!is_gimple_debug (init_stmt) && MAY_HAVE_DEBUG_STMTS)
+ if (!is_gimple_debug (init_stmt))
{
tree def = gimple_assign_lhs (init_stmt);
insert_init_debug_bind (id, bb, def, def, init_stmt);
}
else if (!optimize)
{
- def = make_ssa_name (var, NULL);
+ def = make_ssa_name (var);
init_stmt = gimple_build_assign (def, rhs);
}
}
is set only for CALL_EXPR_RETURN_SLOT_OPT. MODIFY_DEST, if non-null,
was the LHS of the MODIFY_EXPR to which this call is the RHS.
+ RETURN_BOUNDS holds a destination for returned bounds.
+
The return value is a (possibly null) value that holds the result
as seen by the caller. */
static tree
declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest,
- basic_block entry_bb)
+ tree return_bounds, basic_block entry_bb)
{
tree callee = id->src_fn;
tree result = DECL_RESULT (callee);
if (gimple_in_ssa_p (id->src_cfun)
&& is_gimple_reg (result))
{
- temp = make_ssa_name (temp, NULL);
+ temp = make_ssa_name (temp);
insert_decl_map (id, ssa_default_def (id->src_cfun, result), temp);
}
insert_init_stmt (id, entry_bb, gimple_build_assign (temp, var));
/* Remember this so we can ignore it in remap_decls. */
id->retvar = var;
+ /* If returned bounds are used, then make var for them. */
+ if (return_bounds)
+ {
+ tree bndtemp = create_tmp_var (pointer_bounds_type_node, "retbnd");
+ DECL_SEEN_IN_BIND_EXPR_P (bndtemp) = 1;
+ TREE_NO_WARNING (bndtemp) = 1;
+ declare_inline_vars (id->block, bndtemp);
+
+ id->retbnd = bndtemp;
+ insert_init_stmt (id, entry_bb,
+ gimple_build_assign (bndtemp, chkp_get_zero_bounds_var ()));
+ }
+
return use;
}
/* Determine if the function can be copied. If so return NULL. If
not return a string describng the reason for failure. */
-static const char *
+const char *
copy_forbidden (struct function *fun, tree fndecl)
{
const char *reason = fun->cannot_be_copied_reason;
VLA objects as those can't cause unbounded growth (they're always
wrapped inside stack_save/stack_restore regions. */
if (gimple_alloca_call_p (stmt)
- && !gimple_call_alloca_for_var_p (stmt)
+ && !gimple_call_alloca_for_var_p (as_a <gcall *> (stmt))
&& !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
{
inline_forbidden_reason
case RSHIFT_EXPR:
case LROTATE_EXPR:
case RROTATE_EXPR:
- case VEC_RSHIFT_EXPR:
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
break;
case GIMPLE_SWITCH:
- /* Take into account cost of the switch + guess 2 conditional jumps for
- each case label.
-
- TODO: once the switch expansion logic is sufficiently separated, we can
- do better job on estimating cost of the switch. */
- if (weights->time_based)
- cost = floor_log2 (gimple_switch_num_labels (stmt)) * 2;
- else
- cost = gimple_switch_num_labels (stmt) * 2;
+ {
+ gswitch *switch_stmt = as_a <gswitch *> (stmt);
+ /* Take into account cost of the switch + guess 2 conditional jumps for
+ each case label.
+
+ TODO: once the switch expansion logic is sufficiently separated, we can
+ do better job on estimating cost of the switch. */
+ if (weights->time_based)
+ cost = floor_log2 (gimple_switch_num_labels (switch_stmt)) * 2;
+ else
+ cost = gimple_switch_num_labels (switch_stmt) * 2;
+ }
break;
case GIMPLE_CALL:
case GIMPLE_ASM:
{
- int count = asm_str_count (gimple_asm_string (stmt));
+ int count = asm_str_count (gimple_asm_string (as_a <gasm *> (stmt)));
/* 1000 means infinity. This avoids overflows later
with very long asm statements. */
if (count > 1000)
return 10;
case GIMPLE_BIND:
- return estimate_num_insns_seq (gimple_bind_body (stmt), weights);
+ return estimate_num_insns_seq (
+ gimple_bind_body (as_a <gbind *> (stmt)),
+ weights);
case GIMPLE_EH_FILTER:
return estimate_num_insns_seq (gimple_eh_filter_failure (stmt), weights);
case GIMPLE_CATCH:
- return estimate_num_insns_seq (gimple_catch_handler (stmt), weights);
+ return estimate_num_insns_seq (gimple_catch_handler (
+ as_a <gcatch *> (stmt)),
+ weights);
case GIMPLE_TRY:
return (estimate_num_insns_seq (gimple_try_eval (stmt), weights)
+ estimate_num_insns_seq (gimple_try_cleanup (stmt), weights));
- /* OpenMP directives are generally very expensive. */
+ /* OMP directives are generally very expensive. */
case GIMPLE_OMP_RETURN:
case GIMPLE_OMP_SECTIONS_SWITCH:
case GIMPLE_TRANSACTION:
return (weights->tm_cost
- + estimate_num_insns_seq (gimple_transaction_body (stmt),
+ + estimate_num_insns_seq (gimple_transaction_body (
+ as_a <gtransaction *> (stmt)),
weights));
default:
}
}
+/* Add to BINDINGS a debug stmt resetting SRCVAR if inlining might
+ have brought in or introduced any debug stmts for SRCVAR. */
+
+static inline void
+reset_debug_binding (copy_body_data *id, tree srcvar, gimple_seq *bindings)
+{
+ tree *remappedvarp = id->decl_map->get (srcvar);
+
+ if (!remappedvarp)
+ return;
+
+ if (TREE_CODE (*remappedvarp) != VAR_DECL)
+ return;
+
+ if (*remappedvarp == id->retvar || *remappedvarp == id->retbnd)
+ return;
+
+ tree tvar = target_for_debug_bind (*remappedvarp);
+ if (!tvar)
+ return;
+
+ gdebug *stmt = gimple_build_debug_bind (tvar, NULL_TREE,
+ id->call_stmt);
+ gimple_seq_add_stmt (bindings, stmt);
+}
+
+/* For each inlined variable for which we may have debug bind stmts,
+ add before GSI a final debug stmt resetting it, marking the end of
+ its life, so that var-tracking knows it doesn't have to compute
+ further locations for it. */
+
+static inline void
+reset_debug_bindings (copy_body_data *id, gimple_stmt_iterator gsi)
+{
+ tree var;
+ unsigned ix;
+ gimple_seq bindings = NULL;
+
+ if (!gimple_in_ssa_p (id->src_cfun))
+ return;
+
+ if (!opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
+ return;
+
+ for (var = DECL_ARGUMENTS (id->src_fn);
+ var; var = DECL_CHAIN (var))
+ reset_debug_binding (id, var, &bindings);
+
+ FOR_EACH_LOCAL_DECL (id->src_cfun, ix, var)
+ reset_debug_binding (id, var, &bindings);
+
+ gsi_insert_seq_before_without_update (&gsi, bindings, GSI_SAME_STMT);
+}
+
/* If STMT is a GIMPLE_CALL, replace it with its inline expansion. */
static bool
hash_map<tree, tree> *st = NULL;
tree return_slot;
tree modify_dest;
+ tree return_bounds = NULL;
location_t saved_location;
struct cgraph_edge *cg_edge;
cgraph_inline_failed_t reason;
gimple_stmt_iterator gsi, stmt_gsi;
bool successfully_inlined = FALSE;
bool purge_dead_abnormal_edges;
+ gcall *call_stmt;
+ unsigned int i;
/* Set input_location here so we get the right instantiation context
if we call instantiate_decl from inlinable_function_p. */
input_location = gimple_location (stmt);
/* From here on, we're only interested in CALL_EXPRs. */
- if (gimple_code (stmt) != GIMPLE_CALL)
+ call_stmt = dyn_cast <gcall *> (stmt);
+ if (!call_stmt)
goto egress;
cg_edge = id->dst_node->get_edge (stmt);
goto egress;
}
fn = cg_edge->callee->decl;
- cg_edge->callee->get_body ();
+ cg_edge->callee->get_untransformed_body ();
#ifdef ENABLE_CHECKING
if (cg_edge->callee->decl != id->dst_node->decl)
/* We will be inlining this callee. */
id->eh_lp_nr = lookup_stmt_eh_lp (stmt);
+ id->assign_stmts.create (0);
/* Update the callers EH personality. */
if (DECL_FUNCTION_PERSONALITY (cg_edge->callee->decl))
id->src_fn = fn;
id->src_node = cg_edge->callee;
id->src_cfun = DECL_STRUCT_FUNCTION (fn);
- id->gimple_call = stmt;
+ id->call_stmt = stmt;
+
+ /* If the the src function contains an IFN_VA_ARG, then so will the dst
+ function after inlining. */
+ if ((id->src_cfun->curr_properties & PROP_gimple_lva) == 0)
+ {
+ struct function *dst_cfun = DECL_STRUCT_FUNCTION (id->dst_fn);
+ dst_cfun->curr_properties &= ~PROP_gimple_lva;
+ }
gcc_assert (!id->src_cfun->after_inlining);
{
modify_dest = gimple_call_lhs (stmt);
+ /* Remember where to copy returned bounds. */
+ if (gimple_call_with_bounds_p (stmt)
+ && TREE_CODE (modify_dest) == SSA_NAME)
+ {
+ gcall *retbnd = chkp_retbnd_call_by_val (modify_dest);
+ if (retbnd)
+ {
+ return_bounds = gimple_call_lhs (retbnd);
+ /* If returned bounds are not used then just
+ remove unused call. */
+ if (!return_bounds)
+ {
+ gimple_stmt_iterator iter = gsi_for_stmt (retbnd);
+ gsi_remove (&iter, true);
+ }
+ }
+ }
+
/* The function which we are inlining might not return a value,
in which case we should issue a warning that the function
does not return a value. In that case the optimizers will
if (DECL_P (modify_dest))
TREE_NO_WARNING (modify_dest) = 1;
- if (gimple_call_return_slot_opt_p (stmt))
+ if (gimple_call_return_slot_opt_p (call_stmt))
{
return_slot = modify_dest;
modify_dest = NULL;
}
/* Declare the return variable for the function. */
- use_retvar = declare_return_variable (id, return_slot, modify_dest, bb);
+ use_retvar = declare_return_variable (id, return_slot, modify_dest,
+ return_bounds, bb);
/* Add local vars in this inlined callee to caller. */
add_local_variables (id->src_cfun, cfun, id);
GCOV_COMPUTE_SCALE (cg_edge->frequency, CGRAPH_FREQ_BASE),
bb, return_block, NULL);
+ reset_debug_bindings (id, stmt_gsi);
+
/* Reset the escaped solution. */
if (cfun->gimple_df)
pt_solution_reset (&cfun->gimple_df->escaped);
stmt = gimple_build_assign (gimple_call_lhs (stmt), use_retvar);
gsi_replace (&stmt_gsi, stmt, false);
maybe_clean_or_replace_eh_stmt (old_stmt, stmt);
+
+ /* Copy bounds if we copy structure with bounds. */
+ if (chkp_function_instrumented_p (id->dst_fn)
+ && !BOUNDED_P (use_retvar)
+ && chkp_type_has_pointer (TREE_TYPE (use_retvar)))
+ id->assign_stmts.safe_push (stmt);
}
else
{
gsi_remove (&stmt_gsi, true);
}
+ /* Put returned bounds into the correct place if required. */
+ if (return_bounds)
+ {
+ gimple old_stmt = SSA_NAME_DEF_STMT (return_bounds);
+ gimple new_stmt = gimple_build_assign (return_bounds, id->retbnd);
+ gimple_stmt_iterator bnd_gsi = gsi_for_stmt (old_stmt);
+ unlink_stmt_vdef (old_stmt);
+ gsi_replace (&bnd_gsi, new_stmt, false);
+ maybe_clean_or_replace_eh_stmt (old_stmt, new_stmt);
+ cgraph_update_edges_for_call_stmt (old_stmt,
+ gimple_call_fndecl (old_stmt),
+ new_stmt);
+ }
+
if (purge_dead_abnormal_edges)
{
gimple_purge_dead_eh_edges (return_block);
TREE_USED (gimple_assign_rhs1 (stmt)) = 1;
}
+ /* Copy bounds for all generated assigns that need it. */
+ for (i = 0; i < id->assign_stmts.length (); i++)
+ chkp_copy_bounds_for_assign (id->assign_stmts[i], cg_edge);
+ id->assign_stmts.release ();
+
/* Output the inlining info for this abstract function, since it has been
inlined. If we don't do this now, we can lose the information about the
variables in the function when the blocks get blown away as soon as we
gimple_expand_calls_inline (basic_block bb, copy_body_data *id)
{
gimple_stmt_iterator gsi;
+ bool inlined = false;
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi);)
{
gimple stmt = gsi_stmt (gsi);
+ gsi_prev (&gsi);
if (is_gimple_call (stmt)
- && !gimple_call_internal_p (stmt)
- && expand_call_inline (bb, stmt, id))
- return true;
+ && !gimple_call_internal_p (stmt))
+ inlined |= expand_call_inline (bb, stmt, id);
}
- return false;
+ return inlined;
}
struct walk_stmt_info *wi)
{
copy_body_data *id = (copy_body_data *) wi->info;
- gimple stmt = gsi_stmt (*gsip);
+ glabel *stmt = dyn_cast <glabel *> (gsi_stmt (*gsip));
- if (gimple_code (stmt) == GIMPLE_LABEL)
+ if (stmt)
{
tree decl = gimple_label_label (stmt);
struct walk_stmt_info *wi)
{
copy_body_data *id = (copy_body_data *) wi->info;
- gimple stmt = gsi_stmt (*gsip);
+ gimple gs = gsi_stmt (*gsip);
- if (gimple_code (stmt) == GIMPLE_BIND)
+ if (gbind *stmt = dyn_cast <gbind *> (gs))
{
tree block = gimple_bind_block (stmt);
delete id.decl_map;
if (id.debug_map)
delete id.debug_map;
+ if (id.dependence_map)
+ {
+ delete id.dependence_map;
+ id.dependence_map = NULL;
+ }
return copy;
}
&& DECL_BY_REFERENCE (DECL_RESULT (old_decl))
&& (old_name = ssa_default_def (id.src_cfun, DECL_RESULT (old_decl))))
{
- tree new_name = make_ssa_name (DECL_RESULT (new_decl), NULL);
+ tree new_name = make_ssa_name (DECL_RESULT (new_decl));
insert_decl_map (&id, old_name, new_name);
SSA_NAME_DEF_STMT (new_name) = gimple_build_nop ();
set_ssa_default_def (cfun, DECL_RESULT (new_decl), new_name);
return type;
}
+
+/* Unshare the entire DECL_SAVED_TREE of FN and return the remapped
+ parameters and RESULT_DECL in PARMS and RESULT. Used by C++ constexpr
+ evaluation. */
+
+tree
+copy_fn (tree fn, tree& parms, tree& result)
+{
+ copy_body_data id;
+ tree param;
+ hash_map<tree, tree> decl_map;
+
+ tree *p = &parms;
+ *p = NULL_TREE;
+
+ memset (&id, 0, sizeof (id));
+ id.src_fn = fn;
+ id.dst_fn = current_function_decl;
+ id.src_cfun = DECL_STRUCT_FUNCTION (fn);
+ id.decl_map = &decl_map;
+
+ id.copy_decl = copy_decl_no_change;
+ id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+ id.transform_new_cfg = false;
+ id.transform_return_to_modify = false;
+ id.transform_parameter = true;
+ id.transform_lang_insert_block = NULL;
+
+ /* Make sure not to unshare trees behind the front-end's back
+ since front-end specific mechanisms may rely on sharing. */
+ id.regimplify = false;
+ id.do_not_unshare = true;
+
+ /* We're not inside any EH region. */
+ id.eh_lp_nr = 0;
+
+ /* Remap the parameters and result and return them to the caller. */
+ for (param = DECL_ARGUMENTS (fn);
+ param;
+ param = DECL_CHAIN (param))
+ {
+ *p = remap_decl (param, &id);
+ p = &DECL_CHAIN (*p);
+ }
+
+ if (DECL_RESULT (fn))
+ result = remap_decl (DECL_RESULT (fn), &id);
+ else
+ result = NULL_TREE;
+
+ return copy_tree_body (&id);
+}