Hi!

The following patch (except for the else if (!INTEGRAL_TYPE_P ) part is
(improved) code to fix up the ptr1 != ptr2 comparison handling. 
Unfortunately it looks like we don't really want to lower pointer equality
comparisons performed in integral type to normal pointer equality
comparisons for GCC 9, as it is too risky, so the following patch keeps
doing what we were doing for pointer comparisons, but for the comparisons
of addresses in integral types
1) fixes a bug, where for zero sized objects we'd happily optimize
(uintptr_t) &e[0] != (uintptr_t) &f[0] even for int e[0] = {}, f[0] = {}
2) improves the rest, mainly if the offset is different:
   - if one variable is automatic and the other one is global, we can fold
   - if one or both pointers are in the middle of objects they point to,
     it is fine too
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2019-01-14  Jakub Jelinek  <ja...@redhat.com>

        PR tree-optimization/88775
        * match.pd (cmp (convert1?@2 addr@0) (convert2? addr@1)): Optimize
        equal == 0 equality pointer comparisons some more if compared in
        integral types and either one points to an automatic var and the
        other to a global, or we can prove at least one points to the middle
        or both point to start or both point to end.

        * gcc.dg/tree-ssa/pr88775-1.c: New test.
        * gcc.dg/tree-ssa/pr88775-2.c: New test.

--- gcc/match.pd.jj     2019-01-12 12:20:56.190999044 +0100
+++ gcc/match.pd        2019-01-14 14:26:18.805750285 +0100
@@ -3896,6 +3896,52 @@ (define_operator_list COND_TERNARY
                    || TREE_CODE (base1) == SSA_NAME
                    || TREE_CODE (base1) == STRING_CST))
          equal = (base0 == base1);
