[PATCH] RISC-V: Use clmulr for reversed crc32 on rv32

From: Loeka Rogge <loeka.rogge@globalfoundries.com>

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 <michiel.derhaeg@globalfoundries.com>
Signed-off-by: Loeka Rogge <loeka.rogge@globalfoundries.com>
3 files changed