[v7,03/12] RISC-V: Add CRC expander to generate faster CRC.

Message ID bfac11cd-5791-46c0-a341-000c25a86057@gmail.com
State Committed
Commit 74eb3570e6fba73b0e2bfce2a14d7696e30b48a8
Headers
Series None |

Checks

Context Check Description
rivoscibot/toolchain-ci-rivos-lint warning Lint failed
rivoscibot/toolchain-ci-rivos-apply-patch success Patch applied
rivoscibot/toolchain-ci-rivos-build--newlib-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gc_zba_zbb_zbc_zbs-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gcv-lp64d-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--newlib-rv64gc-lp64d-non-multilib success Build passed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gc-lp64d-non-multilib success Build passed
rivoscibot/toolchain-ci-rivos-test fail Testing failed

Commit Message

Jeff Law Nov. 29, 2024, 3:04 p.m. UTC
  This is parts of patches #3 and #4 from Mariam's series.  Specifically 
it introduces an expander in the RISC-V backend that can generate a CRC 
using a clmul based sequence.  It also has support for exploiting zbkb 
if we need to reflect values.

I'm explicitly not including most of the tests.  Essentially the 
execution tests are dependent on the generic CRC tests which were part 
of a later patch.  I don't want to introduce those generic CRC tests 
until after the detection/validation/optimization are integrated, 
otherwise they'll fail.  So the bulk of the tests won't be integrated 
until later.

This has been tested with rv32 and rv64 in my tester.  I've also 
bootstrapped & regression tested riscv64-linux-gnu with the full patch 
series, but not with this patch broken out (it's in the middle of a 
bootstrap cycle now and the cycle is ~26 hours on my BPI).

Pushing to the trunk.


Jeff
commit 74eb3570e6fba73b0e2bfce2a14d7696e30b48a8
Author: Mariam Arutunian <mariamarutunian@gmail.com>
Date:   Thu Nov 28 14:35:23 2024 -0700

    [PATCH v7 03/12] RISC-V: Add CRC expander to generate faster CRC.
    
    If the target is ZBC or ZBKC, it uses clmul instruction for the CRC
    calculation.  Otherwise, if the target is ZBKB, generates table-based
    CRC, but for reversing inputs and the output uses bswap and brev8
    instructions.  Add new tests to check CRC generation for ZBC, ZBKC and
    ZBKB targets.
    
    gcc/
    
            * expr.cc (gf2n_poly_long_div_quotient): New function.
            * expr.h (gf2n_poly_long_div_quotient):  New function declaration.
            * hwint.cc (reflect_hwi): New function.
            * hwint.h (reflect_hwi): New function declaration.
            * config/riscv/bitmanip.md (crc_rev<ANYI1:mode><ANYI:mode>4): New
            expander for reversed CRC.
            (crc<SUBX1:mode><SUBX:mode>4): New expander for bit-forward CRC.
            * config/riscv/iterators.md (SUBX1, ANYI1): New iterators.
            * config/riscv/riscv-protos.h (generate_reflecting_code_using_brev):
            New function declaration.
            (expand_crc_using_clmul): Likewise.
            (expand_reversed_crc_using_clmul): Likewise.
            * config/riscv/riscv.cc (generate_reflecting_code_using_brev): New
            function.
            (expand_crc_using_clmul): Likewise.
            (expand_reversed_crc_using_clmul): Likewise.
            * config/riscv/riscv.md (UNSPEC_CRC, UNSPEC_CRC_REV):  New unspecs.
    
            * doc/sourcebuild.texi: Document new target selectors.
    
    gcc/testsuite
            * lib/target-supports.exp (check_effective_target_riscv_zbc): New
            target supports predicate.
            (check_effective_target_riscv_zbkb): Likewise.
            (check_effective_target_riscv_zbkc): Likewise.
            (check_effective_target_zbc_ok): Likewise.
            (check_effective_target_zbkb_ok): Likewise.
            (check_effective_target_zbkc_ok): Likewise.
            (riscv_get_arch): Add zbkb and zbkc support.
    
            * gcc.target/riscv/crc-builtin-zbc32.c: New file.
            * gcc.target/riscv/crc-builtin-zbc64.c: Likewise.
    
                Co-author: Jeff Law  <jlaw@ventanamicro.com>
  

