re PR lto/85574 (LTO bootstapped binaries differ)
[gcc.git] / gcc / ipa-icf.c
index a2d99e9a8e831b8a3258068f41f3a409c8515d8e..ebaf5825e1e99c6a525b08793c986ae8e17c021e 100644 (file)
@@ -1,5 +1,5 @@
 /* Interprocedural Identical Code Folding pass
-   Copyright (C) 2014-2015 Free Software Foundation, Inc.
+   Copyright (C) 2014-2019 Free Software Foundation, Inc.
 
    Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
 
@@ -52,82 +52,48 @@ along with GCC; see the file COPYING3.  If not see
 */
 
 #include "config.h"
+#define INCLUDE_LIST
 #include "system.h"
-#include <list>
 #include "coretypes.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "double-int.h"
-#include "input.h"
-#include "alias.h"
-#include "symtab.h"
-#include "options.h"
-#include "wide-int.h"
-#include "inchash.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
 #include "tree.h"
-#include "fold-const.h"
-#include "predict.h"
-#include "tm.h"
-#include "hard-reg-set.h"
-#include "function.h"
-#include "dominance.h"
-#include "cfg.h"
-#include "basic-block.h"
-#include "tree-ssa-alias.h"
-#include "internal-fn.h"
-#include "gimple-expr.h"
-#include "is-a.h"
 #include "gimple.h"
-#include "hashtab.h"
-#include "rtl.h"
-#include "flags.h"
-#include "statistics.h"
-#include "real.h"
-#include "fixed-value.h"
-#include "insn-config.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "cgraph.h"
+#include "coverage.h"
+#include "gimple-pretty-print.h"
+#include "data-streamer.h"
+#include "fold-const.h"
 #include "calls.h"
-#include "emit-rtl.h"
 #include "varasm.h"
-#include "stmt.h"
-#include "expr.h"
 #include "gimple-iterator.h"
-#include "gimple-ssa.h"
 #include "tree-cfg.h"
-#include "tree-phinodes.h"
-#include "stringpool.h"
-#include "tree-ssanames.h"
-#include "tree-dfa.h"
-#include "tree-pass.h"
-#include "gimple-pretty-print.h"
-#include "hash-map.h"
-#include "plugin-api.h"
-#include "ipa-ref.h"
-#include "cgraph.h"
-#include "alloc-pool.h"
 #include "symbol-summary.h"
 #include "ipa-prop.h"
-#include "ipa-inline.h"
-#include "cfgloop.h"
+#include "ipa-fnsummary.h"
 #include "except.h"
-#include "hash-table.h"
-#include "coverage.h"
 #include "attribs.h"
 #include "print-tree.h"
-#include "lto-streamer.h"
-#include "data-streamer.h"
 #include "ipa-utils.h"
 #include "ipa-icf-gimple.h"
 #include "ipa-icf.h"
 #include "stor-layout.h"
+#include "dbgcnt.h"
+#include "tree-vector-builder.h"
 
 using namespace ipa_icf_gimple;
 
 namespace ipa_icf {
 
+/* Initialization and computation of symtab node hash, there data
+   are propagated later on.  */
+
+static sem_item_optimizer *optimizer = NULL;
+
 /* Constructor.  */
 
 symbol_compare_collection::symbol_compare_collection (symtab_node *node)
@@ -140,9 +106,8 @@ symbol_compare_collection::symbol_compare_collection (symtab_node *node)
   if (is_a <varpool_node *> (node) && DECL_VIRTUAL_P (node->decl))
     return;
 
-  for (unsigned i = 0; i < node->num_references (); i++)
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
     {
-      ref = node->iterate_reference (i, ref);
       if (ref->address_matters_p ())
        m_references.safe_push (ref->referred);
 
@@ -167,27 +132,20 @@ symbol_compare_collection::symbol_compare_collection (symtab_node *node)
 
 /* Constructor for key value pair, where _ITEM is key and _INDEX is a target.  */
 
-sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
-  item (_item), index (_index)
+sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index)
+: item (_item), index (_index)
 {
 }
 
-/* Semantic item constructor for a node of _TYPE, where STACK is used
-   for bitmap memory allocation.  */
-
-sem_item::sem_item (sem_item_type _type,
-                   bitmap_obstack *stack): type(_type), hash(0)
+sem_item::sem_item (sem_item_type _type, bitmap_obstack *stack)
+: type (_type), m_hash (-1), m_hash_set (false)
 {
   setup (stack);
 }
 
-/* Semantic item constructor for a node of _TYPE, where STACK is used
-   for bitmap memory allocation. The item is based on symtab node _NODE
-   with computed _HASH.  */
-
 sem_item::sem_item (sem_item_type _type, symtab_node *_node,
-                   hashval_t _hash, bitmap_obstack *stack): type(_type),
-  node (_node), hash (_hash)
+                   bitmap_obstack *stack)
+: type (_type), node (_node), m_hash (-1), m_hash_set (false)
 {
   decl = node->decl;
   setup (stack);
@@ -238,13 +196,13 @@ sem_item::dump (void)
 {
   if (dump_file)
     {
-      fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
-              name(), node->order, (void *) node->decl);
+      fprintf (dump_file, "[%s] %s (tree:%p)\n", type == FUNC ? "func" : "var",
+              node->dump_name (), (void *) node->decl);
       fprintf (dump_file, "  hash: %u\n", get_hash ());
       fprintf (dump_file, "  references: ");
 
       for (unsigned i = 0; i < refs.length (); i++)
-       fprintf (dump_file, "%s%s ", refs[i]->name (),
+       fprintf (dump_file, "%s%s ", refs[i]->node->name (),
                 i < refs.length() - 1 ? "," : "");
 
       fprintf (dump_file, "\n");
@@ -263,24 +221,26 @@ sem_item::target_supports_symbol_aliases_p (void)
 #endif
 }
 
+void sem_item::set_hash (hashval_t hash)
+{
+  m_hash = hash;
+  m_hash_set = true;
+}
+
+hash_map<const_tree, hashval_t> sem_item::m_type_hash_cache;
+
 /* Semantic function constructor that uses STACK as bitmap memory stack.  */
 
-sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
-  m_checker (NULL), m_compared_func (NULL)
+sem_function::sem_function (bitmap_obstack *stack)
+: sem_item (FUNC, stack), m_checker (NULL), m_compared_func (NULL)
 {
-  arg_types.create (0);
   bb_sizes.create (0);
   bb_sorted.create (0);
 }
 
-/*  Constructor based on callgraph node _NODE with computed hash _HASH.
-    Bitmap STACK is used for memory allocation.  */
-sem_function::sem_function (cgraph_node *node, hashval_t hash,
-                           bitmap_obstack *stack):
-  sem_item (FUNC, node, hash, stack),
-  m_checker (NULL), m_compared_func (NULL)
+sem_function::sem_function (cgraph_node *node, bitmap_obstack *stack)
+: sem_item (FUNC, node, stack), m_checker (NULL), m_compared_func (NULL)
 {
-  arg_types.create (0);
   bb_sizes.create (0);
   bb_sorted.create (0);
 }
@@ -290,7 +250,6 @@ sem_function::~sem_function ()
   for (unsigned i = 0; i < bb_sorted.length (); i++)
     delete (bb_sorted[i]);
 
-  arg_types.release ();
   bb_sizes.release ();
   bb_sorted.release ();
 }
@@ -313,7 +272,7 @@ sem_function::get_bb_hash (const sem_bb *basic_block)
 hashval_t
 sem_function::get_hash (void)
 {
-  if(!hash)
+  if (!m_hash_set)
     {
       inchash::hash hstate;
       hstate.add_int (177454); /* Random number for function type.  */
@@ -328,23 +287,166 @@ sem_function::get_hash (void)
       for (unsigned i = 0; i < bb_sizes.length (); i++)
        hstate.add_int (bb_sizes[i]);
 
-      hash = hstate.end ();
+      /* Add common features of declaration itself.  */
+      if (DECL_FUNCTION_SPECIFIC_TARGET (decl))
+        hstate.add_hwi
+        (cl_target_option_hash
+          (TREE_TARGET_OPTION (DECL_FUNCTION_SPECIFIC_TARGET (decl))));
+      if (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl))
+       hstate.add_hwi
+        (cl_optimization_hash
+          (TREE_OPTIMIZATION (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl))));
+      hstate.add_flag (DECL_CXX_CONSTRUCTOR_P (decl));
+      hstate.add_flag (DECL_CXX_DESTRUCTOR_P (decl));
+
+      set_hash (hstate.end ());
     }
 
-  return hash;
+  return m_hash;
 }
 
+/* Compare properties of symbols N1 and N2 that does not affect semantics of
+   symbol itself but affects semantics of its references from USED_BY (which
+   may be NULL if it is unknown).  If comparsion is false, symbols
+   can still be merged but any symbols referring them can't.
+
+   If ADDRESS is true, do extra checking needed for IPA_REF_ADDR.
+
+   TODO: We can also split attributes to those that determine codegen of
+   a function body/variable constructor itself and those that are used when
+   referring to it.  */
+
+bool
+sem_item::compare_referenced_symbol_properties (symtab_node *used_by,
+                                               symtab_node *n1,
+                                               symtab_node *n2,
+                                               bool address)
+{
+  if (is_a <cgraph_node *> (n1))
+    {
+      /* Inline properties matters: we do now want to merge uses of inline
+        function to uses of normal function because inline hint would be lost.
+        We however can merge inline function to noinline because the alias
+        will keep its DECL_DECLARED_INLINE flag.
+
+        Also ignore inline flag when optimizing for size or when function
+        is known to not be inlinable.
+
+        TODO: the optimize_size checks can also be assumed to be true if
+        unit has no !optimize_size functions. */
+
+      if ((!used_by || address || !is_a <cgraph_node *> (used_by)
+          || !opt_for_fn (used_by->decl, optimize_size))
+         && !opt_for_fn (n1->decl, optimize_size)
+         && n1->get_availability () > AVAIL_INTERPOSABLE
+         && (!DECL_UNINLINABLE (n1->decl) || !DECL_UNINLINABLE (n2->decl)))
+       {
+         if (DECL_DISREGARD_INLINE_LIMITS (n1->decl)
+             != DECL_DISREGARD_INLINE_LIMITS (n2->decl))
+           return return_false_with_msg
+                    ("DECL_DISREGARD_INLINE_LIMITS are different");
+
+         if (DECL_DECLARED_INLINE_P (n1->decl)
+             != DECL_DECLARED_INLINE_P (n2->decl))
+           return return_false_with_msg ("inline attributes are different");
+       }
+
+      if (DECL_IS_OPERATOR_NEW (n1->decl)
+         != DECL_IS_OPERATOR_NEW (n2->decl))
+       return return_false_with_msg ("operator new flags are different");
+    }
+
+  /* Merging two definitions with a reference to equivalent vtables, but
+     belonging to a different type may result in ipa-polymorphic-call analysis
+     giving a wrong answer about the dynamic type of instance.  */
+  if (is_a <varpool_node *> (n1))
+    {
+      if ((DECL_VIRTUAL_P (n1->decl) || DECL_VIRTUAL_P (n2->decl))
+         && (DECL_VIRTUAL_P (n1->decl) != DECL_VIRTUAL_P (n2->decl)
+             || !types_must_be_same_for_odr (DECL_CONTEXT (n1->decl),
+                                             DECL_CONTEXT (n2->decl)))
+         && (!used_by || !is_a <cgraph_node *> (used_by) || address
+             || opt_for_fn (used_by->decl, flag_devirtualize)))
+       return return_false_with_msg
+                ("references to virtual tables can not be merged");
+
+      if (address && DECL_ALIGN (n1->decl) != DECL_ALIGN (n2->decl))
+       return return_false_with_msg ("alignment mismatch");
+
+      /* For functions we compare attributes in equals_wpa, because we do
+        not know what attributes may cause codegen differences, but for
+        variables just compare attributes for references - the codegen
+        for constructors is affected only by those attributes that we lower
+        to explicit representation (such as DECL_ALIGN or DECL_SECTION).  */
+      if (!attribute_list_equal (DECL_ATTRIBUTES (n1->decl),
+                                DECL_ATTRIBUTES (n2->decl)))
+       return return_false_with_msg ("different var decl attributes");
+      if (comp_type_attributes (TREE_TYPE (n1->decl),
+                               TREE_TYPE (n2->decl)) != 1)
+       return return_false_with_msg ("different var type attributes");
+    }
+
+  /* When matching virtual tables, be sure to also match information
+     relevant for polymorphic call analysis.  */
+  if (used_by && is_a <varpool_node *> (used_by)
+      && DECL_VIRTUAL_P (used_by->decl))
+    {
+      if (DECL_VIRTUAL_P (n1->decl) != DECL_VIRTUAL_P (n2->decl))
+       return return_false_with_msg ("virtual flag mismatch");
+      if (DECL_VIRTUAL_P (n1->decl) && is_a <cgraph_node *> (n1)
+         && (DECL_FINAL_P (n1->decl) != DECL_FINAL_P (n2->decl)))
+       return return_false_with_msg ("final flag mismatch");
+    }
+  return true;
+}
+
+/* Hash properties that are compared by compare_referenced_symbol_properties. */
+
+void
+sem_item::hash_referenced_symbol_properties (symtab_node *ref,
+                                            inchash::hash &hstate,
+                                            bool address)
+{
+  if (is_a <cgraph_node *> (ref))
+    {
+      if ((type != FUNC || address || !opt_for_fn (decl, optimize_size))
+         && !opt_for_fn (ref->decl, optimize_size)
+         && !DECL_UNINLINABLE (ref->decl))
+       {
+         hstate.add_flag (DECL_DISREGARD_INLINE_LIMITS (ref->decl));
+         hstate.add_flag (DECL_DECLARED_INLINE_P (ref->decl));
+       }
+      hstate.add_flag (DECL_IS_OPERATOR_NEW (ref->decl));
+    }
+  else if (is_a <varpool_node *> (ref))
+    {
+      hstate.add_flag (DECL_VIRTUAL_P (ref->decl));
+      if (address)
+       hstate.add_int (DECL_ALIGN (ref->decl));
+    }
+}
+
+
 /* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
    point to a same function. Comparison can be skipped if IGNORED_NODES
    contains these nodes.  ADDRESS indicate if address is taken.  */
 
 bool
