[CRIS] Support bitreverse32, rotatesi3_16 and other swap variants.
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
Commit Message
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 <roger@nextmovesoftware.com>
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.
@@ -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.
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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); }
new file mode 100644
@@ -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); }
new file mode 100644
@@ -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);
+}
+
new file mode 100644
@@ -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);
+}
+