Patch

diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index 06ff698bfe7..23dc47eaaef 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -1192,3 +1192,66 @@  (define_insn "riscv_clmulr_<mode>"
   "TARGET_ZBC"
   "clmulr\t%0,%1,%2"
   [(set_attr "type" "clmul")])
+
+;; Reversed CRC 8, 16, 32 for TARGET_64
+(define_expand "crc_rev<ANYI1:mode><ANYI:mode>4"
+	;; return value (calculated CRC)
+  [(set (match_operand:ANYI 0 "register_operand" "=r")
+		      ;; initial CRC
+	(unspec:ANYI [(match_operand:ANYI 1 "register_operand" "r")
+		      ;; data
+		      (match_operand:ANYI1 2 "register_operand" "r")
+		      ;; polynomial without leading 1
+		      (match_operand:ANYI 3)]
+		      UNSPEC_CRC_REV))]
+  /* We don't support the case when data's size is bigger than CRC's size.  */
+  "<ANYI:MODE>mode >= <ANYI1:MODE>mode"
+{
+  /* If we have the ZBC or ZBKC extension (ie, clmul) and
+     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,
+     otherwise (TARGET_ZBKB) generate table based using brev.  */
+  if ((TARGET_ZBKC || TARGET_ZBC) && <ANYI:MODE>mode < word_mode)
+    expand_reversed_crc_using_clmul (<ANYI:MODE>mode, <ANYI1:MODE>mode,
+				     operands);
+  else if (TARGET_ZBKB)
+    /* Generate table-based CRC.
+       To reflect values use brev and bswap instructions.  */
+    expand_reversed_crc_table_based (operands[0], operands[1],
+				     operands[2], operands[3],
+				     GET_MODE (operands[2]),
+				     generate_reflecting_code_using_brev);
+  else
+    /* Generate table-based CRC.
+       To reflect values use standard reflecting algorithm.  */
+    expand_reversed_crc_table_based (operands[0], operands[1],
+				     operands[2], operands[3],
+				     GET_MODE (operands[2]),
+				     generate_reflecting_code_standard);
+  DONE;
+})
+
+;; CRC 8, 16, (32 for TARGET_64)
+(define_expand "crc<SUBX1:mode><SUBX:mode>4"
+	;; return value (calculated CRC)
+  [(set (match_operand:SUBX 0 "register_operand" "=r")
+		      ;; initial CRC
+	(unspec:SUBX [(match_operand:SUBX 1 "register_operand" "r")
+		      ;; data
+		      (match_operand:SUBX1 2 "register_operand" "r")
+		      ;; polynomial without leading 1
+		      (match_operand:SUBX 3)]
+		      UNSPEC_CRC))]
+  /* We don't support the case when data's size is bigger than CRC's size.  */
+  "(TARGET_ZBKC || TARGET_ZBC) && <SUBX:MODE>mode >= <SUBX1:MODE>mode"
+{
+  /* If we have the ZBC or ZBKC extension (ie, clmul) and
+     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.  */
+  expand_crc_using_clmul (<SUBX:MODE>mode, <SUBX1:MODE>mode, operands);
+  DONE;
+})
diff --git a/gcc/config/riscv/iterators.md b/gcc/config/riscv/iterators.md
index 081659499a9..fae89893ecb 100644
--- a/gcc/config/riscv/iterators.md
+++ b/gcc/config/riscv/iterators.md
@@ -62,9 +62,15 @@  (define_mode_iterator SUPERQI [HI SI (DI "TARGET_64BIT")])
 ;; Iterator for hardware integer modes narrower than XLEN.
 (define_mode_iterator SUBX [QI HI (SI "TARGET_64BIT")])
 
+;; Iterator for hardware integer modes narrower than XLEN, same as SUBX.
+(define_mode_iterator SUBX1 [QI HI (SI "TARGET_64BIT")])
+
 ;; Iterator for hardware-supported integer modes.
 (define_mode_iterator ANYI [QI HI SI (DI "TARGET_64BIT")])
 