-sem_item::compare_cgraph_references (
+sem_item::compare_symbol_references (
     hash_map <symtab_node *, sem_item *> &ignored_nodes,
     symtab_node *n1, symtab_node *n2, bool address)
 {
   enum availability avail1, avail2;
 
+  if (n1 == n2)
+    return true;
+
+  /* Never match variable and function.  */
+  if (is_a <varpool_node *> (n1) != is_a <varpool_node *> (n2))
+    return false;
+
+  if (!compare_referenced_symbol_properties (node, n1, n2, address))
+    return false;
   if (address && n1->equal_address_to (n2) == 1)
     return true;
   if (!address && n1->semantically_equivalent_p (n2))
@@ -353,8 +455,8 @@ sem_item::compare_cgraph_references (
   n1 = n1->ultimate_alias_target (&avail1);
   n2 = n2->ultimate_alias_target (&avail2);
 
-  if (avail1 >= AVAIL_INTERPOSABLE && ignored_nodes.get (n1)
-      && avail2 >= AVAIL_INTERPOSABLE && ignored_nodes.get (n2))
+  if (avail1 > AVAIL_INTERPOSABLE && ignored_nodes.get (n1)
+      && avail2 > AVAIL_INTERPOSABLE && ignored_nodes.get (n2))
     return true;
 
   return return_false_with_msg ("different references");
@@ -379,6 +481,46 @@ bool sem_function::compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2)
   return true;
 }
 
+/* Return true if parameter I may be used.  */
+
+bool
+sem_function::param_used_p (unsigned int i)
+{
+  if (ipa_node_params_sum == NULL)
+    return true;
+
+  struct ipa_node_params *parms_info = IPA_NODE_REF (get_node ());
+
+  if (vec_safe_length (parms_info->descriptors) <= i)
+    return true;
+
+  return ipa_is_param_used (IPA_NODE_REF (get_node ()), i);
+}
+
+/* Perform additional check needed to match types function parameters that are
+   used.  Unlike for normal decls it matters if type is TYPE_RESTRICT and we
+   make an assumption that REFERENCE_TYPE parameters are always non-NULL.  */
+
+bool
+sem_function::compatible_parm_types_p (tree parm1, tree parm2)
+{
+  /* Be sure that parameters are TBAA compatible.  */
+  if (!func_checker::compatible_types_p (parm1, parm2))
+    return return_false_with_msg ("parameter type is not compatible");
+
+  if (POINTER_TYPE_P (parm1)
+      && (TYPE_RESTRICT (parm1) != TYPE_RESTRICT (parm2)))
+    return return_false_with_msg ("argument restrict flag mismatch");
+
+  /* nonnull_arg_p implies non-zero range to REFERENCE types.  */
+  if (POINTER_TYPE_P (parm1)
+      && TREE_CODE (parm1) != TREE_CODE (parm2)
+      && opt_for_fn (decl, flag_delete_null_pointer_checks))
+    return return_false_with_msg ("pointer wrt reference mismatch");
+
+  return true;
+}
+
 /* Fast equality function based on knowledge known in WPA.  */
 
 bool
@@ -386,43 +528,172 @@ sem_function::equals_wpa (sem_item *item,
                          hash_map <symtab_node *, sem_item *> &ignored_nodes)
 {
   gcc_assert (item->type == FUNC);
+  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+  cgraph_node *cnode2 = dyn_cast <cgraph_node *> (item->node);
 
   m_compared_func = static_cast<sem_function *> (item);
 
-  if (arg_types.length () != m_compared_func->arg_types.length ())
-    return return_false_with_msg ("different number of arguments");
+  if (cnode->thunk.thunk_p != cnode2->thunk.thunk_p)
+    return return_false_with_msg ("thunk_p mismatch");
+
+  if (cnode->thunk.thunk_p)
+    {
+      if (cnode->thunk.fixed_offset != cnode2->thunk.fixed_offset)
+        return return_false_with_msg ("thunk fixed_offset mismatch");
+      if (cnode->thunk.virtual_value != cnode2->thunk.virtual_value)
+        return return_false_with_msg ("thunk virtual_value mismatch");
+      if (cnode->thunk.indirect_offset != cnode2->thunk.indirect_offset)
+        return return_false_with_msg ("thunk indirect_offset mismatch");
+      if (cnode->thunk.this_adjusting != cnode2->thunk.this_adjusting)
+        return return_false_with_msg ("thunk this_adjusting mismatch");
+      if (cnode->thunk.virtual_offset_p != cnode2->thunk.virtual_offset_p)
+        return return_false_with_msg ("thunk virtual_offset_p mismatch");
+      if (cnode->thunk.add_pointer_bounds_args
+         != cnode2->thunk.add_pointer_bounds_args)
+        return return_false_with_msg ("thunk add_pointer_bounds_args mismatch");
+    }
+
+  /* Compare special function DECL attributes.  */
+  if (DECL_FUNCTION_PERSONALITY (decl)
+      != DECL_FUNCTION_PERSONALITY (item->decl))
+    return return_false_with_msg ("function personalities are different");
+
+  if (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl)
+       != DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (item->decl))
+    return return_false_with_msg ("intrument function entry exit "
+                                 "attributes are different");
+
+  if (DECL_NO_LIMIT_STACK (decl) != DECL_NO_LIMIT_STACK (item->decl))
+    return return_false_with_msg ("no stack limit attributes are different");
+
+  if (DECL_CXX_CONSTRUCTOR_P (decl) != DECL_CXX_CONSTRUCTOR_P (item->decl))
+    return return_false_with_msg ("DECL_CXX_CONSTRUCTOR mismatch");
+
+  if (DECL_CXX_DESTRUCTOR_P (decl) != DECL_CXX_DESTRUCTOR_P (item->decl))
+    return return_false_with_msg ("DECL_CXX_DESTRUCTOR mismatch");
+
+  /* TODO: pure/const flags mostly matters only for references, except for
+     the fact that codegen takes LOOPING flag as a hint that loops are
+     finite.  We may arrange the code to always pick leader that has least
+     specified flags and then this can go into comparing symbol properties.  */
+  if (flags_from_decl_or_type (decl) != flags_from_decl_or_type (item->decl))
+    return return_false_with_msg ("decl_or_type flags are different");
+
+  /* Do not match polymorphic constructors of different types.  They calls
+     type memory location for ipa-polymorphic-call and we do not want
+     it to get confused by wrong type.  */
+  if (DECL_CXX_CONSTRUCTOR_P (decl)
+      && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+    {
+      if (TREE_CODE (TREE_TYPE (item->decl)) != METHOD_TYPE)
+        return return_false_with_msg ("DECL_CXX_CONSTURCTOR type mismatch");
+      else if (!func_checker::compatible_polymorphic_types_p
+                (TYPE_METHOD_BASETYPE (TREE_TYPE (decl)),
+                 TYPE_METHOD_BASETYPE (TREE_TYPE (item->decl)), false))
+        return return_false_with_msg ("ctor polymorphic type mismatch");
+    }
+
+  /* Checking function TARGET and OPTIMIZATION flags.  */
+  cl_target_option *tar1 = target_opts_for_fn (decl);
+  cl_target_option *tar2 = target_opts_for_fn (item->decl);
+
+  if (tar1 != tar2 && !cl_target_option_eq (tar1, tar2))
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "target flags difference");
+         cl_target_option_print_diff (dump_file, 2, tar1, tar2);
+       }
+
+      return return_false_with_msg ("Target flags are different");
+    }
+
+  cl_optimization *opt1 = opts_for_fn (decl);
+  cl_optimization *opt2 = opts_for_fn (item->decl);
+
+  if (opt1 != opt2 && !cl_optimization_option_eq (opt1, opt2))
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "optimization flags difference");
+         cl_optimization_print_diff (dump_file, 2, opt1, opt2);
+       }
+
+      return return_false_with_msg ("optimization flags are different");
+    }
+
+  /* Result type checking.  */
+  if (!func_checker::compatible_types_p
+        (TREE_TYPE (TREE_TYPE (decl)),
+         TREE_TYPE (TREE_TYPE (m_compared_func->decl))))
+    return return_false_with_msg ("result types are different");
 
   /* Checking types of arguments.  */
-  for (unsigned i = 0; i < arg_types.length (); i++)
+  tree list1 = TYPE_ARG_TYPES (TREE_TYPE (decl)),
+       list2 = TYPE_ARG_TYPES (TREE_TYPE (m_compared_func->decl));
+  for (unsigned i = 0; list1 && list2;
+       list1 = TREE_CHAIN (list1), list2 = TREE_CHAIN (list2), i++)
     {
+      tree parm1 = TREE_VALUE (list1);
+      tree parm2 = TREE_VALUE (list2);
+
       /* This guard is here for function pointer with attributes (pr59927.c).  */
-      if (!arg_types[i] || !m_compared_func->arg_types[i])
+      if (!parm1 || !parm2)
        return return_false_with_msg ("NULL argument type");
 
-      /* Polymorphic comparison is executed just for non-leaf functions.  */
-      bool is_not_leaf = get_node ()->callees != NULL
-                        || get_node ()->indirect_calls != NULL;
+      /* Verify that types are compatible to ensure that both functions
+        have same calling conventions.  */
+      if (!types_compatible_p (parm1, parm2))
+       return return_false_with_msg ("parameter types are not compatible");
 
-      if (!func_checker::compatible_types_p (arg_types[i],
-                                            m_compared_func->arg_types[i],
-                                            is_not_leaf, i == 0))
-       return return_false_with_msg ("argument type is different");
+      if (!param_used_p (i))
+       continue;
+
+      /* Perform additional checks for used parameters.  */
+      if (!compatible_parm_types_p (parm1, parm2))
+       return false;
     }
 
-  /* Result type checking.  */
-  if (!func_checker::compatible_types_p (result_type,
-                                        m_compared_func->result_type))
-    return return_false_with_msg ("result types are different");
+  if (list1 || list2)
+    return return_false_with_msg ("Mismatched number of parameters");
 
   if (node->num_references () != item->node->num_references ())
     return return_false_with_msg ("different number of references");
 
