[gcc r17-3306] [PATCH][RISC-V][PR rtl-optimization/125731] Improving sequences for conditional xor with a constant
Jeff Law via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:af0cf36fb47a704a02dd2ead5f8db5d140e45351 commit r17-3306-gaf0cf36fb47a704a02dd2ead5f8db5d140e45351 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: --- gcc/ifcvt.cc | 16 +++++++---- gcc/testsuite/gcc.target/riscv/pr125731.c | 47 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc index 5ea25f8fbe71..722cd8c1bbe2 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 000000000000..a5d551b0bbe2 --- /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" } } */