diff gcc/ipa-icf-gimple.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
line wrap: on
line diff
--- a/gcc/ipa-icf-gimple.c	Thu Oct 25 07:37:49 2018 +0900
+++ b/gcc/ipa-icf-gimple.c	Thu Feb 13 11:34:05 2020 +0900
@@ -1,5 +1,5 @@
 /* Interprocedural Identical Code Folding pass
-   Copyright (C) 2014-2018 Free Software Foundation, Inc.
+   Copyright (C) 2014-2020 Free Software Foundation, Inc.
 
    Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
 
@@ -31,12 +31,12 @@
 #include "cgraph.h"
 #include "data-streamer.h"
 #include "gimple-pretty-print.h"
-#include "alias.h"
 #include "fold-const.h"
 #include "gimple-iterator.h"
 #include "ipa-utils.h"
 #include "tree-eh.h"
 #include "builtins.h"
+#include "cfgloop.h"
 
 #include "ipa-icf-gimple.h"
 
@@ -50,14 +50,12 @@
    of declarations that can be skipped.  */
 
 func_checker::func_checker (tree source_func_decl, tree target_func_decl,
-			    bool compare_polymorphic,
 			    bool ignore_labels,
 			    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)
 {
   function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
@@ -87,7 +85,7 @@
 /* Verifies that trees T1 and T2 are equivalent from perspective of ICF.  */
 
 bool
-func_checker::compare_ssa_name (tree t1, tree t2)
+func_checker::compare_ssa_name (const_tree t1, const_tree t2)
 {
   gcc_assert (TREE_CODE (t1) == SSA_NAME);
   gcc_assert (TREE_CODE (t2) == SSA_NAME);
@@ -110,13 +108,7 @@
       tree b1 = SSA_NAME_VAR (t1);
       tree b2 = SSA_NAME_VAR (t2);
 
-      if (b1 == NULL && b2 == NULL)
-	return true;
-
-      if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
-	return return_false ();
-
-      return compare_cst_or_decl (b1, b2);
+      return compare_operand (b1, b2);
     }
 
   return true;
@@ -147,7 +139,7 @@
    come from functions FUNC1 and FUNC2.  */
 
 bool
-func_checker::compare_decl (tree t1, tree t2)
+func_checker::compare_decl (const_tree t1, const_tree t2)
 {
   if (!auto_var_in_fn_p (t1, m_source_func_decl)
       || !auto_var_in_fn_p (t2, m_target_func_decl))
@@ -161,24 +153,8 @@
   if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2)))
     return return_false ();
 
-  /* TODO: we are actually too strict here.  We only need to compare if
-     T1 can be used in polymorphic call.  */
-  if (TREE_ADDRESSABLE (t1)
-      && m_compare_polymorphic
-      && !compatible_polymorphic_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
-					  false))
-    return return_false ();
-
-  if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL)
-      && DECL_BY_REFERENCE (t1)
-      && m_compare_polymorphic
-      && !compatible_polymorphic_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
-					  true))
-    return return_false ();
-
   bool existed_p;
-
-  tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+  const_tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
   if (existed_p)
     return return_with_debug (slot == t2);
   else
@@ -232,154 +208,86 @@
   if (!types_compatible_p (t1, t2))
     return return_false_with_msg ("types are not compatible");
 
-  /* We do a lot of unnecesary matching of types that are not being
-     accessed and thus do not need to be compatible.  In longer term we should
-     remove these checks on all types which are not accessed as memory
-     locations.
-
-     For time being just avoid calling get_alias_set on types that are not
-     having alias sets defined at all.  */
-  if (type_with_alias_set_p (t1) && type_with_alias_set_p (t2)
-      && get_alias_set (t1) != get_alias_set (t2))
-    return return_false_with_msg ("alias sets are different");
-
   return true;
 }
 
