re PR bootstrap/65150 (r220875 causes bootstrap failure on x86_64 darwin)
PR bootstrap/65150 * ipa-icf.c (symbol_compare_collection::symbol_compare_colleciton): Use address_matters_p. (redirect_all_callers, set_addressable): New functions. (sem_function::merge): Reorganize and fix merging issues. (sem_variable::merge): Likewise. (sem_variable::compare_sections): Remove. * common.opt (fmerge-all-constants, fmerge-constants): Remove Optimization flag. * symtab.c (symtab_node::resolve_alias): When alias has aliases, redirect them. (symtab_node::make_decl_local): Set ADDRESSABLE bit when decl is used. (address_matters_1): New function. (symtab_node::address_matters_p): New function. * cgraph.c (cgraph_edge::verify_corresponds_to_fndecl): Fix check for merged flag. * cgraph.h (address_matters_p): Declare. (symtab_node::address_taken_from_non_vtable_p): Remove. (symtab_node::address_can_be_compared_p): New method. (ipa_ref::address_matters_p): Move here from ipa-ref.c; simplify. * ipa-visibility.c (symtab_node::address_taken_from_non_vtable_p): Remove. (comdat_can_be_unshared_p_1) Use address_matters_p. (update_vtable_references): Fix formating. * ipa-ref.c (ipa_ref::address_matters_p): Move inline. * cgraphunit.c (cgraph_node::create_wrapper): Drop UNINLINABLE flag. * cgraphclones.c: Preserve merged and icf_merged flags. Co-Authored-By: Martin Liska <mliska@suse.cz> From-SVN: r221040
This commit is contained in:
parent
f91f1c1312
commit
0a7246ee38
18 changed files with 492 additions and 225 deletions
|
@ -1,3 +1,35 @@
|
|||
2015-02-26 Jan Hubicka <hubicka@ucw.cz>
|
||||
Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR bootstrap/65150
|
||||
* ipa-icf.c (symbol_compare_collection::symbol_compare_colleciton):
|
||||
Use address_matters_p.
|
||||
(redirect_all_callers, set_addressable): New functions.
|
||||
(sem_function::merge): Reorganize and fix merging issues.
|
||||
(sem_variable::merge): Likewise.
|
||||
(sem_variable::compare_sections): Remove.
|
||||
* common.opt (fmerge-all-constants, fmerge-constants): Remove
|
||||
Optimization flag.
|
||||
* symtab.c (symtab_node::resolve_alias): When alias has aliases,
|
||||
redirect them.
|
||||
(symtab_node::make_decl_local): Set ADDRESSABLE bit when
|
||||
decl is used.
|
||||
(address_matters_1): New function.
|
||||
(symtab_node::address_matters_p): New function.
|
||||
* cgraph.c (cgraph_edge::verify_corresponds_to_fndecl): Fix
|
||||
check for merged flag.
|
||||
* cgraph.h (address_matters_p): Declare.
|
||||
(symtab_node::address_taken_from_non_vtable_p): Remove.
|
||||
(symtab_node::address_can_be_compared_p): New method.
|
||||
(ipa_ref::address_matters_p): Move here from ipa-ref.c; simplify.
|
||||
* ipa-visibility.c (symtab_node::address_taken_from_non_vtable_p):
|
||||
Remove.
|
||||
(comdat_can_be_unshared_p_1) Use address_matters_p.
|
||||
(update_vtable_references): Fix formating.
|
||||
* ipa-ref.c (ipa_ref::address_matters_p): Move inline.
|
||||
* cgraphunit.c (cgraph_node::create_wrapper): Drop UNINLINABLE flag.
|
||||
* cgraphclones.c: Preserve merged and icf_merged flags.
|
||||
|
||||
2015-02-26 Sandra Loosemore <sandra@codesourcery.com>
|
||||
|
||||
* doc/extend.texi (Function Attributes): Fix spelling and typos.
|
||||
|
|
|
@ -2630,7 +2630,7 @@ cgraph_edge::verify_corresponds_to_fndecl (tree decl)
|
|||
if (!node
|
||||
|| node->body_removed
|
||||
|| node->in_other_partition
|
||||
|| node->icf_merged
|
||||
|| callee->icf_merged
|
||||
|| callee->in_other_partition)
|
||||
return false;
|
||||
|
||||
|
|
49
gcc/cgraph.h
49
gcc/cgraph.h
|
@ -326,9 +326,6 @@ public:
|
|||
/* Return true if ONE and TWO are part of the same COMDAT group. */
|
||||
inline bool in_same_comdat_group_p (symtab_node *target);
|
||||
|
||||
/* Return true when there is a reference to node and it is not vtable. */
|
||||
bool address_taken_from_non_vtable_p (void);
|
||||
|
||||
/* Return true if symbol is known to be nonzero. */
|
||||
bool nonzero_address ();
|
||||
|
||||
|
@ -337,6 +334,15 @@ public:
|
|||
return 2 otherwise. */
|
||||
int equal_address_to (symtab_node *s2);
|
||||
|
||||
/* Return true if symbol's address may possibly be compared to other
|
||||
symbol's address. */
|
||||
bool address_matters_p ();
|
||||
|
||||
/* Return true if NODE's address can be compared. This use properties
|
||||
of NODE only and does not look if the address is actually taken in
|
||||
interesting way. For that use ADDRESS_MATTERS_P instead. */
|
||||
bool address_can_be_compared_p (void);
|
||||
|
||||
/* Return symbol table node associated with DECL, if any,
|
||||
and NULL otherwise. */
|
||||
static inline symtab_node *get (const_tree decl)
|
||||
|
@ -3022,6 +3028,43 @@ varpool_node::call_for_symbol_and_aliases (bool (*callback) (varpool_node *,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Return true if NODE's address can be compared. */
|
||||
|
||||
inline bool
|
||||
symtab_node::address_can_be_compared_p ()
|
||||
{
|
||||
/* Address of virtual tables and functions is never compared. */
|
||||
if (DECL_VIRTUAL_P (decl))
|
||||
return false;
|
||||
/* Address of C++ cdtors is never compared. */
|
||||
if (is_a <cgraph_node *> (this)
|
||||
&& (DECL_CXX_CONSTRUCTOR_P (decl)
|
||||
|| DECL_CXX_DESTRUCTOR_P (decl)))
|
||||
return false;
|
||||
/* Constant pool symbols addresses are never compared.
|
||||
flag_merge_constants permits us to assume the same on readonly vars. */
|
||||
if (is_a <varpool_node *> (this)
|
||||
&& (DECL_IN_CONSTANT_POOL (decl)
|
||||
|| (flag_merge_constants >= 2
|
||||
&& TREE_READONLY (decl) && !TREE_THIS_VOLATILE (decl))))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return true if refernece may be used in address compare. */
|
||||
|
||||
inline bool
|
||||
ipa_ref::address_matters_p ()
|
||||
{
|
||||
if (use != IPA_REF_ADDR)
|
||||
return false;
|
||||
/* Addresses taken from virtual tables are never compared. */
|
||||
if (is_a <varpool_node *> (referring)
|
||||
&& DECL_VIRTUAL_P (referring->decl))
|
||||
return false;
|
||||
return referred->address_can_be_compared_p ();
|
||||
}
|
||||
|
||||
/* Build polymorphic call context for indirect call E. */
|
||||
|
||||
inline
|
||||
|
|
|
@ -471,6 +471,8 @@ cgraph_node::create_clone (tree decl, gcov_type gcov_count, int freq,
|
|||
new_node->frequency = frequency;
|
||||
new_node->tp_first_run = tp_first_run;
|
||||
new_node->tm_clone = tm_clone;
|
||||
new_node->icf_merged = icf_merged;
|
||||
new_node->merged = merged;
|
||||
|
||||
new_node->clone.tree_map = NULL;
|
||||
new_node->clone.args_to_skip = args_to_skip;
|
||||
|
|
|
@ -2468,6 +2468,7 @@ cgraph_node::create_wrapper (cgraph_node *target)
|
|||
release_body (true);
|
||||
reset ();
|
||||
|
||||
DECL_UNINLINABLE (decl) = false;
|
||||
DECL_RESULT (decl) = decl_result;
|
||||
DECL_INITIAL (decl) = NULL;
|
||||
allocate_struct_function (decl, false);
|
||||
|
|
|
@ -1644,11 +1644,11 @@ Report on permanent memory allocation in WPA only
|
|||
; string constants and constants from constant pool, if 2 also constant
|
||||
; variables.
|
||||
fmerge-all-constants
|
||||
Common Report Var(flag_merge_constants,2) Init(1) Optimization
|
||||
Common Report Var(flag_merge_constants,2) Init(1)
|
||||
Attempt to merge identical constants and constant variables
|
||||
|
||||
fmerge-constants
|
||||
Common Report Var(flag_merge_constants,1) Optimization
|
||||
Common Report Var(flag_merge_constants,1)
|
||||
Attempt to merge identical constants across compilation units
|
||||
|
||||
fmerge-debug-strings
|
||||
|
|
453
gcc/ipa-icf.c
453
gcc/ipa-icf.c
|
@ -147,7 +147,7 @@ symbol_compare_collection::symbol_compare_collection (symtab_node *node)
|
|||
|
||||
if (ref->referred->get_availability () <= AVAIL_INTERPOSABLE)
|
||||
{
|
||||
if (ref->use == IPA_REF_ADDR)
|
||||
if (ref->address_matters_p ())
|
||||
m_references.safe_push (ref->referred);
|
||||
else
|
||||
m_interposables.safe_push (ref->referred);
|
||||
|
@ -632,8 +632,56 @@ set_local (cgraph_node *node, void *data)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* TREE_ADDRESSABLE of NODE to true if DATA is non-NULL.
|
||||
Helper for call_for_symbol_thunks_and_aliases. */
|
||||
|
||||
static bool
|
||||
set_addressable (varpool_node *node, void *)
|
||||
{
|
||||
TREE_ADDRESSABLE (node->decl) = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Redirect all callers of N and its aliases to TO. Remove aliases if
|
||||
possible. Return number of redirections made. */
|
||||
|
||||
static int
|
||||
redirect_all_callers (cgraph_node *n, cgraph_node *to)
|
||||
{
|
||||
int nredirected = 0;
|
||||
ipa_ref *ref;
|
||||
|
||||
while (n->callers)
|
||||
{
|
||||
cgraph_edge *e = n->callers;
|
||||
e->redirect_callee (to);
|
||||
nredirected++;
|
||||
}
|
||||
for (unsigned i = 0; n->iterate_direct_aliases (i, ref);)
|
||||
{
|
||||
bool removed = false;
|
||||
cgraph_node *n_alias = dyn_cast <cgraph_node *> (ref->referring);
|
||||
|
||||
if ((DECL_COMDAT_GROUP (n->decl)
|
||||
&& (DECL_COMDAT_GROUP (n->decl)
|
||||
== DECL_COMDAT_GROUP (n_alias->decl)))
|
||||
|| (n_alias->get_availability () > AVAIL_INTERPOSABLE
|
||||
&& n->get_availability () > AVAIL_INTERPOSABLE))
|
||||
{
|
||||
nredirected += redirect_all_callers (n_alias, to);
|
||||
if (n_alias->can_remove_if_no_direct_calls_p ()
|
||||
&& !n_alias->has_aliases_p ())
|
||||
n_alias->remove ();
|
||||
}
|
||||
if (!removed)
|
||||
i++;
|
||||
}
|
||||
return nredirected;
|
||||
}
|
||||
|
||||
/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
|
||||
be applied. */
|
||||
|
||||
bool
|
||||
sem_function::merge (sem_item *alias_item)
|
||||
{
|
||||
|
@ -642,16 +690,29 @@ sem_function::merge (sem_item *alias_item)
|
|||
sem_function *alias_func = static_cast<sem_function *> (alias_item);
|
||||
|
||||
cgraph_node *original = get_node ();
|
||||
cgraph_node *local_original = original;
|
||||
cgraph_node *local_original = NULL;
|
||||
cgraph_node *alias = alias_func->get_node ();
|
||||
bool original_address_matters;
|
||||
bool alias_address_matters;
|
||||
|
||||
bool create_thunk = false;
|
||||
bool create_wrapper = false;
|
||||
bool create_alias = false;
|
||||
bool redirect_callers = false;
|
||||
bool remove = false;
|
||||
|
||||
bool original_discardable = false;
|
||||
|
||||
bool original_address_matters = original->address_matters_p ();
|
||||
bool alias_address_matters = alias->address_matters_p ();
|
||||
|
||||
if (DECL_NO_INLINE_WARNING_P (original->decl)
|
||||
!= DECL_NO_INLINE_WARNING_P (alias->decl))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"Not unifying; "
|
||||
"DECL_NO_INLINE_WARNING mismatch.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Do not attempt to mix functions from different user sections;
|
||||
we do not know what user intends with those. */
|
||||
if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
|
||||
|
@ -660,123 +721,173 @@ sem_function::merge (sem_item *alias_item)
|
|||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"Not unifying; original and alias are in different sections.\n\n");
|
||||
"Not unifying; "
|
||||
"original and alias are in different sections.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See if original is in a section that can be discarded if the main
|
||||
symbol is not used. */
|
||||
if (DECL_EXTERNAL (original->decl))
|
||||
original_discardable = true;
|
||||
if (original->resolution == LDPR_PREEMPTED_REG
|
||||
|| original->resolution == LDPR_PREEMPTED_IR)
|
||||
original_discardable = true;
|
||||
if (original->can_be_discarded_p ())
|
||||
symbol is not used.
|
||||
|
||||
Also consider case where we have resolution info and we know that
|
||||
original's definition is not going to be used. In this case we can not
|
||||
create alias to original. */
|
||||
if (original->can_be_discarded_p ()
|
||||
|| (node->resolution != LDPR_UNKNOWN
|
||||
&& !decl_binds_to_current_def_p (node->decl)))
|
||||
original_discardable = true;
|
||||
|
||||
/* See if original and/or alias address can be compared for equality. */
|
||||
original_address_matters
|
||||
= (!DECL_VIRTUAL_P (original->decl)
|
||||
&& (original->externally_visible
|
||||
|| original->address_taken_from_non_vtable_p ()));
|
||||
alias_address_matters
|
||||
= (!DECL_VIRTUAL_P (alias->decl)
|
||||
&& (alias->externally_visible
|
||||
|| alias->address_taken_from_non_vtable_p ()));
|
||||
/* Creating a symtab alias is the optimal way to merge.
|
||||
It however can not be used in the following cases:
|
||||
|
||||
/* If alias and original can be compared for address equality, we need
|
||||
to create a thunk. Also we can not create extra aliases into discardable
|
||||
section (or we risk link failures when section is discarded). */
|
||||
if ((original_address_matters
|
||||
&& alias_address_matters)
|
||||
1) if ORIGINAL and ALIAS may be possibly compared for address equality.
|
||||
2) if ORIGINAL is in a section that may be discarded by linker or if
|
||||
it is an external functions where we can not create an alias
|
||||
(ORIGINAL_DISCARDABLE)
|
||||
3) if target do not support symbol aliases.
|
||||
|
||||
If we can not produce alias, we will turn ALIAS into WRAPPER of ORIGINAL
|
||||
and/or redirect all callers from ALIAS to ORIGINAL. */
|
||||
if ((original_address_matters && alias_address_matters)
|
||||
|| original_discardable
|
||||
|| DECL_COMDAT_GROUP (alias->decl)
|
||||
|| !sem_item::target_supports_symbol_aliases_p ())
|
||||
{
|
||||
create_thunk = !stdarg_p (TREE_TYPE (alias->decl));
|
||||
create_alias = false;
|
||||
/* When both alias and original are not overwritable, we can save
|
||||
the extra thunk wrapper for direct calls. */
|
||||
redirect_callers
|
||||
= (!original_discardable
|
||||
&& !DECL_COMDAT_GROUP (alias->decl)
|
||||
&& alias->get_availability () > AVAIL_INTERPOSABLE
|
||||
&& original->get_availability () > AVAIL_INTERPOSABLE
|
||||
&& !alias->instrumented_version);
|
||||
}
|
||||
else
|
||||
{
|
||||
create_alias = true;
|
||||
create_thunk = false;
|
||||
redirect_callers = false;
|
||||
}
|
||||
/* First see if we can produce wrapper. */
|
||||
|
||||
/* We want thunk to always jump to the local function body
|
||||
unless the body is comdat and may be optimized out. */
|
||||
if ((create_thunk || redirect_callers)
|
||||
&& (!original_discardable
|
||||
/* Do not turn function in one comdat group into wrapper to another
|
||||
comdat group. Other compiler producing the body of the
|
||||
another comdat group may make opossite decision and with unfortunate
|
||||
linker choices this may close a loop. */
|
||||
if (DECL_COMDAT_GROUP (alias->decl)
|
||||
&& (DECL_COMDAT_GROUP (alias->decl)
|
||||
!= DECL_COMDAT_GROUP (original->decl)))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"Wrapper cannot be created because of COMDAT\n");
|
||||
}
|
||||
else if (DECL_STATIC_CHAIN (alias->decl))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"Can not create wrapper of nested functions.\n");
|
||||
}
|
||||
/* TODO: We can also deal with variadic functions never calling
|
||||
VA_START. */
|
||||
else if (stdarg_p (TREE_TYPE (alias->decl)))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"can not create wrapper of stdarg function.\n");
|
||||
}
|
||||
else if (inline_summaries
|
||||
&& inline_summaries->get (alias)->self_size <= 2)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Wrapper creation is not "
|
||||
"profitable (function is too small).\n");
|
||||
}
|
||||
/* If user paid attention to mark function noinline, assume it is
|
||||
somewhat special and do not try to turn it into a wrapper that can
|
||||
not be undone by inliner. */
|
||||
else if (lookup_attribute ("noinline", DECL_ATTRIBUTES (alias->decl)))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Wrappers are not created for noinline.\n");
|
||||
}
|
||||
else
|
||||
create_wrapper = true;
|
||||
|
||||
/* We can redirect local calls in the case both alias and orignal
|
||||
are not interposable. */
|
||||
redirect_callers
|
||||
= alias->get_availability () > AVAIL_INTERPOSABLE
|
||||
&& original->get_availability () > AVAIL_INTERPOSABLE
|
||||
&& !alias->instrumented_version;
|
||||
|
||||
if (!redirect_callers && !create_wrapper)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Not unifying; can not redirect callers nor "
|
||||
"produce wrapper\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Work out the symbol the wrapper should call.
|
||||
If ORIGINAL is interposable, we need to call a local alias.
|
||||
Also produce local alias (if possible) as an optimization. */
|
||||
if (!original_discardable
|
||||
|| (DECL_COMDAT_GROUP (original->decl)
|
||||
&& (DECL_COMDAT_GROUP (original->decl)
|
||||
== DECL_COMDAT_GROUP (alias->decl)))))
|
||||
local_original
|
||||
= dyn_cast <cgraph_node *> (original->noninterposable_alias ());
|
||||
== DECL_COMDAT_GROUP (alias->decl))))
|
||||
{
|
||||
local_original
|
||||
= dyn_cast <cgraph_node *> (original->noninterposable_alias ());
|
||||
if (!local_original
|
||||
&& original->get_availability () > AVAIL_INTERPOSABLE)
|
||||
local_original = original;
|
||||
/* If original is COMDAT local, we can not really redirect external
|
||||
callers to it. */
|
||||
if (original->comdat_local_p ())
|
||||
redirect_callers = false;
|
||||
}
|
||||
/* If we can not use local alias, fallback to the original
|
||||
when possible. */
|
||||
else if (original->get_availability () > AVAIL_INTERPOSABLE)
|
||||
local_original = original;
|
||||
if (!local_original)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Not unifying; "
|
||||
"can not produce local alias.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!local_original)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Noninterposable alias cannot be created.\n\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!decl_binds_to_current_def_p (alias->decl))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Declaration does not bind to currect definition.\n\n");
|
||||
return false;
|
||||
if (!redirect_callers && !create_wrapper)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Not unifying; "
|
||||
"can not redirect callers nor produce a wrapper\n\n");
|
||||
return false;
|
||||
}
|
||||
if (!create_wrapper
|
||||
&& !alias->can_remove_if_no_direct_calls_p ())
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Not unifying; can not make wrapper and "
|
||||
"function has other uses than direct calls\n\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
create_alias = true;
|
||||
|
||||
if (redirect_callers)
|
||||
{
|
||||
/* If alias is non-overwritable then
|
||||
all direct calls are safe to be redirected to the original. */
|
||||
bool redirected = false;
|
||||
while (alias->callers)
|
||||
int nredirected = redirect_all_callers (alias, local_original);
|
||||
|
||||
if (nredirected)
|
||||
{
|
||||
cgraph_edge *e = alias->callers;
|
||||
e->redirect_callee (local_original);
|
||||
push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
|
||||
alias->icf_merged = true;
|
||||
local_original->icf_merged = true;
|
||||
|
||||
if (e->call_stmt)
|
||||
e->redirect_call_stmt_to_callee ();
|
||||
|
||||
pop_cfun ();
|
||||
redirected = true;
|
||||
if (dump_file && nredirected)
|
||||
fprintf (dump_file, "%i local calls have been "
|
||||
"redirected.\n", nredirected);
|
||||
}
|
||||
|
||||
alias->icf_merged = true;
|
||||
if (local_original->lto_file_data
|
||||
&& alias->lto_file_data
|
||||
&& local_original->lto_file_data != alias->lto_file_data)
|
||||
local_original->merged = true;
|
||||
|
||||
/* The alias function is removed if symbol address
|
||||
does not matter. */
|
||||
if (!alias_address_matters)
|
||||
alias->remove ();
|
||||
|
||||
if (dump_file && redirected)
|
||||
fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
|
||||
/* If all callers was redirected, do not produce wrapper. */
|
||||
if (alias->can_remove_if_no_direct_calls_p ()
|
||||
&& !alias->has_aliases_p ())
|
||||
{
|
||||
create_wrapper = false;
|
||||
remove = true;
|
||||
}
|
||||
gcc_assert (!create_alias);
|
||||
}
|
||||
/* If the condtion above is not met, we are lucky and can turn the
|
||||
function into real alias. */
|
||||
else if (create_alias)
|
||||
{
|
||||
alias->icf_merged = true;
|
||||
if (local_original->lto_file_data
|
||||
&& alias->lto_file_data
|
||||
&& local_original->lto_file_data != alias->lto_file_data)
|
||||
local_original->merged = true;
|
||||
|
||||
/* Remove the function's body. */
|
||||
ipa_merge_profiles (original, alias);
|
||||
|
@ -791,39 +902,38 @@ sem_function::merge (sem_item *alias_item)
|
|||
(set_local, (void *)(size_t) original->local_p (), true);
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Callgraph alias has been created.\n\n");
|
||||
fprintf (dump_file, "Unified; Function alias has been created.\n\n");
|
||||
}
|
||||
else if (create_thunk)
|
||||
if (create_wrapper)
|
||||
{
|
||||
if (DECL_COMDAT_GROUP (alias->decl))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (DECL_STATIC_CHAIN (alias->decl))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Thunk creation is risky for static-chain functions.\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gcc_assert (!create_alias);
|
||||
alias->icf_merged = true;
|
||||
if (local_original->lto_file_data
|
||||
&& alias->lto_file_data
|
||||
&& local_original->lto_file_data != alias->lto_file_data)
|
||||
local_original->merged = true;
|
||||
local_original->icf_merged = true;
|
||||
|
||||
ipa_merge_profiles (local_original, alias, true);
|
||||
alias->create_wrapper (local_original);
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Callgraph thunk has been created.\n\n");
|
||||
fprintf (dump_file, "Unified; Wrapper has been created.\n\n");
|
||||
}
|
||||
gcc_assert (alias->icf_merged || remove);
|
||||
original->icf_merged = true;
|
||||
|
||||
/* Inform the inliner about cross-module merging. */
|
||||
if ((original->lto_file_data || alias->lto_file_data)
|
||||
&& original->lto_file_data != alias->lto_file_data)
|
||||
local_original->merged = original->merged = true;
|
||||
|
||||
if (remove)
|
||||
{
|
||||
ipa_merge_profiles (original, alias);
|
||||
alias->release_body ();
|
||||
alias->reset ();
|
||||
alias->body_removed = true;
|
||||
alias->icf_merged = true;
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Unified; Function body was removed.\n");
|
||||
}
|
||||
else if (dump_file)
|
||||
fprintf (dump_file, "Callgraph merge operation cannot be performed.\n\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1319,7 +1429,8 @@ sem_variable::merge (sem_item *alias_item)
|
|||
if (!sem_item::target_supports_symbol_aliases_p ())
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Symbol aliases are not supported by target\n\n");
|
||||
fprintf (dump_file, "Not unifying; "
|
||||
"Symbol aliases are not supported by target\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1329,73 +1440,93 @@ sem_variable::merge (sem_item *alias_item)
|
|||
varpool_node *alias = alias_var->get_node ();
|
||||
bool original_discardable = false;
|
||||
|
||||
bool original_address_matters = original->address_matters_p ();
|
||||
bool alias_address_matters = alias->address_matters_p ();
|
||||
|
||||
/* See if original is in a section that can be discarded if the main
|
||||
symbol is not used. */
|
||||
if (DECL_EXTERNAL (original->decl))
|
||||
original_discardable = true;
|
||||
if (original->resolution == LDPR_PREEMPTED_REG
|
||||
|| original->resolution == LDPR_PREEMPTED_IR)
|
||||
original_discardable = true;
|
||||
if (original->can_be_discarded_p ())
|
||||
symbol is not used.
|
||||
Also consider case where we have resolution info and we know that
|
||||
original's definition is not going to be used. In this case we can not
|
||||
create alias to original. */
|
||||
if (original->can_be_discarded_p ()
|
||||
|| (node->resolution != LDPR_UNKNOWN
|
||||
&& !decl_binds_to_current_def_p (node->decl)))
|
||||
original_discardable = true;
|
||||
|
||||
gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
|
||||
|
||||
if (original_discardable || DECL_EXTERNAL (alias_var->decl) ||
|
||||
!compare_sections (alias_var))
|
||||
/* Constant pool machinery is not quite ready for aliases.
|
||||
TODO: varasm code contains logic for merging DECL_IN_CONSTANT_POOL.
|
||||
For LTO merging does not happen that is an important missing feature.
|
||||
We can enable merging with LTO if the DECL_IN_CONSTANT_POOL
|
||||
flag is dropped and non-local symbol name is assigned. */
|
||||
if (DECL_IN_CONSTANT_POOL (alias->decl)
|
||||
|| DECL_IN_CONSTANT_POOL (original->decl))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Varpool alias cannot be created\n\n");
|
||||
fprintf (dump_file,
|
||||
"Not unifying; constant pool variables.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Do not attempt to mix functions from different user sections;
|
||||
we do not know what user intends with those. */
|
||||
if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
|
||||
|| (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
|
||||
&& DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"Not unifying; "
|
||||
"original and alias are in different sections.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We can not merge if address comparsion metters. */
|
||||
if (original_address_matters && alias_address_matters
|
||||
&& flag_merge_constants < 2)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file,
|
||||
"Not unifying; "
|
||||
"adress of original and alias may be compared.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (original_discardable
|
||||
&& (!DECL_COMDAT_GROUP (original->decl)
|
||||
|| (DECL_COMDAT_GROUP (original->decl)
|
||||
!= DECL_COMDAT_GROUP (alias->decl))))
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Not unifying; alias cannot be created; "
|
||||
"target is discardable\n\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// alias cycle creation check
|
||||
varpool_node *n = original;
|
||||
|
||||
while (n->alias)
|
||||
{
|
||||
n = n->get_alias_target ();
|
||||
if (n == alias)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
gcc_assert (!original->alias);
|
||||
gcc_assert (!alias->alias);
|
||||
|
||||
alias->analyzed = false;
|
||||
|
||||
DECL_INITIAL (alias->decl) = NULL;
|
||||
alias->need_bounds_init = false;
|
||||
alias->remove_all_references ();
|
||||
if (TREE_ADDRESSABLE (alias->decl))
|
||||
original->call_for_symbol_and_aliases (set_addressable, NULL, true);
|
||||
|
||||
varpool_node::create_alias (alias_var->decl, decl);
|
||||
alias->resolve_alias (original);
|
||||
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Varpool alias has been created.\n\n");
|
||||
fprintf (dump_file, "Unified; Variable alias has been created.\n\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sem_variable::compare_sections (sem_variable *alias)
|
||||
{
|
||||
const char *source = node->get_section ();
|
||||
const char *target = alias->node->get_section();
|
||||
|
||||
if (source == NULL && target == NULL)
|
||||
return true;
|
||||
else if(!source || !target)
|
||||
return false;
|
||||
else
|
||||
return strcmp (source, target) == 0;
|
||||
}
|
||||
|
||||
/* Dump symbol to FILE. */
|
||||
|
||||
void
|
||||
|
|
|
@ -124,23 +124,3 @@ ipa_ref::referred_ref_list (void)
|
|||
{
|
||||
return &referred->ref_list;
|
||||
}
|
||||
|
||||
/* Return true if refernece may be used in address compare. */
|
||||
bool
|
||||
ipa_ref::address_matters_p ()
|
||||
{
|
||||
if (use != IPA_REF_ADDR)
|
||||
return false;
|
||||
/* Addresses taken from virtual tables are never compared. */
|
||||
if (is_a <varpool_node *> (referring)
|
||||
&& DECL_VIRTUAL_P (referring->decl))
|
||||
return false;
|
||||
/* Address of virtual tables and functions is never compared. */
|
||||
if (DECL_VIRTUAL_P (referred->decl))
|
||||
return false;
|
||||
/* Address of C++ cdtors is never compared. */
|
||||
if (is_a <cgraph_node *> (referred)
|
||||
&& (DECL_CXX_CONSTRUCTOR_P (referred->decl) || DECL_CXX_DESTRUCTOR_P (referred->decl)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -129,27 +129,6 @@ cgraph_node::local_p (void)
|
|||
|
||||
}
|
||||
|
||||
/* Return true when there is a reference to node and it is not vtable. */
|
||||
|
||||
bool
|
||||
symtab_node::address_taken_from_non_vtable_p (void)
|
||||
{
|
||||
int i;
|
||||
struct ipa_ref *ref = NULL;
|
||||
|
||||
for (i = 0; iterate_referring (i, ref); i++)
|
||||
if (ref->use == IPA_REF_ADDR)
|
||||
{
|
||||
varpool_node *node;
|
||||
if (is_a <cgraph_node *> (ref->referring))
|
||||
return true;
|
||||
node = dyn_cast <varpool_node *> (ref->referring);
|
||||
if (!DECL_VIRTUAL_P (node->decl))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* A helper for comdat_can_be_unshared_p. */
|
||||
|
||||
static bool
|
||||
|
@ -157,16 +136,14 @@ comdat_can_be_unshared_p_1 (symtab_node *node)
|
|||
{
|
||||
if (!node->externally_visible)
|
||||
return true;
|
||||
/* When address is taken, we don't know if equality comparison won't
|
||||
break eventually. Exception are virutal functions, C++
|
||||
constructors/destructors and vtables, where this is not possible by
|
||||
language standard. */
|
||||
if (!DECL_VIRTUAL_P (node->decl)
|
||||
&& (TREE_CODE (node->decl) != FUNCTION_DECL
|
||||
|| (!DECL_CXX_CONSTRUCTOR_P (node->decl)
|
||||
&& !DECL_CXX_DESTRUCTOR_P (node->decl)))
|
||||
&& node->address_taken_from_non_vtable_p ())
|
||||
return false;
|
||||
if (node->address_can_be_compared_p ())
|
||||
{
|
||||
struct ipa_ref *ref;
|
||||
|
||||
for (unsigned int i = 0; node->iterate_referring (i, ref); i++)
|
||||
if (ref->address_matters_p ())
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the symbol is used in some weird way, better to not touch it. */
|
||||
if (node->force_output)
|
||||
|
@ -387,7 +364,8 @@ can_replace_by_local_alias_in_vtable (symtab_node *node)
|
|||
/* walk_tree callback that rewrites initializer references. */
|
||||
|
||||
static tree
|
||||
update_vtable_references (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
|
||||
update_vtable_references (tree *tp, int *walk_subtrees,
|
||||
void *data ATTRIBUTE_UNUSED)
|
||||
{
|
||||
if (TREE_CODE (*tp) == VAR_DECL
|
||||
|| TREE_CODE (*tp) == FUNCTION_DECL)
|
||||
|
|
47
gcc/symtab.c
47
gcc/symtab.c
|
@ -1156,7 +1156,11 @@ symtab_node::make_decl_local (void)
|
|||
return;
|
||||
|
||||
if (TREE_CODE (decl) == VAR_DECL)
|
||||
DECL_COMMON (decl) = 0;
|
||||
{
|
||||
DECL_COMMON (decl) = 0;
|
||||
/* ADDRESSABLE flag is not defined for public symbols. */
|
||||
TREE_ADDRESSABLE (decl) = 1;
|
||||
}
|
||||
else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
|
||||
|
||||
DECL_COMDAT (decl) = 0;
|
||||
|
@ -1513,6 +1517,19 @@ symtab_node::resolve_alias (symtab_node *target)
|
|||
/* If alias has address taken, so does the target. */
|
||||
if (address_taken)
|
||||
target->ultimate_alias_target ()->address_taken = true;
|
||||
|
||||
/* All non-weakref aliases of THIS are now in fact aliases of TARGET. */
|
||||
ipa_ref *ref;
|
||||
for (unsigned i = 0; iterate_direct_aliases (i, ref);)
|
||||
{
|
||||
struct symtab_node *alias_alias = ref->referring;
|
||||
if (!alias_alias->weakref)
|
||||
{
|
||||
alias_alias->remove_all_references ();
|
||||
alias_alias->create_reference (target, IPA_REF_ALIAS, NULL);
|
||||
}
|
||||
else i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1863,3 +1880,31 @@ symtab_node::call_for_symbol_and_aliases_1 (bool (*callback) (symtab_node *,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return ture if address of N is possibly compared. */
|
||||
|
||||
static bool
|
||||
address_matters_1 (symtab_node *n, void *)
|
||||
{
|
||||
struct ipa_ref *ref;
|
||||
|
||||
if (!n->address_can_be_compared_p ())
|
||||
return false;
|
||||
if (n->externally_visible || n->force_output)
|
||||
return true;
|
||||
|
||||
for (unsigned int i = 0; n->iterate_referring (i, ref); i++)
|
||||
if (ref->address_matters_p ())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return true if symbol's address may possibly be compared to other
|
||||
symbol's address. */
|
||||
|
||||
bool
|
||||
symtab_node::address_matters_p ()
|
||||
{
|
||||
gcc_assert (!alias);
|
||||
return call_for_symbol_and_aliases (address_matters_1, NULL, true);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
2015-02-26 Jan Hubicka <hubicka@ucw.cz>
|
||||
Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR bootstrap/65150
|
||||
* gcc.dg/pr64454.c: Disable ICF.
|
||||
* gcc.dg/pr28685-1.c: Disable ICF
|
||||
* gcc.dg/ipa/iinline-5.c: Disable ICF.
|
||||
* g++.dg/warn/Wsuggest-final.C: Force methods to be different.
|
||||
* g++.dg/ipa/ipa-icf-4.C: Update template.
|
||||
|
||||
2015-02-26 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR tree-optimization/65216
|
||||
|
|
|
@ -43,6 +43,6 @@ int main()
|
|||
return 123;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-ipa-dump "\(Varpool alias has been created\)|\(Symbol aliases are not supported by target\)" "icf" } } */
|
||||
/* { dg-final { scan-ipa-dump "\(Unified; Variable alias has been created\)|\(Symbol aliases are not supported by target\)" "icf" } } */
|
||||
/* { dg-final { scan-ipa-dump "Equal symbols: 6" "icf" } } */
|
||||
/* { dg-final { cleanup-ipa-dump "icf" } } */
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// { dg-do compile }
|
||||
// { dg-options "-O2 -Wsuggest-final-types -Wsuggest-final-methods" }
|
||||
int c;
|
||||
struct A { // { dg-warning "final would enable devirtualization of 4 calls" }
|
||||
virtual void a() {} // { dg-warning "final would enable devirtualization of 2 calls" }
|
||||
virtual void b() {} // { dg-warning "final would enable devirtualization of 2 calls" }
|
||||
virtual void b() {c++;} // { dg-warning "final would enable devirtualization of 2 calls" }
|
||||
};
|
||||
void
|
||||
t(struct A *a)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* Verify that simple indirect calls are inlined even without early
|
||||
inlining.. */
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining" } */
|
||||
/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-icf" } */
|
||||
|
||||
extern void abort (void);
|
||||
|
||||
|
|
22
gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c
Normal file
22
gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-ipa-cp" } */
|
||||
int n;
|
||||
|
||||
static void
|
||||
__attribute__ ((noinline))
|
||||
test(void *a)
|
||||
{
|
||||
__builtin_memset (a,0,n);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int aa;
|
||||
short bb;
|
||||
test (&aa);
|
||||
test (&bb);
|
||||
return 0;
|
||||
}
|
||||
/* { dg-final { scan-ipa-dump "Alignment 2" "cp" } } */
|
||||
/* { dg-final { cleanup-ipa-dump "cp" } } */
|
22
gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c
Normal file
22
gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-ipa-cp" } */
|
||||
int n;
|
||||
|
||||
static void
|
||||
__attribute__ ((noinline))
|
||||
test(void *a)
|
||||
{
|
||||
__builtin_memset (a,0,n);
|
||||
}
|
||||
|
||||
static __attribute__ ((aligned(16))) int aa[10];
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test (&aa[1]);
|
||||
test (&aa[3]);
|
||||
return 0;
|
||||
}
|
||||
/* { dg-final { scan-ipa-dump "Alignment 8, misalignment 4" "cp" } } */
|
||||
/* { dg-final { cleanup-ipa-dump "cp" } } */
|
|
@ -1,5 +1,5 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-optimized" } */
|
||||
/* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-icf" } */
|
||||
|
||||
/* Should produce <=. */
|
||||
int test1 (int a, int b)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* PR tree-optimization/64454 */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-tree-vrp1" } */
|
||||
/* { dg-options "-O2 -fdump-tree-vrp1 -fno-ipa-icf" } */
|
||||
|
||||
unsigned
|
||||
f1 (unsigned x)
|
||||
|
|
Loading…
Add table
Reference in a new issue