+;; Iterator for hardware integer modes narrower than XLEN, same as ANYI.
+(define_mode_iterator ANYI1 [QI HI SI (DI "TARGET_64BIT")])
+
 (define_mode_iterator ANYI_DOUBLE_TRUNC [HI SI (DI "TARGET_64BIT")])
 
 (define_mode_iterator ANYI_QUAD_TRUNC [SI (DI "TARGET_64BIT")])
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 500b357f6eb..99ee6ef1e0d 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -175,6 +175,9 @@  extern bool riscv_reg_frame_related (rtx);
 extern void riscv_split_sum_of_two_s12 (HOST_WIDE_INT, HOST_WIDE_INT *,
 					HOST_WIDE_INT *);
 extern bool riscv_vector_float_type_p (const_tree type);
+extern void generate_reflecting_code_using_brev (rtx *);
+extern void expand_crc_using_clmul (scalar_mode, scalar_mode, rtx *);
+extern void expand_reversed_crc_using_clmul (scalar_mode, scalar_mode, rtx *);
 
 /* Routines implemented in riscv-c.cc.  */
 void riscv_cpu_cpp_builtins (cpp_reader *);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index efdb1d33283..7a1724d6e73 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -13502,6 +13502,163 @@  riscv_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT size,
   return default_use_by_pieces_infrastructure_p (size, alignment, op, speed_p);
 }
 