+  /* Checking function attributes.
+     This is quadratic in number of attributes  */
+  if (comp_type_attributes (TREE_TYPE (decl),
+                           TREE_TYPE (item->decl)) != 1)
+    return return_false_with_msg ("different type attributes");
+  if (!attribute_list_equal (DECL_ATTRIBUTES (decl),
+                            DECL_ATTRIBUTES (item->decl)))
+    return return_false_with_msg ("different decl attributes");
+
+  /* The type of THIS pointer type memory location for
+     ipa-polymorphic-call-analysis.  */
+  if (opt_for_fn (decl, flag_devirtualize)
+      && (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE
+          || TREE_CODE (TREE_TYPE (item->decl)) == METHOD_TYPE)
+      && param_used_p (0)
+      && compare_polymorphic_p ())
+    {
+      if (TREE_CODE (TREE_TYPE (decl)) != TREE_CODE (TREE_TYPE (item->decl)))
+       return return_false_with_msg ("METHOD_TYPE and FUNCTION_TYPE mismatch");
+      if (!func_checker::compatible_polymorphic_types_p
+          (TYPE_METHOD_BASETYPE (TREE_TYPE (decl)),
+           TYPE_METHOD_BASETYPE (TREE_TYPE (item->decl)), false))
+       return return_false_with_msg ("THIS pointer ODR type mismatch");
+    }
+
   ipa_ref *ref = NULL, *ref2 = NULL;
   for (unsigned i = 0; node->iterate_reference (i, ref); i++)
     {
       item->node->iterate_reference (i, ref2);
 
-      if (!compare_cgraph_references (ignored_nodes, ref->referred,
+      if (ref->use != ref2->use)
+       return return_false_with_msg ("reference use mismatch");
+
+      if (!compare_symbol_references (ignored_nodes, ref->referred,
                                      ref2->referred,
                                      ref->address_matters_p ()))
        return false;
@@ -433,28 +704,116 @@ sem_function::equals_wpa (sem_item *item,
 
   while (e1 && e2)
     {
-      if (!compare_cgraph_references (ignored_nodes, e1->callee,
+      if (!compare_symbol_references (ignored_nodes, e1->callee,
                                      e2->callee, false))
        return false;
+      if (!compare_edge_flags (e1, e2))
+       return false;
 
       e1 = e1->next_callee;
       e2 = e2->next_callee;
     }
 
   if (e1 || e2)
-    return return_false_with_msg ("different number of edges");
+    return return_false_with_msg ("different number of calls");
+
+  e1 = dyn_cast <cgraph_node *> (node)->indirect_calls;
+  e2 = dyn_cast <cgraph_node *> (item->node)->indirect_calls;
+
+  while (e1 && e2)
+    {
+      if (!compare_edge_flags (e1, e2))
+       return false;
+
+      e1 = e1->next_callee;
+      e2 = e2->next_callee;
+    }
+
+  if (e1 || e2)
+    return return_false_with_msg ("different number of indirect calls");
 
   return true;
 }
 
+/* Update hash by address sensitive references. We iterate over all
+   sensitive references (address_matters_p) and we hash ultime alias
+   target of these nodes, which can improve a semantic item hash.
+
+   Also hash in referenced symbols properties.  This can be done at any time
+   (as the properties should not change), but it is convenient to do it here
+   while we walk the references anyway.  */
+
+void
+sem_item::update_hash_by_addr_refs (hash_map <symtab_node *,
+                                   sem_item *> &m_symtab_node_map)
+{
+  ipa_ref* ref;
+  inchash::hash hstate (get_hash ());
+
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+    {
+      hstate.add_int (ref->use);
+      hash_referenced_symbol_properties (ref->referred, hstate,
+                                        ref->use == IPA_REF_ADDR);
+      if (ref->address_matters_p () || !m_symtab_node_map.get (ref->referred))
+       hstate.add_int (ref->referred->ultimate_alias_target ()->order);
+    }
+
+  if (is_a <cgraph_node *> (node))
+    {
+      for (cgraph_edge *e = dyn_cast <cgraph_node *> (node)->callers; e;
+          e = e->next_caller)
+       {
+         sem_item **result = m_symtab_node_map.get (e->callee);
+         hash_referenced_symbol_properties (e->callee, hstate, false);
+         if (!result)
+           hstate.add_int (e->callee->ultimate_alias_target ()->order);
+       }
+    }
+
+  set_hash (hstate.end ());
+}
+
+/* Update hash by computed local hash values taken from different
+   semantic items.
+   TODO: stronger SCC based hashing would be desirable here.  */
+
+void
+sem_item::update_hash_by_local_refs (hash_map <symtab_node *,
+                                    sem_item *> &m_symtab_node_map)
+{
+  ipa_ref* ref;
+  inchash::hash state (get_hash ());
+
+  for (unsigned j = 0; node->iterate_reference (j, ref); j++)
+    {
+      sem_item **result = m_symtab_node_map.get (ref->referring);
+      if (result)
+       state.merge_hash ((*result)->get_hash ());
+    }
+
+  if (type == FUNC)
+    {
+      for (cgraph_edge *e = dyn_cast <cgraph_node *> (node)->callees; e;
+          e = e->next_callee)
+       {
+         sem_item **result = m_symtab_node_map.get (e->caller);
+         if (result)
+           state.merge_hash ((*result)->get_hash ());
+       }
+    }
+
+  global_hash = state.end ();
+}
+
 /* Returns true if the item equals to ITEM given as argument.  */
 
 bool
 sem_function::equals (sem_item *item,
-                     hash_map <symtab_node *, sem_item *> &ignored_nodes)
+                     hash_map <symtab_node *, sem_item *> &)
 {
   gcc_assert (item->type == FUNC);
-  bool eq = equals_private (item, ignored_nodes);
+  bool eq = equals_private (item);
 
   if (m_checker != NULL)
     {
@@ -464,9 +823,10 @@ sem_function::equals (sem_item *item,
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     fprintf (dump_file,
-            "Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
-            name(), item->name (), node->order, item->node->order, asm_name (),
-            item->asm_name (), eq ? "true" : "false");
+            "Equals called for: %s:%s with result: %s\n\n",
+            node->dump_name (),
+            item->node->dump_name (),
+            eq ? "true" : "false");
 
   return eq;
 }
@@ -474,8 +834,7 @@ sem_function::equals (sem_item *item,
 /* Processes function equality comparison.  */
 
 bool
-sem_function::equals_private (sem_item *item,
-                             hash_map <symtab_node *, sem_item *> &ignored_nodes)
+sem_function::equals_private (sem_item *item)
 {
   if (item->type != FUNC)
     return false;
@@ -495,93 +854,31 @@ sem_function::equals_private (sem_item *item,
       || cfg_checksum != m_compared_func->cfg_checksum)
     return return_false ();
 
-  if (!equals_wpa (item, ignored_nodes))
-    return false;
-
-  /* Checking function TARGET and OPTIMIZATION flags.  */
-  cl_target_option *tar1 = target_opts_for_fn (decl);
-  cl_target_option *tar2 = target_opts_for_fn (m_compared_func->decl);
-
-  if (tar1 != NULL && tar2 != NULL)
-    {
-      if (!cl_target_option_eq (tar1, tar2))
-       {
-         if (dump_file && (dump_flags & TDF_DETAILS))
-           {
-             fprintf (dump_file, "target flags difference");
-             cl_target_option_print_diff (dump_file, 2, tar1, tar2);
-           }
-
-         return return_false_with_msg ("Target flags are different");
-       }
-    }
-  else if (tar1 != NULL || tar2 != NULL)
-    return return_false_with_msg ("Target flags are different");
-
-  cl_optimization *opt1 = opts_for_fn (decl);
-  cl_optimization *opt2 = opts_for_fn (m_compared_func->decl);
-
-  if (opt1 != NULL && opt2 != NULL)
-    {
-      if (memcmp (opt1, opt2, sizeof(cl_optimization)))
-       {
-         if (dump_file && (dump_flags & TDF_DETAILS))
-           {
-             fprintf (dump_file, "optimization flags difference");
-             cl_optimization_print_diff (dump_file, 2, opt1, opt2);
-           }
-
-         return return_false_with_msg ("optimization flags are different");
-       }
-    }
-  else if (opt1 != NULL || opt2 != NULL)
-    return return_false_with_msg ("optimization flags are different");
-
-  /* Checking function arguments.  */
-  tree decl1 = DECL_ATTRIBUTES (decl);
-  tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
-
   m_checker = new func_checker (decl, m_compared_func->decl,
                                compare_polymorphic_p (),
                                false,
                                &refs_set,
                                &m_compared_func->refs_set);
-  while (decl1)
+  arg1 = DECL_ARGUMENTS (decl);
+  arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+  for (unsigned i = 0;
+       arg1 && arg2; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2), i++)
     {
-      if (decl2 == NULL)
-       return return_false ();
-
-      if (get_attribute_name (decl1) != get_attribute_name (decl2))
-       return return_false ();
-
-      tree attr_value1 = TREE_VALUE (decl1);
-      tree attr_value2 = TREE_VALUE (decl2);
-
-      if (attr_value1 && attr_value2)
-       {
-         bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1),
-                                                TREE_VALUE (attr_value2));
-         if (!ret)
-           return return_false_with_msg ("attribute values are different");
-       }
-      else if (!attr_value1 && !attr_value2)
-       {}
-      else
-       return return_false ();
-
-      decl1 = TREE_CHAIN (decl1);
-      decl2 = TREE_CHAIN (decl2);
+      if (!types_compatible_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
+       return return_false_with_msg ("argument types are not compatible");
+      if (!param_used_p (i))
+       continue;
+      /* Perform additional checks for used parameters.  */
+      if (!compatible_parm_types_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
+       return false;
+      if (!m_checker->compare_decl (arg1, arg2))
+        return return_false ();
     }
+  if (arg1 || arg2)
+    return return_false_with_msg ("Mismatched number of arguments");
 
-  if (decl1 != decl2)
-    return return_false();
-
-
-  for (arg1 = DECL_ARGUMENTS (decl),
-       arg2 = DECL_ARGUMENTS (m_compared_func->decl);
-       arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
-    if (!m_checker->compare_decl (arg1, arg2))
-      return return_false ();
+  if (!dyn_cast <cgraph_node *> (node)->has_gimple_body_p ())
+    return true;
 
   /* Fill-up label dictionary.  */
   for (unsigned i = 0; i < bb_sorted.length (); ++i)
@@ -632,30 +929,6 @@ sem_function::equals_private (sem_item *item,
     if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb))
       return return_false_with_msg ("PHI node comparison returns false");
 
-  /* Compare special function DECL attributes.  */
-  if (DECL_FUNCTION_PERSONALITY (decl) != DECL_FUNCTION_PERSONALITY (item->decl))
-    return return_false_with_msg ("function personalities are different");
-
-  if (DECL_DISREGARD_INLINE_LIMITS (decl) != DECL_DISREGARD_INLINE_LIMITS (item->decl))
-    return return_false_with_msg ("DECL_DISREGARD_INLINE_LIMITS are different");
-
-  if (DECL_DECLARED_INLINE_P (decl) != DECL_DECLARED_INLINE_P (item->decl))
-    return return_false_with_msg ("inline attributes are different");
-
-  if (DECL_IS_OPERATOR_NEW (decl) != DECL_IS_OPERATOR_NEW (item->decl))
-    return return_false_with_msg ("operator new flags are different");
-
-  if (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl)
-       != DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (item->decl))
-    return return_false_with_msg ("intrument function entry exit "
-                                 "attributes are different");
-
-  if (DECL_NO_LIMIT_STACK (decl) != DECL_NO_LIMIT_STACK (item->decl))
-    return return_false_with_msg ("no stack limit attributes are different");
-
-  if (flags_from_decl_or_type (decl) != flags_from_decl_or_type (item->decl))
-    return return_false_with_msg ("decl_or_type flags are different");
-
   return result;
 }
 
@@ -697,12 +970,22 @@ redirect_all_callers (cgraph_node *n, cgraph_node *to)
 {
   int nredirected = 0;
   ipa_ref *ref;
+  cgraph_edge *e = n->callers;
 
-  while (n->callers)
+  while (e)
     {
-      cgraph_edge *e = n->callers;
-      e->redirect_callee (to);
-      nredirected++;
+      /* Redirecting thunks to interposable symbols or symbols in other sections
+        may not be supported by target output code.  Play safe for now and
+        punt on redirection.  */
+      if (!e->caller->thunk.thunk_p)
+       {
+         struct cgraph_edge *nexte = e->next_caller;
+          e->redirect_callee (to);
+         e = nexte;
+          nredirected++;
+       }
+      else
+       e = e->next_callee;
     }
   for (unsigned i = 0; n->iterate_direct_aliases (i, ref);)
     {
@@ -717,6 +1000,8 @@ redirect_all_callers (cgraph_node *n, cgraph_node *to)
        {
          nredirected += redirect_all_callers (n_alias, to);
          if (n_alias->can_remove_if_no_direct_calls_p ()
+             && !n_alias->call_for_symbol_and_aliases (cgraph_node::has_thunk_p,
+                                                       NULL, true)
              && !n_alias->has_aliases_p ())
            n_alias->remove ();
        }
@@ -751,6 +1036,13 @@ sem_function::merge (sem_item *alias_item)
   bool original_address_matters = original->address_matters_p ();
   bool alias_address_matters = alias->address_matters_p ();
 
+  if (DECL_EXTERNAL (alias->decl))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Not unifying; alias is external.\n\n");
+      return false;
+    }
+
   if (DECL_NO_INLINE_WARNING_P (original->decl)
       != DECL_NO_INLINE_WARNING_P (alias->decl))
     {
@@ -774,6 +1066,17 @@ sem_function::merge (sem_item *alias_item)
       return false;
     }
 
+  if (!original->in_same_comdat_group_p (alias)
+      || original->comdat_local_p ())
+    {
+      if (dump_file)
+       fprintf (dump_file,
+                "Not unifying; alias nor wrapper cannot be created; "
+                "across comdat group boundary\n\n");
+
+      return false;
+    }
+
   /* See if original is in a section that can be discarded if the main
      symbol is not used.  */
 
@@ -809,23 +1112,36 @@ sem_function::merge (sem_item *alias_item)
     {
       /* First see if we can produce wrapper.  */
 
+      /* Symbol properties that matter for references must be preserved.
+        TODO: We can produce wrapper, but we need to produce alias of ORIGINAL
+        with proper properties.  */
+      if (!sem_item::compare_referenced_symbol_properties (NULL, original, alias,
+                                                          alias->address_taken))
+        {
+         if (dump_file)
+           fprintf (dump_file,
+                    "Wrapper cannot be created because referenced symbol "
+                    "properties mismatch\n");
+        }
       /* 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 (original->decl) && DECL_COMDAT_GROUP (alias->decl)
-         && (DECL_COMDAT_GROUP (alias->decl)
-             != DECL_COMDAT_GROUP (original->decl)))
+      else if (DECL_COMDAT_GROUP (original->decl)
+              && 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))
+      else if (DECL_STATIC_CHAIN (alias->decl)
+              || DECL_STATIC_CHAIN (original->decl))
         {
          if (dump_file)
            fprintf (dump_file,
-                    "Can not create wrapper of nested functions.\n");
+                    "Cannot create wrapper of nested function.\n");
         }
       /* TODO: We can also deal with variadic functions never calling
         VA_START.  */
@@ -835,8 +1151,9 @@ sem_function::merge (sem_item *alias_item)
            fprintf (dump_file,
                     "can not create wrapper of stdarg function.\n");
        }
-      else if (inline_summaries
-              && inline_summaries->get (alias)->self_size <= 2)
+      else if (ipa_fn_summaries
+              && ipa_fn_summaries->get (alias) != NULL
+              && ipa_fn_summaries->get (alias)->self_size <= 2)
        {
          if (dump_file)
            fprintf (dump_file, "Wrapper creation is not "
@@ -857,8 +1174,12 @@ sem_function::merge (sem_item *alias_item)
         are not interposable.  */
       redirect_callers
        = alias->get_availability () > AVAIL_INTERPOSABLE
-         && original->get_availability () > AVAIL_INTERPOSABLE
-         && !alias->instrumented_version;
+         && original->get_availability () > AVAIL_INTERPOSABLE;
+      /* TODO: We can redirect, but we need to produce alias of ORIGINAL
+        with proper properties.  */
+      if (!sem_item::compare_referenced_symbol_properties (NULL, original, alias,
+                                                          alias->address_taken))
+       redirect_callers = false;
 
       if (!redirect_callers && !create_wrapper)
        {
@@ -907,6 +1228,8 @@ sem_function::merge (sem_item *alias_item)
          return false;
        }
       if (!create_wrapper
+         && !alias->call_for_symbol_and_aliases (cgraph_node::has_thunk_p,
+                                                 NULL, true)
          && !alias->can_remove_if_no_direct_calls_p ())
        {
          if (dump_file)
@@ -934,6 +1257,7 @@ sem_function::merge (sem_item *alias_item)
 
       /* If all callers was redirected, do not produce wrapper.  */
       if (alias->can_remove_if_no_direct_calls_p ()
+         && !DECL_VIRTUAL_P (alias->decl)
          && !alias->has_aliases_p ())
        {
          create_wrapper = false;
@@ -969,19 +1293,30 @@ sem_function::merge (sem_item *alias_item)
       alias->icf_merged = true;
       local_original->icf_merged = true;
 
-      ipa_merge_profiles (local_original, alias, true);
+      /* FIXME update local_original counts.  */
+      ipa_merge_profiles (original, alias, true);
       alias->create_wrapper (local_original);
 
       if (dump_file)
        fprintf (dump_file, "Unified; Wrapper has been created.\n\n");
     }
-  gcc_assert (alias->icf_merged || remove);
+
+  /* It's possible that redirection can hit thunks that block
+     redirection opportunities.  */
+  gcc_assert (alias->icf_merged || remove || redirect_callers);
   original->icf_merged = true;
 
-  /* Inform the inliner about cross-module merging.  */
-  if ((original->lto_file_data || alias->lto_file_data)
-      && original->lto_file_data != alias->lto_file_data)
-    local_original->merged = original->merged = true;
+  /* We use merged flag to track cases where COMDAT function is known to be
+     compatible its callers.  If we merged in non-COMDAT, we need to give up
+     on this optimization.  */
+  if (original->merged_comdat && !alias->merged_comdat)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Dropping merged_comdat flag.\n\n");
+      if (local_original)
+        local_original->merged_comdat = false;
+      original->merged_comdat = false;
+    }
 
   if (remove)
     {
@@ -1021,46 +1356,60 @@ sem_function::init (void)
   arg_count = count_formal_params (fndecl);
 
   edge_count = n_edges_for_fn (func);
-  cfg_checksum = coverage_compute_cfg_checksum (func);
-
-  inchash::hash hstate;
-
-  basic_block bb;
-  FOR_EACH_BB_FN (bb, func)
-  {
-    unsigned nondbg_stmt_count = 0;
+  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+  if (!cnode->thunk.thunk_p)
+    {
+      cfg_checksum = coverage_compute_cfg_checksum (func);
 
-    edge e;
-    for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e);
-        ei_next (&ei))
-      cfg_checksum = iterative_hash_host_wide_int (e->flags,
-                    cfg_checksum);
+      inchash::hash hstate;
 
-    for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
-        gsi_next (&gsi))
+      basic_block bb;
+      FOR_EACH_BB_FN (bb, func)
       {
-       gimple stmt = gsi_stmt (gsi);
+       unsigned nondbg_stmt_count = 0;
+
+       edge e;
+       for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e);
+            ei_next (&ei))
+         cfg_checksum = iterative_hash_host_wide_int (e->flags,
+                        cfg_checksum);
 
-       if (gimple_code (stmt) != GIMPLE_DEBUG
-           && gimple_code (stmt) != GIMPLE_PREDICT)
+       for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+            gsi_next (&gsi))
          {
-           hash_stmt (stmt, hstate);
-           nondbg_stmt_count++;
-         }
-      }
+           gimple *stmt = gsi_stmt (gsi);
 
-    gcode_hash = hstate.end ();
-    bb_sizes.safe_push (nondbg_stmt_count);
+           if (gimple_code (stmt) != GIMPLE_DEBUG
+               && gimple_code (stmt) != GIMPLE_PREDICT)
+             {
+               hash_stmt (stmt, hstate);
+               nondbg_stmt_count++;
+             }
+         }
 
-    /* Inserting basic block to hash table.  */
-    sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
-                                     EDGE_COUNT (bb->preds)
-                                     + EDGE_COUNT (bb->succs));
+       hstate.commit_flag ();
+       gcode_hash = hstate.end ();
+       bb_sizes.safe_push (nondbg_stmt_count);
 
-    bb_sorted.safe_push (semantic_bb);
-  }
+       /* Inserting basic block to hash table.  */
+       sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+                                         EDGE_COUNT (bb->preds)
+                                         + EDGE_COUNT (bb->succs));
 
-  parse_tree_args ();
+       bb_sorted.safe_push (semantic_bb);
+      }
+    }
+  else
+    {
+      cfg_checksum = 0;
+      inchash::hash hstate;
+      hstate.add_hwi (cnode->thunk.fixed_offset);
+      hstate.add_hwi (cnode->thunk.virtual_value);
+      hstate.add_flag (cnode->thunk.this_adjusting);
+      hstate.add_flag (cnode->thunk.virtual_offset_p);
+      hstate.add_flag (cnode->thunk.add_pointer_bounds_args);
+      gcode_hash = hstate.end ();
+    }
 }
 
 /* Accumulate to HSTATE a hash of expression EXP.
@@ -1102,7 +1451,7 @@ sem_item::add_expr (const_tree exp, inchash::hash &hstate)
        unsigned HOST_WIDE_INT idx;
        tree value;
 
-       hstate.add_wide_int (int_size_in_bytes (TREE_TYPE (exp)));
+       hstate.add_hwi (int_size_in_bytes (TREE_TYPE (exp)));
 
        FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
          if (value)
@@ -1117,7 +1466,7 @@ sem_item::add_expr (const_tree exp, inchash::hash &hstate)
     case VAR_DECL:
     case CONST_DECL:
     case PARM_DECL:
-      hstate.add_wide_int (int_size_in_bytes (TREE_TYPE (exp)));
+      hstate.add_hwi (int_size_in_bytes (TREE_TYPE (exp)));
       break;
     case MEM_REF:
     case POINTER_PLUS_EXPR:
@@ -1135,17 +1484,93 @@ sem_item::add_expr (const_tree exp, inchash::hash &hstate)
       }
       break;
     CASE_CONVERT:
-      hstate.add_wide_int (int_size_in_bytes (TREE_TYPE (exp)));
+      hstate.add_hwi (int_size_in_bytes (TREE_TYPE (exp)));
       return add_expr (TREE_OPERAND (exp, 0), hstate);
     default:
       break;
     }
 }
 
+/* Accumulate to HSTATE a hash of type t.
+   TYpes that may end up being compatible after LTO type merging needs to have
+   the same hash.  */
+
+void
+sem_item::add_type (const_tree type, inchash::hash &hstate)
+{
+  if (type == NULL_TREE)
+    {
+      hstate.merge_hash (0);
+      return;
+    }
+
+  type = TYPE_MAIN_VARIANT (type);
+
+  hstate.add_int (TYPE_MODE (type));
+
+  if (TREE_CODE (type) == COMPLEX_TYPE)
+    {
+      hstate.add_int (COMPLEX_TYPE);
+      sem_item::add_type (TREE_TYPE (type), hstate);
+    }
+  else if (INTEGRAL_TYPE_P (type))
+    {
+      hstate.add_int (INTEGER_TYPE);
+      hstate.add_flag (TYPE_UNSIGNED (type));
+      hstate.add_int (TYPE_PRECISION (type));
+    }
+  else if (VECTOR_TYPE_P (type))
+    {
+      hstate.add_int (VECTOR_TYPE);
+      hstate.add_int (TYPE_PRECISION (type));
+      sem_item::add_type (TREE_TYPE (type), hstate);
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      hstate.add_int (ARRAY_TYPE);
+      /* Do not hash size, so complete and incomplete types can match.  */
+      sem_item::add_type (TREE_TYPE (type), hstate);
+    }
+  else if (RECORD_OR_UNION_TYPE_P (type))
+    {
+      /* Incomplete types must be skipped here.  */
+      if (!COMPLETE_TYPE_P (type))
+       {
+         hstate.add_int (RECORD_TYPE);
+         return;
+       }
+
+      hashval_t *val = m_type_hash_cache.get (type);
+
+      if (!val)
+       {
+         inchash::hash hstate2;
+         unsigned nf;
+         tree f;
+         hashval_t hash;
+
+         hstate2.add_int (RECORD_TYPE);
+         for (f = TYPE_FIELDS (type), nf = 0; f; f = TREE_CHAIN (f))
+           if (TREE_CODE (f) == FIELD_DECL)
+             {
+               add_type (TREE_TYPE (f), hstate2);
+               nf++;
+             }
+
+         hstate2.add_int (nf);
+         hash = hstate2.end ();
+         hstate.add_hwi (hash);
+         m_type_hash_cache.put (type, hash);
+       }
+      else
+        hstate.add_hwi (*val);
+    }
+}
+
 /* Improve accumulated hash for HSTATE based on a gimple statement STMT.  */
 
 void
