diff mbox

[1/3] tree-ssa-tail-merge: add IPA ICF infrastructure.

Message ID e5926353149121a294214d67494d6fe437eb3348.1436450591.git.mliska@suse.cz
State New
Headers show

Commit Message

Martin Liška July 9, 2015, 1:56 p.m. UTC
gcc/ChangeLog:

2015-07-09  Martin Liska  <mliska@suse.cz>

	* dbgcnt.def: Add new debug counter.
	* ipa-icf-gimple.c (func_checker::compare_ssa_name): Add flag
	for strict mode.
	(func_checker::compare_memory_operand): Likewise.
	(func_checker::compare_cst_or_decl): Handle if we are in
	tail_merge_mode.
	(func_checker::compare_operand): Pass strict flag properly.
	(func_checker::stmt_local_def): New function.
	(func_checker::compare_phi_node): Move from sem_function class.
	(func_checker::compare_bb_tail_merge): New function.
	(func_checker::compare_bb): Improve STMT iteration.
	(func_checker::compare_gimple_call): Pass strict flag.
	(func_checker::compare_gimple_assign): Likewise.
	(func_checker::compare_gimple_label): Remove unused flag.
	(ssa_names_set): New class.
	(ssa_names_set::build): New function.
	* ipa-icf-gimple.h (func_checker::gsi_next_nonlocal): New
	function.
	(ssa_names_set::contains): New function.
	(ssa_names_set::add): Likewise.
	* ipa-icf.c (sem_function::equals_private): Use transformed
	function.
	(sem_function::compare_phi_node): Move to func_checker class.
	* ipa-icf.h: Add new declarations.
	* tree-ssa-tail-merge.c (check_edges_correspondence): New
	function.
	(find_duplicate): Add usage of IPA ICF gimple infrastructure.
	(find_clusters_1): Pass new sem_function argument.
	(find_clusters): Likewise.
	(tail_merge_optimize): Call IPA ICF comparison machinery.
---
 gcc/dbgcnt.def            |   1 +
 gcc/ipa-icf-gimple.c      | 272 ++++++++++++++++++++++++++++++++++++++++++----
 gcc/ipa-icf-gimple.h      |  78 +++++++++++--
 gcc/ipa-icf.c             |  67 +-----------
 gcc/ipa-icf.h             |  14 ++-
 gcc/tree-ssa-tail-merge.c |  87 +++++++++++++--
 6 files changed, 407 insertions(+), 112 deletions(-)

Comments

Jeff Law July 9, 2015, 4:24 p.m. UTC | #1
On 07/09/2015 07:56 AM, mliska wrote:
> gcc/ChangeLog:
>
> 2015-07-09  Martin Liska  <mliska@suse.cz>
>
> 	* dbgcnt.def: Add new debug counter.
> 	* ipa-icf-gimple.c (func_checker::compare_ssa_name): Add flag
> 	for strict mode.
> 	(func_checker::compare_memory_operand): Likewise.
> 	(func_checker::compare_cst_or_decl): Handle if we are in
> 	tail_merge_mode.
> 	(func_checker::compare_operand): Pass strict flag properly.
> 	(func_checker::stmt_local_def): New function.
> 	(func_checker::compare_phi_node): Move from sem_function class.
> 	(func_checker::compare_bb_tail_merge): New function.
> 	(func_checker::compare_bb): Improve STMT iteration.
> 	(func_checker::compare_gimple_call): Pass strict flag.
> 	(func_checker::compare_gimple_assign): Likewise.
> 	(func_checker::compare_gimple_label): Remove unused flag.
> 	(ssa_names_set): New class.
> 	(ssa_names_set::build): New function.
> 	* ipa-icf-gimple.h (func_checker::gsi_next_nonlocal): New
> 	function.
> 	(ssa_names_set::contains): New function.
> 	(ssa_names_set::add): Likewise.
> 	* ipa-icf.c (sem_function::equals_private): Use transformed
> 	function.
> 	(sem_function::compare_phi_node): Move to func_checker class.
> 	* ipa-icf.h: Add new declarations.
> 	* tree-ssa-tail-merge.c (check_edges_correspondence): New
> 	function.
> 	(find_duplicate): Add usage of IPA ICF gimple infrastructure.
> 	(find_clusters_1): Pass new sem_function argument.
> 	(find_clusters): Likewise.
> 	(tail_merge_optimize): Call IPA ICF comparison machinery.
So a general question.  We're passing in STRICT to several routines, 
which is fine.  But then we're also checking M_TAIL_MERGE_MODE.  What's 
the difference between the two?  Can they be unified?


>
> -/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
> +/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.
> +   If STRICT flag is true, versions must match strictly.  */
>
>   bool
> -func_checker::compare_ssa_name (tree t1, tree t2)
> +func_checker::compare_ssa_name (tree t1, tree t2, bool strict)
This (and other) functions would seem to be used more than just ICF at 
this point.  A pass over the comments to update them as appropriate 
would be appreciated.

