diff gcc/ipa-icf.c @ 16:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/ipa-icf.c	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,3695 @@
+/* Interprocedural Identical Code Folding pass
+   Copyright (C) 2014-2017 Free Software Foundation, Inc.
+
+   Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Interprocedural Identical Code Folding for functions and
+   read-only variables.
+
+   The goal of this transformation is to discover functions and read-only
+   variables which do have exactly the same semantics.
+
+   In case of functions,
+   we could either create a virtual clone or do a simple function wrapper
+   that will call equivalent function. If the function is just locally visible,
+   all function calls can be redirected. For read-only variables, we create
+   aliases if possible.
+
+   Optimization pass arranges as follows:
+   1) All functions and read-only variables are visited and internal
+      data structure, either sem_function or sem_variables is created.
+   2) For every symbol from the previous step, VAR_DECL and FUNCTION_DECL are
+      saved and matched to corresponding sem_items.
+   3) These declaration are ignored for equality check and are solved
+      by Value Numbering algorithm published by Alpert, Zadeck in 1992.
+   4) We compute hash value for each symbol.
+   5) Congruence classes are created based on hash value. If hash value are
+      equal, equals function is called and symbols are deeply compared.
+      We must prove that all SSA names, declarations and other items
+      correspond.
+   6) Value Numbering is executed for these classes. At the end of the process
+      all symbol members in remaining classes can be merged.
+   7) Merge operation creates alias in case of read-only variables. For
+      callgraph node, we must decide if we can redirect local calls,
+      create an alias or a thunk.
+
+*/
+
+#include "config.h"
+#define INCLUDE_LIST
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple.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 "varasm.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "symbol-summary.h"
+#include "ipa-prop.h"
+#include "ipa-fnsummary.h"
+#include "except.h"
+#include "attribs.h"
+#include "print-tree.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)
+{
+  m_references.create (0);
+  m_interposables.create (0);
+
+  ipa_ref *ref;
+
+  if (is_a <varpool_node *> (node) && DECL_VIRTUAL_P (node->decl))
+    return;
+
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+    {
+      if (ref->address_matters_p ())
+	m_references.safe_push (ref->referred);
+
+      if (ref->referred->get_availability () <= AVAIL_INTERPOSABLE)
+        {
+	  if (ref->address_matters_p ())
+	    m_references.safe_push (ref->referred);
+	  else
+	    m_interposables.safe_push (ref->referred);
+	}
+    }
+
+  if (is_a <cgraph_node *> (node))
+    {
+      cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+
+      for (cgraph_edge *e = cnode->callees; e; e = e->next_callee)
+	if (e->callee->get_availability () <= AVAIL_INTERPOSABLE)
+	  m_interposables.safe_push (e->callee);
+    }
+}
+
+/* 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_item::sem_item (sem_item_type _type, bitmap_obstack *stack)
+: type (_type), m_hash (-1), m_hash_set (false)
+{
+  setup (stack);
+}
+
+sem_item::sem_item (sem_item_type _type, symtab_node *_node,
+		    bitmap_obstack *stack)
+: type (_type), node (_node), m_hash (-1), m_hash_set (false)
+{
+  decl = node->decl;
+  setup (stack);
+}
+
+/* Add reference to a semantic TARGET.  */
+
+void
+sem_item::add_reference (sem_item *target)
+{
+  refs.safe_push (target);
+  unsigned index = refs.length ();
+  target->usages.safe_push (new sem_usage_pair(this, index));
+  bitmap_set_bit (target->usage_index_bitmap, index);
+  refs_set.add (target->node);
+}
+
+/* Initialize internal data structures. Bitmap STACK is used for
+   bitmap memory allocation process.  */
+
+void
+sem_item::setup (bitmap_obstack *stack)
+{
+  gcc_checking_assert (node);
+
+  refs.create (0);
+  tree_refs.create (0);
+  usages.create (0);
+  usage_index_bitmap = BITMAP_ALLOC (stack);
+}
+
+sem_item::~sem_item ()
+{
+  for (unsigned i = 0; i < usages.length (); i++)
+    delete usages[i];
+
+  refs.release ();
+  tree_refs.release ();
+  usages.release ();
+
+  BITMAP_FREE (usage_index_bitmap);
+}
+
+/* Dump function for debugging purpose.  */
+
+DEBUG_FUNCTION void
+sem_item::dump (void)
+{
+  if (dump_file)
+    {
+      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]->node->name (),
+		 i < refs.length() - 1 ? "," : "");
+
+      fprintf (dump_file, "\n");
+    }
+}
+
+/* Return true if target supports alias symbols.  */
+
+bool
+sem_item::target_supports_symbol_aliases_p (void)
+{
+#if !defined (ASM_OUTPUT_DEF) || (!defined(ASM_OUTPUT_WEAK_ALIAS) && !defined (ASM_WEAKEN_DECL))
+  return false;
+#else
+  return true;
+#endif
+}
+
+void sem_item::set_hash (hashval_t hash)
+{
+  m_hash = hash;
+  m_hash_set = true;
+}
+
+/* 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)
+{
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::sem_function (cgraph_node *node, bitmap_obstack *stack)
+: sem_item (FUNC, node, stack), m_checker (NULL), m_compared_func (NULL)
+{
+  bb_sizes.create (0);
+  bb_sorted.create (0);
+}
+
+sem_function::~sem_function ()
+{
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    delete (bb_sorted[i]);
+
+  bb_sizes.release ();
+  bb_sorted.release ();
+}
+
+/* Calculates hash value based on a BASIC_BLOCK.  */
+
+hashval_t
+sem_function::get_bb_hash (const sem_bb *basic_block)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (basic_block->nondbg_stmt_count);
+  hstate.add_int (basic_block->edge_count);
+
+  return hstate.end ();
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_function::get_hash (void)
+{
+  if (!m_hash_set)
+    {
+      inchash::hash hstate;
+      hstate.add_int (177454); /* Random number for function type.  */
+
+      hstate.add_int (arg_count);
+      hstate.add_int (cfg_checksum);
+      hstate.add_int (gcode_hash);
+
+      for (unsigned i = 0; i < bb_sorted.length (); i++)
+	hstate.merge_hash (get_bb_hash (bb_sorted[i]));
+
+      for (unsigned i = 0; i < bb_sizes.length (); i++)
+	hstate.add_int (bb_sizes[i]);
+
+      /* 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 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_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))
+    return true;
+
+  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))
+    return true;
+
+  return return_false_with_msg ("different references");
+}
+
+/* If cgraph edges E1 and E2 are indirect calls, verify that
+   ECF flags are the same.  */
+
+bool sem_function::compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2)
+{
+  if (e1->indirect_info && e2->indirect_info)
+    {
+      int e1_flags = e1->indirect_info->ecf_flags;
+      int e2_flags = e2->indirect_info->ecf_flags;
+
+      if (e1_flags != e2_flags)
+	return return_false_with_msg ("ICF flags are different");
+    }
+  else if (e1->indirect_info || e2->indirect_info)
+    return false;
+
+  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
+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 (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.  */
+  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 (!parm1 || !parm2)
+	return return_false_with_msg ("NULL argument type");
+
+      /* 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;
+
+      /* Perform additional checks for used parameters.  */
+      if (!compatible_parm_types_p (parm1, parm2))
+	return false;
+    }
+
+  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 (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;
+    }
+
+  cgraph_edge *e1 = dyn_cast <cgraph_node *> (node)->callees;
+  cgraph_edge *e2 = dyn_cast <cgraph_node *> (item->node)->callees;
+
+  while (e1 && e2)
+    {
+      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 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 *> &)
+{
+  gcc_assert (item->type == FUNC);
+  bool eq = equals_private (item);
+
+  if (m_checker != NULL)
+    {
+      delete m_checker;
+      m_checker = NULL;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file,
+	     "Equals called for: %s:%s with result: %s\n\n",
+	     node->dump_name (),
+	     item->node->dump_name (),
+	     eq ? "true" : "false");
+
+  return eq;
+}
+
+/* Processes function equality comparison.  */
+
+bool
+sem_function::equals_private (sem_item *item)
+{
+  if (item->type != FUNC)
+    return false;
+
+  basic_block bb1, bb2;
+  edge e1, e2;
+  edge_iterator ei1, ei2;
+  bool result = true;
+  tree arg1, arg2;
+
+  m_compared_func = static_cast<sem_function *> (item);
+
+  gcc_assert (decl != item->decl);
+
+  if (bb_sorted.length () != m_compared_func->bb_sorted.length ()
+      || edge_count != m_compared_func->edge_count
+      || cfg_checksum != m_compared_func->cfg_checksum)
+    return return_false ();
+
+  m_checker = new func_checker (decl, m_compared_func->decl,
+				compare_polymorphic_p (),
+				false,
+				&refs_set,
+				&m_compared_func->refs_set);
+  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 (!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 (!dyn_cast <cgraph_node *> (node)->has_gimple_body_p ())
+    return true;
+
+  /* Fill-up label dictionary.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      m_checker->parse_labels (bb_sorted[i]);
+      m_checker->parse_labels (m_compared_func->bb_sorted[i]);
+    }
+
+  /* Checking all basic blocks.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i]))
+      return return_false();
+
+  dump_message ("All BBs are equal\n");
+
+  auto_vec <int> bb_dict;
+
+  /* Basic block edges check.  */
+  for (unsigned i = 0; i < bb_sorted.length (); ++i)
+    {
+      bb1 = bb_sorted[i]->bb;
+      bb2 = m_compared_func->bb_sorted[i]->bb;
+
+      ei2 = ei_start (bb2->preds);
+
+      for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1))
+	{
+	  ei_cond (ei2, &e2);
+
+	  if (e1->flags != e2->flags)
+	    return return_false_with_msg ("flags comparison returns false");
+
+	  if (!bb_dict_test (&bb_dict, e1->src->index, e2->src->index))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  if (!bb_dict_test (&bb_dict, e1->dest->index, e2->dest->index))
+	    return return_false_with_msg ("BB comparison returns false");
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false_with_msg ("edge comparison returns false");
+
+	  ei_next (&ei2);
+	}
+    }
+
+  /* Basic block PHI nodes comparison.  */
+  for (unsigned i = 0; i < bb_sorted.length (); i++)
+    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");
+
+  return result;
+}
+
+/* Set LOCAL_P of NODE to true if DATA is non-NULL.
+   Helper for call_for_symbol_thunks_and_aliases.  */
+
+static bool
+set_local (cgraph_node *node, void *data)
+{
+  node->local.local = data != NULL;
+  return false;
+}
+
+/* TREE_ADDRESSABLE of NODE to true.
+   Helper for call_for_symbol_thunks_and_aliases.  */
+
+static bool
+set_addressable (varpool_node *node, void *)
+{
+  TREE_ADDRESSABLE (node->decl) = 1;
+  return false;
+}
+
+/* Clear DECL_RTL of NODE. 
+   Helper for call_for_symbol_thunks_and_aliases.  */
+
+static bool
+clear_decl_rtl (symtab_node *node, void *)
+{
+  SET_DECL_RTL (node->decl, NULL);
+  return false;
+}
+
+/* Redirect all callers of N and its aliases to TO.  Remove aliases if
+   possible.  Return number of redirections made.  */
+
+static int
+redirect_all_callers (cgraph_node *n, cgraph_node *to)
+{
+  int nredirected = 0;
+  ipa_ref *ref;
+  cgraph_edge *e = n->callers;
+
+  while (e)
+    {
+      /* 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);)
+    {
+      bool removed = false;
+      cgraph_node *n_alias = dyn_cast <cgraph_node *> (ref->referring);
+
+      if ((DECL_COMDAT_GROUP (n->decl)
+	   && (DECL_COMDAT_GROUP (n->decl)
+	       == DECL_COMDAT_GROUP (n_alias->decl)))
+	  || (n_alias->get_availability () > AVAIL_INTERPOSABLE
+	      && n->get_availability () > AVAIL_INTERPOSABLE))
+	{
+	  nredirected += redirect_all_callers (n_alias, to);
+	  if (n_alias->can_remove_if_no_direct_calls_p ()
+	      && !n_alias->call_for_symbol_and_aliases (cgraph_node::has_thunk_p,
+							NULL, true)
+	      && !n_alias->has_aliases_p ())
+	    n_alias->remove ();
+	}
+      if (!removed)
+	i++;
+    }
+  return nredirected;
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_function::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == FUNC);
+
+  sem_function *alias_func = static_cast<sem_function *> (alias_item);
+
+  cgraph_node *original = get_node ();
+  cgraph_node *local_original = NULL;
+  cgraph_node *alias = alias_func->get_node ();
+
+  bool create_wrapper = false;
+  bool create_alias = false;
+  bool redirect_callers = false;
+  bool remove = false;
+
+  bool original_discardable = false;
+  bool original_discarded = false;
+
+  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))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; "
+		 "DECL_NO_INLINE_WARNING mismatch.\n\n");
+      return false;
+    }
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; "
+		 "original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.  */
+
+  if (original->can_be_discarded_p ())
+    original_discardable = true;
+  /* Also consider case where we have resolution info and we know that
+     original's definition is not going to be used.  In this case we can not
+     create alias to original.  */
+  if (node->resolution != LDPR_UNKNOWN
+      && !decl_binds_to_current_def_p (node->decl))
+    original_discardable = original_discarded = true;
+
+  /* Creating a symtab alias is the optimal way to merge.
+     It however can not be used in the following cases:
+
+     1) if ORIGINAL and ALIAS may be possibly compared for address equality.
+     2) if ORIGINAL is in a section that may be discarded by linker or if
+	it is an external functions where we can not create an alias
+	(ORIGINAL_DISCARDABLE)
+     3) if target do not support symbol aliases.
+     4) original and alias lie in different comdat groups.
+
+     If we can not produce alias, we will turn ALIAS into WRAPPER of ORIGINAL
+     and/or redirect all callers from ALIAS to ORIGINAL.  */
+  if ((original_address_matters && alias_address_matters)
+      || (original_discardable
+	  && (!DECL_COMDAT_GROUP (alias->decl)
+	      || (DECL_COMDAT_GROUP (alias->decl)
+		  != DECL_COMDAT_GROUP (original->decl))))
+      || original_discarded
+      || !sem_item::target_supports_symbol_aliases_p ()
+      || DECL_COMDAT_GROUP (alias->decl) != DECL_COMDAT_GROUP (original->decl))
+    {
+      /* 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.  */
+      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)
+	       || DECL_STATIC_CHAIN (original->decl))
+        {
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Cannot create wrapper of nested function.\n");
+        }
+      /* TODO: We can also deal with variadic functions never calling
+	 VA_START.  */
+      else if (stdarg_p (TREE_TYPE (alias->decl)))
+	{
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "can not create wrapper of stdarg function.\n");
+	}
+      else if (ipa_fn_summaries
+	       && ipa_fn_summaries->get (alias)->self_size <= 2)
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Wrapper creation is not "
+		     "profitable (function is too small).\n");
+	}
+      /* If user paid attention to mark function noinline, assume it is
+	 somewhat special and do not try to turn it into a wrapper that can
+	 not be undone by inliner.  */
+      else if (lookup_attribute ("noinline", DECL_ATTRIBUTES (alias->decl)))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Wrappers are not created for noinline.\n");
+	}
+      else
+        create_wrapper = true;
+
+      /* We can redirect local calls in the case both alias and orignal
+	 are not interposable.  */
+      redirect_callers
+	= 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)
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Not unifying; can not redirect callers nor "
+		     "produce wrapper\n\n");
+	  return false;
+	}
+
+      /* Work out the symbol the wrapper should call.
+	 If ORIGINAL is interposable, we need to call a local alias.
+	 Also produce local alias (if possible) as an optimization.
+
+	 Local aliases can not be created inside comdat groups because that
+	 prevents inlining.  */
+      if (!original_discardable && !original->get_comdat_group ())
+	{
+	  local_original
+	    = dyn_cast <cgraph_node *> (original->noninterposable_alias ());
+	  if (!local_original
+	      && original->get_availability () > AVAIL_INTERPOSABLE)
+	    local_original = original;
+	}
+      /* If we can not use local alias, fallback to the original
+	 when possible.  */
+      else if (original->get_availability () > AVAIL_INTERPOSABLE)
+	local_original = original;
+
+      /* If original is COMDAT local, we can not really redirect calls outside
+	 of its comdat group to it.  */
+      if (original->comdat_local_p ())
+        redirect_callers = false;
+      if (!local_original)
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Not unifying; "
+		     "can not produce local alias.\n\n");
+	  return false;
+	}
+
+      if (!redirect_callers && !create_wrapper)
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Not unifying; "
+		     "can not redirect callers nor produce a wrapper\n\n");
+	  return false;
+	}
+      if (!create_wrapper
+	  && !alias->call_for_symbol_and_aliases (cgraph_node::has_thunk_p,
+						  NULL, true)
+	  && !alias->can_remove_if_no_direct_calls_p ())
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Not unifying; can not make wrapper and "
+		     "function has other uses than direct calls\n\n");
+	  return false;
+	}
+    }
+  else
+    create_alias = true;
+
+  if (redirect_callers)
+    {
+      int nredirected = redirect_all_callers (alias, local_original);
+
+      if (nredirected)
+	{
+	  alias->icf_merged = true;
+	  local_original->icf_merged = true;
+
+	  if (dump_file && nredirected)
+	    fprintf (dump_file, "%i local calls have been "
+		     "redirected.\n", nredirected);
+	}
+
+      /* 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;
+	  remove = true;
+	}
+      gcc_assert (!create_alias);
+    }
+  else if (create_alias)
+    {
+      alias->icf_merged = true;
+
+      /* Remove the function's body.  */
+      ipa_merge_profiles (original, alias);
+      alias->release_body (true);
+      alias->reset ();
+      /* Notice global symbol possibly produced RTL.  */
+      ((symtab_node *)alias)->call_for_symbol_and_aliases (clear_decl_rtl,
+							   NULL, true);
+
+      /* Create the alias.  */
+      cgraph_node::create_alias (alias_func->decl, decl);
+      alias->resolve_alias (original);
+
+      original->call_for_symbol_thunks_and_aliases
+	 (set_local, (void *)(size_t) original->local_p (), true);
+
+      if (dump_file)
+	fprintf (dump_file, "Unified; Function alias has been created.\n\n");
+    }
+  if (create_wrapper)
+    {
+      gcc_assert (!create_alias);
+      alias->icf_merged = true;
+      local_original->icf_merged = 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");
+    }
+
+  /* It's possible that redirection can hit thunks that block
+     redirection opportunities.  */
+  gcc_assert (alias->icf_merged || remove || redirect_callers);
+  original->icf_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)
+    {
+      ipa_merge_profiles (original, alias);
+      alias->release_body ();
+      alias->reset ();
+      alias->body_removed = true;
+      alias->icf_merged = true;
+      if (dump_file)
+	fprintf (dump_file, "Unified; Function body was removed.\n");
+    }
+
+  return true;
+}
+
+/* Semantic item initialization function.  */
+
+void
+sem_function::init (void)
+{
+  if (in_lto_p)
+    get_node ()->get_untransformed_body ();
+
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  gcc_assert (func);
+  gcc_assert (SSANAMES (func));
+
+  ssa_names_size = SSANAMES (func)->length ();
+  node = node;
+
+  decl = fndecl;
+  region_tree = func->eh->region_tree;
+
+  /* iterating all function arguments.  */
+  arg_count = count_formal_params (fndecl);
+
+  edge_count = n_edges_for_fn (func);
+  cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+  if (!cnode->thunk.thunk_p)
+    {
+      cfg_checksum = coverage_compute_cfg_checksum (func);
+
+      inchash::hash hstate;
+
+      basic_block bb;
+      FOR_EACH_BB_FN (bb, func)
+      {
+	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);
+
+	for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+	     gsi_next (&gsi))
+	  {
+	    gimple *stmt = gsi_stmt (gsi);
+
+	    if (gimple_code (stmt) != GIMPLE_DEBUG
+		&& gimple_code (stmt) != GIMPLE_PREDICT)
+	      {
+		hash_stmt (stmt, hstate);
+		nondbg_stmt_count++;
+	      }
+	  }
+
+	hstate.commit_flag ();
+	gcode_hash = hstate.end ();
+	bb_sizes.safe_push (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));
+
+	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.
+   Identical to inchash::add_expr, but guaranteed to be stable across LTO
+   and DECL equality classes.  */
+
+void
+sem_item::add_expr (const_tree exp, inchash::hash &hstate)
+{
+  if (exp == NULL_TREE)
+    {
+      hstate.merge_hash (0);
+      return;
+    }
+
+  /* Handled component can be matched in a cureful way proving equivalence
+     even if they syntactically differ.  Just skip them.  */
+  STRIP_NOPS (exp);
+  while (handled_component_p (exp))
+    exp = TREE_OPERAND (exp, 0);
+
+  enum tree_code code = TREE_CODE (exp);
+  hstate.add_int (code);
+
+  switch (code)
+    {
+    /* Use inchash::add_expr for everything that is LTO stable.  */
+    case VOID_CST:
+    case INTEGER_CST:
+    case REAL_CST:
+    case FIXED_CST:
+    case STRING_CST:
+    case COMPLEX_CST:
+    case VECTOR_CST:
+      inchash::add_expr (exp, hstate);
+      break;
+    case CONSTRUCTOR:
+      {
+	unsigned HOST_WIDE_INT idx;
+	tree value;
+
+	hstate.add_hwi (int_size_in_bytes (TREE_TYPE (exp)));
+
+	FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
+	  if (value)
+	    add_expr (value, hstate);
+	break;
+      }
+    case ADDR_EXPR:
+    case FDESC_EXPR:
+      add_expr (get_base_address (TREE_OPERAND (exp, 0)), hstate);
+      break;
+    case SSA_NAME:
+    case VAR_DECL:
+    case CONST_DECL:
+    case PARM_DECL:
+      hstate.add_hwi (int_size_in_bytes (TREE_TYPE (exp)));
+      break;
+    case MEM_REF:
+    case POINTER_PLUS_EXPR:
+    case MINUS_EXPR:
+    case RANGE_EXPR:
+      add_expr (TREE_OPERAND (exp, 0), hstate);
+      add_expr (TREE_OPERAND (exp, 1), hstate);
+      break;
+    case PLUS_EXPR:
+      {
+	inchash::hash one, two;
+	add_expr (TREE_OPERAND (exp, 0), one);
+	add_expr (TREE_OPERAND (exp, 1), two);
+	hstate.add_commutative (one, two);
+      }
+      break;
+    CASE_CONVERT:
+      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))
+    {
+      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_hwi (hash);
+	  optimizer->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)
+{
+  enum gimple_code code = gimple_code (stmt);
+
+  hstate.add_int (code);
+
+  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 */
+    case GIMPLE_CALL:
+    case GIMPLE_ASM:
+    case GIMPLE_COND:
+    case GIMPLE_GOTO:
+    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);
+	  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;
+    }
+}
+
+
+/* Return true if polymorphic comparison must be processed.  */
+
+bool
+sem_function::compare_polymorphic_p (void)
+{
+  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
+   semantic function item.  */
+
+sem_function *
+sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
+{
+  tree fndecl = node->decl;
+  function *func = DECL_STRUCT_FUNCTION (fndecl);
+
+  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;
+
+  if (lookup_attribute_by_prefix ("oacc ",
+				  DECL_ATTRIBUTES (node->decl)) != NULL)
+    return NULL;
+
+  /* PR ipa/70306.  */
+  if (DECL_STATIC_CONSTRUCTOR (node->decl)
+      || DECL_STATIC_DESTRUCTOR (node->decl))
+    return NULL;
+
+  sem_function *f = new sem_function (node, stack);
+
+  f->init ();
+
+  return f;
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks .  */
+
+bool
+sem_function::compare_phi_node (basic_block bb1, basic_block bb2)
+{
+  gphi_iterator si1, si2;
+  gphi *phi1, *phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  gcc_assert (bb1 != NULL);
+  gcc_assert (bb2 != NULL);
+
+  si2 = gsi_start_phis (bb2);
+  for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1);
+       gsi_next (&si1))
+    {
+      gsi_next_nonvirtual_phi (&si1);
+      gsi_next_nonvirtual_phi (&si2);
+
+      if (gsi_end_p (si1) && gsi_end_p (si2))
+	break;
+
+      if (gsi_end_p (si1) || gsi_end_p (si2))
+	return return_false();
+
+      phi1 = si1.phi ();
+      phi2 = si2.phi ();
+
+      tree phi_result1 = gimple_phi_result (phi1);
+      tree phi_result2 = gimple_phi_result (phi2);
+
+      if (!m_checker->compare_operand (phi_result1, phi_result2))
+	return return_false_with_msg ("PHI results are different");
+
+      size1 = gimple_phi_num_args (phi1);
+      size2 = gimple_phi_num_args (phi2);
+
+      if (size1 != size2)
+	return return_false ();
+
+      for (i = 0; i < size1; ++i)
+	{
+	  t1 = gimple_phi_arg (phi1, i)->def;
+	  t2 = gimple_phi_arg (phi2, i)->def;
+
+	  if (!m_checker->compare_operand (t1, t2))
+	    return return_false ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!m_checker->compare_edge (e1, e2))
+	    return return_false ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+/* Returns true if tree T can be compared as a handled component.  */
+
+bool
+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 == OBJ_TYPE_REF);
+}
+
+/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
+   corresponds to TARGET.  */
+
+bool
+sem_function::bb_dict_test (vec<int> *bb_dict, int source, int target)
+{
+  source++;
+  target++;
+
+  if (bb_dict->length () <= (unsigned)source)
+    bb_dict->safe_grow_cleared (source + 1);
+
+  if ((*bb_dict)[source] == 0)
+    {
+      (*bb_dict)[source] = target;
+      return true;
+    }
+  else
+    return (*bb_dict)[source] == target;
+}
+
+sem_variable::sem_variable (bitmap_obstack *stack): sem_item (VAR, 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 ());
+}
+
+/* Fast equality function based on knowledge known in WPA.  */
+
+bool
+sem_variable::equals_wpa (sem_item *item,
+			  hash_map <symtab_node *, sem_item *> &ignored_nodes)
+{
+  gcc_assert (item->type == VAR);
+
+  if (node->num_references () != item->node->num_references ())
+    return return_false_with_msg ("different number of references");
+
+  if (DECL_TLS_MODEL (decl) || DECL_TLS_MODEL (item->decl))
+    return return_false_with_msg ("TLS model");
+
+  /* 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");
+
+  if (DECL_SIZE (decl) != DECL_SIZE (item->decl)
+      && ((!DECL_SIZE (decl) || !DECL_SIZE (item->decl))
+	  || !operand_equal_p (DECL_SIZE (decl),
+			       DECL_SIZE (item->decl), OEP_ONLY_CONST)))
+    return return_false_with_msg ("size mismatch");
+
+  /* Do not attempt to mix data from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (decl) && !node->implicit_section)
+       || (DECL_SECTION_NAME (item->decl) && !item->node->implicit_section))
+      && DECL_SECTION_NAME (decl) != DECL_SECTION_NAME (item->decl))
+    return return_false_with_msg ("user section mismatch");
+
+  if (DECL_IN_TEXT_SECTION (decl) != DECL_IN_TEXT_SECTION (item->decl))
+    return return_false_with_msg ("text section");
+
+  ipa_ref *ref = NULL, *ref2 = NULL;
+  for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+    {
+      item->node->iterate_reference (i, ref2);
+
+      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;
+    }
+
+  return true;
+}
+
+/* Returns true if the item equals to ITEM given as argument.  */
+
+bool
+sem_variable::equals (sem_item *item,
+		      hash_map <symtab_node *, sem_item *> &)
+{
+  gcc_assert (item->type == VAR);
+  bool ret;
+
+  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 ();
+
+  /* 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 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.  */
+
+bool
+sem_variable::equals (tree t1, tree t2)
+{
+  if (!t1 || !t2)
+    return return_with_debug (t1 == t2);
+  if (t1 == t2)
+    return true;
+  tree_code tc1 = TREE_CODE (t1);
+  tree_code tc2 = TREE_CODE (t2);
+
+  if (tc1 != tc2)
+    return return_false_with_msg ("TREE_CODE mismatch");
+
+  switch (tc1)
+    {
+    case CONSTRUCTOR:
+      {
+	vec<constructor_elt, va_gc> *v1, *v2;
+	unsigned HOST_WIDE_INT idx;
+
+	enum tree_code typecode = TREE_CODE (TREE_TYPE (t1));
+	if (typecode != TREE_CODE (TREE_TYPE (t2)))
+	  return return_false_with_msg ("constructor type mismatch");
+
+	if (typecode == ARRAY_TYPE)
+	  {
+	    HOST_WIDE_INT size_1 = int_size_in_bytes (TREE_TYPE (t1));
+	    /* For arrays, check that the sizes all match.  */
+	    if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2))
+		|| size_1 == -1
+		|| size_1 != int_size_in_bytes (TREE_TYPE (t2)))
+	      return return_false_with_msg ("constructor array size mismatch");
+	  }
+	else if (!func_checker::compatible_types_p (TREE_TYPE (t1),
+						    TREE_TYPE (t2)))
+	  return return_false_with_msg ("constructor type incompatible");
+
+	v1 = CONSTRUCTOR_ELTS (t1);
+	v2 = CONSTRUCTOR_ELTS (t2);
+	if (vec_safe_length (v1) != vec_safe_length (v2))
+	  return return_false_with_msg ("constructor number of elts mismatch");
+
+	for (idx = 0; idx < vec_safe_length (v1); ++idx)
+	  {
+	    constructor_elt *c1 = &(*v1)[idx];
+	    constructor_elt *c2 = &(*v2)[idx];
+
+	    /* Check that each value is the same...  */
+	    if (!sem_variable::equals (c1->value, c2->value))
+	      return false;
+	    /* ... and that they apply to the same fields!  */
+	    if (!sem_variable::equals (c1->index, c2->index))
+	      return false;
+	  }
+	return true;
+      }
+    case MEM_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	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));
+      }
+    case ADDR_EXPR:
+    case FDESC_EXPR:
+      {
+	tree op1 = TREE_OPERAND (t1, 0);
+	tree op2 = TREE_OPERAND (t2, 0);
+	return sem_variable::equals (op1, op2);
+      }
+    /* References to other vars/decls are compared using ipa-ref.  */
+    case FUNCTION_DECL:
+    case VAR_DECL:
+      if (decl_in_symtab_p (t1) && decl_in_symtab_p (t2))
+	return true;
+      return return_false_with_msg ("Declaration mismatch");
+    case CONST_DECL:
+      /* TODO: We can check CONST_DECL by its DECL_INITIAL, but for that we
+	 need to process its VAR/FUNCTION references without relying on ipa-ref
+	 compare.  */
+    case FIELD_DECL:
+    case LABEL_DECL:
+      return return_false_with_msg ("Declaration mismatch");
+    case INTEGER_CST:
+      /* Integer 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 ("INTEGER_CST precision mismatch");
+      if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)))
+        return return_false_with_msg ("INTEGER_CST mode mismatch");
+      return return_with_debug (tree_int_cst_equal (t1, t2));
+    case STRING_CST:
+      if (TYPE_MODE (TREE_TYPE (t1)) != TYPE_MODE (TREE_TYPE (t2)))
+        return return_false_with_msg ("STRING_CST mode mismatch");
+      if (TREE_STRING_LENGTH (t1) != TREE_STRING_LENGTH (t2))
+	return return_false_with_msg ("STRING_CST length mismatch");
+      if (memcmp (TREE_STRING_POINTER (t1), TREE_STRING_POINTER (t2),
+		    TREE_STRING_LENGTH (t1)))
+	return return_false_with_msg ("STRING_CST mismatch");
+      return true;
+    case FIXED_CST:
+      /* Fixed 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 ("FIXED_CST precision mismatch");
+
+      return return_with_debug (FIXED_VALUES_IDENTICAL (TREE_FIXED_CST (t1),
+							TREE_FIXED_CST (t2)));
+    case COMPLEX_CST:
+      return (sem_variable::equals (TREE_REALPART (t1), TREE_REALPART (t2))
+	      && sem_variable::equals (TREE_IMAGPART (t1), TREE_IMAGPART (t2)));
+    case REAL_CST:
+      /* 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_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");
+
+	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;
+
+	return 1;
+      }
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	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)))
+	  return false;
+        if (!sem_variable::equals (array_ref_element_size (t1),
+			           array_ref_element_size (t2)))
+	  return false;
+	return true;
+      }
+     
+    case COMPONENT_REF:
+    case POINTER_PLUS_EXPR:
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case RANGE_EXPR:
+      {
+	tree x1 = TREE_OPERAND (t1, 0);
+	tree x2 = TREE_OPERAND (t2, 0);
+	tree y1 = TREE_OPERAND (t1, 1);
+	tree y2 = TREE_OPERAND (t2, 1);
+
+	return sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2);
+      }
+
+    CASE_CONVERT:
+    case VIEW_CONVERT_EXPR:
+      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:
+      return return_false_with_msg ("ERROR_MARK");
+    default:
+      return return_false_with_msg ("Unknown TREE code reached");
+    }
+}
+
+/* Parser function that visits a varpool NODE.  */
+
+sem_variable *
+sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
+{
+  if (TREE_THIS_VOLATILE (node->decl) || DECL_HARD_REGISTER (node->decl)
+      || node->alias)
+    return NULL;
+
+  sem_variable *v = new sem_variable (node, stack);
+
+  v->init ();
+
+  return v;
+}
+
+/* References independent hash function.  */
+
+hashval_t
+sem_variable::get_hash (void)
+{
+  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.
+     DECL_INITIAL (decl) would be error_mark_node in that case.  */
+  gcc_assert (!node->lto_file_data);
+  tree ctor = DECL_INITIAL (decl);
+  inchash::hash hstate;
+
+  hstate.add_int (456346417);
+  if (DECL_SIZE (decl) && tree_fits_shwi_p (DECL_SIZE (decl)))
+    hstate.add_hwi (tree_to_shwi (DECL_SIZE (decl)));
+  add_expr (ctor, hstate);
+  set_hash (hstate.end ());
+
+  return m_hash;
+}
+
+/* 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);
+    }
+}
+
+/* Merges instance with an ALIAS_ITEM, where alias, thunk or redirection can
+   be applied.  */
+
+bool
+sem_variable::merge (sem_item *alias_item)
+{
+  gcc_assert (alias_item->type == VAR);
+
+  if (!sem_item::target_supports_symbol_aliases_p ())
+    {
+      if (dump_file)
+	fprintf (dump_file, "Not unifying; "
+		 "Symbol aliases are not supported by target\n\n");
+      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 alias_address_matters = alias->address_matters_p ();
+
+  /* See if original is in a section that can be discarded if the main
+     symbol is not used.
+     Also consider case where we have resolution info and we know that
+     original's definition is not going to be used.  In this case we can not
+     create alias to original.  */
+  if (original->can_be_discarded_p ()
+      || (node->resolution != LDPR_UNKNOWN
+	  && !decl_binds_to_current_def_p (node->decl)))
+    original_discardable = true;
+
+  gcc_assert (!TREE_ASM_WRITTEN (alias->decl));
+
+  /* Constant pool machinery is not quite ready for aliases.
+     TODO: varasm code contains logic for merging DECL_IN_CONSTANT_POOL.
+     For LTO merging does not happen that is an important missing feature.
+     We can enable merging with LTO if the DECL_IN_CONSTANT_POOL
+     flag is dropped and non-local symbol name is assigned.  */
+  if (DECL_IN_CONSTANT_POOL (alias->decl)
+      || DECL_IN_CONSTANT_POOL (original->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; constant pool variables.\n\n");
+      return false;
+    }
+
+  /* Do not attempt to mix functions from different user sections;
+     we do not know what user intends with those.  */
+  if (((DECL_SECTION_NAME (original->decl) && !original->implicit_section)
+       || (DECL_SECTION_NAME (alias->decl) && !alias->implicit_section))
+      && DECL_SECTION_NAME (original->decl) != DECL_SECTION_NAME (alias->decl))
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "Not unifying; "
+		 "original and alias are in different sections.\n\n");
+      return false;
+    }
+
+  /* We can not merge if address comparsion metters.  */
+  if (alias_address_matters && flag_merge_constants < 2)
+    {
+      if (dump_file)
+	fprintf (dump_file,
+		 "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)
+	fprintf (dump_file, "Not unifying; alias cannot be created; "
+		 "across comdat group boundary\n\n");
+
+      return false;
+    }
+
+  if (original_discardable)
+    {
+      if (dump_file)
+	fprintf (dump_file, "Not unifying; alias cannot be created; "
+		 "target is discardable\n\n");
+
+      return false;
+    }
+  else
+    {
+      gcc_assert (!original->alias);
+      gcc_assert (!alias->alias);
+
+      alias->analyzed = false;
+
+      DECL_INITIAL (alias->decl) = NULL;
+      ((symtab_node *)alias)->call_for_symbol_and_aliases (clear_decl_rtl,
+							   NULL, true);
+      alias->need_bounds_init = false;
+      alias->remove_all_references ();
+      if (TREE_ADDRESSABLE (alias->decl))
+        original->call_for_symbol_and_aliases (set_addressable, NULL, true);
+
+      varpool_node::create_alias (alias_var->decl, decl);
+      alias->resolve_alias (original);
+
+      if (dump_file)
+	fprintf (dump_file, "Unified; Variable alias has been created.\n");
+
+      set_alias_uids (original, DECL_UID (original->decl));
+      return true;
+    }
+}
+
+/* Dump symbol to FILE.  */
+
+void
+sem_variable::dump_to_file (FILE *file)
+{
+  gcc_assert (file);
+
+  print_node (file, "", decl, 0);
+  fprintf (file, "\n\n");
+}
+
+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)
+{
+  m_items.create (0);
+  bitmap_obstack_initialize (&m_bmstack);
+}
+
+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_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      for (unsigned int i = 0; i < (*it)->classes.length (); i++)
+	delete (*it)->classes[i];
+
+      (*it)->classes.release ();
+      free (*it);
+    }
+
+  m_items.release ();
+
+  bitmap_obstack_release (&m_bmstack);
+}
+
+/* Write IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::write_summary (void)
+{
+  unsigned int count = 0;
+
+  output_block *ob = create_output_block (LTO_section_ipa_icf);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  ob->symbol = NULL;
+
+  /* Calculate number of symbols to be serialized.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      if (m_symtab_node_map.get (node))
+	count++;
+    }
+
+  streamer_write_uhwi (ob, count);
+
+  /* Process all of the symbols.  */
+  for (lto_symtab_encoder_iterator lsei = lsei_start_in_partition (encoder);
+       !lsei_end_p (lsei);
+       lsei_next_in_partition (&lsei))
+    {
+      symtab_node *node = lsei_node (lsei);
+
+      sem_item **item = m_symtab_node_map.get (node);
+
+      if (item && *item)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  streamer_write_uhwi (ob, (*item)->get_hash ());
+	}
+    }
+
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+/* Reads a section from LTO stream file FILE_DATA. Input block for DATA
+   contains LEN bytes.  */
+
+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 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;
+  data_in *data_in;
+  unsigned int i;
+  unsigned int count;
+
+  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);
+
+  count = streamer_read_uhwi (&ib_main);
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int index;
+      symtab_node *node;
+      lto_symtab_encoder_t encoder;
+
+      index = streamer_read_uhwi (&ib_main);
+      encoder = file_data->symtab_node_encoder;
+      node = lto_symtab_encoder_deref (encoder, index);
+
+      hashval_t hash = streamer_read_uhwi (&ib_main);
+
+      gcc_assert (node->definition);
+
+      if (dump_file)
+	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);
+
+	  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);
+
+	  sem_variable *var = new sem_variable (vnode, &m_bmstack);
+	  var->set_hash (hash);
+	  m_items.safe_push (var);
+	}
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_icf, NULL, data,
+			 len);
+  lto_data_in_delete (data_in);
+}
+
+/* Read IPA ICF summary for symbols.  */
+
+void
+sem_item_optimizer::read_summary (void)
+{
+  lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_section_data (file_data,
+			 LTO_section_ipa_icf, NULL, &len);
+
+      if (data)
+	read_section (file_data, data, len);
+    }
+}
+
+/* Register callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::register_hooks (void)
+{
+  if (!m_cgraph_node_hooks)
+    m_cgraph_node_hooks = symtab->add_cgraph_removal_hook
+			  (&sem_item_optimizer::cgraph_removal_hook, this);
+
+  if (!m_varpool_node_hooks)
+    m_varpool_node_hooks = symtab->add_varpool_removal_hook
+			   (&sem_item_optimizer::varpool_removal_hook, this);
+}
+
+/* Unregister callgraph and varpool hooks.  */
+
+void
+sem_item_optimizer::unregister_hooks (void)
+{
+  if (m_cgraph_node_hooks)
+    symtab->remove_cgraph_removal_hook (m_cgraph_node_hooks);
+
+  if (m_varpool_node_hooks)
+    symtab->remove_varpool_removal_hook (m_varpool_node_hooks);
+}
+
+/* Adds a CLS to hashtable associated by hash value.  */
+
+void
+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);
+  group->classes.safe_push (cls);
+}
+
+/* Gets a congruence class group based on given HASH value and TYPE.  */
+
+congruence_class_group *
+sem_item_optimizer::get_group_by_hash (hashval_t hash, sem_item_type type)
+{
+  congruence_class_group *item = XNEW (congruence_class_group);
+  item->hash = hash;
+  item->type = type;
+
+  congruence_class_group **slot = m_classes.find_slot (item, INSERT);
+
+  if (*slot)
+    free (item);
+  else
+    {
+      item->classes.create (1);
+      *slot = item;
+    }
+
+  return *slot;
+}
+
+/* Callgraph removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::cgraph_removal_hook (cgraph_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Varpool removal hook called for a NODE with a custom DATA.  */
+
+void
+sem_item_optimizer::varpool_removal_hook (varpool_node *node, void *data)
+{
+  sem_item_optimizer *optimizer = (sem_item_optimizer *) data;
+  optimizer->remove_symtab_node (node);
+}
+
+/* Remove symtab NODE triggered by symtab removal hooks.  */
+
+void
+sem_item_optimizer::remove_symtab_node (symtab_node *node)
+{
+  gcc_assert (!m_classes.elements ());
+
+  m_removed_items_set.add (node);
+}
+
+void
+sem_item_optimizer::remove_item (sem_item *item)
+{
+  if (m_symtab_node_map.get (item->node))
+    m_symtab_node_map.remove (item->node);
+  delete item;
+}
+
+/* Removes all callgraph and varpool nodes that are marked by symtab
+   as deleted.  */
+
+void
+sem_item_optimizer::filter_removed_items (void)
+{
+  auto_vec <sem_item *> filtered;
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    {
+      sem_item *item = m_items[i];
+
+      if (m_removed_items_set.contains (item->node))
+        {
+	  remove_item (item);
+	  continue;
+        }
+
+      if (item->type == FUNC)
+        {
+	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
+
+	  if (in_lto_p && (cnode->alias || cnode->body_removed))
+	    remove_item (item);
+	  else
+	    filtered.safe_push (item);
+        }
+      else /* VAR.  */
+        {
+	  if (!flag_ipa_icf_variables)
+	    remove_item (item);
+	  else
+	    {
+	      /* Filter out non-readonly variables.  */
+	      tree decl = item->decl;
+	      if (TREE_READONLY (decl))
+		filtered.safe_push (item);
+	      else
+		remove_item (item);
+	    }
+        }
+    }
+
+  /* Clean-up of released semantic items.  */
+
+  m_items.release ();
+  for (unsigned int i = 0; i < filtered.length(); i++)
+    m_items.safe_push (filtered[i]);
+}
+
+/* Optimizer entry point which returns true in case it processes
+   a merge operation. True is returned if there's a merge operation
+   processed.  */
+
+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)
+    fprintf (dump_file, "Dump after hash based groups\n");
+  dump_cong_classes ();
+
+  for (unsigned int i = 0; i < m_items.length(); i++)
+    m_items[i]->init_wpa ();
+
+  subdivide_classes_by_equality (true);
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after WPA based types groups\n");
+
+  dump_cong_classes ();
+
+  process_cong_reduction ();
+  checking_verify_classes ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after callgraph-based congruence reduction\n");
+
+  dump_cong_classes ();
+
+  parse_nonsingleton_classes ();
+  subdivide_classes_by_equality ();
+
+  if (dump_file)
+    fprintf (dump_file, "Dump after full equality comparison of groups\n");
+
+  dump_cong_classes ();
+
+  unsigned int prev_class_count = m_classes_count;
+
+  process_cong_reduction ();
+  dump_cong_classes ();
+  checking_verify_classes ();
+  bool merged_p = merge_classes (prev_class_count);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab->dump (dump_file);
+
+  return merged_p;
+}
+
+/* Function responsible for visiting all potential functions and
+   read-only variables that can be merged.  */
+
+void
+sem_item_optimizer::parse_funcs_and_vars (void)
+{
+  cgraph_node *cnode;
+
+  if (flag_ipa_icf_functions)
+    FOR_EACH_DEFINED_FUNCTION (cnode)
+    {
+      sem_function *f = sem_function::parse (cnode, &m_bmstack);
+      if (f)
+	{
+	  m_items.safe_push (f);
+	  m_symtab_node_map.put (cnode, f);
+
+	  if (dump_file)
+	    fprintf (dump_file, "Parsed function:%s\n", f->node->asm_name ());
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    f->dump_to_file (dump_file);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "Not parsed function:%s\n", cnode->asm_name ());
+    }
+
+  varpool_node *vnode;
+
+  if (flag_ipa_icf_variables)
+    FOR_EACH_DEFINED_VARIABLE (vnode)
+    {
+      sem_variable *v = sem_variable::parse (vnode, &m_bmstack);
+
+      if (v)
+	{
+	  m_items.safe_push (v);
+	  m_symtab_node_map.put (vnode, v);
+	}
+    }
+}
+
+/* Makes pairing between a congruence class CLS and semantic ITEM.  */
+
+void
+sem_item_optimizer::add_item_to_class (congruence_class *cls, sem_item *item)
+{
+  item->index_in_class = cls->members.length ();
+  cls->members.safe_push (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
+sem_item_optimizer::build_hash_based_classes (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      sem_item *item = m_items[i];
+
+      congruence_class_group *group
+	= get_group_by_hash (item->get_hash (), item->type);
+
+      if (!group->classes.length ())
+	{
+	  m_classes_count++;
+	  group->classes.safe_push (new congruence_class (class_id++));
+	}
+
+      add_item_to_class (group->classes[0], item);
+    }
+}
+
+/* Build references according to call graph.  */
+
+void
+sem_item_optimizer::build_graph (void)
+{
+  for (unsigned i = 0; i < m_items.length (); i++)
+    {
+      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++)
+    {
+      sem_item *item = m_items[i];
+
+      if (item->type == FUNC)
+	{
+	  cgraph_node *cnode = dyn_cast <cgraph_node *> (item->node);
+
+	  cgraph_edge *e = cnode->callees;
+	  while (e)
+	    {
+	      sem_item **slot = m_symtab_node_map.get
+		(e->callee->ultimate_alias_target ());
+	      if (slot)
+		item->add_reference (*slot);
+
+	      e = e->next_callee;
+	    }
+	}
+
+      ipa_ref *ref = NULL;
+      for (unsigned i = 0; item->node->iterate_reference (i, ref); i++)
+	{
+	  sem_item **slot = m_symtab_node_map.get
+	    (ref->referred->ultimate_alias_target ());
+	  if (slot)
+	    item->add_reference (*slot);
+	}
+    }
+}
+
+/* Semantic items in classes having more than one element and initialized.
+   In case of WPA, we load function body.  */
+
+void
+sem_item_optimizer::parse_nonsingleton_classes (void)
+{
+  unsigned int init_called_count = 0;
+
+  for (unsigned i = 0; i < m_items.length (); i++)
+    if (m_items[i]->cls->members.length () > 1)
+      {
+	m_items[i]->init ();
+	init_called_count++;
+      }
+
+  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);
+}
+
+/* Equality function for semantic items is used to subdivide existing
+   classes. If IN_WPA, fast equality function is invoked.  */
+
+void
+sem_item_optimizer::subdivide_classes_by_equality (bool in_wpa)
+{
+  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];
+
+	  if (c->members.length() > 1)
+	    {
+	      auto_vec <sem_item *> new_vector;
+
+	      sem_item *first = c->members[0];
+	      new_vector.safe_push (first);
+
+	      unsigned class_split_first = (*it)->classes.length ();
+
+	      for (unsigned j = 1; j < c->members.length (); j++)
+		{
+		  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);
+
+		  if (equals)
+		    new_vector.safe_push (item);
+		  else
+		    {
+		      bool integrated = false;
+
+		      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);
+
+			  if (equals)
+			    {
+			      integrated = true;
+			      add_item_to_class ((*it)->classes[k], item);
+
+			      break;
+			    }
+			}
+
+		      if (!integrated)
+			{
+			  congruence_class *c
+			    = new congruence_class (class_id++);
+			  m_classes_count++;
+			  add_item_to_class (c, item);
+
+			  (*it)->classes.safe_push (c);
+			}
+		    }
+		}
+
+	      // We replace newly created new_vector for the class we've just
+	      // splitted.
+	      c->members.release ();
+	      c->members.create (new_vector.length ());
+
+	      for (unsigned int j = 0; j < new_vector.length (); j++)
+		add_item_to_class (c, new_vector[j]);
+	    }
+	}
+    }
+
+  checking_verify_classes ();
+}
+
+/* Subdivide classes by address references that members of the class
+   reference. Example can be a pair of functions that have an address
+   taken from a function. If these addresses are different the class
+   is split.  */
+
+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_hash>::iterator it = m_classes.begin ();
+       it != m_classes.end (); ++it)
+    {
+      unsigned int class_count = (*it)->classes.length ();
+      auto_vec<congruence_class *> new_classes;
+
+      for (unsigned i = 0; i < class_count; i++)
+	{
+	  congruence_class *c = (*it)->classes[i];
+
+	  if (c->members.length() > 1)
+	    {
+	      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);
+
+		  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 (split_map.elements () != 1)
+	        {
+		  bool first_class = true;
+
+		  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++);
+
+		      for (unsigned k = 0; k < (*it2).second.length (); k++)
+			add_item_to_class (new_cls, (*it2).second[k]);
+
+		      worklist_push (new_cls);
+		      newly_created_classes++;
+
+		      if (first_class)
+		        {
+			  (*it)->classes[i] = new_cls;
+			  first_class = false;
+			}
+		      else
+		        {
+		          new_classes.safe_push (new_cls);
+			  m_classes_count++;
+		        }
+		    }
+		}
+
+	      /* Release memory.  */
+	      for (subdivide_hash_map::iterator it2 = split_map.begin ();
+		   it2 != split_map.end (); ++it2)
+		{
+		  delete (*it2).first;
+		  (*it2).second.release ();
+		}
+	    }
+	  }
+
+	for (unsigned i = 0; i < new_classes.length (); i++)
+	  (*it)->classes.safe_push (new_classes[i]);
+    }
+
+  return newly_created_classes;
+}
+
+/* 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)
+{
+  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_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_assert (item);
+	      gcc_assert (item->cls == cls);
+
+	      for (unsigned k = 0; k < item->usages.length (); k++)
+		{
+		  sem_usage_pair *usage = item->usages[k];
+		  gcc_assert (usage->item->index_in_class
+			      < usage->item->cls->members.length ());
+		}
+	    }
+	}
+    }
+}
+
+/* Disposes split map traverse function. CLS_PTR is pointer to congruence
+   class, BSLOT is bitmap slot we want to release. DATA is mandatory,
+   but unused argument.  */
+
+bool
+sem_item_optimizer::release_split_map (congruence_class * const &,
+				       bitmap const &b, traverse_split_pair *)
+{
+  bitmap bmp = b;
+
+  BITMAP_FREE (bmp);
+
+  return true;
+}
+
+/* Process split operation for a class given as pointer CLS_PTR,
+   where bitmap B splits congruence class members. DATA is used
+   as argument of split pair.  */
+
+bool
+sem_item_optimizer::traverse_congruence_split (congruence_class * const &cls,
+					       bitmap const &b,
+					       traverse_split_pair *pair)
+{
+  sem_item_optimizer *optimizer = pair->optimizer;
+  const congruence_class *splitter_cls = pair->cls;
+
+  /* If counted bits are greater than zero and less than the number of members
+     a group will be splitted.  */
+  unsigned popcount = bitmap_count_bits (b);
+
+  if (popcount > 0 && popcount < cls->members.length ())
+    {
+      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++)
+	{
+	  int target = bitmap_bit_p (b, i);
+	  congruence_class *tc = newclasses[target];
+
+	  add_item_to_class (tc, cls->members[i]);
+	}
+
+      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;
+
+      /* Remove old class from worklist if presented.  */
+      bool in_worklist = cls->in_worklist;
+
+      if (in_worklist)
+	cls->in_worklist = false;
+
+      congruence_class_group g;
+      g.hash = cls->members[0]->get_hash ();
+      g.type = cls->members[0]->type;
+
+      congruence_class_group *slot = optimizer->m_classes.find (&g);
+
+      for (unsigned int i = 0; i < slot->classes.length (); i++)
+	if (slot->classes[i] == cls)
+	  {
+	    slot->classes.ordered_remove (i);
+	    break;
+	  }
+
+      /* New class will be inserted and integrated to work list.  */
+      for (unsigned int i = 0; i < 2; i++)
+	optimizer->add_class (newclasses[i]);
+
+      /* Two classes replace one, so that increment just by one.  */
+      optimizer->m_classes_count++;
+
+      /* If OLD class was presented in the worklist, we remove the class
+         and replace it will both newly created classes.  */
+      if (in_worklist)
+	for (unsigned int i = 0; i < 2; i++)
+	  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);
+	  optimizer->worklist_push (newclasses[smaller_index]);
+	}
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "  congruence class splitted:\n");
+	  cls->dump (dump_file, 4);
+
+	  fprintf (dump_file, "  newly created groups:\n");
+	  for (unsigned int i = 0; i < 2; i++)
+	    newclasses[i]->dump (dump_file, 4);
+	}
+
+      /* Release class if not presented in work list.  */
+      if (!in_worklist)
+	delete cls;
+    }
+
+
+  return true;
+}
+
+/* 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)
+{
+  hash_map <congruence_class *, bitmap> split_map;
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    {
+      sem_item *item = cls->members[i];
+
+      /* Iterate all usages that have INDEX as usage of the item.  */
+      for (unsigned int j = 0; j < item->usages.length (); j++)
+	{
+	  sem_usage_pair *usage = item->usages[j];
+
+	  if (usage->index != index)
+	    continue;
+
+	  bitmap *slot = split_map.get (usage->item->cls);
+	  bitmap b;
+
+	  if(!slot)
+	    {
+	      b = BITMAP_ALLOC (&m_bmstack);
+	      split_map.put (usage->item->cls, b);
+	    }
+	  else
+	    b = *slot;
+
+	  gcc_checking_assert (usage->item->cls);
+	  gcc_checking_assert (usage->item->index_in_class
+			       < usage->item->cls->members.length ());
+
+	  bitmap_set_bit (b, usage->item->index_in_class);
+	}
+    }
+
+  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);
+
+  /* Bitmap clean-up.  */
+  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
+   collection of classes. Bitmap stack BMSTACK is used for bitmap
+   allocation.  */
+
+void
+sem_item_optimizer::do_congruence_step (congruence_class *cls)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+
+  bitmap usage = BITMAP_ALLOC (&m_bmstack);
+
+  for (unsigned int i = 0; i < cls->members.length (); i++)
+    bitmap_ior_into (usage, cls->members[i]->usage_index_bitmap);
+
+  EXECUTE_IF_SET_IN_BITMAP (usage, 0, i, bi)
+  {
+    if (dump_file && (dump_flags & TDF_DETAILS))
+      fprintf (dump_file, "  processing congruence step for class: %u, "
+	       "index: %u\n", cls->id, i);
+
+    do_congruence_step_for_index (cls, i);
+
+    if (splitter_class_removed)
+      break;
+  }
+
+  BITMAP_FREE (usage);
+}
+
+/* Adds a newly created congruence class CLS to worklist.  */
+
+void
+sem_item_optimizer::worklist_push (congruence_class *cls)
+{
+  /* Return if the class CLS is already presented in work list.  */
+  if (cls->in_worklist)
+    return;
+
+  cls->in_worklist = true;
+  worklist.push_back (cls);
+}
+
+/* Pops a class from worklist. */
+
+congruence_class *
+sem_item_optimizer::worklist_pop (void)
+{
+  congruence_class *cls;
+
+  while (!worklist.empty ())
+    {
+      cls = worklist.front ();
+      worklist.pop_front ();
+      if (cls->in_worklist)
+	{
+	  cls->in_worklist = false;
+
+	  return cls;
+	}
+      else
+	{
+	  /* Work list item was already intended to be removed.
+	     The only reason for doing it is to split a class.
+	     Thus, the class CLS is deleted.  */
+	  delete cls;
+	}
+    }
+
+  return NULL;
+}
+
+/* Iterative congruence reduction function.  */
+
+void
+sem_item_optimizer::process_cong_reduction (void)
+{
+  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 ())
+	worklist_push ((*it)->classes[i]);
+
+  if (dump_file)
+    fprintf (dump_file, "Worklist has been filled with: %lu\n",
+	     (unsigned long) worklist.size ());
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Congruence class reduction\n");
+
+  congruence_class *cls;
+
+  /* Process complete congruence reduction.  */
+  while ((cls = worklist_pop ()) != NULL)
+    do_congruence_step (cls);
+
+  /* Subdivide newly created classes according to references.  */
+  unsigned new_classes = subdivide_classes_by_sensitive_refs ();
+
+  if (dump_file)
+    fprintf (dump_file, "Address reference subdivision created: %u "
+	     "new classes.\n", new_classes);
+}
+
+/* Debug function prints all informations about congruence classes.  */
+
+void
+sem_item_optimizer::dump_cong_classes (void)
+{
+  if (!dump_file)
+    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 ());
+
+  /* Histogram calculation.  */
+  unsigned int max_index = 0;
+  unsigned int* histogram = XCNEWVEC (unsigned int, m_items.length () + 1);
+
+  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 ();
+	histogram[c]++;
+
+	if (c > max_index)
+	  max_index = c;
+      }
+
+  fprintf (dump_file,
+	   "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])
+      fprintf (dump_file, "[%u]: %u classes\n", i, histogram[i]);
+
+  fprintf (dump_file, "\n\n");
+
+  if (dump_flags & TDF_DETAILS)
+  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 ());
+
+	for (unsigned i = 0; i < (*it)->classes.length (); i++)
+	  {
+	    (*it)->classes[i]->dump (dump_file, 4);
+
+	    if (i < (*it)->classes.length () - 1)
+	      fprintf (dump_file, " ");
+	  }
+      }
+
+  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. True is returned if there's a merge operation
+   processed. */
+
+bool
+sem_item_optimizer::merge_classes (unsigned int prev_class_count)
+{
+  unsigned int item_count = m_items.length ();
+  unsigned int class_count = m_classes_count;
+  unsigned int equal_items = item_count - class_count;
+
+  unsigned int non_singular_classes_count = 0;
+  unsigned int non_singular_classes_sum = 0;
+
+  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++)
+      {
+	congruence_class *c = (*it)->classes[i];
+	if (c->members.length () > 1)
+	  {
+	    non_singular_classes_count++;
+	    non_singular_classes_sum += c->members.length ();
+	  }
+      }
+
+  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);
+      fprintf (dump_file, "Congruent classes before: %u, after: %u\n",
+	       prev_class_count, class_count);
+      fprintf (dump_file, "Average class size before: %.2f, after: %.2f\n",
+	       prev_class_count ? 1.0f * item_count / prev_class_count : 0.0f,
+	       class_count ? 1.0f * item_count / class_count : 0.0f);
+      fprintf (dump_file, "Average non-singular class size: %.2f, count: %u\n",
+	       non_singular_classes_count ? 1.0f * non_singular_classes_sum /
+	       non_singular_classes_count : 0.0f,
+	       non_singular_classes_count);
+      fprintf (dump_file, "Equal symbols: %u\n", equal_items);
+      fprintf (dump_file, "Fraction of visited symbols: %.2f%%\n\n",
+	       item_count ? 100.0f * equal_items / item_count : 0.0f);
+    }
+
+  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];
+
+	if (c->members.length () == 1)
+	  continue;
+
+	sem_item *source = c->members[0];
+
+	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",
+			 xstrdup_for_dump (source->node->name ()),
+			 xstrdup_for_dump (alias->node->name ()));
+		fprintf (dump_file, "Assembler symbol names:%s->%s\n",
+			 xstrdup_for_dump (source->node->asm_name ()),
+			 xstrdup_for_dump (alias->node->asm_name ()));
+	      }
+
+	    if (lookup_attribute ("no_icf", DECL_ATTRIBUTES (alias->decl)))
+	      {
+	        if (dump_file)
+		  fprintf (dump_file,
+			   "Merge operation is skipped due to no_icf "
+			   "attribute.\n\n");
+
+		continue;
+	      }
+
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      {
+		source->dump_to_file (dump_file);
+		alias->dump_to_file (dump_file);
+	      }
+
+	    if (dbg_cnt (merged_ipa_icf))
+	      merged_p |= source->merge (alias);
+	  }
+      }
+
+  return merged_p;
+}
+
+/* Dump function prints all class members to a FILE with an INDENT.  */
+
+void
+congruence_class::dump (FILE *file, unsigned int indent) const
+{
+  FPRINTF_SPACES (file, indent, "class with id: %u, hash: %u, items: %u\n",
+		  id, members[0]->get_hash (), members.length ());
+
+  FPUTS_SPACES (file, indent + 2, "");
+  for (unsigned i = 0; i < members.length (); i++)
+    fprintf (file, "%s ", members[i]->node->dump_asm_name ());
+
+  fprintf (file, "\n");
+}
+
+/* Returns true if there's a member that is used from another group.  */
+
+bool
+congruence_class::is_class_used (void)
+{
+  for (unsigned int i = 0; i < members.length (); i++)
+    if (members[i]->usages.length ())
+      return true;
+
+  return false;
+}
+
+/* Generate pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_generate_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->register_hooks ();
+  optimizer->parse_funcs_and_vars ();
+}
+
+/* Write pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_write_summary (void)
+{
+  gcc_assert (optimizer);
+
+  optimizer->write_summary ();
+}
+
+/* Read pass summary for IPA ICF pass.  */
+
+static void
+ipa_icf_read_summary (void)
+{
+  if (!optimizer)
+    optimizer = new sem_item_optimizer ();
+
+  optimizer->read_summary ();
+  optimizer->register_hooks ();
+}
+
+/* Semantic equality exection function.  */
+
+static unsigned int
+ipa_icf_driver (void)
+{
+  gcc_assert (optimizer);
+
+  bool merged_p = optimizer->execute ();
+
+  delete optimizer;
+  optimizer = NULL;
+
+  return merged_p ? TODO_remove_functions : 0;
+}
+
+const pass_data pass_data_ipa_icf =
+{
+  IPA_PASS,		    /* type */
+  "icf",		    /* name */
+  OPTGROUP_IPA,             /* optinfo_flags */
+  TV_IPA_ICF,		    /* tv_id */
+  0,                        /* properties_required */
+  0,                        /* properties_provided */
+  0,                        /* properties_destroyed */
+  0,                        /* todo_flags_start */
+  0,                        /* todo_flags_finish */
+};
+
+class pass_ipa_icf : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_icf (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_icf, ctxt,
+		      ipa_icf_generate_summary, /* generate_summary */
+		      ipa_icf_write_summary, /* write_summary */
+		      ipa_icf_read_summary, /* read_summary */
+		      NULL, /*
+		      write_optimization_summary */
+		      NULL, /*
+		      read_optimization_summary */
+		      NULL, /* stmt_fixup */
+		      0, /* function_transform_todo_flags_start */
+		      NULL, /* function_transform */
+		      NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+  {
+    return in_lto_p || flag_ipa_icf_variables || flag_ipa_icf_functions;
+  }
+
+  virtual unsigned int execute (function *)
+  {
+    return ipa_icf_driver();
+  }
+}; // class pass_ipa_icf
+
+} // ipa_icf namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_icf (gcc::context *ctxt)
+{
+  return new ipa_icf::pass_ipa_icf (ctxt);
+}