#include "toplev.h"
#include "tree.h"
#include "tree-inline.h"
-#include "rtl.h"
-#include "expr.h"
#include "flags.h"
#include "params.h"
#include "input.h"
#include "tree-mudflap.h"
#include "tree-flow.h"
#include "function.h"
-#include "ggc.h"
#include "tree-flow.h"
-#include "diagnostic.h"
+#include "tree-pretty-print.h"
#include "except.h"
#include "debug.h"
#include "pointer-set.h"
#include "target.h"
#include "integrate.h"
+#include "rtl.h" /* FIXME: For asm_str_count. */
+
/* I'm not real happy about this, but we need to handle gimple and
non-gimple trees. */
#include "gimple.h"
calls? */
-/* Weights that estimate_num_insns uses for heuristics in inlining. */
-
-eni_weights eni_inlining_weights;
-
/* Weights that estimate_num_insns uses to estimate the size of the
produced code. */
&& (TREE_CODE (SSA_NAME_VAR (name)) != RESULT_DECL
|| !id->transform_return_to_modify))
{
+ struct ptr_info_def *pi;
new_tree = make_ssa_name (new_tree, NULL);
insert_decl_map (id, name, new_tree);
SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_tree)
= SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name);
TREE_TYPE (new_tree) = TREE_TYPE (SSA_NAME_VAR (new_tree));
+ /* At least IPA points-to info can be directly transferred. */
+ if (id->src_cfun->gimple_df
+ && id->src_cfun->gimple_df->ipa_pta
+ && (pi = SSA_NAME_PTR_INFO (name))
+ && !pi->pt.anything)
+ {
+ struct ptr_info_def *new_pi = get_ptr_info (new_tree);
+ new_pi->pt = pi->pt;
+ }
if (gimple_nop_p (SSA_NAME_DEF_STMT (name)))
{
/* By inlining function having uninitialized variable, we might
gcc_assert (DECL_P (new_var));
TREE_CHAIN (new_var) = new_decls;
new_decls = new_var;
+
+ /* Also copy value-expressions. */
+ if (TREE_CODE (new_var) == VAR_DECL
+ && DECL_HAS_VALUE_EXPR_P (new_var))
+ {
+ tree tem = DECL_VALUE_EXPR (new_var);
+ bool old_regimplify = id->regimplify;
+ id->remapping_type_depth++;
+ walk_tree (&tem, copy_tree_body_r, id, NULL);
+ id->remapping_type_depth--;
+ id->regimplify = old_regimplify;
+ SET_DECL_VALUE_EXPR (new_var, tem);
+ }
}
}
/* Create a new gimple_seq by remapping all the statements in BODY
using the inlining information in ID. */
-gimple_seq
+static gimple_seq
remap_gimple_seq (gimple_seq body, copy_body_data *id)
{
gimple_stmt_iterator si;
tree *n;
n = (tree *) pointer_map_contains (id->decl_map,
TREE_BLOCK (*tp));
- gcc_assert (n);
- new_block = *n;
+ gcc_assert (n || id->remapping_type_depth != 0);
+ if (n)
+ new_block = *n;
}
TREE_BLOCK (*tp) = new_block;
}
default:
break;
}
+
+ /* Reset alias info if we didn't apply measures to
+ 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);
}
break;
basic_block copy_basic_block;
tree decl;
gcov_type freq;
+ basic_block prev;
+
+ /* Search for previous copied basic block. */
+ prev = bb->prev_bb;
+ while (!prev->aux)
+ prev = prev->prev_bb;
/* create_basic_block() will append every new block to
basic_block_info automatically. */
copy_basic_block = create_basic_block (NULL, (void *) 0,
- (basic_block) bb->prev_bb->aux);
+ (basic_block) prev->aux);
copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE;
/* We are going to rebuild frequencies from scratch. These values
bb->frequency,
copy_basic_block->frequency);
}
+ stmt = cgraph_redirect_edge_call_stmt_to_callee (edge);
}
break;
/* Constant propagation on argument done during inlining
may create new direct call. Produce an edge for it. */
if ((!edge
- || (edge->indirect_call
+ || (edge->indirect_inlining_edge
&& id->transform_call_graph_edges == CB_CGE_MOVE_CLONES))
- && is_gimple_call (stmt)
&& (fn = gimple_call_fndecl (stmt)) != NULL)
{
struct cgraph_node *dest = cgraph_node (fn);
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
|| !id->src_node->analyzed);
if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)
cgraph_create_edge_including_clones
= CIF_ORIGINALLY_INDIRECT_CALL;
if (dump_file)
{
- fprintf (dump_file, "Created new direct edge to %s",
+ fprintf (dump_file, "Created new direct edge to %s\n",
cgraph_node_name (dest));
}
}
/* Copy edges from BB into its copy constructed earlier, scale profile
accordingly. Edges will be taken care of later. Assume aux
- pointers to point to the copies of each BB. */
+ pointers to point to the copies of each BB. Return true if any
+ debug stmts are left after a statement that must end the basic block. */
-static void
+static bool
copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb)
{
basic_block new_bb = (basic_block) bb->aux;
edge old_edge;
gimple_stmt_iterator si;
int flags;
+ bool need_debug_cleanup = false;
/* Use the indices from the original blocks to create edges for the
new ones. */
}
if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK)
- return;
+ return false;
for (si = gsi_start_bb (new_bb); !gsi_end_p (si);)
{
if (can_throw || nonlocal_goto)
{
if (!gsi_end_p (si))
+ {
+ while (!gsi_end_p (si) && is_gimple_debug (gsi_stmt (si)))
+ gsi_next (&si);
+ if (gsi_end_p (si))
+ need_debug_cleanup = true;
+ }
+ if (!gsi_end_p (si))
/* Note that bb's predecessor edges aren't necessarily
right at this point; split_block doesn't care. */
{
update_ssa_across_abnormal_edges (gimple_bb (copy_stmt), ret_bb,
can_throw, nonlocal_goto);
}
+ return need_debug_cleanup;
}
/* Copy the PHIs. All blocks and edges are copied, some blocks
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;
- cfun->function_frequency = src_cfun->function_frequency;
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;
cfun->returns_struct = src_cfun->returns_struct;
cfun->returns_pcc_struct = src_cfun->returns_pcc_struct;
cfun->after_tree_profile = src_cfun->after_tree_profile;
pop_cfun ();
}
+/* Helper function for copy_cfg_body. Move debug stmts from the end
+ of NEW_BB to the beginning of successor basic blocks when needed. If the
+ successor has multiple predecessors, reset them, otherwise keep
+ their value. */
+
+static void
+maybe_move_debug_stmts_to_successors (copy_body_data *id, basic_block new_bb)
+{
+ edge e;
+ edge_iterator ei;
+ gimple_stmt_iterator si = gsi_last_nondebug_bb (new_bb);
+
+ if (gsi_end_p (si)
+ || gsi_one_before_end_p (si)
+ || !(stmt_can_throw_internal (gsi_stmt (si))
+ || stmt_can_make_abnormal_goto (gsi_stmt (si))))
+ return;
+
+ FOR_EACH_EDGE (e, ei, new_bb->succs)
+ {
+ gimple_stmt_iterator ssi = gsi_last_bb (new_bb);
+ gimple_stmt_iterator dsi = gsi_after_labels (e->dest);
+ while (is_gimple_debug (gsi_stmt (ssi)))
+ {
+ gimple stmt = gsi_stmt (ssi), new_stmt;
+ tree var;
+ tree value;
+
+ /* For the last edge move the debug stmts instead of copying
+ them. */
+ if (ei_one_before_end_p (ei))
+ {
+ si = ssi;
+ gsi_prev (&ssi);
+ if (!single_pred_p (e->dest))
+ 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))
+ {
+ value = gimple_debug_bind_get_value (stmt);
+ value = unshare_expr (value);
+ }
+ else
+ value = NULL_TREE;
+ new_stmt = gimple_build_debug_bind (var, value, stmt);
+ gsi_insert_before (&dsi, new_stmt, GSI_SAME_STMT);
+ VEC_safe_push (gimple, heap, id->debug_stmts, new_stmt);
+ gsi_prev (&ssi);
+ }
+ }
+}
+
/* Make a copy of the body of FN so that it can be inserted inline in
another function. Walks FN via CFG, returns new fndecl. */
static tree
copy_cfg_body (copy_body_data * id, gcov_type count, int frequency_scale,
- basic_block entry_block_map, basic_block exit_block_map)
+ basic_block entry_block_map, basic_block exit_block_map,
+ bitmap blocks_to_copy, basic_block new_entry)
{
tree callee_fndecl = id->src_fn;
/* Original cfun for the callee, doesn't change. */
struct function *cfun_to_copy;
basic_block bb;
tree new_fndecl = NULL;
+ bool need_debug_cleanup = false;
gcov_type count_scale;
int last;
/* Use aux pointers to map the original blocks to copy. */
FOR_EACH_BB_FN (bb, cfun_to_copy)
- {
- basic_block new_bb = copy_bb (id, bb, frequency_scale, count_scale);
- bb->aux = new_bb;
- new_bb->aux = bb;
- }
+ if (!blocks_to_copy || bitmap_bit_p (blocks_to_copy, bb->index))
+ {
+ basic_block new_bb = copy_bb (id, bb, frequency_scale, count_scale);
+ bb->aux = new_bb;
+ new_bb->aux = bb;
+ }
last = last_basic_block;
/* Now that we've duplicated the blocks, duplicate their edges. */
FOR_ALL_BB_FN (bb, cfun_to_copy)
- copy_edges_for_bb (bb, count_scale, exit_block_map);
+ if (!blocks_to_copy
+ || (bb->index > 0 && bitmap_bit_p (blocks_to_copy, bb->index)))
+ need_debug_cleanup |= copy_edges_for_bb (bb, count_scale, exit_block_map);
if (gimple_in_ssa_p (cfun))
FOR_ALL_BB_FN (bb, cfun_to_copy)
- copy_phis_for_bb (bb, id);
+ if (!blocks_to_copy
+ || (bb->index > 0 && bitmap_bit_p (blocks_to_copy, bb->index)))
+ copy_phis_for_bb (bb, id);
- FOR_ALL_BB_FN (bb, cfun_to_copy)
+ if (new_entry)
{
- ((basic_block)bb->aux)->aux = NULL;
- bb->aux = NULL;
+ edge e;
+ e = make_edge (entry_block_map, (basic_block)new_entry->aux, EDGE_FALLTHRU);
+ e->probability = REG_BR_PROB_BASE;
+ e->count = entry_block_map->count;
}
+ FOR_ALL_BB_FN (bb, cfun_to_copy)
+ if (bb->aux)
+ {
+ if (need_debug_cleanup
+ && bb->index != ENTRY_BLOCK
+ && bb->index != EXIT_BLOCK)
+ maybe_move_debug_stmts_to_successors (id, (basic_block) bb->aux);
+ ((basic_block)bb->aux)->aux = NULL;
+ bb->aux = NULL;
+ }
+
/* Zero out AUX fields of newly created block during EH edge
insertion. */
for (; last < last_basic_block; last++)
- BASIC_BLOCK (last)->aux = NULL;
+ {
+ if (need_debug_cleanup)
+ maybe_move_debug_stmts_to_successors (id, BASIC_BLOCK (last));
+ BASIC_BLOCK (last)->aux = NULL;
+ }
entry_block_map->aux = NULL;
exit_block_map->aux = NULL;
static tree
copy_body (copy_body_data *id, gcov_type count, int frequency_scale,
- basic_block entry_block_map, basic_block exit_block_map)
+ basic_block entry_block_map, basic_block exit_block_map,
+ bitmap blocks_to_copy, basic_block new_entry)
{
tree fndecl = id->src_fn;
tree body;
/* If this body has a CFG, walk CFG and copy. */
gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fndecl)));
- body = copy_cfg_body (id, count, frequency_scale, entry_block_map, exit_block_map);
+ body = copy_cfg_body (id, count, frequency_scale, entry_block_map, exit_block_map,
+ blocks_to_copy, new_entry);
copy_debug_stmts (id);
return body;
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. */
tree decl = gimple_call_fndecl (stmt);
tree addr = gimple_call_fn (stmt);
tree funtype = TREE_TYPE (addr);
+ bool stdarg = false;
if (POINTER_TYPE_P (funtype))
funtype = TREE_TYPE (funtype);
if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (decl))
{
+ /* Builtins that expand to constants. */
case BUILT_IN_CONSTANT_P:
- return 0;
case BUILT_IN_EXPECT:
- return 0;
-
- /* Prefetch instruction is not expensive. */
- case BUILT_IN_PREFETCH:
- cost = weights->target_builtin_call_cost;
- break;
-
+ case BUILT_IN_OBJECT_SIZE:
+ case BUILT_IN_UNREACHABLE:
+ /* Simple register moves or loads from stack. */
+ case BUILT_IN_RETURN_ADDRESS:
+ case BUILT_IN_EXTRACT_RETURN_ADDR:
+ case BUILT_IN_FROB_RETURN_ADDR:
+ case BUILT_IN_RETURN:
+ case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
+ case BUILT_IN_FRAME_ADDRESS:
+ case BUILT_IN_VA_END:
+ case BUILT_IN_STACK_SAVE:
+ case BUILT_IN_STACK_RESTORE:
/* Exception state returns or moves registers around. */
case BUILT_IN_EH_FILTER:
case BUILT_IN_EH_POINTER:
case BUILT_IN_EH_COPY_VALUES:
return 0;
+ /* builtins that are not expensive (that is they are most probably
+ expanded inline into resonably simple code). */
+ case BUILT_IN_ABS:
+ case BUILT_IN_ALLOCA:
+ case BUILT_IN_BSWAP32:
+ case BUILT_IN_BSWAP64:
+ case BUILT_IN_CLZ:
+ case BUILT_IN_CLZIMAX:
+ case BUILT_IN_CLZL:
+ case BUILT_IN_CLZLL:
+ case BUILT_IN_CTZ:
+ case BUILT_IN_CTZIMAX:
+ case BUILT_IN_CTZL:
+ case BUILT_IN_CTZLL:
+ case BUILT_IN_FFS:
+ case BUILT_IN_FFSIMAX:
+ case BUILT_IN_FFSL:
+ case BUILT_IN_FFSLL:
+ case BUILT_IN_IMAXABS:
+ case BUILT_IN_FINITE:
+ case BUILT_IN_FINITEF:
+ case BUILT_IN_FINITEL:
+ case BUILT_IN_FINITED32:
+ case BUILT_IN_FINITED64:
+ case BUILT_IN_FINITED128:
+ case BUILT_IN_FPCLASSIFY:
+ case BUILT_IN_ISFINITE:
+ case BUILT_IN_ISINF_SIGN:
+ case BUILT_IN_ISINF:
+ case BUILT_IN_ISINFF:
+ case BUILT_IN_ISINFL:
+ case BUILT_IN_ISINFD32:
+ case BUILT_IN_ISINFD64:
+ case BUILT_IN_ISINFD128:
+ case BUILT_IN_ISNAN:
+ case BUILT_IN_ISNANF:
+ case BUILT_IN_ISNANL:
+ case BUILT_IN_ISNAND32:
+ case BUILT_IN_ISNAND64:
+ case BUILT_IN_ISNAND128:
+ case BUILT_IN_ISNORMAL:
+ case BUILT_IN_ISGREATER:
+ case BUILT_IN_ISGREATEREQUAL:
+ case BUILT_IN_ISLESS:
+ case BUILT_IN_ISLESSEQUAL:
+ case BUILT_IN_ISLESSGREATER:
+ case BUILT_IN_ISUNORDERED:
+ case BUILT_IN_VA_ARG_PACK:
+ case BUILT_IN_VA_ARG_PACK_LEN:
+ case BUILT_IN_VA_COPY:
+ case BUILT_IN_TRAP:
+ case BUILT_IN_SAVEREGS:
+ case BUILT_IN_POPCOUNTL:
+ case BUILT_IN_POPCOUNTLL:
+ case BUILT_IN_POPCOUNTIMAX:
+ case BUILT_IN_POPCOUNT:
+ case BUILT_IN_PARITYL:
+ case BUILT_IN_PARITYLL:
+ case BUILT_IN_PARITYIMAX:
+ case BUILT_IN_PARITY:
+ case BUILT_IN_LABS:
+ case BUILT_IN_LLABS:
+ case BUILT_IN_PREFETCH:
+ cost = weights->target_builtin_call_cost;
+ break;
+
default:
break;
}
if (!VOID_TYPE_P (TREE_TYPE (funtype)))
cost += estimate_move_cost (TREE_TYPE (funtype));
+
+ if (funtype)
+ stdarg = stdarg_p (funtype);
+
/* Our cost must be kept in sync with
cgraph_estimate_size_after_inlining that does use function
- declaration to figure out the arguments. */
- if (decl && DECL_ARGUMENTS (decl))
+ declaration to figure out the arguments.
+
+ For functions taking variable list of arguments we must
+ look into call statement intself. This is safe because
+ we will get only higher costs and in most cases we will
+ not inline these anyway. */
+ if (decl && DECL_ARGUMENTS (decl) && !stdarg)
{
tree arg;
for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
if (!VOID_TYPE_P (TREE_TYPE (arg)))
cost += estimate_move_cost (TREE_TYPE (arg));
}
- else if (funtype && prototype_p (funtype))
+ else if (funtype && prototype_p (funtype) && !stdarg)
{
tree t;
for (t = TYPE_ARG_TYPES (funtype); t && t != void_list_node;
struct cgraph_edge *cs;
cs = cgraph_edge (node, stmt);
- if (cs)
+ if (cs && !cs->indirect_unknown_callee)
return cs->callee->decl;
return NULL_TREE;
cg_edge = cgraph_edge (id->dst_node, stmt);
- /* Don't inline functions with different EH personalities. */
- if (DECL_FUNCTION_PERSONALITY (cg_edge->caller->decl)
- && DECL_FUNCTION_PERSONALITY (cg_edge->callee->decl)
- && (DECL_FUNCTION_PERSONALITY (cg_edge->caller->decl)
- != DECL_FUNCTION_PERSONALITY (cg_edge->callee->decl)))
+ /* 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. */
+ /* Don't try to inline functions that are not well-suited to inlining. */
if (!cgraph_inline_p (cg_edge, &reason))
{
/* 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. */
- if (cg_edge->indirect_call)
+ if (cg_edge->indirect_inlining_edge)
goto egress;
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
&& cgraph_global_info_ready)
{
sorry ("inlining failed in call to %q+F: %s", fn,
- cgraph_inline_failed_string (reason));
+ _(cgraph_inline_failed_string (reason)));
sorry ("called from here");
}
else if (warn_inline && DECL_DECLARED_INLINE_P (fn)
&& cgraph_global_info_ready)
{
warning (OPT_Winline, "inlining failed in call to %q+F: %s",
- fn, cgraph_inline_failed_string (reason));
+ fn, _(cgraph_inline_failed_string (reason)));
warning (OPT_Winline, "called from here");
}
goto egress;
duplicate our body before altering anything. */
copy_body (id, bb->count,
cg_edge->frequency * REG_BR_PROB_BASE / CGRAPH_FREQ_BASE,
- bb, return_block);
+ bb, return_block, NULL, NULL);
- /* Reset the escaped and callused solutions. */
+ /* Reset the escaped solution. */
if (cfun->gimple_df)
- {
- pt_solution_reset (&cfun->gimple_df->escaped);
- pt_solution_reset (&cfun->gimple_df->callused);
- }
+ pt_solution_reset (&cfun->gimple_df->escaped);
/* Clean up. */
if (id->debug_map)
/* There is no point in performing inlining if errors have already
occurred -- and we might crash if we try to inline invalid
code. */
- if (errorcount || sorrycount)
+ if (seen_error ())
return 0;
/* Clear out ID. */
return NULL;
}
-bool
+DEBUG_FUNCTION bool
debug_find_tree (tree top, tree search)
{
return walk_tree_without_duplicates (&top, debug_find_tree_1, search) != 0;
/* The new variable/label has no RTL, yet. */
if (CODE_CONTAINS_STRUCT (TREE_CODE (copy), TS_DECL_WRTL)
&& !TREE_STATIC (copy) && !DECL_EXTERNAL (copy))
- SET_DECL_RTL (copy, NULL_RTX);
+ SET_DECL_RTL (copy, 0);
/* These args would always appear unused, if not for this. */
TREE_USED (copy) = 1;
copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
VAR_DECL, DECL_NAME (decl), type);
+ if (DECL_PT_UID_SET_P (decl))
+ SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
TREE_READONLY (copy) = TREE_READONLY (decl);
TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn),
VAR_DECL, DECL_NAME (decl), type);
+ if (DECL_PT_UID_SET_P (decl))
+ SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
TREE_READONLY (copy) = TREE_READONLY (decl);
TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
if (!DECL_BY_REFERENCE (decl))
tree with another tree while duplicating the function's
body, TREE_MAP represents the mapping between these
trees. If UPDATE_CLONES is set, the call_stmt fields
- of edges of clones of the function will be updated. */
+ of edges of clones of the function will be updated.
+
+ If non-NULL ARGS_TO_SKIP determine function parameters to remove
+ from new version.
+ If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
+ If non_NULL NEW_ENTRY determine new entry BB of the clone.
+*/
void
tree_function_versioning (tree old_decl, tree new_decl,
VEC(ipa_replace_map_p,gc)* tree_map,
- bool update_clones, bitmap args_to_skip)
+ bool update_clones, bitmap args_to_skip,
+ bitmap blocks_to_copy, basic_block new_entry)
{
struct cgraph_node *old_version_node;
struct cgraph_node *new_version_node;
(DECL_STRUCT_FUNCTION (old_decl));
initialize_cfun (new_decl, old_decl,
old_entry_block->count);
+ DECL_STRUCT_FUNCTION (new_decl)->gimple_df->ipa_pta
+ = id.src_cfun->gimple_df->ipa_pta;
push_cfun (DECL_STRUCT_FUNCTION (new_decl));
/* Copy the function's static chain. */
if (replace_info->replace_p)
{
tree op = replace_info->new_tree;
+ if (!replace_info->old_tree)
+ {
+ int i = replace_info->parm_num;
+ tree parm;
+ for (parm = DECL_ARGUMENTS (old_decl); i; parm = TREE_CHAIN (parm))
+ i --;
+ replace_info->old_tree = parm;
+ }
+
STRIP_NOPS (op);
/* Copy the Function's body. */
copy_body (&id, old_entry_block->count, REG_BR_PROB_BASE,
- ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR);
+ ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, blocks_to_copy, new_entry);
if (DECL_RESULT (old_decl) != NULL_TREE)
{
pointer_set_destroy (id.statements_to_fold);
fold_cond_expr_cond ();
delete_unreachable_blocks_update_callgraph (&id);
+ if (id.dst_node->analyzed)
+ cgraph_rebuild_references ();
update_ssa (TODO_update_ssa);
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);
return false;
}
#endif
- tree caller, callee;
+ tree caller, callee, lhs;
caller = e->caller->decl;
callee = e->callee->decl;
- /* We cannot inline a function that uses a different EH personality
- than the caller. */
- if (DECL_FUNCTION_PERSONALITY (caller)
- && DECL_FUNCTION_PERSONALITY (callee)
- && (DECL_FUNCTION_PERSONALITY (caller)
- != DECL_FUNCTION_PERSONALITY (callee)))
+ /* First check that inlining isn't simply forbidden in this case. */
+ if (inline_forbidden_into_p (caller, callee))
{
e->inline_failed = CIF_UNSPECIFIED;
gimple_call_set_cannot_inline (e->call_stmt, true);
return false;
}
+ /* Do not inline calls where we cannot triviall work around mismatches
+ in argument or return types. */
if (e->call_stmt
- && !gimple_check_call_args (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;
gimple_call_set_cannot_inline (e->call_stmt, true);