> @@ -626,6 +648,136 @@ func_checker::parse_labels (sem_bb *bb)
>       }
>   }
>
> +/* Return true if gimple STMT is just a local difinition in a
> +   basic block.  Used SSA names are contained in SSA_NAMES_SET.  */
s/difinition/definition/

I didn't find this comment particularly useful in understanding what 
this function does.  AFAICT the function looks as the uses of the LHS of 
STMT and verifies they're all in the same block as STMT, right?

It also verifies that the none of the operands within STMT are part of 
SSA_NAMES_SET.

What role do those properties play in the meaning of "local definition"?




> @@ -1037,4 +1205,60 @@ func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2)
>     return true;
>   }
>
> +void
> +ssa_names_set::build (basic_block bb)
Needs a function comment.  What are the "important" names we're 
collecting here?

Is a single forward and backward pass really sufficient to find all the 
important names?

In the backward pass, do you have to consider things like ASMs?  I guess 
it's difficult to understand what you need to look at because it's not 
entirely clear the set of SSA_NAMEs you're building.



> @@ -149,12 +153,20 @@ public:
>        mapping between basic blocks and labels.  */
>     void parse_labels (sem_bb *bb);
>
> +  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
> +     true value is returned if phi nodes are semantically
> +     equivalent in these blocks.  */
> +  bool compare_phi_node (sem_bb *sem_bb1, sem_bb *sem_bb2);
Presumably in the case of tail merging, FUNC1 and FUNC will be the same :-)


>     /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
> -  bool compare_ssa_name (tree t1, tree t2);
> +  bool compare_ssa_name (tree t1, tree t2, bool strict = true);
>
>     /* Verification function for edges E1 and E2.  */
>     bool compare_edge (edge e1, edge e2);
> @@ -204,7 +216,7 @@ public:
>     bool compare_tree_ssa_label (tree t1, tree t2);
>
>     /* Function compare for equality given memory operands T1 and T2.  */
> -  bool compare_memory_operand (tree t1, tree t2);
> +  bool compare_memory_operand (tree t1, tree t2, bool strict = true);
>
>     /* Function compare for equality given trees T1 and T2 which
>        can be either a constant or a declaration type.  */
> @@ -213,7 +225,7 @@ public:
>     /* Function responsible for comparison of various operands T1 and T2.
>        If these components, from functions FUNC1 and FUNC2, are equal, true
>        is returned.  */
> -  bool compare_operand (tree t1, tree t2);
> +  bool compare_operand (tree t1, tree t2, bool strict = false);
Is the default value for the parameter really needed in these methods? 
Why not go ahead and update the callers, I don't think we have that many 
right now.

>
>     /* Compares two tree list operands T1 and T2 and returns true if these
>        two trees are semantically equivalent.  */
> @@ -237,6 +249,13 @@ public:
>        first parameter of a function.  */
>     static bool compatible_types_p (tree t1, tree t2);
>
> +  /* Return true if gimple STMT is just a local difinition in a
> +     basic block.  Used SSA names are contained in SSA_NAMES_SET.  */
> +  static bool stmt_local_def (gimple stmt, ssa_names_set *ssa_names_set);
s/difinition/definition/
As with the implementation, I think the comment needs some clarification.

>  +/* SSA NAMES set.  */
> +class ssa_names_set
So what names are in this set?  Ie, what is the ::build method searching 
for?


> diff --git a/gcc/tree-ssa-tail-merge.c b/gcc/tree-ssa-tail-merge.c
> index 018a966..b8632d7 100644
> --- a/gcc/tree-ssa-tail-merge.c
> +++ b/gcc/tree-ssa-tail-merge.c
> @@ -187,6 +187,7 @@ along with GCC; see the file COPYING3.  If not see
>
>   #include "config.h"
>   #include "system.h"
> +#include <list>
>   #include "coretypes.h"
>   #include "backend.h"
>   #include "tree.h"
I think Andrew has defined an ordering for the initial include files. 
I think you've #included <list> in the wrong place.  Can it be moved down?

> +
> +using namespace ipa_icf;
> +using namespace ipa_icf_gimple;
Is this wise?  Does it significantly help with conciseness within the 
tail merging pass where it wants things ipa-icf and ipa-icf-gimple?

I'm not objecting at this point, I want to hear your thoughts.


