diff mbox series

Improve handling of memory operands in ipa-icf 2/4

Message ID 20201111235520.GC91415@kam.mff.cuni.cz
State New
Headers show
Series Improve handling of memory operands in ipa-icf 2/4 | expand

Commit Message

Jan Hubicka Nov. 11, 2020, 11:55 p.m. UTC
Hi,
this patch iplements new class ao_compare that is derived from operand_compare
and adds a method to compare and hash ao_refs.  This is used by ICF to enable
more merging.

Comparsion is done as follows

1) Verify that the memory access will happen at the same address
   and will have same size.

   For constant addresses this is done by comparing ao_ref_base
   and offset/size

   For varable accesses it uses operand_equal_p but with OEP_ADDRESS
   (that does not match TBAA metadata) and then operand_equal_p on
   type size.

2) Compare alignments.  I use get_object_alignment_1 like ipa-icf
   did before revamp to operand_equal_p in gcc 9.
   I noticed that return value is bitodd so added a comment

3) Match MR_DEPENDENCE_CLIQUE

At this point the memory refrences are same except for TBAA information.
We continue by checking

4) ref and base alias sets.  Now if lto streaming is going to happen
   instead of comparing alias sets themselves we compare alias_ptr_types

   (the patch depends on the ao_ref_alias_ptr_tyep and
    ao_ref_base_alias_ptr_type acessors I sent yesterday)

5) See if accesses are view converted.
   If they are we are done since access path is not present

6) Compare the part of access path relevant for TBAA.
   I recall FRE relies on the fact that if base and ref types are same the
   access path is, but I do not thing this is 100% reliable especially with LTO
   alias sets.

   The access path comparsion logic is also useful for modref (for next stage1).
   Tracking the access paths improves quite noticeably disambiguation in C++ code
   by being able to distinquish different fields of same type within a struct.
   I had the comparsion logic in my tree for some time and it seems to work
   quite well.

   During cc1plus build we have some cases where we find mismatch after matching
   the base/ref alias sets.  These are due to failed type merging: access path
   oracle in LTO uses TYPE_MAIN_VARIANTs.

I implemented relatively basic hashing using base and offset.

Patch bootstraps with LTO with checking enabled, disabled and with or without
strict aliasing.  With -fno-strict-aliasing we now perform a lot more merging.
After fixing two indenpendent issues i will send separately we have quite good
chance that functions with same hashes are actually merged.  cc1plus stats are
as follows:

     61   false returned: 'compare_ao_refs failed (semantic difference)' in compare_operand at ../../gcc/ipa-icf-gimple.c:336
     73   false returned: 'THIS pointer ODR type mismatch' in equals_wpa at ../../gcc/ipa-icf.c:673
     74   false returned: 'types are not same for ODR' in compatible_polymorphic_types_p at ../../gcc/ipa-icf-gimple.c:197
     94   false returned: 'parameter type is not compatible' in compatible_parm_types_p at ../../gcc/ipa-icf.c:508
    111   false returned: 'GIMPLE LHS type mismatch' in compare_gimple_assign at ../../gcc/ipa-icf-gimple.c:706
    116   false returned: '' in compare_decl at ../../gcc/ipa-icf-gimple.c:162
    236   false returned: 'GIMPLE assignment operands are different' in compare_gimple_assign at ../../gcc/ipa-icf-gimple.c:710
    274   false returned: 'inline attributes are different' in compare_referenced_symbol_properties at ../../gcc/ipa-icf.c:346
    446   false returned: 'parameter types are not compatible' in equals_wpa at ../../gcc/ipa-icf.c:635
    546   false returned: '' in equals_private at ../../gcc/ipa-icf.c:882
    631   false returned: '' in compare_phi_node at ../../gcc/ipa-icf.c:1555
    631   false returned: 'PHI node comparison returns false' in equals_private at ../../gcc/ipa-icf.c:917
    969   false returned: 'decl_or_type flags are different' in equals_wpa at ../../gcc/ipa-icf.c:568
    973   false returned: 'different tree types' in compatible_types_p at ../../gcc/ipa-icf-gimple.c:206
   3109   false returned: 'types are not compatible' in compatible_types_p at ../../gcc/ipa-icf-gimple.c:212
   3849   false returned: 'result types are different' in equals_wpa at ../../gcc/ipa-icf.c:617

So we now have only memory reference 61 mismatches.  There are 11313 functions
merged and 546 fails in equals_private which I think is number of function body
compares that did not go well.  Code size reduction is about 3% compared to
0.2% without patch.

With strict aliasing the situation is worse
     46   false returned: 'references to virtual tables cannot be merged' in compare_referenced_symbol_properties at ../../gcc/ipa-icf.c:369
     48   false returned: 'different references' in compare_symbol_references at ../../gcc/ipa-icf.c:461
     48   false returned: '' in operand_equal_p at ../../gcc/ipa-icf-gimple.c:282
     53   false returned: 'GIMPLE LHS type mismatch' in compare_gimple_assign at ../../gcc/ipa-icf-gimple.c:701
     73   false returned: 'THIS pointer ODR type mismatch' in equals_wpa at ../../gcc/ipa-icf.c:673
     74   false returned: 'types are not same for ODR' in compatible_polymorphic_types_p at ../../gcc/ipa-icf-gimple.c:197
     94   false returned: 'parameter type is not compatible' in compatible_parm_types_p at ../../gcc/ipa-icf.c:508
     98   false returned: 'compare_ao_refs failed (access path difference)' in compare_operand at ../../gcc/ipa-icf-gimple.c:345
    116   false returned: '' in compare_decl at ../../gcc/ipa-icf-gimple.c:162
    180   false returned: 'compare_ao_refs failed (semantic difference)' in compare_operand at ../../gcc/ipa-icf-gimple.c:336
    274   false returned: 'inline attributes are different' in compare_referenced_symbol_properties at ../../gcc/ipa-icf.c:346
    446   false returned: 'parameter types are not compatible' in equals_wpa at ../../gcc/ipa-icf.c:635
    630   false returned: '' in compare_phi_node at ../../gcc/ipa-icf.c:1555
    630   false returned: 'PHI node comparison returns false' in equals_private at ../../gcc/ipa-icf.c:917
    948   false returned: 'different tree types' in compatible_types_p at ../../gcc/ipa-icf-gimple.c:206
    954   false returned: 'operand_equal_p failed' in compare_operand at ../../gcc/ipa-icf-gimple.c:356
    969   false returned: 'decl_or_type flags are different' in equals_wpa at ../../gcc/ipa-icf.c:568
   3076   false returned: 'types are not compatible' in compatible_types_p at ../../gcc/ipa-icf-gimple.c:212
   3849   false returned: 'result types are different' in equals_wpa at ../../gcc/ipa-icf.c:617
  76679   false returned: 'compare_ao_refs failed (ref alias set difference)' in compare_operand at ../../gcc/ipa-icf-gimple.c:342
 218089   false returned: 'compare_ao_refs failed (base alias set difference)' in compare_operand at ../../gcc/ipa-icf-gimple.c:339
 295145   false returned: 'GIMPLE assignment operands are different' in compare_gimple_assign at ../../gcc/ipa-icf-gimple.c:705
 295414   false returned: '' in equals_private at ../../gcc/ipa-icf.c:882

So majority of mismathces are base alias sets (and thus also access path)
that is not big surprise since this is what happens in template instnatiations.

Good news is that we could avoid touching the bodies by streaming the access
types like Martin proposed here
https://gcc.gnu.org/legacy-ml/gcc-patches/2019-11/msg02633.html
Now it is well defined what types needs to be streamed
(ao_ref_base_alias_ptr_type for accesses with strict aliasing where we do not
want to zap TBAA). It is also easy to rewrite the memory operand to zap base
alias set and access path while keeping ref alias set while merging if we
decide it is a good idea (for -Os it certainly is).  The code size reduction is
0.6% which is still an improvement over current mainline. 6118 functions
are merged.

ICF breaks two testcases on warnings where we output fewer of them since
functions get merged.

Bootstrapped/regtested x86_64-linux (together with previous patch and the
ao_ref alias ptr type accessors). Also tested on Firefox and clang build.
I hope it is not too ugly :)

Honza

	* ipa-icf-gimple.c: Include tree-ssa-alias-compare.h.
	(find_checker::func_checker): Initialize m_tbaa.
	(func_checker::hash_operand): Use hash_ao_ref for memory accesses.
	(func_checker::compare_operand): Use compare_ao_refs for memory
	accesses.
	(func_checker::cmopare_gimple_assign): Do not check LHS types
	of memory stores.
	* ipa-icf-gimple.h (func_checker): Derive from ao_compare;
	add m_tbaa.
	* ipa-icf.c: Include tree-ssa-alias-compare.h.
	(sem_function::equals_private): Update call of
	func_checker::func_checker.
	* ipa-utils.h (lto_streaming_expected_p): New inline
	predicate.
	* tree-ssa-alias-compare.h: New file.
	* tree-ssa-alias.c: Include tree-ssa-alias-compare.h
	and bultins.h
	(view_converted_memref_p): New function.
	(types_equal_for_same_type_for_tbaa_p): New function.
	(ao_compare::compare_ao_refs): New member function.
	(ao_compare::hash_ao_ref): New function

	* c-c++-common/Wstringop-overflow-2.c: Disable ICF.
	* g++.dg/warn/Warray-bounds-8.C: Disable ICF.

Comments

Jan Hubicka Nov. 12, 2020, 5:42 p.m. UTC | #1
Hi,
this is updated patch.  It fixes the comparsion of bitfield where I now
check that they bitsizes and bitoffsets match (and OEP_ADDRESSOF is not
used for bitfield references).
I also noticed problem with dependence clique in ao_refs_may_alias that
I copied here.  Instead of base rbase should be used.

Finally I ran statistics on when access paths mismatches and noticed
that I do not really need to check that component_refs and array_refs
are semantically equivalent since this is implied from earlier tests.
This is described in inline comment and simplifies the code.

Bootstrapped/regtested x86_64-linux, OK?
Honza


	* ipa-icf-gimple.c: Include tree-ssa-alias-compare.h.
	(find_checker::func_checker): Initialize m_tbaa.
	(func_checker::hash_operand): Use hash_ao_ref for memory accesses.
	(func_checker::compare_operand): Use compare_ao_refs for memory
	accesses.
	(func_checker::cmopare_gimple_assign): Do not check LHS types
	of memory stores.
	* ipa-icf-gimple.h (func_checker): Derive from ao_compare;
	add m_tbaa.
	* ipa-icf.c: Include tree-ssa-alias-compare.h.
	(sem_function::equals_private): Update call of
	func_checker::func_checker.
	* ipa-utils.h (lto_streaming_expected_p): New inline
	predicate.
	* tree-ssa-alias-compare.h: New file.
	* tree-ssa-alias.c: Include tree-ssa-alias-compare.h
	and bultins.h
	(view_converted_memref_p): New function.
	(types_equal_for_same_type_for_tbaa_p): New function.
	(ao_compare::compare_ao_refs): New member function.
	(ao_compare::hash_ao_ref): New function

	* c-c++-common/Wstringop-overflow-2.c: Disable ICF.
	* g++.dg/warn/Warray-bounds-8.C: Disable ICF.