+/* Generate instruction sequence
+   which reflects the value of the OP using bswap and brev8 instructions.
+   OP's mode may be less than word_mode, to get the correct number,
+   after reflecting we shift right the value by SHIFT_VAL.
+   E.g. we have 1111 0001, after reflection (target 32-bit) we will get
+   1000 1111 0000 0000, if we shift-out 16 bits,
+   we will get the desired one: 1000 1111.  */
+
+void
+generate_reflecting_code_using_brev (rtx *op)
+{
+  machine_mode op_mode = GET_MODE (*op);
+  HOST_WIDE_INT shift_val = (BITS_PER_WORD
+			     - GET_MODE_BITSIZE (op_mode).to_constant ());
+  riscv_expand_op (BSWAP, word_mode, *op, *op, *op);
+  riscv_expand_op (LSHIFTRT, word_mode, *op, *op,
+		   gen_int_mode (shift_val, word_mode));
+  if (TARGET_64BIT)
+    emit_insn (gen_riscv_brev8_di (*op, *op));
+  else
+    emit_insn (gen_riscv_brev8_si (*op, *op));
+}
+
+
+/* Generate assembly to calculate CRC using clmul instruction.
+   The following code will be generated when the CRC and data sizes are equal:
+     li      a4,quotient
+     li      a5,polynomial
+     xor     a0,a1,a0
+     clmul   a0,a0,a4
+     srli    a0,a0,crc_size
+     clmul   a0,a0,a5
+     slli    a0,a0,word_mode_size - crc_size
+     srli    a0,a0,word_mode_size - crc_size
+     ret
+   crc_size may be 8, 16, 32.
+   Some instructions will be added for the cases when CRC's size is larger than
+   data's size.
+   OPERANDS[1] is input CRC,
+   OPERANDS[2] is data (message),
+   OPERANDS[3] is the polynomial without the leading 1.  */
+
+void
+expand_crc_using_clmul (scalar_mode crc_mode, scalar_mode data_mode,
+			rtx *operands)
+{
+  /* Check and keep arguments.  */
+  gcc_assert (!CONST_INT_P (operands[0]));
+  gcc_assert (CONST_INT_P (operands[3]));
+  unsigned short crc_size = GET_MODE_BITSIZE (crc_mode);
+  gcc_assert (crc_size <= 32);
+  unsigned short data_size = GET_MODE_BITSIZE (data_mode);
+
+  /* Calculate the quotient.  */
+  unsigned HOST_WIDE_INT
+      q = gf2n_poly_long_div_quotient (UINTVAL (operands[3]), crc_size);
+
+  rtx crc_extended = gen_rtx_ZERO_EXTEND (word_mode, operands[1]);
+  rtx crc = gen_reg_rtx (word_mode);
+  if (crc_size > data_size)
+    riscv_expand_op (LSHIFTRT, word_mode, crc, crc_extended,
+		     gen_int_mode (crc_size - data_size, word_mode));
+  else
+    crc = gen_rtx_ZERO_EXTEND (word_mode, operands[1]);
+  rtx t0 = gen_reg_rtx (word_mode);
+  riscv_emit_move (t0, gen_int_mode (q, word_mode));
+  rtx t1 = gen_reg_rtx (word_mode);
+  riscv_emit_move (t1, operands[3]);
+
+  rtx a0 = gen_reg_rtx (word_mode);
+  rtx data = gen_rtx_ZERO_EXTEND (word_mode, operands[2]);
+  riscv_expand_op (XOR, word_mode, a0, crc, data);
+
+  if (TARGET_64BIT)
+    emit_insn (gen_riscv_clmul_di (a0, a0, t0));
+  else
+    emit_insn (gen_riscv_clmul_si (a0, a0, t0));
+
+  riscv_expand_op (LSHIFTRT, word_mode, a0, a0,
+		   gen_int_mode (crc_size, word_mode));
+  if (TARGET_64BIT)
+    emit_insn (gen_riscv_clmul_di (a0, a0, t1));
+  else
+    emit_insn (gen_riscv_clmul_si (a0, a0, t1));
+
+  if (crc_size > data_size)
+    {
+      rtx crc_part = gen_reg_rtx (word_mode);
+      riscv_expand_op (ASHIFT, word_mode, crc_part, operands[1],
+		       gen_int_mode (data_size, word_mode));
+      riscv_expand_op (XOR, word_mode, a0, a0, crc_part);
+    }
+  riscv_emit_move (operands[0], gen_lowpart (crc_mode, a0));
+}
+
+/* Generate assembly to calculate reversed CRC using clmul instruction.
+   OPERANDS[1] is input CRC,
+   OPERANDS[2] is data (message),
+   OPERANDS[3] is the polynomial without the leading 1.  */
+
+void
+expand_reversed_crc_using_clmul (scalar_mode crc_mode, scalar_mode data_mode,
+				 rtx *operands)
+{
+  /* Check and keep arguments.  */
+  gcc_assert (!CONST_INT_P (operands[0]));
+  gcc_assert (CONST_INT_P (operands[3]));
+  unsigned short crc_size = GET_MODE_BITSIZE (crc_mode);
+  gcc_assert (crc_size <= 32);
+  unsigned short data_size = GET_MODE_BITSIZE (data_mode);
+  rtx polynomial = operands[3];
+
+  /* Calculate the quotient.  */
+  unsigned HOST_WIDE_INT
+  q = gf2n_poly_long_div_quotient (UINTVAL (polynomial), crc_size);
+  /* Reflect the calculated quotient.  */
+  q = reflect_hwi (q, crc_size + 1);
+  rtx t0 = gen_reg_rtx (word_mode);
+  riscv_emit_move (t0, gen_int_mode (q, word_mode));
+
+  /* Reflect the polynomial.  */
+  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]);
+  rtx a0 = gen_reg_rtx (word_mode);
+  riscv_expand_op (XOR, word_mode, a0, crc, data);
+
+  if (TARGET_64BIT)
+    emit_insn (gen_riscv_clmul_di (a0, a0, t0));
+  else
+    emit_insn (gen_riscv_clmul_si (a0, a0, t0));
+
+  rtx num_shift = gen_int_mode (GET_MODE_BITSIZE (word_mode) - 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));
+  else
+    emit_insn (gen_riscv_clmulh_si (a0, a0, t1));
+
+  if (crc_size > data_size)
+    {
+      rtx data_size_shift = gen_int_mode (data_size, word_mode);
+      rtx crc_part = gen_reg_rtx (word_mode);
+      riscv_expand_op (LSHIFTRT, word_mode, crc_part, crc, data_size_shift);
+      riscv_expand_op (XOR, word_mode, a0, a0, crc_part);
+    }
+
+  riscv_emit_move (operands[0], gen_lowpart (crc_mode, a0));
+}
+
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
 #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index afde5294467..5f0749aff66 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -95,6 +95,10 @@  (define_c_enum "unspec" [
   ;; XTheadFmv moves
   UNSPEC_XTHEADFMV
   UNSPEC_XTHEADFMV_HW
