Hi H-P and Jeff,
I appreciate that folks are on vacation at the moment, but I thought I'd
post this for the record, there's no rush/urgency on a review.

Version 8 of the Axis Communications' CRIS architecture contains a
very cool SWAP instruction.  This patch adds support for several more
variants not currently supported by the cris.md backend.

One example use of the swap function is to implement 32-bit rotate
by 16 bits.

unsigned int foo(unsigned int x)
{
  return (x >> 16) | (x << 16);
}

Previously with -O2 -march=v8, gcc would generate:

foo:    move.d $r10,$r9
        lsrq 16,$r9
        lslq 16,$r10
        ret
        add.d $r9,$r10

with this patch we now generate:

foo:    ret
        swapw $r10

Likewise, this instruction can be used to implement bitreverse
[cris.md currently uses this internally, but renaming it to a
standard optab name exposes it to the compiler].

unsigned int bar(unsigned int x)
{
  return __bitreverse32(x);
}

Previously with -O2 -march=v8 generated:

bar:    move.d $r10,$r9
        swapwb $r9
        move.d $r9,$r10
        lsrq 4,$r10
        and.d 252645135,$r10
        and.d 252645135,$r9
        lslq 4,$r9
        or.d $r9,$r10
        move.d $r10,$r9
        lsrq 2,$r9
        and.d 858993459,$r9
        and.d 858993459,$r10
        lslq 2,$r10
        or.d $r10,$r9
        move.d $r9,$r10
        lsrq 1,$r10
        and.d 1431655765,$r10
        and.d 1431655765,$r9
        lslq 1,$r9
        ret
        or.d $r9,$r10

with this patch, it now generates:

bar:    ret
        swapwbr $r10


This patch has been tested on a cross-compiler to cris-elf with
"make" and "make check RUNTESTFLAGS="--target_board=cris-sim" with
no new failures.  Ok for mainline?


2026-08-04  Roger Sayle  <[email protected]>

gcc/ChangeLog
        * gcc/config/cris/cris.md (<...>bswapsi2_not<...>): New define_insn.
        (cris_swap_bits): Rename to...
        (<...>bitreversesi2<...>): Renamed from cris_swap_bits.
        (<...>bitreversesi2_not<...>): New define_insn.
        (<...>rotsi2_16<...>): Likewise.
        (<...>rotsi2_16_not<...>): Likewise.

testsuite/ChangeLog
        * gcc.target/cris/swapnw_v3.c: New test case.
        * gcc.target/cris/swapnw_v8.c: Likewise.
        * gcc.target/cris/swapnwb_v3.c: Likewise.
        * gcc.target/cris/swapnwb_v8.c: Likewise.
        * gcc.target/cris/swapnwbr_v3.c: Likewise.
        * gcc.target/cris/swapnwbr_v8.c: Likewise.
        * gcc.target/cris/swapw_v3.c: Likewise.
        * gcc.target/cris/swapw_v8.c: Likewise.
        * gcc.target/cris/swapwbr_v3.c: Likewise.
        * gcc.target/cris/swapwbr_v8.c: Likewise.

diff --git a/gcc/config/cris/cris.md b/gcc/config/cris/cris.md
index 83b60fbeca4e..4f944d447b76 100644
--- a/gcc/config/cris/cris.md
+++ b/gcc/config/cris/cris.md
@@ -2195,11 +2195,20 @@
   "swapwb %0"
   [(set_attr "slottable" "yes")])
 
+(define_insn "<acc><anz><anzvc>bswapsi2_not<setcc><setnz><setnzvc>"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (bswap:SI
+         (not:SI (match_operand:SI 1 "register_operand" "0"))))
+   (clobber (reg:CC CRIS_CC0_REGNUM))]
+  "TARGET_HAS_SWAP"
+  "swapnwb %0"
+  [(set_attr "slottable" "yes")])
+
 ;; This instruction swaps all bits in a register.
 ;; That means that the most significant bit is put in the place
 ;; of the least significant bit, and so on.
 
