https://gcc.gnu.org/g:d082f8f075aae207884370f96f27222e85d50c32
commit r17-2015-gd082f8f075aae207884370f96f27222e85d50c32 Author: Jeff Law <[email protected]> Date: Tue Jun 30 07:38:23 2026 -0600 [RISC-V] Improve logicals when ~C is cheaper to synthesize than C when Zbb is enabled This patch covers another set of cases where we can improve synthesis of logical operations spotted by comparing LLVM and GCC generated code. With Zbb, we have andn, orn and xnor. Right now we rely on combine to identify cases where the inverted constant is cheaper to produce than the original constant and then invert things via define_insn_and_split patterns. This interacts particularly poorly with mvconst_internal and we're generally moving towards trying to generate better code earlier in the RTL pipeline rather relying so much on combine to clean things up later. So in the logical synthesis code, if we've determined constant synthesis is necessary and Zbb is enabled, then we query to the cost on the original constant C and the adjusted constant ~C. If ~C is cheaper to synthesize, then use that in combination with andn, orn or xnor. Bootstrapped and regression tested on the K3, the c920 is in flight. Also tested on riscv64-elf and riscv32-elf without regressions. As usual waiting for pre-commit CI to do its thing. gcc/ * config/riscv/riscv.cc (synthesize_ior_xor): If we must synthesize a constant and Zbb is enabled, try both C and ~C to see which is cheaper. (synthesize_and): Likewise. gcc/testsuite * gcc.target/riscv/and-synthesis-2.c: New test. Diff: --- gcc/config/riscv/riscv.cc | 41 ++++++++++++++++++++++++ gcc/testsuite/gcc.target/riscv/and-synthesis-2.c | 13 ++++++++ 2 files changed, 54 insertions(+) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index c2d64f63c491..d5eab5421318 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -15836,6 +15836,32 @@ synthesize_ior_xor (rtx_code code, rtx operands[3]) is complete. */ if (budget < 0) { + /* We're going to have to synthesize the constant. However, if + we have Zbb, then we have XNOR and ORN. So if the inverted constant + is cheaper, invert it and use XNOR/ORN. */ + if (TARGET_ZBB + && riscv_const_insns (GEN_INT (~UINTVAL (operands[2])), true) > 0 + && (riscv_const_insns (operands[2], true) + > riscv_const_insns (GEN_INT (~UINTVAL (operands[2])), true))) + { + rtx x = force_reg (word_mode, GEN_INT (~UINTVAL (operands[2]))); + + /* Unfortunately canonical forms vary here. */ + if (code == IOR) + { + x = gen_rtx_NOT (word_mode, x); + x = gen_rtx_IOR (word_mode, x, operands[1]); + } + else + { + x = gen_rtx_XOR (word_mode, x, operands[1]); + x = gen_rtx_NOT (word_mode, x); + } + + emit_insn (gen_rtx_SET (operands[0], x)); + return true; + } + rtx x = force_reg (word_mode, operands[2]); x = gen_rtx_fmt_ee (code, word_mode, operands[1], x); emit_insn (gen_rtx_SET (operands[0], x)); @@ -16069,6 +16095,21 @@ synthesize_and (rtx operands[3]) patch in the series is enabled. */ if (ival || budget < 0) { + /* We're going to have to synthesize the constant. However, if + we have Zbb, then we have ANDN. So if the inverted constant + is cheaper, invert it and use ANDN. */ + if (TARGET_ZBB + && riscv_const_insns (GEN_INT (~UINTVAL (operands[2])), true) > 0 + && (riscv_const_insns (operands[2], true) + > riscv_const_insns (GEN_INT (~UINTVAL (operands[2])), true))) + { + rtx x = force_reg (word_mode, GEN_INT (~UINTVAL (operands[2]))); + x = gen_rtx_NOT (word_mode, x); + x = gen_rtx_AND (word_mode, x, operands[1]); + emit_insn (gen_rtx_SET (operands[0], x)); + return true; + } + rtx x = force_reg (word_mode, operands[2]); x = gen_rtx_AND (word_mode, operands[1], x); emit_insn (gen_rtx_SET (operands[0], x)); diff --git a/gcc/testsuite/gcc.target/riscv/and-synthesis-2.c b/gcc/testsuite/gcc.target/riscv/and-synthesis-2.c new file mode 100644 index 000000000000..e56a672dc2a2 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/and-synthesis-2.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target rv64 } } */ +/* { dg-options "-march=rv64gcb -mabi=lp64d" } */ + +unsigned long xor(unsigned long x) { return x ^ 0xfff0000000000fffULL; } +unsigned long ior(unsigned long x) { return x | 0xfff0000000000fffULL; } +unsigned long and(unsigned long x) { return x & 0xfff0000000000fffULL; } + +/* { dg-final { scan-assembler-times "\\tli\\t" 3 } } */ +/* { dg-final { scan-assembler-times "\\tsrli\t" 3 } } */ + +/* { dg-final { scan-assembler-times "\\txnor\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\torn\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\tandn\t" 1 } } */