+
+  ;; CRC unspecs
+  UNSPEC_CRC
+  UNSPEC_CRC_REV
 ])
 
 (define_c_enum "unspecv" [
diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index b7167b39bb8..54e485a4eb7 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -2548,6 +2548,24 @@  Test target architecture has support for the zacas extension.
 @item riscv_zalrsc
 Test target architecture has support for the zalrsc extension.
 
+@item riscv_zbc
+Test target architecture has support for the zbc extension.
+
+@item riscv_zbc_ok
+Test target architecture can execute code with zbc extension enabled.
+
+@item riscv_zbkb
+Test target architecture has support for the zbkb extension.
+
+@item riscv_zbkb_ok
+Test target architecture can execute code with zbkb extension enabled.
+
+@item riscv_zbkc
+Test target architecture has support for the zbkc extension.
+
+@item riscv_zbkc_ok
+Test target architecture can execute code with zbkc extension enabled.
+
 @item riscv_ztso
 Test target architecture has support for the ztso extension.
 
diff --git a/gcc/expr.cc b/gcc/expr.cc
index de25437660e..70f2ecec998 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -14178,6 +14178,33 @@  int_expr_size (const_tree exp)
   return tree_to_shwi (size);
 }
 
+/* Return the quotient of polynomial long division of x^2N by POLYNOMIAL
+   in GF (2^N).
+   Author: Richard Sandiford <richard.sandiford@arm.com>  */
+
+unsigned HOST_WIDE_INT
+gf2n_poly_long_div_quotient (unsigned HOST_WIDE_INT polynomial,
+			     unsigned short n)
+{
+  /* The result has degree N, so needs N + 1 bits.  */
+  gcc_assert (n < 64);
+
+  /* Perform a division step for the x^2N coefficient.  At this point the
+     quotient and remainder have N implicit trailing zeros.  */
+  unsigned HOST_WIDE_INT quotient = 1;
+  unsigned HOST_WIDE_INT remainder = polynomial;
+
+  /* Process the coefficients for x^(2N-1) down to x^N, with each step
+     reducing the number of implicit trailing zeros by one.  */
+  for (unsigned int i = 0; i < n; ++i)
+    {
+      bool coeff = remainder & (HOST_WIDE_INT_1U << (n - 1));
+      quotient = (quotient << 1) | coeff;
+      remainder = (remainder << 1) ^ (coeff ? polynomial : 0);
+    }
+  return quotient;
+}
+
 /* Calculate CRC for the initial CRC and given POLYNOMIAL.
    CRC_BITS is CRC size.  */
 
diff --git a/gcc/expr.h b/gcc/expr.h
index 5dd059de801..fe93ee7238f 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -377,6 +377,11 @@  extern rtx expr_size (tree);
 extern bool mem_ref_refers_to_non_mem_p (tree);
 extern bool non_mem_decl_p (tree);
 
+/* Return the quotient of the polynomial long division of x^2N by POLYNOMIAL
+   in GF (2^N).  */
+extern unsigned HOST_WIDE_INT
+gf2n_poly_long_div_quotient (unsigned HOST_WIDE_INT, unsigned short);
+
 /* Generate table-based CRC.  */
 extern void generate_reflecting_code_standard (rtx *);
 extern void expand_crc_table_based (rtx, rtx, rtx, rtx, machine_mode);
diff --git a/gcc/hwint.cc b/gcc/hwint.cc
index e5c3619a35b..adb30e2b9ad 100644
--- a/gcc/hwint.cc
+++ b/gcc/hwint.cc
@@ -188,3 +188,21 @@  least_common_multiple (HOST_WIDE_INT a, HOST_WIDE_INT b)
 {
   return mul_hwi (abs_hwi (a) / gcd (a, b), abs_hwi (b));
 }
+
+/* Reflect (reverse) the bits of a given VALUE within a specified BITWIDTH.  */
+
+unsigned HOST_WIDE_INT
+reflect_hwi (unsigned HOST_WIDE_INT value, unsigned bitwidth)
+{
+  unsigned HOST_WIDE_INT reflected_value = 0;
+  /* Loop through each bit in the specified BITWIDTH.  */
+  for (size_t i = 0; i < bitwidth; i++)
+    {
+      reflected_value <<= 1;
+      /* Add the least significant bit of the current value to the
+	 reflected value.  */
+      reflected_value |= (value & 1);
+      value >>= 1;
+    }
+  return reflected_value;
+}
\ No newline at end of file
diff --git a/gcc/hwint.h b/gcc/hwint.h
index 82416bf366e..f69b61db9cc 100644
--- a/gcc/hwint.h
+++ b/gcc/hwint.h
@@ -284,6 +284,7 @@  extern HOST_WIDE_INT gcd (HOST_WIDE_INT, HOST_WIDE_INT);
 extern HOST_WIDE_INT pos_mul_hwi (HOST_WIDE_INT, HOST_WIDE_INT);
 extern HOST_WIDE_INT mul_hwi (HOST_WIDE_INT, HOST_WIDE_INT);
 extern HOST_WIDE_INT least_common_multiple (HOST_WIDE_INT, HOST_WIDE_INT);
+extern unsigned HOST_WIDE_INT reflect_hwi (unsigned HOST_WIDE_INT, unsigned);
 
 /* Like ctz_hwi, except 0 when x == 0.  */
 
diff --git a/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc32.c b/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc32.c
new file mode 100644
index 00000000000..20d7d25f60e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc32.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile { target { riscv32*-*-* } } } */
+/* { dg-options "-march=rv32gc_zbc" } */
+
+#include <stdint-gcc.h>
+
+int8_t crc8_data8 ()
+{
+  return __builtin_crc8_data8 (0x34, 'a', 0x12);
+}
+
+int16_t crc16_data8 ()
+{
+  return __builtin_crc16_data8 (0x1234, 'a', 0x1021);
+}
+
+int16_t crc16_data16 ()
+{
+  return __builtin_crc16_data16 (0x1234, 0x3214, 0x1021);
+}
+
+/* { dg-final { scan-assembler-times "clmul\t" 6 } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc64.c b/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc64.c
new file mode 100644
index 00000000000..d99a78daaa9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/crc-builtin-zbc64.c
@@ -0,0 +1,66 @@ 
+/* { dg-do compile { target { riscv64*-*-* } } } */
+/* { dg-options "-march=rv64gc_zbc" } */
+
+#include <stdint-gcc.h>
+
+int8_t crc8_data8 ()
+{
+  return __builtin_crc8_data8 (0x34, 'a', 0x12);
+}
+
+int16_t crc16_data8 ()
+{
+  return __builtin_crc16_data8 (0x1234, 'a', 0x1021);
+}
+
+int16_t crc16_data16 ()
+{
+  return __builtin_crc16_data16 (0x1234, 0x3214, 0x1021);
+}
+
+int32_t crc32_data8 ()
+{
+  return __builtin_crc32_data8 (0xffffffff, 0x32, 0x4002123);
+}
+
+int32_t crc32_data16 ()
+{
+  return __builtin_crc32_data16 (0xffffffff, 0x3232, 0x4002123);
+}
+
+int32_t crc32_data32 ()
+{
+  return __builtin_crc32_data32 (0xffffffff, 0x123546ff, 0x4002123);
+}
+
+int8_t rev_crc8_data8 ()
+{
+  return __builtin_rev_crc8_data8 (0x34, 'a', 0x12);
+}
+
+int16_t rev_crc16_data8 ()
+{
+  return __builtin_rev_crc16_data8 (0x1234, 'a', 0x1021);
+}
+
+int16_t rev_crc16_data16 ()
+{
+  return __builtin_rev_crc16_data16 (0x1234, 0x3214, 0x1021);
+}
+
+int32_t rev_crc32_data8 ()
+{
+  return __builtin_rev_crc32_data8 (0xffffffff, 0x32, 0x4002123);
+}
+
+int32_t rev_crc32_data16 ()
+{
+  return __builtin_rev_crc32_data16 (0xffffffff, 0x3232, 0x4002123);
+}
+
+int32_t rev_crc32_data32 ()
+{
+  return __builtin_rev_crc32_data32 (0xffffffff, 0x123546ff, 0x4002123);
+}
+/* { dg-final { scan-assembler-times "clmul\t" 18 } } */
+/* { dg-final { scan-assembler-times "clmulh" 6 } } */
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 95acd0975bb..f2edbef92da 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -2065,6 +2065,39 @@  proc check_effective_target_riscv_zvbb { } {
     }]
 }
 
