analyze_and_compute_bitop_with_inv_effect replaces a loop that repeatedly
applies a bitwise op with a loop-invariant operand by its closed form.  The
BIT_XOR case only handled a constant trip count; for a variable count it
bailed out, leaving loops like "for (i=0;i<n;i++) j ^= 1;" in place.

Mask the invariant with that runtime parity to handle a variable count.
A constant count folds as before.

        PR tree-optimization/112104

gcc/ChangeLog:

        * tree-scalar-evolution.cc (analyze_and_compute_bitop_with_inv_effect):
        Handle BIT_XOR_EXPR with a variable trip count.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/pr105735-1.c: Bump final value replacement count
        from 8 to 9.
        * gcc.target/i386/pr105735-3.c: Likewise.
        * gcc.dg/tree-ssa/pr112104-1.c: New test.
        * gcc.dg/tree-ssa/pr112104-2.c: New test.

Signed-off-by: Eikansh Gupta <[email protected]>
---
 gcc/testsuite/gcc.dg/tree-ssa/pr112104-1.c | 45 ++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr112104-2.c | 60 ++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr105735-1.c |  2 +-
 gcc/testsuite/gcc.target/i386/pr105735-3.c |  2 +-
 gcc/tree-scalar-evolution.cc               | 16 +++---
 5 files changed, 117 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr112104-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr112104-2.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr112104-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr112104-1.c
new file mode 100644
index 00000000000..da05f05962b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr112104-1.c
@@ -0,0 +1,45 @@
+/* PR tree-optimization/112104 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-sccp-details" } */
+
+unsigned int
+__attribute__ ((noipa))
+f_xor (unsigned int tmp, int n, unsigned int inv)
+{
+  unsigned int r = tmp;
+  for (int i = 0; i < n; i++)
+    r ^= inv;
+  return r;
+}
+
+int
+__attribute__ ((noipa))
+f_xor1 (int n)
+{
+  int j = 0;
+  for (int i = 0; i < n; i++)
+    j ^= 1;
+  return j;
+}
+
+unsigned long long
+__attribute__ ((noipa))
+f_xor64 (unsigned long long tmp, long n, unsigned long long inv)
+{
+  unsigned long long r = tmp;
+  for (long i = 0; i < n; i++)
+    r ^= inv;
+  return r;
+}
+
+unsigned int
+__attribute__ ((noipa))
+f_xorc (unsigned int tmp, int n)
+{
+  unsigned int r = tmp;
+  for (int i = 0; i < n; i++)
+    r ^= 11304;
+  return r;
+}
+
+/* { dg-final { scan-tree-dump-times {final value replacement} 4 "sccp" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr112104-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr112104-2.c
new file mode 100644
index 00000000000..99efd58f1e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr112104-2.c
@@ -0,0 +1,60 @@
+/* PR tree-optimization/112104 */
+/* { dg-do run } */
+/* { dg-options "-O1" } */
+
+#include "pr112104-1.c"
+
+int
+main (void)
+{
+  unsigned int tmp = 0x1101;
+  unsigned int inv = 0x111101;
+  unsigned long long tmp64 = 0x1101ULL;
+  unsigned long long inv64 = 0xdeadbeefcafebabeULL;
+
+  if (f_xor (tmp, 64, inv) != 0x1101)
+    __builtin_abort ();
+  if (f_xor (tmp, 63, inv) != 0x110000)
+    __builtin_abort ();
+
+  if (f_xor (tmp, 1, inv) != 0x110000)
+    __builtin_abort ();
+  if (f_xor (tmp, 2, inv) != 0x1101)
+    __builtin_abort ();
+
+  if (f_xor1 (64) != 0)
+    __builtin_abort ();
+  if (f_xor1 (63) != 1)
+    __builtin_abort ();
+  if (f_xor1 (1) != 1)
+    __builtin_abort ();
+  if (f_xor1 (2) != 0)
+    __builtin_abort ();
+
+  if (f_xor64 (tmp64, 64, inv64) != 0x1101ULL)
+    __builtin_abort ();
+  if (f_xor64 (tmp64, 63, inv64) != 0xdeadbeefcafeabbfULL)
+    __builtin_abort ();
+  if (f_xor64 (tmp64, 1, inv64) != 0xdeadbeefcafeabbfULL)
+    __builtin_abort ();
+  if (f_xor64 (tmp64, 2, inv64) != 0x1101ULL)
+    __builtin_abort ();
+
+  if (f_xorc (tmp, 64) != 0x1101)
+    __builtin_abort ();
+  if (f_xorc (tmp, 63) != 0x3d29)
+    __builtin_abort ();
+  if (f_xorc (tmp, 1) != 0x3d29)
+    __builtin_abort ();
+  if (f_xorc (tmp, 2) != 0x1101)
+    __builtin_abort ();
+
+  if (f_xor (tmp, 0, inv) != 0x1101)
+    __builtin_abort ();
+  if (f_xor (tmp, -5, inv) != 0x1101)
+    __builtin_abort ();
+  if (f_xor1 (0) != 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr105735-1.c 
b/gcc/testsuite/gcc.target/i386/pr105735-1.c
index 69de6b2911a..4d9ca085127 100644
--- a/gcc/testsuite/gcc.target/i386/pr105735-1.c
+++ b/gcc/testsuite/gcc.target/i386/pr105735-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-options "-O1 -fdump-tree-sccp-details" } */
-/* { dg-final { scan-tree-dump-times {final value replacement} 8 "sccp" } } */
+/* { dg-final { scan-tree-dump-times {final value replacement} 9 "sccp" } } */
 
 unsigned int
 __attribute__((noipa))
diff --git a/gcc/testsuite/gcc.target/i386/pr105735-3.c 
b/gcc/testsuite/gcc.target/i386/pr105735-3.c
index 9e268a1a997..a3280ba6860 100644
--- a/gcc/testsuite/gcc.target/i386/pr105735-3.c
+++ b/gcc/testsuite/gcc.target/i386/pr105735-3.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-options "-O1 -fdump-tree-sccp-details" } */
-/* { dg-final { scan-tree-dump-times {final value replacement} 8 "sccp" } } */
+/* { dg-final { scan-tree-dump-times {final value replacement} 9 "sccp" } } */
 
 unsigned int
 __attribute__((noipa))
