aboutsummaryrefslogtreecommitdiff
path: root/gcc/cgraphunit.c
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2009-11-29 11:32:08 +0100
committerJan Hubicka <hubicka@gcc.gnu.org>2009-11-29 10:32:08 +0000
commit6744a6abc335d55f98ba226f4ff169a55cf3fb94 (patch)
tree2260e6f71c9022e13476588b282bc487939fa1e4 /gcc/cgraphunit.c
parente55690913ecef32cb80d5f169a9aa50fd5ae4a98 (diff)
downloadgcc-6744a6abc335d55f98ba226f4ff169a55cf3fb94.zip
gcc-6744a6abc335d55f98ba226f4ff169a55cf3fb94.tar.gz
gcc-6744a6abc335d55f98ba226f4ff169a55cf3fb94.tar.bz2
cgraph.c (same_body_alias_1): Break out of
* 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. From-SVN: r154736
Diffstat (limited to 'gcc/cgraphunit.c')
-rw-r--r--gcc/cgraphunit.c404
1 files changed, 366 insertions, 38 deletions
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 4a4e6a4..e382543 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));