+# Return 1 if the target arch supports the Zbc extension, 0 otherwise.
+# Cache the result.
+
+proc check_effective_target_riscv_zbc { } {
+    return [check_no_compiler_messages riscv_ext_zbc assembly {
+       #ifndef __riscv_zbc
+       #error "Not __riscv_zbc"
+       #endif
+    }]
+}
+
+# Return 1 if the target arch supports the Zbkb extension, 0 otherwise.
+# Cache the result.
+
+proc check_effective_target_riscv_zbkb { } {
+    return [check_no_compiler_messages riscv_ext_zbkb assembly {
+       #ifndef __riscv_zbkb
+       #error "Not __riscv_zbkb"
+       #endif
+    }]
+}
+
+# Return 1 if the target arch supports the Zbkc extension, 0 otherwise.
+# Cache the result.
+
+proc check_effective_target_riscv_zbkc { } {
+    return [check_no_compiler_messages riscv_ext_zbkc assembly {
+       #ifndef __riscv_zbkc
+       #error "Not __riscv_zbkc"
+       #endif
+    }]
+}
+
 # Return 1 if the target arch supports the XTheadVector extension, 0 otherwise.
 # Cache the result.
 
@@ -2153,6 +2186,77 @@  proc check_effective_target_riscv_zvfh_ok { } {
     return 0
 }
 