>
>   /* Describes a group of bbs with the same successors.  The successor bbs are
>      cached in succs, and the successor edge flags are cached in succ_flags.
> @@ -1220,17 +1231,48 @@ gsi_advance_bw_nondebug_nonlocal (gimple_stmt_iterator *gsi, tree *vuse,
>       }
>   }
>
> +static bool
> +check_edges_correspondence (basic_block bb1, basic_block bb2)
Needs a function comment.


> +{
> +  edge e1, e2;
> +  edge_iterator ei2 = ei_start (bb2->succs);
> +
> +  for (edge_iterator ei1 = ei_start (bb1->succs); ei_cond (ei1, &e1);
> +       ei_next (&ei1))
> +    {
> +      ei_cond (ei2, &e2);
> +
> +      if (e1->dest->index != e2->dest->index ||
> +	  (e1->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
> +	  != (e2->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
> +	return false;
Formatting looks wrong in that conditional.

> @@ -1265,11 +1307,29 @@ find_duplicate (same_succ same_succ, basic_block bb1, basic_block bb2)
>     if (vuse_escaped && vuse1 != vuse2)
>       return;
>
> -  if (dump_file)
> -    fprintf (dump_file, "find_duplicates: <bb %d> duplicate of <bb %d>\n",
> +  if (!icf_result && dump_file)
> +    fprintf (dump_file,
> +	     "missed merge optimization: <bb %d> duplicate of <bb %d>\n",
>   	     bb1->index, bb2->index);
So I realize this goes away in the #2 patch.  But I'm curious how often 
it triggered and if there were any interesting patterns when the old 
tail merging triggered by the version utilizing ICF didn't or vice-versa.

You mentioned posting some before/after results, but I haven't seen them 
yet.

I'd like to do another pass over these changes, so if you could repost 
after the cleanups noted above, it'd be appreciated.

Jeff
diff mbox

Patch

diff --git a/gcc/dbgcnt.def b/gcc/dbgcnt.def
index 95f6b06..79d99d9 100644
--- a/gcc/dbgcnt.def
+++ b/gcc/dbgcnt.def
@@ -187,6 +187,7 @@  DEBUG_COUNTER (sms_sched_loop)
 DEBUG_COUNTER (split_for_sched2)
 DEBUG_COUNTER (store_motion)
 DEBUG_COUNTER (tail_call)
+DEBUG_COUNTER (tail_merge)
 DEBUG_COUNTER (treepre_insert)
 DEBUG_COUNTER (tree_sra)
 DEBUG_COUNTER (vect_loop)
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
index e727693..df99c0d 100644
--- a/gcc/ipa-icf-gimple.c
+++ b/gcc/ipa-icf-gimple.c
@@ -54,6 +54,7 @@  along with GCC; see the file COPYING3.  If not see
 #include <list>
 #include "tree-eh.h"
 #include "builtins.h"
+#include "trans-mem.h"
 
 #include "ipa-icf-gimple.h"
 #include "ipa-icf.h"
@@ -69,14 +70,14 @@  namespace ipa_icf_gimple {
 
 func_checker::func_checker (tree source_func_decl, tree target_func_decl,
 			    bool compare_polymorphic,
-			    bool ignore_labels,
+			    bool tail_merge_mode,
 			    hash_set<symtab_node *> *ignored_source_nodes,
 			    hash_set<symtab_node *> *ignored_target_nodes)
   : m_source_func_decl (source_func_decl), m_target_func_decl (target_func_decl),
     m_ignored_source_nodes (ignored_source_nodes),
     m_ignored_target_nodes (ignored_target_nodes),
     m_compare_polymorphic (compare_polymorphic),
-    m_ignore_labels (ignore_labels)
+    m_tail_merge_mode (tail_merge_mode)
 {
   function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
   function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
@@ -102,10 +103,11 @@  func_checker::~func_checker ()
   m_target_ssa_names.release();
 }
 
-/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF.
+   If STRICT flag is true, versions must match strictly.  */
 
 bool
-func_checker::compare_ssa_name (tree t1, tree t2)
+func_checker::compare_ssa_name (tree t1, tree t2, bool strict)
 {
   gcc_assert (TREE_CODE (t1) == SSA_NAME);
   gcc_assert (TREE_CODE (t2) == SSA_NAME);
@@ -113,6 +115,10 @@  func_checker::compare_ssa_name (tree t1, tree t2)
   unsigned i1 = SSA_NAME_VERSION (t1);
   unsigned i2 = SSA_NAME_VERSION (t2);
 
+  if (strict && m_tail_merge_mode)
+    return t1 == t2 ||
+      (m_source_ssa_names[i1] != -1 && m_source_ssa_names[i1] == (int) i2);
+
   if (m_source_ssa_names[i1] == -1)
     m_source_ssa_names[i1] = i2;
   else if (m_source_ssa_names[i1] != (int) i2)
@@ -256,10 +262,11 @@  func_checker::compatible_types_p (tree t1, tree t2)
   return true;
 }
 
-/* Function compare for equality given memory operands T1 and T2.  */
+/* Function compare for equality given memory operands T1 and T2.
+   If STRICT flag is true, versions must match strictly.  */
 
 bool
-func_checker::compare_memory_operand (tree t1, tree t2)
+func_checker::compare_memory_operand (tree t1, tree t2, bool strict)
 {
   if (!t1 && !t2)
     return true;
@@ -327,7 +334,7 @@  func_checker::compare_memory_operand (tree t1, tree t2)
 	return return_false_with_msg ("different dependence info");
     }
 
