mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 23:25:24 +02:00
[PATCH GCC17-stage1] riscv: Optimize power-of-2 boundary comparisons in conditional moves
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.
This commit is contained in:
committed by
Jeff Law
parent
0727299846
commit
c29a38d644
@@ -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
|
||||
|
||||
20
gcc/testsuite/gcc.target/riscv/zicond-shift-cond.c
Normal file
20
gcc/testsuite/gcc.target/riscv/zicond-shift-cond.c
Normal file
@@ -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" } } */
|
||||
Reference in New Issue
Block a user