+       if (equal == 0)
+        {
+          if (!DECL_P (base0) || !DECL_P (base1))
+            equal = 2;
+          else if (cmp != EQ_EXPR && cmp != NE_EXPR)
+            equal = 2;
+          /* If this is a pointer comparison, ignore for now even
+             valid equalities where one pointer is the offset zero
+             of one object and the other to one past end of another one.  */
+          else if (!INTEGRAL_TYPE_P (TREE_TYPE (@2)))
+            ;
+          /* Assume that automatic variables can't be adjacent to global
+             variables.  */
+          else if (is_global_var (base0) != is_global_var (base1))
+            ;
+          else
+            {
+              tree sz0 = DECL_SIZE_UNIT (base0);
+              tree sz1 = DECL_SIZE_UNIT (base1);
+              /* If sizes are unknown, e.g. VLA or not representable,
+                 punt.  */
+              if (!tree_fits_poly_int64_p (sz0)
+                  || !tree_fits_poly_int64_p (sz1))
+                equal = 2;
+              else
+                {
+                  poly_int64 size0 = tree_to_poly_int64 (sz0);
+                  poly_int64 size1 = tree_to_poly_int64 (sz1);
+                  /* If one offset is pointing (or could be) to the beginning
+                     of one object and the other is pointing to one past the
+                     last byte of the other object, punt.  */
+                  if (maybe_eq (off0, 0) && maybe_eq (off1, size1))
+                    equal = 2;
+                  else if (maybe_eq (off1, 0) && maybe_eq (off0, size0))
+                    equal = 2;
+                  /* If both offsets are the same, there are some cases
+                     we know that are ok.  Either if we know they aren't
+                     zero, or if we know both sizes are no zero.  */
+                  if (equal == 2
+                      && known_eq (off0, off1)
+                      && (known_ne (off0, 0)
+                          || (known_ne (size0, 0) && known_ne (size1, 0))))
+                    equal = 0;
+                }
+            }
+        }
      }
      (if (equal == 1
          && (cmp == EQ_EXPR || cmp == NE_EXPR
@@ -3918,16 +3964,12 @@ (define_operator_list COND_TERNARY
        { constant_boolean_node (known_ge (off0, off1), type); })
        (if (cmp == GT_EXPR && (known_gt (off0, off1) || known_le (off0, off1)))
        { constant_boolean_node (known_gt (off0, off1), type); }))
-      (if (equal == 0
-          && DECL_P (base0) && DECL_P (base1)
-          /* If we compare this as integers require equal offset.  */
-          && (!INTEGRAL_TYPE_P (TREE_TYPE (@2))
-              || known_eq (off0, off1)))
-       (switch
-       (if (cmp == EQ_EXPR)
-        { constant_boolean_node (false, type); })
-       (if (cmp == NE_EXPR)
-        { constant_boolean_node (true, type); })))))))))
+      (if (equal == 0)
+       (switch
+        (if (cmp == EQ_EXPR)
+         { constant_boolean_node (false, type); })
+        (if (cmp == NE_EXPR)
+         { constant_boolean_node (true, type); })))))))))
 
 /* Simplify pointer equality compares using PTA.  */
 (for neeq (ne eq)
--- gcc/testsuite/gcc.dg/tree-ssa/pr88775-1.c.jj        2019-01-14 
13:18:09.332006700 +0100
+++ gcc/testsuite/gcc.dg/tree-ssa/pr88775-1.c   2019-01-14 14:23:24.007622756 
+0100
@@ -0,0 +1,73 @@
+/* PR tree-optimization/88775 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times "return 1;" 10 "optimized" } } */
+
+int a[64] = {};
+int b[64] = {};
+
+int
+f1 (void)
+{
+  return (__UINTPTR_TYPE__) &a[2] != (__UINTPTR_TYPE__) &b[2];
+}
+
+int
+f2 (void)
+{
+  return (__UINTPTR_TYPE__) &a[2] != (__UINTPTR_TYPE__) &b[10];
+}
+
+int
+f3 (void)
+{
+  return (__UINTPTR_TYPE__) &a[0] != (__UINTPTR_TYPE__) &b[0];
+}
+
+int
+f4 (void)
+{
+  return (__UINTPTR_TYPE__) &a[64] != (__UINTPTR_TYPE__) &b[64];
+}
+
+int
+f5 (void)
+{
+  int c[64] = {};
+  return (__UINTPTR_TYPE__) &a[0] != (__UINTPTR_TYPE__) &c[64];
+}
+
+int
+f6 (void)
+{
+  int c[64] = {};
+  return (__UINTPTR_TYPE__) &b[64] != (__UINTPTR_TYPE__) &c[0];
+}
+
+int
+f7 (void)
+{
+  int c[64] = {}, d[64] = {};
+  return (__UINTPTR_TYPE__) &c[2] != (__UINTPTR_TYPE__) &d[2];
+}
+
+int
+f8 (void)
+{
+  int c[64] = {}, d[64] = {};
+  return (__UINTPTR_TYPE__) &c[2] != (__UINTPTR_TYPE__) &d[10];
+}
+
+int
+f9 (void)
+{
+  int c[64] = {}, d[64] = {};
+  return (__UINTPTR_TYPE__) &c[0] != (__UINTPTR_TYPE__) &d[0];
+}
+
+int
+f10 (void)
+{
+  int c[64] = {}, d[64] = {};
+  return (__UINTPTR_TYPE__) &c[64] != (__UINTPTR_TYPE__) &d[64];
+}
--- gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c.jj        2019-01-14 
13:18:09.332006700 +0100
+++ gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c   2019-01-14 14:24:01.838001078 
+0100
@@ -0,0 +1,43 @@
+/* PR tree-optimization/88775 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* These can't be decided until we know how the variables will
+   be laid out in memory.  */
+/* { dg-final { scan-tree-dump-not "return 1;" "optimized" } } */
+
+int a[64] = {};
+int b[64] = {};
+int e[0] = {};
+int f[0] = {};
+
+int
+f1 (void)
+{
+  return (__UINTPTR_TYPE__) &a[0] != (__UINTPTR_TYPE__) &b[64];
+}
+
+int
+f2 (void)
+{
+  return (__UINTPTR_TYPE__) &a[64] != (__UINTPTR_TYPE__) &b[0];
+}
+
+int
+f3 (void)
+{
+  return (__UINTPTR_TYPE__) &e[0] != (__UINTPTR_TYPE__) &f[0];
+}
+
+int
+f4 (void)
+{
+  int c[64] = {}, d[64] = {};
+  return (__UINTPTR_TYPE__) &c[0] != (__UINTPTR_TYPE__) &d[64];
+}
+
+int
+f5 (void)
+{
+  int c[64] = {}, d[64] = {};
+  return (__UINTPTR_TYPE__) &c[64] != (__UINTPTR_TYPE__) &d[0];
+}

        Jakub

Reply via email to