From 110cfe1cdcd721d4d6936bb8a922a3c4b129abbe Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Fri, 29 Dec 2006 12:10:31 +0100 Subject: [PATCH] tree-inline.c (remap_ssa_name): New function. * tree-inline.c (remap_ssa_name): New function. (remap_decl): Update SSA datastructures for DECLs. (copy_body_r): Deal with SSA_NAMEs; add referenced global vars. (copy_bb): Set SSA_NAME def stmts. (update_ssa_acorss_eh_edges): New function. (copy_edge_for_bb): Call it; mark new vars for renaming. (copy_phis_for_bb): New function. (initialize_cfun): Break out from ... (copy_cfg_body): ... here; maintain AUX map for both directions; call SSA updating workers; do not produce copy of cfun to be copied. (setup_one_parameter): Do propagation across SSA form. (declare_return_variable): Work on SSA; use return_slot instead of address of return slot of argument to avoid folding back and forth. (expand_call_inline): Update SSA from on return values. (optimize_inline_calls): Do sanity checking, dead blocks removal, update SSA form. (tree_function_verioning): Update initialize_cfun. From-SVN: r120260 --- gcc/ChangeLog | 20 ++ gcc/tree-inline.c | 647 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 524 insertions(+), 143 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 60d76370502..f4c9b9c14ea 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2006-12-29 Jan Hubicka + + * tree-inline.c (remap_ssa_name): New function. + (remap_decl): Update SSA datastructures for DECLs. + (copy_body_r): Deal with SSA_NAMEs; add referenced global vars. + (copy_bb): Set SSA_NAME def stmts. + (update_ssa_acorss_eh_edges): New function. + (copy_edge_for_bb): Call it; mark new vars for renaming. + (copy_phis_for_bb): New function. + (initialize_cfun): Break out from ... + (copy_cfg_body): ... here; maintain AUX map for both directions; + call SSA updating workers; do not produce copy of cfun to be copied. + (setup_one_parameter): Do propagation across SSA form. + (declare_return_variable): Work on SSA; use return_slot instead of + address of return slot of argument to avoid folding back and forth. + (expand_call_inline): Update SSA from on return values. + (optimize_inline_calls): Do sanity checking, dead blocks removal, + update SSA form. + (tree_function_verioning): Update initialize_cfun. + 2006-12-29 Marcin Dalecki * doc/invoke.texi: Replace no longer supported -fno-strength-reduce diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 39d9f11cc7d..8843eb27fde 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -50,6 +50,7 @@ Boston, MA 02110-1301, USA. */ #include "pointer-set.h" #include "ipa-prop.h" #include "value-prof.h" +#include "tree-pass.h" /* I'm not real happy about this, but we need to handle gimple and non-gimple trees. */ @@ -141,6 +142,50 @@ insert_decl_map (copy_body_data *id, tree key, tree value) (splay_tree_value) value); } +/* Construct new SSA name for old NAME. ID is the inline context. */ + +static tree +remap_ssa_name (tree name, copy_body_data *id) +{ + tree new; + splay_tree_node n; + + gcc_assert (TREE_CODE (name) == SSA_NAME); + + n = splay_tree_lookup (id->decl_map, (splay_tree_key) name); + if (n) + return (tree) n->value; + + /* Do not set DEF_STMT yet as statement is not copied yet. We do that + in copy_bb. */ + new = remap_decl (SSA_NAME_VAR (name), id); + /* We might've substituted constant or another SSA_NAME for + the variable. + + Replace the SSA name representing RESULT_DECL by variable during + inlining: this saves us from need to introduce PHI node in a case + return value is just partly initialized. */ + if ((TREE_CODE (new) == VAR_DECL || TREE_CODE (new) == PARM_DECL) + && (TREE_CODE (SSA_NAME_VAR (name)) != RESULT_DECL + || !id->transform_return_to_modify)) + { + new = make_ssa_name (new, NULL); + insert_decl_map (id, name, new); + if (IS_EMPTY_STMT (SSA_NAME_DEF_STMT (name))) + { + SSA_NAME_DEF_STMT (new) = build_empty_stmt (); + if (gimple_default_def (id->src_cfun, SSA_NAME_VAR (name)) == name) + set_default_def (SSA_NAME_VAR (new), new); + } + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new) + = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (name); + TREE_TYPE (new) = TREE_TYPE (SSA_NAME_VAR (new)); + } + else + insert_decl_map (id, name, new); + return new; +} + /* Remap DECL during the copying of the BLOCK tree for the function. */ tree @@ -188,6 +233,22 @@ remap_decl (tree decl, copy_body_data *id) walk_tree (&DECL_QUALIFIER (t), copy_body_r, id, NULL); } + if (cfun && gimple_in_ssa_p (cfun) + && (TREE_CODE (t) == VAR_DECL + || TREE_CODE (t) == RESULT_DECL || TREE_CODE (t) == PARM_DECL)) + { + tree def = gimple_default_def (id->src_cfun, decl); + get_var_ann (t); + if (TREE_CODE (decl) != PARM_DECL && def) + { + tree map = remap_ssa_name (def, id); + /* Watch out RESULT_DECLs whose SSA names map directly + to them. */ + if (TREE_CODE (map) == SSA_NAME) + set_default_def (t, map); + } + add_referenced_var (t); + } return t; } @@ -500,6 +561,12 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) return (tree) (void *)1; } } + else if (TREE_CODE (*tp) == SSA_NAME) + { + *tp = remap_ssa_name (*tp, id); + *walk_subtrees = 0; + return NULL; + } /* Local variables and labels need to be replaced by equivalent variables. We don't want to copy static variables; there's only @@ -621,6 +688,11 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) /* Here is the "usual case". Copy this tree node, and then tweak some special cases. */ copy_tree_r (tp, walk_subtrees, NULL); + + /* Global variables we didn't seen yet needs to go into referenced + vars. */ + if (gimple_in_ssa_p (cfun) && TREE_CODE (*tp) == VAR_DECL) + add_referenced_var (*tp); /* If EXPR has block defined, map it to newly constructed block. When inlining we want EXPRs without block appear in the block @@ -718,74 +790,132 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, int count_scal gimplify_stmt (&stmt); bsi_insert_after (©_bsi, stmt, BSI_NEW_STMT); - call = get_call_expr_in (stmt); - /* We're duplicating a CALL_EXPR. Find any corresponding - callgraph edges and update or duplicate them. */ - if (call && (decl = get_callee_fndecl (call))) + + /* Process new statement. gimplify_stmt possibly turned statement + into multiple statements, we need to process all of them. */ + while (!bsi_end_p (copy_bsi)) { - struct cgraph_node *node; - struct cgraph_edge *edge; - - switch (id->transform_call_graph_edges) + stmt = bsi_stmt (copy_bsi); + call = get_call_expr_in (stmt); + /* We're duplicating a CALL_EXPR. Find any corresponding + callgraph edges and update or duplicate them. */ + if (call && (decl = get_callee_fndecl (call))) { - case CB_CGE_DUPLICATE: - edge = cgraph_edge (id->src_node, orig_stmt); - if (edge) - cgraph_clone_edge (edge, id->dst_node, stmt, - REG_BR_PROB_BASE, 1, true); - break; - - case CB_CGE_MOVE_CLONES: - for (node = id->dst_node->next_clone; - node; - node = node->next_clone) + struct cgraph_node *node; + struct cgraph_edge *edge; + + switch (id->transform_call_graph_edges) { - edge = cgraph_edge (node, orig_stmt); - gcc_assert (edge); - cgraph_set_call_stmt (edge, stmt); + case CB_CGE_DUPLICATE: + edge = cgraph_edge (id->src_node, orig_stmt); + if (edge) + cgraph_clone_edge (edge, id->dst_node, stmt, + REG_BR_PROB_BASE, 1, true); + break; + + case CB_CGE_MOVE_CLONES: + for (node = id->dst_node->next_clone; + node; + node = node->next_clone) + { + edge = cgraph_edge (node, orig_stmt); + gcc_assert (edge); + cgraph_set_call_stmt (edge, stmt); + } + /* FALLTHRU */ + + case CB_CGE_MOVE: + edge = cgraph_edge (id->dst_node, orig_stmt); + if (edge) + cgraph_set_call_stmt (edge, stmt); + break; + + default: + gcc_unreachable (); } - /* FALLTHRU */ - - case CB_CGE_MOVE: - edge = cgraph_edge (id->dst_node, orig_stmt); - if (edge) - cgraph_set_call_stmt (edge, stmt); - break; - - default: - gcc_unreachable (); } - } - /* If you think we can abort here, you are wrong. - There is no region 0 in tree land. */ - gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) - != 0); + /* If you think we can abort here, you are wrong. + There is no region 0 in tree land. */ + gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) + != 0); - if (tree_could_throw_p (stmt)) - { - int region = lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt); - /* Add an entry for the copied tree in the EH hashtable. - When cloning or versioning, use the hashtable in - cfun, and just copy the EH number. When inlining, use the - hashtable in the caller, and adjust the region number. */ - if (region > 0) - add_stmt_to_eh_region (stmt, region + id->eh_region_offset); + if (tree_could_throw_p (stmt)) + { + int region = lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt); + /* Add an entry for the copied tree in the EH hashtable. + When cloning or versioning, use the hashtable in + cfun, and just copy the EH number. When inlining, use the + hashtable in the caller, and adjust the region number. */ + if (region > 0) + add_stmt_to_eh_region (stmt, region + id->eh_region_offset); - /* If this tree doesn't have a region associated with it, - and there is a "current region," - then associate this tree with the current region - and add edges associated with this region. */ - if ((lookup_stmt_eh_region_fn (id->src_cfun, - orig_stmt) <= 0 - && id->eh_region > 0) - && tree_could_throw_p (stmt)) - add_stmt_to_eh_region (stmt, id->eh_region); + /* If this tree doesn't have a region associated with it, + and there is a "current region," + then associate this tree with the current region + and add edges associated with this region. */ + if ((lookup_stmt_eh_region_fn (id->src_cfun, + orig_stmt) <= 0 + && id->eh_region > 0) + && tree_could_throw_p (stmt)) + add_stmt_to_eh_region (stmt, id->eh_region); + } + if (gimple_in_ssa_p (cfun)) + { + ssa_op_iter i; + tree def; + + find_new_referenced_vars (bsi_stmt_ptr (copy_bsi)); + FOR_EACH_SSA_TREE_OPERAND (def, stmt, i, SSA_OP_DEF) + if (TREE_CODE (def) == SSA_NAME) + SSA_NAME_DEF_STMT (def) = stmt; + } + bsi_next (©_bsi); } + copy_bsi = bsi_last (copy_basic_block); } } return copy_basic_block; } +/* Inserting Single Entry Multiple Exit region in SSA form into code in SSA + form is quite easy, since dominator relationship for old basic blocks does + not change. + + There is however exception where inlining might change dominator relation + across EH edges from basic block within inlined functions destinating + to landging pads in function we inline into. + + The function mark PHI_RESULT of such PHI nodes for renaming; it is + safe the EH edges are abnormal and SSA_NAME_OCCURS_IN_ABNORMAL_PHI + must be set. This means, that there will be no overlapping live ranges + for the underlying symbol. + + This might change in future if we allow redirecting of EH edges and + we might want to change way build CFG pre-inlining to include + all the possible edges then. */ +static void +update_ssa_across_eh_edges (basic_block bb) +{ + edge e; + edge_iterator ei; + + FOR_EACH_EDGE (e, ei, bb->succs) + if (!e->dest->aux + || ((basic_block)e->dest->aux)->index == ENTRY_BLOCK) + { + tree phi; + + gcc_assert (e->flags & EDGE_EH); + for (phi = phi_nodes (e->dest); phi; phi = PHI_CHAIN (phi)) + { + gcc_assert (SSA_NAME_OCCURS_IN_ABNORMAL_PHI + (PHI_RESULT (phi))); + mark_sym_for_renaming + (SSA_NAME_VAR (PHI_RESULT (phi))); + } + } +} + /* 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. */ @@ -825,6 +955,8 @@ copy_edges_for_bb (basic_block bb, int count_scale) copy_stmt = bsi_stmt (bsi); update_stmt (copy_stmt); + if (gimple_in_ssa_p (cfun)) + mark_symbols_for_renaming (copy_stmt); /* Do this before the possible split_block. */ bsi_next (&bsi); @@ -847,11 +979,54 @@ copy_edges_for_bb (basic_block bb, int count_scale) right at this point; split_block doesn't care. */ { edge e = split_block (new_bb, copy_stmt); + new_bb = e->dest; + new_bb->aux = e->src->aux; bsi = bsi_start (new_bb); } make_eh_edges (copy_stmt); + + if (gimple_in_ssa_p (cfun)) + update_ssa_across_eh_edges (bb_for_stmt (copy_stmt)); + } + } +} + +/* Copy the PHIs. All blocks and edges are copied, some blocks + was possibly split and new outgoing EH edges inserted. + BB points to the block of original function and AUX pointers links + the original and newly copied blocks. */ + +static void +copy_phis_for_bb (basic_block bb, copy_body_data *id) +{ + basic_block new_bb = bb->aux; + edge_iterator ei; + tree phi; + + for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) + { + tree res = PHI_RESULT (phi); + tree new_res = res; + tree new_phi; + edge new_edge; + + if (is_gimple_reg (res)) + { + walk_tree (&new_res, copy_body_r, id, NULL); + SSA_NAME_DEF_STMT (new_res) + = new_phi = create_phi_node (new_res, new_bb); + FOR_EACH_EDGE (new_edge, ei, new_bb->preds) + { + edge old_edge = find_edge (new_edge->src->aux, bb); + tree arg = PHI_ARG_DEF_FROM_EDGE (phi, old_edge); + tree new_arg = arg; + + walk_tree (&new_arg, copy_body_r, id, NULL); + gcc_assert (new_arg); + add_phi_arg (new_phi, new_arg, new_edge); + } } } } @@ -863,6 +1038,67 @@ remap_decl_1 (tree decl, void *data) return remap_decl (decl, (copy_body_data *) data); } +/* Build struct function and associated datastructures for the new clone + NEW_FNDECL to be build. CALLEE_FNDECL is the original */ + +static void +initialize_cfun (tree new_fndecl, tree callee_fndecl, gcov_type count, + int frequency) +{ + struct function *new_cfun + = (struct function *) ggc_alloc_cleared (sizeof (struct function)); + struct function *src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl); + int count_scale, frequency_scale; + + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count) + count_scale = (REG_BR_PROB_BASE * count + / ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count); + else + count_scale = 1; + + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency) + frequency_scale = (REG_BR_PROB_BASE * frequency + / + ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency); + else + frequency_scale = count_scale; + + /* Register specific tree functions. */ + tree_register_cfg_hooks (); + *new_cfun = *DECL_STRUCT_FUNCTION (callee_fndecl); + VALUE_HISTOGRAMS (new_cfun) = NULL; + new_cfun->unexpanded_var_list = NULL; + new_cfun->cfg = NULL; + new_cfun->decl = new_fndecl /*= copy_node (callee_fndecl)*/; + new_cfun->ib_boundaries_block = NULL; + DECL_STRUCT_FUNCTION (new_fndecl) = new_cfun; + push_cfun (new_cfun); + init_empty_tree_cfg (); + + ENTRY_BLOCK_PTR->count = + (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale / + REG_BR_PROB_BASE); + ENTRY_BLOCK_PTR->frequency = + (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency * + frequency_scale / REG_BR_PROB_BASE); + EXIT_BLOCK_PTR->count = + (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale / + REG_BR_PROB_BASE); + EXIT_BLOCK_PTR->frequency = + (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency * + frequency_scale / REG_BR_PROB_BASE); + if (src_cfun->eh) + init_eh_for_function (); + + if (src_cfun->gimple_df) + { + init_tree_ssa (); + cfun->gimple_df->in_ssa_p = true; + init_ssa_operands (); + } + pop_cfun (); +} + /* 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. */ @@ -873,15 +1109,11 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency, tree callee_fndecl = id->src_fn; /* Original cfun for the callee, doesn't change. */ struct function *src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl); - /* Copy, built by this function. */ - struct function *new_cfun; - /* Place to copy from; when a copy of the function was saved off earlier, - use that instead of the main copy. */ - struct function *cfun_to_copy = - (struct function *) ggc_alloc_cleared (sizeof (struct function)); + struct function *cfun_to_copy; basic_block bb; tree new_fndecl = NULL; int count_scale, frequency_scale; + int last; if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count) count_scale = (REG_BR_PROB_BASE * count @@ -903,64 +1135,47 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency, gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (callee_fndecl))); - *cfun_to_copy = *DECL_STRUCT_FUNCTION (callee_fndecl); + cfun_to_copy = id->src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl); - id->src_cfun = cfun_to_copy; - - /* If requested, create new basic_block_info and label_to_block_maps. - Otherwise, insert our new blocks and labels into the existing cfg. */ - if (id->transform_new_cfg) - { - new_cfun = - (struct function *) ggc_alloc_cleared (sizeof (struct function)); - *new_cfun = *DECL_STRUCT_FUNCTION (callee_fndecl); - new_cfun->cfg = NULL; - new_cfun->decl = new_fndecl = copy_node (callee_fndecl); - new_cfun->ib_boundaries_block = NULL; - DECL_STRUCT_FUNCTION (new_fndecl) = new_cfun; - push_cfun (new_cfun); - init_empty_tree_cfg (); - - ENTRY_BLOCK_PTR->count = - (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale / - REG_BR_PROB_BASE); - ENTRY_BLOCK_PTR->frequency = - (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency * - frequency_scale / REG_BR_PROB_BASE); - EXIT_BLOCK_PTR->count = - (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale / - REG_BR_PROB_BASE); - EXIT_BLOCK_PTR->frequency = - (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency * - frequency_scale / REG_BR_PROB_BASE); - - entry_block_map = ENTRY_BLOCK_PTR; - exit_block_map = EXIT_BLOCK_PTR; - } ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = entry_block_map; EXIT_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = exit_block_map; + entry_block_map->aux = ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy); + exit_block_map->aux = EXIT_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy); /* Duplicate any exception-handling regions. */ if (cfun->eh) { - if (id->transform_new_cfg) - init_eh_for_function (); id->eh_region_offset = duplicate_eh_regions (cfun_to_copy, remap_decl_1, id, 0, id->eh_region); } /* Use aux pointers to map the original blocks to copy. */ FOR_EACH_BB_FN (bb, cfun_to_copy) - bb->aux = copy_bb (id, bb, frequency_scale, count_scale); + { + basic_block new = copy_bb (id, bb, frequency_scale, count_scale); + bb->aux = new; + new->aux = bb; + } + + last = n_basic_blocks; /* 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); + if (gimple_in_ssa_p (cfun)) + FOR_ALL_BB_FN (bb, cfun_to_copy) + copy_phis_for_bb (bb, id); FOR_ALL_BB_FN (bb, cfun_to_copy) - bb->aux = NULL; - - if (id->transform_new_cfg) - pop_cfun (); + { + ((basic_block)bb->aux)->aux = NULL; + bb->aux = NULL; + } + /* Zero out AUX fields of newly created block during EH edge + insertion. */ + for (; last < n_basic_blocks; last++) + BASIC_BLOCK (last)->aux = NULL; + entry_block_map->aux = NULL; + exit_block_map->aux = NULL; return new_fndecl; } @@ -1017,13 +1232,17 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, tree init_stmt; tree var; tree var_sub; + tree rhs = value ? fold_convert (TREE_TYPE (p), value) : NULL; + tree def = (gimple_in_ssa_p (cfun) + ? gimple_default_def (id->src_cfun, p) : NULL); - /* If the parameter is never assigned to, we may not need to - create a new variable here at all. Instead, we may be able - to just use the argument value. */ + /* If the parameter is never assigned to, has no SSA_NAMEs created, + we may not need to create a new variable here at all. Instead, we may + be able to just use the argument value. */ if (TREE_READONLY (p) && !TREE_ADDRESSABLE (p) - && value && !TREE_SIDE_EFFECTS (value)) + && value && !TREE_SIDE_EFFECTS (value) + && !def) { /* We may produce non-gimple trees by adding NOPs or introduce invalid sharing when operand is not really constant. @@ -1047,6 +1266,11 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, here since the type of this decl must be visible to the calling function. */ var = copy_decl_to_var (p, id); + if (gimple_in_ssa_p (cfun) && TREE_CODE (var) == VAR_DECL) + { + get_var_ann (var); + add_referenced_var (var); + } /* See if the frontend wants to pass this by invisible reference. If so, our new VAR_DECL will have REFERENCE_TYPE, and we need to @@ -1085,21 +1309,54 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (p))) TREE_READONLY (var) = 0; + /* If there is no setup required and we are in SSA, take the easy route + replacing all SSA names representing the function parameter by the + SSA name passed to function. + + We need to construct map for the variable anyway as it might be used + in different SSA names when parameter is set in function. + + FIXME: This usually kills the last connection in between inlined + function parameter and the actual value in debug info. Can we do + better here? If we just inserted the statement, copy propagation + would kill it anyway as it always did in older versions of GCC. + + We might want to introduce a notion that single SSA_NAME might + represent multiple variables for purposes of debugging. */ + if (gimple_in_ssa_p (cfun) && rhs && def && is_gimple_reg (p) + && (TREE_CODE (rhs) == SSA_NAME + || is_gimple_min_invariant (rhs))) + { + insert_decl_map (id, def, rhs); + return; + } + /* Initialize this VAR_DECL from the equivalent argument. Convert the argument to the proper type in case it was promoted. */ if (value) { - tree rhs = fold_convert (TREE_TYPE (var), value); block_stmt_iterator bsi = bsi_last (bb); if (rhs == error_mark_node) - return; + { + insert_decl_map (id, p, var_sub); + return; + } STRIP_USELESS_TYPE_CONVERSION (rhs); /* We want to use GIMPLE_MODIFY_STMT, not INIT_EXPR here so that we keep our trees in gimple form. */ - init_stmt = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (var), var, rhs); + if (def && gimple_in_ssa_p (cfun) && is_gimple_reg (p)) + { + def = remap_ssa_name (def, id); + init_stmt = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (var), def, rhs); + SSA_NAME_DEF_STMT (def) = init_stmt; + SSA_NAME_IS_DEFAULT_DEF (def) = 0; + set_default_def (var, NULL); + } + else + init_stmt = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (var), var, rhs); /* If we did not create a gimple value and we did not create a gimple cast of a gimple value, then we will need to gimplify INIT_STMTS @@ -1110,12 +1367,29 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, && (!is_gimple_cast (rhs) || !is_gimple_val (TREE_OPERAND (rhs, 0)))) || !is_gimple_reg (var)) - gimplify_stmt (&init_stmt); + { + tree_stmt_iterator i; + + push_gimplify_context (); + gimplify_stmt (&init_stmt); + if (gimple_in_ssa_p (cfun) + && init_stmt && TREE_CODE (init_stmt) == STATEMENT_LIST) + { + /* The replacement can expose previously unreferenced + variables. */ + for (i = tsi_start (init_stmt); !tsi_end_p (i); tsi_next (&i)) + find_new_referenced_vars (tsi_stmt_ptr (i)); + } + pop_gimplify_context (NULL); + } /* If VAR represents a zero-sized variable, it's possible that the assignment statment may result in no gimple statements. */ if (init_stmt) bsi_insert_after (&bsi, init_stmt, BSI_NEW_STMT); + if (gimple_in_ssa_p (cfun)) + for (;!bsi_end_p (bsi); bsi_next (&bsi)) + mark_symbols_for_renaming (bsi_stmt (bsi)); } } @@ -1170,17 +1444,17 @@ initialize_inlined_parameters (copy_body_data *id, tree args, tree static_chain, The USE_STMT is filled to contain a use of the declaration to indicate the return value of the function. - RETURN_SLOT_ADDR, if non-null, was a fake parameter that - took the address of the result. MODIFY_DEST, if non-null, was the LHS of - the GIMPLE_MODIFY_STMT to which this call is the RHS. + RETURN_SLOT, if non-null is place where to store the result. It + is set only for CALL_EXPR_RETURN_SLOT_OPT. MODIFY_DEST, if non-null, + was the LHS of the GIMPLE_MODIFY_STMT to which this call is the RHS. The return value is a (possibly null) value that is the result of the function as seen by the callee. *USE_P 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_addr, - tree modify_dest, tree *use_p) +declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest, + tree *use_p) { tree callee = id->src_fn; tree caller = id->dst_fn; @@ -1199,15 +1473,49 @@ declare_return_variable (copy_body_data *id, tree return_slot_addr, /* If there was a return slot, then the return value is the dereferenced address of that object. */ - if (return_slot_addr) + if (return_slot) { - /* The front end shouldn't have used both return_slot_addr and + /* The front end shouldn't have used both return_slot and a modify expression. */ gcc_assert (!modify_dest); if (DECL_BY_REFERENCE (result)) - var = return_slot_addr; + { + tree return_slot_addr = build_fold_addr_expr (return_slot); + STRIP_USELESS_TYPE_CONVERSION (return_slot_addr); + + /* We are going to construct *&return_slot and we can't do that + for variables believed to be not addressable. + + FIXME: This check possibly can match, because values returned + via return slot optimization are not believed to have address + taken by alias analysis. */ + gcc_assert (TREE_CODE (return_slot) != SSA_NAME); + if (gimple_in_ssa_p (cfun)) + { + HOST_WIDE_INT bitsize; + HOST_WIDE_INT bitpos; + tree offset; + enum machine_mode mode; + int unsignedp; + int volatilep; + tree base; + base = get_inner_reference (return_slot, &bitsize, &bitpos, + &offset, + &mode, &unsignedp, &volatilep, + false); + if (TREE_CODE (base) == INDIRECT_REF) + base = TREE_OPERAND (base, 0); + if (TREE_CODE (base) == SSA_NAME) + base = SSA_NAME_VAR (base); + mark_sym_for_renaming (base); + } + var = return_slot_addr; + } else - var = build_fold_indirect_ref (return_slot_addr); + { + var = return_slot; + gcc_assert (TREE_CODE (var) != SSA_NAME); + } if ((TREE_CODE (TREE_TYPE (result)) == COMPLEX_TYPE || TREE_CODE (TREE_TYPE (result)) == VECTOR_TYPE) && !DECL_GIMPLE_REG_P (result) @@ -1221,7 +1529,8 @@ declare_return_variable (copy_body_data *id, tree return_slot_addr, gcc_assert (!TREE_ADDRESSABLE (callee_type)); /* Attempt to avoid creating a new temporary variable. */ - if (modify_dest) + if (modify_dest + && TREE_CODE (modify_dest) != SSA_NAME) { bool use_it = false; @@ -1270,6 +1579,11 @@ declare_return_variable (copy_body_data *id, tree return_slot_addr, 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)) + { + get_var_ann (var); + add_referenced_var (var); + } DECL_SEEN_IN_BIND_EXPR_P (var) = 1; DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list @@ -1938,7 +2252,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) tree fn; splay_tree st; tree args; - tree return_slot_addr; + tree return_slot; tree modify_dest; location_t saved_location; struct cgraph_edge *cg_edge; @@ -2095,6 +2409,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) /* Record the function we are about to inline. */ id->src_fn = fn; id->src_node = cg_edge->callee; + id->src_cfun = DECL_STRUCT_FUNCTION (fn); initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb); @@ -2108,7 +2423,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK); /* Find the lhs to which the result of this call is assigned. */ - return_slot_addr = NULL; + return_slot = NULL; if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT) { modify_dest = GIMPLE_STMT_OPERAND (stmt, 0); @@ -2123,8 +2438,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) TREE_NO_WARNING (modify_dest) = 1; if (CALL_EXPR_RETURN_SLOT_OPT (t)) { - return_slot_addr = build_fold_addr_expr (modify_dest); - STRIP_USELESS_TYPE_CONVERSION (return_slot_addr); + return_slot = modify_dest; modify_dest = NULL; } } @@ -2132,7 +2446,7 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) modify_dest = NULL; /* Declare the return variable for the function. */ - declare_return_variable (id, return_slot_addr, + declare_return_variable (id, return_slot, modify_dest, &use_retvar); /* This is it. Duplicate the callee body. Assume callee is @@ -2164,12 +2478,44 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) if (use_retvar && (TREE_CODE (bsi_stmt (stmt_bsi)) != CALL_EXPR)) { *tp = use_retvar; + if (gimple_in_ssa_p (cfun)) + { + update_stmt (stmt); + mark_symbols_for_renaming (stmt); + } maybe_clean_or_replace_eh_stmt (stmt, stmt); } else /* We're modifying a TSI owned by gimple_expand_calls_inline(); tsi_delink() will leave the iterator in a sane state. */ - bsi_remove (&stmt_bsi, true); + { + /* Handle case of inlining function that miss return statement so + return value becomes undefined. */ + if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT + && TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 0)) == SSA_NAME) + { + tree name = TREE_OPERAND (stmt, 0); + tree var = SSA_NAME_VAR (TREE_OPERAND (stmt, 0)); + tree def = gimple_default_def (cfun, var); + + /* If the variable is used undefined, make this name undefined via + move. */ + if (def) + { + TREE_OPERAND (stmt, 1) = def; + update_stmt (stmt); + } + /* Otherwise make this variable undefined. */ + else + { + bsi_remove (&stmt_bsi, true); + set_default_def (var, name); + SSA_NAME_DEF_STMT (name) = build_empty_stmt (); + } + } + else + bsi_remove (&stmt_bsi, true); + } if (purge_dead_abnormal_edges) tree_purge_dead_abnormal_call_edges (return_block); @@ -2290,7 +2636,23 @@ optimize_inline_calls (tree fn) as inlining loops might increase the maximum. */ if (ENTRY_BLOCK_PTR->count) counts_to_freqs (); - fold_cond_expr_cond (); + if (gimple_in_ssa_p (cfun)) + { + /* We make no attempts to keep dominance info up-to-date. */ + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + delete_unreachable_blocks (); + update_ssa (TODO_update_ssa); + fold_cond_expr_cond (); + if (need_ssa_update_p ()) + update_ssa (TODO_update_ssa); + } + else + fold_cond_expr_cond (); + /* It would be nice to check SSA/CFG/statement consistency here, but it is + not possible yet - the IPA passes might make various functions to not + throw and they don't care to proactively update local EH info. This is + done later in fixup_cfg pass that also execute the verification. */ } /* FN is a function that has a complete body, and CLONE is a function whose @@ -2782,7 +3144,7 @@ tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map, struct cgraph_node *old_version_node; struct cgraph_node *new_version_node; copy_body_data id; - tree p, new_fndecl; + tree p; unsigned i; struct ipa_replace_map *replace_info; basic_block old_entry_block; @@ -2828,6 +3190,12 @@ tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map, id.transform_lang_insert_block = false; current_function_decl = new_decl; + old_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION + (DECL_STRUCT_FUNCTION (old_decl)); + initialize_cfun (new_decl, old_decl, + old_entry_block->count, + old_entry_block->frequency); + push_cfun (DECL_STRUCT_FUNCTION (new_decl)); /* Copy the function's static chain. */ p = DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl; @@ -2871,22 +3239,8 @@ tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map, } /* Copy the Function's body. */ - old_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION - (DECL_STRUCT_FUNCTION (old_decl)); - new_fndecl = copy_body (&id, - old_entry_block->count, - old_entry_block->frequency, NULL, NULL); + copy_body (&id, old_entry_block->count, old_entry_block->frequency, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR); - DECL_SAVED_TREE (new_decl) = DECL_SAVED_TREE (new_fndecl); - - DECL_STRUCT_FUNCTION (new_decl)->cfg = - DECL_STRUCT_FUNCTION (new_fndecl)->cfg; - DECL_STRUCT_FUNCTION (new_decl)->eh = DECL_STRUCT_FUNCTION (new_fndecl)->eh; - DECL_STRUCT_FUNCTION (new_decl)->ib_boundaries_block = - DECL_STRUCT_FUNCTION (new_fndecl)->ib_boundaries_block; - DECL_STRUCT_FUNCTION (new_decl)->last_label_uid = - DECL_STRUCT_FUNCTION (new_fndecl)->last_label_uid; - if (DECL_RESULT (old_decl) != NULL_TREE) { tree *res_decl = &DECL_RESULT (old_decl); @@ -2894,13 +3248,20 @@ tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map, lang_hooks.dup_lang_specific_decl (DECL_RESULT (new_decl)); } - current_function_decl = NULL; /* Renumber the lexical scoping (non-code) blocks consecutively. */ number_blocks (new_decl); /* Clean up. */ splay_tree_delete (id.decl_map); fold_cond_expr_cond (); + if (gimple_in_ssa_p (cfun)) + { + update_ssa (TODO_update_ssa); + } + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + pop_cfun (); + current_function_decl = NULL; return; }