]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[RISC-V] Improve sequences to generate -1, 1 in some cases.
authorJeff Law <jlaw@ventanamicro.com>
Thu, 5 Jun 2025 12:17:25 +0000 (06:17 -0600)
committerJeff Law <jlaw@ventanamicro.com>
Thu, 5 Jun 2025 12:17:25 +0000 (06:17 -0600)
This patch has a minor improvement to if-converted sequences based on
observations I found while evaluating another patch from Shreya to handle more
cases with zicond insns.

Specifically there is a smaller/faster way than zicond to generate a -1,1
result when the condition is testing the sign bit.

So let's consider these two tests (rv64):

long foo1 (long c, long a) { return c >= 0 ? 1 : -1; }
long foo2 (long c, long a) { return c < 0 ? -1 : 1; }

So if we right arithmetic shift c by 63 bits, that splats the sign bit across a
register giving us 0, -1 for the first test and -1, 0 for the second test.  We
then unconditionally turn on the LSB resulting in 1, -1 for the first case and
-1, 1 for the second.

This is implemented as a 4->2 splitter.  There's another pair of cases we don't
handle because we don't have 4->3 splitters.  Specifically if the true/false
values are reversed in the above examples without reversing the condition.

Raphael is playing a bit in the gimple space to see what opportunities might
exist to recognize more idioms in phiopt and generate better code earlier.  No
idea how that's likely to pan out.

This is a pretty consistent small win.  It's been through the rounds in my
tester.  Just waiting on a green light from pre-commit testing.

gcc/
* config/riscv/zicond.md: Add new splitters to select
1, -1 or -1, 1 based on a sign bit test.

gcc/testsuite/

* gcc.target/riscv/nozicond-1.c: New test.
* gcc.target/riscv/nozicond-2.c: New test.

gcc/config/riscv/zicond.md
gcc/testsuite/gcc.target/riscv/nozicond-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/riscv/nozicond-2.c [new file with mode: 0644]

index f87b4f25c0b08ebe423f731edb1f23dd336b0d65..d170f6ab262844ab240748b4a462e57431767173 100644 (file)
                                      (const_int 0)
                                      (match_dup 4)))])
 
+;; We can splat the sign bit across a GPR with a arithmetic right shift
+;; which gives us a 0, -1 result.  We then turn on bit #0 unconditionally
+;; which results in 1, -1.  There's probably other cases that could be
+;; handled, this seems particularly important though.
+(define_split
+  [(set (match_operand:X 0 "register_operand")
+       (plus:X (if_then_else:X (ge:X (match_operand:X 1 "register_operand")
+                                     (const_int 0))
+                               (match_operand 2 "const_int_operand")
+                               (match_operand 3 "const_int_operand"))
+               (match_operand 4 "const_int_operand")))]
+  "((TARGET_ZICOND_LIKE || TARGET_XTHEADCONDMOV)
+    && INTVAL (operands[2]) + INTVAL (operands[4]) == 1
+    && INTVAL (operands[3]) + INTVAL (operands[4]) == -1)"
+  [(set (match_dup 0) (ashiftrt:X (match_dup 1) (match_dup 2)))
+   (set (match_dup 0) (ior:X (match_dup 0) (const_int 1)))]
+  { operands[2] = GEN_INT (GET_MODE_BITSIZE (word_mode) - 1); })
 
-
+;; Similarly, but the condition and true/false values are reversed
+;;
+;; Note the case where the condition is reversed, but not the true/false
+;; values.  Or vice-versa is not handled because we don't support 4->3
+;; splits.
+(define_split
+  [(set (match_operand:X 0 "register_operand")
+       (plus:X (if_then_else:X (lt:X (match_operand:X 1 "register_operand")
+                                     (const_int 0))
+                               (match_operand 2 "const_int_operand")
+                               (match_operand 3 "const_int_operand"))
+               (match_operand 4 "const_int_operand")))]
+  "((TARGET_ZICOND_LIKE || TARGET_XTHEADCONDMOV)
+    && INTVAL (operands[2]) + INTVAL (operands[4]) == -1
+    && INTVAL (operands[3]) + INTVAL (operands[4]) == 1)"
+  [(set (match_dup 0) (ashiftrt:X (match_dup 1) (match_dup 2)))
+   (set (match_dup 0) (ior:X (match_dup 0) (const_int 1)))]
+  { operands[2] = GEN_INT (GET_MODE_BITSIZE (word_mode) - 1); })
diff --git a/gcc/testsuite/gcc.target/riscv/nozicond-1.c b/gcc/testsuite/gcc.target/riscv/nozicond-1.c
new file mode 100644 (file)
index 0000000..35ab6fe
--- /dev/null
@@ -0,0 +1,11 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-additional-options "-march=rv64gc_zicond -mabi=lp64d -mbranch-cost=4" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+
+
+long foo1 (long c) { return c >= 0 ? 1 : -1; }
+long foo2 (long c) { return c < 0 ? -1 : 1; }
+
+/* { dg-final { scan-assembler-times {srai\t}  2 } } */
+/* { dg-final { scan-assembler-times {ori\t}  2 } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/nozicond-2.c b/gcc/testsuite/gcc.target/riscv/nozicond-2.c
new file mode 100644 (file)
index 0000000..f705253
--- /dev/null
@@ -0,0 +1,15 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-additional-options "-march=rv64gc_zicond -mabi=lp64d -mbranch-cost=4" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+
+
+long foo1 (long c) { return c < 0 ? 1 : -1; }
+long foo2 (long c) { return c >= 0 ? -1 : 1; }
+
+/* We don't support 4->3 splitters, so this fails.  We could perhaps
+   try to catch it in the expander as a special case rather than waiting
+   for combine.  */
+/* { dg-final { scan-assembler-times {srai\t} 2 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {ori\t} 2 { xfail *-*-* } } } */
+/* { dg-final { scan-assembler-times {not\t} 2 { xfail *-*-* } } } */
+