gcc - GNU Compiler Collection (original) (raw)
| author | Jeff Law jeffrey.law@oss.qualcomm.com | 2026-06-18 16:56:31 -0600 |
|---|---|---|
| committer | Jeff Law jeffrey.law@oss.qualcomm.com | 2026-06-18 16:56:31 -0600 |
| commit | 04742e434ec81c26cd52f81e8d5c1832d55bb16b (patch) | |
| tree | 79896512550425f5b42daa2df7383b20cbf2f2ec | |
| parent | libgomp: Prototype "accel" 'GOMP_INDIRECT_ADDR_MAP', 'GOMP_INDIRECT_ADDR_HMAP... (diff) |
[RISC-V] Split X eq/ne C where -C is a small constantHEADtrunkmaster
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.
| -rw-r--r-- | gcc/config/riscv/riscv.md | 18 |
|---|---|---|
| -rw-r--r-- | gcc/testsuite/gcc.target/riscv/test-equal.c | 24 |
2 files changed, 42 insertions, 0 deletions
| diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.mdindex 88defdd43f0e..b90bfe0745a9 100644--- a/gcc/config/riscv/riscv.md+++ b/gcc/config/riscv/riscv.md | |||
|---|---|---|---|
| @@ -5338,6 +5338,24 @@ | |||
| 5338 | operands[7] = gen_lowpart (SImode, operands[6]); | 5338 | operands[7] = gen_lowpart (SImode, operands[6]); |
| 5339 | }) | 5339 | }) |
| 5340 | 5340 | ||
| 5341 | ;; If through a series of combinations/simplifications we ultimately | ||
| 5342 | ;; recover an equality test against a small constant we can win because | ||
| 5343 | ;; that's a 2 instruction sequence. addi to set a zero/nonzero status | ||
| 5344 | ;; followed be seqz/snez to canonicalize into 0/1. | ||
| 5345 | ;; | ||
| 5346 | ;; Since we're going to use the negated constant in an addi to get the | ||
| 5347 | ;; zero/nonzero status we need to verify the negated constant is a | ||
| 5348 | ;; small operand, not the original constant. | ||
| 5349 | (define_split | ||
| 5350 | [(set (match_operand:X 0 "register_operand") | ||
| 5351 | (any_eq:X (match_operand:X 1 "register_operand") | ||
| 5352 | (match_operand 2 "const_int_operand")))] | ||
| 5353 | "(SMALL_OPERAND (-UINTVAL (operands[2])) | ||
| 5354 | && operands[2] != CONST0_RTX (GET_MODE (operands[1])))" | ||
| 5355 | [(set (match_dup 0) (plus:X (match_dup 1) (match_dup 2))) | ||
| 5356 | (set (match_dup 0) (any_eq:X (match_dup 0) (const_int 0)))] | ||
| 5357 | { operands[2] = GEN_INT (-UINTVAL (operands[2])); }) | ||
| 5358 | |||
| 5341 | (include "bitmanip.md") | 5359 | (include "bitmanip.md") |
| 5342 | (include "crypto.md") | 5360 | (include "crypto.md") |
| 5343 | (include "sync.md") | 5361 | (include "sync.md") |
| diff --git a/gcc/testsuite/gcc.target/riscv/test-equal.c b/gcc/testsuite/gcc.target/riscv/test-equal.cnew file mode 100644index 000000000000..a65019d9322f--- /dev/null+++ b/gcc/testsuite/gcc.target/riscv/test-equal.c | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* { dg-do compile } */ | ||
| 2 | /* { dg-options "-march=rv64gcbv_zicond -mabi=lp64d" { target rv64 } } */ | ||
| 3 | /* { dg-options "-march=rv32gcbv_zicond -mabi=ilp32" { target rv32 } } */ | ||
| 4 | /* { dg-skip-if "" { *-*-* } { "-O0" "-Og"} } */ | ||
| 5 | |||
| 6 | enum machine_mode | ||
| 7 | { | ||
| 8 | VOIDmode, | ||
| 9 | MAX_MACHINE_MODE, | ||
| 10 | NUM_MACHINE_MODES = MAX_MACHINE_MODE | ||
| 11 | }; | ||
| 12 | extern unsigned char mode_size[NUM_MACHINE_MODES]; | ||
| 13 | void oof (enum machine_mode); | ||
| 14 | void | ||
| 15 | init_emit_once (enum machine_mode double_mode, enum machine_mode mode) | ||
| 16 | { | ||
| 17 | if (((unsigned short) (((unsigned short) mode_size[mode]) * 8)) == 64 | ||
| 18 | && double_mode == VOIDmode) | ||
| 19 | double_mode = mode; | ||
| 20 | oof (double_mode); | ||
| 21 | } | ||
| 22 | /* { dg-final { scan-assembler-not "addi\t\[a-x0-9\]+,\[a-x0-9\]+,-64" } } */ | ||
| 23 | /* { dg-final { scan-assembler "addi\t\[a-x0-9\]+,\[a-x0-9\]+,-8" } } */ | ||
| 24 |