Add patterns to handle the case where we're checking if a bit is set and
setting it in case it's not.  Similar to the work done in PR64567 but in
this case the bit is a result of a 1 << val lshift:

(A & BIT) EQ 0 ? A | BIT : A , BIT = 1 << val => A | BIT
(A & BIT) NE 0 ? A : A | BIT , BIT = 1 << val => A | BIT

We're also adding cases where a full bitmask is tested, i.e.:

(A & MASK) EQ MASK ? A : A | MASK => A | MASK
(A & MASK) NE MASK ? A | MASK : A => A | MASK

Note that for these simplifications we're not limited to a bit/pow2
value like the zero comparisons, which don't work with multiple bits
because there's no guarantees to preserve the 'non-zero' cases. E.g.:
"(A & 0xF) == 0 ? A | 0xF : A" can't be simplified to just "A | 0xF"
because there's a whole range of A lower bits (1,2...E) that would be
turned to 0xF in the simplification - bits that would be preserved in
the original pattern. This is the same scenario discussed before in
PR64567.

Finally, we're going with Andrea's suggestion and we're also adding
variations of these patterns above that has the true/false legs switched
and will reduce to just 'A'.

Bootstrapped and regression tested in x86_64, aarch64 and riscv64.

        PR tree-optimization/124667

gcc/ChangeLog:

        * match.pd(`(A & BIT) EQ 0 ? A | BIT : A`): New
        pattern.
        (`(A & BIT) EQ 0 ? A : A | BIT`): New pattern.
        (`(A & BIT) NE 0 ? A : A | BIT`): New pattern.
        (`(A & BIT) NE 0 ? A | BIT : A`): New pattern.
        (`(A & MASK) EQ MASK ? A : A | MASK`): New pattern.
        (`(A & MASK) EQ MASK ? A | MASK : A`): New pattern.
        (`(A & MASK) NE MASK ? A | MASK : A`): New pattern.
        (`(A & MASK) NE MASK ? A : A | MASK`): New pattern.
        (`A | (((A >> N) & 1) << N)`): New pattern.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr124667-2.c: New test.
        * gcc.dg/tree-ssa/pr124667.c: New test.
---
 gcc/match.pd                               |  99 +++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr124667-2.c |  67 +++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr124667.c   | 107 +++++++++++++++++++++
 3 files changed, 273 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr124667-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr124667.c

diff --git a/gcc/match.pd b/gcc/match.pd
index a7cec25dbad..179d758e937 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -6102,6 +6102,105 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (if (wi::to_wide (@2) == ~wi::to_wide (@1))
    @3))
 
