This patch implements match.pd patterns for the following transformations:

(1) (UB-on-overflow types) C1 - X cmp C2 -> X cmp C1 - C2

(2) (unsigned types) C1 - X cmp C2 ->
    (a) X cmp C1 - C2, when cmp is !=, ==
    (b) X - (C1 - C2) cmp C2, when cmp is <=, >
    (c) X - (C1 - C2 + 1) cmp C2, when cmp is <, >=

(3) (signed wrapping types) C1 - X cmp C2
    (a) X cmp C1 - C2, when cmp is !=, ==
    (b) X - (C1 + 1) rcmp -C2 - 1, otherwise

(4) (all wrapping types) X + C1 cmp C2 ->
    (a) X cmp C2 - C1, when cmp is !=, ==
    (b) X cmp -C1, when cmp is <=, > and C2 - C1 == max
    (c) X cmp -C1, when cmp is <, >= and C2 - C1 == min

Included along are testcases for all the aforementioned changes.  This
patch has been bootstrapped and regtested on aarch64, x86_64, and i386,
and additionally regtested on riscv32.  Existing tests were adjusted
where necessary.

gcc/ChangeLog:

        PR tree-optimization/116024
        * match.pd: New transformations around integer comparison.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr116024.c: New test.
        * gcc.dg/tree-ssa/pr116024-1.c: Ditto.
        * gcc.dg/tree-ssa/pr116024-1-fwrapv.c: Ditto.
        * gcc.dg/tree-ssa/pr116024-2.c: Ditto.
        * gcc.dg/tree-ssa/pr116024-2-fwrapv.c: Ditto.
        * gcc.dg/pr67089-6.c: Adjust.
        * gcc.target/aarch64/gtu_to_ltu_cmp_1.c: Ditto.

Signed-off-by: Artemiy Volkov <arte...@synopsys.com>
---
 gcc/match.pd                                       | 75 +++++++++++++++++++++-
 gcc/testsuite/gcc.dg/pr67089-6.c                   |  4 +-
 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c  | 73 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c         | 73 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c  | 37 +++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c         | 38 +++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr116024.c           | 73 +++++++++++++++++++++
 .../gcc.target/aarch64/gtu_to_ltu_cmp_1.c          |  2 +-
 8 files changed, 371 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr116024.c

diff --git a/gcc/match.pd b/gcc/match.pd
index d401e75..97d4398 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -8418,6 +8418,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        (cmp @0 { TREE_OVERFLOW (res)
                 ? drop_tree_overflow (res) : res; }))))))))
 (for cmp (lt le gt ge)
+     rcmp (gt ge lt le)
  (for op (plus minus)
       rop (minus plus)
   (simplify
@@ -8445,7 +8446,79 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
                                  "X cmp C2 -+ C1"),
                                 WARN_STRICT_OVERFLOW_COMPARISON);
        }
