Commit 0a7246ee by Jan Hubicka Committed by Jan Hubicka

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
parent f91f1c13
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> 2015-02-26 Sandra Loosemore <sandra@codesourcery.com>
* doc/extend.texi (Function Attributes): Fix spelling and typos. * doc/extend.texi (Function Attributes): Fix spelling and typos.
......
...@@ -2630,7 +2630,7 @@ cgraph_edge::verify_corresponds_to_fndecl (tree decl) ...@@ -2630,7 +2630,7 @@ cgraph_edge::verify_corresponds_to_fndecl (tree decl)
if (!node if (!node
|| node->body_removed || node->body_removed
|| node->in_other_partition || node->in_other_partition
|| node->icf_merged || callee->icf_merged
|| callee->in_other_partition) || callee->in_other_partition)
return false; return false;
......
...@@ -326,9 +326,6 @@ public: ...@@ -326,9 +326,6 @@ public:
/* Return true if ONE and TWO are part of the same COMDAT group. */ /* Return true if ONE and TWO are part of the same COMDAT group. */
inline bool in_same_comdat_group_p (symtab_node *target); 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. */ /* Return true if symbol is known to be nonzero. */
bool nonzero_address (); bool nonzero_address ();
...@@ -337,6 +334,15 @@ public: ...@@ -337,6 +334,15 @@ public:
return 2 otherwise. */ return 2 otherwise. */
int equal_address_to (symtab_node *s2); 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, /* Return symbol table node associated with DECL, if any,
and NULL otherwise. */ and NULL otherwise. */
static inline symtab_node *get (const_tree decl) static inline symtab_node *get (const_tree decl)
...@@ -3022,6 +3028,43 @@ varpool_node::call_for_symbol_and_aliases (bool (*callback) (varpool_node *, ...@@ -3022,6 +3028,43 @@ varpool_node::call_for_symbol_and_aliases (bool (*callback) (varpool_node *,
return false; 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. */ /* Build polymorphic call context for indirect call E. */
inline inline
......
...@@ -471,6 +471,8 @@ cgraph_node::create_clone (tree decl, gcov_type gcov_count, int freq, ...@@ -471,6 +471,8 @@ cgraph_node::create_clone (tree decl, gcov_type gcov_count, int freq,
new_node->frequency = frequency; new_node->frequency = frequency;
new_node->tp_first_run = tp_first_run; new_node->tp_first_run = tp_first_run;
new_node->tm_clone = tm_clone; 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.tree_map = NULL;
new_node->clone.args_to_skip = args_to_skip; new_node->clone.args_to_skip = args_to_skip;
......
...@@ -2468,6 +2468,7 @@ cgraph_node::create_wrapper (cgraph_node *target) ...@@ -2468,6 +2468,7 @@ cgraph_node::create_wrapper (cgraph_node *target)
release_body (true); release_body (true);
reset (); reset ();
DECL_UNINLINABLE (decl) = false;
DECL_RESULT (decl) = decl_result; DECL_RESULT (decl) = decl_result;
DECL_INITIAL (decl) = NULL; DECL_INITIAL (decl) = NULL;
allocate_struct_function (decl, false); allocate_struct_function (decl, false);
......
...@@ -1644,11 +1644,11 @@ Report on permanent memory allocation in WPA only ...@@ -1644,11 +1644,11 @@ Report on permanent memory allocation in WPA only
; string constants and constants from constant pool, if 2 also constant ; string constants and constants from constant pool, if 2 also constant
; variables. ; variables.
fmerge-all-constants 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 Attempt to merge identical constants and constant variables
fmerge-constants 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 Attempt to merge identical constants across compilation units
fmerge-debug-strings fmerge-debug-strings
......
...@@ -147,7 +147,7 @@ symbol_compare_collection::symbol_compare_collection (symtab_node *node) ...@@ -147,7 +147,7 @@ symbol_compare_collection::symbol_compare_collection (symtab_node *node)
if (ref->referred->get_availability () <= AVAIL_INTERPOSABLE) if (ref->referred->get_availability () <= AVAIL_INTERPOSABLE)
{ {
if (ref->use == IPA_REF_ADDR) if (ref->address_matters_p ())
m_references.safe_push (ref->referred); m_references.safe_push (ref->referred);
else else
m_interposables.safe_push (ref->referred); m_interposables.safe_push (ref->referred);
...@@ -632,8 +632,56 @@ set_local (cgraph_node *node, void *data) ...@@ -632,8 +632,56 @@ set_local (cgraph_node *node, void *data)
return false; 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 /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
be applied. */ be applied. */
bool bool
sem_function::merge (sem_item *alias_item) sem_function::merge (sem_item *alias_item)
{ {
...@@ -642,16 +690,29 @@ 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); sem_function *alias_func = static_cast<sem_function *> (alias_item);
cgraph_node *original = get_node (); cgraph_node *original = get_node ();
cgraph_node *local_original = original; cgraph_node *local_original = NULL;
cgraph_node *alias = alias_func->get_node (); 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 create_alias = false;
bool redirect_callers = false; bool redirect_callers = false;
bool remove = false;
bool original_discardable = 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; /* Do not attempt to mix functions from different user sections;
we do not know what user intends with those. */ we do not know what user intends with those. */
if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section) if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
...@@ -660,123 +721,173 @@ sem_function::merge (sem_item *alias_item) ...@@ -660,123 +721,173 @@ sem_function::merge (sem_item *alias_item)
{ {
if (dump_file) if (dump_file)
fprintf (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; return false;
} }
/* See if original is in a section that can be discarded if the main /* See if original is in a section that can be discarded if the main
symbol is not used. */ symbol is not used.
if (DECL_EXTERNAL (original->decl))
original_discardable = true; Also consider case where we have resolution info and we know that
if (original->resolution == LDPR_PREEMPTED_REG original's definition is not going to be used. In this case we can not
|| original->resolution == LDPR_PREEMPTED_IR) create alias to original. */
original_discardable = true; if (original->can_be_discarded_p ()
if (original->can_be_discarded_p ()) || (node->resolution != LDPR_UNKNOWN
&& !decl_binds_to_current_def_p (node->decl)))
original_discardable = true; original_discardable = true;
/* See if original and/or alias address can be compared for equality. */ /* Creating a symtab alias is the optimal way to merge.
original_address_matters It however can not be used in the following cases:
= (!DECL_VIRTUAL_P (original->decl)
&& (original->externally_visible 1) if ORIGINAL and ALIAS may be possibly compared for address equality.
|| original->address_taken_from_non_vtable_p ())); 2) if ORIGINAL is in a section that may be discarded by linker or if
alias_address_matters it is an external functions where we can not create an alias
= (!DECL_VIRTUAL_P (alias->decl) (ORIGINAL_DISCARDABLE)
&& (alias->externally_visible 3) if target do not support symbol aliases.
|| alias->address_taken_from_non_vtable_p ()));
If we can not produce alias, we will turn ALIAS into WRAPPER of ORIGINAL
/* If alias and original can be compared for address equality, we need and/or redirect all callers from ALIAS to ORIGINAL. */
to create a thunk. Also we can not create extra aliases into discardable if ((original_address_matters && alias_address_matters)
section (or we risk link failures when section is discarded). */
if ((original_address_matters
&& alias_address_matters)
|| original_discardable || original_discardable
|| DECL_COMDAT_GROUP (alias->decl)
|| !sem_item::target_supports_symbol_aliases_p ()) || !sem_item::target_supports_symbol_aliases_p ())
{ {
create_thunk = !stdarg_p (TREE_TYPE (alias->decl)); /* First see if we can produce wrapper. */
create_alias = false;
/* When both alias and original are not overwritable, we can save /* Do not turn function in one comdat group into wrapper to another
the extra thunk wrapper for direct calls. */ 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 redirect_callers
= (!original_discardable = alias->get_availability () > AVAIL_INTERPOSABLE
&& !DECL_COMDAT_GROUP (alias->decl) && original->get_availability () > AVAIL_INTERPOSABLE
&& alias->get_availability () > AVAIL_INTERPOSABLE && !alias->instrumented_version;
&& original->get_availability () > AVAIL_INTERPOSABLE
&& !alias->instrumented_version);
}
else
{
create_alias = true;
create_thunk = false;
redirect_callers = false;
}
/* We want thunk to always jump to the local function body if (!redirect_callers && !create_wrapper)
unless the body is comdat and may be optimized out. */ {
if ((create_thunk || redirect_callers) if (dump_file)
&& (!original_discardable 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 (original->decl) && (DECL_COMDAT_GROUP (original->decl)
== DECL_COMDAT_GROUP (alias->decl))))) == DECL_COMDAT_GROUP (alias->decl))))
local_original {
= dyn_cast <cgraph_node *> (original->noninterposable_alias ()); local_original
= dyn_cast <cgraph_node *> (original->noninterposable_alias ());
if (!local_original) if (!local_original
{ && original->get_availability () > AVAIL_INTERPOSABLE)
if (dump_file) local_original = original;
fprintf (dump_file, "Noninterposable alias cannot be created.\n\n"); /* If original is COMDAT local, we can not really redirect external
callers to it. */
return false; 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 (!decl_binds_to_current_def_p (alias->decl)) if (!redirect_callers && !create_wrapper)
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Declaration does not bind to currect definition.\n\n"); fprintf (dump_file, "Not unifying; "
return false; "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 (redirect_callers)
{ {
/* If alias is non-overwritable then int nredirected = redirect_all_callers (alias, local_original);
all direct calls are safe to be redirected to the original. */
bool redirected = false;
while (alias->callers)
{
cgraph_edge *e = alias->callers;
e->redirect_callee (local_original);
push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
if (e->call_stmt) if (nredirected)
e->redirect_call_stmt_to_callee (); {
alias->icf_merged = true;
local_original->icf_merged = true;
pop_cfun (); if (dump_file && nredirected)
redirected = true; fprintf (dump_file, "%i local calls have been "
"redirected.\n", nredirected);
} }
alias->icf_merged = true; /* If all callers was redirected, do not produce wrapper. */
if (local_original->lto_file_data if (alias->can_remove_if_no_direct_calls_p ()
&& alias->lto_file_data && !alias->has_aliases_p ())
&& local_original->lto_file_data != alias->lto_file_data) {
local_original->merged = true; create_wrapper = false;
remove = true;
/* The alias function is removed if symbol address }
does not matter. */ gcc_assert (!create_alias);
if (!alias_address_matters)
alias->remove ();
if (dump_file && redirected)
fprintf (dump_file, "Callgraph local calls have been redirected.\n\n");
} }
/* If the condtion above is not met, we are lucky and can turn the
function into real alias. */
else if (create_alias) else if (create_alias)
{ {
alias->icf_merged = true; 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. */ /* Remove the function's body. */
ipa_merge_profiles (original, alias); ipa_merge_profiles (original, alias);
...@@ -791,39 +902,38 @@ sem_function::merge (sem_item *alias_item) ...@@ -791,39 +902,38 @@ sem_function::merge (sem_item *alias_item)
(set_local, (void *)(size_t) original->local_p (), true); (set_local, (void *)(size_t) original->local_p (), true);
if (dump_file) 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)) gcc_assert (!create_alias);
{ alias->icf_merged = true;
if (dump_file) local_original->icf_merged = true;
fprintf (dump_file, "Callgraph thunk cannot be created because of COMDAT\n");
return 0; ipa_merge_profiles (local_original, alias, true);
} alias->create_wrapper (local_original);
if (DECL_STATIC_CHAIN (alias->decl)) if (dump_file)
{ fprintf (dump_file, "Unified; Wrapper has been created.\n\n");
if (dump_file) }
fprintf (dump_file, "Thunk creation is risky for static-chain functions.\n\n"); gcc_assert (alias->icf_merged || remove);
original->icf_merged = true;
return 0; /* 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; 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;
ipa_merge_profiles (local_original, alias, true);
alias->create_wrapper (local_original);
if (dump_file) if (dump_file)
fprintf (dump_file, "Callgraph thunk has been created.\n\n"); 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; return true;
} }
...@@ -1319,7 +1429,8 @@ sem_variable::merge (sem_item *alias_item) ...@@ -1319,7 +1429,8 @@ sem_variable::merge (sem_item *alias_item)
if (!sem_item::target_supports_symbol_aliases_p ()) if (!sem_item::target_supports_symbol_aliases_p ())
{ {
if (dump_file) 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; return false;
} }
...@@ -1329,73 +1440,93 @@ sem_variable::merge (sem_item *alias_item) ...@@ -1329,73 +1440,93 @@ sem_variable::merge (sem_item *alias_item)
varpool_node *alias = alias_var->get_node (); varpool_node *alias = alias_var->get_node ();
bool original_discardable = false; 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 /* See if original is in a section that can be discarded if the main
symbol is not used. */ symbol is not used.
if (DECL_EXTERNAL (original->decl)) Also consider case where we have resolution info and we know that
original_discardable = true; original's definition is not going to be used. In this case we can not
if (original->resolution == LDPR_PREEMPTED_REG create alias to original. */
|| original->resolution == LDPR_PREEMPTED_IR) if (original->can_be_discarded_p ()
original_discardable = true; || (node->resolution != LDPR_UNKNOWN
if (original->can_be_discarded_p ()) && !decl_binds_to_current_def_p (node->decl)))
original_discardable = true; original_discardable = true;
gcc_assert (!TREE_ASM_WRITTEN (alias->decl)); gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
if (original_discardable || DECL_EXTERNAL (alias_var->decl) || /* Constant pool machinery is not quite ready for aliases.
!compare_sections (alias_var)) 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) 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; return false;
} }
else
/* We can not merge if address comparsion metters. */
if (original_address_matters && alias_address_matters
&& flag_merge_constants < 2)
{ {
// alias cycle creation check if (dump_file)
varpool_node *n = original; fprintf (dump_file,
"Not unifying; "
"adress of original and alias may be compared.\n\n");
return false;
}
while (n->alias) if (original_discardable
{ && (!DECL_COMDAT_GROUP (original->decl)
n = n->get_alias_target (); || (DECL_COMDAT_GROUP (original->decl)
if (n == alias) != DECL_COMDAT_GROUP (alias->decl))))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Varpool alias cannot be created (alias cycle).\n\n"); fprintf (dump_file, "Not unifying; alias cannot be created; "
"target is discardable\n\n");
return false; return false;
} }
} else
{
gcc_assert (!original->alias);
gcc_assert (!alias->alias);
alias->analyzed = false; alias->analyzed = false;
DECL_INITIAL (alias->decl) = NULL; DECL_INITIAL (alias->decl) = NULL;
alias->need_bounds_init = false; alias->need_bounds_init = false;
alias->remove_all_references (); 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); varpool_node::create_alias (alias_var->decl, decl);
alias->resolve_alias (original); alias->resolve_alias (original);
if (dump_file) 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; 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. */ /* Dump symbol to FILE. */
void void
......
...@@ -124,23 +124,3 @@ ipa_ref::referred_ref_list (void) ...@@ -124,23 +124,3 @@ ipa_ref::referred_ref_list (void)
{ {
return &referred->ref_list; 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) ...@@ -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. */ /* A helper for comdat_can_be_unshared_p. */
static bool static bool
...@@ -157,16 +136,14 @@ comdat_can_be_unshared_p_1 (symtab_node *node) ...@@ -157,16 +136,14 @@ comdat_can_be_unshared_p_1 (symtab_node *node)
{ {
if (!node->externally_visible) if (!node->externally_visible)
return true; return true;
/* When address is taken, we don't know if equality comparison won't if (node->address_can_be_compared_p ())
break eventually. Exception are virutal functions, C++ {
constructors/destructors and vtables, where this is not possible by struct ipa_ref *ref;
language standard. */
if (!DECL_VIRTUAL_P (node->decl) for (unsigned int i = 0; node->iterate_referring (i, ref); i++)
&& (TREE_CODE (node->decl) != FUNCTION_DECL if (ref->address_matters_p ())
|| (!DECL_CXX_CONSTRUCTOR_P (node->decl) return false;
&& !DECL_CXX_DESTRUCTOR_P (node->decl))) }
&& node->address_taken_from_non_vtable_p ())
return false;
/* If the symbol is used in some weird way, better to not touch it. */ /* If the symbol is used in some weird way, better to not touch it. */
if (node->force_output) if (node->force_output)
...@@ -387,7 +364,8 @@ can_replace_by_local_alias_in_vtable (symtab_node *node) ...@@ -387,7 +364,8 @@ can_replace_by_local_alias_in_vtable (symtab_node *node)
/* walk_tree callback that rewrites initializer references. */ /* walk_tree callback that rewrites initializer references. */
static tree 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 if (TREE_CODE (*tp) == VAR_DECL
|| TREE_CODE (*tp) == FUNCTION_DECL) || TREE_CODE (*tp) == FUNCTION_DECL)
......
...@@ -1156,7 +1156,11 @@ symtab_node::make_decl_local (void) ...@@ -1156,7 +1156,11 @@ symtab_node::make_decl_local (void)
return; return;
if (TREE_CODE (decl) == VAR_DECL) 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); else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
DECL_COMDAT (decl) = 0; DECL_COMDAT (decl) = 0;
...@@ -1513,6 +1517,19 @@ symtab_node::resolve_alias (symtab_node *target) ...@@ -1513,6 +1517,19 @@ symtab_node::resolve_alias (symtab_node *target)
/* If alias has address taken, so does the target. */ /* If alias has address taken, so does the target. */
if (address_taken) if (address_taken)
target->ultimate_alias_target ()->address_taken = true; 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; return true;
} }
...@@ -1863,3 +1880,31 @@ symtab_node::call_for_symbol_and_aliases_1 (bool (*callback) (symtab_node *, ...@@ -1863,3 +1880,31 @@ symtab_node::call_for_symbol_and_aliases_1 (bool (*callback) (symtab_node *,
} }
return false; 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);
}
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> 2015-02-26 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/65216 PR tree-optimization/65216
......
...@@ -43,6 +43,6 @@ int main() ...@@ -43,6 +43,6 @@ int main()
return 123; 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 { scan-ipa-dump "Equal symbols: 6" "icf" } } */
/* { dg-final { cleanup-ipa-dump "icf" } } */ /* { dg-final { cleanup-ipa-dump "icf" } } */
// { dg-do compile } // { dg-do compile }
// { dg-options "-O2 -Wsuggest-final-types -Wsuggest-final-methods" } // { dg-options "-O2 -Wsuggest-final-types -Wsuggest-final-methods" }
int c;
struct A { // { dg-warning "final would enable devirtualization of 4 calls" } 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 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 void
t(struct A *a) t(struct A *a)
......
/* Verify that simple indirect calls are inlined even without early /* Verify that simple indirect calls are inlined even without early
inlining.. */ inlining.. */
/* { dg-do run } */ /* { 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); extern void abort (void);
......
/* { 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" } } */
/* { 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" } } */
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */ /* { dg-options "-O2 -fdump-tree-optimized -fno-ipa-icf" } */
/* Should produce <=. */ /* Should produce <=. */
int test1 (int a, int b) int test1 (int a, int b)
......
/* PR tree-optimization/64454 */ /* PR tree-optimization/64454 */
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-vrp1" } */ /* { dg-options "-O2 -fdump-tree-vrp1 -fno-ipa-icf" } */
unsigned unsigned
f1 (unsigned x) f1 (unsigned x)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment