* ipa-icf.c (sem_function::merge): Check that local_original exists.
[platform/upstream/gcc.git] / gcc / ipa-icf.c
index 1055c23..0029a48 100644 (file)
@@ -53,81 +53,46 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #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 <list>
+#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 "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"
 
 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 +105,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);
 
@@ -176,7 +140,7 @@ sem_usage_pair::sem_usage_pair (sem_item *_item, unsigned int _index):
    for bitmap memory allocation.  */
 
 sem_item::sem_item (sem_item_type _type,
-                   bitmap_obstack *stack): type(_type), hash(0)
+                   bitmap_obstack *stack): type (_type), m_hash (0)
 {
   setup (stack);
 }
@@ -187,7 +151,7 @@ sem_item::sem_item (sem_item_type _type,
 
 sem_item::sem_item (sem_item_type _type, symtab_node *_node,
                    hashval_t _hash, bitmap_obstack *stack): type(_type),
-  node (_node), hash (_hash)
+  node (_node), m_hash (_hash)
 {
   decl = node->decl;
   setup (stack);
@@ -239,12 +203,12 @@ 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);
+              node->name(), node->order, (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,12 +227,16 @@ sem_item::target_supports_symbol_aliases_p (void)
 #endif
 }
 
+void sem_item::set_hash (hashval_t hash)
+{
+  m_hash = hash;
+}
+
 /* 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)
 {
-  arg_types.create (0);
   bb_sizes.create (0);
   bb_sorted.create (0);
 }
@@ -280,7 +248,6 @@ sem_function::sem_function (cgraph_node *node, hashval_t hash,
   sem_item (FUNC, node, hash, stack),
   m_checker (NULL), m_compared_func (NULL)
 {
-  arg_types.create (0);
   bb_sizes.create (0);
   bb_sorted.create (0);
 }
@@ -290,7 +257,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 +279,7 @@ sem_function::get_bb_hash (const sem_bb *basic_block)
 hashval_t
 sem_function::get_hash (void)
 {
-  if(!hash)
+  if (!m_hash)
     {
       inchash::hash hstate;
       hstate.add_int (177454); /* Random number for function type.  */
@@ -328,23 +294,216 @@ 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_wide_int
+        (cl_target_option_hash
+          (TREE_TARGET_OPTION (DECL_FUNCTION_SPECIFIC_TARGET (decl))));
+      if (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl))
+        (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;
 }
 
+/* Return ture if A1 and A2 represent equivalent function attribute lists.
+   Based on comp_type_attributes.  */
+
+bool
+sem_item::compare_attributes (const_tree a1, const_tree a2)
+{
+  const_tree a;
+  if (a1 == a2)
+    return true;
+  for (a = a1; a != NULL_TREE; a = TREE_CHAIN (a))
+    {
+      const struct attribute_spec *as;
+      const_tree attr;
+
+      as = lookup_attribute_spec (get_attribute_name (a));
+      /* TODO: We can introduce as->affects_decl_identity
+        and as->affects_decl_reference_identity if attribute mismatch
+        gets a common reason to give up on merging.  It may not be worth
+        the effort.
+        For example returns_nonnull affects only references, while
+        optimize attribute can be ignored because it is already lowered
+        into flags representation and compared separately.  */
+      if (!as)
+        continue;
+
+      attr = lookup_attribute (as->name, CONST_CAST_TREE (a2));
+      if (!attr || !attribute_value_equal (a, attr))
+        break;
+    }
+  if (!a)
+    {
+      for (a = a2; a != NULL_TREE; a = TREE_CHAIN (a))
+       {
+         const struct attribute_spec *as;
+
+         as = lookup_attribute_spec (get_attribute_name (a));
+         if (!as)
+           continue;
+
+         if (!lookup_attribute (as->name, CONST_CAST_TREE (a1)))
+           break;
+         /* We don't need to compare trees again, as we did this
+            already in first loop.  */
+       }
+      if (!a)
+        return true;
+    }
+  /* TODO: As in comp_type_attributes we may want to introduce target hook.  */
+  return false;
+}
+
+/* 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 (!compare_attributes (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 +512,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 +538,47 @@ 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 false;
+
+  struct ipa_node_params *parms_info = IPA_NODE_REF (get_node ());
+
+  if (parms_info->descriptors.is_empty ()
+      || parms_info->descriptors.length () <= 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 +586,170 @@ 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.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 && 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");
+    }
+
+  /* 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 (!param_used_p (i))
+       continue;
 
-      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");
+      /* 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 (!compare_attributes (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 +760,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 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 edges");
+    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)
     {
@@ -465,8 +880,13 @@ 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");
+            xstrdup_for_dump (node->name()),
+            xstrdup_for_dump (item->node->name ()),
+            node->order,
+            item->node->order,
+            xstrdup_for_dump (node->asm_name ()),
+            xstrdup_for_dump (item->node->asm_name ()),
+            eq ? "true" : "false");
 
   return eq;
 }
@@ -474,8 +894,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 +914,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 +989,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;
 }
 
@@ -763,6 +1096,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))
     {
@@ -821,13 +1161,25 @@ 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,
@@ -871,6 +1223,11 @@ sem_function::merge (sem_item *alias_item)
        = alias->get_availability () > AVAIL_INTERPOSABLE
          && original->get_availability () > AVAIL_INTERPOSABLE
          && !alias->instrumented_version;
+      /* 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)
        {
@@ -995,10 +1352,17 @@ sem_function::merge (sem_item *alias_item)
   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)
     {
@@ -1038,46 +1402,59 @@ 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));
+       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_wide_int (cnode->thunk.fixed_offset);
+      hstate.add_wide_int (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.
@@ -1159,10 +1536,82 @@ sem_item::add_expr (const_tree exp, inchash::hash &hstate)
     }
 }
 
+/* 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))
+    {
+      gcc_checking_assert (COMPLETE_TYPE_P (type));
+      hashval_t *val = optimizer->m_type_hash_cache.get (type);
+
+      if (!val)
+       {
+         inchash::hash hstate2;
+         unsigned nf;
+         tree f;
+         hashval_t hash;
+
+         hstate2.add_int (RECORD_TYPE);
+         gcc_assert (COMPLETE_TYPE_P (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_wide_int (hash);
+         optimizer->m_type_hash_cache.put (type, hash);
+       }
+      else
+        hstate.add_wide_int (*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);
 
@@ -1170,16 +1619,27 @@ 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 ... */
@@ -1190,7 +1650,11 @@ 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);
+       }
     default:
       break;
     }
@@ -1202,10 +1666,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
@@ -1217,9 +1690,7 @@ 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)
@@ -1232,37 +1703,6 @@ sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
   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);
-
-  for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
-    arg_types.safe_push (DECL_ARG_TYPE (parm));
-
-  /* Function result type.  */
-  result = DECL_RESULT (decl);
-  result_type = result ? TREE_TYPE (result) : NULL;
-
-  /* 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)));
-
-      result_type = TREE_TYPE (TREE_TYPE (decl));
-    }
-}
-
 /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
    return true if phi nodes are semantically equivalent in these blocks .  */
 
@@ -1334,9 +1774,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
@@ -1360,41 +1799,6 @@ 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.  */
 
