]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[V3][RISC-V][PR rtl-optimization/96692] Improve xor+xor+ior sequence when possible
authorJeff Law <jeffrey.law@oss.qualcomm.com>
Fri, 1 May 2026 03:37:34 +0000 (21:37 -0600)
committerJeff Law <jeffrey.law@oss.qualcomm.com>
Fri, 1 May 2026 03:37:34 +0000 (21:37 -0600)
Consider this code:

int f(int a, int b, int c)
{
    return (a ^ b) ^ (a | c);
}

For RISC-V we generate something like this:

        xor     a1,a0,a1
        or      a0,a0,a2
        xor     a0,a1,a0

But this would be better:

        andn    a0,a2,a0
        xor     a0,a0,a1

It looks like Roger tackled this earlier with splitters for x86. I'd have
leaned more towards simplify-rtx, but there may be secondary concerns at play.
So I'll attack in the RISC-V target files in a similar manner.

The patch, but not the testcase, have been in my tester for a while, so it's
been bootstrapped and regression tested on the Pioneer and BPI-F3 board and
regression tested on riscv32-elf and riscv64-elf. Obviously I'll wait for
pre-commit CI before moving forward.

PR rtl-optimization/96692
gcc/
* config/riscv/bitmanip.md (xor+xor+ior splitters): New splitters
that ultimately generate andn+xor when possible.

gcc/testsuite

* gcc.target/riscv/pr96692.c: New test.

gcc/config/riscv/bitmanip.md
gcc/testsuite/gcc.target/riscv/pr96692.c [new file with mode: 0644]

index c9561b0b622805817887655573a58166e426ffd9..79ab17b6a60f1c9ee2ac5893a93d8a0758cb1ca1 100644 (file)
     operands[3] = gen_lowpart (DImode, operands[3]);
     operands[6] = gen_lowpart (SImode, operands[5]);
   })
+
+(define_split
+  [(set (match_operand:X 0 "register_operand")
+        (xor:X (xor:X (ior:X (match_operand:X 1 "register_operand")
+                             (match_operand:X 2 "register_operand"))
+                      (match_dup 1))
+               (match_operand:X 3 "register_operand")))
+   (clobber (match_operand:X 4 "register_operand"))]
+  "TARGET_ZBB || TARGET_ZBKB"
+  [(set (match_dup 4) (and:X (not:X (match_dup 1)) (match_dup 2)))
+   (set (match_dup 0) (xor:X (match_dup 4) (match_dup 3)))])
+
+(define_split
+  [(set (match_operand:X 0 "register_operand")
+        (xor:X (xor:X (ior:X (match_operand:X 1 "register_operand")
+                             (match_operand:X 2 "register_operand"))
+                      (match_dup 2))
+               (match_operand:X 3 "register_operand")))
+   (clobber (match_operand:X 4 "register_operand"))]
+  "TARGET_ZBB || TARGET_ZBKB"
+  [(set (match_dup 4) (and:X (not:X (match_dup 2)) (match_dup 1)))
+   (set (match_dup 0) (xor:X (match_dup 4) (match_dup 3)))])
+
diff --git a/gcc/testsuite/gcc.target/riscv/pr96692.c b/gcc/testsuite/gcc.target/riscv/pr96692.c
new file mode 100644 (file)
index 0000000..650f4f0
--- /dev/null
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv64gcb_zicond -mabi=lp64d" { target rv64 } } */
+/* { dg-additional-options "-march=rv32gcb_zicond -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" } } */
+
+int f(int a, int b, int c)
+{
+    return (a ^ b) ^ (a | c);
+}
+
+/* { dg-final { scan-assembler-times "xor\t" 1 } } */
+/* { dg-final { scan-assembler-times "andn\t" 1 } } */