+# Return 1 if we can execute code when using dg-add-options riscv_zbc
+
+proc check_effective_target_riscv_zbc_ok { } {
+    # If the target already supports zbc without any added options,
+    # we may assume we can execute just fine.
+    if { [check_effective_target_riscv_zbc] } {
+	return 1
+    }
+
+    # check if we can execute zbc insns with the given hardware or
+    # simulator
+    set gcc_march [riscv_get_arch]
+    if { [check_runtime ${gcc_march}_zbc_exec {
+	int main()
+	{
+	    asm ("clmul a0,a0,a1");
+	    asm ("clmulh a0,a0,a1");
+	    return 0;
+	} } "-march=${gcc_march}"] } {
+	    return 1
+	}
+    return 0
+}
+
+# Return 1 if we can execute code when using dg-add-options riscv_zbkb
+
+proc check_effective_target_riscv_zbkb_ok { } {
+    # If the target already supports zbkb without any added options,
+    # we may assume we can execute just fine.
+    if { [check_effective_target_riscv_zbkb] } {
+	return 1
+    }
+
+    # check if we can execute zbkb insns with the given hardware or
+    # simulator
+    set gcc_march [riscv_get_arch]
+    if { [check_runtime ${gcc_march}_zbkb_exec {
+	int main()
+	{
+	    asm ("brev8 a0,a0");
+	    return 0;
+	} } "-march=${gcc_march}"] } {
+	    return 1
+	}
+    return 0
+}
+
+# Return 1 if we can execute code when using dg-add-options riscv_zbkc
+
+proc check_effective_target_riscv_zbkc_ok { } {
+    # If the target already supports zbkc without any added options,
+    # we may assume we can execute just fine.
+    if { [check_effective_target_riscv_zbkc] } {
+	return 1
+    }
+
+    # check if we can execute zbkc insns with the given hardware or
+    # simulator
+    set gcc_march [riscv_get_arch]
+    if { [check_runtime ${gcc_march}_zbkc_exec {
+	int main()
+	{
+	    asm ("clmul a0,a0,a1");
+	    asm ("clmulh a0,a0,a1");
+	    return 0;
+	} } "-march=${gcc_march}"] } {
+	    return 1
+	}
+    return 0
+}
+
 # Return 1 if we can execute code when using dg-add-options riscv_zvbb
 
 proc check_effective_target_riscv_zvbb_ok { } {
@@ -2206,7 +2310,7 @@  proc check_effective_target_riscv_v_misalign_ok { } {
 proc riscv_get_arch { } {
     set gcc_march ""
     # ??? do we neeed to add more extensions to the list below?
-    foreach ext { i e m a f d q c b v zicsr zifencei zfh zba zbb zbc zbs zvbb zvfh ztso zaamo zalrsc zabha zacas } {
+    foreach ext { i e m a f d q c b v zicsr zifencei zfh zba zbb zbc zbkb zbkc zbs zvbb zvfh ztso zaamo zalrsc zabha zacas } {
 	if { [check_no_compiler_messages  riscv_ext_$ext assembly [string map [list DEF __riscv_$ext] {
 		#ifndef DEF
 		#error "Not DEF"