-sem_function::hash_stmt (gimple stmt, inchash::hash &hstate)
+sem_function::hash_stmt (gimple *stmt, inchash::hash &hstate)
 {
   enum gimple_code code = gimple_code (stmt);
 
@@ -1153,19 +1578,30 @@ sem_function::hash_stmt (gimple stmt, inchash::hash &hstate)
 
   switch (code)
     {
+    case GIMPLE_SWITCH:
+      add_expr (gimple_switch_index (as_a <gswitch *> (stmt)), hstate);
+      break;
     case GIMPLE_ASSIGN:
+      hstate.add_int (gimple_assign_rhs_code (stmt));
       if (commutative_tree_code (gimple_assign_rhs_code (stmt))
          || commutative_ternary_tree_code (gimple_assign_rhs_code (stmt)))
        {
          inchash::hash one, two;
 
          add_expr (gimple_assign_rhs1 (stmt), one);
+         add_type (TREE_TYPE (gimple_assign_rhs1 (stmt)), one);
          add_expr (gimple_assign_rhs2 (stmt), two);
          hstate.add_commutative (one, two);
+         if (commutative_ternary_tree_code (gimple_assign_rhs_code (stmt)))
+           {
+             add_expr (gimple_assign_rhs3 (stmt), hstate);
+             add_type (TREE_TYPE (gimple_assign_rhs3 (stmt)), hstate);
+           }
          add_expr (gimple_assign_lhs (stmt), hstate);
+         add_type (TREE_TYPE (gimple_assign_lhs (stmt)), two);
          break;
        }
-      /* ... fall through ... */
+      /* fall through */
     case GIMPLE_CALL:
     case GIMPLE_ASM:
     case GIMPLE_COND:
@@ -1173,7 +1609,16 @@ sem_function::hash_stmt (gimple stmt, inchash::hash &hstate)
     case GIMPLE_RETURN:
       /* All these statements are equivalent if their operands are.  */
       for (unsigned i = 0; i < gimple_num_ops (stmt); ++i)
-       add_expr (gimple_op (stmt, i), hstate);
+       {
+         add_expr (gimple_op (stmt, i), hstate);
+         if (gimple_op (stmt, i))
+           add_type (TREE_TYPE (gimple_op (stmt, i)), hstate);
+       }
+      /* Consider nocf_check attribute in hash as it affects code
+        generation.  */
+      if (code == GIMPLE_CALL
+         && flag_cf_protection & CF_BRANCH)
+       hstate.add_flag (gimple_call_nocf_check_p (as_a <gcall *> (stmt)));
     default:
       break;
     }
@@ -1185,10 +1630,19 @@ sem_function::hash_stmt (gimple stmt, inchash::hash &hstate)
 bool
 sem_function::compare_polymorphic_p (void)
 {
-  return get_node ()->callees != NULL
-        || get_node ()->indirect_calls != NULL
-        || m_compared_func->get_node ()->callees != NULL
-        || m_compared_func->get_node ()->indirect_calls != NULL;
+  struct cgraph_edge *e;
+
+  if (!opt_for_fn (get_node ()->decl, flag_devirtualize))
+    return false;
+  if (get_node ()->indirect_calls != NULL)
+    return true;
+  /* TODO: We can do simple propagation determining what calls may lead to
+     a polymorphic call.  */
+  for (e = get_node ()->callees; e; e = e->next_callee)
+    if (e->callee->definition
+       && opt_for_fn (e->callee->decl, flag_devirtualize))
+      return true;
+  return false;
 }
 
 /* For a given call graph NODE, the function constructs new
@@ -1200,50 +1654,26 @@ sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
   tree fndecl = node->decl;
   function *func = DECL_STRUCT_FUNCTION (fndecl);
 
-  /* TODO: add support for thunks.  */
-
-  if (!func || !node->has_gimple_body_p ())
+  if (!func || (!node->has_gimple_body_p () && !node->thunk.thunk_p))
     return NULL;
 
   if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
     return NULL;
 
-  sem_function *f = new sem_function (node, 0, stack);
-
-  f->init ();
-
-  return f;
-}
-
-/* Parses function arguments and result type.  */
-
-void
-sem_function::parse_tree_args (void)
-{
-  tree result;
-
-  if (arg_types.exists ())
-    arg_types.release ();
-
-  arg_types.create (4);
-  tree fnargs = DECL_ARGUMENTS (decl);
+  if (lookup_attribute_by_prefix ("oacc ",
+                                 DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
 
-  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
-    arg_types.safe_push (DECL_ARG_TYPE (parm));
+  /* PR ipa/70306.  */
+  if (DECL_STATIC_CONSTRUCTOR (node->decl)
+      || DECL_STATIC_DESTRUCTOR (node->decl))
+    return NULL;
 
-  /* Function result type.  */
-  result = DECL_RESULT (decl);
-  result_type = result ? TREE_TYPE (result) : NULL;
+  sem_function *f = new sem_function (node, stack);
 
-  /* During WPA, we can get arguments by following method.  */
-  if (!fnargs)
-    {
-      tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
-      for (tree parm = type; parm; parm = TREE_CHAIN (parm))
-       arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
+  f->init ();
 
-      result_type = TREE_TYPE (TREE_TYPE (decl));
-    }
+  return f;
 }
 
 /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
@@ -1317,9 +1747,8 @@ sem_function::icf_handled_component_p (tree t)
 {
   tree_code tc = TREE_CODE (t);
 
-  return ((handled_component_p (t))
-         || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
-         || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+  return (handled_component_p (t)
+         || tc == ADDR_EXPR || tc == MEM_REF || tc == OBJ_TYPE_REF);
 }
 
 /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
@@ -1343,54 +1772,12 @@ sem_function::bb_dict_test (vec<int> *bb_dict, int source, int target)
     return (*bb_dict)[source] == target;
 }
 
-/* Iterates all tree types in T1 and T2 and returns true if all types
-   are compatible. If COMPARE_POLYMORPHIC is set to true,
-   more strict comparison is executed.  */
-
-bool
-sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
-{
-  tree tv1, tv2;
-  tree_code tc1, tc2;
-
-  if (!t1 && !t2)
-    return true;
-
-  while (t1 != NULL && t2 != NULL)
-    {
-      tv1 = TREE_VALUE (t1);
-      tv2 = TREE_VALUE (t2);
-
-      tc1 = TREE_CODE (tv1);
-      tc2 = TREE_CODE (tv2);
-
-      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
-       {}
-      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
-       return false;
-      else if (!func_checker::compatible_types_p (tv1, tv2, compare_polymorphic))
-       return false;
-
-      t1 = TREE_CHAIN (t1);
-      t2 = TREE_CHAIN (t2);
-    }
-
-  return !(t1 || t2);
-}
-
-
-/* Semantic variable constructor that uses STACK as bitmap memory stack.  */
-
 sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, stack)
 {
 }
 
-/*  Constructor based on varpool node _NODE with computed hash _HASH.
-    Bitmap STACK is used for memory allocation.  */
-
-sem_variable::sem_variable (varpool_node *node, hashval_t _hash,
-                           bitmap_obstack *stack): sem_item(VAR,
-                                 node, _hash, stack)
+sem_variable::sem_variable (varpool_node *node, bitmap_obstack *stack)
+: sem_item (VAR, node, stack)
 {
   gcc_checking_assert (node);
   gcc_checking_assert (get_node ());
@@ -1410,8 +1797,8 @@ sem_variable::equals_wpa (sem_item *item,
   if (DECL_TLS_MODEL (decl) || DECL_TLS_MODEL (item->decl))
     return return_false_with_msg ("TLS model");
 
-  if (DECL_ALIGN (decl) != DECL_ALIGN (item->decl))
-    return return_false_with_msg ("alignment mismatch");
+  /* DECL_ALIGN is safe to merge, because we will always chose the largest
+     alignment out of all aliases.  */
 
   if (DECL_VIRTUAL_P (decl) != DECL_VIRTUAL_P (item->decl))
     return return_false_with_msg ("Virtual flag mismatch");
@@ -1437,7 +1824,10 @@ sem_variable::equals_wpa (sem_item *item,
     {
       item->node->iterate_reference (i, ref2);
 
-      if (!compare_cgraph_references (ignored_nodes,
+      if (ref->use != ref2->use)
+       return return_false_with_msg ("reference use mismatch");
+
+      if (!compare_symbol_references (ignored_nodes,
                                      ref->referred, ref2->referred,
                                      ref->address_matters_p ()))
        return false;
@@ -1450,16 +1840,30 @@ sem_variable::equals_wpa (sem_item *item,
 
 bool
 sem_variable::equals (sem_item *item,
-                     hash_map <symtab_node *, sem_item *> & ARG_UNUSED (ignored_nodes))
+                     hash_map <symtab_node *, sem_item *> &)
 {
   gcc_assert (item->type == VAR);
+  bool ret;
 
-  sem_variable *v = static_cast<sem_variable *>(item);
+  if (DECL_INITIAL (decl) == error_mark_node && in_lto_p)
+    dyn_cast <varpool_node *>(node)->get_constructor ();
+  if (DECL_INITIAL (item->decl) == error_mark_node && in_lto_p)
+    dyn_cast <varpool_node *>(item->node)->get_constructor ();
 
-  if (!ctor || !v->ctor)
-    return return_false_with_msg ("ctor is missing for semantic variable");
+  /* As seen in PR ipa/65303 we have to compare variables types.  */
+  if (!func_checker::compatible_types_p (TREE_TYPE (decl),
+                                        TREE_TYPE (item->decl)))
+    return return_false_with_msg ("variables types are different");
 
-  return sem_variable::equals (ctor, v->ctor);
+  ret = sem_variable::equals (DECL_INITIAL (decl),
+                             DECL_INITIAL (item->node->decl));
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+            "Equals called for vars: %s:%s with result: %s\n\n",
+            node->dump_name (), item->node->dump_name (),
+            ret ? "true" : "false");
+
+  return ret;
 }
 
 /* Compares trees T1 and T2 for semantic equality.  */
@@ -1527,14 +1931,13 @@ sem_variable::equals (tree t1, tree t2)
        tree y1 = TREE_OPERAND (t1, 1);
        tree y2 = TREE_OPERAND (t2, 1);
 
-       if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2),
-                                              true))
+       if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
          return return_false ();
 
        /* Type of the offset on MEM_REF does not matter.  */
        return return_with_debug (sem_variable::equals (x1, x2)
-                                 && wi::to_offset  (y1)
-                                    == wi::to_offset  (y2));
+                                 && known_eq (wi::to_poly_offset (y1),
+                                              wi::to_poly_offset (y2)));
       }
     case ADDR_EXPR:
     case FDESC_EXPR:
@@ -1586,21 +1989,21 @@ sem_variable::equals (tree t1, tree t2)
       /* Real constants are the same only if the same width of type.  */
       if (TYPE_PRECISION (TREE_TYPE (t1)) != TYPE_PRECISION (TREE_TYPE (t2)))
         return return_false_with_msg ("REAL_CST precision mismatch");
-      return return_with_debug (REAL_VALUES_IDENTICAL (TREE_REAL_CST (t1),
-                                                      TREE_REAL_CST (t2)));
+      return return_with_debug (real_identical (&TREE_REAL_CST (t1),
+                                               &TREE_REAL_CST (t2)));
     case VECTOR_CST:
       {
-       unsigned i;
-
-        if (VECTOR_CST_NELTS (t1) != VECTOR_CST_NELTS (t2))
-          return return_false_with_msg ("VECTOR_CST nelts mismatch");
+       if (maybe_ne (VECTOR_CST_NELTS (t1), VECTOR_CST_NELTS (t2)))
+         return return_false_with_msg ("VECTOR_CST nelts mismatch");
 
-       for (i = 0; i < VECTOR_CST_NELTS (t1); ++i)
-         if (!sem_variable::equals (VECTOR_CST_ELT (t1, i),
-                                    VECTOR_CST_ELT (t2, i)))
-           return 0;
+       unsigned int count
+         = tree_vector_builder::binary_encoded_nelts (t1, t2);
+       for (unsigned int i = 0; i < count; ++i)
+         if (!sem_variable::equals (VECTOR_CST_ENCODED_ELT (t1, i),
+                                    VECTOR_CST_ENCODED_ELT (t2, i)))
+           return false;
 
-       return 1;
+       return true;
       }
     case ARRAY_REF:
     case ARRAY_RANGE_REF:
@@ -1610,7 +2013,7 @@ sem_variable::equals (tree t1, tree t2)
        tree y1 = TREE_OPERAND (t1, 1);
        tree y2 = TREE_OPERAND (t2, 1);
 
-       if (!sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2))
+       if (!sem_variable::equals (x1, x2) || !sem_variable::equals (y1, y2))
          return false;
        if (!sem_variable::equals (array_ref_low_bound (t1),
                                   array_ref_low_bound (t2)))
@@ -1637,8 +2040,7 @@ sem_variable::equals (tree t1, tree t2)
 
     CASE_CONVERT:
     case VIEW_CONVERT_EXPR:
