addi+and keeps a 32 bit object sign extended on rv64, bclr does not:
clearing bit 31 leaves 0xffffffff00000000, so the loop
(m = x; m; m &= m - 1) never terminates.

A sext.w would fix that, but ctzw+bclr+sext.w is no shorter than
ctzw+addiw+and and one instruction deeper, so leave the 32 bit case
alone.

Wrong code since r16-3984-g4f01f39d021; it hangs rte_softrss() in DPDK
built with -march=rva23u64.

gcc/
        * config/riscv/riscv-bclr-lowest-set-bit.cc
        (pass_bclr_lowest_set_bit::execute): Skip a decrement wrapped in
        a SIGN_EXTEND.

gcc/testsuite/
        * gcc.target/riscv/bclr-lowest-set-bit-1.c: Expect addi+and for
        the unsigned int cases on rv64.
        * gcc.target/riscv/bclr-lowest-set-bit-2.c: New test.

Signed-off-by: Sun Yuechi <[email protected]>
---

v2: skip the SIGN_EXTEND case instead of extending the result, and
update bclr-lowest-set-bit-1.c for rv64.

Regtested on riscv64-linux-gnu (16.1.0 tree), no regressions.

 gcc/config/riscv/riscv-bclr-lowest-set-bit.cc |  6 ++--
 .../gcc.target/riscv/bclr-lowest-set-bit-1.c  |  6 ++--
 .../gcc.target/riscv/bclr-lowest-set-bit-2.c  | 31 +++++++++++++++++++
 3 files changed, 38 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-2.c

diff --git a/gcc/config/riscv/riscv-bclr-lowest-set-bit.cc 
b/gcc/config/riscv/riscv-bclr-lowest-set-bit.cc
index ecfa511a882..4907518e2ee 100644
--- a/gcc/config/riscv/riscv-bclr-lowest-set-bit.cc
+++ b/gcc/config/riscv/riscv-bclr-lowest-set-bit.cc
@@ -193,10 +193,10 @@ pass_bclr_lowest_set_bit::execute (function *fn)
          rtx dec_src = SET_SRC (dec_set);
          rtx dec_dest = SET_DEST (dec_set);
 
-         /* For a 32 bit object on rv64, the decrement will
-            be wrapped by a SIGN_EXTEND.  Strip it.  */
+         /* For a 32 bit object on rv64 the decrement is wrapped by a
+            SIGN_EXTEND.  bclr would not keep the result sign extended.  */
          if (GET_CODE (dec_src) == SIGN_EXTEND)
-           dec_src = XEXP (dec_src, 0);
+           continue;
 
          /* Verify it's res = x - 1, if not proceed to the next insn.  */
          if (!dec_set
diff --git a/gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-1.c 
b/gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-1.c
index fa201cec359..211f3cb6a40 100644
--- a/gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-1.c
+++ b/gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-1.c
@@ -123,6 +123,8 @@ void foo12(unsigned long x)
   foo12_res2 = tz;
 }
 
-/* { dg-final { scan-assembler-not "\\sand\\s" } } */
-/* { dg-final { scan-assembler-times "\\sbclr\\s" 12 } } */
+/* { dg-final { scan-assembler-not "\\sand\\s" { target rv32 } } } */
+/* { dg-final { scan-assembler-times "\\sbclr\\s" 12 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "\\sand\\s" 6 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "\\sbclr\\s" 6 { target rv64 } } } */
 
diff --git a/gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-2.c 
b/gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-2.c
new file mode 100644
index 00000000000..0c36817c103
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/bclr-lowest-set-bit-2.c
@@ -0,0 +1,31 @@
+/* { dg-do run { target { rv64 && riscv_b_ok } } } */
+/* { dg-options "-march=rv64gc_zbb_zbs -mabi=lp64d -O2 -save-temps" } */
+
+/* If bclr replaces the addiw+and, this loop never terminates.  */
+
+extern void abort (void);
+
+__attribute__((noipa)) unsigned
+bit_walk (unsigned x)
+{
+  unsigned r = 0;
+  unsigned guard = 0;
+
+  for (unsigned m = x; m; m &= m - 1)
+    {
+      if (++guard > 32)
+       abort ();
+      r += (unsigned) __builtin_ctz (m);
+    }
+  return r;
+}
+
+int
+main (void)
+{
+  if (bit_walk (0x80000001u) != 31)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "\\sbclr\\s" } } */

base-commit: 919c0d16c91c930c374db6665f55595653511636
-- 
2.55.0

Reply via email to