diff --git a/gcc/tree-scalar-evolution.cc b/gcc/tree-scalar-evolution.cc
index cd99cbe86ce..0466a1d36b6 100644
--- a/gcc/tree-scalar-evolution.cc
+++ b/gcc/tree-scalar-evolution.cc
@@ -3850,12 +3850,16 @@ analyze_and_compute_bitop_with_inv_effect (class loop* 
loop, tree phidef,
 
   if (code1 == BIT_XOR_EXPR)
     {
-       if (!tree_fits_uhwi_p (niter))
-       return NULL_TREE;
-       unsigned HOST_WIDE_INT niter_num;
-       niter_num = tree_to_uhwi (niter);
-       if (niter_num % 2 != 0)
-       match_op[0] =  build_zero_cst (type);
+      tree niter_type = TREE_TYPE (niter);
+      tree one = build_one_cst (niter_type);
+      tree contributes = fold_build2 (BIT_XOR_EXPR, niter_type,
+                                     fold_build2 (BIT_AND_EXPR, niter_type,
+                                                  niter, one),
+                                     one);
+      /* mask is all-ones when the invariant contributes, zero otherwise.  */
+      tree mask = fold_build1 (NEGATE_EXPR, type,
+                              fold_convert (type, contributes));
+      match_op[0] = fold_build2 (BIT_AND_EXPR, type, match_op[0], mask);
     }
 
   inv = PHI_ARG_DEF_FROM_EDGE (header_phi, loop_preheader_edge (loop));
-- 
2.34.1

Reply via email to