index f75951f7c49..26337dd7384 100644
--- a/gcc/ipa-icf-gimple.c
+++ b/gcc/ipa-icf-gimple.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "gimple-walk.h"
 
+#include "tree-ssa-alias-compare.h"
 #include "ipa-icf-gimple.h"
 
 namespace ipa_icf_gimple {
@@ -52,13 +53,13 @@ namespace ipa_icf_gimple {
    of declarations that can be skipped.  */
 
 func_checker::func_checker (tree source_func_decl, tree target_func_decl,
-			    bool ignore_labels,
+			    bool ignore_labels, bool tbaa,
 			    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_ignore_labels (ignore_labels)
+    m_ignore_labels (ignore_labels), m_tbaa (tbaa)
 {
   function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
   function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
@@ -252,9 +253,16 @@ func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
 
 void
 func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
-			    unsigned int flags, operand_access_type)
+			    unsigned int flags, operand_access_type access)
 {
-  return hash_operand (arg, hstate, flags);
+  if (access == OP_MEMORY)
+    {
+      ao_ref ref;
+      ao_ref_init (&ref, const_cast <tree> (arg));
+      return hash_ao_ref (&ref, lto_streaming_expected_p (), m_tbaa, hstate);
+    }
+  else
+    return hash_operand (arg, hstate, flags);
 }
 
 bool
@@ -314,18 +322,40 @@ func_checker::compare_operand (tree t1, tree t2, operand_access_type access)
     return true;
   else if (!t1 || !t2)
     return false;
-  if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
-    return true;
-  switch (access)
+  if (access == OP_MEMORY)
     {
-    case OP_MEMORY:
-      return return_false_with_msg
-		 ("operand_equal_p failed (access == memory)");
-    case OP_NORMAL:
+      ao_ref ref1, ref2;
+      ao_ref_init (&ref1, const_cast <tree> (t1));
+      ao_ref_init (&ref2, const_cast <tree> (t2));
+      int flags = compare_ao_refs (&ref1, &ref2,
+				   lto_streaming_expected_p (), m_tbaa);
+
+      if (!flags)
+	return true;
+      if (flags & SEMANTICS)
+	return return_false_with_msg
+		("compare_ao_refs failed (semantic difference)");
+      if (flags & BASE_ALIAS_SET)
+	return return_false_with_msg
+		("compare_ao_refs failed (base alias set difference)");
+      if (flags & REF_ALIAS_SET)
+	return return_false_with_msg
+		 ("compare_ao_refs failed (ref alias set difference)");
+      if (flags & ACCESS_PATH)
+	return return_false_with_msg
+		 ("compare_ao_refs failed (access path difference)");
+      if (flags & DEPENDENCE_CLIQUE)
+	return return_false_with_msg
+		 ("compare_ao_refs failed (dependence clique difference)");
+      gcc_unreachable ();
+    }
+  else
+    {
+      if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
+	return true;
       return return_false_with_msg
-		 ("operand_equal_p failed (access == normal)");
+		 ("operand_equal_p failed");
     }
-  gcc_unreachable ();
 }
 
 bool
@@ -593,10 +623,17 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2)
 
   tree fntype1 = gimple_call_fntype (s1);
   tree fntype2 = gimple_call_fntype (s2);
-  if ((fntype1 && !fntype2)
-      || (!fntype1 && fntype2)
-      || (fntype1 && !types_compatible_p (fntype1, fntype2)))
-    return return_false_with_msg ("call function types are not compatible");
+
+  /* For direct calls we verify that types are comopatible so if we matced
+     callees, callers must match, too.  For indirect calls however verify
+     function type.  */
+  if (!gimple_call_fndecl (s1))
+    {
+      if ((fntype1 && !fntype2)
+	  || (!fntype1 && fntype2)
+	  || (fntype1 && !types_compatible_p (fntype1, fntype2)))
+	return return_false_with_msg ("call function types are not compatible");
+    }
 
   if (fntype1 && fntype2 && comp_type_attributes (fntype1, fntype2) != 1)
     return return_false_with_msg ("different fntype attributes");
@@ -658,10 +695,10 @@ func_checker::compare_gimple_assign (gimple *s1, gimple *s2)
       arg2 = gimple_op (s2, i);
 
       /* Compare types for LHS.  */
-      if (i == 0)
+      if (i == 0 && !gimple_store_p (s1))
 	{
 	  if (!compatible_types_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
-	    return return_false_with_msg ("GIMPLE NOP LHS type mismatch");
+	    return return_false_with_msg ("GIMPLE LHS type mismatch");
 	}
 
       if (!compare_operand (arg1, arg2, get_operand_access_type (&map, arg1)))
@@ -889,7 +926,7 @@ func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2)
 /* Helper for func_checker::classify_operands.  Record that T is a load.  */
 
 static bool
