This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: ipa-icf::merge TLC
- From: Jan Hubicka <hubicka at ucw dot cz>
- To: Jack Howarth <howarth dot at dot gcc at gmail dot com>
- Cc: Jan Hubicka <hubicka at ucw dot cz>, GCC Patches <gcc-patches at gcc dot gnu dot org>, mliska at suse dot cz, Jakub Jelinek <jakub at redhat dot com>
- Date: Fri, 27 Feb 2015 03:10:47 +0100
- Subject: Re: ipa-icf::merge TLC
- Authentication-results: sourceware.org; auth=none
- References: <20150225083810 dot GA86854 at kam dot mff dot cuni dot cz> <CAJMcOU-9=VSQK3RgDqezLbkQq=0CkCWC6VSaR1UxQaFopp4=sA at mail dot gmail dot com>
Hi,
this is the final version of patch I comitted. It has new fix to make_decl_local
to set TREE_ADDRESSABLE becuase we leave the flag undefined for non-local decls.
I also dropped Optimization from fmerge-all-constants, fmerge-constants
those can not be done in function speicfic way, I made ipa_ref::address_matters_p
to use fmerge-constants, added code to drop UNINLINABLE flag when function is turned
into a wrapper, added check to require DECL_NO_INLINE_WARNING_P match
and added code to set TREE_ADDRESSABLE when non-addressable and addressable vars are merged.
I also disabled merging for DECL_CONSTANT_POOL because it does not work (symtab does not
expect aliases here)
Bootstrapped/regtested x86_64-linux, comitted.
Honza
* 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.
* 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.
Index: symtab.c
===================================================================
--- symtab.c (revision 221034)
+++ symtab.c (working copy)
@@ -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
/* 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
}
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);
+}
Index: common.opt
===================================================================
--- common.opt (revision 221034)
+++ common.opt (working copy)
@@ -1644,11 +1644,11 @@ Report on permanent memory allocation in
; 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
Index: testsuite/gcc.dg/pr64454.c
===================================================================
--- testsuite/gcc.dg/pr64454.c (revision 221034)
+++ testsuite/gcc.dg/pr64454.c (working copy)
@@ -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)
Index: testsuite/gcc.dg/pr28685-1.c
===================================================================
--- testsuite/gcc.dg/pr28685-1.c (revision 221034)
+++ testsuite/gcc.dg/pr28685-1.c (working copy)
@@ -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)
Index: testsuite/gcc.dg/ipa/iinline-5.c
===================================================================
--- testsuite/gcc.dg/ipa/iinline-5.c (revision 221034)
+++ testsuite/gcc.dg/ipa/iinline-5.c (working copy)
@@ -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);
Index: testsuite/g++.dg/warn/Wsuggest-final.C
===================================================================
--- testsuite/g++.dg/warn/Wsuggest-final.C (revision 221034)
+++ testsuite/g++.dg/warn/Wsuggest-final.C (working copy)
@@ -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)
Index: testsuite/g++.dg/ipa/ipa-icf-4.C
===================================================================
--- testsuite/g++.dg/ipa/ipa-icf-4.C (revision 221034)
+++ testsuite/g++.dg/ipa/ipa-icf-4.C (working copy)
@@ -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" } } */
Index: ipa-ref.c
===================================================================
--- ipa-ref.c (revision 221034)
+++ ipa-ref.c (working copy)
@@ -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;
-}
Index: cgraph.c
===================================================================
--- cgraph.c (revision 221034)
+++ cgraph.c (working copy)
@@ -2630,7 +2630,7 @@ cgraph_edge::verify_corresponds_to_fndec
if (!node
|| node->body_removed
|| node->in_other_partition
- || node->icf_merged
+ || callee->icf_merged
|| callee->in_other_partition)
return false;
Index: cgraph.h
===================================================================
--- cgraph.h (revision 221034)
+++ cgraph.h (working copy)
@@ -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_aliase
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
Index: ipa-visibility.c
===================================================================
--- ipa-visibility.c (revision 221034)
+++ ipa-visibility.c (working copy)
@@ -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
{
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 (sy
/* 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)
Index: cgraphunit.c
===================================================================
--- cgraphunit.c (revision 221034)
+++ cgraphunit.c (working copy)
@@ -2468,6 +2468,7 @@ cgraph_node::create_wrapper (cgraph_node
release_body (true);
reset ();
+ DECL_UNINLINABLE (decl) = false;
DECL_RESULT (decl) = decl_result;
DECL_INITIAL (decl) = NULL;
allocate_struct_function (decl, false);
Index: ipa-icf.c
===================================================================
--- ipa-icf.c (revision 221034)
+++ ipa-icf.c (working copy)
@@ -147,7 +147,7 @@ symbol_compare_collection::symbol_compar
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 +633,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 +691,29 @@ sem_function::merge (sem_item *alias_ite
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 +722,173 @@ sem_function::merge (sem_item *alias_ite
{
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 ()));
-
- /* 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)
+ /* Creating a symtab alias is the optimal way to merge.
+ It however can not be used in the following cases:
+
+ 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. */
+ /* First see if we can produce wrapper. */
+
+ /* 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
- = (!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;
- }
+ = alias->get_availability () > AVAIL_INTERPOSABLE
+ && original->get_availability () > AVAIL_INTERPOSABLE
+ && !alias->instrumented_version;
- /* 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
+ 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 ());
-
- if (!local_original)
- {
- if (dump_file)
- fprintf (dump_file, "Noninterposable alias cannot be created.\n\n");
-
- return false;
- }
+ == 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 (!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)
- {
- cgraph_edge *e = alias->callers;
- e->redirect_callee (local_original);
- push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+ int nredirected = redirect_all_callers (alias, local_original);
- if (e->call_stmt)
- e->redirect_call_stmt_to_callee ();
+ if (nredirected)
+ {
+ alias->icf_merged = true;
+ local_original->icf_merged = true;
- 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 +903,38 @@ sem_function::merge (sem_item *alias_ite
(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");
+ gcc_assert (!create_alias);
+ alias->icf_merged = true;
+ local_original->icf_merged = true;
- 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, "Thunk creation is risky for static-chain functions.\n\n");
+ if (dump_file)
+ fprintf (dump_file, "Unified; Wrapper has been created.\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;
- 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)
- 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;
}
@@ -1319,7 +1430,8 @@ sem_variable::merge (sem_item *alias_ite
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 +1441,94 @@ sem_variable::merge (sem_item *alias_ite
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;
}
- else
+
+ /* We can not merge if address comparsion metters. */
+ if (original_address_matters && alias_address_matters
+ && flag_merge_constants < 2)
{
- // alias cycle creation check
- varpool_node *n = original;
+ if (dump_file)
+ fprintf (dump_file,
+ "Not unifying; "
+ "adress of original and alias may be compared.\n\n");
+ return false;
+ }
- 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");
+ 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;
- }
- }
+ return false;
+ }
+ else
+ {
+ 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_thunks_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
Index: cgraphclones.c
===================================================================
--- cgraphclones.c (revision 221034)
+++ cgraphclones.c (working copy)
@@ -471,6 +471,8 @@ cgraph_node::create_clone (tree decl, gc
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;