From: hubicka Date: Sun, 29 Nov 2009 10:32:08 +0000 (+0000) Subject: * cgraph.c (same_body_alias_1): Break out of X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=commitdiff_plain;h=284545178e8dd05756e5b236cf3b47834b87abf2 * cgraph.c (same_body_alias_1): Break out of (same_body_alias): ... here; remove comdat check; it is handled in cp already. (cgraph_add_thunk): New. (dump_cgraph_node): Dump aliases and thunks. * cgraph.h (cgraph_thunk_info): New structure. (struct cgraph_node): Add thunk info. (cgraph_add_thunk): New. * cgraphunit.c (cgraph_emit_thunks): Remove. (cgraph_finalize_compilation_unit): Do not call cgraph_emit_thunks. (assemble_thunk): New function. (cgraph_expand_function): Handle thunks. (thunk_adjust): New. (init_lowered_empty_function): New. * optimize.c (maybe_clone_body): Emit thunks associated to alias. * Make-lang.in (method.o): Add dependency on gimple.h. * method.c: Include gimple.h (make_alias_for_thunk): Use same body alias instead of assemble_alias. (use_thunk): Drop codegen; use cgraph_add_thunk; gimplify generic thunks. * semantics.c (expand_or_defer_fn): Emit associated thunks. * cp-objcp-common.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove. * lto-cgraph.c (lto_output_node): Stream thunk info. (input_node): Likewise. * langhooks.h (lang_hooks_for_callgraph): Remove emit_associated_thunks. * langhooks-def.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove. (LANG_HOOKS_CALLGRAPH_INITIALIZER): Update. * i386.c (x86_output_mi_thunk): Make output prettier. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@154736 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e68cf959468..aedf2aef8e8 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2009-11-28 Jan Hubicka + + * cgraph.c (same_body_alias_1): Break out of + (same_body_alias): ... here; remove comdat check; it is handled + in cp already. + (cgraph_add_thunk): New. + (dump_cgraph_node): Dump aliases and thunks. + * cgraph.h (cgraph_thunk_info): New structure. + (struct cgraph_node): Add thunk info. + (cgraph_add_thunk): New. + * cgraphunit.c (cgraph_emit_thunks): Remove. + (cgraph_finalize_compilation_unit): Do not call cgraph_emit_thunks. + (assemble_thunk): New function. + (cgraph_expand_function): Handle thunks. + (thunk_adjust): New. + (init_lowered_empty_function): New. + * cp-objcp-common.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove. + * lto-cgraph.c (lto_output_node): Stream thunk info. + (input_node): Likewise. + * langhooks.h (lang_hooks_for_callgraph): Remove emit_associated_thunks. + * langhooks-def.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove. + (LANG_HOOKS_CALLGRAPH_INITIALIZER): Update. + * i386.c (x86_output_mi_thunk): Make output prettier. + 2009-11-28 Richard Guenther PR tree-optimization/42183 diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 8fd5e7fbf08..651618c4232 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -85,6 +85,7 @@ The callgraph: #include "tree-flow.h" #include "value-prof.h" #include "except.h" +#include "diagnostic.h" static void cgraph_node_remove_callers (struct cgraph_node *node); static inline void cgraph_edge_remove_caller (struct cgraph_edge *e); @@ -507,29 +508,15 @@ cgraph_node (tree decl) return node; } -/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful. - Same body aliases are output whenever the body of DECL is output, - and cgraph_node (ALIAS) transparently returns cgraph_node (DECL). */ +/* Mark ALIAS as an alias to DECL. */ -bool -cgraph_same_body_alias (tree alias, tree decl) +static struct cgraph_node * +cgraph_same_body_alias_1 (tree alias, tree decl) { struct cgraph_node key, *alias_node, *decl_node, **slot; gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); gcc_assert (TREE_CODE (alias) == FUNCTION_DECL); - gcc_assert (!assembler_name_hash); - -#ifndef ASM_OUTPUT_DEF - /* If aliases aren't supported by the assembler, fail. */ - return false; -#endif - - /* Comdat same body aliases are only supported when comdat groups - are supported and the symbols are weak. */ - if (DECL_ONE_ONLY (decl) && (!HAVE_COMDAT_GROUP || !DECL_WEAK (decl))) - return false; - decl_node = cgraph_node (decl); key.decl = alias; @@ -538,7 +525,7 @@ cgraph_same_body_alias (tree alias, tree decl) /* If the cgraph_node has been already created, fail. */ if (*slot) - return false; + return NULL; alias_node = cgraph_allocate_node (); alias_node->decl = alias; @@ -548,9 +535,56 @@ cgraph_same_body_alias (tree alias, tree decl) if (decl_node->same_body) decl_node->same_body->previous = alias_node; alias_node->next = decl_node->same_body; + alias_node->thunk.alias = decl; decl_node->same_body = alias_node; *slot = alias_node; - return true; + return alias_node; +} + +/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful. + Same body aliases are output whenever the body of DECL is output, + and cgraph_node (ALIAS) transparently returns cgraph_node (DECL). */ + +bool +cgraph_same_body_alias (tree alias, tree decl) +{ +#ifndef ASM_OUTPUT_DEF + /* If aliases aren't supported by the assembler, fail. */ + return false; +#endif + + /*gcc_assert (!assembler_name_hash);*/ + + return cgraph_same_body_alias_1 (alias, decl) != NULL; +} + +void +cgraph_add_thunk (tree alias, tree decl, bool this_adjusting, + HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value, + tree virtual_offset, + tree real_alias) +{ + struct cgraph_node *node = cgraph_get_node (alias); + + if (node) + { + gcc_assert (node->local.finalized); + gcc_assert (!node->same_body); + cgraph_remove_node (node); + } + + node = cgraph_same_body_alias_1 (alias, decl); + gcc_assert (node); +#ifdef ENABLE_CHECKING + gcc_assert (!virtual_offset + || tree_int_cst_equal (virtual_offset, size_int (virtual_value))); +#endif + node->thunk.fixed_offset = fixed_offset; + node->thunk.this_adjusting = this_adjusting; + node->thunk.virtual_value = virtual_value; + node->thunk.virtual_offset_p = virtual_offset != NULL; + node->thunk.alias = real_alias; + node->thunk.thunk_p = true; } /* Returns the cgraph node assigned to DECL or NULL if no cgraph node @@ -1646,6 +1680,27 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) fprintf(f, "(can throw external) "); } fprintf (f, "\n"); + + if (node->same_body) + { + struct cgraph_node *n; + fprintf (f, " aliases & thunks:"); + for (n = node->same_body; n; n = n->next) + { + fprintf (f, " %s/%i", cgraph_node_name (n), n->uid); + if (n->thunk.thunk_p) + { + fprintf (f, " (thunk of %s fixed ofset %i virtual value %i has " + "virtual offset %i", + lang_hooks.decl_printable_name (n->thunk.alias, 2), + (int)n->thunk.fixed_offset, + (int)n->thunk.virtual_value, + (int)n->thunk.virtual_offset_p); + fprintf (f, ")"); + } + } + fprintf (f, "\n"); + } } diff --git a/gcc/cgraph.h b/gcc/cgraph.h index a683c5bc970..1017176ff3f 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -69,6 +69,19 @@ struct GTY(()) inline_summary int time_inlining_benefit; }; +/* Information about thunk, used only for same body aliases. */ + +struct GTY(()) cgraph_thunk_info { + /* Information about the thunk. */ + HOST_WIDE_INT fixed_offset; + HOST_WIDE_INT virtual_value; + tree alias; + bool this_adjusting; + bool virtual_offset_p; + /* Set to true when alias node is thunk. */ + bool thunk_p; +}; + /* Information about the function collected locally. Available after function is analyzed. */ @@ -184,8 +197,8 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node { struct cgraph_node *prev_sibling_clone; struct cgraph_node *clones; struct cgraph_node *clone_of; - /* For normal nodes pointer to the list of alias nodes, in alias - nodes pointer to the normal node. */ + /* For normal nodes pointer to the list of alias and thunk nodes, + in alias/thunk nodes pointer to the normal node. */ struct cgraph_node *same_body; /* For functions with many calls sites it holds map from call expression to the edge to speed up cgraph_edge function. */ @@ -202,6 +215,7 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node { struct cgraph_global_info global; struct cgraph_rtl_info rtl; struct cgraph_clone_info clone; + struct cgraph_thunk_info thunk; /* Expected number of executions: calculated in profile.c. */ gcov_type count; @@ -244,8 +258,8 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node { unsigned alias : 1; /* Set for nodes that was constructed and finalized by frontend. */ unsigned finalized_by_frontend : 1; - /* Set for alias nodes, same_body points to the node they are alias of - and they are linked through the next/previous pointers. */ + /* Set for alias and thunk nodes, same_body points to the node they are alias + of and they are linked through the next/previous pointers. */ unsigned same_body_alias : 1; }; @@ -423,6 +437,7 @@ struct cgraph_edge *cgraph_create_edge (struct cgraph_node *, struct cgraph_node * cgraph_get_node (tree); struct cgraph_node *cgraph_node (tree); bool cgraph_same_body_alias (tree, tree); +void cgraph_add_thunk (tree, tree, bool, HOST_WIDE_INT, HOST_WIDE_INT, tree, tree); void cgraph_remove_same_body_alias (struct cgraph_node *); struct cgraph_node *cgraph_node_for_asm (tree); struct cgraph_edge *cgraph_edge (struct cgraph_node *, gimple); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 4a4e6a4b6e3..e3825433d87 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -149,6 +149,9 @@ static GTY (()) VEC(tree, gc) *static_ctors; /* A vector of FUNCTION_DECLs declared as static destructors. */ static GTY (()) VEC(tree, gc) *static_dtors; +/* Used for vtable lookup in thunk adjusting. */ +static GTY (()) tree vtable_entry_type; + /* When target does not have ctors and dtors, we call all constructor and destructor by special initialization/destruction function recognized by collect2. @@ -741,8 +744,13 @@ verify_cgraph_node (struct cgraph_node *node) debug_gimple_stmt (stmt); error_found = true; } - if (!clone_of_p (cgraph_node (decl), e->callee) - && !e->callee->global.inlined_to) + if (e->callee->same_body_alias) + { + error ("edge points to same body alias:"); + debug_tree (e->callee->decl); + } + else if (!clone_of_p (cgraph_node (decl), e->callee) + && !e->callee->global.inlined_to) { error ("edge points to wrong declaration:"); debug_tree (e->callee->decl); @@ -1034,35 +1042,6 @@ cgraph_analyze_functions (void) } -/* Emit thunks for every node in the cgraph. - FIXME: We really ought to emit thunks only for functions that are needed. */ - -static void -cgraph_emit_thunks (void) -{ - struct cgraph_node *n, *alias; - - for (n = cgraph_nodes; n; n = n->next) - { - /* Only emit thunks on functions defined in this TU. - Note that this may emit more thunks than strictly necessary. - During optimization some nodes may disappear. It would be - nice to only emit thunks only for the functions that will be - emitted, but we cannot know that until the inliner and other - IPA passes have run (see the sequencing of the call to - cgraph_mark_functions_to_output in cgraph_optimize). */ - if (n->reachable - && !DECL_EXTERNAL (n->decl)) - { - lang_hooks.callgraph.emit_associated_thunks (n->decl); - for (alias = n->same_body; alias; alias = alias->next) - if (!DECL_EXTERNAL (alias->decl)) - lang_hooks.callgraph.emit_associated_thunks (alias->decl); - } - } -} - - /* Analyze the whole compilation unit once it is parsed completely. */ void @@ -1093,10 +1072,6 @@ cgraph_finalize_compilation_unit (void) remove unreachable nodes. */ cgraph_analyze_functions (); - /* Emit thunks for reachable nodes, if needed. */ - if (lang_hooks.callgraph.emit_associated_thunks) - cgraph_emit_thunks (); - /* Mark alias targets necessary and emit diagnostics. */ finish_aliases_1 (); @@ -1159,6 +1134,347 @@ cgraph_mark_functions_to_output (void) } } +/* DECL is FUNCTION_DECL. Initialize datastructures so DECL is a function + in lowered gimple form. + + Set current_function_decl and cfun to newly constructed empty function body. + return basic block in the function body. */ + +static basic_block +init_lowered_empty_function (tree decl) +{ + basic_block bb; + + current_function_decl = decl; + allocate_struct_function (decl, false); + gimple_register_cfg_hooks (); + init_empty_tree_cfg (); + init_tree_ssa (cfun); + init_ssa_operands (); + cfun->gimple_df->in_ssa_p = true; + DECL_INITIAL (decl) = make_node (BLOCK); + + DECL_SAVED_TREE (decl) = error_mark_node; + cfun->curr_properties |= + (PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_referenced_vars | + PROP_ssa); + + /* Create BB for body of the function and connect it properly. */ + bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR); + make_edge (ENTRY_BLOCK_PTR, bb, 0); + make_edge (bb, EXIT_BLOCK_PTR, 0); + + return bb; +} + +/* Adjust PTR by the constant FIXED_OFFSET, and by the vtable + offset indicated by VIRTUAL_OFFSET, if that is + non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and + zero for a result adjusting thunk. */ + +static tree +thunk_adjust (gimple_stmt_iterator * bsi, + tree ptr, bool this_adjusting, + HOST_WIDE_INT fixed_offset, tree virtual_offset) +{ + gimple stmt; + tree ret; + + if (this_adjusting) + { + stmt = gimple_build_assign (ptr, + fold_build2_loc (input_location, + POINTER_PLUS_EXPR, + TREE_TYPE (ptr), ptr, + size_int (fixed_offset))); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + } + + /* If there's a virtual offset, look up that value in the vtable and + adjust the pointer again. */ + if (virtual_offset) + { + tree vtabletmp; + tree vtabletmp2; + tree vtabletmp3; + tree offsettmp; + + if (!vtable_entry_type) + { + tree vfunc_type = make_node (FUNCTION_TYPE); + TREE_TYPE (vfunc_type) = integer_type_node; + TYPE_ARG_TYPES (vfunc_type) = NULL_TREE; + layout_type (vfunc_type); + + vtable_entry_type = build_pointer_type (vfunc_type); + } + + vtabletmp = + create_tmp_var (build_pointer_type + (build_pointer_type (vtable_entry_type)), "vptr"); + + /* The vptr is always at offset zero in the object. */ + stmt = gimple_build_assign (vtabletmp, + build1 (NOP_EXPR, TREE_TYPE (vtabletmp), + ptr)); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + mark_symbols_for_renaming (stmt); + find_referenced_vars_in (stmt); + + /* Form the vtable address. */ + vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)), + "vtableaddr"); + stmt = gimple_build_assign (vtabletmp2, + build1 (INDIRECT_REF, + TREE_TYPE (vtabletmp2), vtabletmp)); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + mark_symbols_for_renaming (stmt); + find_referenced_vars_in (stmt); + + /* Find the entry with the vcall offset. */ + stmt = gimple_build_assign (vtabletmp2, + fold_build2_loc (input_location, + POINTER_PLUS_EXPR, + TREE_TYPE (vtabletmp2), + vtabletmp2, + fold_convert (sizetype, + virtual_offset))); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + + /* Get the offset itself. */ + vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)), + "vcalloffset"); + stmt = gimple_build_assign (vtabletmp3, + build1 (INDIRECT_REF, + TREE_TYPE (vtabletmp3), + vtabletmp2)); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + mark_symbols_for_renaming (stmt); + find_referenced_vars_in (stmt); + + /* Cast to sizetype. */ + offsettmp = create_tmp_var (sizetype, "offset"); + stmt = gimple_build_assign (offsettmp, fold_convert (sizetype, vtabletmp3)); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + mark_symbols_for_renaming (stmt); + find_referenced_vars_in (stmt); + + /* Adjust the `this' pointer. */ + ptr = fold_build2_loc (input_location, + POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, + offsettmp); + } + + if (!this_adjusting) + /* Adjust the pointer by the constant. */ + { + tree ptrtmp; + + if (TREE_CODE (ptr) == VAR_DECL) + ptrtmp = ptr; + else + { + ptrtmp = create_tmp_var (TREE_TYPE (ptr), "ptr"); + stmt = gimple_build_assign (ptrtmp, ptr); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + mark_symbols_for_renaming (stmt); + find_referenced_vars_in (stmt); + } + ptr = fold_build2_loc (input_location, + POINTER_PLUS_EXPR, TREE_TYPE (ptrtmp), ptrtmp, + size_int (fixed_offset)); + } + + /* Emit the statement and gimplify the adjustment expression. */ + ret = create_tmp_var (TREE_TYPE (ptr), "adjusted_this"); + stmt = gimple_build_assign (ret, ptr); + mark_symbols_for_renaming (stmt); + find_referenced_vars_in (stmt); + gsi_insert_after (bsi, stmt, GSI_NEW_STMT); + + return ret; +} + +/* Produce assembler for thunk NODE. */ + +static void +assemble_thunk (struct cgraph_node *node) +{ + bool this_adjusting = node->thunk.this_adjusting; + HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset; + HOST_WIDE_INT virtual_value = node->thunk.virtual_value; + tree virtual_offset = NULL; + tree alias = node->thunk.alias; + tree thunk_fndecl = node->decl; + tree a = DECL_ARGUMENTS (thunk_fndecl); + + current_function_decl = thunk_fndecl; + + if (this_adjusting + && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset, + virtual_value, alias)) + { + const char *fnname; + tree fn_block; + + DECL_RESULT (thunk_fndecl) + = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl), + RESULT_DECL, 0, integer_type_node); + fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl)); + + /* The back end expects DECL_INITIAL to contain a BLOCK, so we + create one. */ + fn_block = make_node (BLOCK); + BLOCK_VARS (fn_block) = a; + DECL_INITIAL (thunk_fndecl) = fn_block; + init_function_start (thunk_fndecl); + cfun->is_thunk = 1; + assemble_start_function (thunk_fndecl, fnname); + + targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl, + fixed_offset, virtual_value, alias); + + assemble_end_function (thunk_fndecl, fnname); + init_insn_lengths (); + free_after_compilation (cfun); + set_cfun (NULL); + TREE_ASM_WRITTEN (thunk_fndecl) = 1; + } + else + { + tree restype; + basic_block bb, then_bb, else_bb, return_bb; + gimple_stmt_iterator bsi; + int nargs = 0; + tree arg; + int i; + tree resdecl; + tree restmp = NULL; + VEC(tree, heap) *vargs; + + gimple call; + gimple ret; + + DECL_IGNORED_P (thunk_fndecl) = 1; + bitmap_obstack_initialize (NULL); + + if (node->thunk.virtual_offset_p) + virtual_offset = size_int (virtual_value); + + /* Build the return declaration for the function. */ + restype = TREE_TYPE (TREE_TYPE (thunk_fndecl)); + if (DECL_RESULT (thunk_fndecl) == NULL_TREE) + { + resdecl = build_decl (input_location, RESULT_DECL, 0, restype); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + DECL_RESULT (thunk_fndecl) = resdecl; + } + else + resdecl = DECL_RESULT (thunk_fndecl); + + bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl); + + bsi = gsi_start_bb (bb); + + /* Build call to the function being thunked. */ + if (!VOID_TYPE_P (restype)) + { + if (!is_gimple_reg_type (restype)) + { + restmp = resdecl; + cfun->local_decls = tree_cons (NULL_TREE, restmp, cfun->local_decls); + BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp; + } + else + restmp = create_tmp_var_raw (restype, "retval"); + } + + for (arg = a; arg; arg = TREE_CHAIN (arg)) + nargs++; + vargs = VEC_alloc (tree, heap, nargs); + if (this_adjusting) + VEC_quick_push (tree, vargs, + thunk_adjust (&bsi, + a, 1, fixed_offset, + virtual_offset)); + else + VEC_quick_push (tree, vargs, a); + for (i = 1, arg = TREE_CHAIN (a); i < nargs; i++, arg = TREE_CHAIN (arg)) + VEC_quick_push (tree, vargs, arg); + call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs); + VEC_free (tree, heap, vargs); + gimple_call_set_cannot_inline (call, true); + gimple_call_set_from_thunk (call, true); + if (restmp) + gimple_call_set_lhs (call, restmp); + gsi_insert_after (&bsi, call, GSI_NEW_STMT); + mark_symbols_for_renaming (call); + find_referenced_vars_in (call); + update_stmt (call); + + if (restmp && !this_adjusting) + { + tree true_label = NULL_TREE, false_label = NULL_TREE; + + if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE) + { + gimple stmt; + /* If the return type is a pointer, we need to + protect against NULL. We know there will be an + adjustment, because that's why we're emitting a + thunk. */ + then_bb = create_basic_block (NULL, (void *) 0, bb); + return_bb = create_basic_block (NULL, (void *) 0, then_bb); + else_bb = create_basic_block (NULL, (void *) 0, else_bb); + remove_edge (single_succ_edge (bb)); + true_label = gimple_block_label (then_bb); + false_label = gimple_block_label (else_bb); + stmt = gimple_build_cond (NE_EXPR, restmp, + fold_convert (TREE_TYPE (restmp), + integer_zero_node), + NULL_TREE, NULL_TREE); + gsi_insert_after (&bsi, stmt, GSI_NEW_STMT); + make_edge (bb, then_bb, EDGE_TRUE_VALUE); + make_edge (bb, else_bb, EDGE_FALSE_VALUE); + make_edge (return_bb, EXIT_BLOCK_PTR, 0); + make_edge (then_bb, return_bb, EDGE_FALLTHRU); + make_edge (else_bb, return_bb, EDGE_FALLTHRU); + bsi = gsi_last_bb (then_bb); + } + + restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0, + fixed_offset, virtual_offset); + if (true_label) + { + gimple stmt; + bsi = gsi_last_bb (else_bb); + stmt = gimple_build_assign (restmp, fold_convert (TREE_TYPE (restmp), + integer_zero_node)); + gsi_insert_after (&bsi, stmt, GSI_NEW_STMT); + bsi = gsi_last_bb (return_bb); + } + } + else + gimple_call_set_tail (call, true); + + /* Build return value. */ + ret = gimple_build_return (restmp); + gsi_insert_after (&bsi, ret, GSI_NEW_STMT); + + delete_unreachable_blocks (); + update_ssa (TODO_update_ssa); + + cgraph_remove_same_body_alias (node); + /* Since we want to emit the thunk, we explicitly mark its name as + referenced. */ + mark_decl_referenced (thunk_fndecl); + cgraph_add_new_function (thunk_fndecl, true); + bitmap_obstack_release (NULL); + } + current_function_decl = NULL; +} + /* Expand function specified by NODE. */ static void @@ -1182,10 +1498,22 @@ cgraph_expand_function (struct cgraph_node *node) current_function_decl = NULL; if (node->same_body) { - struct cgraph_node *alias; + struct cgraph_node *alias, *next; bool saved_alias = node->alias; - for (alias = node->same_body; alias; alias = alias->next) - assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl)); + for (alias = node->same_body; + alias && alias->next; alias = alias->next) + ; + /* Walk aliases in the order they were created; it is possible that + thunks reffers to the aliases made earlier. */ + for (; alias; alias = next) + { + next = alias->previous; + if (!alias->thunk.thunk_p) + assemble_alias (alias->decl, + DECL_ASSEMBLER_NAME (alias->thunk.alias)); + else + assemble_thunk (alias); + } node->alias = saved_alias; } gcc_assert (!cgraph_preserve_function_body_p (decl)); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 443b4eb80b3..37fe24f6798 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -26002,7 +26002,10 @@ x86_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED, /* Adjust the this parameter by a fixed constant. */ if (delta) { - xops[0] = GEN_INT (delta); + /* Make things pretty and `subl $4,%eax' rather than `addl $-4,%eax'. + Exceptions: -128 encodes smaller than 128, so swap sign and op. */ + bool sub = delta < 0 || delta == 128; + xops[0] = GEN_INT (sub ? -delta : delta); xops[1] = this_reg ? this_reg : this_param; if (TARGET_64BIT) { @@ -26014,8 +26017,13 @@ x86_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED, xops[0] = tmp; xops[1] = this_param; } - output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops); + if (sub) + output_asm_insn ("sub{q}\t{%0, %1|%1, %0}", xops); + else + output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops); } + else if (sub) + output_asm_insn ("sub{l}\t{%0, %1|%1, %0}", xops); else output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops); } diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 4f5845305fe..ab252ea9804 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,13 @@ +2009-11-29 Jan Hubicka + + * optimize.c (maybe_clone_body): Emit thunks associated to alias. + * Make-lang.in (method.o): Add dependency on gimple.h. + * method.c: Include gimple.h + (make_alias_for_thunk): Use same body alias instead of assemble_alias. + (use_thunk): Drop codegen; use cgraph_add_thunk; gimplify + generic thunks. + * semantics.c (expand_or_defer_fn): Emit associated thunks. + 2009-11-28 Dodji Seketeli PR c++/36408 diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index 6e102960da0..ef44201bb95 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -275,7 +275,7 @@ cp/friend.o: cp/friend.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) toplev.h \ cp/init.o: cp/init.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) $(EXPR_H) \ toplev.h except.h $(TARGET_H) cp/method.o: cp/method.c $(CXX_TREE_H) $(TM_H) toplev.h $(RTL_H) $(EXPR_H) \ - $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h + $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h $(GIMPLE_H) cp/cvt.o: cp/cvt.c $(CXX_TREE_H) $(TM_H) cp/decl.h $(FLAGS_H) toplev.h \ convert.h $(TARGET_H) cp/search.o: cp/search.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h $(RTL_H) diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h index 8f0cdc324e4..1a97ad2b119 100644 --- a/gcc/cp/cp-objcp-common.h +++ b/gcc/cp/cp-objcp-common.h @@ -104,8 +104,6 @@ extern bool cp_function_decl_explicit_p (tree decl); #undef LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR cxx_callgraph_analyze_expr -#undef LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS -#define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS emit_associated_thunks #undef LANG_HOOKS_MAKE_TYPE #define LANG_HOOKS_MAKE_TYPE cxx_make_type diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 47f9e424dbd..cda7ed3b863 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "diagnostic.h" #include "cgraph.h" +#include "gimple.h" /* Various flags to control the mangling process. */ @@ -58,7 +59,6 @@ enum mangling_flags typedef enum mangling_flags mangling_flags; -static tree thunk_adjust (tree, bool, HOST_WIDE_INT, tree); static void do_build_assign_ref (tree); static void do_build_copy_constructor (tree); static tree synthesize_exception_spec (tree, tree (*) (tree, void *), void *); @@ -205,56 +205,6 @@ finish_thunk (tree thunk) SET_DECL_ASSEMBLER_NAME (thunk, name); } -/* Adjust PTR by the constant FIXED_OFFSET, and by the vtable - offset indicated by VIRTUAL_OFFSET, if that is - non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and - zero for a result adjusting thunk. */ - -static tree -thunk_adjust (tree ptr, bool this_adjusting, - HOST_WIDE_INT fixed_offset, tree virtual_offset) -{ - if (this_adjusting) - /* Adjust the pointer by the constant. */ - ptr = fold_build2_loc (input_location, - POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, - size_int (fixed_offset)); - - /* If there's a virtual offset, look up that value in the vtable and - adjust the pointer again. */ - if (virtual_offset) - { - tree vtable; - - ptr = save_expr (ptr); - /* The vptr is always at offset zero in the object. */ - vtable = build1 (NOP_EXPR, - build_pointer_type (build_pointer_type - (vtable_entry_type)), - ptr); - /* Form the vtable address. */ - vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable); - /* Find the entry with the vcall offset. */ - vtable = fold_build2_loc (input_location, - POINTER_PLUS_EXPR, TREE_TYPE (vtable), vtable, - fold_convert (sizetype, virtual_offset)); - /* Get the offset itself. */ - vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable); - /* Adjust the `this' pointer. */ - ptr = fold_build2_loc (input_location, - POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, - fold_convert (sizetype, vtable)); - } - - if (!this_adjusting) - /* Adjust the pointer by the constant. */ - ptr = fold_build2_loc (input_location, - POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, - size_int (fixed_offset)); - - return ptr; -} - static GTY (()) int thunk_labelno; /* Create a static alias to function. */ @@ -303,7 +253,11 @@ make_alias_for_thunk (tree function) alias = make_alias_for (function, get_identifier (buf)); if (!flag_syntax_only) - assemble_alias (alias, DECL_ASSEMBLER_NAME (function)); + { + bool ok = cgraph_same_body_alias (alias, function); + DECL_ASSEMBLER_NAME (function); + gcc_assert (ok); + } return alias; } @@ -416,42 +370,15 @@ use_thunk (tree thunk_fndecl, bool emit_p) } a = nreverse (t); DECL_ARGUMENTS (thunk_fndecl) = a; - - if (this_adjusting - && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset, - virtual_value, alias)) - { - const char *fnname; - tree fn_block; - - current_function_decl = thunk_fndecl; - DECL_RESULT (thunk_fndecl) - = build_decl (DECL_SOURCE_LOCATION (thunk_fndecl), - RESULT_DECL, 0, integer_type_node); - fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl)); - /* The back end expects DECL_INITIAL to contain a BLOCK, so we - create one. */ - fn_block = make_node (BLOCK); - BLOCK_VARS (fn_block) = a; - DECL_INITIAL (thunk_fndecl) = fn_block; - init_function_start (thunk_fndecl); - cfun->is_thunk = 1; - assemble_start_function (thunk_fndecl, fnname); - - targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl, - fixed_offset, virtual_value, alias); - - assemble_end_function (thunk_fndecl, fnname); - init_insn_lengths (); - free_after_compilation (cfun); - current_function_decl = 0; - set_cfun (NULL); - TREE_ASM_WRITTEN (thunk_fndecl) = 1; - } - else + TREE_ASM_WRITTEN (thunk_fndecl) = 1; + cgraph_add_thunk (thunk_fndecl, function, + this_adjusting, fixed_offset, virtual_value, + virtual_offset, alias); + + if (!this_adjusting + || !targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset, + virtual_value, alias)) { - int i; - tree *argarray = (tree *) alloca (list_length (a) * sizeof (tree)); /* If this is a covariant thunk, or we don't have the necessary code for efficient thunks, generate a thunk function that just makes a call to the real function. Unfortunately, this @@ -460,69 +387,6 @@ use_thunk (tree thunk_fndecl, bool emit_p) if (varargs_function_p (function)) error ("generic thunk code fails for method %q#D which uses %<...%>", function); - - DECL_RESULT (thunk_fndecl) = NULL_TREE; - - start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED); - /* We don't bother with a body block for thunks. */ - - /* There's no need to check accessibility inside the thunk body. */ - push_deferring_access_checks (dk_no_check); - - t = a; - if (this_adjusting) - t = thunk_adjust (t, /*this_adjusting=*/1, - fixed_offset, virtual_offset); - - /* Build up the call to the real function. */ - argarray[0] = t; - for (i = 1, a = TREE_CHAIN (a); a; a = TREE_CHAIN (a), i++) - argarray[i] = a; - t = build_call_a (alias, i, argarray); - CALL_FROM_THUNK_P (t) = 1; - CALL_CANNOT_INLINE_P (t) = 1; - - if (VOID_TYPE_P (TREE_TYPE (t))) - finish_expr_stmt (t); - else - { - if (!this_adjusting) - { - tree cond = NULL_TREE; - - if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE) - { - /* If the return type is a pointer, we need to - protect against NULL. We know there will be an - adjustment, because that's why we're emitting a - thunk. */ - t = save_expr (t); - cond = cp_convert (boolean_type_node, t); - } - - t = thunk_adjust (t, /*this_adjusting=*/0, - fixed_offset, virtual_offset); - if (cond) - t = build3 (COND_EXPR, TREE_TYPE (t), cond, t, - cp_convert (TREE_TYPE (t), integer_zero_node)); - } - if (MAYBE_CLASS_TYPE_P (TREE_TYPE (t))) - t = build_cplus_new (TREE_TYPE (t), t); - finish_return_stmt (t); - } - - /* Since we want to emit the thunk, we explicitly mark its name as - referenced. */ - mark_decl_referenced (thunk_fndecl); - - /* But we don't want debugging information about it. */ - DECL_IGNORED_P (thunk_fndecl) = 1; - - /* Re-enable access control. */ - pop_deferring_access_checks (); - - thunk_fndecl = finish_function (0); - cgraph_add_new_function (thunk_fndecl, false); } pop_from_top_level (); diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c index c691f0b6a0b..838a7305a71 100644 --- a/gcc/cp/optimize.c +++ b/gcc/cp/optimize.c @@ -250,7 +250,10 @@ maybe_clone_body (tree fn) && DECL_INTERFACE_KNOWN (fns[0]) && !DECL_ONE_ONLY (fns[0]) && cgraph_same_body_alias (clone, fns[0])) - alias = true; + { + alias = true; + emit_associated_thunks (clone); + } /* Build the delete destructor by calling complete destructor and delete function. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 5253df85ff9..0a1a547d13d 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3445,6 +3445,7 @@ expand_or_defer_fn (tree fn) /* Expand or defer, at the whim of the compilation unit manager. */ cgraph_finalize_function (fn, function_depth > 1); + emit_associated_thunks (fn); function_depth--; } diff --git a/gcc/ira.c b/gcc/ira.c index 7165caa1b18..e5a6171f067 100644 --- a/gcc/ira.c +++ b/gcc/ira.c @@ -3172,6 +3172,7 @@ ira (FILE *f) ira_assert (current_loops == NULL); flow_loops_find (&ira_loops); + record_loop_exits (); current_loops = &ira_loops; if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL) @@ -3215,6 +3216,7 @@ ira (FILE *f) df_analyze (); flow_loops_find (&ira_loops); + record_loop_exits (); current_loops = &ira_loops; setup_allocno_assignment_flags (); diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h index 4f01483846b..f6ef3f10fc8 100644 --- a/gcc/langhooks-def.h +++ b/gcc/langhooks-def.h @@ -126,11 +126,9 @@ extern void lhd_omp_firstprivatize_type_sizes (struct gimplify_omp_ctx *, } #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr -#define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS NULL #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \ - LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR, \ - LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS, \ + LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR \ } /* Hooks for tree gimplification. */ diff --git a/gcc/langhooks.h b/gcc/langhooks.h index 81f24366234..faac14af859 100644 --- a/gcc/langhooks.h +++ b/gcc/langhooks.h @@ -49,9 +49,6 @@ struct lang_hooks_for_callgraph /* The node passed is a language-specific tree node. If its contents are relevant to use of other declarations, mark them. */ tree (*analyze_expr) (tree *, int *); - - /* Emit thunks associated to function. */ - void (*emit_associated_thunks) (tree); }; /* The following hooks are used by tree-dump.c. */ diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c index 0f77df1a145..309a1e6e964 100644 --- a/gcc/lto-cgraph.c +++ b/gcc/lto-cgraph.c @@ -317,6 +317,21 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, { lto_output_fn_decl_index (ob->decl_state, ob->main_stream, alias->decl); + if (alias->thunk.thunk_p) + { + lto_output_uleb128_stream + (ob->main_stream, + 1 + (alias->thunk.this_adjusting != 0) * 2 + + (alias->thunk.virtual_offset_p != 0) * 4); + lto_output_uleb128_stream (ob->main_stream, + alias->thunk.fixed_offset); + lto_output_uleb128_stream (ob->main_stream, + alias->thunk.virtual_value); + lto_output_fn_decl_index (ob->decl_state, ob->main_stream, + alias->thunk.alias); + } + else + lto_output_uleb128_stream (ob->main_stream, 0); alias = alias->previous; } while (alias); @@ -575,9 +590,24 @@ input_node (struct lto_file_decl_data *file_data, while (same_body_count-- > 0) { tree alias_decl; + int type; decl_index = lto_input_uleb128 (ib); alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index); - cgraph_same_body_alias (alias_decl, fn_decl); + type = lto_input_uleb128 (ib); + if (!type) + cgraph_same_body_alias (alias_decl, fn_decl); + else + { + HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib); + HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib); + tree real_alias; + decl_index = lto_input_uleb128 (ib); + real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index); + cgraph_add_thunk (alias_decl, fn_decl, type & 2, fixed_offset, + virtual_value, + (type & 4) ? size_int (virtual_value) : NULL_TREE, + real_alias); + } } return node; } diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index a70b5b1cbea..10baf62b0c0 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -3832,10 +3832,6 @@ 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;