-(define_insn "cris_swap_bits"
+(define_insn "<acc><anz><anzvc>bitreversesi2<setcc><setnz><setnzvc>"
   [(set (match_operand:SI 0 "register_operand" "=r")
        (bitreverse:SI (match_operand:SI 1 "register_operand" "0")))
    (clobber (reg:CC CRIS_CC0_REGNUM))]
@@ -2207,6 +2216,35 @@
   "swapwbr %0"
   [(set_attr "slottable" "yes")])
 
+;; Takes 2-cycles but is shorter than a BITREVERSE and a NOT.
+(define_insn "<acc><anz><anzvc>bitreversesi2_not<setcc><setnz><setnzvc>"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (bitreverse:SI
+         (not:SI (match_operand:SI 1 "register_operand" "0"))))
+   (clobber (reg:CC CRIS_CC0_REGNUM))]
+  "TARGET_HAS_SWAP"
+  "swapnwbr %0"
+  [(set_attr "slottable" "yes")])
+
+(define_insn "<acc><anz><anzvc>rotsi2_16<setcc><setnz><setnzvc>"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (rotate:SI (match_operand:SI 1 "register_operand" "0")
+                  (const_int 16)))
+   (clobber (reg:CC CRIS_CC0_REGNUM))]
+  "TARGET_HAS_SWAP"
+  "swapw %0"
+  [(set_attr "slottable" "yes")])
+
+(define_insn "<acc><anz><anzvc>rotsi2_16_not<setcc><setnz><setnzvc>"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (not:SI
+         (rotate:SI (match_operand:SI 1 "register_operand" "0")
+                    (const_int 16))))
+   (clobber (reg:CC CRIS_CC0_REGNUM))]
+  "TARGET_HAS_SWAP"
+  "swapnw %0"
+  [(set_attr "slottable" "yes")])
+
 ;; Implement ctz using two instructions, one for bit swap and one for clz.
 ;; Defines a scratch register to avoid clobbering input.
 
