Hi.

It's quite some time the discussion has started. Now is time for me to refresh 
IPA ICF
and I would like integrate operand_equal_p with what I currently have in ICF 
(::compare_operand).
I like the idea of a class that will provide both operand_equal_valueize and 
hash_operand_valueize.
These will be implemented in func_checker and can provide basically the same 
what Honza suggested.

Reading the thread, I noticed Richi would prefer to use something like:

template <bool with_valueize>
int
operand_equal_p_1 (const_tree arg0, const_tree arg1, unsigned int flags,
                   tree (*valueize)(tree))
{
#define VALUEIZE(op) (with_valueize && valueize) ? valueize (op) : op 
...
}

To be honest, it looks to me only as an optimization which will fold call
to valueize in current operand_equal_p.

I'm sending a slightly tested pair of patches which does the abstraction
factoring and ICF adaptation.

I would expect a feedback before I'll prepare a proper fix.
Thanks,
Martin
>From b864e44e14a86e9cc7ba494b7af687a0a3e74896 Mon Sep 17 00:00:00 2001
From: Martin Liska <mli...@suse.cz>
Date: Mon, 10 Jun 2019 14:34:15 +0200
Subject: [PATCH 2/2] Integrate that for IPA ICF.

---
 gcc/ipa-icf-gimple.c | 224 +++++++++++++------------------------------
 gcc/ipa-icf-gimple.h |   8 +-
 gcc/ipa-icf.c        |   7 +-
 3 files changed, 79 insertions(+), 160 deletions(-)

diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
index 0713e125898..6da3e19e317 100644
--- a/gcc/ipa-icf-gimple.c
+++ b/gcc/ipa-icf-gimple.c
@@ -324,6 +324,28 @@ func_checker::compare_memory_operand (tree t1, tree t2)
 /* Function compare for equality given trees T1 and T2 which
    can be either a constant or a declaration type.  */
 
+bool
+func_checker::hash_operand_valueize (const_tree arg, inchash::hash &hstate,
+				     unsigned int flags)
+{
+  switch (TREE_CODE (arg))
+    {
+    case FUNCTION_DECL:
+    case VAR_DECL:
+    case LABEL_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+    case SSA_NAME:
+      return true;
+
+    default:
+      break;
+    }
+
+  return false;
+}
+
 bool
 func_checker::compare_cst_or_decl (tree t1, tree t2)
 {
@@ -347,19 +369,6 @@ func_checker::compare_cst_or_decl (tree t1, tree t2)
       return true;
     case VAR_DECL:
       return return_with_debug (compare_variable_decl (t1, t2));
-    case FIELD_DECL:
-      {
-	tree offset1 = DECL_FIELD_OFFSET (t1);
-	tree offset2 = DECL_FIELD_OFFSET (t2);
-
-	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
-	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
-
-	ret = compare_operand (offset1, offset2)
-	      && compare_operand (bit_offset1, bit_offset2);
-
-	return return_with_debug (ret);
-      }
     case LABEL_DECL:
       {
 	if (t1 == t2)
@@ -383,165 +392,66 @@ func_checker::compare_cst_or_decl (tree t1, tree t2)
     }
 }
 
-/* Function responsible for comparison of various operands T1 and T2.
-   If these components, from functions FUNC1 and FUNC2, are equal, true
-   is returned.  */
-
-bool
-func_checker::compare_operand (tree t1, tree t2)
+int
+func_checker::operand_equal_valueize (const_tree ct1, const_tree ct2, unsigned int)
 {
-  tree x1, x2, y1, y2, z1, z2;
-  bool ret;
-
-  if (!t1 && !t2)
-    return true;
-  else if (!t1 || !t2)
-    return false;
-
-  tree tt1 = TREE_TYPE (t1);
-  tree tt2 = TREE_TYPE (t2);
-
-  if (!func_checker::compatible_types_p (tt1, tt2))
-    return false;
-
-  if (TREE_CODE (t1) != TREE_CODE (t2))
-    return return_false ();
+  tree t1 = const_cast <tree> (ct1);
+  tree t2 = const_cast <tree> (ct2);
 
   switch (TREE_CODE (t1))
     {
-    case CONSTRUCTOR:
+    case FUNCTION_DECL:
+      /* All function decls are in the symbol table and known to match
+	 before we start comparing bodies.  */
+      return true;
+    case VAR_DECL:
+      return return_with_debug (compare_variable_decl (t1, t2));
+    case LABEL_DECL:
       {
-	unsigned length1 = CONSTRUCTOR_NELTS (t1);
-	unsigned length2 = CONSTRUCTOR_NELTS (t2);
-
-	if (length1 != length2)
-	  return return_false ();
-
-	for (unsigned i = 0; i < length1; i++)
-	  if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
-				CONSTRUCTOR_ELT (t2, i)->value))
-	    return return_false();
-
-	return true;
+	int *bb1 = m_label_bb_map.get (t1);
+	int *bb2 = m_label_bb_map.get (t2);
+	return return_with_debug (*bb1 == *bb2);
       }
