From: Loeka Rogge <[email protected]>

The clmul-based reversed crc expansion multiplies by the reflected
polynomial shifted left by one.  For a 32-bit crc on rv32 that operand
is 33 bits wide and does not fit in a register, so rv32 fell back to the
table based expansion.

clmulr produces bits 2*xlen-2:xlen-1 of the carry-less product, which is
what clmulh produces from an operand that is already shifted left by one:

  clmulh (a, ref_poly << 1) = (a * ref_poly)[62:31] = clmulr (a, ref_poly)

so the shift can be dropped and the unshifted 32-bit reflected polynomial
handed to clmulr instead.

The Linux kernel uses the same identity in crc32_le_zbc(),
arch/riscv/lib/crc32.c (since moved to lib/crc/riscv/):

        /* We don't have a "clmulrh" insn, so use clmul + slli instead. */
        asm volatile (".option push\n"
                      ".option arch,+zbc\n"
                      "clmul    %0, %1, %2\n"
                      "slli     %0, %0, 1\n"
                      "xor      %0, %0, %1\n"
                      "clmulr   %0, %0, %3\n"
                      ".option pop\n"
                      : "=&r" (crc)
                      : "r" (s), "r" (poly_qt), "r" (poly) :);

__builtin_rev_crc32_data32 (crc, data, 0x04C11DB7) on rv32_zbc now
generates:

        li      a5,305479680
        addi    t0,a5,1791
        li      a4,-150925312
        xor     t1,a0,t0
        addi    t2,a4,1601
        clmul   a1,t1,t2
        li      a0,-306675712
        addi    a2,a0,800
        clmulr  a0,a1,a2

Regtested for rv32gc and rv64gc.

gcc/ChangeLog:

        * config/riscv/bitmanip.md (crc_rev<ANYI1:mode><ANYI:mode>4):
        Use the clmul expansion for a word-sized crc on rv32 with Zbc.
        * config/riscv/riscv.cc (expand_reversed_crc_using_clmul): Use
        clmulr with the unshifted reflected polynomial when the crc is
        word-sized.
        (riscv_optab_supported_p): Allow crc_rev for a word-sized result
        on rv32 with Zbc when optimizing for size.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/crc-builtin-zbc32.c: Add reversed crc32 tests.

Signed-off-by: Michiel Derhaeg <[email protected]>
Signed-off-by: Loeka Rogge <[email protected]>
---
 gcc/config/riscv/bitmanip.md                  |  9 +++--
 gcc/config/riscv/riscv.cc                     | 38 ++++++++++++++-----
 .../gcc.target/riscv/crc-builtin-zbc32.c      | 18 ++++++++-
 3 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index a9fc5af5a0e..55bbf304bff 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -1385,7 +1385,7 @@
   "clmulr\t%0,%1,%2"
   [(set_attr "type" "clmul")])
 
