From: Jeff Law Date: Sun, 26 Apr 2026 00:12:27 +0000 (-0600) Subject: [RISC-V][PR rtl-optimization/56096] Improve equality comparisons of a logical AND... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5de59c6d8267ddfefec34317a67fa96b4f0900e2;p=thirdparty%2Fgcc.git [RISC-V][PR rtl-optimization/56096] Improve equality comparisons of a logical AND expressions This BZ shows that we can improve certain comparisons for RISC-V. In particular if we are testing the result of a logical AND for equality and one operand of the AND requires synthesis, we may be able to do better if we right shift away any trailing zeros from the constant and shift the other input as well. This wins when the shifted constant does not require synthesis. That may in turn allow improvement of a select of 0 and 2^n based on the zero/nonzero status of a logical AND. Essentially we can rewrite the sequence to remove a data dependency. Concretely: > > unsigned f1 (unsigned x, unsigned m) > { > x >>= ((m & 0x008080) ? 8 : 0); > return x; > } Compiles into: > li a5,32768 > addi a5,a5,128 > and a1,a1,a5 > snez a1,a1 > slliw a1,a1,3 > srlw a0,a0,a1 > ret But after this patch we generate this instead: > srai a5,a1,7 > andi a5,a5,257 > li a4,8 > czero.eqz a1,a4,a5 > srlw a0,a0,a1 > ret It's just one less instruction, but the li can issue whenever the uarch wants before the srlw as it has no incoming dependency. So we're slight more dense on encoding and slightly more efficient as well. Much like 57650, I'm focused on the low level RISC-V codegen issues, not the broader issues that are raised in the PR. This has been in my tree for a while, so it's been tested on riscv32-elf, riscv64-elf and bootstrapped on the BPI which has support for czero. Waiting on pre-commit CI before moving forward. PR rtl-optimization/56096 gcc/ * config/riscv/riscv.md: Add new patterns to optimize certain cases with a logical AND feeding an equality test against zero. gcc/testsuite/ * gcc.target/riscv/pr56096.c: New test. --- diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index 52f52c400cc..c350283fa34 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -3201,6 +3201,62 @@ [(set_attr "type" "shift") (set_attr "mode" "DI")]) +;; Handle logical AND feeding an equality test against zero where an operand +;; to the AND is a constant requiring synthesis. Because we only care about +;; zero/nonzero state afte the AND, we may be able to shift both operands +;; of the AND to the right and eliminate the need for constant synthesis. +;; +;; Once mvconst_internal goes away, this likely turns into a simple splitter. +(define_insn_and_split "" + [(set (match_operand:X 0 "register_operand" "=r") + (any_eq:X (and:X (match_operand:X 1 "register_operand" "r") + (match_operand 2 "shifted_const_arith_operand")) + (const_int 0))) + (clobber (match_scratch:X 3 "=&r"))] + "!SMALL_OPERAND (INTVAL (operands[2]))" + "#" + "&& reload_completed" + [(set (match_dup 3) (ashiftrt:X (match_dup 1) (match_dup 4))) + (set (match_dup 3) (and:X (match_dup 3) (match_dup 2))) + (set (match_dup 0) (any_eq:X (match_dup 3) (const_int 0)))] +{ + HOST_WIDE_INT shift = ctz_hwi (INTVAL (operands[2])); + operands[4] = gen_int_mode (shift, QImode); + operands[2] = gen_int_mode (INTVAL (operands[2]) >> shift, word_mode); +} + [(set_attr "type" "shift")]) + +;; The pattern above is a bridge to this pattern. Essentially a select +;; between 0 and 2^n based on the zero/nonzero status of the AND. +;; +;; It's no fewer instructions, but the resulting code has fewer data +;; dependencies and may compress better depending on 2^n. +(define_insn_and_split "" + [(set (match_operand:X 0 "register_operand" "=r") + (ashift:X (any_eq:X + (and:X (match_operand:X 1 "register_operand" "r") + (match_operand 2 "shifted_const_arith_operand")) + (const_int 0)) + (match_operand 3 "const_int_operand"))) + (clobber (match_scratch:X 4 "=&r")) + (clobber (match_scratch:X 5 "=&r"))] + "TARGET_ZICOND && TARGET_ZBS" + "#" + "&& reload_completed" + [(set (match_dup 4) (ashiftrt:X (match_dup 1) (match_dup 6))) + (set (match_dup 4) (and:X (match_dup 4) (match_dup 2))) + (set (match_dup 5) (match_dup 3)) + (set (match_dup 0) (if_then_else:X (any_eq:X (match_dup 4) (const_int 0)) + (match_dup 5) + (const_int 0)))] +{ + HOST_WIDE_INT shift = ctz_hwi (INTVAL (operands[2])); + operands[3] = gen_int_mode (HOST_WIDE_INT_1U << INTVAL (operands[3]), word_mode); + operands[6] = gen_int_mode (shift, QImode); + operands[2] = gen_int_mode (INTVAL (operands[2]) >> shift, word_mode); +} + [(set_attr "type" "shift")]) + ;; ;; .................... ;; diff --git a/gcc/testsuite/gcc.target/riscv/pr56096.c b/gcc/testsuite/gcc.target/riscv/pr56096.c new file mode 100644 index 00000000000..76ccd0b03ce --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr56096.c @@ -0,0 +1,19 @@ +/* { 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" } } */ + +unsigned f1 (unsigned x, unsigned m) +{ + x >>= ((m & 0x008080) ? 8 : 0); + return x; +} + +/* { dg-final { scan-assembler-not "addi\t" } } */ +/* { dg-final { scan-assembler-not "and\t" } } */ +/* { dg-final { scan-assembler-not "snez\t" } } */ +/* { dg-final { scan-assembler-not "slli\t" } } */ +/* { dg-final { scan-assembler-not "slliw\t" } } */ +/* { dg-final { scan-assembler-times "srai\t" 1 } } */ +/* { dg-final { scan-assembler-times "andi\t" 1 } } */ +/* { dg-final { scan-assembler-times "czero" 1 } } */