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:
parent
e55690913e
commit
6744a6abc3
@ -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
|
||||
|
93
gcc/cgraph.c
93
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
23
gcc/cgraph.h
23
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);
|
||||
|
404
gcc/cgraphunit.c
404
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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
162
gcc/cp/method.c
162
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;
|
||||
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))
|
||||
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 ();
|
||||
|
@ -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. */
|
||||
|
@ -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--;
|
||||
}
|
||||
|
@ -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 ();
|
||||
|
@ -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. */
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user