/* Tree inlining.
- Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+ 2012 Free Software Foundation, Inc.
Contributed by Alexandre Oliva <aoliva@redhat.com>
This file is part of GCC.
if (processing_debug_stmt)
{
+ if (TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL
+ && SSA_NAME_IS_DEFAULT_DEF (name)
+ && id->entry_bb == NULL
+ && single_succ_p (ENTRY_BLOCK_PTR))
+ {
+ tree vexpr = make_node (DEBUG_EXPR_DECL);
+ gimple def_temp;
+ gimple_stmt_iterator gsi;
+ tree val = SSA_NAME_VAR (name);
+
+ n = (tree *) pointer_map_contains (id->decl_map, val);
+ if (n != NULL)
+ val = *n;
+ if (TREE_CODE (val) != PARM_DECL)
+ {
+ processing_debug_stmt = -1;
+ return name;
+ }
+ def_temp = gimple_build_debug_source_bind (vexpr, val, NULL);
+ DECL_ARTIFICIAL (vexpr) = 1;
+ TREE_TYPE (vexpr) = TREE_TYPE (name);
+ DECL_MODE (vexpr) = DECL_MODE (SSA_NAME_VAR (name));
+ gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR));
+ gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
+ return vexpr;
+ }
+
processing_debug_stmt = -1;
return name;
}
{
tree stmt = tsi_stmt (oi);
if (TREE_CODE (stmt) == STATEMENT_LIST)
+ /* This copy is not redundant; tsi_link_after will smash this
+ STATEMENT_LIST into the end of the one we're building, and we
+ don't want to do that with the original. */
copy_statement_list (&stmt);
tsi_link_after (&ni, stmt, TSI_CONTINUE_LINKING);
}
{
/* Otherwise, just copy the node. Note that copy_tree_r already
knows not to copy VAR_DECLs, etc., so this is safe. */
+
+ /* We should never have TREE_BLOCK set on non-statements. */
+ if (EXPR_P (*tp))
+ gcc_assert (!TREE_BLOCK (*tp));
+
if (TREE_CODE (*tp) == MEM_REF)
{
tree ptr = TREE_OPERAND (*tp, 0);
+ tree type = remap_type (TREE_TYPE (*tp), id);
tree old = *tp;
- tree tem;
/* We need to re-canonicalize MEM_REFs from inline substitutions
that can happen when a pointer argument is an ADDR_EXPR.
Recurse here manually to allow that. */
walk_tree (&ptr, remap_gimple_op_r, data, NULL);
- if ((tem = maybe_fold_offset_to_reference (EXPR_LOCATION (*tp),
- ptr,
- TREE_OPERAND (*tp, 1),
- TREE_TYPE (*tp)))
- && TREE_THIS_VOLATILE (tem) == TREE_THIS_VOLATILE (old))
- {
- tree *tem_basep = &tem;
- while (handled_component_p (*tem_basep))
- tem_basep = &TREE_OPERAND (*tem_basep, 0);
- if (TREE_CODE (*tem_basep) == MEM_REF)
- *tem_basep
- = build2 (MEM_REF, TREE_TYPE (*tem_basep),
- TREE_OPERAND (*tem_basep, 0),
- fold_convert (TREE_TYPE (TREE_OPERAND (*tp, 1)),
- TREE_OPERAND (*tem_basep, 1)));
- else
- *tem_basep
- = build2 (MEM_REF, TREE_TYPE (*tem_basep),
- build_fold_addr_expr (*tem_basep),
- build_int_cst
- (TREE_TYPE (TREE_OPERAND (*tp, 1)), 0));
- *tp = tem;
- }
- else
- {
- *tp = fold_build2 (MEM_REF, TREE_TYPE (*tp),
- ptr, TREE_OPERAND (*tp, 1));
- TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
- TREE_THIS_NOTRAP (*tp) = TREE_THIS_NOTRAP (old);
- }
+ *tp = fold_build2 (MEM_REF, type,
+ ptr, TREE_OPERAND (*tp, 1));
+ TREE_THIS_NOTRAP (*tp) = TREE_THIS_NOTRAP (old);
+ TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old);
*walk_subtrees = 0;
return NULL;
tweak some special cases. */
copy_tree_r (tp, walk_subtrees, NULL);
+ if (TREE_CODE (*tp) != OMP_CLAUSE)
+ TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id);
+
/* Global variables we haven't seen yet need to go into referenced
vars. If not referenced from types only. */
if (gimple_in_ssa_p (cfun)
&& !processing_debug_stmt)
add_referenced_var (*tp);
- /* We should never have TREE_BLOCK set on non-statements. */
- if (EXPR_P (*tp))
- gcc_assert (!TREE_BLOCK (*tp));
-
- if (TREE_CODE (*tp) != OMP_CLAUSE)
- TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id);
-
if (TREE_CODE (*tp) == TARGET_EXPR && TREE_OPERAND (*tp, 3))
{
/* The copied TARGET_EXPR has never been expanded, even if the
old_nr = tree_low_cst (old_t_nr, 0);
new_nr = remap_eh_region_nr (old_nr, id);
- return build_int_cst (NULL, new_nr);
+ return build_int_cst (integer_type_node, new_nr);
}
/* Helper for copy_bb. Remap statement STMT using the inlining
= gimple_build_omp_critical (s1, gimple_omp_critical_name (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));
+ break;
+
default:
gcc_unreachable ();
}
VEC_safe_push (gimple, heap, id->debug_stmts, copy);
return copy;
}
+ 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);
+ VEC_safe_push (gimple, heap, id->debug_stmts, copy);
+ return copy;
+ }
/* Create a new deep copy of the statement. */
copy = gimple_copy (stmt);
gimple_set_block (copy, new_block);
- if (gimple_debug_bind_p (copy))
+ if (gimple_debug_bind_p (copy) || gimple_debug_source_bind_p (copy))
return copy;
/* Remap all the operands in COPY. */
edge = cgraph_clone_edge (edge, id->dst_node, stmt,
gimple_uid (stmt),
REG_BR_PROB_BASE, CGRAPH_FREQ_BASE,
- edge->frequency, true);
+ true);
/* We could also just rescale the frequency, but
doing so would introduce roundoff errors and make
verifier unhappy. */
if ((!edge
|| (edge->indirect_inlining_edge
&& id->transform_call_graph_edges == CB_CGE_MOVE_CLONES))
+ && id->dst_node->analyzed
&& (fn = gimple_call_fndecl (stmt)) != NULL)
{
- struct cgraph_node *dest = cgraph_node (fn);
+ struct cgraph_node *dest = cgraph_get_node (fn);
/* We have missing edge in the callgraph. This can happen
when previous inlining turned an indirect call into a
(id->dst_node, dest, orig_stmt, stmt, bb->count,
compute_call_stmt_bb_frequency (id->dst_node->decl,
copy_basic_block),
- bb->loop_depth, CIF_ORIGINALLY_INDIRECT_CALL);
+ CIF_ORIGINALLY_INDIRECT_CALL);
else
cgraph_create_edge (id->dst_node, dest, stmt,
bb->count,
compute_call_stmt_bb_frequency
- (id->dst_node->decl, copy_basic_block),
- bb->loop_depth)->inline_failed
+ (id->dst_node->decl, copy_basic_block))->inline_failed
= CIF_ORIGINALLY_INDIRECT_CALL;
if (dump_file)
{
cfun->va_list_fpr_size = src_cfun->va_list_fpr_size;
cfun->has_nonlocal_label = src_cfun->has_nonlocal_label;
cfun->stdarg = src_cfun->stdarg;
- cfun->dont_save_pending_sizes_p = src_cfun->dont_save_pending_sizes_p;
cfun->after_inlining = src_cfun->after_inlining;
cfun->can_throw_non_call_exceptions
= src_cfun->can_throw_non_call_exceptions;
{
si = ssi;
gsi_prev (&ssi);
- if (!single_pred_p (e->dest))
+ if (!single_pred_p (e->dest) && gimple_debug_bind_p (stmt))
gimple_debug_bind_reset_value (stmt);
gsi_remove (&si, false);
gsi_insert_before (&dsi, stmt, GSI_SAME_STMT);
continue;
}
- var = gimple_debug_bind_get_var (stmt);
- if (single_pred_p (e->dest))
+ if (gimple_debug_bind_p (stmt))
+ {
+ var = gimple_debug_bind_get_var (stmt);
+ if (single_pred_p (e->dest))
+ {
+ value = gimple_debug_bind_get_value (stmt);
+ value = unshare_expr (value);
+ }
+ else
+ value = NULL_TREE;
+ new_stmt = gimple_build_debug_bind (var, value, stmt);
+ }
+ else if (gimple_debug_source_bind_p (stmt))
{
- value = gimple_debug_bind_get_value (stmt);
- value = unshare_expr (value);
+ var = gimple_debug_source_bind_get_var (stmt);
+ value = gimple_debug_source_bind_get_value (stmt);
+ new_stmt = gimple_build_debug_source_bind (var, value, stmt);
}
else
- value = NULL_TREE;
- new_stmt = gimple_build_debug_bind (var, value, stmt);
+ gcc_unreachable ();
gsi_insert_before (&dsi, new_stmt, GSI_SAME_STMT);
VEC_safe_push (gimple, heap, id->debug_stmts, new_stmt);
gsi_prev (&ssi);
t = id->block;
if (gimple_block (stmt))
{
- tree *n;
n = (tree *) pointer_map_contains (id->decl_map, gimple_block (stmt));
if (n)
t = *n;
processing_debug_stmt = 1;
- t = gimple_debug_bind_get_var (stmt);
+ if (gimple_debug_source_bind_p (stmt))
+ t = gimple_debug_source_bind_get_var (stmt);
+ else
+ t = gimple_debug_bind_get_var (stmt);
if (TREE_CODE (t) == PARM_DECL && id->debug_map
&& (n = (tree *) pointer_map_contains (id->debug_map, t)))
else
walk_tree (&t, remap_gimple_op_r, &wi, NULL);
- gimple_debug_bind_set_var (stmt, t);
+ if (gimple_debug_bind_p (stmt))
+ {
+ gimple_debug_bind_set_var (stmt, t);
- if (gimple_debug_bind_has_value_p (stmt))
- walk_tree (gimple_debug_bind_get_value_ptr (stmt),
- remap_gimple_op_r, &wi, NULL);
+ if (gimple_debug_bind_has_value_p (stmt))
+ walk_tree (gimple_debug_bind_get_value_ptr (stmt),
+ remap_gimple_op_r, &wi, NULL);
- /* Punt if any decl couldn't be remapped. */
- if (processing_debug_stmt < 0)
- gimple_debug_bind_reset_value (stmt);
+ /* Punt if any decl couldn't be remapped. */
+ if (processing_debug_stmt < 0)
+ gimple_debug_bind_reset_value (stmt);
+ }
+ else if (gimple_debug_source_bind_p (stmt))
+ {
+ gimple_debug_source_bind_set_var (stmt, t);
+ walk_tree (gimple_debug_source_bind_get_value_ptr (stmt),
+ remap_gimple_op_r, &wi, NULL);
+ }
processing_debug_stmt = 0;
&& value != error_mark_node
&& !useless_type_conversion_p (TREE_TYPE (p), TREE_TYPE (value)))
{
+ /* If we can match up types by promotion/demotion do so. */
if (fold_convertible_p (TREE_TYPE (p), value))
- rhs = fold_build1 (NOP_EXPR, TREE_TYPE (p), value);
+ rhs = fold_convert (TREE_TYPE (p), value);
else
- /* ??? For valid (GIMPLE) programs we should not end up here.
- Still if something has gone wrong and we end up with truly
- mismatched types here, fall back to using a VIEW_CONVERT_EXPR
- to not leak invalid GIMPLE to the following passes. */
- rhs = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (p), value);
+ {
+ /* ??? For valid programs we should not end up here.
+ Still if we end up with truly mismatched types here, fall back
+ to using a VIEW_CONVERT_EXPR or a literal zero to not leak invalid
+ GIMPLE to the following passes. */
+ if (!is_gimple_reg_type (TREE_TYPE (value))
+ || TYPE_SIZE (TREE_TYPE (p)) == TYPE_SIZE (TREE_TYPE (value)))
+ rhs = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (p), value);
+ else
+ rhs = build_zero_cst (TREE_TYPE (p));
+ }
}
/* Make an equivalent VAR_DECL. Note that we must NOT remap the type
else
caller_type = TREE_TYPE (TREE_TYPE (callee));
- /* We don't need to do anything for functions that don't return
- anything. */
- if (!result || VOID_TYPE_P (callee_type))
+ /* We don't need to do anything for functions that don't return anything. */
+ if (VOID_TYPE_P (callee_type))
return NULL_TREE;
/* If there was a return slot, then the return value is the
promoted, convert it back to the expected type. */
use = var;
if (!useless_type_conversion_p (caller_type, TREE_TYPE (var)))
- use = fold_convert (caller_type, var);
+ {
+ /* If we can match up types by promotion/demotion do so. */
+ if (fold_convertible_p (caller_type, var))
+ use = fold_convert (caller_type, var);
+ else
+ {
+ /* ??? For valid programs we should not end up here.
+ Still if we end up with truly mismatched types here, fall back
+ to using a MEM_REF to not leak invalid GIMPLE to the following
+ passes. */
+ /* Prevent var from being written into SSA form. */
+ if (TREE_CODE (TREE_TYPE (var)) == VECTOR_TYPE
+ || TREE_CODE (TREE_TYPE (var)) == COMPLEX_TYPE)
+ DECL_GIMPLE_REG_P (var) = false;
+ else if (is_gimple_reg_type (TREE_TYPE (var)))
+ TREE_ADDRESSABLE (var) = true;
+ use = fold_build2 (MEM_REF, caller_type,
+ build_fold_addr_expr (var),
+ build_int_cst (ptr_type_node, 0));
+ }
+ }
STRIP_USELESS_TYPE_CONVERSION (use);
this may change program's memory overhead drastically when the
function using alloca is called in loop. In GCC present in
SPEC2000 inlining into schedule_block cause it to require 2GB of
- RAM instead of 256MB. */
+ RAM instead of 256MB. Don't do so for alloca calls emitted for
+ 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)
&& !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
{
inline_forbidden_reason
return forbidden_p;
}
-/* Return true if CALLEE cannot be inlined into CALLER. */
-
-static bool
-inline_forbidden_into_p (tree caller, tree callee)
-{
- /* Don't inline if the functions have different EH personalities. */
- if (DECL_FUNCTION_PERSONALITY (caller)
- && DECL_FUNCTION_PERSONALITY (callee)
- && (DECL_FUNCTION_PERSONALITY (caller)
- != DECL_FUNCTION_PERSONALITY (callee)))
- return true;
-
- /* Don't inline if the callee can throw non-call exceptions but the
- caller cannot. */
- if (DECL_STRUCT_FUNCTION (callee)
- && DECL_STRUCT_FUNCTION (callee)->can_throw_non_call_exceptions
- && !(DECL_STRUCT_FUNCTION (caller)
- && DECL_STRUCT_FUNCTION (caller)->can_throw_non_call_exceptions))
- return true;
-
- return false;
-}
-
/* Returns nonzero if FN is a function that does not have any
fundamental inline blocking properties. */
As a bonus we can now give more details about the reason why a
function is not inlinable. */
if (always_inline)
- sorry (inline_forbidden_reason, fn);
+ error (inline_forbidden_reason, fn);
else if (do_warning)
warning (OPT_Winline, inline_forbidden_reason, fn);
??? We may consider mapping RTL costs to this. */
case COND_EXPR:
case VEC_COND_EXPR:
+ case VEC_PERM_EXPR:
case PLUS_EXPR:
case POINTER_PLUS_EXPR:
case DOT_PROD_EXPR:
case WIDEN_MULT_PLUS_EXPR:
case WIDEN_MULT_MINUS_EXPR:
+ case WIDEN_LSHIFT_EXPR:
case VEC_WIDEN_MULT_HI_EXPR:
case VEC_WIDEN_MULT_LO_EXPR:
case VEC_PACK_TRUNC_EXPR:
case VEC_PACK_SAT_EXPR:
case VEC_PACK_FIX_TRUNC_EXPR:
- case VEC_EXTRACT_EVEN_EXPR:
- case VEC_EXTRACT_ODD_EXPR:
- case VEC_INTERLEAVE_HIGH_EXPR:
- case VEC_INTERLEAVE_LOW_EXPR:
+ case VEC_WIDEN_LSHIFT_HI_EXPR:
+ case VEC_WIDEN_LSHIFT_LO_EXPR:
return 1;
likely be a real store, so the cost of the GIMPLE_ASSIGN is the cost
of moving something into "a", which we compute using the function
estimate_move_cost. */
+ if (gimple_clobber_p (stmt))
+ return 0; /* ={v} {CLOBBER} stmt expands to nothing. */
+
lhs = gimple_assign_lhs (stmt);
rhs = gimple_assign_rhs1 (stmt);
case GIMPLE_CALL:
{
tree decl = gimple_call_fndecl (stmt);
+ struct cgraph_node *node = NULL;
/* Do not special case builtins where we see the body.
This just confuse inliner. */
- if (!decl || cgraph_node (decl)->analyzed)
+ if (!decl || !(node = cgraph_get_node (decl)) || node->analyzed)
;
/* For buitins that are likely expanded to nothing or
inlined do not account operand costs. */
}
}
- cost = weights->call_cost;
+ cost = node ? weights->call_cost : weights->indirect_call_cost;
if (gimple_call_lhs (stmt))
cost += estimate_move_cost (TREE_TYPE (gimple_call_lhs (stmt)));
for (i = 0; i < gimple_call_num_args (stmt); i++)
return (weights->omp_cost
+ estimate_num_insns_seq (gimple_omp_body (stmt), weights));
+ case GIMPLE_TRANSACTION:
+ return (weights->tm_cost
+ + estimate_num_insns_seq (gimple_transaction_body (stmt),
+ weights));
+
default:
gcc_unreachable ();
}
init_inline_once (void)
{
eni_size_weights.call_cost = 1;
+ eni_size_weights.indirect_call_cost = 3;
eni_size_weights.target_builtin_call_cost = 1;
eni_size_weights.div_mod_cost = 1;
eni_size_weights.omp_cost = 40;
+ eni_size_weights.tm_cost = 10;
eni_size_weights.time_based = false;
eni_size_weights.return_cost = 1;
underestimating the cost does less harm than overestimating it, so
we choose a rather small value here. */
eni_time_weights.call_cost = 10;
+ eni_time_weights.indirect_call_cost = 15;
eni_time_weights.target_builtin_call_cost = 1;
eni_time_weights.div_mod_cost = 10;
eni_time_weights.omp_cost = 40;
+ eni_time_weights.tm_cost = 40;
eni_time_weights.time_based = true;
eni_time_weights.return_cost = 2;
}
&& gimple_has_body_p (DECL_ABSTRACT_ORIGIN (fn)))
fn = DECL_ABSTRACT_ORIGIN (fn);
- /* First check that inlining isn't simply forbidden in this case. */
- if (inline_forbidden_into_p (cg_edge->caller->decl, cg_edge->callee->decl))
- goto egress;
-
/* Don't try to inline functions that are not well-suited to inlining. */
if (!cgraph_inline_p (cg_edge, &reason))
{
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
/* Avoid warnings during early inline pass. */
- && cgraph_global_info_ready)
+ && cgraph_global_info_ready
+ /* PR 20090218-1_0.c. Body can be provided by another module. */
+ && (reason != CIF_BODY_NOT_AVAILABLE || !flag_generate_lto))
{
- sorry ("inlining failed in call to %q+F: %s", fn,
- _(cgraph_inline_failed_string (reason)));
- sorry ("called from here");
+ error ("inlining failed in call to always_inline %q+F: %s", fn,
+ cgraph_inline_failed_string (reason));
+ error ("called from here");
}
- else if (warn_inline && DECL_DECLARED_INLINE_P (fn)
+ else if (warn_inline
+ && DECL_DECLARED_INLINE_P (fn)
+ && !DECL_NO_INLINE_WARNING_P (fn)
&& !DECL_IN_SYSTEM_HEADER (fn)
&& reason != CIF_UNSPECIFIED
&& !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn))
/* Expand call statements reachable from STMT_P.
We can only have CALL_EXPRs as the "toplevel" tree code or nested
- in a MODIFY_EXPR. See gimple.c:get_call_expr_in(). We can
- unfortunately not use that function here because we need a pointer
- to the CALL_EXPR, not the tree itself. */
+ in a MODIFY_EXPR. */
static bool
gimple_expand_calls_inline (basic_block bb, copy_body_data *id)
if (fold_stmt (&gsi))
{
gimple new_stmt;
+ /* If a builtin at the end of a bb folded into nothing,
+ the following loop won't work. */
+ if (gsi_end_p (gsi))
+ {
+ cgraph_update_edges_for_call_stmt (old_stmt,
+ old_decl, NULL);
+ break;
+ }
if (gsi_end_p (i2))
i2 = gsi_start_bb (BASIC_BLOCK (first));
else
struct gimplify_ctx gctx;
bool inlined_p = false;
- /* There is no point in performing inlining if errors have already
- occurred -- and we might crash if we try to inline invalid
- code. */
- if (seen_error ())
- return 0;
-
/* Clear out ID. */
memset (&id, 0, sizeof (id));
- id.src_node = id.dst_node = cgraph_node (fn);
+ id.src_node = id.dst_node = cgraph_get_node (fn);
gcc_assert (id.dst_node->analyzed);
id.dst_fn = fn;
/* Or any functions that aren't finished yet. */
here. */
tree chain = NULL_TREE, new_tree;
- chain = TREE_CHAIN (*tp);
+ if (CODE_CONTAINS_STRUCT (code, TS_COMMON))
+ chain = TREE_CHAIN (*tp);
/* Copy the node. */
new_tree = copy_node (*tp);
CONSTRUCTOR_ELTS (*tp));
*tp = new_tree;
}
+ else if (code == STATEMENT_LIST)
+ /* We used to just abort on STATEMENT_LIST, but we can run into them
+ with statement-expressions (c++/40975). */
+ copy_statement_list (tp);
else if (TREE_CODE_CLASS (code) == tcc_type)
*walk_subtrees = 0;
else if (TREE_CODE_CLASS (code) == tcc_declaration)
*walk_subtrees = 0;
else if (TREE_CODE_CLASS (code) == tcc_constant)
*walk_subtrees = 0;
- else
- gcc_assert (code != STATEMENT_LIST);
return NULL_TREE;
}
if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
{
tree new_tree = remap_decl (arg, id);
+ if (TREE_CODE (new_tree) != PARM_DECL)
+ new_tree = id->copy_decl (arg, id);
lang_hooks.dup_lang_specific_decl (new_tree);
*parg = new_tree;
parg = &DECL_CHAIN (new_tree);
If non-NULL ARGS_TO_SKIP determine function parameters to remove
from new version.
+ If SKIP_RETURN is true, the new version will return void.
If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
If non_NULL NEW_ENTRY determine new entry BB of the clone.
*/
tree_function_versioning (tree old_decl, tree new_decl,
VEC(ipa_replace_map_p,gc)* tree_map,
bool update_clones, bitmap args_to_skip,
- bitmap blocks_to_copy, basic_block new_entry)
+ bool skip_return, bitmap blocks_to_copy,
+ basic_block new_entry)
{
struct cgraph_node *old_version_node;
struct cgraph_node *new_version_node;
&& TREE_CODE (new_decl) == FUNCTION_DECL);
DECL_POSSIBLY_INLINED (old_decl) = 1;
- old_version_node = cgraph_node (old_decl);
- new_version_node = cgraph_node (new_decl);
+ old_version_node = cgraph_get_node (old_decl);
+ gcc_checking_assert (old_version_node);
+ new_version_node = cgraph_get_node (new_decl);
+ gcc_checking_assert (new_version_node);
+
+ /* Copy over debug args. */
+ if (DECL_HAS_DEBUG_ARGS_P (old_decl))
+ {
+ VEC(tree, gc) **new_debug_args, **old_debug_args;
+ gcc_checking_assert (decl_debug_args_lookup (new_decl) == NULL);
+ DECL_HAS_DEBUG_ARGS_P (new_decl) = 0;
+ old_debug_args = decl_debug_args_lookup (old_decl);
+ if (old_debug_args)
+ {
+ new_debug_args = decl_debug_args_insert (new_decl);
+ *new_debug_args = VEC_copy (tree, gc, *old_debug_args);
+ }
+ }
/* 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
/* Add local vars. */
add_local_variables (DECL_STRUCT_FUNCTION (old_decl), cfun, &id, false);
- if (DECL_RESULT (old_decl) != NULL_TREE)
+ if (DECL_RESULT (old_decl) == NULL_TREE)
+ ;
+ else if (skip_return && !VOID_TYPE_P (TREE_TYPE (DECL_RESULT (old_decl))))
+ {
+ DECL_RESULT (new_decl)
+ = build_decl (DECL_SOURCE_LOCATION (DECL_RESULT (old_decl)),
+ RESULT_DECL, NULL_TREE, void_type_node);
+ DECL_CONTEXT (DECL_RESULT (new_decl)) = new_decl;
+ cfun->returns_struct = 0;
+ cfun->returns_pcc_struct = 0;
+ }
+ else
{
tree old_name;
DECL_RESULT (new_decl) = remap_decl (DECL_RESULT (old_decl), &id);
id.transform_call_graph_edges = CB_CGE_DUPLICATE;
id.transform_new_cfg = false;
id.transform_return_to_modify = true;
- id.transform_lang_insert_block = false;
+ 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. */
return type;
}
-
-/* Return whether it is safe to inline a function because it used different
- target specific options or call site actual types mismatch parameter types.
- E is the call edge to be checked. */
-bool
-tree_can_inline_p (struct cgraph_edge *e)
-{
-#if 0
- /* This causes a regression in SPEC in that it prevents a cold function from
- inlining a hot function. Perhaps this should only apply to functions
- that the user declares hot/cold/optimize explicitly. */
-
- /* Don't inline a function with a higher optimization level than the
- caller, or with different space constraints (hot/cold functions). */
- tree caller_tree = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (caller);
- tree callee_tree = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (callee);
-
- if (caller_tree != callee_tree)
- {
- struct cl_optimization *caller_opt
- = TREE_OPTIMIZATION ((caller_tree)
- ? caller_tree
- : optimization_default_node);
-
- struct cl_optimization *callee_opt
- = TREE_OPTIMIZATION ((callee_tree)
- ? callee_tree
- : optimization_default_node);
-
- if ((caller_opt->optimize > callee_opt->optimize)
- || (caller_opt->optimize_size != callee_opt->optimize_size))
- return false;
- }
-#endif
- tree caller, callee, lhs;
-
- caller = e->caller->decl;
- callee = e->callee->decl;
-
- /* First check that inlining isn't simply forbidden in this case. */
- if (inline_forbidden_into_p (caller, callee))
- {
- e->inline_failed = CIF_UNSPECIFIED;
- if (e->call_stmt)
- gimple_call_set_cannot_inline (e->call_stmt, true);
- return false;
- }
-
- /* Allow the backend to decide if inlining is ok. */
- if (!targetm.target_option.can_inline_p (caller, callee))
- {
- e->inline_failed = CIF_TARGET_OPTION_MISMATCH;
- if (e->call_stmt)
- gimple_call_set_cannot_inline (e->call_stmt, true);
- e->call_stmt_cannot_inline_p = true;
- return false;
- }
-
- /* Do not inline calls where we cannot triviall work around mismatches
- in argument or return types. */
- if (e->call_stmt
- && ((DECL_RESULT (callee)
- && !DECL_BY_REFERENCE (DECL_RESULT (callee))
- && (lhs = gimple_call_lhs (e->call_stmt)) != NULL_TREE
- && !useless_type_conversion_p (TREE_TYPE (DECL_RESULT (callee)),
- TREE_TYPE (lhs))
- && !fold_convertible_p (TREE_TYPE (DECL_RESULT (callee)), lhs))
- || !gimple_check_call_args (e->call_stmt)))
- {
- e->inline_failed = CIF_MISMATCHED_ARGUMENTS;
- if (e->call_stmt)
- gimple_call_set_cannot_inline (e->call_stmt, true);
- e->call_stmt_cannot_inline_p = true;
- return false;
- }
-
- return true;
-}