+/* PR124667: (A & (1 << val)) == 0 ? A |= (1 << val) : A
+   Similar to PR64567 above but with the bit given
+   by an (1 << val) lshift.  Note that this shift pattern
+   guarantees that this is a single bit operation.  */
+(simplify
+ (cond
+  (eq (bit_and:c @0 (lshift@1 integer_onep @2)) integer_zerop)
+      (bit_ior:c@3 @0 @1)
+      @0)
+ @3)
+
+/* PR124667: (A & (1 << val)) != 0 ? A : A |= (1 << val)
+   NE variant of the previous match.  */
+(simplify
+ (cond
+  (ne (bit_and:c @0 (lshift@1 integer_onep @2)) integer_zerop)
+      @0
+      (bit_ior:c@3 @0 @1))
+  @3)
+
+/* PR124667: same patterns as above but with true/false legs
+   switched, reducing all to just "A":
+   (A & (1 << val)) == 0 ? A : (A | (1 << val))
+   (A & (1 << val)) != 0 ? (A | (1 << val)) : A;  */
+(simplify
+ (cond
+  (eq (bit_and:c @0 (lshift@1 integer_onep @2)) integer_zerop)
+      @0
+      (bit_ior:c @0 @1))
+  @0)
+(simplify
+ (cond
+  (ne (bit_and:c @0 (lshift@1 integer_onep @2)) integer_zerop)
+      (bit_ior:c @0 @1)
+      @0)
+  @0)
+
+/* PR124667: a bitmask set simplification that is possible as
+   long as the whole bitmask is being checked:
+   (A & B) == B ?   A    : A |= B  => A | B
+   (A & B) != B ? A |= B :   A     => A | B.  */
+(simplify
+ (cond
+  (eq:c (bit_and @0 @1) @1)
+       @0
+       (bit_ior@2 @0 @1))
+  @2)
+(simplify
+ (cond
+  (ne:c (bit_and @0 @1) @1)
+       (bit_ior@2 @0 @1)
+       @0)
+  @2)
+
+/* PR124667: similar from above with true/false legs switched,
+   reducing to just A:
+   (A & B) == B ? (A | B) : A
+   (A & B) != B ? A : (A | B);  */
+(simplify
+ (cond
+  (eq:c (bit_and @0 @1) @1)
+       (bit_ior @0 @1)
+       @0)
+  @0)
+(simplify
+ (cond
+  (ne:c (bit_and @0 @1) @1)
+       @0
+       (bit_ior @0 @1))
+  @0)
+
+/* PR124667: these also reduces to just "A":
+   ((A >> val) & 1) == 0 ?  A : (A | (1 << val))
+   ((A >> val) & 1) != 0 ?  (A | (1 << val)) : A;
+
+   For both cases the zero_one patterns in lines 4860-4890
+   will turn both into the following 'original':
+
+   intD.7 bitshiftD.4489 = 1 << bitD.4485;
+   arrD.4488 = arrD.4488 >> bitD.4485 & 1) * bitshiftD.4489 | arrD.4488;
+
+   And it'll appear back in phiopt as:
+
+   _1 = arrD.4482;
+   _2 = _1 >> bit_7;
+   _3 = _2 & 1;
+   _4 = _3 << bit_7;
+   _6 = _1 | _4;
+
+   I.e. it'll drop the 'mult' and lshift the zero_one result from the
+   bit check.  This is where we can reduce it.  */
+(simplify
+  (bit_ior:c
+    @0
+    (lshift
+      (bit_and:c (rshift @0 @1) integer_onep)
+      @1))
+  @0)
+
 #if GIMPLE
 (match (nop_atomic_bit_test_and_p @0 @1 @4)
  (bit_and (convert?@4 (ATOMIC_FETCH_OR_XOR_N @2 INTEGER_CST@0 @3))
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr124667-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr124667-2.c
new file mode 100644
index 00000000000..aa7ac22e07d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr124667-2.c
@@ -0,0 +1,67 @@
+/* { dg-additional-options -O2 } */
+/* { dg-additional-options -fdump-tree-optimized } */
+
+int test1 (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  arr[n] = (arr[n] & bitshift) == 0 ? arr[n] : arr[n] | bitshift;
+
+  return arr[n];
+}
+
+int test1_alt (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  arr[n] = (arr[n] & bitshift) != 0 ? arr[n] | bitshift : arr[n];
+
+  return arr[n];
+}
+
+int test2 (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  arr[n] = ((arr[n] >> bit) & 1) == 0 ? arr[n] : arr[n] | bitshift;
+
+  return arr[n];
+}
+
+int test2_alt (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  arr[n] = ((arr[n] >> bit) & 1) != 0 ? arr[n] | bitshift : arr[n];
+
+  return arr[n];
+}
+
+int test3 (int n)
+{
+  int arr[16];
+  int bits = 0xF;
+
+  arr[n] = (arr[n] & bits) == bits ? arr[n] | bits : arr[n];
+
+  return arr[n];
+}
+
+int test3_alt (int n)
+{
+  int arr[16];
+  int bits = 0xF;
+
+  arr[n] = (arr[n] & bits) != bits ? arr[n] : arr[n] | bits;
+
+  return arr[n];
+}
+
+/* { dg-final { scan-tree-dump-times " \\| " 0 optimized } } */
+/* { dg-final { scan-tree-dump-times " \& " 0 optimized } } */
+/* { dg-final { scan-tree-dump-times " << " 0 optimized } } */
+/* { dg-final { scan-tree-dump-times " >> " 0 optimized } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr124667.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr124667.c
new file mode 100644
index 00000000000..9bdbe05a00d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr124667.c
@@ -0,0 +1,107 @@
+/* { dg-additional-options -O2 } */
+/* { dg-additional-options -fdump-tree-optimized } */
+
+int bitset1 (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  if ((arr[n] & bitshift) == 0)
+    arr[n] |= bitshift;
+
+  return arr[n];
+}
+
+int bitset1_alt (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  arr[n] = (arr[n] & bitshift) != 0 ? arr[n] : arr[n] | bitshift;
+
+  return arr[n];
+}
+
+int bitset1b (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  if ((bitshift & arr[n]) == 0)
+    arr[n] |= bitshift;
+
+  return arr[n];
+}
+
+int bitset1b_alt (int n, int bit)
+{
+  int arr[16];
+  int bitshift = 1 << bit;
+
+  arr[n] = (bitshift & arr[n]) != 0 ? arr[n] : bitshift | arr[n];
+
+  return arr[n];
+}
+
+int bitset2 (int n)
+{
+  int arr[16];
+  int bits = 0xF;
+
+  if ((arr[n] & bits) != bits)
+    arr[n] |= bits;
+
+  return arr[n];
+}
+
+int bitset2_alt (int n)
+{
+  int arr[16];
+  int bits = 0xF;
+
+  arr[n] = (arr[n] & bits) == bits ?  arr[n]: arr[n] | bits;
+
+  return arr[n];
+}
+
+int bitset2b (int n)
+{
+  int arr[16];
+  int bits = 0xF;
+
+  if ((bits & arr[n]) != bits)
+    arr[n] |= bits;
+
+  return arr[n];
+}
+
+int bitset2b_alt (int n)
+{
+  int arr[16];
+  int bits = 0xF;
+
+  arr[n] = (bits & arr[n]) == bits ?  arr[n]: bits | arr[n];
+
+  return arr[n];
+}
+
+/* A negative test to ensure we're not optimizing something
+   we shouldn't. */
+int bitset_nosimplify (int n)
+{
+  int arr[16];
+
+  int bits = 0xF;
+
+  /* We can't make the bit_ior unconditional here because the
+     cond matches for multiple values aside from '0xF' in
+     which the value wouldn't be touched, e.g. for arr[n] = 0x1
+     "arr[n] & bits" is also non-zero and the value wouldn't be
+     changed.  */
+  if ((arr[n] & bits) == 0)
+    arr[n] |= bits;
+
+  return arr[n];
+}
+
+/* { dg-final { scan-tree-dump-times "goto" 2 optimized } } */
-- 
2.43.0

Reply via email to