-;; Reversed CRC 8, 16, 32 for TARGET_64
+;; Reversed CRC 8, 16, 32
 (define_expand "crc_rev<ANYI1:mode><ANYI:mode>4"
        ;; return value (calculated CRC)
   [(set (match_operand:ANYI 0 "register_operand")
@@ -1403,8 +1403,11 @@
      it is possible to store the quotient within a single variable
      (E.g.  CRC64's quotient may need 65 bits,
      we can't keep it in 64 bit variable.)
-     then use clmul instruction to implement the CRC.  */
-  if ((TARGET_ZBKC || TARGET_ZBC || TARGET_ZVBC) && <ANYI:MODE>mode < 
word_mode)
+     then use clmul instruction to implement the CRC.
+     We can also use clmulr for CRC-32 on RV32, requiring ZBC.  */
+  if (((TARGET_ZBKC || TARGET_ZBC || TARGET_ZVBC)
+       && <ANYI:MODE>mode < word_mode)
+      || (!TARGET_64BIT && TARGET_ZBC && <ANYI:MODE>mode == word_mode))
     expand_reversed_crc_using_clmul (<ANYI:MODE>mode, <ANYI1:MODE>mode,
                                     operands);
   else
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 0b73f03e169..61444a81224 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -13422,8 +13422,9 @@ riscv_optab_supported_p (int op, machine_mode, 
machine_mode result_mode,
   /* The second CRC optab mode is the result mode.  The CLMUL expansion
      requires room for a quotient wider than the CRC value itself.  */
   if (op == crc_rev_optab && opt_type != OPTIMIZE_FOR_SPEED)
-    return ((TARGET_ZBKC || TARGET_ZBC || TARGET_ZVBC)
-           && result_mode < word_mode);
+    return (((TARGET_ZBKC || TARGET_ZBC || TARGET_ZVBC)
+            && result_mode < word_mode)
+           || (!TARGET_64BIT && TARGET_ZBC && result_mode == word_mode));
 
   return true;
 }
@@ -15858,11 +15859,22 @@ expand_reversed_crc_using_clmul (scalar_mode 
crc_mode, scalar_mode data_mode,
   unsigned HOST_WIDE_INT
   ref_polynomial = reflect_hwi (UINTVAL (polynomial),
                                crc_size);
-  rtx t1 = gen_reg_rtx (word_mode);
-  riscv_emit_move (t1, gen_int_mode (ref_polynomial << 1, word_mode));
 
-  rtx crc = gen_rtx_ZERO_EXTEND (word_mode, operands[1]);
-  rtx data = gen_rtx_ZERO_EXTEND (word_mode, operands[2]);
+  bool use_clmulr = crc_size == BITS_PER_WORD;
+  gcc_assert (TARGET_ZBC || !use_clmulr);
+
+  rtx t1 = gen_reg_rtx (word_mode);
+  if (use_clmulr)
+    riscv_emit_move (t1, gen_int_mode (ref_polynomial, word_mode));
+  else
+    riscv_emit_move (t1, gen_int_mode (ref_polynomial << 1, word_mode));
+
+  rtx crc = operands[1];
+  if (crc_size != BITS_PER_WORD)
+    crc = gen_rtx_ZERO_EXTEND (word_mode, crc);
+  rtx data = operands[2];
+  if (data_size != BITS_PER_WORD)
+    data = gen_rtx_ZERO_EXTEND (word_mode, data);
   rtx a0 = gen_reg_rtx (word_mode);
   riscv_expand_op (XOR, word_mode, a0, crc, data);
 
@@ -15876,10 +15888,18 @@ expand_reversed_crc_using_clmul (scalar_mode 
crc_mode, scalar_mode data_mode,
       rtx num_shift = gen_int_mode (BITS_PER_WORD - data_size, word_mode);
       riscv_expand_op (ASHIFT, word_mode, a0, a0, num_shift);
 
-      if (TARGET_64BIT)
-       emit_insn (gen_riscv_clmulh_di (a0, a0, t1));
+      if (use_clmulr)
+       {
+         gcc_assert (!TARGET_64BIT);
+         emit_insn (gen_riscv_clmulr_si (a0, a0, t1));
+       }
       else
-       emit_insn (gen_riscv_clmulh_si (a0, a0, t1));
+       {
+         if (TARGET_64BIT)
+           emit_insn (gen_riscv_clmulh_di (a0, a0, t1));
+         else
+           emit_insn (gen_riscv_clmulh_si (a0, a0, t1));
+       }
     }
   else
     {
diff --git a/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc32.c 
b/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc32.c
index 8496555b93d..ba0b855ee55 100644
--- a/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc32.c
+++ b/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc32.c
@@ -19,4 +19,20 @@ int16_t crc16_data16 (int16_t x)
   return __builtin_crc16_data16 (x, 0x3214, 0x1021);
 }
 
-/* { dg-final { scan-assembler-times "clmul\t" 6 } } */
+int32_t rev_crc32_data8 (int32_t x)
+{
+  return __builtin_rev_crc32_data8 (x, 'a', 0x04C11DB7);
+}
+
+int32_t rev_crc32_data16 (int32_t x)
+{
+  return __builtin_rev_crc32_data16 (x, 0x3214, 0x04C11DB7);
+}
+
+int32_t rev_crc32_data32 (int32_t x)
+{
+  return __builtin_rev_crc32_data32 (x, 0x123546ff, 0x04C11DB7);
+}
+
+/* { dg-final { scan-assembler-times "clmul\t" 9 } } */
+/* { dg-final { scan-assembler-times "clmulr\t" 3 } } */
-- 
2.54.0

Reply via email to