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.
jeff
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index d31d4a8aeabf..6a576e119ce1 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,54 @@ 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
+ {
+ rtx 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));
+ output = gen_reg_rtx (word_mode);
+ 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 +16146,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 } } */