diff --git a/gcc/testsuite/gcc.target/cris/swapnw_v3.c 
b/gcc/testsuite/gcc.target/cris/swapnw_v3.c
new file mode 100644
index 000000000000..057b7ef5eb1c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapnw_v3.c
@@ -0,0 +1,20 @@
+/* Check that we don't use the swap insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v3" } */
+/* { dg-final { scan-assembler-not "\[ \t\]swapnw\[ \t\]" } } */
+
+unsigned int foo(unsigned int x)
+{
+  unsigned int t = ~x;
+  t = (t >> 16) | (t << 16);
+  return t;
+}
+
+unsigned int bar(unsigned int x)
+{
+  unsigned int t = x;
+  t = (t >> 16) | (t << 16);
+  return ~t;
+}
diff --git a/gcc/testsuite/gcc.target/cris/swapnw_v8.c 
b/gcc/testsuite/gcc.target/cris/swapnw_v8.c
new file mode 100644
index 000000000000..8a4810aa776e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapnw_v8.c
@@ -0,0 +1,20 @@
+/* Check that we use the swapnw insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v8" } */
+/* { dg-final { scan-assembler-times "\[ \t\]swapnw\[ \t\]" 2 } } */
+
+unsigned int foo(unsigned int x)
+{
+  unsigned int t = ~x;
+  t = (t >> 16) | (t << 16);
+  return t;
+}
+
+unsigned int bar(unsigned int x)
+{
+  unsigned int t = x;
+  t = (t >> 16) | (t << 16);
+  return ~t;
+}
diff --git a/gcc/testsuite/gcc.target/cris/swapnwb_v3.c 
b/gcc/testsuite/gcc.target/cris/swapnwb_v3.c
new file mode 100644
index 000000000000..b5ec7a471bc9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapnwb_v3.c
@@ -0,0 +1,16 @@
+/* Check that we don't use the swap insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v3" } */
+/* { dg-final { scan-assembler-not "\[ \t\]swapnwb\[ \t\]" } } */
+
+unsigned int foo(unsigned int x)
+{
+  return __builtin_bswap32(~x);
+}
+
+unsigned int bar(unsigned int x)
+{
+  return ~__builtin_bswap32(x);
+}
diff --git a/gcc/testsuite/gcc.target/cris/swapnwb_v8.c 
b/gcc/testsuite/gcc.target/cris/swapnwb_v8.c
new file mode 100644
index 000000000000..80bbb3d22f4d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapnwb_v8.c
@@ -0,0 +1,16 @@
+/* Check that we use the swapnwb insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v8" } */
+/* { dg-final { scan-assembler-times "\[ \t\]swapnwb\[ \t\]" 2 } } */
+
+unsigned int foo(unsigned int x)
+{
+  return __builtin_bswap32(~x);
+}
+
+unsigned int bar(unsigned int x)
+{
+  return ~__builtin_bswap32(x);
+}
diff --git a/gcc/testsuite/gcc.target/cris/swapnwbr_v3.c 
b/gcc/testsuite/gcc.target/cris/swapnwbr_v3.c
new file mode 100644
index 000000000000..35bd7d4db77f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapnwbr_v3.c
@@ -0,0 +1,16 @@
+/* Check that we don't use the swap insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v3" } */
+/* { dg-final { scan-assembler-not "\[ \t\]swapnwbr\[ \t\]" } } */
+
+unsigned int foo(unsigned int x)
+{
+  return __builtin_bitreverse32(~x);
+}
+
+unsigned int bar(unsigned int x)
+{
+  return ~__builtin_bitreverse32(x);
+}
diff --git a/gcc/testsuite/gcc.target/cris/swapnwbr_v8.c 
b/gcc/testsuite/gcc.target/cris/swapnwbr_v8.c
new file mode 100644
index 000000000000..9cddf6b4c0b9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapnwbr_v8.c
@@ -0,0 +1,16 @@
+/* Check that we use the swapnwbr insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v8" } */
+/* { dg-final { scan-assembler-times "\[ \t\]swapnwbr\[ \t\]" 2 } } */
+
+unsigned int foo(unsigned int x)
+{
+  return __builtin_bitreverse32(~x);
+}
+
+unsigned int bar(unsigned int x)
+{
+  return ~__builtin_bitreverse32(x);
+}
diff --git a/gcc/testsuite/gcc.target/cris/swapw_v3.c 
b/gcc/testsuite/gcc.target/cris/swapw_v3.c
new file mode 100644
index 000000000000..d816b58ce625
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapw_v3.c
@@ -0,0 +1,10 @@
+/* Check that we don't use the swap insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v3" } */
+/* { dg-final { scan-assembler-not "\[ \t\]swapw\[ \t\]" } } */
+
+unsigned int rot16_ior(unsigned int x) { return (x >> 16) | (x << 16); }
+unsigned int rot16_xor(unsigned int x) { return (x >> 16) ^ (x << 16); }
+unsigned int rot16_add(unsigned int x) { return (x >> 16) + (x << 16); }
diff --git a/gcc/testsuite/gcc.target/cris/swapw_v8.c 
b/gcc/testsuite/gcc.target/cris/swapw_v8.c
new file mode 100644
index 000000000000..be867fe04e12
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapw_v8.c
@@ -0,0 +1,10 @@
+/* Check that we use the swapw insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v8" } */
+/* { dg-final { scan-assembler-times "\[ \t\]swapw\[ \t\]" 3 } } */
+
+unsigned int rot16_ior(unsigned int x) { return (x >> 16) | (x << 16); }
+unsigned int rot16_xor(unsigned int x) { return (x >> 16) ^ (x << 16); }
+unsigned int rot16_add(unsigned int x) { return (x >> 16) + (x << 16); }
diff --git a/gcc/testsuite/gcc.target/cris/swapwbr_v3.c 
b/gcc/testsuite/gcc.target/cris/swapwbr_v3.c
new file mode 100644
index 000000000000..e2cb862b6e0c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapwbr_v3.c
@@ -0,0 +1,12 @@
+/* Check that we don't use the swap insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v3" } */
+/* { dg-final { scan-assembler-not "\[ \t\]swapwbr\[ \t\]" } } */
+
+unsigned int foo(unsigned int x)
+{
+  return __builtin_bitreverse32(x);
+}
+
diff --git a/gcc/testsuite/gcc.target/cris/swapwbr_v8.c 
b/gcc/testsuite/gcc.target/cris/swapwbr_v8.c
new file mode 100644
index 000000000000..a022fc12c4eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/cris/swapwbr_v8.c
@@ -0,0 +1,12 @@
+/* Check that we use the swapwbr insn by checking assembler output.
+   The swap instruction was added in v8.  */
+/* { dg-do compile } */
+/* { dg-skip-if "" { "cris*-*-elf" } { "-march*" } { "" } } */
+/* { dg-options "-O2 -march=v8" } */
+/* { dg-final { scan-assembler "\[ \t\]swapwbr\[ \t\]" } } */
+
+unsigned int foo(unsigned int x)
+{
+  return __builtin_bitreverse32(x);
+}
+

Reply via email to