re PR bootstrap/65150 (r220875 causes bootstrap failure on x86_64 darwin)
authorJan Hubicka <hubicka@ucw.cz>
Fri, 27 Feb 2015 02:06:48 +0000 (03:06 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Fri, 27 Feb 2015 02:06:48 +0000 (02:06 +0000)
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

18 files changed:
gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/cgraphclones.c
gcc/cgraphunit.c
gcc/common.opt
gcc/ipa-icf.c
gcc/ipa-ref.c
gcc/ipa-visibility.c
gcc/symtab.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/ipa-icf-4.C
gcc/testsuite/g++.dg/warn/Wsuggest-final.C
gcc/testsuite/gcc.dg/ipa/iinline-5.c
gcc/testsuite/gcc.dg/ipa/ipa-cp-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipa-cp-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr28685-1.c
gcc/testsuite/gcc.dg/pr64454.c

index e0bafa7..e4346a0 100644 (file)
@@ -1,3 +1,35 @@
+2015-02-26  Jan Hubicka  <hubicka@ucw.cz>
+           Martin Liska  <mliska@suse.cz>
+
+       PR bootstrap/65150
+       * ipa-icf.c (symbol_compare_collection::symbol_compare_colleciton):
+       Use address_matters_p.
+       (redirect_all_callers, set_addressable): New functions.
+       (sem_function::merge): Reorganize and fix merging issues.
+       (sem_variable::merge): Likewise.
+       (sem_variable::compare_sections): Remove.
+       * common.opt (fmerge-all-constants, fmerge-constants): Remove
+       Optimization flag.
+       * symtab.c (symtab_node::resolve_alias): When alias has aliases,
+       redirect them.
+       (symtab_node::make_decl_local): Set ADDRESSABLE bit when
+       decl is used.
+       (address_matters_1): New function.
+       (symtab_node::address_matters_p): New function.
+       * cgraph.c (cgraph_edge::verify_corresponds_to_fndecl): Fix
+       check for merged flag.
+       * cgraph.h (address_matters_p): Declare.
+       (symtab_node::address_taken_from_non_vtable_p): Remove.
+       (symtab_node::address_can_be_compared_p): New method.
+       (ipa_ref::address_matters_p): Move here from ipa-ref.c; simplify.
+       * ipa-visibility.c (symtab_node::address_taken_from_non_vtable_p):
+       Remove.
+       (comdat_can_be_unshared_p_1) Use address_matters_p.
+       (update_vtable_references): Fix formating.
+       * ipa-ref.c (ipa_ref::address_matters_p): Move inline.
+       * cgraphunit.c (cgraph_node::create_wrapper): Drop UNINLINABLE flag.
+       * cgraphclones.c: Preserve merged and icf_merged flags.
+
 2015-02-26  Sandra Loosemore  <sandra@codesourcery.com>
 
        * doc/extend.texi (Function Attributes): Fix spelling and typos.
index 1ad08dc..5555439 100644 (file)
@@ -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;
 
index ec3cccd..ff437cf 100644 (file)
@@ -326,9 +326,6 @@ public:
   /* Return true if ONE and TWO are part of the same COMDAT group.  */
   inline bool in_same_comdat_group_p (symtab_node *target);
 
-  /* Return true when there is a reference to node and it is not vtable.  */
-  bool address_taken_from_non_vtable_p (void);
-
   /* Return true if symbol is known to be nonzero.  */
   bool nonzero_address ();
 
@@ -337,6 +334,15 @@ public:
      return 2 otherwise.   */
   int equal_address_to (symtab_node *s2);
 
+  /* Return true if symbol's address may possibly be compared to other
+     symbol's address.  */
+  bool address_matters_p ();
+
+  /* Return true if NODE's address can be compared.  This use properties
+     of NODE only and does not look if the address is actually taken in
+     interesting way.  For that use ADDRESS_MATTERS_P instead.  */
+  bool address_can_be_compared_p (void);
+
   /* Return symbol table node associated with DECL, if any,
      and NULL otherwise.  */
   static inline symtab_node *get (const_tree decl)
@@ -3022,6 +3028,43 @@ varpool_node::call_for_symbol_and_aliases (bool (*callback) (varpool_node *,
   return false;
 }
 
+/* Return true if NODE's address can be compared.  */
+
+inline bool
+symtab_node::address_can_be_compared_p ()
+{
+  /* Address of virtual tables and functions is never compared.  */
+  if (DECL_VIRTUAL_P (decl))
+    return false;
+  /* Address of C++ cdtors is never compared.  */
+  if (is_a <cgraph_node *> (this)
+      && (DECL_CXX_CONSTRUCTOR_P (decl)
+         || DECL_CXX_DESTRUCTOR_P (decl)))
+    return false;
+  /* Constant pool symbols addresses are never compared.
+     flag_merge_constants permits us to assume the same on readonly vars.  */
+  if (is_a <varpool_node *> (this)
+      && (DECL_IN_CONSTANT_POOL (decl)
+         || (flag_merge_constants >= 2
+             && TREE_READONLY (decl) && !TREE_THIS_VOLATILE (decl))))
+    return false;
+  return true;
+}
+
+/* Return true if refernece may be used in address compare.  */
+
+inline bool
+ipa_ref::address_matters_p ()
+{
+  if (use != IPA_REF_ADDR)
+    return false;
+  /* Addresses taken from virtual tables are never compared.  */
+  if (is_a <varpool_node *> (referring)
+      && DECL_VIRTUAL_P (referring->decl))
+    return false;
+  return referred->address_can_be_compared_p ();
+}
+
 /* Build polymorphic call context for indirect call E.  */
 
 inline
index d0a5f70..c740176 100644 (file)
@@ -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;
index 942826d..9f6878a 100644 (file)
@@ -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);
index 4fa12f5..b49ac46 100644 (file)
@@ -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
index 1d6cbeb..5d50b6f 100644 (file)
@@ -147,7 +147,7 @@ symbol_compare_collection::symbol_compare_collection (symtab_node *node)
 
       if (ref->referred->get_availability () <= AVAIL_INTERPOSABLE)
         {
-         if (ref->use == IPA_REF_ADDR)
+         if (ref->address_matters_p ())
            m_references.safe_push (ref->referred);
          else
            m_interposables.safe_push (ref->referred);
@@ -632,8 +632,56 @@ set_local (cgraph_node *node, void *data)
   return false;
 }
 
+/* TREE_ADDRESSABLE of NODE to true if DATA is non-NULL.
+   Helper for call_for_symbol_thunks_and_aliases.  */
+
+static bool
+set_addressable (varpool_node *node, void *)
+{
+  TREE_ADDRESSABLE (node->decl) = 1;
+  return false;
+}
+
+/* Redirect all callers of N and its aliases to TO.  Remove aliases if
+   possible.  Return number of redirections made.  */
+
+static int
+redirect_all_callers (cgraph_node *n, cgraph_node *to)
+{
+  int nredirected = 0;
+  ipa_ref *ref;
+
+  while (n->callers)
+    {
+      cgraph_edge *e = n->callers;
+      e->redirect_callee (to);
+      nredirected++;
+    }
+  for (unsigned i = 0; n->iterate_direct_aliases (i, ref);)
+    {
+      bool removed = false;
+      cgraph_node *n_alias = dyn_cast <cgraph_node *> (ref->referring);
+
+      if ((DECL_COMDAT_GROUP (n->decl)
+          && (DECL_COMDAT_GROUP (n->decl)
+              == DECL_COMDAT_GROUP (n_alias->decl)))
+         || (n_alias->get_availability () > AVAIL_INTERPOSABLE
+             && n->get_availability () > AVAIL_INTERPOSABLE))
+       {
+         nredirected += redirect_all_callers (n_alias, to);
+         if (n_alias->can_remove_if_no_direct_calls_p ()
+             && !n_alias->has_aliases_p ())
+           n_alias->remove ();
+       }
+      if (!removed)
+       i++;
+    }
+  return nredirected;
+}
+
 /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
    be applied.  */
+
 bool
 sem_function::merge (sem_item *alias_item)
 {
@@ -642,16 +690,29 @@ sem_function::merge (sem_item *alias_item)
   sem_function *alias_func = static_cast<sem_function *> (alias_item);
 
   cgraph_node *original = get_node ();
-  cgraph_node *local_original = original;
+  cgraph_node *local_original = NULL;
   cgraph_node *alias = alias_func->get_node ();
-  bool original_address_matters;
-  bool alias_address_matters;
 
-  bool create_thunk = false;
+  bool create_wrapper = false;
   bool create_alias = false;
   bool redirect_callers = false;
+  bool remove = false;
+
   bool original_discardable = false;
 
+  bool original_address_matters = original->address_matters_p ();
+  bool alias_address_matters = alias->address_matters_p ();
+
+  if (DECL_NO_INLINE_WARNING_P (original->decl)
+      != DECL_NO_INLINE_WARNING_P (alias->decl))
+    {
+      if (dump_file)
+       fprintf (dump_file,
+                "Not unifying; "
+                "DECL_NO_INLINE_WARNING mismatch.\n\n");
+      return false;
+    }
+
   /* Do not attempt to mix functions from different user sections;
      we do not know what user intends with those.  */
   if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
@@ -660,123 +721,173 @@ sem_function::merge (sem_item *alias_item)
     {
       if (dump_file)
        fprintf (dump_file,
-                "Not unifying; original and alias are in different sections.\n\n");
+                "Not unifying; "
+                "original and alias are in different sections.\n\n");
       return false;
     }
 
   /* See if original is in a section that can be discarded if the main
-     symbol is not used.  */
-  if (DECL_EXTERNAL (original->decl))
-    original_discardable = true;
-  if (original->resolution == LDPR_PREEMPTED_REG
-      || original->resolution == LDPR_PREEMPTED_IR)
-    original_discardable = true;
-  if (original->can_be_discarded_p ())
+     symbol is not used.
+
+     Also consider case where we have resolution info and we know that
+     original's definition is not going to be used.  In this case we can not
+     create alias to original.  */
+  if (original->can_be_discarded_p ()
+      || (node->resolution != LDPR_UNKNOWN
+         && !decl_binds_to_current_def_p (node->decl)))
     original_discardable = true;
 
-  /* See if original and/or alias address can be compared for equality.  */
-  original_address_matters
-    = (!DECL_VIRTUAL_P (original->decl)
-       && (original->externally_visible
-          || original->address_taken_from_non_vtable_p ()));
-  alias_address_matters
-    = (!DECL_VIRTUAL_P (alias->decl)
-       && (alias->externally_visible
-          || alias->address_taken_from_non_vtable_p ()));
-
-  /* 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 +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
index f9af352..91c2f89 100644 (file)
@@ -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 9247e29..7614cfb 100644 (file)
@@ -129,27 +129,6 @@ cgraph_node::local_p (void)
                                        
 }
 
-/* Return true when there is a reference to node and it is not vtable.  */
-
-bool
-symtab_node::address_taken_from_non_vtable_p (void)
-{
-  int i;
-  struct ipa_ref *ref = NULL;
-
-  for (i = 0; iterate_referring (i, ref); i++)
-    if (ref->use == IPA_REF_ADDR)
-      {
-       varpool_node *node;
-       if (is_a <cgraph_node *> (ref->referring))
-         return true;
-       node = dyn_cast <varpool_node *> (ref->referring);
-       if (!DECL_VIRTUAL_P (node->decl))
-         return true;
-      }
-  return false;
-}
-
 /* A helper for comdat_can_be_unshared_p.  */
 
 static bool
@@ -157,16 +136,14 @@ comdat_can_be_unshared_p_1 (symtab_node *node)
 {
   if (!node->externally_visible)
     return true;
-  /* When address is taken, we don't know if equality comparison won't
-     break eventually. Exception are virutal functions, C++
-     constructors/destructors and vtables, where this is not possible by
-     language standard.  */
-  if (!DECL_VIRTUAL_P (node->decl)
-      && (TREE_CODE (node->decl) != FUNCTION_DECL
-         || (!DECL_CXX_CONSTRUCTOR_P (node->decl)
-             && !DECL_CXX_DESTRUCTOR_P (node->decl)))
-      && node->address_taken_from_non_vtable_p ())
-    return false;
+  if (node->address_can_be_compared_p ())
+    {
+      struct ipa_ref *ref;
+
+      for (unsigned int i = 0; node->iterate_referring (i, ref); i++)
+       if (ref->address_matters_p ())
+         return false;
+    }
 
   /* If the symbol is used in some weird way, better to not touch it.  */
   if (node->force_output)
@@ -387,7 +364,8 @@ can_replace_by_local_alias_in_vtable (symtab_node *node)
 /* walk_tree callback that rewrites initializer references.   */
 
 static tree
-update_vtable_references (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+update_vtable_references (tree *tp, int *walk_subtrees,
+                         void *data ATTRIBUTE_UNUSED)
 {
   if (TREE_CODE (*tp) == VAR_DECL
       || TREE_CODE (*tp) == FUNCTION_DECL)
index 957457b..a46ebd4 100644 (file)
@@ -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);
+}
index 93fc50f..3566d03 100644 (file)
@@ -1,3 +1,13 @@
+2015-02-26  Jan Hubicka  <hubicka@ucw.cz>
+           Martin Liska  <mliska@suse.cz>
+
+       PR bootstrap/65150
+       * gcc.dg/pr64454.c: Disable ICF.
+       * gcc.dg/pr28685-1.c: Disable ICF
+       * gcc.dg/ipa/iinline-5.c: Disable ICF.
+       * g++.dg/warn/Wsuggest-final.C: Force methods to be different.
+       * g++.dg/ipa/ipa-icf-4.C: Update template.
+
 2015-02-26  Jakub Jelinek  <jakub@redhat.com>
 
        PR tree-optimization/65216
index 912e06b..2cd7a2e 100644 (file)
@@ -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 5371063..f1d419e 100644 (file)
@@ -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 8fb47ca..b83b8c2 100644 (file)
@@ -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 (file)
index 0000000..b1ec4d4
--- /dev/null
@@ -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 (file)
index 0000000..0dbbcf1
--- /dev/null
@@ -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" } } */
index 1d9d97e..1eb8c2d 100644 (file)
@@ -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 33018d3..3554234 100644 (file)
@@ -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)