-    case ARRAY_REF:
-    case ARRAY_RANGE_REF:
-      /* First argument is the array, second is the index.  */
-      x1 = TREE_OPERAND (t1, 0);
-      x2 = TREE_OPERAND (t2, 0);
-      y1 = TREE_OPERAND (t1, 1);
-      y2 = TREE_OPERAND (t2, 1);
-
-      if (!compare_operand (array_ref_low_bound (t1),
-			    array_ref_low_bound (t2)))
-	return return_false_with_msg ("");
-      if (!compare_operand (array_ref_element_size (t1),
-			    array_ref_element_size (t2)))
-	return return_false_with_msg ("");
-
-      if (!compare_operand (x1, x2))
-	return return_false_with_msg ("");
-      return compare_operand (y1, y2);
-    case MEM_REF:
-      {
-	x1 = TREE_OPERAND (t1, 0);
-	x2 = TREE_OPERAND (t2, 0);
-	y1 = TREE_OPERAND (t1, 1);
-	y2 = TREE_OPERAND (t2, 1);
-
-	/* See if operand is an memory access (the test originate from
-	 gimple_load_p).
-
-	In this case the alias set of the function being replaced must
-	be subset of the alias set of the other function.  At the moment
-	we seek for equivalency classes, so simply require inclussion in
-	both directions.  */
-
-	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
-	  return return_false ();
 
-	if (!compare_operand (x1, x2))
-	  return return_false_with_msg ("");
-
-	/* Type of the offset on MEM_REF does not matter.  */
-	return known_eq (wi::to_poly_offset (y1), wi::to_poly_offset (y2));
-      }
-    case COMPONENT_REF:
+    case PARM_DECL:
+    case RESULT_DECL:
+    case CONST_DECL:
+      return compare_decl (t1, t2);
+    case SSA_NAME:
+      return compare_ssa_name (t1, t2);
+    case FIELD_DECL:
       {
-	x1 = TREE_OPERAND (t1, 0);
-	x2 = TREE_OPERAND (t2, 0);
-	y1 = TREE_OPERAND (t1, 1);
-	y2 = TREE_OPERAND (t2, 1);
+	tree offset1 = DECL_FIELD_OFFSET (t1);
+	tree offset2 = DECL_FIELD_OFFSET (t2);
 
-	ret = compare_operand (x1, x2)
-	      && compare_cst_or_decl (y1, y2);
+	tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
+	tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
 
+	bool ret = (compare_operand (offset1, offset2)
+		    && compare_operand (bit_offset1, bit_offset2));
 	return return_with_debug (ret);
       }
-    /* Virtual table call.  */
-    case OBJ_TYPE_REF:
-      {
-	if (!compare_ssa_name (OBJ_TYPE_REF_EXPR (t1), OBJ_TYPE_REF_EXPR (t2)))
-	  return return_false ();
-	if (opt_for_fn (m_source_func_decl, flag_devirtualize)
-	    && virtual_method_call_p (t1))
-	  {
-	    if (tree_to_uhwi (OBJ_TYPE_REF_TOKEN (t1))
-		!= tree_to_uhwi (OBJ_TYPE_REF_TOKEN (t2)))
-	      return return_false_with_msg ("OBJ_TYPE_REF token mismatch");
-	    if (!types_same_for_odr (obj_type_ref_class (t1),
-				     obj_type_ref_class (t2)))
-	      return return_false_with_msg ("OBJ_TYPE_REF OTR type mismatch");
-	    if (!compare_operand (OBJ_TYPE_REF_OBJECT (t1),
-				  OBJ_TYPE_REF_OBJECT (t2)))
-	      return return_false_with_msg ("OBJ_TYPE_REF object mismatch");
-	  }
-
-	return return_with_debug (true);
-      }
-    case IMAGPART_EXPR:
-    case REALPART_EXPR:
-    case ADDR_EXPR:
-      {
-	x1 = TREE_OPERAND (t1, 0);
-	x2 = TREE_OPERAND (t2, 0);
+    default:
+      break;
+    }
 
-	ret = compare_operand (x1, x2);
-	return return_with_debug (ret);
-      }
-    case BIT_FIELD_REF:
-      {
-	x1 = TREE_OPERAND (t1, 0);
-	x2 = TREE_OPERAND (t2, 0);
-	y1 = TREE_OPERAND (t1, 1);
-	y2 = TREE_OPERAND (t2, 1);
-	z1 = TREE_OPERAND (t1, 2);
-	z2 = TREE_OPERAND (t2, 2);
+  return -1;
+}
 
-	ret = compare_operand (x1, x2)
-	      && compare_cst_or_decl (y1, y2)
-	      && compare_cst_or_decl (z1, z2);
+/* Function responsible for comparison of various operands T1 and T2.
+   If these components, from functions FUNC1 and FUNC2, are equal, true
+   is returned.  */
 