@@ -1427,8 +1831,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");
@@ -1454,7 +1858,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;
@@ -1465,8 +1872,6 @@ sem_variable::equals_wpa (sem_item *item,
 
 /* Returns true if the item equals to ITEM given as argument.  */
 
-/* Returns true if the item equals to ITEM given as argument.  */
-
 bool
 sem_variable::equals (sem_item *item,
                      hash_map <symtab_node *, sem_item *> &)
@@ -1479,13 +1884,21 @@ sem_variable::equals (sem_item *item,
   if (DECL_INITIAL (item->decl) == error_mark_node && in_lto_p)
     dyn_cast <varpool_node *>(item->node)->get_constructor ();
 
+  /* 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");
+
   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 (%u:%u) (%s:%s) with result: %s\n\n",
-            name(), item->name (), node->order, item->node->order, asm_name (),
-            item->asm_name (), ret ? "true" : "false");
+            xstrdup_for_dump (node->name()),
+            xstrdup_for_dump (item->node->name ()),
+            node->order, item->node->order,
+            xstrdup_for_dump (node->asm_name ()),
+            xstrdup_for_dump (item->node->asm_name ()), ret ? "true" : "false");
 
   return ret;
 }
@@ -1555,8 +1968,7 @@ 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.  */
@@ -1614,8 +2026,8 @@ 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;
@@ -1665,8 +2077,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:
@@ -1697,9 +2108,9 @@ sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
 hashval_t
 sem_variable::get_hash (void)
 {
-  if (hash)
+  if (m_hash)
+    return m_hash;
 
-    return 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.
      DECL_INITIAL (decl) would be error_mark_node in that case.  */
@@ -1711,9 +2122,9 @@ sem_variable::get_hash (void)
   if (DECL_SIZE (decl) && tree_fits_shwi_p (DECL_SIZE (decl)))
     hstate.add_wide_int (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
@@ -1732,6 +2143,13 @@ 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 ();
@@ -1843,40 +2261,6 @@ 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),
@@ -1993,8 +2377,8 @@ 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, uid:%u)\n",
+                node->asm_name (), (void *) node->decl, node->order);
 
       if (is_a<cgraph_node *> (node))
        {
@@ -2015,7 +2399,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)
@@ -2154,10 +2538,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);
@@ -2195,6 +2576,8 @@ sem_item_optimizer::execute (void)
   filter_removed_items ();
   unregister_hooks ();
 
+  build_graph ();
+  update_hash_by_addr_refs ();
   build_hash_based_classes ();
 
   if (dump_file)
@@ -2204,8 +2587,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)
@@ -2214,7 +2595,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");
@@ -2233,7 +2614,7 @@ sem_item_optimizer::execute (void)
 
   process_cong_reduction ();
   dump_cong_classes ();
-  verify_classes ();
+  checking_verify_classes ();
   bool merged_p = merge_classes (prev_class_count);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
@@ -2260,7 +2641,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);
@@ -2294,6 +2675,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_wide_int
+                   (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
@@ -2325,6 +2753,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++)
@@ -2451,7 +2883,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
@@ -2462,6 +2894,8 @@ 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 ();
@@ -2476,8 +2910,7 @@ sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
 
          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++)
                {
@@ -2485,10 +2918,15 @@ sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
 
                  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
@@ -2497,9 +2935,8 @@ sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
                {
                  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++);
@@ -2522,6 +2959,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 ();
+               }
            }
          }
 
@@ -2532,12 +2977,20 @@ 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 ();
        it != m_classes.end (); ++it)
     {
@@ -2545,26 +2998,25 @@ sem_item_optimizer::verify_classes (void)
        {
          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
@@ -2599,7 +3051,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++)
        {
@@ -2609,10 +3063,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;
@@ -2707,11 +3162,9 @@ 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
 
          bitmap_set_bit (b, usage->item->index_in_class);
        }
@@ -2748,7 +3201,7 @@ 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",
+      fprintf (dump_file, "  processing congruence step for class: %u, index: %u\n",
               cls->id, i);
 
     do_congruence_step_for_index (cls, i);
@@ -2956,9 +3409,11 @@ sem_item_optimizer::merge_classes (unsigned int prev_class_count)
            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)))
@@ -2977,7 +3432,8 @@ sem_item_optimizer::merge_classes (unsigned int prev_class_count)
                alias->dump_to_file (dump_file);
              }
 
-           merged_p |= source->merge (alias);
+           if (dbg_cnt (merged_ipa_icf))
+             merged_p |= source->merge (alias);
          }
       }
 
@@ -2994,7 +3450,8 @@ 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,
+    fprintf (file, "%s(%p/%u) ", members[i]->node->asm_name (),
+            (void *) members[i]->decl,
             members[i]->node->order);
 
   fprintf (file, "\n");
@@ -3012,11 +3469,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