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