-	return return_with_debug (ret);
-      }
-    case SSA_NAME:
-	return compare_ssa_name (t1, t2);
-    case INTEGER_CST:
-    case COMPLEX_CST:
-    case VECTOR_CST:
-    case STRING_CST:
-    case REAL_CST:
-    case FUNCTION_DECL:
-    case VAR_DECL:
-    case FIELD_DECL:
-    case LABEL_DECL:
-    case PARM_DECL:
-    case RESULT_DECL:
-    case CONST_DECL:
-      return compare_cst_or_decl (t1, t2);
-    default:
-      return return_false_with_msg ("Unknown TREE code reached");
-    }
+bool
+func_checker::compare_operand (tree t1, tree t2)
+{
+  if (!t1 && !t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+  if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
+    return true;
+  return return_false_with_msg ("operand_equal_p failed");
 }
 
 bool
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
index 351bddfb2f6..85f95ed62e4 100644
--- a/gcc/ipa-icf-gimple.h
+++ b/gcc/ipa-icf-gimple.h
@@ -118,7 +118,7 @@ public:
 
 /* A class aggregating all connections and semantic equivalents
    for a given pair of semantic function candidates.  */
-class func_checker
+class func_checker : operand_compare
 {
 public:
   /* Initialize internal structures for a given SOURCE_FUNC_DECL and
@@ -134,7 +134,7 @@ public:
 		hash_set<symtab_node *> *ignored_target_nodes = NULL);
 
   /* Memory release routine.  */
-  ~func_checker();
+  virtual ~func_checker();
 
   /* Function visits all gimple labels and creates corresponding
      mapping between basic blocks and labels.  */
@@ -267,6 +267,10 @@ private:
 
   /* Flag if ignore labels in comparison.  */
   bool m_ignore_labels;
+
+  virtual int operand_equal_valueize (const_tree, const_tree, unsigned int);
+  virtual bool hash_operand_valueize (const_tree, inchash::hash &,
+				      unsigned int);
 };
 
 } // ipa_icf_gimple namespace
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
index 7c486eda758..1a9a0f695be 100644
--- a/gcc/ipa-icf.c
+++ b/gcc/ipa-icf.c
@@ -878,9 +878,14 @@ sem_function::equals_private (sem_item *item)
     }
 
   /* Checking all basic blocks.  */
+  push_cfun (DECL_STRUCT_FUNCTION (decl));
   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();
+      {
+	pop_cfun ();
+        return return_false();
+      }
+  pop_cfun ();
 
   auto_vec <int> bb_dict;
 
-- 
2.21.0

>From 03893a306d9c37f7590c23cbccf36f124e92f1f0 Mon Sep 17 00:00:00 2001
From: Martin Liska <mli...@suse.cz>
Date: Mon, 10 Jun 2019 14:33:27 +0200
Subject: [PATCH 1/2] Come up with an abstraction.

---
 gcc/fold-const.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++-
 gcc/fold-const.h |  30 +++-
 gcc/tree.c       | 286 ---------------------------------------
 3 files changed, 372 insertions(+), 290 deletions(-)

diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 1fdd085545a..001b7f02dc3 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -2940,7 +2940,8 @@ combine_comparisons (location_t loc,
    even if var is volatile.  */
 
 bool
-operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags)
+operand_compare::operand_equal_p (const_tree arg0, const_tree arg1,
+				  unsigned int flags)
 {
   /* When checking, verify at the outermost operand_equal_p call that
      if operand_equal_p returns non-zero then ARG0 and ARG1 has the same
@@ -2952,8 +2953,8 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags)
 	  if (arg0 != arg1)
 	    {
 	      inchash::hash hstate0 (0), hstate1 (0);
-	      inchash::add_expr (arg0, hstate0, flags | OEP_HASH_CHECK);
-	      inchash::add_expr (arg1, hstate1, flags | OEP_HASH_CHECK);
+	      hash_operand (arg0, hstate0, flags | OEP_HASH_CHECK);
+	      hash_operand (arg1, hstate1, flags | OEP_HASH_CHECK);
 	      hashval_t h0 = hstate0.end ();
 	      hashval_t h1 = hstate1.end ();
 	      gcc_assert (h0 == h1);
@@ -3094,6 +3095,12 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags)
 	  || (! TREE_SIDE_EFFECTS (arg0) && ! TREE_SIDE_EFFECTS (arg1))))
     return true;
 
+  int val = operand_equal_valueize (arg0, arg1, flags);
+  if (val == 1)
+    return 1;
+  if (val == 0)
+    return 0;
+
   /* Next handle constant cases, those for which we can return 1 even
      if ONLY_CONST is set.  */
   if (TREE_CONSTANT (arg0) && TREE_CONSTANT (arg1))
@@ -3606,6 +3613,339 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags)
 #undef OP_SAME
 #undef OP_SAME_WITH_NULL
 }