-  return compare_operand (t1, t2);
+  return compare_operand (t1, t2, strict);
 }
 
 /* Function compare for equality given trees T1 and T2 which
@@ -351,11 +358,18 @@  func_checker::compare_cst_or_decl (tree t1, tree t2)
 	return return_with_debug (ret);
       }
     case FUNCTION_DECL:
-      /* All function decls are in the symbol table and known to match
+      /* If we are called within an invocation of IPA ICF,
+	 all function decls are in the symbol table and known to match
 	 before we start comparing bodies.  */
-      return true;
+      return m_tail_merge_mode ? t1 == t2 : true;
     case VAR_DECL:
-      return return_with_debug (compare_variable_decl (t1, t2));
+      {
+	  /* Be strict in case of tail-merge optimization.  */
+	  if (m_tail_merge_mode)
+	    return t1 == t2;
+
+	  return return_with_debug (compare_variable_decl (t1, t2));
+	}
     case FIELD_DECL:
       {
 	tree offset1 = DECL_FIELD_OFFSET (t1);
@@ -371,6 +385,10 @@  func_checker::compare_cst_or_decl (tree t1, tree t2)
       }
     case LABEL_DECL:
       {
+	/* Be strict in case of tail-merge optimization.  */
+	if (m_tail_merge_mode)
+	  return t1 == t2;
+
 	int *bb1 = m_label_bb_map.get (t1);
 	int *bb2 = m_label_bb_map.get (t2);
 
@@ -380,6 +398,9 @@  func_checker::compare_cst_or_decl (tree t1, tree t2)
     case RESULT_DECL:
     case CONST_DECL:
       {
+	if (m_tail_merge_mode)
+	  return t1 == t2;
+
 	ret = compare_decl (t1, t2);
 	return return_with_debug (ret);
       }
@@ -393,7 +414,7 @@  func_checker::compare_cst_or_decl (tree t1, tree t2)
    is returned.  */
 
 bool
-func_checker::compare_operand (tree t1, tree t2)
+func_checker::compare_operand (tree t1, tree t2, bool strict)
 {
   tree x1, x2, y1, y2, z1, z2;
   bool ret;
@@ -465,7 +486,7 @@  func_checker::compare_operand (tree t1, tree t2)
 	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
 	  return return_false ();
 
-	if (!compare_operand (x1, x2))
+	if (!compare_operand (x1, x2, strict))
 	  return return_false_with_msg ("");
 
 	/* Type of the offset on MEM_REF does not matter.  */
@@ -486,7 +507,8 @@  func_checker::compare_operand (tree t1, tree t2)
     /* Virtual table call.  */
     case OBJ_TYPE_REF:
       {
-	if (!compare_ssa_name (OBJ_TYPE_REF_EXPR (t1), OBJ_TYPE_REF_EXPR (t2)))
+	if (!compare_ssa_name (OBJ_TYPE_REF_EXPR (t1), OBJ_TYPE_REF_EXPR (t2),
+			       strict))
 	  return return_false ();
 	if (opt_for_fn (m_source_func_decl, flag_devirtualize)
 	    && virtual_method_call_p (t1))
@@ -530,7 +552,7 @@  func_checker::compare_operand (tree t1, tree t2)
 	return return_with_debug (ret);
       }
     case SSA_NAME:
-	return compare_ssa_name (t1, t2);
+	return compare_ssa_name (t1, t2, strict);
     case INTEGER_CST:
     case COMPLEX_CST:
     case VECTOR_CST:
@@ -626,6 +648,136 @@  func_checker::parse_labels (sem_bb *bb)
     }
 }
 
+/* Return true if gimple STMT is just a local difinition in a
+   basic block.  Used SSA names are contained in SSA_NAMES_SET.  */
+
+bool
+func_checker::stmt_local_def (gimple stmt, ssa_names_set *ssa_names_set)
+{
+  basic_block bb, def_bb;
+  imm_use_iterator iter;
+  use_operand_p use_p;
+  tree val;
+  def_operand_p def_p;
+
+  if (gimple_vdef (stmt) != NULL_TREE
+      || gimple_has_side_effects (stmt)
+      || gimple_could_trap_p_1 (stmt, false, false)
+      || gimple_vuse (stmt) != NULL_TREE)
+    return false;
+
+  def_p = SINGLE_SSA_DEF_OPERAND (stmt, SSA_OP_DEF);
+  if (def_p == NULL)
+    return false;
+
+  val = DEF_FROM_PTR (def_p);
+  if (val == NULL_TREE || TREE_CODE (val) != SSA_NAME)
+    return false;
+
+  def_bb = gimple_bb (stmt);
+
+  FOR_EACH_IMM_USE_FAST (use_p, iter, val)
+    {
+      if (is_gimple_debug (USE_STMT (use_p)))
+	continue;
+      bb = gimple_bb (USE_STMT (use_p));
+      if (bb == def_bb)
+	continue;
+
+      if (gimple_code (USE_STMT (use_p)) == GIMPLE_PHI
+	  && EDGE_PRED (bb, PHI_ARG_INDEX_FROM_USE (use_p))->src == def_bb)
+	continue;
+
+      return false;
+    }
+
+  for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
+    if (ssa_names_set && ssa_names_set->contains (gimple_op (stmt, i)))
+      return false;
+
+  return true;
+}
+
+/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+   return true if phi nodes are semantically equivalent in these blocks.  */
+
+bool
+func_checker::compare_phi_node (sem_bb *sem_bb1, sem_bb *sem_bb2)
+{
+  gphi_iterator si1, si2;
+  gphi *phi1, *phi2;
+  unsigned size1, size2, i;
+  tree t1, t2;
+  edge e1, e2;
+
+  basic_block bb1 = sem_bb1->bb;
+  basic_block bb2 = sem_bb2->bb;
+
+  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 (!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 (!compare_operand (t1, t2))
+	    return return_false ();
+
+	  e1 = gimple_phi_arg_edge (phi1, i);
+	  e2 = gimple_phi_arg_edge (phi2, i);
+
+	  if (!compare_edge (e1, e2))
+	    return return_false ();
+	}
+
+      gsi_next (&si2);
+    }
+
+  return true;
+}
+
+
+bool
+func_checker::compare_bb_tail_merge (sem_bb *bb1, sem_bb *bb2)
+{
+  if (!compare_bb (bb1, bb2))
+    return return_false_with_msg ("BB are different");
+
+  if (!compare_phi_node (bb1, bb2))
+    return return_false_with_msg ("PHI nodes are different");
+
+  return true;
+}
+
 /* Basic block equivalence comparison function that returns true if
    basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
 
@@ -642,20 +794,39 @@  func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
   gsi1 = gsi_start_bb_nondebug (bb1->bb);
   gsi2 = gsi_start_bb_nondebug (bb2->bb);
 
-  while (!gsi_end_p (gsi1))
+  ssa_names_set ssa_names_set1;
+  ssa_names_set ssa_names_set2;
+
+  ssa_names_set1.build (bb1->bb);
+  ssa_names_set2.build (bb2->bb);
+
+  while (true)
     {
-      if (gsi_end_p (gsi2))
-	return return_false ();
+      if (m_tail_merge_mode)
+	{
+	  gsi_next_nonlocal (&gsi1, &ssa_names_set1);
+	  gsi_next_nonlocal (&gsi2, &ssa_names_set2);
+	}
+
+      if (gsi_end_p (gsi1) || gsi_end_p (gsi2))
+	break;
 
       s1 = gsi_stmt (gsi1);
       s2 = gsi_stmt (gsi2);
 
+      /* What could be better than to this this here is to blacklist the bb
+	 containing the stmt, when encountering the stmt f.i. in
+	 same_succ_hash.  */
+      if (is_tm_ending (s1)
+	  || is_tm_ending (s2))
+	return return_false_with_msg ("TM endings are different");
+
       int eh1 = lookup_stmt_eh_lp_fn
 		(DECL_STRUCT_FUNCTION (m_source_func_decl), s1);
       int eh2 = lookup_stmt_eh_lp_fn
 		(DECL_STRUCT_FUNCTION (m_target_func_decl), s2);
 
-      if (eh1 != eh2)
+      if (eh1 != eh2 && !m_tail_merge_mode)
 	return return_false_with_msg ("EH regions are different");
 
       if (gimple_code (s1) != gimple_code (s2))
@@ -723,7 +894,7 @@  func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
       gsi_next_nondebug (&gsi2);
     }
 
-  if (!gsi_end_p (gsi2))
+  if (!gsi_end_p (gsi1) || !gsi_end_p (gsi2))
     return return_false ();
 
   return true;
@@ -789,7 +960,7 @@  func_checker::compare_gimple_call (gcall *s1, gcall *s2)
   t1 = gimple_get_lhs (s1);
   t2 = gimple_get_lhs (s2);
 
-  return compare_memory_operand (t1, t2);
+  return compare_memory_operand (t1, t2, false);
 }
 
 
@@ -820,7 +991,7 @@  func_checker::compare_gimple_assign (gimple s1, gimple s2)
       arg1 = gimple_op (s1, i);
       arg2 = gimple_op (s2, i);
 
-      if (!compare_memory_operand (arg1, arg2))
+      if (!compare_memory_operand (arg1, arg2, i != 0))
 	return return_false_with_msg ("memory operands are different");
     }
 
@@ -869,9 +1040,6 @@  func_checker::compare_tree_ssa_label (tree t1, tree t2)
 bool
 func_checker::compare_gimple_label (const glabel *g1, const glabel *g2)
 {
-  if (m_ignore_labels)
-    return true;
-
   tree t1 = gimple_label_label (g1);
   tree t2 = gimple_label_label (g2);
 
@@ -1037,4 +1205,60 @@  func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2)
   return true;
 }
 
+void
+ssa_names_set::build (basic_block bb)
+{
+  tree var;
+  ssa_op_iter iter;
+
+  /* Build default set of important SSA names.  */
+  gimple_stmt_iterator gsi = gsi_start_nondebug_bb (bb);
+
+  while (!gsi_end_p (gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+      if (!func_checker::stmt_local_def (stmt, NULL))
+	for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
+	  add (gimple_op (stmt, i));
+
+      gsi_next_nondebug (&gsi);
+    }
+
+  /* Process reverse run.  */
+  gsi = gsi_last_nondebug_bb (bb);
+
+  while (!gsi_end_p (gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+
+      switch (gimple_code (stmt))
+	{
+	  case GIMPLE_ASSIGN:
+	    {
+	      tree lhs = gimple_assign_lhs (stmt);
+	      if (contains (lhs))
+		{
+		  FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_USE)
+		    add (var);
+		}
+	      break;
+	    }
+	  case GIMPLE_COND:
+	  case GIMPLE_SWITCH:
+	  case GIMPLE_CALL:
+	  case GIMPLE_RETURN:
+	    {
+	      FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_USE)
+		add (var);
+
+	      break;
+	    }
+	  default:
+	    break;
+	}
+
+      gsi_prev_nondebug (&gsi);
+    }
+}
+
 } // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
index 6a9cbed..674e95d 100644
--- a/gcc/ipa-icf-gimple.h
+++ b/gcc/ipa-icf-gimple.h
@@ -112,7 +112,8 @@  namespace ipa_icf_gimple {
 class sem_bb
 {
 public:
-  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_):
+  sem_bb (basic_block bb_, unsigned nondbg_stmt_count_ = 0,
+	  unsigned edge_count_ = 0):
     bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {}
 
   /* Basic block the structure belongs to.  */
@@ -125,6 +126,9 @@  public:
   unsigned edge_count;
 };
 
+/* Forward declaration.  */
+class ssa_names_set;
+
 /* A class aggregating all connections and semantic equivalents
    for a given pair of semantic function candidates.  */
 class func_checker
@@ -138,7 +142,7 @@  public:
      of declarations that can be skipped.  */
   func_checker (tree source_func_decl, tree target_func_decl,
 		bool compare_polymorphic,
-		bool ignore_labels = false,
+		bool tail_merge_mode = false,
 		hash_set<symtab_node *> *ignored_source_nodes = NULL,
 		hash_set<symtab_node *> *ignored_target_nodes = NULL);
 
@@ -149,12 +153,20 @@  public:
      mapping between basic blocks and labels.  */
   void parse_labels (sem_bb *bb);
 
+  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
+     true value is returned if phi nodes are semantically
+     equivalent in these blocks.  */
+  bool compare_phi_node (sem_bb *sem_bb1, sem_bb *sem_bb2);
+
+  /* Run tail-merge comparison for basic blocks BB1 and BB2.  */
+  bool compare_bb_tail_merge (sem_bb *bb1, sem_bb *bb2);
+
   /* Basic block equivalence comparison function that returns true if
      basic blocks BB1 and BB2 correspond.  */
   bool compare_bb (sem_bb *bb1, sem_bb *bb2);
 
   /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
-  bool compare_ssa_name (tree t1, tree t2);
+  bool compare_ssa_name (tree t1, tree t2, bool strict = true);
 
   /* Verification function for edges E1 and E2.  */
   bool compare_edge (edge e1, edge e2);
@@ -204,7 +216,7 @@  public:
   bool compare_tree_ssa_label (tree t1, tree t2);
 
   /* Function compare for equality given memory operands T1 and T2.  */
-  bool compare_memory_operand (tree t1, tree t2);
+  bool compare_memory_operand (tree t1, tree t2, bool strict = true);
 
   /* Function compare for equality given trees T1 and T2 which
      can be either a constant or a declaration type.  */
@@ -213,7 +225,7 @@  public:
   /* Function responsible for comparison of various operands T1 and T2.
      If these components, from functions FUNC1 and FUNC2, are equal, true
      is returned.  */
-  bool compare_operand (tree t1, tree t2);
+  bool compare_operand (tree t1, tree t2, bool strict = false);
 
   /* Compares two tree list operands T1 and T2 and returns true if these
      two trees are semantically equivalent.  */
@@ -237,6 +249,13 @@  public:
      first parameter of a function.  */
   static bool compatible_types_p (tree t1, tree t2);
 
+  /* Return true if gimple STMT is just a local difinition in a
+     basic block.  Used SSA names are contained in SSA_NAMES_SET.  */
+  static bool stmt_local_def (gimple stmt, ssa_names_set *ssa_names_set);
+
+  /* Advance the iterator to the next non-local gimple statement.  */
+  static void gsi_next_nonlocal (gimple_stmt_iterator *i,
+			  ssa_names_set *ssa_names_set);
 
 private:
   /* Vector mapping source SSA names to target ones.  */
@@ -271,8 +290,53 @@  private:
   /* Flag if polymorphic comparison should be executed.  */
   bool m_compare_polymorphic;
 
-  /* Flag if ignore labels in comparison.  */
-  bool m_ignore_labels;
+  /* Flag which changes behavior for tree-ssa-tail-merge pass.  */
+  bool m_tail_merge_mode;
+};
+
+/* SSA NAMES set.  */
+class ssa_names_set
+{
+public:
+  /* Return true if SSA_NAME is in the set.  */
+  bool contains (tree ssa_name);
+
+  /* Add a new SSA_NAME to the set.  */
+  void add (tree ssa_name);
+
+  /* Build the set for given basic block BB.  */
+  void build (basic_block bb);
+
+private:
+  hash_set <tree> m_set;
 };
 
+inline void
+func_checker::gsi_next_nonlocal (gimple_stmt_iterator *i,
+				 ssa_names_set *ssa_names_set)
+{
+  while (!gsi_end_p (*i) &&
+	 (is_gimple_debug (gsi_stmt (*i))
+	  || func_checker::stmt_local_def (gsi_stmt (*i), ssa_names_set)))
+    gsi_next (i);
+}
+
+inline bool
+ssa_names_set::contains (tree ssa_name)
+{
+  if (ssa_name == NULL || TREE_CODE (ssa_name) != SSA_NAME)
+    return false;
+
+  return m_set.contains (ssa_name);
+}
+
+inline void
+ssa_names_set::add (tree ssa_name)
+{
+  if (ssa_name == NULL || TREE_CODE (ssa_name) != SSA_NAME)
+    return;
+
+  m_set.add (ssa_name);
+}
+
 } // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
index c4386c0..7e65bce 100644
--- a/gcc/ipa-icf.c
+++ b/gcc/ipa-icf.c
@@ -995,7 +995,8 @@  sem_function::equals_private (sem_item *item)
 
   /* 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))
+    if (!m_checker->compare_phi_node (bb_sorted[i],
+				      m_compared_func->bb_sorted[i]))
       return return_false_with_msg ("PHI node comparison returns false");
 
   return result;
@@ -1707,70 +1708,6 @@  sem_function::parse (cgraph_node *node, bitmap_obstack *stack)
   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
diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h
index 67d5bdc..6d67e52 100644
--- a/gcc/ipa-icf.h
+++ b/gcc/ipa-icf.h
@@ -355,15 +355,19 @@  public:
   /* Return true if parameter I may be used.  */
   bool param_used_p (unsigned int i);
 
+  /* Set a new function checker.  */
+  void set_checker (ipa_icf_gimple::func_checker *checker)
+  {
+    if (m_checker)
+      delete m_checker;
+
+    m_checker = checker;
+  }
+
 private:
   /* Calculates hash value based on a BASIC_BLOCK.  */
   hashval_t get_bb_hash (const ipa_icf_gimple::sem_bb *basic_block);
 
-  /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
-     true value is returned if phi nodes are semantically
-     equivalent in these blocks .  */
-  bool compare_phi_node (basic_block bb1, basic_block bb2);
-
   /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
      corresponds to TARGET.  */
   bool bb_dict_test (vec<int> *bb_dict, int source, int target);
diff --git a/gcc/tree-ssa-tail-merge.c b/gcc/tree-ssa-tail-merge.c
index 018a966..b8632d7 100644
--- a/gcc/tree-ssa-tail-merge.c
+++ b/gcc/tree-ssa-tail-merge.c
@@ -187,6 +187,7 @@  along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #include "system.h"
+#include <list>
 #include "coretypes.h"
 #include "backend.h"
 #include "tree.h"
@@ -197,6 +198,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "fold-const.h"
 #include "stor-layout.h"
 #include "trans-mem.h"
+#include "inchash.h"
 #include "tm_p.h"
 #include "cfganal.h"
 #include "cfgcleanup.h"
@@ -213,6 +215,15 @@  along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "tree-pass.h"
 #include "trans-mem.h"
+#include "ipa-ref.h"
+#include "lto-streamer.h"
+#include "cgraph.h"
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+#include "dbgcnt.h"
+
+using namespace ipa_icf;
+using namespace ipa_icf_gimple;
 
 /* Describes a group of bbs with the same successors.  The successor bbs are
    cached in succs, and the successor edge flags are cached in succ_flags.
@@ -1220,17 +1231,48 @@  gsi_advance_bw_nondebug_nonlocal (gimple_stmt_iterator *gsi, tree *vuse,
     }
 }
 
+static bool
+check_edges_correspondence (basic_block bb1, basic_block bb2)
+{
+  edge e1, e2;
+  edge_iterator ei2 = ei_start (bb2->succs);
+
+  for (edge_iterator ei1 = ei_start (bb1->succs); ei_cond (ei1, &e1);
+       ei_next (&ei1))
+    {
+      ei_cond (ei2, &e2);
+
+      if (e1->dest->index != e2->dest->index ||
+	  (e1->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
+	  != (e2->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
+	return false;
+
+      ei_next (&ei2);
+    }
+
+  return true;
+}
+
+
 /* Determines whether BB1 and BB2 (members of same_succ) are duplicates.  If so,
    clusters them.  */
 
 static void
