X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Ftree-inline.c;h=629ccfb524fb5b5c36b192c788af4582a067bb94;hp=0a383768b043284bae5967a33da2b2ee4a5194fb;hb=6d1cc52ccc8ccbb8d83ceb218c2e24ee9bbd2c4b;hpb=ccf4ab6ba79565bf35426a5aad3140e6354e0121 diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 0a383768b04..629ccfb524f 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -64,7 +64,7 @@ along with GCC; see the file COPYING3. If not see MODIFY_EXPRs that store to a dedicated returned-value variable. The duplicated eh_region info of the copy will later be appended to the info for the caller; the eh_region info in copied throwing - statements and RESX_EXPRs is adjusted accordingly. + statements and RESX statements are adjusted accordingly. Cloning: (only in C++) We have one body for a con/de/structor, and multiple function decls, each with a unique parameter list. @@ -119,7 +119,6 @@ eni_weights eni_time_weights; /* Prototypes. */ static tree declare_return_variable (copy_body_data *, tree, tree, tree *); -static bool inlinable_function_p (tree); static void remap_block (tree *, copy_body_data *); static void copy_bind_expr (tree *, int *, copy_body_data *); static tree mark_local_for_remap_r (tree *, int *, void *); @@ -132,6 +131,7 @@ static tree copy_decl_to_var (tree, copy_body_data *); static tree copy_result_decl_to_var (tree, copy_body_data *); static tree copy_decl_maybe_to_var (tree, copy_body_data *); static gimple remap_gimple_stmt (gimple, copy_body_data *); +static bool delete_unreachable_blocks_update_callgraph (copy_body_data *id); /* Insert a tree->tree mapping for ID. Despite the name suggests that the trees should be variables, it is used for more than that. */ @@ -147,6 +147,30 @@ insert_decl_map (copy_body_data *id, tree key, tree value) *pointer_map_insert (id->decl_map, value) = value; } +/* Insert a tree->tree mapping for ID. This is only used for + variables. */ + +static void +insert_debug_decl_map (copy_body_data *id, tree key, tree value) +{ + if (!gimple_in_ssa_p (id->src_cfun)) + return; + + if (!MAY_HAVE_DEBUG_STMTS) + return; + + if (!target_for_debug_bind (key)) + return; + + gcc_assert (TREE_CODE (key) == PARM_DECL); + gcc_assert (TREE_CODE (value) == VAR_DECL); + + if (!id->debug_map) + id->debug_map = pointer_map_create (); + + *pointer_map_insert (id->debug_map, key) = value; +} + /* Construct new SSA name for old NAME. ID is the inline context. */ static tree @@ -220,6 +244,12 @@ remap_ssa_name (tree name, copy_body_data *id) return new_tree; } +/* If nonzero, we're remapping the contents of inlined debug + statements. If negative, an error has occurred, such as a + reference to a variable that isn't available in the inlined + context. */ +int processing_debug_stmt = 0; + /* Remap DECL during the copying of the BLOCK tree for the function. */ tree @@ -235,6 +265,12 @@ remap_decl (tree decl, copy_body_data *id) n = (tree *) pointer_map_contains (id->decl_map, decl); + if (!n && processing_debug_stmt) + { + processing_debug_stmt = -1; + return decl; + } + /* If we didn't already have an equivalent for this declaration, create one now. */ if (!n) @@ -287,7 +323,10 @@ remap_decl (tree decl, copy_body_data *id) return t; } - return unshare_expr (*n); + if (id->do_not_unshare) + return *n; + else + return unshare_expr (*n); } static tree @@ -303,6 +342,10 @@ remap_type_1 (tree type, copy_body_data *id) new_tree = build_pointer_type_for_mode (remap_type (TREE_TYPE (type), id), TYPE_MODE (type), TYPE_REF_CAN_ALIAS_ALL (type)); + if (TYPE_ATTRIBUTES (type) || TYPE_QUALS (type)) + new_tree = build_type_attribute_qual_variant (new_tree, + TYPE_ATTRIBUTES (type), + TYPE_QUALS (type)); insert_decl_map (id, type, new_tree); return new_tree; } @@ -311,6 +354,10 @@ remap_type_1 (tree type, copy_body_data *id) new_tree = build_reference_type_for_mode (remap_type (TREE_TYPE (type), id), TYPE_MODE (type), TYPE_REF_CAN_ALIAS_ALL (type)); + if (TYPE_ATTRIBUTES (type) || TYPE_QUALS (type)) + new_tree = build_type_attribute_qual_variant (new_tree, + TYPE_ATTRIBUTES (type), + TYPE_QUALS (type)); insert_decl_map (id, type, new_tree); return new_tree; } @@ -497,10 +544,11 @@ remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id) if (can_be_nonlocal (old_var, id)) { if (TREE_CODE (old_var) == VAR_DECL + && ! DECL_EXTERNAL (old_var) && (var_ann (old_var) || !gimple_in_ssa_p (cfun))) cfun->local_decls = tree_cons (NULL_TREE, old_var, cfun->local_decls); - if (debug_info_level > DINFO_LEVEL_TERSE + if ((!optimize || debug_info_level > DINFO_LEVEL_TERSE) && !DECL_IGNORED_P (old_var) && nonlocalized_list) VEC_safe_push (tree, gc, *nonlocalized_list, origin_var); @@ -518,7 +566,7 @@ remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id) ; else if (!new_var) { - if (debug_info_level > DINFO_LEVEL_TERSE + if ((!optimize || debug_info_level > DINFO_LEVEL_TERSE) && !DECL_IGNORED_P (old_var) && nonlocalized_list) VEC_safe_push (tree, gc, *nonlocalized_list, origin_var); @@ -597,10 +645,16 @@ copy_statement_list (tree *tp) new_tree = alloc_stmt_list (); ni = tsi_start (new_tree); oi = tsi_start (*tp); + TREE_TYPE (new_tree) = TREE_TYPE (*tp); *tp = new_tree; for (; !tsi_end_p (oi); tsi_next (&oi)) - tsi_link_after (&ni, tsi_stmt (oi), TSI_NEW_STMT); + { + tree stmt = tsi_stmt (oi); + if (TREE_CODE (stmt) == STATEMENT_LIST) + copy_statement_list (&stmt); + tsi_link_after (&ni, stmt, TSI_CONTINUE_LINKING); + } } static void @@ -775,7 +829,8 @@ remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data) { if (TREE_CODE (new_tree) == ADDR_EXPR) { - *tp = fold_indirect_ref_1 (type, new_tree); + *tp = fold_indirect_ref_1 (EXPR_LOCATION (new_tree), + type, new_tree); /* ??? We should either assert here or build a VIEW_CONVERT_EXPR instead of blindly leaking incompatible types to our IL. */ @@ -802,7 +857,8 @@ remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data) vars. If not referenced from types only. */ if (gimple_in_ssa_p (cfun) && TREE_CODE (*tp) == VAR_DECL - && id->remapping_type_depth == 0) + && id->remapping_type_depth == 0 + && !processing_debug_stmt) add_referenced_var (*tp); /* We should never have TREE_BLOCK set on non-statements. */ @@ -917,7 +973,8 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) } else if (TREE_CODE (*tp) == STATEMENT_LIST) copy_statement_list (tp); - else if (TREE_CODE (*tp) == SAVE_EXPR) + else if (TREE_CODE (*tp) == SAVE_EXPR + || TREE_CODE (*tp) == TARGET_EXPR) remap_save_expr (tp, id->decl_map, walk_subtrees); else if (TREE_CODE (*tp) == LABEL_DECL && (! DECL_CONTEXT (*tp) @@ -973,7 +1030,7 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) STRIP_TYPE_NOPS (value); if (TREE_CONSTANT (value) || TREE_READONLY (value)) { - *tp = build_empty_stmt (); + *tp = build_empty_stmt (EXPR_LOCATION (*tp)); return copy_tree_body_r (tp, walk_subtrees, data); } } @@ -997,14 +1054,18 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) but we absolutely rely on that. As fold_indirect_ref does other useful transformations, try that first, though. */ tree type = TREE_TYPE (TREE_TYPE (*n)); - new_tree = unshare_expr (*n); + if (id->do_not_unshare) + new_tree = *n; + else + new_tree = unshare_expr (*n); old = *tp; *tp = gimple_fold_indirect_ref (new_tree); if (! *tp) { if (TREE_CODE (new_tree) == ADDR_EXPR) { - *tp = fold_indirect_ref_1 (type, new_tree); + *tp = fold_indirect_ref_1 (EXPR_LOCATION (new_tree), + type, new_tree); /* ??? We should either assert here or build a VIEW_CONVERT_EXPR instead of blindly leaking incompatible types to our IL. */ @@ -1028,10 +1089,11 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) copy_tree_r (tp, walk_subtrees, NULL); /* Global variables we haven't seen yet needs to go into referenced - vars. If not referenced from types only. */ + vars. If not referenced from types or debug stmts only. */ if (gimple_in_ssa_p (cfun) && TREE_CODE (*tp) == VAR_DECL - && id->remapping_type_depth == 0) + && id->remapping_type_depth == 0 + && !processing_debug_stmt) add_referenced_var (*tp); /* If EXPR has block defined, map it to newly constructed block. @@ -1051,12 +1113,6 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) TREE_BLOCK (*tp) = new_block; } - if (TREE_CODE (*tp) == RESX_EXPR && id->eh_region_offset) - TREE_OPERAND (*tp, 0) = - build_int_cst (NULL_TREE, - id->eh_region_offset - + TREE_INT_CST_LOW (TREE_OPERAND (*tp, 0))); - if (TREE_CODE (*tp) != OMP_CLAUSE) TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id); @@ -1096,6 +1152,35 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; } +/* Helper for remap_gimple_stmt. Given an EH region number for the + source function, map that to the duplicate EH region number in + the destination function. */ + +static int +remap_eh_region_nr (int old_nr, copy_body_data *id) +{ + eh_region old_r, new_r; + void **slot; + + old_r = get_eh_region_from_number_fn (id->src_cfun, old_nr); + slot = pointer_map_contains (id->eh_map, old_r); + new_r = (eh_region) *slot; + + return new_r->index; +} + +/* Similar, but operate on INTEGER_CSTs. */ + +static tree +remap_eh_region_tree_nr (tree old_t_nr, copy_body_data *id) +{ + int old_nr, new_nr; + + 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); +} /* Helper for copy_bb. Remap statement STMT using the inlining information in ID. Return the new statement copy. */ @@ -1277,8 +1362,67 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) } } + if (gimple_debug_bind_p (stmt)) + { + copy = gimple_build_debug_bind (gimple_debug_bind_get_var (stmt), + gimple_debug_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); + + /* Remap the region numbers for __builtin_eh_{pointer,filter}, + RESX and EH_DISPATCH. */ + if (id->eh_map) + switch (gimple_code (copy)) + { + case GIMPLE_CALL: + { + tree r, fndecl = gimple_call_fndecl (copy); + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_EH_COPY_VALUES: + r = gimple_call_arg (copy, 1); + r = remap_eh_region_tree_nr (r, id); + gimple_call_set_arg (copy, 1, r); + /* FALLTHRU */ + + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_FILTER: + r = gimple_call_arg (copy, 0); + r = remap_eh_region_tree_nr (r, id); + gimple_call_set_arg (copy, 0, r); + break; + + default: + break; + } + } + break; + + case GIMPLE_RESX: + { + int r = gimple_resx_region (copy); + r = remap_eh_region_nr (r, id); + gimple_resx_set_region (copy, r); + } + break; + + case GIMPLE_EH_DISPATCH: + { + int r = gimple_eh_dispatch_region (copy); + r = remap_eh_region_nr (r, id); + gimple_eh_dispatch_set_region (copy, r); + } + break; + + default: + break; + } } /* If STMT has a block defined, map it to the newly constructed @@ -1295,6 +1439,9 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) gimple_set_block (copy, new_block); + if (gimple_debug_bind_p (copy)) + return copy; + /* Remap all the operands in COPY. */ memset (&wi, 0, sizeof (wi)); wi.info = id; @@ -1311,12 +1458,6 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) gimple_set_vuse (copy, NULL_TREE); } - /* We have to handle EH region remapping of GIMPLE_RESX specially because - the region number is not an operand. */ - if (gimple_code (stmt) == GIMPLE_RESX && id->eh_region_offset) - { - gimple_resx_set_region (copy, gimple_resx_region (stmt) + id->eh_region_offset); - } return copy; } @@ -1369,8 +1510,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, { tree new_rhs; new_rhs = force_gimple_operand_gsi (&seq_gsi, - gimple_assign_rhs1 (stmt), - true, NULL, true, GSI_SAME_STMT); + gimple_assign_rhs1 (stmt), + true, NULL, false, GSI_NEW_STMT); gimple_assign_set_rhs1 (stmt, new_rhs); id->regimplify = false; } @@ -1482,108 +1623,82 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, callgraph edges and update or duplicate them. */ if (is_gimple_call (stmt)) { - struct cgraph_edge *edge = cgraph_edge (id->src_node, orig_stmt); + struct cgraph_edge *edge; int flags; switch (id->transform_call_graph_edges) { - case CB_CGE_DUPLICATE: - if (edge) - cgraph_clone_edge (edge, id->dst_node, stmt, - REG_BR_PROB_BASE, 1, - edge->frequency, true); - break; - - case CB_CGE_MOVE_CLONES: - cgraph_set_call_stmt_including_clones (id->dst_node, orig_stmt, stmt); - break; - - case CB_CGE_MOVE: - if (edge) - cgraph_set_call_stmt (edge, stmt); - break; - - default: - gcc_unreachable (); + case CB_CGE_DUPLICATE: + edge = cgraph_edge (id->src_node, orig_stmt); + if (edge) + edge = cgraph_clone_edge (edge, id->dst_node, stmt, + gimple_uid (stmt), + REG_BR_PROB_BASE, 1, + edge->frequency, true); + break; + + case CB_CGE_MOVE_CLONES: + cgraph_set_call_stmt_including_clones (id->dst_node, + orig_stmt, stmt); + edge = cgraph_edge (id->dst_node, stmt); + break; + + case CB_CGE_MOVE: + edge = cgraph_edge (id->dst_node, orig_stmt); + if (edge) + cgraph_set_call_stmt (edge, stmt); + break; + + default: + gcc_unreachable (); } - /* Constant propagation on argument done during inlining - may create new direct call. Produce an edge for it. */ - if (!edge && is_gimple_call (stmt) - && (fn = gimple_call_fndecl (stmt)) != NULL - && !cgraph_edge (id->dst_node, stmt)) - { - struct cgraph_node *dest = cgraph_node (fn); - - /* We have missing edge in the callgraph. This can happen in one case - where previous inlining turned indirect call into direct call by - constant propagating arguments. In all other cases we hit a bug - (incorrect node sharing is most common reason for missing edges. */ - gcc_assert (dest->needed || !dest->analyzed); - if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES) - cgraph_create_edge_including_clones (id->dst_node, dest, stmt, - bb->count, CGRAPH_FREQ_BASE, - bb->loop_depth, - CIF_ORIGINALLY_INDIRECT_CALL); - else - cgraph_create_edge (id->dst_node, dest, stmt, - bb->count, CGRAPH_FREQ_BASE, - bb->loop_depth)->inline_failed - = CIF_ORIGINALLY_INDIRECT_CALL; - if (dump_file) - { - fprintf (dump_file, "Created new direct edge to %s", - cgraph_node_name (dest)); - } - } + /* Constant propagation on argument done during inlining + may create new direct call. Produce an edge for it. */ + if ((!edge + || (edge->indirect_call + && 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); + + /* We have missing edge in the callgraph. This can happen + when previous inlining turned an indirect call into a + direct call by constant propagating arguments or we are + producing dead clone (for further clonning). 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 + || !id->src_node->analyzed); + if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES) + cgraph_create_edge_including_clones + (id->dst_node, dest, stmt, bb->count, + compute_call_stmt_bb_frequency (id->dst_node->decl, bb), + bb->loop_depth, CIF_ORIGINALLY_INDIRECT_CALL); + else + cgraph_create_edge (id->dst_node, dest, stmt, + bb->count, CGRAPH_FREQ_BASE, + bb->loop_depth)->inline_failed + = CIF_ORIGINALLY_INDIRECT_CALL; + if (dump_file) + { + fprintf (dump_file, "Created new direct edge to %s", + cgraph_node_name (dest)); + } + } flags = gimple_call_flags (stmt); - if (flags & ECF_MAY_BE_ALLOCA) cfun->calls_alloca = true; if (flags & ECF_RETURNS_TWICE) cfun->calls_setjmp = true; } - /* If you think we can abort here, you are wrong. - There is no region 0 in gimple. */ - gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) != 0); - - if (stmt_could_throw_p (stmt) - /* When we are cloning for inlining, we are supposed to - construct a clone that calls precisely the same functions - as original. However IPA optimizers might've proved - earlier some function calls as non-trapping that might - render some basic blocks dead that might become - unreachable. - - We can't update SSA with unreachable blocks in CFG and thus - we prevent the scenario by preserving even the "dead" eh - edges until the point they are later removed by - fixup_cfg pass. */ - || (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES - && lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) > 0)) - { - 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 - && stmt_could_throw_p (stmt)) - add_stmt_to_eh_region (stmt, id->eh_region); - } + maybe_duplicate_eh_stmt_fn (cfun, stmt, id->src_cfun, orig_stmt, + id->eh_map, id->eh_lp_nr); - if (gimple_in_ssa_p (cfun)) + if (gimple_in_ssa_p (cfun) && !is_gimple_debug (stmt)) { ssa_op_iter i; tree def; @@ -1712,9 +1827,12 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) bool can_throw, nonlocal_goto; copy_stmt = gsi_stmt (si); - update_stmt (copy_stmt); - if (gimple_in_ssa_p (cfun)) - mark_symbols_for_renaming (copy_stmt); + if (!is_gimple_debug (copy_stmt)) + { + update_stmt (copy_stmt); + if (gimple_in_ssa_p (cfun)) + mark_symbols_for_renaming (copy_stmt); + } /* Do this before the possible split_block. */ gsi_next (&si); @@ -1747,7 +1865,9 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) } } - if (can_throw) + if (gimple_code (copy_stmt) == GIMPLE_EH_DISPATCH) + make_eh_dispatch_edges (copy_stmt); + else if (can_throw) make_eh_edges (copy_stmt); if (nonlocal_goto) @@ -1808,7 +1928,8 @@ copy_phis_for_bb (basic_block bb, copy_body_data *id) new_arg = force_gimple_operand (new_arg, &stmts, true, NULL); gsi_insert_seq_on_edge_immediate (new_edge, stmts); } - add_phi_arg (new_phi, new_arg, new_edge); + add_phi_arg (new_phi, new_arg, new_edge, + gimple_phi_arg_location_from_edge (phi, old_edge)); } } } @@ -1864,9 +1985,6 @@ initialize_cfun (tree new_fndecl, tree callee_fndecl, gcov_type count, cfun->function_end_locus = src_cfun->function_end_locus; cfun->curr_properties = src_cfun->curr_properties; cfun->last_verified = src_cfun->last_verified; - if (src_cfun->ipa_transforms_to_apply) - cfun->ipa_transforms_to_apply = VEC_copy (ipa_opt_pass, heap, - src_cfun->ipa_transforms_to_apply); 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; @@ -1949,11 +2067,8 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency, /* Duplicate any exception-handling regions. */ if (cfun->eh) - { - id->eh_region_offset - = duplicate_eh_regions (cfun_to_copy, remap_decl_1, id, - 0, id->eh_region); - } + id->eh_map = duplicate_eh_regions (cfun_to_copy, NULL, id->eh_lp_nr, + remap_decl_1, id); /* Use aux pointers to map the original blocks to copy. */ FOR_EACH_BB_FN (bb, cfun_to_copy) @@ -1986,9 +2101,108 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency, entry_block_map->aux = NULL; exit_block_map->aux = NULL; + if (id->eh_map) + { + pointer_map_destroy (id->eh_map); + id->eh_map = NULL; + } + return new_fndecl; } +/* Copy the debug STMT using ID. We deal with these statements in a + special way: if any variable in their VALUE expression wasn't + remapped yet, we won't remap it, because that would get decl uids + out of sync, causing codegen differences between -g and -g0. If + this arises, we drop the VALUE expression altogether. */ + +static void +copy_debug_stmt (gimple stmt, copy_body_data *id) +{ + tree t, *n; + struct walk_stmt_info wi; + + t = id->block; + if (gimple_block (stmt)) + { + tree *n; + n = (tree *) pointer_map_contains (id->decl_map, gimple_block (stmt)); + if (n) + t = *n; + } + gimple_set_block (stmt, t); + + /* Remap all the operands in COPY. */ + memset (&wi, 0, sizeof (wi)); + wi.info = id; + + processing_debug_stmt = 1; + + 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))) + { + gcc_assert (TREE_CODE (*n) == VAR_DECL); + t = *n; + } + else + walk_tree (&t, remap_gimple_op_r, &wi, NULL); + + 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); + + /* Punt if any decl couldn't be remapped. */ + if (processing_debug_stmt < 0) + gimple_debug_bind_reset_value (stmt); + + 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 + of being successfully remapped, we delay the processing of debug + stmts until all other stmts that might require remapping are + processed. */ + +static void +copy_debug_stmts (copy_body_data *id) +{ + size_t i; + gimple stmt; + + if (!id->debug_stmts) + return; + + for (i = 0; VEC_iterate (gimple, id->debug_stmts, i, stmt); i++) + copy_debug_stmt (stmt, id); + + VEC_free (gimple, heap, id->debug_stmts); +} + +/* Make a copy of the body of SRC_FN so that it can be inserted inline in + another function. */ + +static tree +copy_tree_body (copy_body_data *id) +{ + tree fndecl = id->src_fn; + tree body = DECL_SAVED_TREE (fndecl); + + walk_tree (&body, copy_tree_body_r, id, NULL); + + return body; +} + +/* Make a copy of the body of FN so that it can be inserted inline in + another function. */ + static tree copy_body (copy_body_data *id, gcov_type count, int frequency, basic_block entry_block_map, basic_block exit_block_map) @@ -1999,6 +2213,7 @@ copy_body (copy_body_data *id, gcov_type count, int frequency, /* 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, entry_block_map, exit_block_map); + copy_debug_stmts (id); return body; } @@ -2019,8 +2234,51 @@ self_inlining_addr_expr (tree value, tree fn) return var && auto_var_in_fn_p (var, fn); } +/* Append to BB a debug annotation that binds VAR to VALUE, inheriting + lexical block and line number information from base_stmt, if given, + or from the last stmt of the block otherwise. */ + +static gimple +insert_init_debug_bind (copy_body_data *id, + basic_block bb, tree var, tree value, + gimple base_stmt) +{ + gimple note; + gimple_stmt_iterator gsi; + tree tracked_var; + + if (!gimple_in_ssa_p (id->src_cfun)) + return NULL; + + if (!MAY_HAVE_DEBUG_STMTS) + return NULL; + + tracked_var = target_for_debug_bind (var); + if (!tracked_var) + return NULL; + + if (bb) + { + gsi = gsi_last_bb (bb); + if (!base_stmt && !gsi_end_p (gsi)) + base_stmt = gsi_stmt (gsi); + } + + note = gimple_build_debug_bind (tracked_var, value, base_stmt); + + if (bb) + { + if (!gsi_end_p (gsi)) + gsi_insert_after (&gsi, note, GSI_SAME_STMT); + else + gsi_insert_before (&gsi, note, GSI_SAME_STMT); + } + + return note; +} + static void -insert_init_stmt (basic_block bb, gimple init_stmt) +insert_init_stmt (copy_body_data *id, basic_block bb, gimple init_stmt) { /* If VAR represents a zero-sized variable, it's possible that the assignment statement may result in no gimple statements. */ @@ -2032,7 +2290,8 @@ insert_init_stmt (basic_block bb, gimple init_stmt) from a rhs with a conversion. Handle that here by forcing the rhs into a temporary. gimple_regimplify_operands is not prepared to do this for us. */ - if (!is_gimple_reg (gimple_assign_lhs (init_stmt)) + if (!is_gimple_debug (init_stmt) + && !is_gimple_reg (gimple_assign_lhs (init_stmt)) && is_gimple_reg_type (TREE_TYPE (gimple_assign_lhs (init_stmt))) && gimple_assign_rhs_class (init_stmt) == GIMPLE_UNARY_RHS) { @@ -2047,6 +2306,18 @@ insert_init_stmt (basic_block bb, gimple init_stmt) 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) + { + tree var, def = gimple_assign_lhs (init_stmt); + + if (TREE_CODE (def) == SSA_NAME) + var = SSA_NAME_VAR (def); + else + var = def; + + insert_init_debug_bind (id, bb, var, def, init_stmt); + } } } @@ -2077,9 +2348,29 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, rhs = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (p), value); } + /* Make an equivalent VAR_DECL. Note that we must NOT remap the type + here since the type of this decl must be visible to the calling + function. */ + var = copy_decl_to_var (p, id); + + /* We're actually using the newly-created var. */ + if (gimple_in_ssa_p (cfun) && TREE_CODE (var) == VAR_DECL) + { + get_var_ann (var); + add_referenced_var (var); + } + + /* Declare this new variable. */ + TREE_CHAIN (var) = *vars; + *vars = var; + + /* Make gimplifier happy about this variable. */ + DECL_SEEN_IN_BIND_EXPR_P (var) = 1; + /* 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. */ + 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 + value. */ if (TREE_READONLY (p) && !TREE_ADDRESSABLE (p) && value && !TREE_SIDE_EFFECTS (value) @@ -2100,32 +2391,16 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, && ! self_inlining_addr_expr (value, fn)) { insert_decl_map (id, p, value); - return NULL; + insert_debug_decl_map (id, p, var); + return insert_init_debug_bind (id, bb, var, value, NULL); } } - /* Make an equivalent VAR_DECL. Note that we must NOT remap the type - 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); - } - /* Register the VAR_DECL as the equivalent for the PARM_DECL; that way, when the PARM_DECL is encountered, it will be automatically replaced by the VAR_DECL. */ insert_decl_map (id, p, var); - /* Declare this new variable. */ - TREE_CHAIN (var) = *vars; - *vars = var; - - /* Make gimplifier happy about this variable. */ - DECL_SEEN_IN_BIND_EXPR_P (var) = 1; - /* Even if P was TREE_READONLY, the new VAR should not be. In the original code, we would have constructed a temporary, and then the function body would have never @@ -2147,15 +2422,7 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, Do replacement at -O0 for const arguments replaced by constant. This is important for builtin_constant_p and other construct requiring - constant argument to be visible in inlined function body. - - 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. */ + constant argument to be visible in inlined function body. */ if (gimple_in_ssa_p (cfun) && rhs && def && is_gimple_reg (p) && (optimize || (TREE_READONLY (p) @@ -2165,7 +2432,7 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def)) { insert_decl_map (id, def, rhs); - return NULL; + return insert_init_debug_bind (id, bb, var, rhs, NULL); } /* If the value of argument is never used, don't care about initializing @@ -2173,7 +2440,7 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, if (optimize && gimple_in_ssa_p (cfun) && !def && is_gimple_reg (p)) { gcc_assert (!value || !TREE_SIDE_EFFECTS (value)); - return NULL; + return insert_init_debug_bind (id, bb, var, rhs, NULL); } /* Initialize this VAR_DECL from the equivalent argument. Convert @@ -2183,7 +2450,7 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, if (rhs == error_mark_node) { insert_decl_map (id, p, var); - return NULL; + return insert_init_debug_bind (id, bb, var, rhs, NULL); } STRIP_USELESS_TYPE_CONVERSION (rhs); @@ -2201,7 +2468,7 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn, init_stmt = gimple_build_assign (var, rhs); if (bb && init_stmt) - insert_init_stmt (bb, init_stmt); + insert_init_stmt (id, bb, init_stmt); } return init_stmt; } @@ -2431,26 +2698,32 @@ declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest, return var; } -/* Returns nonzero if a function can be inlined as a tree. */ +/* Callback through walk_tree. Determine if a DECL_INITIAL makes reference + to a local label. */ -bool -tree_inlinable_function_p (tree fn) +static tree +has_label_address_in_static_1 (tree *nodep, int *walk_subtrees, void *fnp) { - return inlinable_function_p (fn); -} + tree node = *nodep; + tree fn = (tree) fnp; -static const char *inline_forbidden_reason; + if (TREE_CODE (node) == LABEL_DECL && DECL_CONTEXT (node) == fn) + return node; + + if (TYPE_P (node)) + *walk_subtrees = 0; + + return NULL_TREE; +} -/* A callback for walk_gimple_seq to handle tree operands. Returns - NULL_TREE if a function can be inlined, otherwise sets the reason - why not and returns a tree representing the offending operand. */ +/* Callback through walk_tree. Determine if we've got an aggregate + type that we can't support; return non-null if so. */ static tree -inline_forbidden_p_op (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, - void *fnp ATTRIBUTE_UNUSED) +cannot_copy_type_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) { - tree node = *nodep; - tree t; + tree t, node = *nodep; if (TREE_CODE (node) == RECORD_TYPE || TREE_CODE (node) == UNION_TYPE) { @@ -2469,21 +2742,78 @@ inline_forbidden_p_op (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, cycle to try to find out. This should be checked for 4.1. */ for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t)) if (variably_modified_type_p (TREE_TYPE (t), NULL)) - { - inline_forbidden_reason - = G_("function %q+F can never be inlined " - "because it uses variable sized variables"); - return node; - } + return node; } return NULL_TREE; } -/* A callback for walk_gimple_seq to handle statements. Returns - non-NULL iff a function can not be inlined. Also sets the reason - why. */ +/* Determine if the function can be copied. If so return NULL. If + not return a string describng the reason for failure. */ + +static const char * +copy_forbidden (struct function *fun, tree fndecl) +{ + const char *reason = fun->cannot_be_copied_reason; + tree step; + + /* Only examine the function once. */ + if (fun->cannot_be_copied_set) + return reason; + + /* We cannot copy a function that receives a non-local goto + because we cannot remap the destination label used in the + function that is performing the non-local goto. */ + /* ??? Actually, this should be possible, if we work at it. + No doubt there's just a handful of places that simply + assume it doesn't happen and don't substitute properly. */ + if (fun->has_nonlocal_label) + { + reason = G_("function %q+F can never be copied " + "because it receives a non-local goto"); + goto fail; + } + + for (step = fun->local_decls; step; step = TREE_CHAIN (step)) + { + tree decl = TREE_VALUE (step); + + if (TREE_CODE (decl) == VAR_DECL + && TREE_STATIC (decl) + && !DECL_EXTERNAL (decl) + && DECL_INITIAL (decl) + && walk_tree_without_duplicates (&DECL_INITIAL (decl), + has_label_address_in_static_1, + fndecl)) + { + reason = G_("function %q+F can never be copied because it saves " + "address of local label in a static variable"); + goto fail; + } + + if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl) + && variably_modified_type_p (TREE_TYPE (decl), NULL) + && walk_tree_without_duplicates (&TREE_TYPE (decl), + cannot_copy_type_1, NULL)) + { + reason = G_("function %q+F can never be copied " + "because it uses variable sized variables"); + goto fail; + } + } + + fail: + fun->cannot_be_copied_reason = reason; + fun->cannot_be_copied_set = true; + return reason; +} + + +static const char *inline_forbidden_reason; + +/* A callback for walk_gimple_seq to handle statements. Returns non-null + iff a function can not be inlined. Also sets the reason why. */ static tree inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p, @@ -2592,21 +2922,6 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p, } break; - case GIMPLE_LABEL: - t = gimple_label_label (stmt); - if (DECL_NONLOCAL (t)) - { - /* We cannot inline a function that receives a non-local goto - because we cannot remap the destination label used in the - function that is performing the non-local goto. */ - inline_forbidden_reason - = G_("function %q+F can never be inlined " - "because it receives a non-local goto"); - *handled_ops_p = true; - return t; - } - break; - default: break; } @@ -2615,42 +2930,25 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p, return NULL_TREE; } - -static tree -inline_forbidden_p_2 (tree *nodep, int *walk_subtrees, - void *fnp) -{ - tree node = *nodep; - tree fn = (tree) fnp; - - if (TREE_CODE (node) == LABEL_DECL && DECL_CONTEXT (node) == fn) - { - inline_forbidden_reason - = G_("function %q+F can never be inlined " - "because it saves address of local label in a static variable"); - return node; - } - - if (TYPE_P (node)) - *walk_subtrees = 0; - - return NULL_TREE; -} - /* Return true if FNDECL is a function that cannot be inlined into another one. */ static bool inline_forbidden_p (tree fndecl) { - location_t saved_loc = input_location; struct function *fun = DECL_STRUCT_FUNCTION (fndecl); - tree step; struct walk_stmt_info wi; struct pointer_set_t *visited_nodes; basic_block bb; bool forbidden_p = false; + /* First check for shared reasons not to copy the code. */ + inline_forbidden_reason = copy_forbidden (fun, fndecl); + if (inline_forbidden_reason != NULL) + return true; + + /* Next, walk the statements of the function looking for + constraucts we can't handle, or are non-optimal for inlining. */ visited_nodes = pointer_set_create (); memset (&wi, 0, sizeof (wi)); wi.info = (void *) fndecl; @@ -2660,41 +2958,21 @@ inline_forbidden_p (tree fndecl) { gimple ret; gimple_seq seq = bb_seq (bb); - ret = walk_gimple_seq (seq, inline_forbidden_p_stmt, - inline_forbidden_p_op, &wi); + ret = walk_gimple_seq (seq, inline_forbidden_p_stmt, NULL, &wi); forbidden_p = (ret != NULL); if (forbidden_p) - goto egress; - } - - for (step = fun->local_decls; step; step = TREE_CHAIN (step)) - { - tree decl = TREE_VALUE (step); - if (TREE_CODE (decl) == VAR_DECL - && TREE_STATIC (decl) - && !DECL_EXTERNAL (decl) - && DECL_INITIAL (decl)) - { - tree ret; - ret = walk_tree_without_duplicates (&DECL_INITIAL (decl), - inline_forbidden_p_2, fndecl); - forbidden_p = (ret != NULL); - if (forbidden_p) - goto egress; - } + break; } -egress: pointer_set_destroy (visited_nodes); - input_location = saved_loc; return forbidden_p; } /* Returns nonzero if FN is a function that does not have any fundamental inline blocking properties. */ -static bool -inlinable_function_p (tree fn) +bool +tree_inlinable_function_p (tree fn) { bool inlinable = true; bool do_warning; @@ -2767,6 +3045,8 @@ estimate_move_cost (tree type) { HOST_WIDE_INT size; + gcc_assert (!VOID_TYPE_P (type)); + size = int_size_in_bytes (type); if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO (!optimize_size)) @@ -2779,7 +3059,8 @@ estimate_move_cost (tree type) /* Returns cost of operation CODE, according to WEIGHTS */ static int -estimate_operator_cost (enum tree_code code, eni_weights *weights) +estimate_operator_cost (enum tree_code code, eni_weights *weights, + tree op1 ATTRIBUTE_UNUSED, tree op2) { switch (code) { @@ -2801,6 +3082,7 @@ estimate_operator_cost (enum tree_code code, eni_weights *weights) case MINUS_EXPR: case MULT_EXPR: + case ADDR_SPACE_CONVERT_EXPR: case FIXED_CONVERT_EXPR: case FIX_TRUNC_EXPR: @@ -2889,7 +3171,9 @@ estimate_operator_cost (enum tree_code code, eni_weights *weights) case FLOOR_MOD_EXPR: case ROUND_MOD_EXPR: case RDIV_EXPR: - return weights->div_mod_cost; + if (TREE_CODE (op2) != INTEGER_CST) + return weights->div_mod_cost; + return 1; default: /* We expect a copy assignment with no operator. */ @@ -2926,6 +3210,7 @@ estimate_num_insns (gimple stmt, eni_weights *weights) unsigned cost, i; enum gimple_code code = gimple_code (stmt); tree lhs; + tree rhs; switch (code) { @@ -2949,16 +3234,27 @@ estimate_num_insns (gimple stmt, eni_weights *weights) of moving something into "a", which we compute using the function estimate_move_cost. */ lhs = gimple_assign_lhs (stmt); + rhs = gimple_assign_rhs1 (stmt); + if (is_gimple_reg (lhs)) cost = 0; else cost = estimate_move_cost (TREE_TYPE (lhs)); - cost += estimate_operator_cost (gimple_assign_rhs_code (stmt), weights); + if (!is_gimple_reg (rhs) && !is_gimple_min_invariant (rhs)) + cost += estimate_move_cost (TREE_TYPE (rhs)); + + cost += estimate_operator_cost (gimple_assign_rhs_code (stmt), weights, + gimple_assign_rhs1 (stmt), + get_gimple_rhs_class (gimple_assign_rhs_code (stmt)) + == GIMPLE_BINARY_RHS + ? gimple_assign_rhs2 (stmt) : NULL); break; case GIMPLE_COND: - cost = 1 + estimate_operator_cost (gimple_cond_code (stmt), weights); + cost = 1 + estimate_operator_cost (gimple_cond_code (stmt), weights, + gimple_op (stmt, 0), + gimple_op (stmt, 1)); break; case GIMPLE_SWITCH: @@ -2967,7 +3263,10 @@ estimate_num_insns (gimple stmt, eni_weights *weights) TODO: once the switch expansion logic is sufficiently separated, we can do better job on estimating cost of the switch. */ - cost = gimple_switch_num_labels (stmt) * 2; + if (weights->time_based) + cost = floor_log2 (gimple_switch_num_labels (stmt)) * 2; + else + cost = gimple_switch_num_labels (stmt) * 2; break; case GIMPLE_CALL: @@ -2990,8 +3289,7 @@ estimate_num_insns (gimple stmt, eni_weights *weights) case BUILT_IN_CONSTANT_P: return 0; case BUILT_IN_EXPECT: - cost = 0; - break; + return 0; /* Prefetch instruction is not expensive. */ case BUILT_IN_PREFETCH: @@ -3005,6 +3303,8 @@ estimate_num_insns (gimple stmt, eni_weights *weights) if (decl) funtype = TREE_TYPE (decl); + if (!VOID_TYPE_P (TREE_TYPE (funtype))) + cost += estimate_move_cost (TREE_TYPE (funtype)); /* Our cost must be kept in sync with cgraph_estimate_size_after_inlining that does use function declaration to figure out the arguments. */ @@ -3012,20 +3312,24 @@ estimate_num_insns (gimple stmt, eni_weights *weights) { tree arg; for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg)) - cost += estimate_move_cost (TREE_TYPE (arg)); + if (!VOID_TYPE_P (TREE_TYPE (arg))) + cost += estimate_move_cost (TREE_TYPE (arg)); } else if (funtype && prototype_p (funtype)) { tree t; - for (t = TYPE_ARG_TYPES (funtype); t; t = TREE_CHAIN (t)) - cost += estimate_move_cost (TREE_VALUE (t)); + for (t = TYPE_ARG_TYPES (funtype); t && t != void_list_node; + t = TREE_CHAIN (t)) + if (!VOID_TYPE_P (TREE_VALUE (t))) + cost += estimate_move_cost (TREE_VALUE (t)); } else { for (i = 0; i < gimple_call_num_args (stmt); i++) { tree arg = gimple_call_arg (stmt, i); - cost += estimate_move_cost (TREE_TYPE (arg)); + if (!VOID_TYPE_P (TREE_TYPE (arg))) + cost += estimate_move_cost (TREE_TYPE (arg)); } } @@ -3037,13 +3341,23 @@ estimate_num_insns (gimple stmt, eni_weights *weights) case GIMPLE_NOP: case GIMPLE_PHI: case GIMPLE_RETURN: - case GIMPLE_CHANGE_DYNAMIC_TYPE: case GIMPLE_PREDICT: + case GIMPLE_DEBUG: return 0; case GIMPLE_ASM: + return asm_str_count (gimple_asm_string (stmt)); + case GIMPLE_RESX: - return 1; + /* This is either going to be an external function call with one + argument, or two register copy statements plus a goto. */ + return 2; + + case GIMPLE_EH_DISPATCH: + /* ??? This is going to turn into a switch statement. Ideally + we'd have a look at the eh region and estimate the number of + edges involved. */ + return 10; case GIMPLE_BIND: return estimate_num_insns_seq (gimple_bind_body (stmt), weights); @@ -3121,15 +3435,11 @@ estimate_num_insns_fn (tree fndecl, eni_weights *weights) void init_inline_once (void) { - eni_inlining_weights.call_cost = PARAM_VALUE (PARAM_INLINE_CALL_COST); - eni_inlining_weights.target_builtin_call_cost = 1; - eni_inlining_weights.div_mod_cost = 10; - eni_inlining_weights.omp_cost = 40; - eni_size_weights.call_cost = 1; eni_size_weights.target_builtin_call_cost = 1; eni_size_weights.div_mod_cost = 1; eni_size_weights.omp_cost = 40; + eni_size_weights.time_based = false; /* Estimating time for call is difficult, since we have no idea what the called function does. In the current uses of eni_time_weights, @@ -3139,6 +3449,7 @@ init_inline_once (void) eni_time_weights.target_builtin_call_cost = 10; eni_time_weights.div_mod_cost = 10; eni_time_weights.omp_cost = 40; + eni_time_weights.time_based = true; } /* Estimate the number of instructions in a gimple_seq. */ @@ -3186,7 +3497,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) { tree retvar, use_retvar; tree fn; - struct pointer_map_t *st; + struct pointer_map_t *st, *dst; tree return_slot; tree modify_dest; location_t saved_location; @@ -3242,6 +3553,13 @@ 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))) + goto egress; + /* Don't try to inline functions that are not well-suited to inlining. */ if (!cgraph_inline_p (cg_edge, &reason)) @@ -3281,7 +3599,12 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) #endif /* We will be inlining this callee. */ - id->eh_region = lookup_stmt_eh_region (stmt); + 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); /* Split the block holding the GIMPLE_CALL. */ e = split_block (bb, stmt); @@ -3326,6 +3649,8 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) map. */ st = id->decl_map; id->decl_map = pointer_map_create (); + dst = id->debug_map; + id->debug_map = NULL; /* Record the function we are about to inline. */ id->src_fn = fn; @@ -3391,13 +3716,6 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) /* Declare the return variable for the function. */ retvar = declare_return_variable (id, return_slot, modify_dest, &use_retvar); - if (DECL_IS_OPERATOR_NEW (fn)) - { - gcc_assert (TREE_CODE (retvar) == VAR_DECL - && POINTER_TYPE_P (TREE_TYPE (retvar))); - DECL_NO_TBAA_P (retvar) = 1; - } - /* Add local vars in this inlined callee to caller. */ t_step = id->src_cfun->local_decls; for (; t_step; t_step = TREE_CHAIN (t_step)) @@ -3421,7 +3739,19 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) duplicate our body before altering anything. */ copy_body (id, bb->count, bb->frequency, bb, return_block); + /* Reset the escaped and callused solutions. */ + if (cfun->gimple_df) + { + pt_solution_reset (&cfun->gimple_df->escaped); + pt_solution_reset (&cfun->gimple_df->callused); + } + /* Clean up. */ + if (id->debug_map) + { + pointer_map_destroy (id->debug_map); + id->debug_map = dst; + } pointer_map_destroy (id->decl_map); id->decl_map = st; @@ -3491,6 +3821,10 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) (*debug_hooks->outlining_inline_function) (cg_edge->callee->decl); /* Update callgraph if needed. */ + if (cg_edge->callee->clone_of + && !cg_edge->callee->clone_of->next_sibling_clone + && !cg_edge->callee->analyzed) + cgraph_remove_node (cg_edge->callee); cgraph_remove_node (cg_edge->callee); id->block = NULL_TREE; @@ -3542,16 +3876,60 @@ fold_marked_statements (int first, struct pointer_set_t *statements) if (pointer_set_contains (statements, gsi_stmt (gsi))) { gimple old_stmt = gsi_stmt (gsi); + tree old_decl = is_gimple_call (old_stmt) ? gimple_call_fndecl (old_stmt) : 0; - if (fold_stmt (&gsi)) + if (old_decl && DECL_BUILT_IN (old_decl)) + { + /* Folding builtins can create multiple instructions, + we need to look at all of them. */ + gimple_stmt_iterator i2 = gsi; + gsi_prev (&i2); + if (fold_stmt (&gsi)) + { + gimple new_stmt; + if (gsi_end_p (i2)) + i2 = gsi_start_bb (BASIC_BLOCK (first)); + else + gsi_next (&i2); + while (1) + { + new_stmt = gsi_stmt (i2); + update_stmt (new_stmt); + cgraph_update_edges_for_call_stmt (old_stmt, old_decl, + new_stmt); + + if (new_stmt == gsi_stmt (gsi)) + { + /* It is okay to check only for the very last + of these statements. If it is a throwing + statement nothing will change. If it isn't + this can remove EH edges. If that weren't + correct then because some intermediate stmts + throw, but not the last one. That would mean + we'd have to split the block, which we can't + here and we'd loose anyway. And as builtins + probably never throw, this all + is mood anyway. */ + if (maybe_clean_or_replace_eh_stmt (old_stmt, + new_stmt)) + gimple_purge_dead_eh_edges (BASIC_BLOCK (first)); + break; + } + gsi_next (&i2); + } + } + } + else if (fold_stmt (&gsi)) { /* Re-read the statement from GSI as fold_stmt() may have changed it. */ gimple new_stmt = gsi_stmt (gsi); update_stmt (new_stmt); - if (is_gimple_call (old_stmt)) - cgraph_update_edges_for_call_stmt (old_stmt, new_stmt); + if (is_gimple_call (old_stmt) + || is_gimple_call (new_stmt)) + cgraph_update_edges_for_call_stmt (old_stmt, old_decl, + new_stmt); if (maybe_clean_or_replace_eh_stmt (old_stmt, new_stmt)) gimple_purge_dead_eh_edges (BASIC_BLOCK (first)); @@ -3648,12 +4026,18 @@ optimize_inline_calls (tree fn) fold_marked_statements (last, id.statements_to_fold); pointer_set_destroy (id.statements_to_fold); + gcc_assert (!id.debug_stmts); + /* Renumber the (code) basic_blocks consecutively. */ compact_blocks (); /* Renumber the lexical scoping (non-code) blocks consecutively. */ number_blocks (fn); fold_cond_expr_cond (); + delete_unreachable_blocks_update_callgraph (&id); +#ifdef ENABLE_CHECKING + verify_cgraph_node (id.dst_node); +#endif /* 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 @@ -3847,7 +4231,8 @@ unsave_r (tree *tp, int *walk_subtrees, void *data) gcc_unreachable (); else if (TREE_CODE (*tp) == BIND_EXPR) copy_bind_expr (tp, walk_subtrees, id); - else if (TREE_CODE (*tp) == SAVE_EXPR) + else if (TREE_CODE (*tp) == SAVE_EXPR + || TREE_CODE (*tp) == TARGET_EXPR) remap_save_expr (tp, st, walk_subtrees); else { @@ -3878,6 +4263,7 @@ unsave_expr_now (tree expr) id.src_fn = current_function_decl; id.dst_fn = current_function_decl; id.decl_map = pointer_map_create (); + id.debug_map = NULL; id.copy_decl = copy_decl_no_change; id.transform_call_graph_edges = CB_CGE_DUPLICATE; @@ -3893,6 +4279,8 @@ unsave_expr_now (tree expr) /* Clean up. */ pointer_map_destroy (id.decl_map); + if (id.debug_map) + pointer_map_destroy (id.debug_map); return expr; } @@ -4024,6 +4412,7 @@ copy_gimple_seq_and_replace_locals (gimple_seq seq) id.src_fn = current_function_decl; id.dst_fn = current_function_decl; id.decl_map = pointer_map_create (); + id.debug_map = NULL; id.copy_decl = copy_decl_no_change; id.transform_call_graph_edges = CB_CGE_DUPLICATE; @@ -4048,6 +4437,8 @@ copy_gimple_seq_and_replace_locals (gimple_seq seq) /* Clean up. */ pointer_map_destroy (id.decl_map); + if (id.debug_map) + pointer_map_destroy (id.debug_map); return copy; } @@ -4143,12 +4534,12 @@ copy_decl_to_var (tree decl, copy_body_data *id) type = TREE_TYPE (decl); - copy = build_decl (VAR_DECL, DECL_NAME (decl), type); + copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn), + VAR_DECL, DECL_NAME (decl), type); TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl); TREE_READONLY (copy) = TREE_READONLY (decl); TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl); DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl); - DECL_NO_TBAA_P (copy) = DECL_NO_TBAA_P (decl); return copy_decl_for_dup_finish (id, decl, copy); } @@ -4168,14 +4559,14 @@ copy_result_decl_to_var (tree decl, copy_body_data *id) if (DECL_BY_REFERENCE (decl)) type = TREE_TYPE (type); - copy = build_decl (VAR_DECL, DECL_NAME (decl), type); + copy = build_decl (DECL_SOURCE_LOCATION (id->dst_fn), + VAR_DECL, DECL_NAME (decl), type); TREE_READONLY (copy) = TREE_READONLY (decl); TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl); if (!DECL_BY_REFERENCE (decl)) { TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl); DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl); - DECL_NO_TBAA_P (copy) = DECL_NO_TBAA_P (decl); } return copy_decl_for_dup_finish (id, decl, copy); @@ -4266,17 +4657,12 @@ copy_static_chain (tree static_chain, copy_body_data * id) /* Return true if the function is allowed to be versioned. This is a guard for the versioning functionality. */ + bool tree_versionable_function_p (tree fndecl) { - if (fndecl == NULL_TREE) - return false; - /* ??? There are cases where a function is - uninlinable but can be versioned. */ - if (!tree_inlinable_function_p (fndecl)) - return false; - - return true; + return (!lookup_attribute ("noclone", DECL_ATTRIBUTES (fndecl)) + && copy_forbidden (DECL_STRUCT_FUNCTION (fndecl), fndecl) == NULL); } /* Delete all unreachable basic blocks and update callgraph. @@ -4373,6 +4759,42 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id) return changed; } +/* Update clone info after duplication. */ + +static void +update_clone_info (copy_body_data * id) +{ + struct cgraph_node *node; + if (!id->dst_node->clones) + return; + for (node = id->dst_node->clones; node != id->dst_node;) + { + /* First update replace maps to match the new body. */ + if (node->clone.tree_map) + { + unsigned int i; + for (i = 0; i < VEC_length (ipa_replace_map_p, node->clone.tree_map); i++) + { + struct ipa_replace_map *replace_info; + replace_info = VEC_index (ipa_replace_map_p, node->clone.tree_map, i); + walk_tree (&replace_info->old_tree, copy_tree_body_r, id, NULL); + walk_tree (&replace_info->new_tree, copy_tree_body_r, id, NULL); + } + } + if (node->clones) + node = node->clones; + else if (node->next_sibling_clone) + node = node->next_sibling_clone; + else + { + while (node != id->dst_node && !node->next_sibling_clone) + node = node->clone_of; + if (node != id->dst_node) + node = node->next_sibling_clone; + } + } +} + /* Create a copy of a function's tree. OLD_DECL and NEW_DECL are FUNCTION_DECL tree nodes of the original function and the new copied function @@ -4382,7 +4804,8 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id) trees. If UPDATE_CLONES is set, the call_stmt fields of edges of clones of the function will be updated. */ void -tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc)* tree_map, +tree_function_versioning (tree old_decl, tree new_decl, + VEC(ipa_replace_map_p,gc)* tree_map, bool update_clones, bitmap args_to_skip) { struct cgraph_node *old_version_node; @@ -4391,7 +4814,7 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc tree p; unsigned i; struct ipa_replace_map *replace_info; - basic_block old_entry_block; + basic_block old_entry_block, bb; VEC (gimple, heap) *init_stmts = VEC_alloc (gimple, heap, 10); tree t_step; @@ -4413,19 +4836,34 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc DECL_ARTIFICIAL (new_decl) = 1; DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl); + DECL_FUNCTION_PERSONALITY (new_decl) = DECL_FUNCTION_PERSONALITY (old_decl); /* Prepare the data structures for the tree copy. */ memset (&id, 0, sizeof (id)); /* Generate a new name for the new version. */ id.statements_to_fold = pointer_set_create (); - + id.decl_map = pointer_map_create (); + id.debug_map = NULL; id.src_fn = old_decl; id.dst_fn = new_decl; id.src_node = old_version_node; id.dst_node = new_version_node; id.src_cfun = DECL_STRUCT_FUNCTION (old_decl); + if (id.src_node->ipa_transforms_to_apply) + { + VEC(ipa_opt_pass,heap) * old_transforms_to_apply = id.dst_node->ipa_transforms_to_apply; + unsigned int i; + + id.dst_node->ipa_transforms_to_apply = VEC_copy (ipa_opt_pass, heap, + id.src_node->ipa_transforms_to_apply); + for (i = 0; i < VEC_length (ipa_opt_pass, old_transforms_to_apply); i++) + VEC_safe_push (ipa_opt_pass, heap, id.dst_node->ipa_transforms_to_apply, + VEC_index (ipa_opt_pass, + old_transforms_to_apply, + i)); + } id.copy_decl = copy_decl_no_change; id.transform_call_graph_edges @@ -4509,7 +4947,8 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc } /* Copy the Function's body. */ - copy_body (&id, old_entry_block->count, old_entry_block->frequency, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR); + copy_body (&id, old_entry_block->count, old_entry_block->frequency, + ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR); if (DECL_RESULT (old_decl) != NULL_TREE) { @@ -4521,15 +4960,28 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc /* Renumber the lexical scoping (non-code) blocks consecutively. */ number_blocks (new_decl); - if (VEC_length (gimple, init_stmts)) + /* We want to create the BB unconditionally, so that the addition of + debug stmts doesn't affect BB count, which may in the end cause + codegen differences. */ + bb = split_edge (single_succ_edge (ENTRY_BLOCK_PTR)); + while (VEC_length (gimple, init_stmts)) + insert_init_stmt (&id, bb, VEC_pop (gimple, init_stmts)); + update_clone_info (&id); + + /* Remap the nonlocal_goto_save_area, if any. */ + if (cfun->nonlocal_goto_save_area) { - basic_block bb = split_edge (single_succ_edge (ENTRY_BLOCK_PTR)); - while (VEC_length (gimple, init_stmts)) - insert_init_stmt (bb, VEC_pop (gimple, init_stmts)); + struct walk_stmt_info wi; + + memset (&wi, 0, sizeof (wi)); + wi.info = &id; + walk_tree (&cfun->nonlocal_goto_save_area, remap_gimple_op_r, &wi, NULL); } /* Clean up. */ pointer_map_destroy (id.decl_map); + if (id.debug_map) + pointer_map_destroy (id.debug_map); free_dominance_info (CDI_DOMINATORS); free_dominance_info (CDI_POST_DOMINATORS); @@ -4541,6 +4993,7 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc free_dominance_info (CDI_DOMINATORS); free_dominance_info (CDI_POST_DOMINATORS); + gcc_assert (!id.debug_stmts); VEC_free (gimple, heap, init_stmts); pop_cfun (); current_function_decl = old_current_function_decl; @@ -4549,6 +5002,60 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc return; } +/* EXP is CALL_EXPR present in a GENERIC expression tree. Try to integrate + the callee and return the inlined body on success. */ + +tree +maybe_inline_call_in_expr (tree exp) +{ + tree fn = get_callee_fndecl (exp); + + /* We can only try to inline "const" functions. */ + if (fn && TREE_READONLY (fn) && DECL_SAVED_TREE (fn)) + { + struct pointer_map_t *decl_map = pointer_map_create (); + call_expr_arg_iterator iter; + copy_body_data id; + tree param, arg, t; + + /* Remap the parameters. */ + for (param = DECL_ARGUMENTS (fn), arg = first_call_expr_arg (exp, &iter); + param; + param = TREE_CHAIN (param), arg = next_call_expr_arg (&iter)) + *pointer_map_insert (decl_map, param) = arg; + + memset (&id, 0, sizeof (id)); + id.src_fn = fn; + id.dst_fn = current_function_decl; + id.src_cfun = DECL_STRUCT_FUNCTION (fn); + id.decl_map = decl_map; + + id.copy_decl = copy_decl_no_change; + 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; + + /* Make sure not to unshare trees behind the front-end's back + since front-end specific mechanisms may rely on sharing. */ + id.regimplify = false; + id.do_not_unshare = true; + + /* We're not inside any EH region. */ + id.eh_lp_nr = 0; + + t = copy_tree_body (&id); + pointer_map_destroy (decl_map); + + /* We can only return something suitable for use in a GENERIC + expression tree. */ + if (TREE_CODE (t) == MODIFY_EXPR) + return TREE_OPERAND (t, 1); + } + + return NULL_TREE; +} + /* Duplicate a type, fields and all. */ tree @@ -4561,11 +5068,14 @@ build_duplicate_type (tree type) id.dst_fn = current_function_decl; id.src_cfun = cfun; id.decl_map = pointer_map_create (); + id.debug_map = NULL; id.copy_decl = copy_decl_no_change; type = remap_type_1 (type, &id); pointer_map_destroy (id.decl_map); + if (id.debug_map) + pointer_map_destroy (id.debug_map); TYPE_CANONICAL (type) = type; @@ -4573,9 +5083,10 @@ build_duplicate_type (tree type) } /* Return whether it is safe to inline a function because it used different - target specific options or different optimization options. */ + target specific options or call site actual types mismatch parameter types. + E is the call edge to be checked. */ bool -tree_can_inline_p (tree caller, tree callee) +tree_can_inline_p (struct cgraph_edge *e) { #if 0 /* This causes a regression in SPEC in that it prevents a cold function from @@ -4604,7 +5115,40 @@ tree_can_inline_p (tree caller, tree callee) return false; } #endif + tree caller, callee; + + 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))) + { + e->inline_failed = CIF_UNSPECIFIED; + gimple_call_set_cannot_inline (e->call_stmt, true); + return false; + } /* Allow the backend to decide if inlining is ok. */ - return targetm.target_option.can_inline_p (caller, callee); + if (!targetm.target_option.can_inline_p (caller, callee)) + { + e->inline_failed = CIF_TARGET_OPTION_MISMATCH; + gimple_call_set_cannot_inline (e->call_stmt, true); + e->call_stmt_cannot_inline_p = true; + return false; + } + + if (e->call_stmt + && !gimple_check_call_args (e->call_stmt)) + { + e->inline_failed = CIF_MISMATCHED_ARGUMENTS; + gimple_call_set_cannot_inline (e->call_stmt, true); + e->call_stmt_cannot_inline_p = true; + return false; + } + + return true; }