+
+/* Generate a hash value for an expression.  This can be used iteratively
+   by passing a previous result as the HSTATE argument.  */
+
+void
+operand_compare::hash_operand (const_tree t, inchash::hash &hstate,
+			       unsigned int flags)
+{
+  int i;
+  enum tree_code code;
+  enum tree_code_class tclass;
+
+  if (t == NULL_TREE || t == error_mark_node)
+    {
+      hstate.merge_hash (0);
+      return;
+    }
+
+  STRIP_ANY_LOCATION_WRAPPER (t);
+
+  if (!(flags & OEP_ADDRESS_OF))
+    STRIP_NOPS (t);
+
+  code = TREE_CODE (t);
+
+  bool ret = hash_operand_valueize (t, hstate, flags);
+  if (ret)
+    return;
+
+  switch (code)
+    {
+    /* Alas, constants aren't shared, so we can't rely on pointer
+       identity.  */
+    case VOID_CST:
+      hstate.merge_hash (0);
+      return;
+    case INTEGER_CST:
+      gcc_checking_assert (!(flags & OEP_ADDRESS_OF));
+      for (i = 0; i < TREE_INT_CST_EXT_NUNITS (t); i++)
+	hstate.add_hwi (TREE_INT_CST_ELT (t, i));
+      return;
+    case REAL_CST:
+      {
+	unsigned int val2;
+	if (!HONOR_SIGNED_ZEROS (t) && real_zerop (t))
+	  val2 = rvc_zero;
+	else
+	  val2 = real_hash (TREE_REAL_CST_PTR (t));
+	hstate.merge_hash (val2);
+	return;
+      }
+    case FIXED_CST:
+      {
+	unsigned int val2 = fixed_hash (TREE_FIXED_CST_PTR (t));
+	hstate.merge_hash (val2);
+	return;
+      }
+    case STRING_CST:
+      hstate.add ((const void *) TREE_STRING_POINTER (t),
+		  TREE_STRING_LENGTH (t));
+      return;
+    case COMPLEX_CST:
+      hash_operand (TREE_REALPART (t), hstate, flags);
+      hash_operand (TREE_IMAGPART (t), hstate, flags);
+      return;
+    case VECTOR_CST:
+      {
+	hstate.add_int (VECTOR_CST_NPATTERNS (t));
+	hstate.add_int (VECTOR_CST_NELTS_PER_PATTERN (t));
+	unsigned int count = vector_cst_encoded_nelts (t);
+	for (unsigned int i = 0; i < count; ++i)
+	  hash_operand (VECTOR_CST_ENCODED_ELT (t, i), hstate, flags);
+	return;
+      }
+    case SSA_NAME:
+      /* We can just compare by pointer.  */
+      hstate.add_hwi (SSA_NAME_VERSION (t));
+      return;
+    case PLACEHOLDER_EXPR:
+      /* The node itself doesn't matter.  */
+      return;
+    case BLOCK:
+    case OMP_CLAUSE:
+      /* Ignore.  */
+      return;
+    case TREE_LIST:
+      /* A list of expressions, for a CALL_EXPR or as the elements of a
+	 VECTOR_CST.  */
+      for (; t; t = TREE_CHAIN (t))
+	hash_operand (TREE_VALUE (t), hstate, flags);
+      return;
+    case CONSTRUCTOR:
+      {
+	unsigned HOST_WIDE_INT idx;
+	tree field, value;
+	flags &= ~OEP_ADDRESS_OF;
+	FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), idx, field, value)
+	  {
+	    hash_operand (field, hstate, flags);
+	    hash_operand (value, hstate, flags);
+	  }
+	return;
+      }
+    case STATEMENT_LIST:
+      {
+	tree_stmt_iterator i;
+	for (i = tsi_start (CONST_CAST_TREE (t));
+	     !tsi_end_p (i); tsi_next (&i))
+	  hash_operand (tsi_stmt (i), hstate, flags);
+	return;
+      }
+    case TREE_VEC:
+      for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
+	hash_operand (TREE_VEC_ELT (t, i), hstate, flags);
+      return;
+    case IDENTIFIER_NODE:
+      hstate.add_object (IDENTIFIER_HASH_VALUE (t));
+      return;
+    case FIELD_DECL:
+      inchash::add_expr (DECL_FIELD_OFFSET (t), hstate, flags);
+      inchash::add_expr (DECL_FIELD_BIT_OFFSET (t), hstate, flags);
+      return;
+    case FUNCTION_DECL:
+      /* When referring to a built-in FUNCTION_DECL, use the __builtin__ form.
+	 Otherwise nodes that compare equal according to operand_equal_p might
+	 get different hash codes.  However, don't do this for machine specific
+	 or front end builtins, since the function code is overloaded in those
+	 cases.  */
+      if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL
+	  && builtin_decl_explicit_p (DECL_FUNCTION_CODE (t)))
+	{
+	  t = builtin_decl_explicit (DECL_FUNCTION_CODE (t));
+	  code = TREE_CODE (t);
+	}
+      /* FALL THROUGH */
+    default:
+      if (POLY_INT_CST_P (t))
+	{
+	  for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i)
+	    hstate.add_wide_int (wi::to_wide (POLY_INT_CST_COEFF (t, i)));
+	  return;
+	}
+      tclass = TREE_CODE_CLASS (code);
+
+      if (tclass == tcc_declaration)
+	{
+	  /* DECL's have a unique ID */
+	  hstate.add_hwi (DECL_UID (t));
+	}
+      else if (tclass == tcc_comparison && !commutative_tree_code (code))
+	{
+	  /* For comparisons that can be swapped, use the lower
+	     tree code.  */
+	  enum tree_code ccode = swap_tree_comparison (code);
+	  if (code < ccode)
+	    ccode = code;
+	  hstate.add_object (ccode);
+	  hash_operand (TREE_OPERAND (t, ccode != code), hstate, flags);
+	  hash_operand (TREE_OPERAND (t, ccode == code), hstate, flags);
+	}
+      else if (CONVERT_EXPR_CODE_P (code))
+	{
+	  /* NOP_EXPR and CONVERT_EXPR are considered equal by
+	     operand_equal_p.  */
+	  enum tree_code ccode = NOP_EXPR;
+	  hstate.add_object (ccode);
+
+	  /* Don't hash the type, that can lead to having nodes which
+	     compare equal according to operand_equal_p, but which
+	     have different hash codes.  Make sure to include signedness
+	     in the hash computation.  */
+	  hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t)));
+	  hash_operand (TREE_OPERAND (t, 0), hstate, flags);
+	}
+      /* For OEP_ADDRESS_OF, hash MEM_EXPR[&decl, 0] the same as decl.  */
+      else if (code == MEM_REF
+	       && (flags & OEP_ADDRESS_OF) != 0
+	       && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR
+	       && DECL_P (TREE_OPERAND (TREE_OPERAND (t, 0), 0))
+	       && integer_zerop (TREE_OPERAND (t, 1)))
+	hash_operand (TREE_OPERAND (TREE_OPERAND (t, 0), 0),
+		      hstate, flags);
+      /* Don't ICE on FE specific trees, or their arguments etc.
+	 during operand_equal_p hash verification.  */
+      else if (!IS_EXPR_CODE_CLASS (tclass))
+	gcc_assert (flags & OEP_HASH_CHECK);
+      else
+	{
+	  unsigned int sflags = flags;
+
+	  hstate.add_object (code);
+
+	  switch (code)
+	    {
+	    case ADDR_EXPR:
+	      gcc_checking_assert (!(flags & OEP_ADDRESS_OF));
+	      flags |= OEP_ADDRESS_OF;
+	      sflags = flags;
+	      break;
+
+	    case INDIRECT_REF:
+	    case MEM_REF:
+	    case TARGET_MEM_REF:
+	      flags &= ~OEP_ADDRESS_OF;
+	      sflags = flags;
+	      break;
+
+	    case ARRAY_REF:
+	    case ARRAY_RANGE_REF:
+	    case COMPONENT_REF:
+	    case BIT_FIELD_REF:
+	      sflags &= ~OEP_ADDRESS_OF;
+	      break;
+
+	    case COND_EXPR:
+	      flags &= ~OEP_ADDRESS_OF;
+	      break;
+
+	    case WIDEN_MULT_PLUS_EXPR:
+	    case WIDEN_MULT_MINUS_EXPR:
+	      {
+		/* The multiplication operands are commutative.  */
+		inchash::hash one, two;
+		hash_operand (TREE_OPERAND (t, 0), one, flags);
+		hash_operand (TREE_OPERAND (t, 1), two, flags);
+		hstate.add_commutative (one, two);
+		hash_operand (TREE_OPERAND (t, 2), two, flags);
+		return;
+	      }
+
+	    case CALL_EXPR:
+	      if (CALL_EXPR_FN (t) == NULL_TREE)
+		hstate.add_int (CALL_EXPR_IFN (t));
+	      break;
+
+	    case TARGET_EXPR:
+	      /* For TARGET_EXPR, just hash on the TARGET_EXPR_SLOT.
+		 Usually different TARGET_EXPRs just should use
+		 different temporaries in their slots.  */
+	      hash_operand (TARGET_EXPR_SLOT (t), hstate, flags);
+	      return;
+
+	    /* Virtual table call.  */
+	    case OBJ_TYPE_REF:
+	      inchash::add_expr (OBJ_TYPE_REF_EXPR (t), hstate, flags);
+	      if (virtual_method_call_p (t))
+		{
+		  inchash::add_expr (OBJ_TYPE_REF_TOKEN (t), hstate, flags);
+		  inchash::add_expr (OBJ_TYPE_REF_OBJECT (t), hstate, flags);
+		}
+	      return;
+	    default:
+	      break;
+	    }
+
+	  /* Don't hash the type, that can lead to having nodes which
+	     compare equal according to operand_equal_p, but which
+	     have different hash codes.  */
+	  if (code == NON_LVALUE_EXPR)
+	    {
+	      /* Make sure to include signness in the hash computation.  */
+	      hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t)));
+	      hash_operand (TREE_OPERAND (t, 0), hstate, flags);
+	    }
+
+	  else if (commutative_tree_code (code))
+	    {
+	      /* It's a commutative expression.  We want to hash it the same
+		 however it appears.  We do this by first hashing both operands
+		 and then rehashing based on the order of their independent
+		 hashes.  */
+	      inchash::hash one, two;
+	      hash_operand (TREE_OPERAND (t, 0), one, flags);
+	      hash_operand (TREE_OPERAND (t, 1), two, flags);
+	      hstate.add_commutative (one, two);
+	    }
+	  else
+	    for (i = TREE_OPERAND_LENGTH (t) - 1; i >= 0; --i)
+	      hash_operand (TREE_OPERAND (t, i), hstate,
+			    i == 0 ? flags : sflags);
+	}
+      return;
+    }
+}
+
+
+/* Valueizer is a virtual method that allows to introduce extra equalities
+   that are not directly visible from the operand.
+   N1 means values are known to be equal, 0 means values are known
+   to be different -1 means that operand_equal_p should
+   continue processing.  */
+
+int
+operand_compare::operand_equal_valueize (const_tree, const_tree, unsigned int)
+{
+  return -1;
+}
+
+/* Valueizer is a function that returns true when the function can
+   hash the ARG.  If so, hash value is added to H.  */
+bool
+operand_compare::hash_operand_valueize (const_tree, inchash::hash &,
+					unsigned int)
+{
+  return false;
+}
+
+static operand_compare default_compare_instance;
+
+/* Conveinece wrapper around operand_compare class because usually we do
+   not need to play with the valueizer.  */
+
+bool
+operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags)
+{
+  return default_compare_instance.operand_equal_p (arg0, arg1, flags);
+}
+
+namespace inchash
+{
+
+/* Generate a hash value for an expression.  This can be used iteratively
+   by passing a previous result as the HSTATE argument.
+
+   This function is intended to produce the same hash for expressions which
+   would compare equal using operand_equal_p.  */
+void
+add_expr (const_tree t, inchash::hash &hstate, unsigned int flags)
+{
+  default_compare_instance.hash_operand (t, hstate, flags);
+}
+
+}
 
 /* Similar to operand_equal_p, but see if ARG0 might be a variant of ARG1
    with a different signedness or a narrower precision.  */
