]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[RISC-V][PR rtl-optimization/56096] Improve equality comparisons of a logical AND...
authorJeff Law <jeffrey.law@oss.qualcomm.com>
Sun, 26 Apr 2026 00:12:27 +0000 (18:12 -0600)
committerJeff Law <jeffrey.law@oss.qualcomm.com>
Sun, 26 Apr 2026 00:15:05 +0000 (18:15 -0600)
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.

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

index 52f52c400cc7ee35345d91bd3a916fa5209fd533..c350283fa344b00af7f6b49354bad1156730c61e 100644 (file)
   [(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 (file)
index 0000000..76ccd0b
--- /dev/null
@@ -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 } } */