On 27/08/25 18:27, Richard Biener wrote:
External email: Use caution opening links or attachments
On Mon, 25 Aug 2025, [email protected] wrote:
From: Dhruv Chawla <[email protected]>
For ==, < and <=, the fold is to 0. For !=, > and >=, the fold is to 1.
This only applies when C != 0.
So -50 << 1 < 1 is true, so does this only work for unsigned types,
or tree_expr_nonnegative_p in addition to tree_expr_nonzero_p?
I've probably mislead you when asking to simply extend it to other compare
operators. For == it should of course work when nonzero only.
Or am I missing something?
Why did you restrict this to #if GIMPLE in v2?
Hi Richi,
Sorry for the (very) long delay in getting back to you, I got quite badly
stuck trying various approaches with this patch. I have attached patch v3
to this email. The #if GIMPLE was something left over from v1, I had
forgotten to remove it.
The primary issue I ran into is that tree_expr_nonnegative_p doesn't really
work for function parameters (which was causing my tests to fail), because it
calls into gimple_stmt_nonnegative_warnv_p which doesn't work for the GIMPLE_NOP
that params have as the gimple_code.
I tried extending tree_expr_nonnegative_p with something similar to
expr_not_equal_to but that kept causing ICEs/miscompiles that I couldn't figure
out so I have left it as a FIXME in match-shift-cmp-1.c.
--
Regards,
Dhruv
-- >8 --
From 8a404f0a7f0bdf3cfff1de70708fb44df8e85a6a Mon Sep 17 00:00:00 2001
From: Dhruv Chawla <[email protected]>
Date: Thu, 21 Aug 2025 02:06:07 -0700
Subject: [PATCH] match.pd: Fold (y << x) <rel op> x -> 0 or 1
- (y << x) == x -> 0 when y != 0
- (y << x) {<,<=} x -> 0 when y > 0.
- (y << x) != x -> 1 when y != 0.
- (y << x) {>,>=} x -> 1 when y > 0.
Bootstrapped and regtested on aarch64-linux-gnu.
Signed-off-by: Dhruv Chawla <[email protected]>
gcc/ChangeLog:
* match.pd: New patterns.
gcc/testsuite/ChangeLog:
* gcc.dg/match-shift-cmp-1.c: New test.
* gcc.dg/match-shift-cmp-2.c: Likewise.
* gcc.dg/match-shift-cmp-3.c: Likewise.
* gcc.dg/match-shift-cmp-4.c: Likewise.
---
gcc/match.pd | 32 ++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-1.c | 50 +++++++++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-2.c | 62 ++++++++++++++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-3.c | 44 +++++++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-4.c | 51 +++++++++++++++++++
5 files changed, 239 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-1.c
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-2.c
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-3.c
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-4.c
diff --git a/gcc/match.pd b/gcc/match.pd
index 6aaf80eee7d..6e4509233d0 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1339,6 +1339,38 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (INTEGRAL_TYPE_P (type))
(rshift (op @0 @2) @1))))
+/* (y << x) == x -> 0 when y != 0. */
+(simplify
+ (eq:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0))
+ { build_zero_cst (type); }))
+
+/* (y << x) {<,<=} x -> 0 when y > 0. */
+(for cmp (lt le)
+ (simplify
+ (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0)
+ && tree_expr_nonnegative_p (@0))
+ { build_zero_cst (type); })))
+
+/* (y << x) != x -> 1 when y != 0. */
+(simplify
+ (ne:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0))
+ { build_one_cst (type); }))
+
+/* (y << x) {>,>=} x -> 1 when y > 0. */
+(for cmp (gt ge)
+ (simplify
+ (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0)
+ && tree_expr_nonnegative_p (@0))
+ { build_one_cst (type); })))
+
/* Fold (1 << (C - x)) where C = precision(type) - 1
into ((1 << C) >> x). */
(simplify
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-1.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-1.c
new file mode 100644
index 00000000000..b22d57d370f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-1.c
@@ -0,0 +1,50 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return (cst << x) op x; }
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, unsigned, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, bool, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if (y <= 0)
\
+ __builtin_unreachable ();
\
+ return (y << x) op x;
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, bool)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (eq, ==, 1)
+TEST_OP_CST (ne, !=, 2)
+TEST_OP_CST (lt, <, 3)
+TEST_OP_CST (gt, >, 4)
+TEST_OP_CST (le, <=, 5)
+TEST_OP_CST (ge, >=, 6)
+
+TEST_OP (eq, ==)
+TEST_OP (ne, !=)
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* FIXME: The lt, le, gt and ge cases for int and enum don't get optimized. */
+/* { dg-final { scan-tree-dump-times "<<" 8 optimized } } */
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-2.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-2.c
new file mode 100644
index 00000000000..96a2fd954f6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-2.c
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* The fold (y << x) <op> x -> 0|1 shouldn't trigger when y is 0. */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return (cst << x) op x; }
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, unsigned, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, bool, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if (y != 0)
\
+ __builtin_unreachable ();
\
+ return (y << x) op x;
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, bool)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (eq, ==, 0)
+TEST_OP_CST (ne, !=, 0)
+TEST_OP_CST (lt, <, 0)
+TEST_OP_CST (gt, >, 0)
+TEST_OP_CST (le, <=, 0)
+TEST_OP_CST (ge, >=, 0)
+
+TEST_OP (eq, ==)
+TEST_OP (ne, !=)
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* These end up getting folded by other patterns. */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) == 0" 8 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) != 0" 8 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) > 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) < 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) >= 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) <= 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "~x_\\d\\(D\\)" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return x_\\d\\(D\\);" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 0;" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 1;" 4 optimized } } */
+/* Total: 48. */
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-3.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-3.c
new file mode 100644
index 00000000000..34380cfeb96
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-3.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* The fold (y << x) <op> x -> 0|1 shouldn't trigger when y is negative or
+ zero unsigned (except for == and !=). */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return ((cst << x) op x); }
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if (y > 0)
\
+ __builtin_unreachable ();
\
+ return ((y << x) op x);
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (lt, <, -1)
+TEST_OP_CST (gt, >, -2)
+TEST_OP_CST (le, <=, -3)
+TEST_OP_CST (ge, >=, -4)
+
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* { dg-final { scan-tree-dump-times "<<" 16 optimized } } */
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-4.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-4.c
new file mode 100644
index 00000000000..629e2a376d1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-4.c
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* The fold (y << x) <op> x -> 0|1 should trigger when y is negative
+ unsigned. */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return ((unsigned) (cst) << x) op x;
}
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, unsigned, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if ((int) y <= 0)
\
+ __builtin_unreachable ();
\
+ return ((unsigned) (y) << x) op x;
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (eq, ==, -1)
+TEST_OP_CST (ne, !=, -2)
+TEST_OP_CST (lt, <, -3)
+TEST_OP_CST (gt, >, -4)
+TEST_OP_CST (le, <=, -5)
+TEST_OP_CST (ge, >=, -6)
+
+TEST_OP (eq, ==)
+TEST_OP (ne, !=)
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* { dg-final { scan-tree-dump-times "return 0;" 18 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 1;" 18 optimized } } */
--
2.44.0
Thanks,
Richard.
Bootstrapped and regtested on aarch64-linux-gnu.
Signed-off-by: Dhruv Chawla <[email protected]>
gcc/ChangeLog:
* match.pd: New patterns.
gcc/testsuite/ChangeLog:
* gcc.dg/match-constant-shift-1.c: New test.
* gcc.dg/match-constant-shift-1.c: Likewise.
---
gcc/match.pd | 18 ++++++
gcc/testsuite/gcc.dg/match-constant-shift-1.c | 46 +++++++++++++++
gcc/testsuite/gcc.dg/match-constant-shift-2.c | 59 +++++++++++++++++++
3 files changed, 123 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/match-constant-shift-1.c
create mode 100644 gcc/testsuite/gcc.dg/match-constant-shift-2.c
diff --git a/gcc/match.pd b/gcc/match.pd
index b1d7a3a1b73..4e8fbaa4dd9 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1316,6 +1316,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (INTEGRAL_TYPE_P (type))
(rshift (op @0 @2) @1))))
+#if GIMPLE
+/* (y << x) {==,<,<=} x -> 0 when y != 0. */
+(for cmp (eq lt le)
+ (simplify
+ (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0))
+ { build_zero_cst (type); })))
+
+/* (y << x) {!=,>,>=} x -> 1 when y != 0. */
+(for cmp (ne gt ge)
+ (simplify
+ (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0))
+ { build_one_cst (type); })))
+#endif
+
/* Fold (1 << (C - x)) where C = precision(type) - 1
into ((1 << C) >> x). */
(simplify
diff --git a/gcc/testsuite/gcc.dg/match-constant-shift-1.c
b/gcc/testsuite/gcc.dg/match-constant-shift-1.c
new file mode 100644
index 00000000000..8e7a620e951
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-constant-shift-1.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-optimized" } */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##n (type x) { return (cst << x) op x; }
+
+#define TEST_OP_CST(n, op)
\
+ TEST_ONE_CST (n, op, unsigned, n)
\
+ TEST_ONE_CST (n, op, int, n)
\
+ TEST_ONE_CST (n, op, bool, n)
\
+ TEST_ONE_CST (n, op, test_enum, n)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##n (type x, type y)
\
+ {
\
+ if (y <= 0)
\
+ __builtin_unreachable ();
\
+ return (y << x) op x;
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, bool)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ ZERO
+} test_enum;
+
+TEST_OP_CST (1, ==)
+TEST_OP_CST (2, !=)
+TEST_OP_CST (3, <)
+TEST_OP_CST (4, >)
+TEST_OP_CST (5, <=)
+TEST_OP_CST (6, >=)
+
+TEST_OP (1, ==)
+TEST_OP (2, !=)
+TEST_OP (3, <)
+TEST_OP (4, >)
+TEST_OP (5, <=)
+TEST_OP (6, >=)
+
+/* { dg-final { scan-tree-dump-not "<<" optimized } } */
diff --git a/gcc/testsuite/gcc.dg/match-constant-shift-2.c
b/gcc/testsuite/gcc.dg/match-constant-shift-2.c
new file mode 100644
index 00000000000..d6a4a3012f2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-constant-shift-2.c
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* The fold (C << x) <op> x -> 0|1 shouldn't trigger when C is 0. */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ type lshift_cst_##type##n (type x) { return (type) ((cst << x) op x); }
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, unsigned, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, bool, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ type lshift_##type##n (type x, type y) \
+ {
\
+ if (y != 0)
\
+ __builtin_unreachable ();
\
+ return (type) ((y << x) op x);
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, bool)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ ZERO
+} test_enum;
+
+TEST_OP_CST (1, ==, 0)
+TEST_OP_CST (2, !=, 0)
+TEST_OP_CST (3, <, 0)
+TEST_OP_CST (4, >, 0)
+TEST_OP_CST (5, <=, 0)
+TEST_OP_CST (6, >=, 0)
+
+TEST_OP (1, ==)
+TEST_OP (2, !=)
+TEST_OP (3, <)
+TEST_OP (4, >)
+TEST_OP (5, <=)
+TEST_OP (6, >=)
+
+/* These end up getting folded by other patterns. */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) == 0" 10 optimized} } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) != 0" 10 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) > 0" 2 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) < 0" 2 optimized} } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) >= 0" 2 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) <= 0" 2 optimized} } */
+/* { dg-final { scan-tree-dump-times "~x_\\d\\(D\\)" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return x_\\d\\(D\\);" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 0;" 6 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 1;" 6 optimized } } */
+/* Total: 48. */
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)
From 8a404f0a7f0bdf3cfff1de70708fb44df8e85a6a Mon Sep 17 00:00:00 2001
From: Dhruv Chawla <[email protected]>
Date: Thu, 21 Aug 2025 02:06:07 -0700
Subject: [PATCH] match.pd: Fold (y << x) <rel op> x -> 0 or 1
- (y << x) == x -> 0 when y != 0
- (y << x) {<,<=} x -> 0 when y > 0.
- (y << x) != x -> 1 when y != 0.
- (y << x) {>,>=} x -> 1 when y > 0.
Bootstrapped and regtested on aarch64-linux-gnu.
Signed-off-by: Dhruv Chawla <[email protected]>
gcc/ChangeLog:
* match.pd: New patterns.
gcc/testsuite/ChangeLog:
* gcc.dg/match-shift-cmp-1.c: New test.
* gcc.dg/match-shift-cmp-2.c: Likewise.
* gcc.dg/match-shift-cmp-3.c: Likewise.
* gcc.dg/match-shift-cmp-4.c: Likewise.
---
gcc/match.pd | 32 ++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-1.c | 50 +++++++++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-2.c | 62 ++++++++++++++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-3.c | 44 +++++++++++++++++
gcc/testsuite/gcc.dg/match-shift-cmp-4.c | 51 +++++++++++++++++++
5 files changed, 239 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-1.c
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-2.c
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-3.c
create mode 100644 gcc/testsuite/gcc.dg/match-shift-cmp-4.c
diff --git a/gcc/match.pd b/gcc/match.pd
index 6aaf80eee7d..6e4509233d0 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1339,6 +1339,38 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (INTEGRAL_TYPE_P (type))
(rshift (op @0 @2) @1))))
+/* (y << x) == x -> 0 when y != 0. */
+(simplify
+ (eq:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0))
+ { build_zero_cst (type); }))
+
+/* (y << x) {<,<=} x -> 0 when y > 0. */
+(for cmp (lt le)
+ (simplify
+ (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0)
+ && tree_expr_nonnegative_p (@0))
+ { build_zero_cst (type); })))
+
+/* (y << x) != x -> 1 when y != 0. */
+(simplify
+ (ne:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0))
+ { build_one_cst (type); }))
+
+/* (y << x) {>,>=} x -> 1 when y > 0. */
+(for cmp (gt ge)
+ (simplify
+ (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
+ && tree_expr_nonzero_p (@0)
+ && tree_expr_nonnegative_p (@0))
+ { build_one_cst (type); })))
+
/* Fold (1 << (C - x)) where C = precision(type) - 1
into ((1 << C) >> x). */
(simplify
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-1.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-1.c
new file mode 100644
index 00000000000..b22d57d370f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-1.c
@@ -0,0 +1,50 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return (cst << x) op x; }
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, unsigned, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, bool, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if (y <= 0)
\
+ __builtin_unreachable ();
\
+ return (y << x) op x;
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, bool)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (eq, ==, 1)
+TEST_OP_CST (ne, !=, 2)
+TEST_OP_CST (lt, <, 3)
+TEST_OP_CST (gt, >, 4)
+TEST_OP_CST (le, <=, 5)
+TEST_OP_CST (ge, >=, 6)
+
+TEST_OP (eq, ==)
+TEST_OP (ne, !=)
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* FIXME: The lt, le, gt and ge cases for int and enum don't get optimized. */
+/* { dg-final { scan-tree-dump-times "<<" 8 optimized } } */
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-2.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-2.c
new file mode 100644
index 00000000000..96a2fd954f6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-2.c
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* The fold (y << x) <op> x -> 0|1 shouldn't trigger when y is 0. */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return (cst << x) op x; }
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, unsigned, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, bool, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if (y != 0)
\
+ __builtin_unreachable ();
\
+ return (y << x) op x;
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, bool)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (eq, ==, 0)
+TEST_OP_CST (ne, !=, 0)
+TEST_OP_CST (lt, <, 0)
+TEST_OP_CST (gt, >, 0)
+TEST_OP_CST (le, <=, 0)
+TEST_OP_CST (ge, >=, 0)
+
+TEST_OP (eq, ==)
+TEST_OP (ne, !=)
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* These end up getting folded by other patterns. */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) == 0" 8 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) != 0" 8 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) > 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) < 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) >= 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) <= 0" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "~x_\\d\\(D\\)" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return x_\\d\\(D\\);" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 0;" 4 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 1;" 4 optimized } } */
+/* Total: 48. */
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-3.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-3.c
new file mode 100644
index 00000000000..34380cfeb96
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-3.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* The fold (y << x) <op> x -> 0|1 shouldn't trigger when y is negative or
+ zero unsigned (except for == and !=). */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return ((cst << x) op x); }
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if (y > 0)
\
+ __builtin_unreachable ();
\
+ return ((y << x) op x);
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (lt, <, -1)
+TEST_OP_CST (gt, >, -2)
+TEST_OP_CST (le, <=, -3)
+TEST_OP_CST (ge, >=, -4)
+
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* { dg-final { scan-tree-dump-times "<<" 16 optimized } } */
diff --git a/gcc/testsuite/gcc.dg/match-shift-cmp-4.c
b/gcc/testsuite/gcc.dg/match-shift-cmp-4.c
new file mode 100644
index 00000000000..629e2a376d1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/match-shift-cmp-4.c
@@ -0,0 +1,51 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* The fold (y << x) <op> x -> 0|1 should trigger when y is negative
+ unsigned. */
+
+#define TEST_ONE_CST(n, op, type, cst)
\
+ bool lshift_cst_##type##_##n (type x) { return ((unsigned) (cst) << x) op x;
}
+
+#define TEST_OP_CST(n, op, cst)
\
+ TEST_ONE_CST (n, op, unsigned, cst)
\
+ TEST_ONE_CST (n, op, int, cst)
\
+ TEST_ONE_CST (n, op, test_enum, cst)
+
+#define TEST_ONE(n, op, type)
\
+ bool lshift_##type##_##n (type x, type y)
\
+ {
\
+ if ((int) y <= 0)
\
+ __builtin_unreachable ();
\
+ return ((unsigned) (y) << x) op x;
\
+ }
+
+#define TEST_OP(n, op)
\
+ TEST_ONE (n, op, unsigned)
\
+ TEST_ONE (n, op, int)
\
+ TEST_ONE (n, op, test_enum)
+
+typedef enum
+{
+ MONE = -1,
+ ZERO = 0,
+ ONE = 1,
+ TWO = 2
+} test_enum;
+
+TEST_OP_CST (eq, ==, -1)
+TEST_OP_CST (ne, !=, -2)
+TEST_OP_CST (lt, <, -3)
+TEST_OP_CST (gt, >, -4)
+TEST_OP_CST (le, <=, -5)
+TEST_OP_CST (ge, >=, -6)
+
+TEST_OP (eq, ==)
+TEST_OP (ne, !=)
+TEST_OP (lt, <)
+TEST_OP (gt, >)
+TEST_OP (le, <=)
+TEST_OP (ge, >=)
+
+/* { dg-final { scan-tree-dump-times "return 0;" 18 optimized } } */
+/* { dg-final { scan-tree-dump-times "return 1;" 18 optimized } } */
--
2.44.0