> From: Hans-Peter Nilsson <[email protected]>
> Date: Tue, 25 Aug 2026 17:50:01 +0200

> I'll chew on this, to minimize the amount of Columboism on my part and
> likely commit it later today.

...for a different value of today.

This is what I'm going to commit in your name, unless I hear
objections within 48h (JFTR: unrelated to approval rights).  Sooner if
ACK:ed.  Compared to your posted version, in addition to the
previously mentioned changes, it has an otherwise corrected changelog
entry, minor changes in indentation (spacing to match up parentheses),
plus a blurb about order of not vs. bitreverse or rotate that's IMHO
is too short for my name to be added.  Tested cross to cris-elf.
Thanks again!

brgds, H-P

-- >8 --
From: Roger Sayle <[email protected]>
Subject: [PATCH] CRIS: Support bitreverse32, rotatesi3_16 and other swap 
variants

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

Beware of the differences in location of the bitwise negation ("not",
cf. bitreversesi2_not and rotsi2_16 patterns).  This is just what
combine currently recognizes.  The canonical expression of "not"
relative to these operations is currently not explicitly defined (in
md.texi), and combine happens to associate it in different directions
for each rotate and bitreverse.

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

gcc/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.
---
 gcc/config/cris/cris.md                     | 40 ++++++++++++++++++++-
 gcc/testsuite/gcc.target/cris/swapnw_v3.c   | 20 +++++++++++
 gcc/testsuite/gcc.target/cris/swapnw_v8.c   | 20 +++++++++++
 gcc/testsuite/gcc.target/cris/swapnwb_v3.c  | 16 +++++++++
 gcc/testsuite/gcc.target/cris/swapnwb_v8.c  | 16 +++++++++
 gcc/testsuite/gcc.target/cris/swapnwbr_v3.c | 16 +++++++++
 gcc/testsuite/gcc.target/cris/swapnwbr_v8.c | 16 +++++++++
 gcc/testsuite/gcc.target/cris/swapw_v3.c    | 10 ++++++
 gcc/testsuite/gcc.target/cris/swapw_v8.c    | 10 ++++++
 gcc/testsuite/gcc.target/cris/swapwbr_v3.c  | 12 +++++++
 gcc/testsuite/gcc.target/cris/swapwbr_v8.c  | 12 +++++++
 11 files changed, 187 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/cris/swapnw_v3.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapnw_v8.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapnwb_v3.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapnwb_v8.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapnwbr_v3.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapnwbr_v8.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapw_v3.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapw_v8.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapwbr_v3.c
 create mode 100644 gcc/testsuite/gcc.target/cris/swapwbr_v8.c

diff --git a/gcc/config/cris/cris.md b/gcc/config/cris/cris.md
index 2935ac18881b..1bb6ae96b1f7 100644
--- a/gcc/config/cris/cris.md
+++ b/gcc/config/cris/cris.md
@@ -2199,11 +2199,20 @@ (define_insn 
"<acc><anz><anzvc>bswapsi2<setcc><setnz><setnzvc>"
   "swapwb %0"
   [(set_attr "slottable" "yes")])
 
+(define_insn "*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))]
@@ -2211,6 +2220,35 @@ (define_insn "cris_swap_bits"
   "swapwbr %0"
   [(set_attr "slottable" "yes")])
 
+;; Takes an cycle extra but is shorter than a BITREVERSE and a NOT.
+(define_insn "*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 "*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 "*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);
+}
+
-- 
2.43.0

Reply via email to