https://gcc.gnu.org/g:af13bb056c65532fc4b3cecd06f51f82a7c70149
commit r17-2119-gaf13bb056c65532fc4b3cecd06f51f82a7c70149 Author: Jeff Law <[email protected]> Date: Fri Jul 3 07:47:09 2026 -0600 [RISC-V] Improve AND synthesis So a couple changes to AND synthesis. First much like the ADD, IOR/XOR cases we should be using riscv_integer_cost rather than riscv_const_insns. This results in improvement for the exact same constants in the IOR/XOR case. This also introduces the ability to use rotation to help AND synthesis. Let's consider x & 0xff000000000000ff. It currently generates this: li a5,-1 slli a5,a5,56 addi a5,a5,255 and a0,a0,a5 The trick here is to realize we only have 16 bits that are relevant and the can be clustered together. So we rotate x right by 56 bit positions, then turn off the high bits using zext.h then rotate 8 more positions to put the bits back into the proper position. That looks like this: rori a0,a0,56 zext.h a0,a0 rori a0,a0,8 We can also use zext.w and andi for the bit clearing step. Built and tested on riscv32-elf and riscv64-elf and bootstrapped + regression tested on the c920 and k3. Waiting on pre-commit CI before moving forward. gcc/ * config/riscv/riscv.cc (and_synthesis): Use riscv_integer_cost rather than riscv_const_insns. Use rotate+clear_bits+rotate when useful. gcc/testsuite/ * gcc.target/riscv/and-synthesis-3.c: New test. Diff: --- gcc/config/riscv/riscv.cc | 55 ++++++++++++++++++++++-- gcc/testsuite/gcc.target/riscv/and-synthesis-3.c | 23 ++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index d31d4a8aeabf..d5d073f37a46 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -15949,7 +15949,7 @@ synthesize_and (rtx operands[3]) execution and fusion in the constant synthesis those would naturally decrease the budget. It also does not account for the AND at the end of the sequence which would increase the budget. */ - int budget = riscv_const_insns (operands[2], true); + int budget = riscv_integer_cost (INTVAL (operands[2]), true); rtx input = NULL_RTX; rtx output = NULL_RTX; @@ -16037,6 +16037,53 @@ synthesize_and (rtx operands[3]) return true; } + /* Similarly if there's a run of 1s on the ends and all zeros in the middle + then rotation to get all the 1s in the mask into the LSB may result in + a SMALL_OPERAND or perhaps a mode mask. In those cases its rotate, + mask, rotate back into position. */ + t = INTVAL (operands[2]); + if (TARGET_64BIT + && (TARGET_ZBB || TARGET_XTHEADBB || TARGET_ZBKB) + && budget >= 3 + && consecutive_bits_operand (GEN_INT (~t), word_mode) + && ((TARGET_ZBA && popcount_hwi (t) == 32) + || popcount_hwi (t) == 16 + || popcount_hwi (t) <= 11)) + { + /* First rotate the relevant bits into the low position. */ + int count = BITS_PER_WORD - clz_hwi (~t); + rtx x = gen_rtx_ROTATERT (word_mode, operands[1], GEN_INT (count)); + output = gen_reg_rtx (word_mode); + emit_insn (gen_rtx_SET (output, x)); + input = output; + + /* Now clear bits according to the mask. */ + if (popcount_hwi (t) == 32) + { + x = gen_rtx_ZERO_EXTEND (word_mode, gen_lowpart (SImode, input)); + emit_insn (gen_rtx_SET (output, x)); + input = output; + } + else if (popcount_hwi (t) == 16) + { + x = gen_rtx_ZERO_EXTEND (word_mode, gen_lowpart (HImode, input)); + emit_insn (gen_rtx_SET (output, x)); + input = output; + } + else + { + x = GEN_INT ((HOST_WIDE_INT_1U << popcount_hwi (t)) - 1); + x = gen_rtx_AND (word_mode, input, x); + emit_insn (gen_rtx_SET (output, x)); + input = output; + } + + /* Now we just need to rotate the bits back into position. */ + x = gen_rtx_ROTATERT (word_mode, input, GEN_INT (BITS_PER_WORD - count)); + emit_insn (gen_rtx_SET (operands[0], x)); + return true; + } + /* If there are all zeros, except for a run of 1s somewhere in the middle of the constant, then this is at worst 3 shifts. */ t = INTVAL (operands[2]); @@ -16098,9 +16145,9 @@ synthesize_and (rtx operands[3]) 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))) + && riscv_integer_cost (~UINTVAL (operands[2]), true) > 0 + && (riscv_integer_cost (UINTVAL (operands[2]), true) + > riscv_integer_cost (~UINTVAL (operands[2]), true))) { rtx x = force_reg (word_mode, GEN_INT (~UINTVAL (operands[2]))); x = gen_rtx_NOT (word_mode, x); diff --git a/gcc/testsuite/gcc.target/riscv/and-synthesis-3.c b/gcc/testsuite/gcc.target/riscv/and-synthesis-3.c new file mode 100644 index 000000000000..c9b63aeec09d --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/and-synthesis-3.c @@ -0,0 +1,23 @@ +/* { dg-do compile { target rv64 } } */ +/* { dg-options "-march=rv64gcb -mabi=lp64d" } */ + + + +#define T(X) long andn##X(long x) { return x & X; } + +T(0x000200c3233fffffUL) +T(0x300000000003ffffUL) +T(0x00004f10000a0fffUL) +T(0x0000c7f3801fefffUL) +T(0xe00000000000fc00UL) +T(0x0eef7ffffffbbfffUL) +T(0xff0000000000ff00UL) +T(0xff000000000000ffUL) +T(0xfffff00000000fffUL) +T(0xf00000000000000fUL) + +/* { dg-final { scan-assembler-times "\\tandn\\t" 7 } } */ +/* { dg-final { scan-assembler-times "\\trori\\t" 6 } } */ +/* { dg-final { scan-assembler-times "\\tzext.h\\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\tzext.w\\t" 1 } } */ +/* { dg-final { scan-assembler-times "\\tandi\\t" 1 } } */