-      if (!func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
-                                            true))
+      if (!func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2)))
          return return_false ();
       return sem_variable::equals (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
     case ERROR_MARK:
@@ -1653,27 +2055,11 @@ sem_variable::equals (tree t1, tree t2)
 sem_variable *
 sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
 {
-  tree decl = node->decl;
-
-  if (node->alias)
-    return NULL;
-
-  bool readonly = TYPE_P (decl) ? TYPE_READONLY (decl) : TREE_READONLY (decl);
-  if (!readonly)
-    return NULL;
-
-  bool can_handle = DECL_VIRTUAL_P (decl)
-                   || flag_merge_constants >= 2
-                   || (!TREE_ADDRESSABLE (decl) && !node->externally_visible);
-
-  if (!can_handle || DECL_EXTERNAL (decl))
-    return NULL;
-
-  tree ctor = ctor_for_folding (decl);
-  if (!ctor)
+  if (TREE_THIS_VOLATILE (node->decl) || DECL_HARD_REGISTER (node->decl)
+      || node->alias)
     return NULL;
 
-  sem_variable *v = new sem_variable (node, 0, stack);
+  sem_variable *v = new sem_variable (node, stack);
 
   v->init ();
 
@@ -1685,8 +2071,8 @@ sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
 hashval_t
 sem_variable::get_hash (void)
 {
-  if (hash)
-    return hash;
+  if (m_hash_set)
+    return m_hash;
 
   /* All WPA streamed in symbols should have their hashes computed at compile
      time.  At this point, the constructor may not be in memory at all.
@@ -1697,11 +2083,11 @@ sem_variable::get_hash (void)
 
   hstate.add_int (456346417);
   if (DECL_SIZE (decl) && tree_fits_shwi_p (DECL_SIZE (decl)))
-    hstate.add_wide_int (tree_to_shwi (DECL_SIZE (decl)));
+    hstate.add_hwi (tree_to_shwi (DECL_SIZE (decl)));
   add_expr (ctor, hstate);
-  hash = hstate.end ();
+  set_hash (hstate.end ());
 
-  return hash;
+  return m_hash;
 }
 
 /* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
@@ -1720,13 +2106,19 @@ sem_variable::merge (sem_item *alias_item)
       return false;
     }
 
+  if (DECL_EXTERNAL (alias_item->decl))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Not unifying; alias is external.\n\n");
+      return false;
+    }
+
   sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
 
   varpool_node *original = get_node ();
   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
@@ -1769,15 +2161,23 @@ sem_variable::merge (sem_item *alias_item)
     }
 
   /* We can not merge if address comparsion metters.  */
-  if (original_address_matters && alias_address_matters
-      && flag_merge_constants < 2)
+  if (alias_address_matters && flag_merge_constants < 2)
     {
       if (dump_file)
        fprintf (dump_file,
-                "Not unifying; "
-                "adress of original and alias may be compared.\n\n");
+                "Not unifying; address of original may be compared.\n\n");
       return false;
     }
+
+  if (DECL_ALIGN (original->decl) < DECL_ALIGN (alias->decl))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Not unifying; "
+                "original and alias have incompatible alignments\n\n");
+
+      return false;
+    }
+
   if (DECL_COMDAT_GROUP (original->decl) != DECL_COMDAT_GROUP (alias->decl))
     {
       if (dump_file)
@@ -1814,7 +2214,7 @@ sem_variable::merge (sem_item *alias_item)
       alias->resolve_alias (original);
 
       if (dump_file)
-       fprintf (dump_file, "Unified; Variable alias has been created.\n\n");
+       fprintf (dump_file, "Unified; Variable alias has been created.\n");
 
       return true;
     }
@@ -1831,44 +2231,11 @@ sem_variable::dump_to_file (FILE *file)
   fprintf (file, "\n\n");
 }
 
-/* Iterates though a constructor and identifies tree references
-   we are interested in semantic function equality.  */
-
-void
-sem_variable::parse_tree_refs (tree t)
-{
-  switch (TREE_CODE (t))
-    {
-    case CONSTRUCTOR:
-      {
-       unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
-
-       for (unsigned i = 0; i < length; i++)
-         parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
-
-       break;
-      }
-    case NOP_EXPR:
-    case ADDR_EXPR:
-      {
-       tree op = TREE_OPERAND (t, 0);
-       parse_tree_refs (op);
-       break;
-      }
-    case FUNCTION_DECL:
-      {
-       tree_refs.safe_push (t);
-       break;
-      }
-    default:
-      break;
-    }
-}
-
 unsigned int sem_item_optimizer::class_id = 0;
 
-sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
-  m_classes_count (0), m_cgraph_node_hooks (NULL), m_varpool_node_hooks (NULL)
+sem_item_optimizer::sem_item_optimizer ()
+: worklist (0), m_classes (0), m_classes_count (0), m_cgraph_node_hooks (NULL),
+  m_varpool_node_hooks (NULL), m_merged_variables ()
 {
   m_items.create (0);
   bitmap_obstack_initialize (&m_bmstack);
@@ -1879,7 +2246,8 @@ sem_item_optimizer::~sem_item_optimizer ()
   for (unsigned int i = 0; i < m_items.length (); i++)
     delete m_items[i];
 
-  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
        it != m_classes.end (); ++it)
     {
       for (unsigned int i = 0; i < (*it)->classes.length (); i++)
@@ -1892,6 +2260,7 @@ sem_item_optimizer::~sem_item_optimizer ()
   m_items.release ();
 
   bitmap_obstack_release (&m_bmstack);
+  m_merged_variables.release ();
 }
 
 /* Write IPA ICF summary for symbols.  */
@@ -1948,8 +2317,8 @@ void
 sem_item_optimizer::read_section (lto_file_decl_data *file_data,
                                  const char *data, size_t len)
 {
-  const lto_function_header *header =
-    (const lto_function_header *) data;
+  const lto_function_header *header
+    (const lto_function_header *) data;
   const int cfg_offset = sizeof (lto_function_header);
   const int main_offset = cfg_offset + header->cfg_size;
   const int string_offset = main_offset + header->main_size;
@@ -1960,9 +2329,9 @@ sem_item_optimizer::read_section (lto_file_decl_data *file_data,
   lto_input_block ib_main ((const char *) data + main_offset, 0,
                           header->main_size, file_data->mode_table);
 
-  data_in =
-    lto_data_in_create (file_data, (const char *) data + string_offset,
-                       header->string_size, vNULL);
+  data_in
+    lto_data_in_create (file_data, (const char *) data + string_offset,
+                         header->string_size, vNULL);
 
   count = streamer_read_uhwi (&ib_main);
 
@@ -1981,20 +2350,24 @@ sem_item_optimizer::read_section (lto_file_decl_data *file_data,
       gcc_assert (node->definition);
 
       if (dump_file)
-       fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
-                (void *) node->decl, node->order);
+       fprintf (dump_file, "Symbol added: %s (tree: %p)\n",
+                node->dump_asm_name (), (void *) node->decl);
 
       if (is_a<cgraph_node *> (node))
        {
          cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
 
-         m_items.safe_push (new sem_function (cnode, hash, &m_bmstack));
+         sem_function *fn = new sem_function (cnode, &m_bmstack);
+         fn->set_hash (hash);
+         m_items.safe_push (fn);
        }
       else
        {
          varpool_node *vnode = dyn_cast <varpool_node *> (node);
 
-         m_items.safe_push (new sem_variable (vnode, hash, &m_bmstack));
+         sem_variable *var = new sem_variable (vnode, &m_bmstack);
+         var->set_hash (hash);
+         m_items.safe_push (var);
        }
     }
 
@@ -2003,7 +2376,7 @@ sem_item_optimizer::read_section (lto_file_decl_data *file_data,
   lto_data_in_delete (data_in);
 }
 
-/* Read IPA IPA ICF summary for symbols.  */
+/* Read IPA ICF summary for symbols.  */
 
 void
 sem_item_optimizer::read_summary (void)
@@ -2056,9 +2429,9 @@ sem_item_optimizer::add_class (congruence_class *cls)
 {
   gcc_assert (cls->members.length ());
 
-  congruence_class_group *group = get_group_by_hash (
-                                   cls->members[0]->get_hash (),
-                                   cls->members[0]->type);
+  congruence_class_group *group
+    = get_group_by_hash (cls->members[0]->get_hash (),
+                        cls->members[0]->type);
   group->classes.safe_push (cls);
 }
 
@@ -2107,7 +2480,7 @@ sem_item_optimizer::varpool_removal_hook (varpool_node *node, void *data)
 void
 sem_item_optimizer::remove_symtab_node (symtab_node *node)
 {
-  gcc_assert (!m_classes.elements());
+  gcc_assert (!m_classes.elements ());
 
   m_removed_items_set.add (node);
 }
@@ -2142,10 +2515,7 @@ sem_item_optimizer::filter_removed_items (void)
         {
          cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
 
-         bool no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
-         if (no_body_function || !opt_for_fn (item->decl, flag_ipa_icf_functions)
-             || DECL_CXX_CONSTRUCTOR_P (item->decl)
-             || DECL_CXX_DESTRUCTOR_P (item->decl))
+         if (in_lto_p && (cnode->alias || cnode->body_removed))
            remove_item (item);
          else
            filtered.safe_push (item);
@@ -2155,7 +2525,14 @@ sem_item_optimizer::filter_removed_items (void)
          if (!flag_ipa_icf_variables)
            remove_item (item);
          else
-           filtered.safe_push (item);
+           {
+             /* Filter out non-readonly variables.  */
+             tree decl = item->decl;
+             if (TREE_READONLY (decl))
+               filtered.safe_push (item);
+             else
+               remove_item (item);
+           }
         }
     }
 
@@ -2166,14 +2543,18 @@ sem_item_optimizer::filter_removed_items (void)
     m_items.safe_push (filtered[i]);
 }
 
-/* Optimizer entry point.  */
+/* Optimizer entry point which returns true in case it processes
+   a merge operation. True is returned if there's a merge operation
+   processed.  */
 
-void
+bool
 sem_item_optimizer::execute (void)
 {
   filter_removed_items ();
   unregister_hooks ();
 
+  build_graph ();
+  update_hash_by_addr_refs ();
   build_hash_based_classes ();
 
   if (dump_file)
@@ -2183,8 +2564,6 @@ sem_item_optimizer::execute (void)
   for (unsigned int i = 0; i < m_items.length(); i++)
     m_items[i]->init_wpa ();
 
-  build_graph ();
-
   subdivide_classes_by_equality (true);
 
   if (dump_file)
@@ -2193,7 +2572,7 @@ sem_item_optimizer::execute (void)
   dump_cong_classes ();
 
   process_cong_reduction ();
-  verify_classes ();
+  checking_verify_classes ();
 
   if (dump_file)
     fprintf (dump_file, "Dump after callgraph-based congruence reduction\n");
@@ -2212,11 +2591,13 @@ sem_item_optimizer::execute (void)
 
   process_cong_reduction ();
   dump_cong_classes ();
-  verify_classes ();
-  merge_classes (prev_class_count);
+  checking_verify_classes ();
+  bool merged_p = merge_classes (prev_class_count);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
-    symtab_node::dump_table (dump_file);
+    symtab->dump (dump_file);
+
+  return merged_p;
 }
 
 /* Function responsible for visiting all potential functions and
@@ -2237,7 +2618,7 @@ sem_item_optimizer::parse_funcs_and_vars (void)
          m_symtab_node_map.put (cnode, f);
 
          if (dump_file)
-           fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+           fprintf (dump_file, "Parsed function:%s\n", f->node->asm_name ());
 
          if (dump_file && (dump_flags & TDF_DETAILS))
            f->dump_to_file (dump_file);
@@ -2271,6 +2652,53 @@ sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
   item->cls = cls;
 }
 
+/* For each semantic item, append hash values of references.  */
+
+void
+sem_item_optimizer::update_hash_by_addr_refs ()
+{
+  /* First, append to hash sensitive references and class type if it need to
+     be matched for ODR.  */
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      m_items[i]->update_hash_by_addr_refs (m_symtab_node_map);
+      if (m_items[i]->type == FUNC)
+       {
+         if (TREE_CODE (TREE_TYPE (m_items[i]->decl)) == METHOD_TYPE
+             && contains_polymorphic_type_p
+                  (TYPE_METHOD_BASETYPE (TREE_TYPE (m_items[i]->decl)))
+             && (DECL_CXX_CONSTRUCTOR_P (m_items[i]->decl)
+                 || (static_cast<sem_function *> (m_items[i])->param_used_p (0)
+                     && static_cast<sem_function *> (m_items[i])
+                          ->compare_polymorphic_p ())))
+            {
+               tree class_type
+                 = TYPE_METHOD_BASETYPE (TREE_TYPE (m_items[i]->decl));
+               inchash::hash hstate (m_items[i]->get_hash ());
+
+               if (TYPE_NAME (class_type)
+                    && DECL_ASSEMBLER_NAME_SET_P (TYPE_NAME (class_type)))
+                 hstate.add_hwi
+                   (IDENTIFIER_HASH_VALUE
+                      (DECL_ASSEMBLER_NAME (TYPE_NAME (class_type))));
+
+               m_items[i]->set_hash (hstate.end ());
+            }
+       }
+    }
+
+  /* Once all symbols have enhanced hash value, we can append
+     hash values of symbols that are seen by IPA ICF and are
+     references by a semantic item. Newly computed values
+     are saved to global_hash member variable.  */
+  for (unsigned i = 0; i < m_items.length (); i++)
+    m_items[i]->update_hash_by_local_refs (m_symtab_node_map);
+
+  /* Global hash value replace current hash values.  */
+  for (unsigned i = 0; i < m_items.length (); i++)
+    m_items[i]->set_hash (m_items[i]->global_hash);
+}
+
 /* Congruence classes are built by hash value.  */
 
 void
