[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--