-       (cmp @0 { res; })))))))))
+       (cmp @0 { res; })))))
+/* For wrapping types, simplify X + C1 CMP C2 to X CMP -C1 when possible.  */
+   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
+     (with
+       {
+       wide_int max = wi::max_value (TREE_TYPE (@0));
+       wide_int min = wi::min_value (TREE_TYPE (@0));
+
+       wide_int c2 = rop == PLUS_EXPR
+                             ? wi::add (wi::to_wide (@2), wi::to_wide (@1))
+                             : wi::sub (wi::to_wide (@2), wi::to_wide (@1));
+       }
+       (if (((cmp == LE_EXPR || cmp == GT_EXPR) && wi::eq_p (c2, max))
+           || ((cmp == LT_EXPR || cmp == GE_EXPR) && wi::eq_p (c2, min)))
+         (with
+          {
+            wide_int c1 = rop == PLUS_EXPR
+                                  ? wi::add (min, wi::to_wide (@1))
+                                  : wi::sub (min, wi::to_wide (@1));
+            tree c1_cst = build_uniform_cst (TREE_TYPE (@0),
+                               wide_int_to_tree (TREE_TYPE (@0), c1));
+          }
+          (rcmp @0 { c1_cst; })))))))))
+
+/* Invert sign of X in comparisons of the form C1 - X CMP C2.  */
+
+(for cmp (lt le gt ge eq ne)
+     rcmp (gt ge lt le eq ne)
+  (simplify
+   (cmp (minus INTEGER_CST@0 @1) INTEGER_CST@2)
+   (if (!TREE_OVERFLOW (@0) && !TREE_OVERFLOW (@2)
+       && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@1)))
+     (with { tree res = int_const_binop (MINUS_EXPR, @0, @2); }
+      (if (TREE_OVERFLOW (res))
+       (with
+       {
+         fold_overflow_warning (("assuming signed overflow does not occur "
+                                 "when simplifying conditional to constant"),
+                                 WARN_STRICT_OVERFLOW_CONDITIONAL);
+       }
+       (switch
+        (if (cmp == NE_EXPR)
+         { constant_boolean_node (true, type); })
+        (if (cmp == EQ_EXPR)
+         { constant_boolean_node (false, type); })
+        {
+          bool less = cmp == LE_EXPR || cmp == LT_EXPR;
+          bool ovf_high = wi::lt_p (wi::to_wide (@0), 0,
+                                    TYPE_SIGN (TREE_TYPE (@0)));
+          constant_boolean_node (less == ovf_high, type);
+        }))
+       (with
+       {
+         fold_overflow_warning (("assuming signed overflow does not occur "
+                                 "when changing C1 - X cmp C2 to "
+                                 "X cmp C1 - C2"),
+                                 WARN_STRICT_OVERFLOW_COMPARISON);
+       }
+       (rcmp @1 { res; }))))
+   (if (TYPE_UNSIGNED (TREE_TYPE (@1)))
+     (switch
+       (if (cmp == EQ_EXPR || cmp == NE_EXPR)
+          (cmp @1 (minus @0 @2)))
+       (if (cmp == LE_EXPR || cmp == GT_EXPR)
+          (cmp (plus @1 (minus @2 @0)) @2))
+       (if (cmp == LT_EXPR || cmp == GE_EXPR)
+          (cmp (plus @1 (minus @2
+                       (plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))) @2)))
+   (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@1)))
+     (if (cmp == EQ_EXPR || cmp == NE_EXPR)
+        (cmp @1 (minus @0 @2))
+     (rcmp (minus @1 (plus @0 { build_int_cst (TREE_TYPE (@1), 1); }))
+               (negate @2))))))))
 
 /* Canonicalizations of BIT_FIELD_REFs.  */
 