-/* Function compare for equality given memory operands T1 and T2.  */
-
-bool
-func_checker::compare_memory_operand (tree t1, tree t2)
-{
-  if (!t1 && !t2)
-    return true;
-  else if (!t1 || !t2)
-    return false;
-
-  ao_ref r1, r2;
-  ao_ref_init (&r1, t1);
-  ao_ref_init (&r2, t2);
-
-  tree b1 = ao_ref_base (&r1);
-  tree b2 = ao_ref_base (&r2);
-
-  bool source_is_memop = DECL_P (b1) || INDIRECT_REF_P (b1)
-			 || TREE_CODE (b1) == MEM_REF
-			 || TREE_CODE (b1) == TARGET_MEM_REF;
-
-  bool target_is_memop = DECL_P (b2) || INDIRECT_REF_P (b2)
-			 || TREE_CODE (b2) == MEM_REF
-			 || TREE_CODE (b2) == TARGET_MEM_REF;
-
-  /* Compare alias sets for memory operands.  */
-  if (source_is_memop && target_is_memop)
-    {
-      if (TREE_THIS_VOLATILE (t1) != TREE_THIS_VOLATILE (t2))
-	return return_false_with_msg ("different operand volatility");
-
-      if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
-	  || ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
-	return return_false_with_msg ("ao alias sets are different");
-
-      /* We can't simply use get_object_alignment_1 on the full
-         reference as for accesses with variable indexes this reports
-	 too conservative alignment.  We also can't use the ao_ref_base
-	 base objects as ao_ref_base happily strips MEM_REFs around
-	 decls even though that may carry alignment info.  */
-      b1 = t1;
-      while (handled_component_p (b1))
-	b1 = TREE_OPERAND (b1, 0);
-      b2 = t2;
-      while (handled_component_p (b2))
-	b2 = TREE_OPERAND (b2, 0);
-      unsigned int align1, align2;
-      unsigned HOST_WIDE_INT tem;
-      get_object_alignment_1 (b1, &align1, &tem);
-      get_object_alignment_1 (b2, &align2, &tem);
-      if (align1 != align2)
-	return return_false_with_msg ("different access alignment");
-
-      /* Similarly we have to compare dependence info where equality
-         tells us we are safe (even some unequal values would be safe
-	 but then we have to maintain a map of bases and cliques).  */
-      unsigned short clique1 = 0, base1 = 0, clique2 = 0, base2 = 0;
-      if (TREE_CODE (b1) == MEM_REF)
-	{
-	  clique1 = MR_DEPENDENCE_CLIQUE (b1);
-	  base1 = MR_DEPENDENCE_BASE (b1);
-	}
-      if (TREE_CODE (b2) == MEM_REF)
-	{
-	  clique2 = MR_DEPENDENCE_CLIQUE (b2);
-	  base2 = MR_DEPENDENCE_BASE (b2);
-	}
-      if (clique1 != clique2 || base1 != base2)
-	return return_false_with_msg ("different dependence info");
-    }
-
-  return compare_operand (t1, t2);
-}
-
 /* Function compare for equality given trees T1 and T2 which
    can be either a constant or a declaration type.  */
 
+void
+func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
+			    unsigned int flags)
+{
+  if (arg == NULL_TREE)
+    {
+      hstate.merge_hash (0);
+      return;
+    }
+
+  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;
+    case FIELD_DECL:
+      inchash::add_expr (DECL_FIELD_OFFSET (arg), hstate, flags);
+      inchash::add_expr (DECL_FIELD_BIT_OFFSET (arg), hstate, flags);
+      return;
+    default:
+      break;
+    }
+
+  return operand_compare::hash_operand (arg, hstate, flags);
+}
+
 bool
-func_checker::compare_cst_or_decl (tree t1, tree t2)
+func_checker::operand_equal_p (const_tree t1, const_tree t2,
+			       unsigned int flags)
 {
-  bool ret;
+  bool r;
+  if (verify_hash_value (t1, t2, flags, &r))
+    return r;
+
+  if (t1 == t2)
+    return true;
+  else if (!t1 || !t2)
+    return false;
+
+  if (TREE_CODE (t1) != TREE_CODE (t2))
+    return return_false ();
 
   switch (TREE_CODE (t1))
     {
-    case INTEGER_CST:
-    case COMPLEX_CST:
-    case VECTOR_CST:
-    case STRING_CST:
-    case REAL_CST:
-      {
-	ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
-	      && operand_equal_p (t1, t2, OEP_ONLY_CONST);
-	return return_with_debug (ret);
-      }
     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 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)
-	  return true;
-
 	int *bb1 = m_label_bb_map.get (t1);
 	int *bb2 = m_label_bb_map.get (t2);
-
 	/* Labels can point to another function (non-local GOTOs).  */
 	return return_with_debug (bb1 != NULL && bb2 != NULL && *bb1 == *bb2);
       }
+
     case PARM_DECL:
     case RESULT_DECL:
     case CONST_DECL:
-      {
-	ret = compare_decl (t1, t2);
-	return return_with_debug (ret);
-      }
+      return compare_decl (t1, t2);
+    case SSA_NAME:
+      return compare_ssa_name (t1, t2);
     default:
-      gcc_unreachable ();
+      break;
     }
+
+  return operand_compare::operand_equal_p (t1, t2, flags);
 }
 
 /* Function responsible for comparison of various operands T1 and T2.
@@ -389,158 +297,13 @@
 bool
 func_checker::compare_operand (tree t1, tree t2)
 {
-  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 ();
-
-  switch (TREE_CODE (t1))
-    {
-    case CONSTRUCTOR:
-      {
-	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;
-      }
-    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:
-      {
-	x1 = TREE_OPERAND (t1, 0);
-	x2 = TREE_OPERAND (t2, 0);
-	y1 = TREE_OPERAND (t1, 1);
-	y2 = TREE_OPERAND (t2, 1);
-
-	ret = compare_operand (x1, x2)
-	      && compare_cst_or_decl (y1, y2);
-
-	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);
-
-	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);
-
-	ret = compare_operand (x1, x2)
-	      && compare_cst_or_decl (y1, y2)
-	      && compare_cst_or_decl (z1, z2);
-
-	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");
-    }
+  if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
+    return true;
+  return return_false_with_msg ("operand_equal_p failed");
 }
 
 bool
@@ -579,7 +342,7 @@
 /* Verifies that trees T1 and T2 do correspond.  */
 
 bool
