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 } } */

Reply via email to