diff --git a/gcc/fold-const.h b/gcc/fold-const.h
index 1519d77300e..9c963b9f994 100644
--- a/gcc/fold-const.h
+++ b/gcc/fold-const.h
@@ -83,7 +83,7 @@ extern bool fold_deferring_overflow_warnings_p (void);
 extern void fold_overflow_warning (const char*, enum warn_strict_overflow_code);
 extern enum tree_code fold_div_compare (enum tree_code, tree, tree,
 					tree *, tree *, bool *);
-extern bool operand_equal_p (const_tree, const_tree, unsigned int);
+extern bool operand_equal_p (const_tree, const_tree, unsigned int flags = 0);
 extern int multiple_of_p (tree, const_tree, const_tree);
 #define omit_one_operand(T1,T2,T3)\
    omit_one_operand_loc (UNKNOWN_LOCATION, T1, T2, T3)
@@ -211,4 +211,32 @@ extern tree fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE
 
 #define fold_build_pointer_plus_hwi(p,o) \
 	fold_build_pointer_plus_hwi_loc (UNKNOWN_LOCATION, p, o)
+
+
+/* Class used to compare gimple operands.  */
+
+class operand_compare
+{
+public:
+  /* Return true if two operands are equal.  The flags fields can be used
+     to specify OEP flags described above.  */
+  bool operand_equal_p (const_tree, const_tree, unsigned int flags = 0);
+
+  /* Generate a hash value for an expression.  This can be used iteratively
+     by passing a previous result as the HSTATE argument.  */
+  void hash_operand (const_tree, inchash::hash &, unsigned flags = 0);
+
+private:
+  /* Valueizer can be used to make non-trivial equalities for expressions
+     that do not look same in isolation.
+     1 means values are known to be equal, 0 means values are known to be
+     different -1 means that operand_equal_p should continue processing.  */
+  virtual int operand_equal_valueize (const_tree, const_tree, unsigned int);
+
+  /* Valueizer is a function that returns true when the function can
+     hash the ARG.  If so, hash value is added to H.  */
+  virtual bool hash_operand_valueize (const_tree arg, inchash::hash &h,
+				      unsigned int flags);
+};
+
 #endif // GCC_FOLD_CONST_H
