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
This commit is contained in:
Jan Hubicka 2009-11-29 11:32:08 +01:00 committed by Jan Hubicka
parent e55690913e
commit 6744a6abc3
16 changed files with 556 additions and 227 deletions

View File

@ -1,3 +1,27 @@
2009-11-28 Jan Hubicka <jh@suse.cz>
* 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 <rguenther@suse.de>
PR tree-optimization/42183

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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,7 +744,12 @@ verify_cgraph_node (struct cgraph_node *node)
debug_gimple_stmt (stmt);
error_found = true;
}
if (!clone_of_p (cgraph_node (decl), e->callee)
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:");
@ -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));

View File

@ -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;
}
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);
}

View File

@ -1,3 +1,13 @@
2009-11-29 Jan Hubicka <jh@suse.cz>
* 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 <dodji@redhat.com>
PR c++/36408

View File

@ -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)

View File

@ -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

View File

@ -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;
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,
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
{
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 ();

View File

@ -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;
emit_associated_thunks (clone);
}
/* Build the delete destructor by calling complete destructor
and delete function. */

View File

@ -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--;
}

View File

@ -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 ();

View File

@ -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. */

View File

@ -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. */

View File

@ -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);
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;
}

View File

@ -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;