https://gcc.gnu.org/g:e91f79891fc657d48310f82da0da61eb4a0587ed

commit r17-4206-ge91f79891fc657d48310f82da0da61eb4a0587ed
Author: Michiel Derhaeg <[email protected]>
Date:   Sun Sep 13 13:30:17 2026 -0600

    [PATCH] RISC-V: Use clmulr for reversed crc32 on rv32
    
    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]>

Diff:
---
 gcc/config/riscv/bitmanip.md                       |  9 +++--
 gcc/config/riscv/riscv.cc                          | 38 +++++++++++++++++-----
 gcc/testsuite/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 a9fc5af5a0e0..55bbf304bffd 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 39309b2b2058..49e7476b8322 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -13433,8 +13433,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;
 }
@@ -15894,11 +15895,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);
 
@@ -15912,10 +15924,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 8496555b93d1..ba0b855ee551 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 } } */

Reply via email to