[CRIS PATCH] Support bitreverse32, rotatesi3_16 and other swap variants.

"Roger Sayle" <[email protected]> Tue, 4 Aug 2026 19:43:39 +0100
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
This is a multipart message in MIME format.

------=_NextPart_000_01B9_01DD2449.8D660970
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit


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.


------=_NextPart_000_01B9_01DD2449.8D660970
Content-Type: text/plain;
	name="patchcr2.txt"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="patchcr2.txt"

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

------=_NextPart_000_01B9_01DD2449.8D660970--