]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[PATCH GCC17-stage1] riscv: Optimize power-of-2 boundary comparisons in conditional...
authorPhilipp Tomsich <philipp.tomsich@vrull.eu>
Fri, 1 May 2026 13:32:17 +0000 (07:32 -0600)
committerJeff Law <jeffrey.law@oss.qualcomm.com>
Fri, 1 May 2026 13:33:05 +0000 (07:33 -0600)
In riscv_expand_conditional_move, detect unsigned comparisons against
power-of-2 boundaries and convert them to shift-based equality tests.
This avoids materializing large constants (e.g. 2^56 - 1) that may
require multiple instructions (bseti + sltu), replacing them with a
single srli that feeds directly into czero.eqz/czero.nez.

The transformation handles four cases:
  GTU x, (2^N-1)  ->  NE (x >> N), 0
  LEU x, (2^N-1)  ->  EQ (x >> N), 0
  GEU x, 2^N      ->  NE (x >> N), 0
  LTU x, 2^N      ->  EQ (x >> N), 0

For example, `(a & (0xff << 56)) ? b : 0` previously generated:
  bseti  a5, zero, 56
  sltu   a0, a0, a5
  czero.nez  a0, a1, a0

Now generates:
  srli      a0, a0, 56
  czero.eqz a0, a1, a0

Existing define_split patterns in riscv.md (lines 3727-3748) handle
the same optimization for standalone SCC operations, but they don't
fire in the conditional move expansion path which goes through
riscv_expand_int_scc directly.

gcc/ChangeLog:

* config/riscv/riscv.cc (riscv_expand_conditional_move):
Convert unsigned comparisons against power-of-2 boundaries
to shift-based equality tests.

gcc/testsuite/ChangeLog:

* gcc.target/riscv/zicond-shift-cond.c: New test.

gcc/config/riscv/riscv.cc
gcc/testsuite/gcc.target/riscv/zicond-shift-cond.c [new file with mode: 0644]

index b0365f682347b838914f99d7f91cd2fcce3c4be3..e09587386ca4cfab45edb776dc45905542fe73e1 100644 (file)
@@ -5808,6 +5808,42 @@ riscv_expand_conditional_move (rtx dest, rtx op, rtx cons, rtx alt)
                             ? word_mode : dst_mode);
       bool invert = false;
 
+      /* For unsigned comparisons against power-of-2 boundaries, convert
+        to a shift-based equality test.  This avoids materializing large
+        constants like (2^N - 1) which may require multiple instructions.
+        GTU x, (2^N-1)  ->  NE (x >> N), 0
+        LEU x, (2^N-1)  ->  EQ (x >> N), 0
+        GEU x, 2^N      ->  NE (x >> N), 0
+        LTU x, 2^N      ->  EQ (x >> N), 0  */
+      if (INTEGRAL_MODE_P (mode0) && CONST_INT_P (op1))
+       {
+         int shift = -1;
+         rtx_code new_code = UNKNOWN;
+
+         if ((code == GTU || code == LEU)
+             && exact_log2 (UINTVAL (op1) + 1) > 0)
+           {
+             shift = exact_log2 (UINTVAL (op1) + 1);
+             new_code = (code == GTU) ? NE : EQ;
+           }
+         else if ((code == GEU || code == LTU)
+                  && exact_log2 (UINTVAL (op1)) > 0)
+           {
+             shift = exact_log2 (UINTVAL (op1));
+             new_code = (code == GEU) ? NE : EQ;
+           }
+
+         if (shift > 0)
+           {
+             op0 = expand_simple_binop (mode0, LSHIFTRT, op0,
+                                        GEN_INT (shift), NULL_RTX,
+                                        1, OPTAB_DIRECT);
+             op1 = const0_rtx;
+             code = new_code;
+             op = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+           }
+       }
+
       /* Canonicalize the comparison.  It must be an equality comparison
         of integer operands, or with SFB it can be any comparison of
         integer operands.  If it isn't, then emit an SCC instruction
diff --git a/gcc/testsuite/gcc.target/riscv/zicond-shift-cond.c b/gcc/testsuite/gcc.target/riscv/zicond-shift-cond.c
new file mode 100644 (file)
index 0000000..50b3a8b
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc_zicond -mabi=lp64d" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } } */
+
+/* Verify that power-of-2 boundary comparisons use shift instead of
+   materializing large constants (vrull/gcc#253).  */
+
+long long high_byte (long long a, long long b)
+{
+  return (a & (0xffULL << 56)) ? b : 0;
+}
+
+long long high_half (long long a, long long b)
+{
+  return (a & (0xffffULL << 48)) ? b : 0;
+}
+
+/* { dg-final { scan-assembler-times "srli\t" 2 } } */
+/* { dg-final { scan-assembler-times "czero\\.eqz\t" 2 } } */
+/* { dg-final { scan-assembler-not "bseti\t" } } */