diff --git a/gcc/tree.c b/gcc/tree.c
index 37e54b84089..1b36e918854 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -7760,292 +7760,6 @@ operation_no_trapping_overflow (tree type, enum tree_code code)
     }
 }
 
-namespace inchash
-{
-
-/* Generate a hash value for an expression.  This can be used iteratively
-   by passing a previous result as the HSTATE argument.
-
-   This function is intended to produce the same hash for expressions which
-   would compare equal using operand_equal_p.  */
-void
-add_expr (const_tree t, inchash::hash &hstate, unsigned int flags)
-{
-  int i;
-  enum tree_code code;
-  enum tree_code_class tclass;
-
-  if (t == NULL_TREE || t == error_mark_node)
-    {
-      hstate.merge_hash (0);
-      return;
-    }
-
-  STRIP_ANY_LOCATION_WRAPPER (t);
-
-  if (!(flags & OEP_ADDRESS_OF))
-    STRIP_NOPS (t);
-
-  code = TREE_CODE (t);
-
-  switch (code)
-    {
-    /* Alas, constants aren't shared, so we can't rely on pointer
-       identity.  */
-    case VOID_CST:
-      hstate.merge_hash (0);
-      return;
-    case INTEGER_CST:
-      gcc_checking_assert (!(flags & OEP_ADDRESS_OF));
-      for (i = 0; i < TREE_INT_CST_EXT_NUNITS (t); i++)
-	hstate.add_hwi (TREE_INT_CST_ELT (t, i));
-      return;
-    case REAL_CST:
-      {
-	unsigned int val2;
-	if (!HONOR_SIGNED_ZEROS (t) && real_zerop (t))
-	  val2 = rvc_zero;
-	else
-	  val2 = real_hash (TREE_REAL_CST_PTR (t));
-	hstate.merge_hash (val2);
-	return;
-      }
-    case FIXED_CST:
-      {
-	unsigned int val2 = fixed_hash (TREE_FIXED_CST_PTR (t));
-	hstate.merge_hash (val2);
-	return;
-      }
-    case STRING_CST:
-      hstate.add ((const void *) TREE_STRING_POINTER (t),
-		  TREE_STRING_LENGTH (t));
-      return;
-    case COMPLEX_CST:
-      inchash::add_expr (TREE_REALPART (t), hstate, flags);
-      inchash::add_expr (TREE_IMAGPART (t), hstate, flags);
-      return;
-    case VECTOR_CST:
-      {
-	hstate.add_int (VECTOR_CST_NPATTERNS (t));
-	hstate.add_int (VECTOR_CST_NELTS_PER_PATTERN (t));
-	unsigned int count = vector_cst_encoded_nelts (t);
-	for (unsigned int i = 0; i < count; ++i)
-	  inchash::add_expr (VECTOR_CST_ENCODED_ELT (t, i), hstate, flags);
-	return;
-      }
-    case SSA_NAME:
-      /* We can just compare by pointer.  */
-      hstate.add_hwi (SSA_NAME_VERSION (t));
-      return;
-    case PLACEHOLDER_EXPR:
-      /* The node itself doesn't matter.  */
-      return;
-    case BLOCK:
-    case OMP_CLAUSE:
-      /* Ignore.  */
-      return;
-    case TREE_LIST:
-      /* A list of expressions, for a CALL_EXPR or as the elements of a
-	 VECTOR_CST.  */
-      for (; t; t = TREE_CHAIN (t))
-	inchash::add_expr (TREE_VALUE (t), hstate, flags);
-      return;
-    case CONSTRUCTOR:
-      {
-	unsigned HOST_WIDE_INT idx;
-	tree field, value;
-	flags &= ~OEP_ADDRESS_OF;
-	FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), idx, field, value)
-	  {
-	    inchash::add_expr (field, hstate, flags);
-	    inchash::add_expr (value, hstate, flags);
-	  }
-	return;
-      }
-    case STATEMENT_LIST:
-      {
-	tree_stmt_iterator i;
-	for (i = tsi_start (CONST_CAST_TREE (t));
-	     !tsi_end_p (i); tsi_next (&i))
-	  inchash::add_expr (tsi_stmt (i), hstate, flags);
-	return;
-      }
-    case TREE_VEC:
-      for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
-	inchash::add_expr (TREE_VEC_ELT (t, i), hstate, flags);
-      return;
-    case IDENTIFIER_NODE:
-      hstate.add_object (IDENTIFIER_HASH_VALUE (t));
-      return;
-    case FIELD_DECL:
-      inchash::add_expr (DECL_FIELD_OFFSET (t), hstate, flags);
-      inchash::add_expr (DECL_FIELD_BIT_OFFSET (t), hstate, flags);
-      return;
-    case FUNCTION_DECL:
-      /* When referring to a built-in FUNCTION_DECL, use the __builtin__ form.
-	 Otherwise nodes that compare equal according to operand_equal_p might
-	 get different hash codes.  However, don't do this for machine specific
-	 or front end builtins, since the function code is overloaded in those
-	 cases.  */
-      if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL
-	  && builtin_decl_explicit_p (DECL_FUNCTION_CODE (t)))
-	{
-	  t = builtin_decl_explicit (DECL_FUNCTION_CODE (t));
-	  code = TREE_CODE (t);
-	}
-      /* FALL THROUGH */
-    default:
-      if (POLY_INT_CST_P (t))
-	{
-	  for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i)
-	    hstate.add_wide_int (wi::to_wide (POLY_INT_CST_COEFF (t, i)));
-	  return;
-	}
-      tclass = TREE_CODE_CLASS (code);
-
-      if (tclass == tcc_declaration)
-	{
-	  /* DECL's have a unique ID */
-	  hstate.add_hwi (DECL_UID (t));
-	}
-      else if (tclass == tcc_comparison && !commutative_tree_code (code))
-	{
-	  /* For comparisons that can be swapped, use the lower
-	     tree code.  */
-	  enum tree_code ccode = swap_tree_comparison (code);
-	  if (code < ccode)
-	    ccode = code;
-	  hstate.add_object (ccode);
-	  inchash::add_expr (TREE_OPERAND (t, ccode != code), hstate, flags);
-	  inchash::add_expr (TREE_OPERAND (t, ccode == code), hstate, flags);
-	}
-      else if (CONVERT_EXPR_CODE_P (code))
-	{
-	  /* NOP_EXPR and CONVERT_EXPR are considered equal by
-	     operand_equal_p.  */
-	  enum tree_code ccode = NOP_EXPR;
-	  hstate.add_object (ccode);
-
-	  /* Don't hash the type, that can lead to having nodes which
-	     compare equal according to operand_equal_p, but which
-	     have different hash codes.  Make sure to include signedness
-	     in the hash computation.  */
-	  hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t)));
-	  inchash::add_expr (TREE_OPERAND (t, 0), hstate, flags);
-	}
-      /* For OEP_ADDRESS_OF, hash MEM_EXPR[&decl, 0] the same as decl.  */
-      else if (code == MEM_REF
-	       && (flags & OEP_ADDRESS_OF) != 0
-	       && TREE_CODE (TREE_OPERAND (t, 0)) == ADDR_EXPR
-	       && DECL_P (TREE_OPERAND (TREE_OPERAND (t, 0), 0))
-	       && integer_zerop (TREE_OPERAND (t, 1)))
-	inchash::add_expr (TREE_OPERAND (TREE_OPERAND (t, 0), 0),
-			   hstate, flags);
-      /* Don't ICE on FE specific trees, or their arguments etc.
-	 during operand_equal_p hash verification.  */
-      else if (!IS_EXPR_CODE_CLASS (tclass))
-	gcc_assert (flags & OEP_HASH_CHECK);
-      else
-	{
-	  unsigned int sflags = flags;
-
-	  hstate.add_object (code);
-
-	  switch (code)
-	    {
-	    case ADDR_EXPR:
-	      gcc_checking_assert (!(flags & OEP_ADDRESS_OF));
-	      flags |= OEP_ADDRESS_OF;
-	      sflags = flags;
-	      break;
-
-	    case INDIRECT_REF:
-	    case MEM_REF:
-	    case TARGET_MEM_REF:
-	      flags &= ~OEP_ADDRESS_OF;
-	      sflags = flags;
-	      break;
-
-	    case ARRAY_REF:
-	    case ARRAY_RANGE_REF:
-	    case COMPONENT_REF:
-	    case BIT_FIELD_REF:
-	      sflags &= ~OEP_ADDRESS_OF;
-	      break;
-
-	    case COND_EXPR:
-	      flags &= ~OEP_ADDRESS_OF;
-	      break;
-
-	    case WIDEN_MULT_PLUS_EXPR:
-	    case WIDEN_MULT_MINUS_EXPR:
-	      {
-		/* The multiplication operands are commutative.  */
-		inchash::hash one, two;
-		inchash::add_expr (TREE_OPERAND (t, 0), one, flags);
-		inchash::add_expr (TREE_OPERAND (t, 1), two, flags);
-		hstate.add_commutative (one, two);
-		inchash::add_expr (TREE_OPERAND (t, 2), two, flags);
-		return;
-	      }
-
-	    case CALL_EXPR:
-	      if (CALL_EXPR_FN (t) == NULL_TREE)
-		hstate.add_int (CALL_EXPR_IFN (t));
-	      break;
-
-	    case TARGET_EXPR:
-	      /* For TARGET_EXPR, just hash on the TARGET_EXPR_SLOT.
-		 Usually different TARGET_EXPRs just should use
-		 different temporaries in their slots.  */
-	      inchash::add_expr (TARGET_EXPR_SLOT (t), hstate, flags);
-	      return;
-
-	    /* Virtual table call.  */
-	    case OBJ_TYPE_REF:
-	      inchash::add_expr (OBJ_TYPE_REF_EXPR (t), hstate, flags);
-	      if (virtual_method_call_p (t))
-		{
-		  inchash::add_expr (OBJ_TYPE_REF_TOKEN (t), hstate, flags);
-		  inchash::add_expr (OBJ_TYPE_REF_OBJECT (t), hstate, flags);
-		}
-	      return;
-	    default:
-	      break;
-	    }
-
-	  /* Don't hash the type, that can lead to having nodes which
-	     compare equal according to operand_equal_p, but which
-	     have different hash codes.  */
-	  if (code == NON_LVALUE_EXPR)
-	    {
-	      /* Make sure to include signness in the hash computation.  */
-	      hstate.add_int (TYPE_UNSIGNED (TREE_TYPE (t)));
-	      inchash::add_expr (TREE_OPERAND (t, 0), hstate, flags);
-	    }
-
-	  else if (commutative_tree_code (code))
-	    {
-	      /* It's a commutative expression.  We want to hash it the same
-		 however it appears.  We do this by first hashing both operands
-		 and then rehashing based on the order of their independent
-		 hashes.  */
-	      inchash::hash one, two;
-	      inchash::add_expr (TREE_OPERAND (t, 0), one, flags);
-	      inchash::add_expr (TREE_OPERAND (t, 1), two, flags);
-	      hstate.add_commutative (one, two);
-	    }
-	  else
-	    for (i = TREE_OPERAND_LENGTH (t) - 1; i >= 0; --i)
-	      inchash::add_expr (TREE_OPERAND (t, i), hstate,
-				 i == 0 ? flags : sflags);
-	}
-      return;
-    }
-}
-
-}
-
 /* Constructors for pointer, array and function types.
    (RECORD_TYPE, UNION_TYPE and ENUMERAL_TYPE nodes are
    constructed by language-dependent code, not here.)  */
-- 
2.21.0

Reply via email to