/* Tree inlining.
- Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- 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.
#include "value-prof.h"
#include "tree-pass.h"
#include "target.h"
-#include "integrate.h"
#include "rtl.h" /* FIXME: For asm_str_count. */
|| decl_function_context (*tp) == id->src_fn))
/* These may need to be remapped for EH handling. */
*tp = remap_decl (*tp, id);
+ else if (TREE_CODE (*tp) == FIELD_DECL)
+ {
+ /* If the enclosing record type is variably_modified_type_p, the field
+ has already been remapped. Otherwise, it need not be. */
+ tree *n = (tree *) pointer_map_contains (id->decl_map, *tp);
+ if (n)
+ *tp = *n;
+ *walk_subtrees = 0;
+ }
else if (TYPE_P (*tp))
/* Types may need remapping as well. */
*tp = remap_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)
- && TREE_CODE (*tp) == VAR_DECL
+ if (gimple_referenced_vars (cfun)
+ && TREE_CODE (*tp) == VAR_DECL && !is_global_var (*tp)
&& id->remapping_type_depth == 0
&& !processing_debug_stmt)
add_referenced_var (*tp);
/* Global variables we haven't seen yet needs to go into referenced
vars. If not referenced from types or debug stmts only. */
- if (gimple_in_ssa_p (cfun)
- && TREE_CODE (*tp) == VAR_DECL
+ if (gimple_referenced_vars (cfun)
+ && TREE_CODE (*tp) == VAR_DECL && !is_global_var (*tp)
&& id->remapping_type_depth == 0
&& !processing_debug_stmt)
add_referenced_var (*tp);
doing so would introduce roundoff errors and make
verifier unhappy. */
edge->frequency
- = compute_call_stmt_bb_frequency (id->dst_node->decl,
+ = compute_call_stmt_bb_frequency (id->dst_node->symbol.decl,
copy_basic_block);
if (dump_file
&& profile_status_for_function (cfun) != PROFILE_ABSENT
producing dead clone (for further cloning). In all
other cases we hit a bug (incorrect node sharing is the
most common reason for missing edges). */
- gcc_assert (dest->needed || !dest->analyzed
- || dest->address_taken
+ gcc_assert (!dest->analyzed
+ || dest->symbol.address_taken
|| !id->src_node->analyzed
|| !id->dst_node->analyzed);
if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)
cgraph_create_edge_including_clones
(id->dst_node, dest, orig_stmt, stmt, bb->count,
- compute_call_stmt_bb_frequency (id->dst_node->decl,
+ compute_call_stmt_bb_frequency (id->dst_node->symbol.decl,
copy_basic_block),
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))->inline_failed
+ (id->dst_node->symbol.decl,
+ copy_basic_block))->inline_failed
= CIF_ORIGINALLY_INDIRECT_CALL;
if (dump_file)
{
ssa_op_iter i;
tree def;
- find_new_referenced_vars (gsi_stmt (copy_gsi));
+ find_referenced_vars_in (gsi_stmt (copy_gsi));
FOR_EACH_SSA_TREE_OPERAND (def, stmt, i, SSA_OP_DEF)
if (TREE_CODE (def) == SSA_NAME)
SSA_NAME_DEF_STMT (def) = stmt;
copy_stmt = gsi_stmt (si);
if (!is_gimple_debug (copy_stmt))
- {
- update_stmt (copy_stmt);
- if (gimple_in_ssa_p (cfun))
- mark_symbols_for_renaming (copy_stmt);
- }
+ update_stmt (copy_stmt);
/* Do this before the possible split_block. */
gsi_next (&si);
edge new_edge;
bool inserted = false;
- for (si = gsi_start (phi_nodes (bb)); !gsi_end_p (si); gsi_next (&si))
+ for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
{
tree res, new_res;
gimple new_phi;
cfun->static_chain_decl = src_cfun->static_chain_decl;
cfun->nonlocal_goto_save_area = src_cfun->nonlocal_goto_save_area;
cfun->function_end_locus = src_cfun->function_end_locus;
- cfun->curr_properties = src_cfun->curr_properties;
+ cfun->curr_properties = src_cfun->curr_properties & ~PROP_loops;
cfun->last_verified = src_cfun->last_verified;
cfun->va_list_gpr_size = src_cfun->va_list_gpr_size;
cfun->va_list_fpr_size = src_cfun->va_list_fpr_size;
{
init_tree_ssa (cfun);
cfun->gimple_df->in_ssa_p = true;
- init_ssa_operands ();
+ init_ssa_operands (cfun);
}
pop_cfun ();
}
processing_debug_stmt = 0;
update_stmt (stmt);
- if (gimple_in_ssa_p (cfun))
- mark_symbols_for_renaming (stmt);
}
/* Process deferred debug stmts. In order to give values better odds
}
gsi_insert_after (&si, init_stmt, GSI_NEW_STMT);
gimple_regimplify_operands (init_stmt, &si);
- mark_symbols_for_renaming (init_stmt);
if (!is_gimple_debug (init_stmt) && MAY_HAVE_DEBUG_STMTS)
{
/* Make gimplifier happy about this variable. */
DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
+ /* We are eventually using the value - make sure all variables
+ referenced therein are properly recorded. */
+ if (value
+ && gimple_referenced_vars (cfun)
+ && TREE_CODE (value) == ADDR_EXPR)
+ {
+ tree base = get_base_address (TREE_OPERAND (value, 0));
+ if (base && TREE_CODE (base) == VAR_DECL && !is_global_var (base))
+ add_referenced_var (base);
+ }
+
/* If the parameter is never assigned to, has no SSA_NAMEs created,
we would not need to create a new variable here at all, if it
weren't for debug info. Still, we can just use the argument
STRIP_USELESS_TYPE_CONVERSION (rhs);
- /* We want to use MODIFY_EXPR, not INIT_EXPR here so that we
- keep our trees in gimple form. */
- if (def && gimple_in_ssa_p (cfun) && is_gimple_reg (p))
+ /* If we are in SSA form properly remap the default definition
+ or assign to a dummy SSA name if the parameter is unused and
+ we are not optimizing. */
+ if (gimple_in_ssa_p (cfun) && is_gimple_reg (p))
{
- def = remap_ssa_name (def, id);
- init_stmt = gimple_build_assign (def, rhs);
- SSA_NAME_IS_DEFAULT_DEF (def) = 0;
- set_default_def (var, NULL);
+ if (def)
+ {
+ def = remap_ssa_name (def, id);
+ init_stmt = gimple_build_assign (def, rhs);
+ SSA_NAME_IS_DEFAULT_DEF (def) = 0;
+ set_default_def (var, NULL);
+ }
+ else if (!optimize)
+ {
+ def = make_ssa_name (var, NULL);
+ init_stmt = gimple_build_assign (def, rhs);
+ }
}
else
init_stmt = gimple_build_assign (var, rhs);
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
gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) == INTEGER_CST);
var = copy_result_decl_to_var (result, id);
- if (gimple_in_ssa_p (cfun))
+ if (gimple_referenced_vars (cfun))
add_referenced_var (var);
DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
TREE_ADDRESSABLE (var) = 1;
var = build_fold_addr_expr (var);
}
+ else if (gimple_in_ssa_p (cfun)
+ && is_gimple_reg (var))
+ /* ??? Re-org id->retval and its special handling so that we can
+ record an SSA name directly and not need to invoke the SSA renamer. */
+ mark_sym_for_renaming (var);
done:
/* Register the VAR_DECL as the equivalent for the RESULT_DECL; that
&& !is_gimple_val (var))
{
tree temp = create_tmp_var (TREE_TYPE (result), "retvalptr");
- if (gimple_in_ssa_p (id->src_cfun))
+ if (gimple_referenced_vars (cfun))
add_referenced_var (temp);
insert_decl_map (id, result, temp);
- /* When RESULT_DECL is in SSA form, we need to use it's default_def
- SSA_NAME. */
- if (gimple_in_ssa_p (id->src_cfun) && gimple_default_def (id->src_cfun, result))
- temp = remap_ssa_name (gimple_default_def (id->src_cfun, result), id);
+ /* When RESULT_DECL is in SSA form, we need to remap and initialize
+ it's default_def SSA_NAME. */
+ if (gimple_in_ssa_p (id->src_cfun)
+ && is_gimple_reg (result))
+ {
+ temp = make_ssa_name (temp, NULL);
+ insert_decl_map (id, gimple_default_def (id->src_cfun, result),
+ temp);
+ }
insert_init_stmt (id, entry_bb, gimple_build_assign (temp, var));
}
else
pointer_set_destroy (visited_nodes);
return forbidden_p;
}
+\f
+/* Return false if the function FNDECL cannot be inlined on account of its
+ attributes, true otherwise. */
+static bool
+function_attribute_inlinable_p (const_tree fndecl)
+{
+ if (targetm.attribute_table)
+ {
+ const_tree a;
+
+ for (a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
+ {
+ const_tree name = TREE_PURPOSE (a);
+ int i;
+
+ for (i = 0; targetm.attribute_table[i].name != NULL; i++)
+ if (is_attribute_p (targetm.attribute_table[i].name, name))
+ return targetm.function_attribute_inlinable_p (fndecl);
+ }
+ }
+
+ return true;
+}
/* Returns nonzero if FN is a function that does not have any
fundamental inline blocking properties. */
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:
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;
+ struct cgraph_node *node = NULL;
/* Do not special case builtins where we see the body.
This just confuse inliner. */
}
}
- 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++)
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;
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;
If we cannot, then there is no hope of inlining the function. */
if (cg_edge->indirect_unknown_callee)
goto egress;
- fn = cg_edge->callee->decl;
+ fn = cg_edge->callee->symbol.decl;
gcc_checking_assert (fn);
/* If FN is a declaration of a function in a nested scope that was
fn = DECL_ABSTRACT_ORIGIN (fn);
/* Don't try to inline functions that are not well-suited to inlining. */
- if (!cgraph_inline_p (cg_edge, &reason))
+ if (cg_edge->inline_failed)
{
+ reason = cg_edge->inline_failed;
/* If this call was originally indirect, we do not want to emit any
inlining related warnings or sorry messages because there are no
guarantees regarding those. */
}
goto egress;
}
- fn = cg_edge->callee->decl;
+ fn = cg_edge->callee->symbol.decl;
#ifdef ENABLE_CHECKING
- if (cg_edge->callee->decl != id->dst_node->decl)
+ if (cg_edge->callee->symbol.decl != id->dst_node->symbol.decl)
verify_cgraph_node (cg_edge->callee);
#endif
id->eh_lp_nr = lookup_stmt_eh_lp (stmt);
/* Update the callers EH personality. */
- if (DECL_FUNCTION_PERSONALITY (cg_edge->callee->decl))
- DECL_FUNCTION_PERSONALITY (cg_edge->caller->decl)
- = DECL_FUNCTION_PERSONALITY (cg_edge->callee->decl);
+ if (DECL_FUNCTION_PERSONALITY (cg_edge->callee->symbol.decl))
+ DECL_FUNCTION_PERSONALITY (cg_edge->caller->symbol.decl)
+ = DECL_FUNCTION_PERSONALITY (cg_edge->callee->symbol.decl);
/* Split the block holding the GIMPLE_CALL. */
e = split_block (bb, stmt);
gimple old_stmt = stmt;
stmt = gimple_build_assign (gimple_call_lhs (stmt), use_retvar);
gsi_replace (&stmt_gsi, stmt, false);
- if (gimple_in_ssa_p (cfun))
- mark_symbols_for_renaming (stmt);
maybe_clean_or_replace_eh_stmt (old_stmt, stmt);
}
else
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
remove the cgraph node. */
- (*debug_hooks->outlining_inline_function) (cg_edge->callee->decl);
+ (*debug_hooks->outlining_inline_function) (cg_edge->callee->symbol.decl);
/* Update callgraph if needed. */
cgraph_remove_node (cg_edge->callee);
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));
if ((e = cgraph_edge (id->dst_node, gsi_stmt (bsi))) != NULL)
{
if (!e->inline_failed)
- cgraph_remove_node_and_inline_clones (e->callee);
+ cgraph_remove_node_and_inline_clones (e->callee, id->dst_node);
else
cgraph_remove_edge (e);
}
if ((e = cgraph_edge (node, gsi_stmt (bsi))) != NULL)
{
if (!e->inline_failed)
- cgraph_remove_node_and_inline_clones (e->callee);
+ cgraph_remove_node_and_inline_clones (e->callee, id->dst_node);
else
cgraph_remove_edge (e);
}
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;
if (TREE_CODE (op) == ADDR_EXPR)
{
- op = TREE_OPERAND (op, 0);
- while (handled_component_p (op))
- op = TREE_OPERAND (op, 0);
- if (TREE_CODE (op) == VAR_DECL)
+ op = get_base_address (TREE_OPERAND (op, 0));
+ if (op && TREE_CODE (op) == VAR_DECL && !is_global_var (op))
add_referenced_var (op);
}
gcc_assert (TREE_CODE (replace_info->old_tree) == PARM_DECL);
/* 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);