diff --git a/gcc/testsuite/gcc.dg/pr67089-6.c b/gcc/testsuite/gcc.dg/pr67089-6.c
index b59d75b..80a33c3 100644
--- a/gcc/testsuite/gcc.dg/pr67089-6.c
+++ b/gcc/testsuite/gcc.dg/pr67089-6.c
@@ -57,5 +57,5 @@ T (25, unsigned short, 2U - x, if (r > 2U) foo (0))
 T (26, unsigned char, 2U - x, if (r <= 2U) foo (0))
 
 /* { dg-final { scan-tree-dump-times "ADD_OVERFLOW" 16 "widening_mul" { target 
{ i?86-*-* x86_64-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 11 "widening_mul" { target 
{ { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
-/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target 
{ { i?86-*-* x86_64-*-* } && ia32 } } } } */
+/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 9 "widening_mul" { target 
{ { i?86-*-* x86_64-*-* } && { ! ia32 } } } } } */
+/* { dg-final { scan-tree-dump-times "SUB_OVERFLOW" 7 "widening_mul" { target 
{ { i?86-*-* x86_64-*-* } && ia32 } } } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
new file mode 100644
index 0000000..fdee4cb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1-fwrapv.c
@@ -0,0 +1,73 @@
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
+
+#include <limits.h>
+
+unsigned f(void);
+
+int i2(void)
+{
+  int l = 2;
+  l = 10 - (int)f();
+  return l <= 20; // f() + 10 + INT_MIN <= 20 
+}
+
+int i2a(void)
+{
+  int l = 2;
+  l = 10 - (int)f();
+  return l < 30; // f() + 19 + INT_MIN <= 29 
+}
+
+int i2b(void)
+{
+  int l = 2;
+  l = 200 - (int)f();
+  return l <= 100; // f() - 100 + INT_MIN <= 100 
+}
+
+int i2c(void)
+{
+  int l = 2;
+  l = 300 - (int)f();
+  return l < 100; // f() - 201 + INT_MIN <= 99 
+}
+
+int i2d(void)
+{
+  int l = 2;
+  l = 1000 - (int)f();
+  return l >= 2000; // f() + 999 + INT_MIN > 1999
+}
+
+int i2e(void)
+{
+  int l = 2;
+  l = 1000 - (int)f();
+  return l > 3000; // f() + 2000 + INT_MIN > 3000
+}
+
+int i2f(void)
+{
+  int l = 2;
+  l = 20000 - (int)f();
+  return l >= 10000; // f() - 10001 + INT_MIN > 9999
+}
+
+int i2g(void)
+{
+  int l = 2;
+  l = 30000 - (int)f();
+  return l > 10000; // f() - 20000 + INT_MIN > 10000
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 "forwprop1" 
} } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -11.*\n.*>= 
-20" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -11.*\n.*>= 
-29" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -201.*\n.*>= 
-100" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -301.*\n.*>= 
-99" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -1001.*\n.*< 
-1999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -1001.*\n.*< 
-3000" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -20001.*\n.*< 
-9999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ -30001.*\n.*< 
-10000" 1 "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
new file mode 100644
index 0000000..40b797c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-1.c
@@ -0,0 +1,73 @@
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
+
+#include <limits.h>
+
+unsigned f(void);
+
+int i2(void)
+{
+  unsigned l = 2;
+  l = 10 - (unsigned)f();
+  return l <= 20; // f() + 10 <= 20 
+}
+
+int i2a(void)
+{
+  unsigned l = 2;
+  l = 10 - (unsigned)f();
+  return l < 30; // f() + 19 <= 29 
+}
+
+int i2b(void)
+{
+  unsigned l = 2;
+  l = 200 - (unsigned)f();
+  return l <= 100; // f() - 100 <= 100 
+}
+
+int i2c(void)
+{
+  unsigned l = 2;
+  l = 300 - (unsigned)f();
+  return l < 100; // f() - 201 <= 99 
+}
+
+int i2d(void)
+{
+  unsigned l = 2;
+  l = 1000 - (unsigned)f();
+  return l >= 2000; // f() + 999 > 1999
+}
+
+int i2e(void)
+{
+  unsigned l = 2;
+  l = 1000 - (unsigned)f();
+  return l > 3000; // f() + 2000 > 3000
+}
+
+int i2f(void)
+{
+  unsigned l = 2;
+  l = 20000 - (unsigned)f();
+  return l >= 10000; // f() - 10001 > 9999
+}
+
+int i2g(void)
+{
+  unsigned l = 2;
+  l = 30000 - (unsigned)f();
+  return l > 10000; // f() - 20000 > 10000
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 8 "forwprop1" 
} } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 10.*\n.*<= 
20" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 19.*\n.*<= 
29" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294967196.*\n.*<= 100" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294967095.*\n.*<= 99" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 999.*\n.*> 
1999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 2000.*\n.*> 
3000" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294957295.*\n.*> 9999" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* \\+ 
4294947296.*\n.*> 10000" 1 "forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
new file mode 100644
index 0000000..4fff886
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2-fwrapv.c
@@ -0,0 +1,37 @@
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details -fwrapv" } */
+
+#include <limits.h>
+
+unsigned f(void);
+
+int i3(void)
+{
+  int l = -10 + (int)f();
+  return l <= INT_MAX - 10; // f() > INT_MIN + 9
+}
+
+int i3a(void)
+{
+  int l = -21 + (int)f();
+  return l < INT_MAX - 20; // f() > INT_MIN + 20
+}
+
+int i3b(void)
+{
+  int l = 30 + (int)f();
+  return l >= INT_MIN + 30; // f() <= INT_MAX - 30
+}
+
+int i3c(void)
+{
+  int l = 40 + (int)f();
+  return l > INT_MIN + 39; // f() <= INT_MAX - 39
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= -2147483638" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* >= -2147483627" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 2147483618" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 2147483608" 1 
"forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
new file mode 100644
index 0000000..f492e72
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024-2.c
@@ -0,0 +1,38 @@
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
+
+#include <limits.h>
+
+unsigned f(void);
+
+int i3(void)
+{
+  unsigned l = 2;
+  l = UINT_MAX - 9 + (unsigned)f();
+  return l <= UINT_MAX - 10; // f() > 9
+}
+
+int i3a(void)
+{
+  unsigned l = UINT_MAX - 20 + (unsigned)f();
+  return l < UINT_MAX - 20; // f() > 20
+}
+
+int i3b(void)
+{
+  unsigned l = 30 + (unsigned)f();
+  return l >= 30; // f() < -30u
+}
+
+int i3c(void)
+{
+  unsigned l = 40 + (unsigned)f();
+  return l > 39; // f() < -39u
+}
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*? \\+" 4 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 9" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 20" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 4294967265" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* <= 4294967255" 1 
"forwprop1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
new file mode 100644
index 0000000..26bb81d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr116024.c
@@ -0,0 +1,73 @@
+/* PR tree-optimization/116024 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */
+
+#include <limits.h>
+
+unsigned f(void);
+
+int i1(void)
+{
+  int l = 2;
+  l = 10 - (int)f();
+  return l <= 9; // f() > 0
+}
+
+int i1a(void)
+{
+  int l = 2;
+  l = 20 - (int)f();
+  return l <= INT_MIN; // return 0
+}
+
+int i1b(void)
+{
+  int l = 2;
+  l = 30 - (int)f();
+  return l <= INT_MIN + 31; // f() == INT_MAX
+}
+
+int i1c(void)
+{
+  int l = 2;
+  l = INT_MAX - 40 - (int)f();
+  return l <= -38; // f() > INT_MAX - 3
+}
+
+int i1d(void)
+{
+  int l = 2;
+  l = INT_MAX - 50 - (int)f();
+  return l <= INT_MAX - 1; // f() != -50
+}
+
+int i1e(void)
+{
+  int l = 2;
+  l = INT_MAX - 60 - (int)f();
+  return l != INT_MAX - 90; // f() != 30
+}
+
+int i1f(void)
+{
+  int l = 2;
+  l = INT_MIN + 70 - (int)f();
+  return l <= INT_MAX - 2; // return 0
+}
+
+int i1g(void)
+{
+  int l = 2;
+  l = INT_MAX/2 + 30 - (int)f();
+  return l <= INT_MIN/2 - 30; // return 1
+}
+
+
+/* { dg-final { scan-tree-dump-times "Removing dead stmt:.*?- _" 5 "forwprop1" 
} } */
+/* { dg-final { scan-tree-dump-times "return 0" 2 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "return 1" 1 "forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 0" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* == 2147483647" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* > 2147483644" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 4294967246" 1 
"forwprop1" } } */
+/* { dg-final { scan-tree-dump-times "gimple_simplified to.* != 30" 1 
"forwprop1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c 
b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
index 81c536c..bfcec67 100644
--- a/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/gtu_to_ltu_cmp_1.c
@@ -10,4 +10,4 @@ f1 (int x, int t)
   return t;
 }
 
-/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #2" 1 } } */
+/* { dg-final { scan-assembler-times "cmn\\tw\[0-9\]+, #3" 1 } } */
-- 
1.8.3.1

Reply via email to