From 0a7246ee3887147e6934684ecf768ebe929a975e Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Fri, 27 Feb 2015 03:06:48 +0100 Subject: [PATCH] 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 From-SVN: r221040 --- gcc/ChangeLog | 32 ++ gcc/cgraph.c | 2 +- gcc/cgraph.h | 49 ++- gcc/cgraphclones.c | 2 + gcc/cgraphunit.c | 1 + gcc/common.opt | 4 +- gcc/ipa-icf.c | 449 +++++++++++++-------- gcc/ipa-ref.c | 20 - gcc/ipa-visibility.c | 42 +- gcc/symtab.c | 47 ++- gcc/testsuite/ChangeLog | 10 + gcc/testsuite/g++.dg/ipa/ipa-icf-4.C | 2 +- gcc/testsuite/g++.dg/warn/Wsuggest-final.C | 3 +- gcc/testsuite/gcc.dg/ipa/iinline-5.c | 2 +- gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c | 22 + gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c | 22 + gcc/testsuite/gcc.dg/pr28685-1.c | 2 +- gcc/testsuite/gcc.dg/pr64454.c | 2 +- 18 files changed, 490 insertions(+), 223 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e0bafa794e4..e4346a0ae93 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,35 @@ +2015-02-26 Jan Hubicka + Martin Liska + + 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 * doc/extend.texi (Function Attributes): Fix spelling and typos. diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 1ad08dc7631..5555439215e 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -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; diff --git a/gcc/cgraph.h b/gcc/cgraph.h index ec3cccda866..ff437cf18b8 100644 --- a/gcc/cgraph.h +++ b/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 (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 (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 (referring) + && DECL_VIRTUAL_P (referring->decl)) + return false; + return referred->address_can_be_compared_p (); +} + /* Build polymorphic call context for indirect call E. */ inline diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c index d0a5f707136..c74017615fd 100644 --- a/gcc/cgraphclones.c +++ b/gcc/cgraphclones.c @@ -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; diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 942826d3682..9f6878a19e3 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -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); diff --git a/gcc/common.opt b/gcc/common.opt index 4fa12f5fa38..b49ac46610c 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -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 diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c index 1d6cbebdf9c..5d50b6f639b 100644 --- a/gcc/ipa-icf.c +++ b/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 (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 (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 ())); - - /* 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 (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 (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 +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"); + 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 +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; } - 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_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 diff --git a/gcc/ipa-ref.c b/gcc/ipa-ref.c index f9af352f2f9..91c2f89af25 100644 --- a/gcc/ipa-ref.c +++ b/gcc/ipa-ref.c @@ -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 (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 (referred) - && (DECL_CXX_CONSTRUCTOR_P (referred->decl) || DECL_CXX_DESTRUCTOR_P (referred->decl))) - return false; - return true; -} diff --git a/gcc/ipa-visibility.c b/gcc/ipa-visibility.c index 9247e2976e7..7614cfbee4b 100644 --- a/gcc/ipa-visibility.c +++ b/gcc/ipa-visibility.c @@ -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 (ref->referring)) - return true; - node = dyn_cast (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) diff --git a/gcc/symtab.c b/gcc/symtab.c index 957457b25e5..a46ebd49efa 100644 --- a/gcc/symtab.c +++ b/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); +} diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 93fc50f2a91..3566d034365 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2015-02-26 Jan Hubicka + Martin Liska + + 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 PR tree-optimization/65216 diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C b/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C index 912e06b9b43..2cd7a2eed3a 100644 --- a/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C +++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C @@ -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" } } */ diff --git a/gcc/testsuite/g++.dg/warn/Wsuggest-final.C b/gcc/testsuite/g++.dg/warn/Wsuggest-final.C index 5371063559d..f1d419e144a 100644 --- a/gcc/testsuite/g++.dg/warn/Wsuggest-final.C +++ b/gcc/testsuite/g++.dg/warn/Wsuggest-final.C @@ -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) diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-5.c b/gcc/testsuite/gcc.dg/ipa/iinline-5.c index 8fb47caffcc..b83b8c25a92 100644 --- a/gcc/testsuite/gcc.dg/ipa/iinline-5.c +++ b/gcc/testsuite/gcc.dg/ipa/iinline-5.c @@ -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); diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c b/gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c new file mode 100644 index 00000000000..b1ec4d4bb66 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c b/gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c new file mode 100644 index 00000000000..0dbbcf1c0e1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/pr28685-1.c b/gcc/testsuite/gcc.dg/pr28685-1.c index 1d9d97ed615..1eb8c2d457b 100644 --- a/gcc/testsuite/gcc.dg/pr28685-1.c +++ b/gcc/testsuite/gcc.dg/pr28685-1.c @@ -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) diff --git a/gcc/testsuite/gcc.dg/pr64454.c b/gcc/testsuite/gcc.dg/pr64454.c index 33018d3bb18..35542349c70 100644 --- a/gcc/testsuite/gcc.dg/pr64454.c +++ b/gcc/testsuite/gcc.dg/pr64454.c @@ -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) -- 2.30.2