@@ -2280,8 +2708,8 @@ sem_item_optimizer::build_hash_based_classes (void)
     {
       sem_item *item = m_items[i];
 
-      congruence_class_group *group = get_group_by_hash (item->get_hash (),
-                                     item->type);
+      congruence_class_group *group
+       = get_group_by_hash (item->get_hash (), item->type);
 
       if (!group->classes.length ())
        {
@@ -2302,6 +2730,10 @@ sem_item_optimizer::build_graph (void)
     {
       sem_item *item = m_items[i];
       m_symtab_node_map.put (item->node, item);
+
+      /* Initialize hash values if we are not in LTO mode.  */
+      if (!in_lto_p)
+       item->get_hash ();
     }
 
   for (unsigned i = 0; i < m_items.length (); i++)
@@ -2351,8 +2783,10 @@ sem_item_optimizer::parse_nonsingleton_classes (void)
       }
 
   if (dump_file)
-    fprintf (dump_file, "Init called for %u items (%.2f%%).\n", init_called_count,
-            m_items.length () ? 100.0f * init_called_count / m_items.length (): 0.0f);
+    fprintf (dump_file, "Init called for %u items (%.2f%%).\n",
+            init_called_count,
+            m_items.length () ? 100.0f * init_called_count / m_items.length ()
+                              : 0.0f);
 }
 
 /* Equality function for semantic items is used to subdivide existing
@@ -2361,14 +2795,14 @@ sem_item_optimizer::parse_nonsingleton_classes (void)
 void
 sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
 {
-  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+  for (hash_table <congruence_class_hash>::iterator it = m_classes.begin ();
        it != m_classes.end (); ++it)
     {
       unsigned int class_count = (*it)->classes.length ();
 
       for (unsigned i = 0; i < class_count; i++)
        {
-         congruence_class *c = (*it)->classes [i];
+         congruence_class *c = (*it)->classes[i];
 
          if (c->members.length() > 1)
            {
@@ -2383,8 +2817,9 @@ sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
                {
                  sem_item *item = c->members[j];
 
-                 bool equals = in_wpa ? first->equals_wpa (item,
-                               m_symtab_node_map) : first->equals (item, m_symtab_node_map);
+                 bool equals
+                   = in_wpa ? first->equals_wpa (item, m_symtab_node_map)
+                            : first->equals (item, m_symtab_node_map);
 
                  if (equals)
                    new_vector.safe_push (item);
@@ -2392,11 +2827,13 @@ sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
                    {
                      bool integrated = false;
 
-                     for (unsigned k = class_split_first; k < (*it)->classes.length (); k++)
+                     for (unsigned k = class_split_first;
+                          k < (*it)->classes.length (); k++)
                        {
                          sem_item *x = (*it)->classes[k]->members[0];
-                         bool equals = in_wpa ? x->equals_wpa (item,
-                                                               m_symtab_node_map) : x->equals (item, m_symtab_node_map);
+                         bool equals
+                           = in_wpa ? x->equals_wpa (item, m_symtab_node_map)
+                                    : x->equals (item, m_symtab_node_map);
 
                          if (equals)
                            {
@@ -2409,7 +2846,8 @@ sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
 
                      if (!integrated)
                        {
-                         congruence_class *c = new congruence_class (class_id++);
+                         congruence_class *c
+                           = new congruence_class (class_id++);
                          m_classes_count++;
                          add_item_to_class (c, item);
 
@@ -2418,7 +2856,8 @@ sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
                    }
                }
 
-             // we replace newly created new_vector for the class we've just splitted
+             // We replace newly created new_vector for the class we've just
+             // splitted.
              c->members.release ();
              c->members.create (new_vector.length ());
 
@@ -2428,7 +2867,7 @@ sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
        }
     }
 
-  verify_classes ();
+  checking_verify_classes ();
 }
 
 /* Subdivide classes by address references that members of the class
@@ -2439,9 +2878,11 @@ sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
 unsigned
 sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
 {
+  typedef hash_map <symbol_compare_hash, vec <sem_item *> > subdivide_hash_map;
+
   unsigned newly_created_classes = 0;
 
-  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+  for (hash_table <congruence_class_hash>::iterator it = m_classes.begin ();
        it != m_classes.end (); ++it)
     {
       unsigned int class_count = (*it)->classes.length ();
@@ -2449,34 +2890,38 @@ sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
 
       for (unsigned i = 0; i < class_count; i++)
        {
-         congruence_class *c = (*it)->classes [i];
+         congruence_class *c = (*it)->classes[i];
 
          if (c->members.length() > 1)
            {
-             hash_map <symbol_compare_collection *, vec <sem_item *>,
-               symbol_compare_hashmap_traits> split_map;
+             subdivide_hash_map split_map;
 
              for (unsigned j = 0; j < c->members.length (); j++)
                {
                  sem_item *source_node = c->members[j];
 
-                 symbol_compare_collection *collection = new symbol_compare_collection (source_node->node);
+                 symbol_compare_collection *collection
+                   = new symbol_compare_collection (source_node->node);
 
-                 vec <sem_item *> *slot = &split_map.get_or_insert (collection);
+                 bool existed;
+                 vec <sem_item *> *slot
+                   = &split_map.get_or_insert (collection, &existed);
                  gcc_checking_assert (slot);
 
                  slot->safe_push (source_node);
+
+                 if (existed)
+                   delete collection;
                }
 
-              /* If the map contains more than one key, we have to split the map
-                 appropriately.  */
+              /* If the map contains more than one key, we have to split
+                 the map appropriately.  */
              if (split_map.elements () != 1)
                {
                  bool first_class = true;
 
-                 hash_map <symbol_compare_collection *, vec <sem_item *>,
-                 symbol_compare_hashmap_traits>::iterator it2 = split_map.begin ();
-                 for (; it2 != split_map.end (); ++it2)
+                 for (subdivide_hash_map::iterator it2 = split_map.begin ();
+                      it2 != split_map.end (); ++it2)
                    {
                      congruence_class *new_cls;
                      new_cls = new congruence_class (class_id++);
@@ -2499,6 +2944,14 @@ sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
                        }
                    }
                }
+
+             /* Release memory.  */
+             for (subdivide_hash_map::iterator it2 = split_map.begin ();
+                  it2 != split_map.end (); ++it2)
+               {
+                 delete (*it2).first;
+                 (*it2).second.release ();
+               }
            }
          }
 
@@ -2509,39 +2962,46 @@ sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
   return newly_created_classes;
 }
 
-/* Verify congruence classes if checking is enabled.  */
+/* Verify congruence classes, if checking is enabled.  */
+
+void
+sem_item_optimizer::checking_verify_classes (void)
+{
+  if (flag_checking)
+    verify_classes ();
+}
+
+/* Verify congruence classes.  */
 
 void
 sem_item_optimizer::verify_classes (void)
 {
-#if ENABLE_CHECKING
-  for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
        it != m_classes.end (); ++it)
     {
       for (unsigned int i = 0; i < (*it)->classes.length (); i++)
        {
          congruence_class *cls = (*it)->classes[i];
 
-         gcc_checking_assert (cls);
-         gcc_checking_assert (cls->members.length () > 0);
+         gcc_assert (cls);
+         gcc_assert (cls->members.length () > 0);
 
          for (unsigned int j = 0; j < cls->members.length (); j++)
            {
              sem_item *item = cls->members[j];
 
-             gcc_checking_assert (item);
-             gcc_checking_assert (item->cls == cls);
+             gcc_assert (item);
+             gcc_assert (item->cls == cls);
 
              for (unsigned k = 0; k < item->usages.length (); k++)
                {
                  sem_usage_pair *usage = item->usages[k];
-                 gcc_checking_assert (usage->item->index_in_class <
-                                      usage->item->cls->members.length ());
+                 gcc_assert (usage->item->index_in_class
+                             < usage->item->cls->members.length ());
                }
            }
        }
     }
-#endif
 }
 
 /* Disposes split map traverse function. CLS_PTR is pointer to congruence
@@ -2565,7 +3025,8 @@ sem_item_optimizer::release_split_map (congruence_class * const &,
 
 bool
 sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
-    bitmap const &b, traverse_split_pair *pair)
+                                              bitmap const &b,
+                                              traverse_split_pair *pair)
 {
   sem_item_optimizer *optimizer = pair->optimizer;
   const congruence_class *splitter_cls = pair->cls;
@@ -2576,7 +3037,9 @@ sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
 
   if (popcount > 0 && popcount < cls->members.length ())
     {
-      congruence_class* newclasses[2] = { new congruence_class (class_id++), new congruence_class (class_id++) };
+      auto_vec <congruence_class *, 2> newclasses;
+      newclasses.quick_push (new congruence_class (class_id++));
+      newclasses.quick_push (new congruence_class (class_id++));
 
       for (unsigned int i = 0; i < cls->members.length (); i++)
        {
@@ -2586,10 +3049,11 @@ sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
          add_item_to_class (tc, cls->members[i]);
        }
 
-#ifdef ENABLE_CHECKING
-      for (unsigned int i = 0; i < 2; i++)
-       gcc_checking_assert (newclasses[i]->members.length ());
-#endif
+      if (flag_checking)
+       {
+         for (unsigned int i = 0; i < 2; i++)
+           gcc_assert (newclasses[i]->members.length ());
+       }
 
       if (splitter_cls == cls)
        optimizer->splitter_class_removed = true;
@@ -2604,7 +3068,7 @@ sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
       g.hash = cls->members[0]->get_hash ();
       g.type = cls->members[0]->type;
 
-      congruence_class_group *slot = optimizer->m_classes.find(&g);
+      congruence_class_group *slot = optimizer->m_classes.find (&g);
 
       for (unsigned int i = 0; i < slot->classes.length (); i++)
        if (slot->classes[i] == cls)
@@ -2627,9 +3091,10 @@ sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
          optimizer->worklist_push (newclasses[i]);
       else /* Just smaller class is inserted.  */
        {
-         unsigned int smaller_index = newclasses[0]->members.length () <
-                                      newclasses[1]->members.length () ?
-                                      0 : 1;
+         unsigned int smaller_index
+           = (newclasses[0]->members.length ()
+              < newclasses[1]->members.length ()
+              ? 0 : 1);
          optimizer->worklist_push (newclasses[smaller_index]);
        }
 
@@ -2652,12 +3117,28 @@ sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
   return true;
 }
 
+/* Compare function for sorting pairs in do_congruence_step_f.  */
+
+int
+sem_item_optimizer::sort_congruence_split (const void *a_, const void *b_)
+{
+  const std::pair<congruence_class *, bitmap> *a
+    = (const std::pair<congruence_class *, bitmap> *)a_;
+  const std::pair<congruence_class *, bitmap> *b
+    = (const std::pair<congruence_class *, bitmap> *)b_;
+  if (a->first->id < b->first->id)
+    return -1;
+  else if (a->first->id > b->first->id)
+    return 1;
+  return 0;
+}
+
 /* Tests if a class CLS used as INDEXth splits any congruence classes.
    Bitmap stack BMSTACK is used for bitmap allocation.  */
 
 void
 sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
-    unsigned int index)
+                                                 unsigned int index)
 {
   hash_map <congruence_class *, bitmap> split_map;
 
@@ -2684,27 +3165,32 @@ sem_item_optimizer::do_congruence_step_for_index (congruence_class *cls,
          else
            b = *slot;
 
-#if ENABLE_CHECKING
          gcc_checking_assert (usage->item->cls);
-         gcc_checking_assert (usage->item->index_in_class <
-                              usage->item->cls->members.length ());
-#endif
+         gcc_checking_assert (usage->item->index_in_class
+                              < usage->item->cls->members.length ());
 
          bitmap_set_bit (b, usage->item->index_in_class);
        }
     }
 
+  auto_vec<std::pair<congruence_class *, bitmap> > to_split;
+  to_split.reserve_exact (split_map.elements ());
+  for (hash_map <congruence_class *, bitmap>::iterator i = split_map.begin ();
+       i != split_map.end (); ++i)
+    to_split.safe_push (*i);
+  to_split.qsort (sort_congruence_split);
+
   traverse_split_pair pair;
   pair.optimizer = this;
   pair.cls = cls;
 
   splitter_class_removed = false;
-  split_map.traverse
-  <traverse_split_pair *, sem_item_optimizer::traverse_congruence_split> (&pair);
+  for (unsigned i = 0; i < to_split.length (); ++i)
+    traverse_congruence_split (to_split[i].first, to_split[i].second, &pair);
 
   /* Bitmap clean-up.  */