-visit_load_store (gimple *, tree t, tree, void *data)
+visit_load_store (gimple *, tree, tree t, void *data)
 {
   func_checker::operand_access_type_map *map =
     (func_checker::operand_access_type_map *) data;
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
index f251d08100f..dd4ef13c716 100644
--- a/gcc/ipa-icf-gimple.h
+++ b/gcc/ipa-icf-gimple.h
@@ -118,14 +118,14 @@ public:
 
 /* A class aggregating all connections and semantic equivalents
    for a given pair of semantic function candidates.  */
-class func_checker : operand_compare
+class func_checker : ao_compare
 {
 public:
   /* Default constructor.  */
   func_checker ():
     m_source_func_decl (NULL_TREE), m_target_func_decl (NULL_TREE),
     m_ignored_source_nodes (NULL), m_ignored_target_nodes (NULL),
-    m_ignore_labels (false)
+    m_ignore_labels (false), m_tbaa (true)
   {
     m_source_ssa_names.create (0);
     m_target_ssa_names.create (0);
@@ -139,6 +139,7 @@ public:
      of declarations that can be skipped.  */
   func_checker (tree source_func_decl, tree target_func_decl,
 		bool ignore_labels = false,
+		bool tbaa = true,
 		hash_set<symtab_node *> *ignored_source_nodes = NULL,
 		hash_set<symtab_node *> *ignored_target_nodes = NULL);
 
@@ -275,6 +276,9 @@ private:
   /* Flag if ignore labels in comparison.  */
   bool m_ignore_labels;
 
+  /* Flag if we should compare type based alias analysis info.  */
+  bool m_tbaa;
+
 public:
   /* Return true if two operands are equal.  The flags fields can be used
      to specify OEP flags described above.  */
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
index 83f9786b4b2..a283195a487 100644
--- a/gcc/ipa-icf.c
+++ b/gcc/ipa-icf.c
@@ -78,6 +78,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "print-tree.h"
 #include "ipa-utils.h"
+#include "tree-ssa-alias-compare.h"
 #include "ipa-icf-gimple.h"
 #include "fibonacci_heap.h"
 #include "ipa-icf.h"
@@ -843,6 +844,8 @@ sem_function::equals_private (sem_item *item)
 
   m_checker = new func_checker (decl, m_compared_func->decl,
 				false,
+				opt_for_fn (m_compared_func->decl,
+					    flag_strict_aliasing),
 				&refs_set,
 				&m_compared_func->refs_set);
   arg1 = DECL_ARGUMENTS (decl);
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 178c2cbe446..880e527c590 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -265,4 +265,16 @@ get_odr_name_for_type (tree type)
   return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (type_name));
 }
 
+/* Return true if we are going to do LTO streaming.  */
+
+inline bool
+lto_streaming_expected_p ()
+{
+  /* Compilation before LTO stremaing.  */
+  if (flag_lto && !in_lto_p && symtab->state < IPA_SSA_AFTER_INLINING)
+    return true;
+  /* WPA or incremental link.  */
+  return (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO);
+}
+
 #endif  /* GCC_IPA_UTILS_H  */
diff --git a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
index 7c7932e3cf0..63b1a309564 100644
--- a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
+++ b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
@@ -1,7 +1,7 @@
 /* PR middle-end/91458 - inconsistent warning for writing past the end
    of an array member
    { dg-do compile }
-   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+   { dg-options "-O2 -Wall -Wno-array-bounds -fno-ipa-icf" } */
 
 void sink (void*);
 
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
index 6db20135668..6e0d7f3ccc4 100644
--- a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
@@ -3,7 +3,7 @@
    See Wstringop-overflow-3.C for the same test that exercises the other
    warning.
    { dg-do compile }
-   { dg-options "-O2 -Wall -Wno-stringop-overflow" }
+   { dg-options "-O2 -Wall -Wno-stringop-overflow -fno-ipa-icf" }
    { dg-skip-if "" { *-*-aix* } } */
 
 void sink (void*);
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index ae66bd1eafe..3b1c4b2c1f7 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "dbgcnt.h"
 #include "gimple-pretty-print.h"
 #include "print-tree.h"
+#include "tree-ssa-alias-compare.h"
+#include "builtins.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -1205,7 +1207,7 @@ aliasing_component_refs_p (tree ref1,
 	   struct a {int array1[0]; int array[];};
 	 Such struct has size 0 but accesses to a.array may have non-zero size.
 	 In this case the size of TREE_TYPE (base1) is smaller than
-	 size of TREE_TYPE (TREE_OPERNAD (base1, 0)).
+	 size of TREE_TYPE (TREE_OPERAND (base1, 0)).
 
 	 Because we compare sizes of arrays just by sizes of their elements,
 	 we only need to care about zero sized array fields here.  */
@@ -1980,6 +1982,20 @@ decl_refs_may_alias_p (tree ref1, tree base1,
   return true;     
 }
 
+/* Return true if access with BASE is view converted.
+   Base must not be stripped from inner MEM_REF (&decl)
+   which is done by ao_ref_base and thus one extra walk
+   of handled components is needed.  */
+
+static bool
+view_converted_memref_p (tree base)
+{
+  if (TREE_CODE (base) != MEM_REF && TREE_CODE (base) != TARGET_MEM_REF)
+    return false;
+  return same_type_for_tbaa (TREE_TYPE (base),
+			     TREE_TYPE (TREE_OPERAND (base, 1))) != 1;
+}
+
 /* Return true if an indirect reference based on *PTR1 constrained
    to [OFFSET1, OFFSET1 + MAX_SIZE1) may alias a variable based on BASE2
    constrained to [OFFSET2, OFFSET2 + MAX_SIZE2).  *PTR1 and BASE2 have
@@ -3870,3 +3886,329 @@ attr_fnspec::verify ()
 	internal_error ("invalid fn spec attribute \"%s\" arg %i", str, i);
     }
 }
+
+/* Return ture if TYPE1 and TYPE2 will always give the same answer
+   when compared wit hother types using same_type_for_tbaa_p.  */
+
+static bool
+types_equal_for_same_type_for_tbaa_p (tree type1, tree type2,
+				      bool lto_streaming_safe)
+{
+  /* We use same_type_for_tbaa_p to match types in the access path.
+     This check is overly conservative.  */
+  type1 = TYPE_MAIN_VARIANT (type1);
+  type2 = TYPE_MAIN_VARIANT (type2);
+
+  if (TYPE_STRUCTURAL_EQUALITY_P (type1)
+      != TYPE_STRUCTURAL_EQUALITY_P (type2))
+    return false;
+  if (TYPE_STRUCTURAL_EQUALITY_P (type1))
+    return true;
+
+  if (lto_streaming_safe)
+    return type1 == type2;
+  else
+    return TYPE_CANONICAL (type1) == TYPE_CANONICAL (type2);
+}
+
+/* Compare REF1 and REF2 and return flags specifying their differences.
+   If LTO_STREAMING_SAFE is true do not use alias sets and canonical
+   types that are going to be recomputed.
+   If TBAA is true also compare TBAA metadata.  */
+
+int
+ao_compare::compare_ao_refs (ao_ref *ref1, ao_ref *ref2,
+			     bool lto_streaming_safe,
+			     bool tbaa)
+{
+  if (TREE_THIS_VOLATILE (ref1->ref) != TREE_THIS_VOLATILE (ref2->ref))
+    return SEMANTICS;
+  tree base1 = ao_ref_base (ref1);
+  tree base2 = ao_ref_base (ref2);
+
+  if (!known_eq (ref1->offset, ref2->offset)
+      || !known_eq (ref1->size, ref2->size)
+      || !known_eq (ref1->max_size, ref2->max_size))
+    return SEMANTICS;
+
+  /* For variable accesses we need to compare actual paths
+     to check that both refs are accessing same address and the access size.  */
+  if (!known_eq (ref1->size, ref1->max_size))
+    {
+      if (!operand_equal_p (TYPE_SIZE (TREE_TYPE (ref1->ref)),
+			    TYPE_SIZE (TREE_TYPE (ref2->ref)), 0))
+	return SEMANTICS;
+      tree r1 = ref1->ref;
+      tree r2 = ref2->ref;
+
+      /* Handle toplevel COMPONENT_REFs of bitfields.
+	 Those are special since they are not allowed in
+	 ADDR_EXPR.  */
+      if (TREE_CODE (r1) == COMPONENT_REF
+	  && DECL_BIT_FIELD (TREE_OPERAND (r1, 1)))
+	{
+	  if (TREE_CODE (r2) != COMPONENT_REF
+	      || !DECL_BIT_FIELD (TREE_OPERAND (r2, 1)))
+	    return SEMANTICS;
+	  tree field1 = TREE_OPERAND (r1, 1);
+	  tree field2 = TREE_OPERAND (r2, 1);
+	  if (!operand_equal_p (DECL_FIELD_OFFSET (field1),
+				DECL_FIELD_OFFSET (field2), 0)
+	      || !operand_equal_p (DECL_FIELD_BIT_OFFSET (field1),
+				   DECL_FIELD_BIT_OFFSET (field2), 0)
+	      || !operand_equal_p (DECL_SIZE (field1), DECL_SIZE (field2), 0)
+	      || !types_compatible_p (TREE_TYPE (r1),
+				      TREE_TYPE (r2)))
+	    return SEMANTICS;
+	  r1 = TREE_OPERAND (r1, 0);
+	  r2 = TREE_OPERAND (r2, 0);
+	}
+      else if (TREE_CODE (r2) == COMPONENT_REF
+	       && DECL_BIT_FIELD (TREE_OPERAND (r2, 1)))
+	return SEMANTICS;
+
+      /* Similarly for bit field refs.  */
+      if (TREE_CODE (r1) == BIT_FIELD_REF)
+	{
+ 	  if (TREE_CODE (r2) != BIT_FIELD_REF
+	      || !operand_equal_p (TREE_OPERAND (r1, 1),
+				   TREE_OPERAND (r2, 1), 0)
+	      || !operand_equal_p (TREE_OPERAND (r1, 2),
+				   TREE_OPERAND (r2, 2), 0)
+	      || !types_compatible_p (TREE_TYPE (r1),
+				      TREE_TYPE (r2)))
+	    return SEMANTICS;
+	  r1 = TREE_OPERAND (r1, 0);
+	  r2 = TREE_OPERAND (r2, 0);
+	}
+      else if (TREE_CODE (r2) == BIT_FIELD_REF)
+	return SEMANTICS;
+
+      /* Now we can compare the address of actual memory access.  */
+      if (!operand_equal_p (r1, r2, OEP_ADDRESS_OF))
+	return SEMANTICS;
+    }
+  /* For constant accesses we get more matches by comparing offset only.  */
+  else if (!operand_equal_p (base1, base2, OEP_ADDRESS_OF))
+    return SEMANTICS;
+
+  /* We can't simply use get_object_alignment_1 on the full
+     reference as for accesses with variable indexes this reports
+     too conservative alignment.  */
+  unsigned int align1, align2;
+  unsigned HOST_WIDE_INT bitpos1, bitpos2;
+  bool known1 = get_object_alignment_1 (base1, &align1, &bitpos1);
+  bool known2 = get_object_alignment_1 (base2, &align2, &bitpos2);
+  /* ??? For MEMREF get_object_alignment_1 determines aligned from
+     TYPE_ALIGN but still returns false.  This seem to contradict
+     its description.  So compare even if alignment is unknown.   */
+  if (known1 != known2
+      || (bitpos1 != bitpos2 || align1 != align2))
+    return SEMANTICS;
+
+  /* Now we know that accesses are semantically same.  */
+  int flags = 0;
+
+  /* ao_ref_base strips inner MEM_REF [&decl], recover from that here.  */
+  tree rbase1 = ref1->ref;
+  if (rbase1)
+    while (handled_component_p (rbase1))
+      rbase1 = TREE_OPERAND (rbase1, 0);
+  tree rbase2 = ref2->ref;
+  while (handled_component_p (rbase2))
+    rbase2 = TREE_OPERAND (rbase2, 0);
+
+  /* MEM_REFs and TARGET_MEM_REFs record dependence cliques which are used to
+     implement restrict pointers.  MR_DEPENDENCE_CLIQUE 0 means no information.
+     Otherwise we need to match bases and cliques.  */
+  if ((((TREE_CODE (rbase1) == MEM_REF || TREE_CODE (rbase1) == TARGET_MEM_REF)
+	&& MR_DEPENDENCE_CLIQUE (rbase1))
+       || ((TREE_CODE (rbase2) == MEM_REF || TREE_CODE (rbase2) == TARGET_MEM_REF)
+	   && MR_DEPENDENCE_CLIQUE (rbase2)))
+      && (TREE_CODE (rbase1) != TREE_CODE (rbase2)
+	  || MR_DEPENDENCE_CLIQUE (rbase1) != MR_DEPENDENCE_CLIQUE (rbase2)
+	  || (MR_DEPENDENCE_BASE (rbase1) != MR_DEPENDENCE_BASE (rbase2))))
+    flags |= DEPENDENCE_CLIQUE;
+
+  if (!tbaa)
+    return flags;
+
+  /* Alias sets are not stable across LTO sreaming; be conservative here
+     and compare types the alias sets are ultimately based on.  */
+  if (lto_streaming_safe)
+    {
+      tree t1 = ao_ref_alias_ptr_type (ref1);
+      tree t2 = ao_ref_alias_ptr_type (ref2);
+      if (!alias_ptr_types_compatible_p (t1, t2))
+	flags |= REF_ALIAS_SET;
+
+      t1 = ao_ref_base_alias_ptr_type (ref1);
+      t2 = ao_ref_base_alias_ptr_type (ref2);
+      if (!alias_ptr_types_compatible_p (t1, t2))
+	flags |= BASE_ALIAS_SET;
+    }
+  else
+    {
+      if (ao_ref_alias_set (ref1) != ao_ref_alias_set (ref2))
+	flags |= REF_ALIAS_SET;
+      if (ao_ref_base_alias_set (ref1) != ao_ref_base_alias_set (ref2))
+	flags |= BASE_ALIAS_SET;
+    }
+
+  /* Access path is used only on non-view-converted references.  */
+  bool view_converted = view_converted_memref_p (rbase1);
+  if (view_converted_memref_p (rbase2) != view_converted)
+    return flags | ACCESS_PATH;
+  else if (view_converted)
+    return flags;
+
+
+  /* Find start of access paths and look for trailing arrays.  */
+  tree c1 = ref1->ref, c2 = ref2->ref;
+  tree end_struct_ref1 = NULL, end_struct_ref2 = NULL;
+  int nskipped1 = 0, nskipped2 = 0;
+  int i = 0;
+
+  for (tree p1 = ref1->ref; handled_component_p (p1); p1 = TREE_OPERAND (p1, 0))
+    {
+      if (component_ref_to_zero_sized_trailing_array_p (p1))
+	end_struct_ref1 = p1;
+      if (ends_tbaa_access_path_p (p1))
+	c1 = p1, nskipped1 = i;
+      i++;
+    }
+  for (tree p2 = ref2->ref; handled_component_p (p2); p2 = TREE_OPERAND (p2, 0))
+    {
+      if (component_ref_to_zero_sized_trailing_array_p (p2))
+	end_struct_ref2 = p2;
+      if (ends_tbaa_access_path_p (p2))
+	c2 = p2, nskipped1 = i;
+      i++;
+    }
+
+  /* For variable accesses we can not rely on offset match bellow.
+     We know that paths are struturally same, so only check that
+     starts of TBAA paths did not diverge.  */
+  if (!known_eq (ref1->size, ref1->max_size)
+      && nskipped1 != nskipped2)
+    return flags | ACCESS_PATH;
+
+  /* Information about trailing refs is used by
+     aliasing_component_refs_p that is applied only if paths
+     has handled components..  */
+  if (!handled_component_p (c1) && !handled_component_p (c2))
+    ;
+  else if ((end_struct_ref1 != NULL) != (end_struct_ref2 != NULL))
+    return flags | ACCESS_PATH;
+  if (end_struct_ref1
+      && TYPE_MAIN_VARIANT (TREE_TYPE (end_struct_ref1))
+	 != TYPE_MAIN_VARIANT (TREE_TYPE (end_struct_ref2)))
+    return flags | ACCESS_PATH;
+
+  /* Now compare all handled components of the access path.
+     We have three oracles that cares about access paths:
+       - aliasing_component_refs_p
+       - nonoverlapping_refs_since_match_p
+       - nonoverlapping_component_refs_p
+     We need to match things these oracles compare.
+
+     It is only necessary to check types for compatibility
+     and offsets.  Rest of what oracles compares are actual
+     addresses.  Those are already known to be same:
+       - for constant accesses we check offsets
+       - for variable accesses we already matched
+	 the path lexically with operand_equal_p.  */
+  while (true)
+    {
+      bool comp1 = handled_component_p (c1);
+      bool comp2 = handled_component_p (c2);
+
+      if (comp1 != comp2)
+	return flags | ACCESS_PATH;
+      if (!comp1)
+	break;
+
+      if (TREE_CODE (c1) != TREE_CODE (c2))
+	return flags | ACCESS_PATH;
+
+      /* aliasing_component_refs_p attempts to find type match within
+	 the paths.  For that reason both types needs to be equal
+	 with respect to same_type_for_tbaa_p.  */
+      if (!types_equal_for_same_type_for_tbaa_p (TREE_TYPE (c1),
+						 TREE_TYPE (c2),
+						 lto_streaming_safe))
+	return flags | ACCESS_PATH;
+      if (component_ref_to_zero_sized_trailing_array_p (c1)
+	  != component_ref_to_zero_sized_trailing_array_p (c2))
+	return flags | ACCESS_PATH;
+
+      /* aliasing_matching_component_refs_p compares
+	 offsets within the path.  Other properties are ignored.
+	 Do not bother to verify offsets in variable accesses.  Here we
+	 already compared them by operand_equal_p so they are
+	 structurally same.  */
+      if (!known_eq (ref1->size, ref1->max_size))
+	{
+	  poly_int64 offadj1, sztmc1, msztmc1;
+	  bool reverse1;
+	  get_ref_base_and_extent (c1, &offadj1, &sztmc1, &msztmc1, &reverse1);
+	  poly_int64 offadj2, sztmc2, msztmc2;
+	  bool reverse2;
+	  get_ref_base_and_extent (c2, &offadj2, &sztmc2, &msztmc2, &reverse2);
+	  if (!known_eq (offadj1, offadj2))
+	    return flags | ACCESS_PATH;
+	}
+      c1 = TREE_OPERAND (c1, 0);
+      c2 = TREE_OPERAND (c2, 0);
+    }
+  /* Finally test the access type.  */
+  if (!types_equal_for_same_type_for_tbaa_p (TREE_TYPE (c1),
+					     TREE_TYPE (c2),
+					     lto_streaming_safe))
+    return flags | ACCESS_PATH;
+  return flags;
+}
+
+/* Hash REF to HSTATE.  If LTO_STREAMING_SAFE do not use alias sets
+   and canonical types.  */
+void
+ao_compare::hash_ao_ref (ao_ref *ref, bool lto_streaming_safe, bool tbaa,
+			 inchash::hash &hstate)
+{
+  tree base = ao_ref_base (ref);
+  tree tbase = base;
+
+  if (!known_eq (ref->size, ref->max_size))
+    {
+      tree r = ref->ref;
+      if (TREE_CODE (r) == COMPONENT_REF
+	  && DECL_BIT_FIELD (TREE_OPERAND (r, 1)))
+	{
+	  tree field = TREE_OPERAND (r, 1);
+	  hash_operand (DECL_FIELD_OFFSET (field), hstate, 0);
+	  hash_operand (DECL_FIELD_BIT_OFFSET (field), hstate, 0);
+	  hash_operand (DECL_SIZE (field), hstate, 0);
+	  r = TREE_OPERAND (r, 0);
+	}
+      if (TREE_CODE (r) == BIT_FIELD_REF)
+	{
+	  hash_operand (TREE_OPERAND (r, 1), hstate, 0);
+	  hash_operand (TREE_OPERAND (r, 2), hstate, 0);
+	  r = TREE_OPERAND (r, 0);
+	}
+      hash_operand (TYPE_SIZE (TREE_TYPE (ref->ref)), hstate, 0);
+      hash_operand (r, hstate, OEP_ADDRESS_OF);
+    }
+  else
+    {
+      hash_operand (tbase, hstate, OEP_ADDRESS_OF);
+      hstate.add_poly_int (ref->offset);
+      hstate.add_poly_int (ref->size);
+      hstate.add_poly_int (ref->max_size);
+    }
+  if (!lto_streaming_safe && tbaa)
+    {
+      hstate.add_int (ao_ref_alias_set (ref));
+      hstate.add_int (ao_ref_base_alias_set (ref));
+    }
+}
Richard Biener Nov. 13, 2020, 7:52 a.m. UTC | #2
On Thu, 12 Nov 2020, Jan Hubicka wrote:

> Hi,
> this is updated patch.  It fixes the comparsion of bitfield where I now
> check that they bitsizes and bitoffsets match (and OEP_ADDRESSOF is not
> used for bitfield references).
> I also noticed problem with dependence clique in ao_refs_may_alias that
> I copied here.  Instead of base rbase should be used.
> 
> Finally I ran statistics on when access paths mismatches and noticed
> that I do not really need to check that component_refs and array_refs
> are semantically equivalent since this is implied from earlier tests.
> This is described in inline comment and simplifies the code.
> 
> Bootstrapped/regtested x86_64-linux, OK?

OK.

Thanks,
Richard.

> Honza
> 
> 
> 	* ipa-icf-gimple.c: Include tree-ssa-alias-compare.h.
> 	(find_checker::func_checker): Initialize m_tbaa.
> 	(func_checker::hash_operand): Use hash_ao_ref for memory accesses.
> 	(func_checker::compare_operand): Use compare_ao_refs for memory
> 	accesses.
> 	(func_checker::cmopare_gimple_assign): Do not check LHS types
> 	of memory stores.
> 	* ipa-icf-gimple.h (func_checker): Derive from ao_compare;
> 	add m_tbaa.
> 	* ipa-icf.c: Include tree-ssa-alias-compare.h.
> 	(sem_function::equals_private): Update call of
> 	func_checker::func_checker.
> 	* ipa-utils.h (lto_streaming_expected_p): New inline
> 	predicate.
> 	* tree-ssa-alias-compare.h: New file.
> 	* tree-ssa-alias.c: Include tree-ssa-alias-compare.h
> 	and bultins.h
> 	(view_converted_memref_p): New function.
> 	(types_equal_for_same_type_for_tbaa_p): New function.
> 	(ao_compare::compare_ao_refs): New member function.
> 	(ao_compare::hash_ao_ref): New function
> 
> 	* c-c++-common/Wstringop-overflow-2.c: Disable ICF.
> 	* g++.dg/warn/Warray-bounds-8.C: Disable ICF.
> 
> index f75951f7c49..26337dd7384 100644
> --- a/gcc/ipa-icf-gimple.c
> +++ b/gcc/ipa-icf-gimple.c
> @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "attribs.h"
>  #include "gimple-walk.h"
>  
> +#include "tree-ssa-alias-compare.h"
>  #include "ipa-icf-gimple.h"
>  
>  namespace ipa_icf_gimple {
> @@ -52,13 +53,13 @@ namespace ipa_icf_gimple {
>     of declarations that can be skipped.  */
>  
>  func_checker::func_checker (tree source_func_decl, tree target_func_decl,
> -			    bool ignore_labels,
> +			    bool ignore_labels, bool tbaa,
>  			    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_ignore_labels (ignore_labels)
> +    m_ignore_labels (ignore_labels), m_tbaa (tbaa)
>  {
>    function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
>    function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
> @@ -252,9 +253,16 @@ func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
>  
>  void
>  func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
> -			    unsigned int flags, operand_access_type)
> +			    unsigned int flags, operand_access_type access)
>  {
> -  return hash_operand (arg, hstate, flags);
> +  if (access == OP_MEMORY)
> +    {
> +      ao_ref ref;
> +      ao_ref_init (&ref, const_cast <tree> (arg));
> +      return hash_ao_ref (&ref, lto_streaming_expected_p (), m_tbaa, hstate);
> +    }
> +  else
> +    return hash_operand (arg, hstate, flags);
>  }
>  
>  bool
> @@ -314,18 +322,40 @@ func_checker::compare_operand (tree t1, tree t2, operand_access_type access)
>      return true;
>    else if (!t1 || !t2)
>      return false;
> -  if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
> -    return true;
> -  switch (access)
> +  if (access == OP_MEMORY)
>      {
> -    case OP_MEMORY:
> -      return return_false_with_msg
> -		 ("operand_equal_p failed (access == memory)");
> -    case OP_NORMAL:
> +      ao_ref ref1, ref2;
> +      ao_ref_init (&ref1, const_cast <tree> (t1));
> +      ao_ref_init (&ref2, const_cast <tree> (t2));
> +      int flags = compare_ao_refs (&ref1, &ref2,
> +				   lto_streaming_expected_p (), m_tbaa);
> +
> +      if (!flags)
> +	return true;
> +      if (flags & SEMANTICS)
> +	return return_false_with_msg
> +		("compare_ao_refs failed (semantic difference)");
> +      if (flags & BASE_ALIAS_SET)
> +	return return_false_with_msg
> +		("compare_ao_refs failed (base alias set difference)");
> +      if (flags & REF_ALIAS_SET)
> +	return return_false_with_msg
> +		 ("compare_ao_refs failed (ref alias set difference)");
> +      if (flags & ACCESS_PATH)
> +	return return_false_with_msg
> +		 ("compare_ao_refs failed (access path difference)");
> +      if (flags & DEPENDENCE_CLIQUE)
> +	return return_false_with_msg
> +		 ("compare_ao_refs failed (dependence clique difference)");
> +      gcc_unreachable ();
> +    }
> +  else
> +    {
> +      if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
> +	return true;
>        return return_false_with_msg
> -		 ("operand_equal_p failed (access == normal)");
> +		 ("operand_equal_p failed");
>      }
> -  gcc_unreachable ();
>  }
>  
>  bool
> @@ -593,10 +623,17 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2)
>  
>    tree fntype1 = gimple_call_fntype (s1);
>    tree fntype2 = gimple_call_fntype (s2);
> -  if ((fntype1 && !fntype2)
> -      || (!fntype1 && fntype2)
> -      || (fntype1 && !types_compatible_p (fntype1, fntype2)))
> -    return return_false_with_msg ("call function types are not compatible");
> +
> +  /* For direct calls we verify that types are comopatible so if we matced
> +     callees, callers must match, too.  For indirect calls however verify
> +     function type.  */
> +  if (!gimple_call_fndecl (s1))
> +    {
> +      if ((fntype1 && !fntype2)
> +	  || (!fntype1 && fntype2)
> +	  || (fntype1 && !types_compatible_p (fntype1, fntype2)))
> +	return return_false_with_msg ("call function types are not compatible");
> +    }
>  
>    if (fntype1 && fntype2 && comp_type_attributes (fntype1, fntype2) != 1)
>      return return_false_with_msg ("different fntype attributes");
> @@ -658,10 +695,10 @@ func_checker::compare_gimple_assign (gimple *s1, gimple *s2)
>        arg2 = gimple_op (s2, i);
>  
>        /* Compare types for LHS.  */
> -      if (i == 0)
> +      if (i == 0 && !gimple_store_p (s1))
>  	{
>  	  if (!compatible_types_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
> -	    return return_false_with_msg ("GIMPLE NOP LHS type mismatch");
> +	    return return_false_with_msg ("GIMPLE LHS type mismatch");
>  	}
>  
>        if (!compare_operand (arg1, arg2, get_operand_access_type (&map, arg1)))
> @@ -889,7 +926,7 @@ func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2)
>  /* Helper for func_checker::classify_operands.  Record that T is a load.  */
>  
>  static bool
> -visit_load_store (gimple *, tree t, tree, void *data)
> +visit_load_store (gimple *, tree, tree t, void *data)
>  {
>    func_checker::operand_access_type_map *map =
>      (func_checker::operand_access_type_map *) data;
> diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
> index f251d08100f..dd4ef13c716 100644
> --- a/gcc/ipa-icf-gimple.h
> +++ b/gcc/ipa-icf-gimple.h
> @@ -118,14 +118,14 @@ public:
>  
>  /* A class aggregating all connections and semantic equivalents
>     for a given pair of semantic function candidates.  */
> -class func_checker : operand_compare
> +class func_checker : ao_compare
>  {
>  public:
>    /* Default constructor.  */
>    func_checker ():
>      m_source_func_decl (NULL_TREE), m_target_func_decl (NULL_TREE),
>      m_ignored_source_nodes (NULL), m_ignored_target_nodes (NULL),
> -    m_ignore_labels (false)
> +    m_ignore_labels (false), m_tbaa (true)
>    {
>      m_source_ssa_names.create (0);
>      m_target_ssa_names.create (0);
> @@ -139,6 +139,7 @@ public:
>       of declarations that can be skipped.  */
>    func_checker (tree source_func_decl, tree target_func_decl,
>  		bool ignore_labels = false,
> +		bool tbaa = true,
>  		hash_set<symtab_node *> *ignored_source_nodes = NULL,
>  		hash_set<symtab_node *> *ignored_target_nodes = NULL);
>  
> @@ -275,6 +276,9 @@ private:
>    /* Flag if ignore labels in comparison.  */
>    bool m_ignore_labels;
>  
> +  /* Flag if we should compare type based alias analysis info.  */
> +  bool m_tbaa;
> +
>  public:
>    /* Return true if two operands are equal.  The flags fields can be used
>       to specify OEP flags described above.  */
> diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
> index 83f9786b4b2..a283195a487 100644
> --- a/gcc/ipa-icf.c
> +++ b/gcc/ipa-icf.c
> @@ -78,6 +78,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "attribs.h"
>  #include "print-tree.h"
>  #include "ipa-utils.h"
> +#include "tree-ssa-alias-compare.h"
>  #include "ipa-icf-gimple.h"
>  #include "fibonacci_heap.h"
>  #include "ipa-icf.h"
> @@ -843,6 +844,8 @@ sem_function::equals_private (sem_item *item)
>  
>    m_checker = new func_checker (decl, m_compared_func->decl,
>  				false,
> +				opt_for_fn (m_compared_func->decl,
> +					    flag_strict_aliasing),
>  				&refs_set,
>  				&m_compared_func->refs_set);
>    arg1 = DECL_ARGUMENTS (decl);
> diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
> index 178c2cbe446..880e527c590 100644
> --- a/gcc/ipa-utils.h
> +++ b/gcc/ipa-utils.h
> @@ -265,4 +265,16 @@ get_odr_name_for_type (tree type)
>    return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (type_name));
>  }
>  
> +/* Return true if we are going to do LTO streaming.  */
> +
> +inline bool
> +lto_streaming_expected_p ()
> +{
> +  /* Compilation before LTO stremaing.  */
> +  if (flag_lto && !in_lto_p && symtab->state < IPA_SSA_AFTER_INLINING)
> +    return true;
> +  /* WPA or incremental link.  */
> +  return (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO);
> +}
> +
>  #endif  /* GCC_IPA_UTILS_H  */
> diff --git a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
> index 7c7932e3cf0..63b1a309564 100644
> --- a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
> +++ b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
> @@ -1,7 +1,7 @@
>  /* PR middle-end/91458 - inconsistent warning for writing past the end
>     of an array member
>     { dg-do compile }
> -   { dg-options "-O2 -Wall -Wno-array-bounds" } */
> +   { dg-options "-O2 -Wall -Wno-array-bounds -fno-ipa-icf" } */
>  
>  void sink (void*);
>  
> diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
> index 6db20135668..6e0d7f3ccc4 100644
> --- a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
> +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
> @@ -3,7 +3,7 @@
>     See Wstringop-overflow-3.C for the same test that exercises the other
>     warning.
>     { dg-do compile }
> -   { dg-options "-O2 -Wall -Wno-stringop-overflow" }
> +   { dg-options "-O2 -Wall -Wno-stringop-overflow -fno-ipa-icf" }
>     { dg-skip-if "" { *-*-aix* } } */
>  
>  void sink (void*);
> diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
> index ae66bd1eafe..3b1c4b2c1f7 100644
> --- a/gcc/tree-ssa-alias.c
> +++ b/gcc/tree-ssa-alias.c
> @@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
>  #include "dbgcnt.h"
>  #include "gimple-pretty-print.h"
>  #include "print-tree.h"
> +#include "tree-ssa-alias-compare.h"
> +#include "builtins.h"
>  
>  /* Broad overview of how alias analysis on gimple works:
>  
> @@ -1205,7 +1207,7 @@ aliasing_component_refs_p (tree ref1,
>  	   struct a {int array1[0]; int array[];};
>  	 Such struct has size 0 but accesses to a.array may have non-zero size.
>  	 In this case the size of TREE_TYPE (base1) is smaller than
> -	 size of TREE_TYPE (TREE_OPERNAD (base1, 0)).
> +	 size of TREE_TYPE (TREE_OPERAND (base1, 0)).
>  
>  	 Because we compare sizes of arrays just by sizes of their elements,
>  	 we only need to care about zero sized array fields here.  */
> @@ -1980,6 +1982,20 @@ decl_refs_may_alias_p (tree ref1, tree base1,
>    return true;     
>  }
>  
> +/* Return true if access with BASE is view converted.
> +   Base must not be stripped from inner MEM_REF (&decl)
> +   which is done by ao_ref_base and thus one extra walk
> +   of handled components is needed.  */
> +
> +static bool
> +view_converted_memref_p (tree base)
> +{
> +  if (TREE_CODE (base) != MEM_REF && TREE_CODE (base) != TARGET_MEM_REF)
> +    return false;
> +  return same_type_for_tbaa (TREE_TYPE (base),
> +			     TREE_TYPE (TREE_OPERAND (base, 1))) != 1;
> +}
> +
>  /* Return true if an indirect reference based on *PTR1 constrained
>     to [OFFSET1, OFFSET1 + MAX_SIZE1) may alias a variable based on BASE2
>     constrained to [OFFSET2, OFFSET2 + MAX_SIZE2).  *PTR1 and BASE2 have
> @@ -3870,3 +3886,329 @@ attr_fnspec::verify ()
>  	internal_error ("invalid fn spec attribute \"%s\" arg %i", str, i);
>      }
>  }
> +
> +/* Return ture if TYPE1 and TYPE2 will always give the same answer
> +   when compared wit hother types using same_type_for_tbaa_p.  */
> +
> +static bool
> +types_equal_for_same_type_for_tbaa_p (tree type1, tree type2,
> +				      bool lto_streaming_safe)
> +{
> +  /* We use same_type_for_tbaa_p to match types in the access path.
> +     This check is overly conservative.  */
> +  type1 = TYPE_MAIN_VARIANT (type1);
> +  type2 = TYPE_MAIN_VARIANT (type2);
> +
> +  if (TYPE_STRUCTURAL_EQUALITY_P (type1)
> +      != TYPE_STRUCTURAL_EQUALITY_P (type2))
> +    return false;
> +  if (TYPE_STRUCTURAL_EQUALITY_P (type1))
> +    return true;
> +
> +  if (lto_streaming_safe)
> +    return type1 == type2;
> +  else
> +    return TYPE_CANONICAL (type1) == TYPE_CANONICAL (type2);
> +}
> +
> +/* Compare REF1 and REF2 and return flags specifying their differences.
> +   If LTO_STREAMING_SAFE is true do not use alias sets and canonical
> +   types that are going to be recomputed.
> +   If TBAA is true also compare TBAA metadata.  */
> +
> +int
> +ao_compare::compare_ao_refs (ao_ref *ref1, ao_ref *ref2,
> +			     bool lto_streaming_safe,
> +			     bool tbaa)
> +{
> +  if (TREE_THIS_VOLATILE (ref1->ref) != TREE_THIS_VOLATILE (ref2->ref))
> +    return SEMANTICS;
> +  tree base1 = ao_ref_base (ref1);
> +  tree base2 = ao_ref_base (ref2);
> +
> +  if (!known_eq (ref1->offset, ref2->offset)
> +      || !known_eq (ref1->size, ref2->size)
> +      || !known_eq (ref1->max_size, ref2->max_size))
> +    return SEMANTICS;
> +
> +  /* For variable accesses we need to compare actual paths
> +     to check that both refs are accessing same address and the access size.  */
> +  if (!known_eq (ref1->size, ref1->max_size))
> +    {
> +      if (!operand_equal_p (TYPE_SIZE (TREE_TYPE (ref1->ref)),
> +			    TYPE_SIZE (TREE_TYPE (ref2->ref)), 0))
> +	return SEMANTICS;
> +      tree r1 = ref1->ref;
> +      tree r2 = ref2->ref;
> +
> +      /* Handle toplevel COMPONENT_REFs of bitfields.
> +	 Those are special since they are not allowed in
> +	 ADDR_EXPR.  */
> +      if (TREE_CODE (r1) == COMPONENT_REF
> +	  && DECL_BIT_FIELD (TREE_OPERAND (r1, 1)))
> +	{
> +	  if (TREE_CODE (r2) != COMPONENT_REF
> +	      || !DECL_BIT_FIELD (TREE_OPERAND (r2, 1)))
> +	    return SEMANTICS;
> +	  tree field1 = TREE_OPERAND (r1, 1);
> +	  tree field2 = TREE_OPERAND (r2, 1);
> +	  if (!operand_equal_p (DECL_FIELD_OFFSET (field1),
> +				DECL_FIELD_OFFSET (field2), 0)
> +	      || !operand_equal_p (DECL_FIELD_BIT_OFFSET (field1),
> +				   DECL_FIELD_BIT_OFFSET (field2), 0)
> +	      || !operand_equal_p (DECL_SIZE (field1), DECL_SIZE (field2), 0)
> +	      || !types_compatible_p (TREE_TYPE (r1),
> +				      TREE_TYPE (r2)))
> +	    return SEMANTICS;
> +	  r1 = TREE_OPERAND (r1, 0);
> +	  r2 = TREE_OPERAND (r2, 0);
> +	}
> +      else if (TREE_CODE (r2) == COMPONENT_REF
> +	       && DECL_BIT_FIELD (TREE_OPERAND (r2, 1)))
> +	return SEMANTICS;
> +
> +      /* Similarly for bit field refs.  */
> +      if (TREE_CODE (r1) == BIT_FIELD_REF)
> +	{
> + 	  if (TREE_CODE (r2) != BIT_FIELD_REF
> +	      || !operand_equal_p (TREE_OPERAND (r1, 1),
> +				   TREE_OPERAND (r2, 1), 0)
> +	      || !operand_equal_p (TREE_OPERAND (r1, 2),
> +				   TREE_OPERAND (r2, 2), 0)
> +	      || !types_compatible_p (TREE_TYPE (r1),
> +				      TREE_TYPE (r2)))
> +	    return SEMANTICS;
> +	  r1 = TREE_OPERAND (r1, 0);
> +	  r2 = TREE_OPERAND (r2, 0);
> +	}
> +      else if (TREE_CODE (r2) == BIT_FIELD_REF)
> +	return SEMANTICS;
> +
> +      /* Now we can compare the address of actual memory access.  */
> +      if (!operand_equal_p (r1, r2, OEP_ADDRESS_OF))
> +	return SEMANTICS;
> +    }
> +  /* For constant accesses we get more matches by comparing offset only.  */
> +  else if (!operand_equal_p (base1, base2, OEP_ADDRESS_OF))
> +    return SEMANTICS;
> +
> +  /* We can't simply use get_object_alignment_1 on the full
> +     reference as for accesses with variable indexes this reports
> +     too conservative alignment.  */
> +  unsigned int align1, align2;
> +  unsigned HOST_WIDE_INT bitpos1, bitpos2;
> +  bool known1 = get_object_alignment_1 (base1, &align1, &bitpos1);
> +  bool known2 = get_object_alignment_1 (base2, &align2, &bitpos2);
> +  /* ??? For MEMREF get_object_alignment_1 determines aligned from
> +     TYPE_ALIGN but still returns false.  This seem to contradict
> +     its description.  So compare even if alignment is unknown.   */
> +  if (known1 != known2
> +      || (bitpos1 != bitpos2 || align1 != align2))
> +    return SEMANTICS;
> +
> +  /* Now we know that accesses are semantically same.  */
> +  int flags = 0;
> +
> +  /* ao_ref_base strips inner MEM_REF [&decl], recover from that here.  */
> +  tree rbase1 = ref1->ref;
> +  if (rbase1)
> +    while (handled_component_p (rbase1))
> +      rbase1 = TREE_OPERAND (rbase1, 0);
> +  tree rbase2 = ref2->ref;
> +  while (handled_component_p (rbase2))
> +    rbase2 = TREE_OPERAND (rbase2, 0);
> +
> +  /* MEM_REFs and TARGET_MEM_REFs record dependence cliques which are used to
> +     implement restrict pointers.  MR_DEPENDENCE_CLIQUE 0 means no information.
> +     Otherwise we need to match bases and cliques.  */
> +  if ((((TREE_CODE (rbase1) == MEM_REF || TREE_CODE (rbase1) == TARGET_MEM_REF)
> +	&& MR_DEPENDENCE_CLIQUE (rbase1))
> +       || ((TREE_CODE (rbase2) == MEM_REF || TREE_CODE (rbase2) == TARGET_MEM_REF)
> +	   && MR_DEPENDENCE_CLIQUE (rbase2)))
> +      && (TREE_CODE (rbase1) != TREE_CODE (rbase2)
> +	  || MR_DEPENDENCE_CLIQUE (rbase1) != MR_DEPENDENCE_CLIQUE (rbase2)
> +	  || (MR_DEPENDENCE_BASE (rbase1) != MR_DEPENDENCE_BASE (rbase2))))
> +    flags |= DEPENDENCE_CLIQUE;
> +
> +  if (!tbaa)
> +    return flags;
> +
> +  /* Alias sets are not stable across LTO sreaming; be conservative here
> +     and compare types the alias sets are ultimately based on.  */
> +  if (lto_streaming_safe)
> +    {
> +      tree t1 = ao_ref_alias_ptr_type (ref1);
> +      tree t2 = ao_ref_alias_ptr_type (ref2);
> +      if (!alias_ptr_types_compatible_p (t1, t2))
> +	flags |= REF_ALIAS_SET;
> +
> +      t1 = ao_ref_base_alias_ptr_type (ref1);
> +      t2 = ao_ref_base_alias_ptr_type (ref2);
> +      if (!alias_ptr_types_compatible_p (t1, t2))
> +	flags |= BASE_ALIAS_SET;
> +    }
> +  else
> +    {
> +      if (ao_ref_alias_set (ref1) != ao_ref_alias_set (ref2))
> +	flags |= REF_ALIAS_SET;
> +      if (ao_ref_base_alias_set (ref1) != ao_ref_base_alias_set (ref2))
> +	flags |= BASE_ALIAS_SET;
> +    }
> +
> +  /* Access path is used only on non-view-converted references.  */
> +  bool view_converted = view_converted_memref_p (rbase1);
> +  if (view_converted_memref_p (rbase2) != view_converted)
> +    return flags | ACCESS_PATH;
> +  else if (view_converted)
> +    return flags;
> +
> +
> +  /* Find start of access paths and look for trailing arrays.  */
> +  tree c1 = ref1->ref, c2 = ref2->ref;
> +  tree end_struct_ref1 = NULL, end_struct_ref2 = NULL;
> +  int nskipped1 = 0, nskipped2 = 0;
> +  int i = 0;
> +
> +  for (tree p1 = ref1->ref; handled_component_p (p1); p1 = TREE_OPERAND (p1, 0))
> +    {
> +      if (component_ref_to_zero_sized_trailing_array_p (p1))
> +	end_struct_ref1 = p1;
> +      if (ends_tbaa_access_path_p (p1))
> +	c1 = p1, nskipped1 = i;
> +      i++;
> +    }
> +  for (tree p2 = ref2->ref; handled_component_p (p2); p2 = TREE_OPERAND (p2, 0))
> +    {
> +      if (component_ref_to_zero_sized_trailing_array_p (p2))
> +	end_struct_ref2 = p2;
> +      if (ends_tbaa_access_path_p (p2))
> +	c2 = p2, nskipped1 = i;
> +      i++;
> +    }
> +
> +  /* For variable accesses we can not rely on offset match bellow.
> +     We know that paths are struturally same, so only check that
> +     starts of TBAA paths did not diverge.  */
> +  if (!known_eq (ref1->size, ref1->max_size)
> +      && nskipped1 != nskipped2)
> +    return flags | ACCESS_PATH;
> +
> +  /* Information about trailing refs is used by
> +     aliasing_component_refs_p that is applied only if paths
> +     has handled components..  */
> +  if (!handled_component_p (c1) && !handled_component_p (c2))
> +    ;
> +  else if ((end_struct_ref1 != NULL) != (end_struct_ref2 != NULL))
> +    return flags | ACCESS_PATH;
> +  if (end_struct_ref1
> +      && TYPE_MAIN_VARIANT (TREE_TYPE (end_struct_ref1))
> +	 != TYPE_MAIN_VARIANT (TREE_TYPE (end_struct_ref2)))
> +    return flags | ACCESS_PATH;
> +
> +  /* Now compare all handled components of the access path.
> +     We have three oracles that cares about access paths:
> +       - aliasing_component_refs_p
> +       - nonoverlapping_refs_since_match_p
> +       - nonoverlapping_component_refs_p
> +     We need to match things these oracles compare.
> +
> +     It is only necessary to check types for compatibility
> +     and offsets.  Rest of what oracles compares are actual
> +     addresses.  Those are already known to be same:
> +       - for constant accesses we check offsets
> +       - for variable accesses we already matched
> +	 the path lexically with operand_equal_p.  */
> +  while (true)
> +    {
> +      bool comp1 = handled_component_p (c1);
> +      bool comp2 = handled_component_p (c2);
> +
> +      if (comp1 != comp2)
> +	return flags | ACCESS_PATH;
> +      if (!comp1)
> +	break;
> +
> +      if (TREE_CODE (c1) != TREE_CODE (c2))
> +	return flags | ACCESS_PATH;
> +
> +      /* aliasing_component_refs_p attempts to find type match within
> +	 the paths.  For that reason both types needs to be equal
> +	 with respect to same_type_for_tbaa_p.  */
> +      if (!types_equal_for_same_type_for_tbaa_p (TREE_TYPE (c1),
> +						 TREE_TYPE (c2),
> +						 lto_streaming_safe))
> +	return flags | ACCESS_PATH;
> +      if (component_ref_to_zero_sized_trailing_array_p (c1)
> +	  != component_ref_to_zero_sized_trailing_array_p (c2))
> +	return flags | ACCESS_PATH;
> +
> +      /* aliasing_matching_component_refs_p compares
> +	 offsets within the path.  Other properties are ignored.
> +	 Do not bother to verify offsets in variable accesses.  Here we
> +	 already compared them by operand_equal_p so they are
> +	 structurally same.  */
> +      if (!known_eq (ref1->size, ref1->max_size))
> +	{
> +	  poly_int64 offadj1, sztmc1, msztmc1;
> +	  bool reverse1;
> +	  get_ref_base_and_extent (c1, &offadj1, &sztmc1, &msztmc1, &reverse1);
> +	  poly_int64 offadj2, sztmc2, msztmc2;
> +	  bool reverse2;
> +	  get_ref_base_and_extent (c2, &offadj2, &sztmc2, &msztmc2, &reverse2);
> +	  if (!known_eq (offadj1, offadj2))
> +	    return flags | ACCESS_PATH;
> +	}
> +      c1 = TREE_OPERAND (c1, 0);
> +      c2 = TREE_OPERAND (c2, 0);
> +    }
> +  /* Finally test the access type.  */
> +  if (!types_equal_for_same_type_for_tbaa_p (TREE_TYPE (c1),
> +					     TREE_TYPE (c2),
> +					     lto_streaming_safe))
> +    return flags | ACCESS_PATH;
> +  return flags;
> +}
> +
> +/* Hash REF to HSTATE.  If LTO_STREAMING_SAFE do not use alias sets
> +   and canonical types.  */
> +void
> +ao_compare::hash_ao_ref (ao_ref *ref, bool lto_streaming_safe, bool tbaa,
> +			 inchash::hash &hstate)
> +{
> +  tree base = ao_ref_base (ref);
> +  tree tbase = base;
> +
> +  if (!known_eq (ref->size, ref->max_size))
> +    {
> +      tree r = ref->ref;
> +      if (TREE_CODE (r) == COMPONENT_REF
> +	  && DECL_BIT_FIELD (TREE_OPERAND (r, 1)))
> +	{
> +	  tree field = TREE_OPERAND (r, 1);
> +	  hash_operand (DECL_FIELD_OFFSET (field), hstate, 0);
> +	  hash_operand (DECL_FIELD_BIT_OFFSET (field), hstate, 0);
> +	  hash_operand (DECL_SIZE (field), hstate, 0);
> +	  r = TREE_OPERAND (r, 0);
> +	}
> +      if (TREE_CODE (r) == BIT_FIELD_REF)
> +	{
> +	  hash_operand (TREE_OPERAND (r, 1), hstate, 0);
> +	  hash_operand (TREE_OPERAND (r, 2), hstate, 0);
> +	  r = TREE_OPERAND (r, 0);
> +	}
> +      hash_operand (TYPE_SIZE (TREE_TYPE (ref->ref)), hstate, 0);
> +      hash_operand (r, hstate, OEP_ADDRESS_OF);
> +    }
> +  else
> +    {
> +      hash_operand (tbase, hstate, OEP_ADDRESS_OF);
> +      hstate.add_poly_int (ref->offset);
> +      hstate.add_poly_int (ref->size);
> +      hstate.add_poly_int (ref->max_size);
> +    }
> +  if (!lto_streaming_safe && tbaa)
> +    {
> +      hstate.add_int (ao_ref_alias_set (ref));
> +      hstate.add_int (ao_ref_base_alias_set (ref));
> +    }
> +}
>
diff mbox series

Patch

diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
index f75951f7c49..26337dd7384 100644
--- a/gcc/ipa-icf-gimple.c
+++ b/gcc/ipa-icf-gimple.c
@@ -40,6 +40,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "gimple-walk.h"
 
+#include "tree-ssa-alias-compare.h"
 #include "ipa-icf-gimple.h"
 
 namespace ipa_icf_gimple {
@@ -52,13 +53,13 @@  namespace ipa_icf_gimple {
    of declarations that can be skipped.  */
 
 func_checker::func_checker (tree source_func_decl, tree target_func_decl,
-			    bool ignore_labels,
+			    bool ignore_labels, bool tbaa,
 			    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_ignore_labels (ignore_labels)
+    m_ignore_labels (ignore_labels), m_tbaa (tbaa)
 {
   function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
   function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
@@ -252,9 +253,16 @@  func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
 
 void
 func_checker::hash_operand (const_tree arg, inchash::hash &hstate,
-			    unsigned int flags, operand_access_type)
+			    unsigned int flags, operand_access_type access)
 {
-  return hash_operand (arg, hstate, flags);
+  if (access == OP_MEMORY)
+    {
+      ao_ref ref;
+      ao_ref_init (&ref, const_cast <tree> (arg));
+      return hash_ao_ref (&ref, lto_streaming_expected_p (), m_tbaa, hstate);
+    }
+  else
+    return hash_operand (arg, hstate, flags);
 }
 
 bool
@@ -314,18 +322,40 @@  func_checker::compare_operand (tree t1, tree t2, operand_access_type access)
     return true;
   else if (!t1 || !t2)
     return false;
-  if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
-    return true;
-  switch (access)
+  if (access == OP_MEMORY)
     {
-    case OP_MEMORY:
-      return return_false_with_msg
-		 ("operand_equal_p failed (access == memory)");
-    case OP_NORMAL:
+      ao_ref ref1, ref2;
+      ao_ref_init (&ref1, const_cast <tree> (t1));
+      ao_ref_init (&ref2, const_cast <tree> (t2));
+      int flags = compare_ao_refs (&ref1, &ref2,
+				   lto_streaming_expected_p (), m_tbaa);
+
+      if (!flags)
+	return true;
+      if (flags & SEMANTICS)
+	return return_false_with_msg
+		("compare_ao_refs failed (semantic difference)");
+      if (flags & BASE_ALIAS_SET)
+	return return_false_with_msg
+		("compare_ao_refs failed (base alias set difference)");
+      if (flags & REF_ALIAS_SET)
+	return return_false_with_msg
+		 ("compare_ao_refs failed (ref alias set difference)");
+      if (flags & ACCESS_PATH)
+	return return_false_with_msg
+		 ("compare_ao_refs failed (access path difference)");
+      if (flags & DEPENDENCE_CLIQUE)
+	return return_false_with_msg
+		 ("compare_ao_refs failed (dependence clique difference)");
+      gcc_unreachable ();
+    }
+  else
+    {
+      if (operand_equal_p (t1, t2, OEP_MATCH_SIDE_EFFECTS))
+	return true;
       return return_false_with_msg
-		 ("operand_equal_p failed (access == normal)");
+		 ("operand_equal_p failed");
     }
-  gcc_unreachable ();
 }
 
 bool
@@ -658,10 +695,10 @@  func_checker::compare_gimple_assign (gimple *s1, gimple *s2)
       arg2 = gimple_op (s2, i);
 
       /* Compare types for LHS.  */
-      if (i == 0)
+      if (i == 0 && !gimple_store_p (s1))
 	{
 	  if (!compatible_types_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
-	    return return_false_with_msg ("GIMPLE NOP LHS type mismatch");
+	    return return_false_with_msg ("GIMPLE LHS type mismatch");
 	}
 
       if (!compare_operand (arg1, arg2, get_operand_access_type (&map, arg1)))
diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h
index f251d08100f..dd4ef13c716 100644
--- a/gcc/ipa-icf-gimple.h
+++ b/gcc/ipa-icf-gimple.h
@@ -118,14 +118,14 @@  public:
 
 /* A class aggregating all connections and semantic equivalents
    for a given pair of semantic function candidates.  */
-class func_checker : operand_compare
+class func_checker : ao_compare
 {
 public:
   /* Default constructor.  */
   func_checker ():
     m_source_func_decl (NULL_TREE), m_target_func_decl (NULL_TREE),
     m_ignored_source_nodes (NULL), m_ignored_target_nodes (NULL),
-    m_ignore_labels (false)
+    m_ignore_labels (false), m_tbaa (true)
   {
     m_source_ssa_names.create (0);
     m_target_ssa_names.create (0);
@@ -139,6 +139,7 @@  public:
      of declarations that can be skipped.  */
   func_checker (tree source_func_decl, tree target_func_decl,
 		bool ignore_labels = false,
+		bool tbaa = true,
 		hash_set<symtab_node *> *ignored_source_nodes = NULL,
 		hash_set<symtab_node *> *ignored_target_nodes = NULL);
 
@@ -275,6 +276,9 @@  private:
   /* Flag if ignore labels in comparison.  */
   bool m_ignore_labels;
 
+  /* Flag if we should compare type based alias analysis info.  */
+  bool m_tbaa;
+
 public:
   /* Return true if two operands are equal.  The flags fields can be used
      to specify OEP flags described above.  */
diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c
index 83f9786b4b2..a283195a487 100644
--- a/gcc/ipa-icf.c
+++ b/gcc/ipa-icf.c
@@ -78,6 +78,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "print-tree.h"
 #include "ipa-utils.h"
+#include "tree-ssa-alias-compare.h"
 #include "ipa-icf-gimple.h"
 #include "fibonacci_heap.h"
 #include "ipa-icf.h"
@@ -843,6 +844,8 @@  sem_function::equals_private (sem_item *item)
 
   m_checker = new func_checker (decl, m_compared_func->decl,
 				false,
+				opt_for_fn (m_compared_func->decl,
+					    flag_strict_aliasing),
 				&refs_set,
 				&m_compared_func->refs_set);
   arg1 = DECL_ARGUMENTS (decl);
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 178c2cbe446..880e527c590 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -265,4 +265,16 @@  get_odr_name_for_type (tree type)
   return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (type_name));
 }
 
+/* Return true if we are going to do LTO streaming.  */
+
+inline bool
+lto_streaming_expected_p ()
+{
+  /* Compilation before LTO stremaing.  */
+  if (flag_lto && !in_lto_p && symtab->state < IPA_SSA_AFTER_INLINING)
+    return true;
+  /* WPA or incremental link.  */
+  return (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO);
+}
+
 #endif  /* GCC_IPA_UTILS_H  */
diff --git a/gcc/tree-ssa-alias-compare.h b/gcc/tree-ssa-alias-compare.h
new file mode 100644
index 00000000000..0e8409a7565
--- /dev/null
+++ b/gcc/tree-ssa-alias-compare.h
@@ -0,0 +1,43 @@ 
+/* Comparsion of AO ref.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, 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/>.  */
+
+#ifndef TREE_SSA_ALIAS_COMPARE_H
+#define TREE_SSA_ALIAS_COMPARE_H
+
+class operand_compare;
+/* A class aggregating all connections and semantic equivalents
+   for a given pair of semantic function candidates.  */
+class ao_compare : public operand_compare
+{
+  public:
+  enum ao_ref_diff
+  {
+    SEMANTICS = 1,
+    BASE_ALIAS_SET = 2,
+    REF_ALIAS_SET = 4,
+    ACCESS_PATH = 8,
+    DEPENDENCE_CLIQUE = 16
+  };
+  int compare_ao_refs (ao_ref *ref1, ao_ref *ref2, bool lto_streaming_safe,
+		       bool tbaa);
+  void hash_ao_ref (ao_ref *ref, bool lto_streaming_safe, bool tbaa,
+		    inchash::hash &hstate);
+};
+
+#endif
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index baee352f2dc..06b3eb55181 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -45,6 +45,8 @@  along with GCC; see the file COPYING3.  If not see
 #include "dbgcnt.h"
 #include "gimple-pretty-print.h"
 #include "print-tree.h"
+#include "tree-ssa-alias-compare.h"
+#include "builtins.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -1980,6 +1982,20 @@  decl_refs_may_alias_p (tree ref1, tree base1,
   return true;     
 }
 
+/* Return true if access with BASE is view converted.
+   Base must not be stripped from inner MEM_REF (&decl)
+   which is done by ao_ref_base and thus one extra walk
+   of handled components is needed.  */
+
+static bool
+view_converted_memref_p (tree base)
+{
+  if (TREE_CODE (base) != MEM_REF && TREE_CODE (base) != TARGET_MEM_REF)
+    return false;
+  return same_type_for_tbaa (TREE_TYPE (base),
+			     TREE_TYPE (TREE_OPERAND (base, 1))) != 1;
+}
+
 /* Return true if an indirect reference based on *PTR1 constrained
    to [OFFSET1, OFFSET1 + MAX_SIZE1) may alias a variable based on BASE2
    constrained to [OFFSET2, OFFSET2 + MAX_SIZE2).  *PTR1 and BASE2 have
@@ -3861,3 +3877,273 @@  attr_fnspec::verify ()
   if (err)
     internal_error ("invalid fn spec attribute \"%s\"", str);
 }
+
+ /* Return true if type1 and type2 will always give same answers from
+    smae_type_for_tbaa_p when compared with other types.  */
+
+static bool
+types_equal_for_same_type_for_tbaa_p (tree type1, tree type2,
+				      bool lto_streaming_safe)
+{
+  /* We use same_type_for_tbaa_p to match types in the access path.
+     This check is overly conservative.  */
+  type1 = TYPE_MAIN_VARIANT (type1);
+  type2 = TYPE_MAIN_VARIANT (type2);
+
+  if (TYPE_STRUCTURAL_EQUALITY_P (type1)
+      != TYPE_STRUCTURAL_EQUALITY_P (type2))
+    return false;
+  if (TYPE_STRUCTURAL_EQUALITY_P (type1))
+    return true;
+
+  if (lto_streaming_safe)
+    return type1 == type2;
+  else
+    return TYPE_CANONICAL (type1) == TYPE_CANONICAL (type2);
+}
+
+/* Compare REF1 and REF2 and return flags specifying their differences.
+   If LTO_STREAMING_SAFE is true do not use alias sets and canonical
+   types that are going to be recomputed.
+   If TBAA is true also compare TBAA metadata.  */
+
+int
+ao_compare::compare_ao_refs (ao_ref *ref1, ao_ref *ref2,
+			     bool lto_streaming_safe,
+			     bool tbaa)
+{
+  if (TREE_THIS_VOLATILE (ref1->ref) != TREE_THIS_VOLATILE (ref2->ref))
+    return SEMANTICS;
+  tree base1 = ao_ref_base (ref1);
+  tree base2 = ao_ref_base (ref2);
+
+  if (!known_eq (ref1->offset, ref2->offset)
+      || !known_eq (ref1->size, ref2->size)
+      || !known_eq (ref1->max_size, ref2->max_size))
+    return SEMANTICS;
+
+  /* For variable accesses we need to compare actual paths
+     to check that both refs are accessing same address and the access size.  */
+  if (!known_eq (ref1->size, ref1->max_size)
+      && (!operand_equal_p (ref1->ref, ref2->ref, OEP_ADDRESS_OF)
+	  || !operand_equal_p (TYPE_SIZE (TREE_TYPE (ref1->ref)),
+			       TYPE_SIZE (TREE_TYPE (ref2->ref)), 0)))
+    return SEMANTICS;
+  /* For constant accesses we get more matches by comparing offset only.  */
+  else if (!operand_equal_p (base1, base2, OEP_ADDRESS_OF))
+    return SEMANTICS;
+
+  /* ao_ref_base strips inner MEM_REF [&decl], recover from that here.  */
+  tree rbase1 = ref1->ref;
+  if (rbase1)
+    while (handled_component_p (rbase1))
+      rbase1 = TREE_OPERAND (rbase1, 0);
+  tree rbase2 = ref2->ref;
+  while (handled_component_p (rbase2))
+    rbase2 = TREE_OPERAND (rbase2, 0);
+
+  /* We can't simply use get_object_alignment_1 on the full
+     reference as for accesses with variable indexes this reports
+     too conservative alignment.  */
+  unsigned int align1, align2;
+  unsigned HOST_WIDE_INT bitpos1, bitpos2;
+  bool known1 = get_object_alignment_1 (base1, &align1, &bitpos1);
+  bool known2 = get_object_alignment_1 (base2, &align2, &bitpos2);
+  /* ??? For MEMREF get_object_alignment_1 determines aligned from
+     TYPE_ALIGN but still returns false.  This seem to contradict
+     its description.  So compare even if alignment is unknown.   */
+  if (known1 != known2
+      || (bitpos1 != bitpos2 || align1 != align2))
+    return SEMANTICS;
+
+  /* Now we know that accesses are semantically same.  */
+  int flags = 0;
+
+  /* MEM_REFs and TARGET_MEM_REFs record dependence cliques which are used to
+     implement restrict pointers.  MR_DEPENDENCE_CLIQUE 0 means no information.
+     Otherwise we need to match bases and cliques.  */
+  if ((((TREE_CODE (base1) == MEM_REF || TREE_CODE (base1) == TARGET_MEM_REF)
+	&& MR_DEPENDENCE_CLIQUE (base1))
+       || ((TREE_CODE (base2) == MEM_REF || TREE_CODE (base2) == TARGET_MEM_REF)
+	   && MR_DEPENDENCE_CLIQUE (base2)))
+      && (TREE_CODE (base1) != TREE_CODE (base2)
+	  || MR_DEPENDENCE_CLIQUE (base1) != MR_DEPENDENCE_CLIQUE (base2)
+	  || (MR_DEPENDENCE_BASE (base1) != MR_DEPENDENCE_BASE (base2))))
+    flags |= DEPENDENCE_CLIQUE;
+
+  if (!tbaa)
+    return flags;
+
+  /* Alias sets are not stable across LTO sreaming; be conservative here
+     and compare types the alias sets are ultimately based on.  */
+  if (lto_streaming_safe)
+    {
+      tree t1 = ao_ref_alias_ptr_type (ref1);
+      tree t2 = ao_ref_alias_ptr_type (ref2);
+      if (!alias_ptr_types_compatible_p (t1, t2))
+	flags |= REF_ALIAS_SET;
+
+      t1 = ao_ref_base_alias_ptr_type (ref1);
+      t2 = ao_ref_base_alias_ptr_type (ref2);
+      if (!alias_ptr_types_compatible_p (t1, t2))
+	flags |= BASE_ALIAS_SET;
+    }
+  else
+    {
+      if (ao_ref_alias_set (ref1) != ao_ref_alias_set (ref2))
+	flags |= REF_ALIAS_SET;
+      if (ao_ref_base_alias_set (ref1) != ao_ref_base_alias_set (ref2))
+	flags |= BASE_ALIAS_SET;
+    }
+
+  /* Access path is used only on non-view-converted references.  */
+  bool view_converted = view_converted_memref_p (rbase1);
+  if (view_converted_memref_p (rbase2) != view_converted)
+    return flags | ACCESS_PATH;
+  else if (view_converted)
+    return flags;
+
+
+  /* Find start of access paths and look for trailing arrays.  */
+  tree c1 = ref1->ref, c2 = ref2->ref;
+  tree end_struct_ref1 = NULL, end_struct_ref2 = NULL;
+  int nskipped1 = 0, nskipped2 = 0;
+  int i = 0;
+
+  for (tree p1 = ref1->ref; handled_component_p (p1); p1 = TREE_OPERAND (p1, 0))
+    {
+      if (component_ref_to_zero_sized_trailing_array_p (p1))
+	end_struct_ref1 = p1;
+      if (ends_tbaa_access_path_p (p1))
+	c1 = p1, nskipped1 = i;
+      i++;
+    }
+  for (tree p2 = ref2->ref; handled_component_p (p2); p2 = TREE_OPERAND (p2, 0))
+    {
+      if (component_ref_to_zero_sized_trailing_array_p (p2))
+	end_struct_ref2 = p2;
+      if (ends_tbaa_access_path_p (p2))
+	c2 = p2, nskipped1 = i;
+      i++;
+    }
+
+  /* For variable accesses we can not rely on offset match bellow.
+     We know that paths are struturally same, so only check that
+     starts of TBAA paths did not diverge.  */
+  if (!known_eq (ref1->size, ref1->max_size)
+      && nskipped1 != nskipped2)
+    return flags | ACCESS_PATH;
+
+  /* Information about trailing refs is used by
+     aliasing_component_refs_p that is applied only if paths
+     has handled components..  */
+  if (!handled_component_p (c1) && !handled_component_p (c2))
+    ;
+  else if ((end_struct_ref1 != NULL) != (end_struct_ref2 != NULL))
+    return flags | ACCESS_PATH;
+  if (end_struct_ref1
+      && TYPE_MAIN_VARIANT (TREE_TYPE (end_struct_ref1))
+	 != TYPE_MAIN_VARIANT (TREE_TYPE (end_struct_ref2)))
+    return flags | ACCESS_PATH;
+
+  /* Now compare all handled components of the access path.
+     We have three oracles that cares about access paths:
+       - aliasing_component_refs_p
+       - nonoverlapping_refs_since_match_p
+       - nonoverlapping_component_refs_p
+     We need to match things these oracles compare.
+     Note that nonoverlapping_refs_since_match_p can also
+     analyze the remainder of access path for address computations
+     but these are already known to match and thus wee need to only
+     inspect the portion of access path relevant for TBAA.  */
+  while (true)
+    {
+      bool comp1 = handled_component_p (c1);
+      bool comp2 = handled_component_p (c2);
+
+      if (comp1 != comp2)
+	return flags | ACCESS_PATH;
+      if (!comp1)
+	break;
+
+      if (TREE_CODE (c1) != TREE_CODE (c2))
+	return flags | ACCESS_PATH;
+
+      /* aliasing_component_refs_p attempts to find type match within
+	 the paths.  For that reason both types needs to be equal
+	 with respect to same_type_for_tbaa_p.  */
+      if (!types_equal_for_same_type_for_tbaa_p (TREE_TYPE (c1),
+						 TREE_TYPE (c2),
+						 lto_streaming_safe))
+	return flags | ACCESS_PATH;
+      if (component_ref_to_zero_sized_trailing_array_p (c1)
+	  != component_ref_to_zero_sized_trailing_array_p (c2))
+	return flags | ACCESS_PATH;
+
+      /* aliasing_matching_component_refs_p compares
+	 offsets within the path.  Other properties are ignored.
+	 Do not bother to verify offsets in variable accesses.  Here we
+	 already compared them by operand_equal_p so they are
+	 structurally same.  */
+      if (!known_eq (ref1->size, ref1->max_size))
+	{
+	  poly_int64 offadj1, sztmc1, msztmc1;
+	  bool reverse1;
+	  get_ref_base_and_extent (c1, &offadj1, &sztmc1, &msztmc1, &reverse1);
+	  poly_int64 offadj2, sztmc2, msztmc2;
+	  bool reverse2;
+	  get_ref_base_and_extent (c2, &offadj2, &sztmc2, &msztmc2, &reverse2);
+	  if (!known_eq (offadj1, offadj2))
+	    return flags | ACCESS_PATH;
+	}
+
+      /* nonoverlapping_array_refs_p looks into offset computations done
+	 by array refs.  */
+      if (TREE_CODE (c1) == ARRAY_REF)
+	if (nonoverlapping_array_refs_p (c1, c2))
+	  return flags | ACCESS_PATH;
+      /* nonoverlapping_component_refs_p cares about offset coputations done
+	 by component refs.  */
+      if (TREE_CODE (c1) == COMPONENT_REF)
+	{
+	  tree field1 = TREE_OPERAND (c1, 1);
+	  tree field2 = TREE_OPERAND (c2, 1);
+
+	  if (nonoverlapping_component_refs_p_1 (field1, field2))
+	    return flags | ACCESS_PATH;
+	}
+      c1 = TREE_OPERAND (c1, 0);
+      c2 = TREE_OPERAND (c2, 0);
+    }
+  /* Finally test the access type.  */
+  if (!types_equal_for_same_type_for_tbaa_p (TREE_TYPE (c1),
+					     TREE_TYPE (c2),
+					     lto_streaming_safe))
+    return flags | ACCESS_PATH;
+  return flags;
+}
+
+/* Hash REF to HSTATE.  If LTO_STREAMING_SAFE do not use alias sets
+   and canonical types.  */
+void
+ao_compare::hash_ao_ref (ao_ref *ref, bool lto_streaming_safe, bool tbaa,
+			 inchash::hash &hstate)
+{
+  tree base = ao_ref_base (ref);
+  tree tbase = base;
+
+  if (!known_eq (ref->size, ref->max_size))
+    {
+      hash_operand (TYPE_SIZE (TREE_TYPE (ref->ref)), hstate, 0);
+      hash_operand (ref->ref, hstate, OEP_ADDRESS_OF);
+    }
+  else
+    {
+      hash_operand (tbase, hstate, OEP_ADDRESS_OF);
+      hstate.add_poly_int (ref->offset);
+      hstate.add_poly_int (ref->size);
+      hstate.add_poly_int (ref->max_size);
+    }
+  if (!lto_streaming_safe && tbaa)
+    {
+      hstate.add_int (ao_ref_alias_set (ref));
+      hstate.add_int (ao_ref_base_alias_set (ref));
+    }
+}
diff --git a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
index 7c7932e3cf0..63b1a309564 100644
--- a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
+++ b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
@@ -1,7 +1,7 @@ 
 /* PR middle-end/91458 - inconsistent warning for writing past the end
    of an array member
    { dg-do compile }
-   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+   { dg-options "-O2 -Wall -Wno-array-bounds -fno-ipa-icf" } */
 
 void sink (void*);
 
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
index 6db20135668..6e0d7f3ccc4 100644
--- a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
@@ -3,7 +3,7 @@ 
    See Wstringop-overflow-3.C for the same test that exercises the other
    warning.
    { dg-do compile }
-   { dg-options "-O2 -Wall -Wno-stringop-overflow" }
+   { dg-options "-O2 -Wall -Wno-stringop-overflow -fno-ipa-icf" }
    { dg-skip-if "" { *-*-aix* } } */
 
 void sink (void*);