https://gcc.gnu.org/g:04742e434ec81c26cd52f81e8d5c1832d55bb16b

commit r17-1679-g04742e434ec81c26cd52f81e8d5c1832d55bb16b
Author: Jeff Law <[email protected]>
Date:   Thu Jun 18 16:56:31 2026 -0600

    [RISC-V] Split X eq/ne C where -C is a small constant
    
    This was something I found while analyzing paths forward for a patch from
    Daniel.
    
    Amazingly, RISC-V does not have anything like a setCC style insn that 
compares
    a register against a constant.  Instead we negate the constant and add it to
    the source value.  That gives us zero (equal) or nonzero (not equal).  We
    follow that with a snez/seqz to give us 0/1 like other setCC style
    instructions.
    
    If we have a 3 or more insns that ultimately combine into something like:
    
    (set (dest) (eq (srcreg) (const_int))
    
    We can use a define_split to rewrite that into a two instruction sequence 
which
    is a small win.  I'd suspected there was some value in this kind of splitter
    for a while, but never had a testcase that could actually be improved.  I 
wrote
    the splitter and tested with Daniel's code, but more importantly, once I had
    the basic splitter working, I could do a before/after comparison and look 
for
    differences which I was able to find.
    
    While the testcase came from 502.gcc, it's not hot at all.  But it does 
clearly
    show how the splitter can improve code.
    
    Tested on riscv32-elf and riscv64-elf.  Bootstraps on the K3 and c920 are in
    flight.  I'll wait for the bootstrap/regression tests as well as the 
pre-commit
    CI testing before moving forward.
    
    gcc/
            * config/riscv/riscv.md (splitter for equality test): New splitter.
    
    gcc/testsuite/
            * gcc.target/riscv/test-equal.c: New test.

Diff:
---
 gcc/config/riscv/riscv.md                   | 18 ++++++++++++++++++
 gcc/testsuite/gcc.target/riscv/test-equal.c | 24 ++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 88defdd43f0e..b90bfe0745a9 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -5338,6 +5338,24 @@
   operands[7] = gen_lowpart (SImode, operands[6]);
 })
 
+;; If through a series of combinations/simplifications we ultimately
+;; recover an equality test against a small constant we can win because
+;; that's a 2 instruction sequence.  addi to set a zero/nonzero status
+;; followed be seqz/snez to canonicalize into 0/1.
+;;
+;; Since we're going to use the negated constant in an addi to get the
+;; zero/nonzero status we need to verify the negated constant is a
+;; small operand, not the original constant.
+(define_split
+  [(set (match_operand:X 0 "register_operand")
+       (any_eq:X (match_operand:X 1 "register_operand")
+                 (match_operand 2 "const_int_operand")))]
+  "(SMALL_OPERAND (-UINTVAL (operands[2]))
+    && operands[2] != CONST0_RTX (GET_MODE (operands[1])))"
+  [(set (match_dup 0) (plus:X (match_dup 1) (match_dup 2)))
+   (set (match_dup 0) (any_eq:X (match_dup 0) (const_int 0)))]
+{ operands[2] = GEN_INT (-UINTVAL (operands[2])); })
+
 (include "bitmanip.md")
 (include "crypto.md")
 (include "sync.md")
diff --git a/gcc/testsuite/gcc.target/riscv/test-equal.c 
b/gcc/testsuite/gcc.target/riscv/test-equal.c
new file mode 100644
index 000000000000..a65019d9322f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/test-equal.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcbv_zicond -mabi=lp64d" { target rv64 } } */
+/* { dg-options "-march=rv32gcbv_zicond -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og"} } */
+
+enum machine_mode
+{
+  VOIDmode,
+  MAX_MACHINE_MODE,
+  NUM_MACHINE_MODES = MAX_MACHINE_MODE
+};
+extern unsigned char mode_size[NUM_MACHINE_MODES];
+void oof (enum machine_mode);
+void
+init_emit_once (enum machine_mode double_mode, enum machine_mode mode)
+{
+      if (((unsigned short) (((unsigned short) mode_size[mode]) * 8)) == 64
+          && double_mode == VOIDmode)
+        double_mode = mode;
+  oof (double_mode);
+}
+/* { dg-final { scan-assembler-not "addi\t\[a-x0-9\]+,\[a-x0-9\]+,-64" } } */
+/* { dg-final { scan-assembler "addi\t\[a-x0-9\]+,\[a-x0-9\]+,-8" } } */
+

Reply via email to