-  split_map.traverse
-  <traverse_split_pair *, sem_item_optimizer::release_split_map> (NULL);
+  split_map.traverse <traverse_split_pair *,
+                     sem_item_optimizer::release_split_map> (NULL);
 }
 
 /* Every usage of a congruence class CLS is a candidate that can split the
@@ -2725,8 +3211,8 @@ sem_item_optimizer::do_congruence_step (congruence_class *cls)
   EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
   {
     if (dump_file && (dump_flags & TDF_DETAILS))
-      fprintf (dump_file, "  processing congruece step for class: %u, index: %u\n",
-              cls->id, i);
+      fprintf (dump_file, "  processing congruence step for class: %u, "
+              "index: %u\n", cls->id, i);
 
     do_congruence_step_for_index (cls, i);
 
@@ -2784,7 +3270,7 @@ sem_item_optimizer::worklist_pop (void)
 void
 sem_item_optimizer::process_cong_reduction (void)
 {
-  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
        it != m_classes.end (); ++it)
     for (unsigned i = 0; i < (*it)->classes.length (); i++)
       if ((*it)->classes[i]->is_class_used ())
@@ -2820,16 +3306,16 @@ sem_item_optimizer::dump_cong_classes (void)
     return;
 
   fprintf (dump_file,
-          "Congruence classes: %u (unique hash values: %lu), with total: %u items\n",
-          m_classes_count, (unsigned long) m_classes.elements(), m_items.length ());
+          "Congruence classes: %u (unique hash values: %lu), with total: "
+          "%u items\n", m_classes_count,
+          (unsigned long) m_classes.elements (), m_items.length ());
 
   /* Histogram calculation.  */
   unsigned int max_index = 0;
   unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length () + 1);
 
-  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
        it != m_classes.end (); ++it)
-
     for (unsigned i = 0; i < (*it)->classes.length (); i++)
       {
        unsigned int c = (*it)->classes[i]->members.length ();
@@ -2840,7 +3326,8 @@ sem_item_optimizer::dump_cong_classes (void)
       }
 
   fprintf (dump_file,
-          "Class size histogram [num of members]: number of classe number of classess\n");
+          "Class size histogram [num of members]: number of classe number "
+          "of classess\n");
 
   for (unsigned int i = 0; i <= max_index; i++)
     if (histogram[i])
@@ -2848,18 +3335,18 @@ sem_item_optimizer::dump_cong_classes (void)
 
   fprintf (dump_file, "\n\n");
 
-
   if (dump_flags & TDF_DETAILS)
-    for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
-        it != m_classes.end (); ++it)
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
       {
-       fprintf (dump_file, "  group: with %u classes:\n", (*it)->classes.length ());
+       fprintf (dump_file, "  group: with %u classes:\n",
+                (*it)->classes.length ());
 
        for (unsigned i = 0; i < (*it)->classes.length (); i++)
          {
            (*it)->classes[i]->dump (dump_file, 4);
 
-           if(i < (*it)->classes.length () - 1)
+           if (i < (*it)->classes.length () - 1)
              fprintf (dump_file, " ");
          }
       }
@@ -2867,11 +3354,72 @@ sem_item_optimizer::dump_cong_classes (void)
   free (histogram);
 }
 
+/* Sort pair of sem_items A and B by DECL_UID.  */
+
+static int
+sort_sem_items_by_decl_uid (const void *a, const void *b)
+{
+  const sem_item *i1 = *(const sem_item * const *)a;
+  const sem_item *i2 = *(const sem_item * const *)b;
+
+  int uid1 = DECL_UID (i1->decl);
+  int uid2 = DECL_UID (i2->decl);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
+/* Sort pair of congruence_classes A and B by DECL_UID of the first member.  */
+
+static int
+sort_congruence_classes_by_decl_uid (const void *a, const void *b)
+{
+  const congruence_class *c1 = *(const congruence_class * const *)a;
+  const congruence_class *c2 = *(const congruence_class * const *)b;
+
+  int uid1 = DECL_UID (c1->members[0]->decl);
+  int uid2 = DECL_UID (c2->members[0]->decl);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
+/* Sort pair of congruence_class_groups A and B by
+   DECL_UID of the first member of a first group.  */
+
+static int
+sort_congruence_class_groups_by_decl_uid (const void *a, const void *b)
+{
+  const congruence_class_group *g1
+    = *(const congruence_class_group * const *)a;
+  const congruence_class_group *g2
+    = *(const congruence_class_group * const *)b;
+
+  int uid1 = DECL_UID (g1->classes[0]->members[0]->decl);
+  int uid2 = DECL_UID (g2->classes[0]->members[0]->decl);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
 /* After reduction is done, we can declare all items in a group
    to be equal. PREV_CLASS_COUNT is start number of classes
-   before reduction.  */
+   before reduction. True is returned if there's a merge operation
+   processed. */
 
-void
+bool
 sem_item_optimizer::merge_classes (unsigned int prev_class_count)
 {
   unsigned int item_count = m_items.length ();
@@ -2881,7 +3429,25 @@ sem_item_optimizer::merge_classes (unsigned int prev_class_count)
   unsigned int non_singular_classes_count = 0;
   unsigned int non_singular_classes_sum = 0;
 
-  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
+  bool merged_p = false;
+
+  /* PR lto/78211
+     Sort functions in congruence classes by DECL_UID and do the same
+     for the classes to not to break -fcompare-debug.  */
+
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+       {
+         congruence_class *c = (*it)->classes[i];
+         c->members.qsort (sort_sem_items_by_decl_uid);
+       }
+
+      (*it)->classes.qsort (sort_congruence_classes_by_decl_uid);
+    }
+
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
        it != m_classes.end (); ++it)
     for (unsigned int i = 0; i < (*it)->classes.length (); i++)
       {
@@ -2893,6 +3459,13 @@ sem_item_optimizer::merge_classes (unsigned int prev_class_count)
          }
       }
 
+  auto_vec <congruence_class_group *> classes (m_classes.elements ());
+  for (hash_table<congruence_class_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    classes.quick_push (*it);
+
+  classes.qsort (sort_congruence_class_groups_by_decl_uid);
+
   if (dump_file)
     {
       fprintf (dump_file, "\nItem count: %u\n", item_count);
@@ -2910,29 +3483,39 @@ sem_item_optimizer::merge_classes (unsigned int prev_class_count)
               item_count ? 100.0f * equal_items / item_count : 0.0f);
     }
 
-  for (hash_table<congruence_class_group_hash>::iterator it = m_classes.begin ();
-       it != m_classes.end (); ++it)
-    for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+  unsigned int l;
+  congruence_class_group *it;
+  FOR_EACH_VEC_ELT (classes, l, it)
+    for (unsigned int i = 0; i < it->classes.length (); i++)
       {
-       congruence_class *c = (*it)->classes[i];
+       congruence_class *c = it->classes[i];
 
        if (c->members.length () == 1)
          continue;
 
-       gcc_assert (c->members.length ());
-
        sem_item *source = c->members[0];
 
-       for (unsigned int j = 1; j < c->members.length (); j++)
+       if (DECL_NAME (source->decl)
+           && MAIN_NAME_P (DECL_NAME (source->decl)))
+         /* If merge via wrappers, picking main as the target can be
+            problematic.  */
+         source = c->members[1];
+
+       for (unsigned int j = 0; j < c->members.length (); j++)
          {
            sem_item *alias = c->members[j];
 
+           if (alias == source)
+             continue;
+
            if (dump_file)
              {
                fprintf (dump_file, "Semantic equality hit:%s->%s\n",
-                        source->name (), alias->name ());
+                        xstrdup_for_dump (source->node->name ()),
+                        xstrdup_for_dump (alias->node->name ()));
                fprintf (dump_file, "Assembler symbol names:%s->%s\n",
-                        source->asm_name (), alias->asm_name ());
+                        xstrdup_for_dump (source->node->asm_name ()),
+                        xstrdup_for_dump (alias->node->asm_name ()));
              }
 
            if (lookup_attribute ("no_icf", DECL_ATTRIBUTES (alias->decl)))
@@ -2951,9 +3534,102 @@ sem_item_optimizer::merge_classes (unsigned int prev_class_count)
                alias->dump_to_file (dump_file);
              }
 
-           source->merge (alias);
+           if (dbg_cnt (merged_ipa_icf))
+             {
+               bool merged = source->merge (alias);
+               merged_p |= merged;
+
+               if (merged && alias->type == VAR)
+                 {
+                   symtab_pair p = symtab_pair (source->node, alias->node);
+                   m_merged_variables.safe_push (p);
+                 }
+             }
          }
       }
+
+  if (!m_merged_variables.is_empty ())
+    fixup_points_to_sets ();
+
+  return merged_p;
+}
+
+/* Fixup points to set PT.  */
+
+void
+sem_item_optimizer::fixup_pt_set (struct pt_solution *pt)
+{
+  if (pt->vars == NULL)
+    return;
+
+  unsigned i;
+  symtab_pair *item;
+  FOR_EACH_VEC_ELT (m_merged_variables, i, item)
+    if (bitmap_bit_p (pt->vars, DECL_UID (item->second->decl)))
+      bitmap_set_bit (pt->vars, DECL_UID (item->first->decl));
+}
+
+/* Set all points-to UIDs of aliases pointing to node N as UID.  */
+
+static void
+set_alias_uids (symtab_node *n, int uid)
+{
+  ipa_ref *ref;
+  FOR_EACH_ALIAS (n, ref)
+    {
+      if (dump_file)
+       fprintf (dump_file, "  Setting points-to UID of [%s] as %d\n",
+                xstrdup_for_dump (ref->referring->asm_name ()), uid);
+
+      SET_DECL_PT_UID (ref->referring->decl, uid);
+      set_alias_uids (ref->referring, uid);
+    }
+}
+
+/* Fixup points to analysis info.  */
+
+void
+sem_item_optimizer::fixup_points_to_sets (void)
+{
+  /* TODO: remove in GCC 9 and trigger PTA re-creation after IPA passes.  */
+  cgraph_node *cnode;
+
+  FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      tree name;
+      unsigned i;
+      function *fn = DECL_STRUCT_FUNCTION (cnode->decl);
+      if (!gimple_in_ssa_p (fn))
+       continue;
+
+      FOR_EACH_SSA_NAME (i, name, fn)
+       if (POINTER_TYPE_P (TREE_TYPE (name))
+           && SSA_NAME_PTR_INFO (name))
+         fixup_pt_set (&SSA_NAME_PTR_INFO (name)->pt);
+      fixup_pt_set (&fn->gimple_df->escaped);
+
+       /* The above get's us to 99% I guess, at least catching the
+         address compares.  Below also gets us aliasing correct
+         but as said we're giving leeway to the situation with
+         readonly vars anyway, so ... */
+       basic_block bb;
+       FOR_EACH_BB_FN (bb, fn)
+       for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+            gsi_next (&gsi))
+         {
+           gcall *call = dyn_cast<gcall *> (gsi_stmt (gsi));
+           if (call)
+             {
+               fixup_pt_set (gimple_call_use_set (call));
+               fixup_pt_set (gimple_call_clobber_set (call));
+             }
+         }
+    }
+
+  unsigned i;
+  symtab_pair *item;
+  FOR_EACH_VEC_ELT (m_merged_variables, i, item)
+    set_alias_uids (item->first, DECL_UID (item->first->decl));
 }
 
 /* Dump function prints all class members to a FILE with an INDENT.  */
@@ -2966,8 +3642,7 @@ congruence_class::dump (FILE *file, unsigned int indent) const
 
   FPUTS_SPACES (file, indent + 2, "");
   for (unsigned i = 0; i < members.length (); i++)
-    fprintf (file, "%s(%p/%u) ", members[i]->asm_name (), (void *) members[i]->decl,
-            members[i]->node->order);
+    fprintf (file, "%s ", members[i]->node->dump_asm_name ());
 
   fprintf (file, "\n");
 }
@@ -2984,11 +3659,6 @@ congruence_class::is_class_used (void)
   return false;
 }
 
-/* Initialization and computation of symtab node hash, there data
-   are propagated later on.  */
-
-static sem_item_optimizer *optimizer = NULL;
-
 /* Generate pass summary for IPA ICF pass.  */
 
 static void
@@ -3030,12 +3700,12 @@ ipa_icf_driver (void)
 {
   gcc_assert (optimizer);
 
-  optimizer->execute ();
+  bool merged_p = optimizer->execute ();
 
   delete optimizer;
   optimizer = NULL;
 
-  return 0;
+  return merged_p ? TODO_remove_functions : 0;
 }
 
 const pass_data pass_data_ipa_icf =