-func_checker::compare_variable_decl (tree t1, tree t2)
+func_checker::compare_variable_decl (const_tree t1, const_tree t2)
 {
   bool ret = false;
 
@@ -593,7 +356,7 @@
     return return_false_with_msg ("DECL_HARD_REGISTER are different");
 
   if (DECL_HARD_REGISTER (t1)
-      && DECL_ASSEMBLER_NAME (t1) != DECL_ASSEMBLER_NAME (t2))
+      && DECL_ASSEMBLER_NAME_RAW (t1) != DECL_ASSEMBLER_NAME_RAW (t2))
     return return_false_with_msg ("HARD REGISTERS are different");
 
   /* Symbol table variables are known to match before we start comparing
@@ -605,6 +368,40 @@
   return return_with_debug (ret);
 }
 
+/* Compare loop information for basic blocks BB1 and BB2.  */
+
+bool
+func_checker::compare_loops (basic_block bb1, basic_block bb2)
+{
+  if ((bb1->loop_father == NULL) != (bb2->loop_father == NULL))
+    return return_false ();
+
+  class loop *l1 = bb1->loop_father;
+  class loop *l2 = bb2->loop_father;
+  if (l1 == NULL)
+    return true;
+
+  if ((bb1 == l1->header) != (bb2 == l2->header))
+    return return_false_with_msg ("header");
+  if ((bb1 == l1->latch) != (bb2 == l2->latch))
+    return return_false_with_msg ("latch");
+  if (l1->simdlen != l2->simdlen)
+    return return_false_with_msg ("simdlen");
+  if (l1->safelen != l2->safelen)
+    return return_false_with_msg ("safelen");
+  if (l1->can_be_parallel != l2->can_be_parallel)
+    return return_false_with_msg ("can_be_parallel");
+  if (l1->dont_vectorize != l2->dont_vectorize)
+    return return_false_with_msg ("dont_vectorize");
+  if (l1->force_vectorize != l2->force_vectorize)
+    return return_false_with_msg ("force_vectorize");
+  if (l1->unroll != l2->unroll)
+    return return_false_with_msg ("unroll");
+  if (!compare_variable_decl (l1->simduid, l2->simduid))
+    return return_false_with_msg ("simduid");
+
+  return true;
+}
 
 /* Function visits all gimple labels and creates corresponding
    mapping between basic blocks and labels.  */
@@ -619,7 +416,7 @@
 
       if (glabel *label_stmt = dyn_cast <glabel *> (stmt))
 	{
-	  tree t = gimple_label_label (label_stmt);
+	  const_tree t = gimple_label_label (label_stmt);
 	  gcc_assert (TREE_CODE (t) == LABEL_DECL);
 
 	  m_label_bb_map.put (t, bb->bb->index);
@@ -727,6 +524,9 @@
   if (!gsi_end_p (gsi2))
     return return_false ();
 
+  if (!compare_loops (bb1->bb, bb2->bb))
+    return return_false ();
+
   return true;
 }
 
@@ -781,15 +581,15 @@
       t1 = gimple_call_arg (s1, i);
       t2 = gimple_call_arg (s2, i);
 
-      if (!compare_memory_operand (t1, t2))
-	return return_false_with_msg ("memory operands are different");
+      if (!compare_operand (t1, t2))
+	return return_false_with_msg ("GIMPLE call operands are different");
     }
 
   /* Return value checking.  */
   t1 = gimple_get_lhs (s1);
   t2 = gimple_get_lhs (s2);
 
-  return compare_memory_operand (t1, t2);
+  return compare_operand (t1, t2);
 }
 
 
@@ -820,8 +620,16 @@
       arg1 = gimple_op (s1, i);
       arg2 = gimple_op (s2, i);
 
-      if (!compare_memory_operand (arg1, arg2))
-	return return_false_with_msg ("memory operands are different");
+      /* LHS types of NOP_EXPR must be compatible.  */
+      if (CONVERT_EXPR_CODE_P (code1) && i == 0)
+	{
+	  if (!compatible_types_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
+	    return return_false_with_msg ("GIMPLE NOP LHS type mismatch");
+	}
+
+      if (!compare_operand (arg1, arg2))
+	return return_false_with_msg ("GIMPLE assignment operands "
+				      "are different");
     }
 
 
@@ -855,14 +663,6 @@
   return compare_operand (t1, t2);
 }
 
-/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2.  */
-
-bool
-func_checker::compare_tree_ssa_label (tree t1, tree t2)
-{
-  return compare_operand (t1, t2);
-}
-
 /* Verifies for given GIMPLE_LABEL stmts S1 and S2 that
    label statements are semantically equivalent.  */
 
@@ -993,6 +793,9 @@
   if (gimple_asm_input_p (g1) != gimple_asm_input_p (g2))
     return false;
 
+  if (gimple_asm_inline_p (g1) != gimple_asm_inline_p (g2))
+    return false;
+
   if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
     return false;