-find_duplicate (same_succ same_succ, basic_block bb1, basic_block bb2)
+find_duplicate (same_succ same_succ, basic_block bb1, basic_block bb2,
+		sem_function &f)
 {
   gimple_stmt_iterator gsi1 = gsi_last_nondebug_bb (bb1);
   gimple_stmt_iterator gsi2 = gsi_last_nondebug_bb (bb2);
   tree vuse1 = NULL_TREE, vuse2 = NULL_TREE;
   bool vuse_escaped = false;
 
+  sem_bb sem_bb1 = sem_bb (bb1);
+  sem_bb sem_bb2 = sem_bb (bb2);
+
+  func_checker *checker = new func_checker (f.decl, f.decl, true, true);
+  f.set_checker (checker);
+  bool icf_result = checker->compare_bb_tail_merge (&sem_bb1, &sem_bb2);
+
   gsi_advance_bw_nondebug_nonlocal (&gsi1, &vuse1, &vuse_escaped);
   gsi_advance_bw_nondebug_nonlocal (&gsi2, &vuse2, &vuse_escaped);
 
@@ -1244,10 +1286,10 @@  find_duplicate (same_succ same_succ, basic_block bb1, basic_block bb2)
 	 same_succ_hash.  */
       if (is_tm_ending (stmt1)
 	  || is_tm_ending (stmt2))
-	return;
+	goto diff;
 
       if (!gimple_equal_p (same_succ, stmt1, stmt2))
-	return;
+	goto diff;
 
       gsi_prev_nondebug (&gsi1);
       gsi_prev_nondebug (&gsi2);
@@ -1265,11 +1307,29 @@  find_duplicate (same_succ same_succ, basic_block bb1, basic_block bb2)
   if (vuse_escaped && vuse1 != vuse2)
     return;
 
-  if (dump_file)
-    fprintf (dump_file, "find_duplicates: <bb %d> duplicate of <bb %d>\n",
+  if (!icf_result && dump_file)
+    fprintf (dump_file,
+	     "missed merge optimization: <bb %d> duplicate of <bb %d>\n",
 	     bb1->index, bb2->index);
 
-  set_cluster (bb1, bb2);
+  if (dbg_cnt (tail_merge))
+    set_cluster (bb1, bb2);
+
+  return;
+
+diff:
+  if (!check_edges_correspondence (bb1, bb2))
+    return;
+
+  if (icf_result)
+    {
+      if (dump_file)
+	fprintf (dump_file, "find_duplicates: <bb %d> duplicate of <bb %d>\n",
+		 bb1->index, bb2->index);
+
+      if (dbg_cnt (tail_merge))
+	set_cluster (bb1, bb2);
+    }
 }
 
 /* Returns whether for all phis in DEST the phi alternatives for E1 and
@@ -1391,7 +1451,7 @@  deps_ok_for_redirect (basic_block bb1, basic_block bb2)
 /* Within SAME_SUCC->bbs, find clusters of bbs which can be merged.  */
 
 static void
-find_clusters_1 (same_succ same_succ)
+find_clusters_1 (same_succ same_succ, sem_function &f)
 {
   basic_block bb1, bb2;
   unsigned int i, j;
@@ -1433,7 +1493,7 @@  find_clusters_1 (same_succ same_succ)
 	  if (!(same_phi_alternatives (same_succ, bb1, bb2)))
 	    continue;
 
-	  find_duplicate (same_succ, bb1, bb2);
+	  find_duplicate (same_succ, bb1, bb2, f);
 	}
     }
 }
@@ -1441,7 +1501,7 @@  find_clusters_1 (same_succ same_succ)
 /* Find clusters of bbs which can be merged.  */
 
 static void
-find_clusters (void)
+find_clusters (sem_function &f)
 {
   same_succ same;
 
@@ -1454,7 +1514,7 @@  find_clusters (void)
 	  fprintf (dump_file, "processing worklist entry\n");
 	  same_succ_print (dump_file, same);
 	}
-      find_clusters_1 (same);
+      find_clusters_1 (same, f);
     }
 }
 
@@ -1659,6 +1719,10 @@  tail_merge_optimize (unsigned int todo)
     }
   init_worklist ();
 
+  bitmap_obstack b;
+  bitmap_obstack_initialize (&b);
+  cgraph_node *node = cgraph_node::get (cfun->decl);
+
   while (!worklist.is_empty ())
     {
       if (!loop_entered)
@@ -1674,7 +1738,8 @@  tail_merge_optimize (unsigned int todo)
       if (dump_file && (dump_flags & TDF_DETAILS))
 	fprintf (dump_file, "worklist iteration #%d\n", iteration_nr);
 
-      find_clusters ();
+      sem_function f (node, 0, &b);
+      find_clusters (f);
       gcc_assert (worklist.is_empty ());
       if (all_clusters.is_empty ())
 	break;