[PATCH][RISC-V][PR rtl-optimization/125731] Improving sequences for conditional xor with a constant
Jeffrey Law <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
This is Shreya's work to address pr125731. I did some testing around
this to verify riscv64-elf and riscv32-elf are happy. Testing on the
K1/K3/c920 will fire up tonight, but not expecting significant issues
there.
--
In PR125731,
We are generating inefficient sequences for conditional xor with a
constant. Given this testcase,
long fun_not1 (int a, long b) { if (!(a & 1)) b ^= 8;
return b; }
We are generating:
fun_not1: andi a5,a0,1 mv a0,a1 bne
a5,zero,.L3 xori a0,a1,8 .L3: ret
However, this can be simplified into a branchless sequence. One such
sequence:
andi a5,a0,1
li a4,8
czero.nez a5,a4,a5
xor a0,a1,a5
ret
Another form:
andi a0,a0,1
seqz t0,a0
slli t1,t0,3
xor a0,a1,t1
ret
-- I've edited Shreya's explanation a bit --
The if-converter's get_base_reg currently returns NULL if presented with
a constant and that inhibits if conversion through the
noce_try_cond_arith which can generate more efficient sequences than
generalized conditional moves. So the first thing we need to do is
support constants in get_base_reg.
The return value from get_base_reg is used to generate a new pseudo
register, so if get_base_reg starts returning constants, then we're
going to run into problems generating the new pseudo because constants
are VOIDmode.
I changed the name to get_base_reg_or_constant to reflect that we are
also handling constants, not just returning the base registers of a reg
or subreg. The point where the caller uses the return value to get a
mode for the new pseudo has been changed to get the mode from the other
operand of the binary operation.
Those changes are sufficient to if-convert this example as well as
enabling more efficient code generation for other cases where we have a
conditional operation where one operand is a constant.
Although the newly generated sequence is the same number of insns, it
will be much faster if the conditional branch is not easily predictable.
I've pushed this to the trunk.
Jeff
pr125731.patch
(text/plain, 5.4 KB)
commit af0cf36fb47a704a02dd2ead5f8db5d140e45351 Author: Shreya Munnangi <[email protected]> Date: Fri Aug 14 14:53:34 2026 -0600 [PATCH][RISC-V][PR rtl-optimization/125731] Improving sequences for conditional xor with a constant This is Shreya's work to address pr125731. I did some testing around this to verify riscv64-elf and riscv32-elf are happy. Testing on the K1/K3/c920 will fire up tonight, but not expecting significant issues there. -- In PR125731, We are generating inefficient sequences for conditional xor with a constant. Given this testcase, long fun_not1 (int a, long b) { if (!(a & 1)) b ^= 8; return b; } We are generating: fun_not1: andi a5,a0,1 mv a0,a1 bne a5,zero,.L3 xori a0,a1,8 .L3: ret However, this can be simplified into a branchless sequence. One such sequence: andi a5,a0,1 li a4,8 czero.nez a5,a4,a5 xor a0,a1,a5 ret Another form: andi a0,a0,1 seqz t0,a0 slli t1,t0,3 xor a0,a1,t1 ret -- I've edited Shreya's explanation a bit -- The if-converter's get_base_reg currently returns NULL if presented with a constant and that inhibits if conversion through the noce_try_cond_arith which can generate more efficient sequences than generalized conditional moves. So the first thing we need to do is support constants in get_base_reg. The return value from get_base_reg is used to generate a new pseudo register, so if get_base_reg starts returning constants, then we're going to run into problems generating the new pseudo because constants are VOIDmode. I changed the name to get_base_reg_or_constant to reflect that we are also handling constants, not just returning the base registers of a reg or subreg. The point where the caller uses the return value to get a mode for the new pseudo has been changed to get the mode from the other operand of the binary operation. Those changes are sufficient to if-convert this example as well as enabling more efficient code generation for other cases where we have a conditional operation where one operand is a constant. Although the newly generated sequence is the same number of insns, it will be much faster if the conditional branch is not easily predictable. PR rtl-optimization/125731 gcc/ * ifcvt.cc (get_base_reg_or_const): Renamed from get_base_reg. Handle CONST_INTs too. (noce_try_cond_arith): Use get_base_reg_or_const. Handle case where get_base_reg_or_const returns a CONST_INT. gcc/testsuite * gcc.target/riscv/pr125731.c: New test. diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc index 5ea25f8fbe7..722cd8c1bbe 100644 --- a/gcc/ifcvt.cc +++ b/gcc/ifcvt.cc @@ -3316,13 +3316,14 @@ noce_try_sign_mask (struct noce_if_info *if_info) otherwise NULL_RTX for other RTX_CODE. */ static rtx -get_base_reg (rtx exp) +get_base_reg_or_constant (rtx exp) { if (REG_P (exp)) return exp; else if (SUBREG_P (exp)) return SUBREG_REG (exp); - + else if (CONST_INT_P (exp)) + return exp; return NULL_RTX; } @@ -3396,24 +3397,27 @@ noce_try_cond_arith (struct noce_if_info *if_info) op = GET_CODE (a); /* Canonicalize x = (z op y) : y to x = (y op z) : y */ - a_op1 = get_base_reg (XEXP (a, 1)); + a_op1 = get_base_reg_or_constant (XEXP (a, 1)); if (a_op1 && rtx_equal_p (a_op1, b) && COMMUTATIVE_ARITH_P (a)) { std::swap (XEXP (a, 0), XEXP (a, 1)); - a_op1 = get_base_reg (XEXP (a, 1)); + a_op1 = get_base_reg_or_constant (XEXP (a, 1)); } if (a_op1 == NULL_RTX) goto fail; /* Ensure the cond is of form: x = (y op z) : y */ - a_op0 = get_base_reg (XEXP (a, 0)); + a_op0 = get_base_reg_or_constant (XEXP (a, 0)); if (!(a_op0 && rtx_equal_p (a_op0, b))) goto fail; start_sequence (); - target = gen_reg_rtx (GET_MODE (XEXP (a, op != AND))); + if (CONST_INT_P (XEXP (a, 1))) + target = gen_reg_rtx (GET_MODE (XEXP (a, 0))); + else + target = gen_reg_rtx (GET_MODE (XEXP (a, op != AND))); /* AND requires !cond, instead we swap ops around. */ target = noce_emit_cmove (if_info, target, code, diff --git a/gcc/testsuite/gcc.target/riscv/pr125731.c b/gcc/testsuite/gcc.target/riscv/pr125731.c new file mode 100644 index 00000000000..a5d551b0bbe --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr125731.c @@ -0,0 +1,47 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -march=rv64gcbv_zicond -mabi=lp64d" { target rv64} } */ +/* { dg-options "-O2 -march=rv32gcbv_zicond -mabi=ilp32" { target rv32} } */ + +long fun_not1 (int a, long b) +{ + if (!(a & 1)) + b ^= 8; + return b; +} + +long fun_not2 (short a, int b) +{ + if (!(a & 1)) + b ^= 8; + return b; +} + +long fun_not21 (long a, long b) +{ + if (!(a & 1)) + b ^= 8; + return b; +} + +long fun_not22 (long a, int b) +{ + if (!(a & 1)) + b ^= 8; + return b; +} + +long fun_not3 (int a, long b) +{ + if (!(a & 1)) + b |= 8; + return b; +} + +long fun_not4 (int a, long b) +{ + if (!(a & 1)) + b &= 8; + return b; +} + +/* { dg-final { scan-assembler-not "\tbne" } } */