X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Ftree-inline.c;h=72bef2189da816188d360f67cb4982f7b744c26e;hb=72350d7baa0fbcf2fcc4aa82bc7d228a7f38734d;hp=59661a7d5096cf3cf5d470babd0a22ae7ab1a417;hpb=33d8f01921475f1c755c4d98b00e054c5765494d;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 59661a7d509..72bef2189da 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -26,8 +26,6 @@ along with GCC; see the file COPYING3. If not see #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" @@ -42,7 +40,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-flow.h" #include "function.h" #include "tree-flow.h" -#include "diagnostic.h" +#include "tree-pretty-print.h" #include "except.h" #include "debug.h" #include "pointer-set.h" @@ -52,6 +50,8 @@ along with GCC; see the file COPYING3. If not see #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" @@ -101,10 +101,6 @@ along with GCC; see the file COPYING3. If not see 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. */ @@ -578,6 +574,19 @@ remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id) 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); + } } } @@ -678,7 +687,7 @@ copy_bind_expr (tree *tp, int *walk_subtrees, copy_body_data *id) /* 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; @@ -1106,8 +1115,9 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) 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; } @@ -1478,11 +1488,17 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, 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 @@ -1688,9 +1704,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, /* 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); @@ -1702,6 +1717,7 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, 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 @@ -1718,7 +1734,7 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, = 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)); } } @@ -1824,9 +1840,10 @@ update_ssa_across_abnormal_edges (basic_block bb, basic_block ret_bb, /* 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; @@ -1834,6 +1851,7 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) 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. */ @@ -1854,7 +1872,7 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) } if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK) - return; + return false; for (si = gsi_start_bb (new_bb); !gsi_end_p (si);) { @@ -1889,6 +1907,13 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) 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. */ { @@ -1913,6 +1938,7 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) 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 @@ -2014,11 +2040,12 @@ initialize_cfun (tree new_fndecl, tree callee_fndecl, gcov_type count) 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; @@ -2048,12 +2075,70 @@ initialize_cfun (tree new_fndecl, tree callee_fndecl, gcov_type count) 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. */ @@ -2061,6 +2146,7 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency_scale, struct function *cfun_to_copy; basic_block bb; tree new_fndecl = NULL; + bool need_debug_cleanup = false; gcov_type count_scale; int last; @@ -2091,32 +2177,54 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency_scale, /* 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; @@ -2230,14 +2338,16 @@ copy_tree_body (copy_body_data *id) 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; @@ -2951,6 +3061,29 @@ inline_forbidden_p (tree fndecl) 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. */ @@ -3257,6 +3390,7 @@ estimate_num_insns (gimple stmt, eni_weights *weights) 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); @@ -3269,22 +3403,93 @@ estimate_num_insns (gimple stmt, eni_weights *weights) 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; } @@ -3294,17 +3499,26 @@ estimate_num_insns (gimple stmt, eni_weights *weights) 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; @@ -3473,7 +3687,7 @@ get_indirect_callee_fndecl (struct cgraph_node *node, gimple stmt) struct cgraph_edge *cs; cs = cgraph_edge (node, stmt); - if (cs) + if (cs && !cs->indirect_unknown_callee) return cs->callee->decl; return NULL_TREE; @@ -3542,21 +3756,17 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) 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)) @@ -3564,7 +3774,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) && 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) @@ -3575,7 +3785,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) && 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; @@ -3737,7 +3947,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) 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 solution. */ if (cfun->gimple_df) @@ -3959,7 +4169,7 @@ optimize_inline_calls (tree fn) /* 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. */ @@ -4443,7 +4653,7 @@ debug_find_tree_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data) 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; @@ -4487,7 +4697,7 @@ copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy) /* 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; @@ -4770,11 +4980,18 @@ update_clone_info (copy_body_data * id) 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; @@ -4845,6 +5062,8 @@ tree_function_versioning (tree old_decl, tree new_decl, (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. */ @@ -4863,6 +5082,15 @@ tree_function_versioning (tree old_decl, tree new_decl, 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); @@ -4915,7 +5143,7 @@ tree_function_versioning (tree old_decl, tree new_decl, /* 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) { @@ -4956,6 +5184,8 @@ tree_function_versioning (tree old_decl, tree new_decl, 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); @@ -5087,12 +5317,8 @@ tree_can_inline_p (struct cgraph_edge *e) 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);