diff --git a/gcc/config/riscv/riscv-builtins.cc b/gcc/config/riscv/riscv-builtins.cc
index 3fe3a89dcc25..4f422e0891c2 100644
--- a/gcc/config/riscv/riscv-builtins.cc
+++ b/gcc/config/riscv/riscv-builtins.cc
@@ -47,12 +47,16 @@ along with GCC; see the file COPYING3.  If not see
 #define RISCV_FTYPE_NAME1(A, B) RISCV_##A##_FTYPE_##B
 #define RISCV_FTYPE_NAME2(A, B, C) RISCV_##A##_FTYPE_##B##_##C
 #define RISCV_FTYPE_NAME3(A, B, C, D) RISCV_##A##_FTYPE_##B##_##C##_##D
+/* Similar, but for variable argument function prototype.  */
+#define RISCV_FTYPE_NAME_VAR1(A, B) RISCV_##A##_FTYPE_##B##_VAR
 
 /* Classifies the prototype of a built-in function.  */
 enum riscv_function_type {
 #define DEF_RISCV_FTYPE(NARGS, LIST) RISCV_FTYPE_NAME##NARGS LIST,
+#define DEF_RISCV_FTYPE_VAR(NARGS, LIST) RISCV_FTYPE_NAME_VAR##NARGS LIST,
 #include "config/riscv/riscv-ftypes.def"
 #undef DEF_RISCV_FTYPE
+#undef DEF_RISCV_FTYPE_VAR
   RISCV_MAX_FTYPE_MAX
 };
 
@@ -62,7 +66,10 @@ enum riscv_builtin_type {
   RISCV_BUILTIN_DIRECT,
 
   /* Likewise, but with return type VOID.  */
-  RISCV_BUILTIN_DIRECT_NO_TARGET
+  RISCV_BUILTIN_DIRECT_NO_TARGET,
+
+  /* The function which requires custom expansion.  */
+  RISCV_BUILTIN_CUSTOM,
 };
 
 /* Declare an availability predicate for built-in functions.  */
@@ -90,6 +97,9 @@ struct riscv_builtin_description {
 
   /* Whether the function is available.  */
   unsigned int (*avail) (void);
+
+  /* Custom expansion function if builtin_type is RISCV_BUILTIN_CUSTOM.  */
+  rtx (*expand_function) (enum insn_code icode, rtx target, tree exp);
 };
 
 AVAIL (hard_float, TARGET_HARD_FLOAT || TARGET_ZFINX)
@@ -101,8 +111,8 @@ AVAIL (inval32, TARGET_ZICBOM && !TARGET_64BIT)
 AVAIL (inval64, TARGET_ZICBOM && TARGET_64BIT)
 AVAIL (zero32,  TARGET_ZICBOZ && !TARGET_64BIT)
 AVAIL (zero64,  TARGET_ZICBOZ && TARGET_64BIT)
-AVAIL (prefetchi32, TARGET_ZICBOP && !TARGET_64BIT)
-AVAIL (prefetchi64, TARGET_ZICBOP && TARGET_64BIT)
+AVAIL (prefetch32, TARGET_ZICBOP && !TARGET_64BIT)
+AVAIL (prefetch64, TARGET_ZICBOP && TARGET_64BIT)
 AVAIL (crypto_zbkb32, TARGET_ZBKB && !TARGET_64BIT)
 AVAIL (crypto_zbkb64, TARGET_ZBKB && TARGET_64BIT)
 AVAIL (crypto_zbkx32, TARGET_ZBKX && !TARGET_64BIT)
@@ -123,7 +133,7 @@ AVAIL (clmulr_zbc32, TARGET_ZBC && !TARGET_64BIT)
 AVAIL (clmulr_zbc64, TARGET_ZBC && TARGET_64BIT)
 AVAIL (hint_pause, (!0))
 
-/* Construct a riscv_builtin_description from the given arguments.
+/* Construct a simple riscv_builtin_description from the given arguments.
 
    INSN is the name of the associated instruction pattern, without the
    leading CODE_FOR_riscv_.
@@ -137,7 +147,7 @@ AVAIL (hint_pause, (!0))
    riscv_builtin_avail_.  */
 #define RISCV_BUILTIN(INSN, NAME, BUILTIN_TYPE,	FUNCTION_TYPE, AVAIL)	\
   { CODE_FOR_riscv_ ## INSN, "__builtin_riscv_" NAME,			\
-    BUILTIN_TYPE, FUNCTION_TYPE, riscv_builtin_avail_ ## AVAIL }
+    BUILTIN_TYPE, FUNCTION_TYPE, riscv_builtin_avail_ ## AVAIL, NULL }
 
 /* Define __builtin_riscv_<INSN>, which is a RISCV_BUILTIN_DIRECT function
    mapped to instruction CODE_FOR_riscv_<INSN>,  FUNCTION_TYPE and AVAIL
@@ -152,6 +162,25 @@ AVAIL (hint_pause, (!0))
   RISCV_BUILTIN (INSN, #INSN, RISCV_BUILTIN_DIRECT_NO_TARGET,		\
 		FUNCTION_TYPE, AVAIL)
 
+/* Construct a custom riscv_builtin_description from the given arguments.
+
+   INSN is the name of the associated instruction pattern, without the
+   leading CODE_FOR_riscv_.
+
+   NAME is the name of the function itself, without the leading
+   "__builtin_riscv_".
+
+   FTYPE is the prototype field in riscv_builtin_description.
+
+   AVAIL is the name of the availability predicate, without the leading
+   riscv_builtin_avail_.
+
+   EXPANDER is the function to expand the builtin.  */
+#define RISCV_CUSTOM_BUILTIN(INSN, NAME, FTYPE, AVAIL, EXPANDER)	\
+  { CODE_FOR_riscv_ ## INSN, "__builtin_riscv_" NAME,			\
+    RISCV_BUILTIN_CUSTOM, FTYPE,					\
+    riscv_builtin_avail_ ## AVAIL, EXPANDER }
+
 /* Argument types.  */
 #define RISCV_ATYPE_VOID void_type_node
 #define RISCV_ATYPE_UQI unsigned_intQI_type_node
@@ -171,6 +200,8 @@ AVAIL (hint_pause, (!0))
 #define RISCV_FTYPE_ATYPES3(A, B, C, D) \
   RISCV_ATYPE_##A, RISCV_ATYPE_##B, RISCV_ATYPE_##C, RISCV_ATYPE_##D
 
+static rtx expand_builtin_prefetch_riscv (enum insn_code icode, rtx target, tree exp);
+
 static const struct riscv_builtin_description riscv_builtins[] = {
   #include "riscv-cmo.def"
   #include "riscv-scalar-crypto.def"
@@ -209,8 +240,15 @@ riscv_build_function_type (enum riscv_function_type type)
       = build_function_type_list (RISCV_FTYPE_ATYPES##NUM ARGS,		\
 				  NULL_TREE);				\
     break;
+#define DEF_RISCV_FTYPE_VAR(NUM, ARGS)					\
+  case RISCV_FTYPE_NAME_VAR##NUM ARGS:				\
+    types[(int) type]							\
+      = build_varargs_function_type_list (RISCV_FTYPE_ATYPES##NUM ARGS,	\
+					  NULL_TREE);			\
+    break;
 #include "config/riscv/riscv-ftypes.def"
 #undef DEF_RISCV_FTYPE
+#undef DEF_RISCV_FTYPE_VAR
       default:
 	gcc_unreachable ();
       }
@@ -387,6 +425,9 @@ riscv_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
 
 	  case RISCV_BUILTIN_DIRECT_NO_TARGET:
 	    return riscv_expand_builtin_direct (d->icode, target, exp, false);
+
+	  case RISCV_BUILTIN_CUSTOM:
+	    return d->expand_function (d->icode, target, exp);
 	  }
       }
     }
@@ -394,6 +435,67 @@ riscv_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   gcc_unreachable ();
 }
 
+static rtx
+expand_builtin_prefetch_riscv (enum insn_code icode,
+			       rtx target ATTRIBUTE_UNUSED,
+			       tree exp)
+{
+  struct expand_operand ops[2];
+  tree arg0, arg1;
+  rtx op1;
+
+  char name_suffix = 'i';
+  switch (icode)
+    {
+    case CODE_FOR_riscv_prefetch_i_si:
+    case CODE_FOR_riscv_prefetch_i_di:
+      break;
+    case CODE_FOR_riscv_prefetch_r_si:
+    case CODE_FOR_riscv_prefetch_r_di:
+      name_suffix = 'r';
+      break;
+    case CODE_FOR_riscv_prefetch_w_si:
+    case CODE_FOR_riscv_prefetch_w_di:
+      name_suffix = 'w';
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Argument 0 is an address.  */
+  arg0 = CALL_EXPR_ARG (exp, 0);
+
+  /* Argument 1 (offset) is an optional compile-time constant and defaults
+     to zero.  It must be a valid offset for a S-type instruction AND must be
+     a multiple of 32.  */
+  if (call_expr_nargs (exp) > 1)
+    arg1 = CALL_EXPR_ARG (exp, 1);
+  else
+    arg1 = integer_zero_node;
+  if (TREE_CODE (arg1) != INTEGER_CST)
+    {
+      error ("second argument to %<__builtin_riscv_prefetch_%c%> must be a"
+	     " constant", name_suffix);
+      arg1 = integer_zero_node;
+    }
+  op1 = expand_normal (arg1);
+  if (INTVAL (op1) < -IMM_REACH / 2
+      || INTVAL (op1) >= IMM_REACH / 2
+      || INTVAL (op1) % 32 != 0)
+    {
+      error ("second argument to %<__builtin_riscv_prefetch_%c%> must be a"
+	     " valid offset for the prefetch instruction", name_suffix);
+      arg1 = integer_zero_node;
+      op1 = expand_normal (arg1);
+    }
+
+  create_input_operand (&ops[0], expand_normal (arg0),
+			TYPE_MODE (TREE_TYPE (arg0)));
+  create_input_operand (&ops[1], op1,
+			TYPE_MODE (TREE_TYPE (arg1)));
+  return riscv_expand_builtin_insn (icode, 2, ops, false);
+}
+
 /* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV.  */
 
 void
diff --git a/gcc/config/riscv/riscv-cmo.def b/gcc/config/riscv/riscv-cmo.def
index ff713b78e19e..3be77710095e 100644
--- a/gcc/config/riscv/riscv-cmo.def
+++ b/gcc/config/riscv/riscv-cmo.def
@@ -13,8 +13,12 @@ RISCV_BUILTIN (zero_si, "zicboz_cbo_zero", RISCV_BUILTIN_DIRECT_NO_TARGET, RISCV
 RISCV_BUILTIN (zero_di, "zicboz_cbo_zero", RISCV_BUILTIN_DIRECT_NO_TARGET, RISCV_VOID_FTYPE_VOID_PTR, zero64),
 
 // zicbop
-RISCV_BUILTIN (prefetchi_si, "zicbop_cbo_prefetchi", RISCV_BUILTIN_DIRECT, RISCV_USI_FTYPE_USI, prefetchi32),
-RISCV_BUILTIN (prefetchi_di, "zicbop_cbo_prefetchi", RISCV_BUILTIN_DIRECT, RISCV_UDI_FTYPE_UDI, prefetchi64),
+RISCV_CUSTOM_BUILTIN (prefetch_i_si, "prefetch_i", RISCV_VOID_FTYPE_VOID_PTR_VAR, prefetch32, expand_builtin_prefetch_riscv),
+RISCV_CUSTOM_BUILTIN (prefetch_i_di, "prefetch_i", RISCV_VOID_FTYPE_VOID_PTR_VAR, prefetch64, expand_builtin_prefetch_riscv),
+RISCV_CUSTOM_BUILTIN (prefetch_r_si, "prefetch_r", RISCV_VOID_FTYPE_VOID_PTR_VAR, prefetch32, expand_builtin_prefetch_riscv),
+RISCV_CUSTOM_BUILTIN (prefetch_r_di, "prefetch_r", RISCV_VOID_FTYPE_VOID_PTR_VAR, prefetch64, expand_builtin_prefetch_riscv),
+RISCV_CUSTOM_BUILTIN (prefetch_w_si, "prefetch_w", RISCV_VOID_FTYPE_VOID_PTR_VAR, prefetch32, expand_builtin_prefetch_riscv),
+RISCV_CUSTOM_BUILTIN (prefetch_w_di, "prefetch_w", RISCV_VOID_FTYPE_VOID_PTR_VAR, prefetch64, expand_builtin_prefetch_riscv),
 
 // zbkc or zbc
 RISCV_BUILTIN (clmul_si, "clmul", RISCV_BUILTIN_DIRECT, RISCV_USI_FTYPE_USI_USI, clmul_zbkc32_or_zbc32),
diff --git a/gcc/config/riscv/riscv-ftypes.def b/gcc/config/riscv/riscv-ftypes.def
index 33620c57ca06..052acf0159d3 100644
--- a/gcc/config/riscv/riscv-ftypes.def
+++ b/gcc/config/riscv/riscv-ftypes.def
@@ -41,3 +41,4 @@ DEF_RISCV_FTYPE (2, (UDI, USI, USI))
 DEF_RISCV_FTYPE (2, (UDI, UDI, USI))
 DEF_RISCV_FTYPE (2, (UDI, UDI, UDI))
 DEF_RISCV_FTYPE (3, (USI, USI, USI, USI))
+DEF_RISCV_FTYPE_VAR (1, (VOID, VOID_PTR))
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index e00b8ee3579d..eaa8b6a9f085 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -118,7 +118,9 @@
   UNSPECV_FLUSH
   UNSPECV_INVAL
   UNSPECV_ZERO
-  UNSPECV_PREI
+  UNSPECV_PREFETCH_I
+  UNSPECV_PREFETCH_R
+  UNSPECV_PREFETCH_W
 
   ;; Zihintpause unspec
   UNSPECV_PAUSE
@@ -3464,12 +3466,28 @@
 }
   [(set_attr "type" "cbo")])
 
-(define_insn "riscv_prefetchi_<mode>"
-  [(unspec_volatile:X [(match_operand:X 0 "address_operand" "r")
-              (match_operand:X 1 "imm5_operand" "i")]
-              UNSPECV_PREI)]
+(define_insn "riscv_prefetch_i_<mode>"
+  [(unspec_volatile:X [(match_operand:X 0 "register_operand" "r")
+		       (match_operand:X 1 "const_int_operand" "n")]
+		       UNSPECV_PREFETCH_I)]
   "TARGET_ZICBOP"
-  "prefetch.i\t%a0"
+  "prefetch.i\t%1(%0)"
+  [(set_attr "type" "cbo")])
+
+(define_insn "riscv_prefetch_r_<mode>"
+  [(unspec_volatile:X [(match_operand:X 0 "register_operand" "r")
+		       (match_operand:X 1 "const_int_operand" "n")]
+		       UNSPECV_PREFETCH_R)]
+  "TARGET_ZICBOP"
+  "prefetch.r\t%1(%0)"
+  [(set_attr "type" "cbo")])
+
+(define_insn "riscv_prefetch_w_<mode>"
+  [(unspec_volatile:X [(match_operand:X 0 "register_operand" "r")
+		       (match_operand:X 1 "const_int_operand" "n")]
+		       UNSPECV_PREFETCH_W)]
+  "TARGET_ZICBOP"
+  "prefetch.w\t%1(%0)"
   [(set_attr "type" "cbo")])
 
 (define_expand "extv<mode>"
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-1.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-1.c
index c5d78c1763d3..f275d4633cce 100644
--- a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-1.c
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-1.c
@@ -1,23 +1,28 @@
-/* { dg-do compile target { { rv64-*-*}}} */
-/* { dg-options "-march=rv64gc_zicbop -mabi=lp64" } */
+/* { dg-do compile } */
+/* { dg-options "-march=rv32i_zicbop -mabi=ilp32" } */
 
 void foo (char *p)
 {
-  __builtin_prefetch (p, 0, 0);
-  __builtin_prefetch (p, 0, 1);
-  __builtin_prefetch (p, 0, 2);
-  __builtin_prefetch (p, 0, 3);
-  __builtin_prefetch (p, 1, 0);
-  __builtin_prefetch (p, 1, 1);
-  __builtin_prefetch (p, 1, 2);
-  __builtin_prefetch (p, 1, 3);
+  __builtin_riscv_prefetch_i (p);
+  __builtin_riscv_prefetch_i (p, 0);
+  __builtin_riscv_prefetch_i (p, 32);
+  __builtin_riscv_prefetch_i (p, -64);
+  __builtin_riscv_prefetch_r (p);
+  __builtin_riscv_prefetch_r (p, 0);
+  __builtin_riscv_prefetch_r (p, 32);
+  __builtin_riscv_prefetch_r (p, -64);
+  __builtin_riscv_prefetch_w (p);
+  __builtin_riscv_prefetch_w (p, 0);
+  __builtin_riscv_prefetch_w (p, 32);
+  __builtin_riscv_prefetch_w (p, -64);
 }
 
-int foo1()
-{
-  return __builtin_riscv_zicbop_cbo_prefetchi(1);
-}
-
-/* { dg-final { scan-assembler-times "prefetch.i" 1 } } */
-/* { dg-final { scan-assembler-times "prefetch.r" 4 } } */
-/* { dg-final { scan-assembler-times "prefetch.w" 4 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.i\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.i\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.i\t-64\\(" 1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t-64\\(" 1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t-64\\(" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-2.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-2.c
index 6576365b39ca..17fff9e199da 100644
--- a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-2.c
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-2.c
@@ -1,23 +1,14 @@
-/* { dg-do compile target { { rv32-*-*}}} */
-/* { dg-options "-march=rv32gc_zicbop -mabi=ilp32" } */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64i_zicbop -mabi=lp64" } */
 
-void foo (char *p)
-{
-  __builtin_prefetch (p, 0, 0);
-  __builtin_prefetch (p, 0, 1);
-  __builtin_prefetch (p, 0, 2);
-  __builtin_prefetch (p, 0, 3);
-  __builtin_prefetch (p, 1, 0);
-  __builtin_prefetch (p, 1, 1);
-  __builtin_prefetch (p, 1, 2);
-  __builtin_prefetch (p, 1, 3);
-}
+#include "cmo-zicbop-1.c"
 
-int foo1()
-{
-  return __builtin_riscv_zicbop_cbo_prefetchi(1);
-}
-
-/* { dg-final { scan-assembler-times "prefetch.i" 1 } } */
-/* { dg-final { scan-assembler-times "prefetch.r" 4 } } */
-/* { dg-final { scan-assembler-times "prefetch.w" 4 } } */ 
+/* { dg-final { scan-assembler-times "prefetch\\.i\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.i\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.i\t-64\\(" 1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t-64\\(" 1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t-64\\(" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-3.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-3.c
new file mode 100644
index 000000000000..89bf6c57a390
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-3.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32i_zicbop -mabi=ilp32" } */
+
+void foo (void)
+{
+  /* Use NULL (0) instead of a pointer.  */
+  __builtin_riscv_prefetch_i (0);
+  __builtin_riscv_prefetch_i (0, 0);
+  __builtin_riscv_prefetch_i (0, 32);
+  __builtin_riscv_prefetch_i (0, -64);
+  __builtin_riscv_prefetch_r (0);
+  __builtin_riscv_prefetch_r (0, 0);
+  __builtin_riscv_prefetch_r (0, 32);
+  __builtin_riscv_prefetch_r (0, -64);
+  __builtin_riscv_prefetch_w (0);
+  __builtin_riscv_prefetch_w (0, 0);
+  __builtin_riscv_prefetch_w (0, 32);
+  __builtin_riscv_prefetch_w (0, -64);
+}
+
+/* { dg-final { scan-assembler-times "prefetch\\.i\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.i\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.i\t-64\\(" 1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.r\t-64\\(" 1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t0\\("   2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t32\\("  1 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w\t-64\\(" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-4.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-4.c
new file mode 100644
index 000000000000..372fdfa3d868
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-4.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32i_zicbop -mabi=ilp32" } */
+/* { dg-skip-if "" { *-*-* } { "-flto"} } */
+
+int foo (char* p, int offset)
+{
+  __builtin_riscv_prefetch_i();
+  __builtin_riscv_prefetch_r();
+  __builtin_riscv_prefetch_w();
+}
+
+/* { dg-error "too few arguments to function '__builtin_riscv_prefetch_i'" "" { target *-*-* } 7 } */
+/* { dg-error "too few arguments to function '__builtin_riscv_prefetch_r'" "" { target *-*-* } 8 } */
+/* { dg-error "too few arguments to function '__builtin_riscv_prefetch_w'" "" { target *-*-* } 9 } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-5.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-5.c
new file mode 100644
index 000000000000..7dbfe49b24fd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-5.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32i_zicbop -mabi=ilp32" } */
+/* { dg-skip-if "" { *-*-* } { "-flto"} } */
+
+int foo (char* p, int offset)
+{
+  __builtin_riscv_prefetch_i(p, offset);
+  __builtin_riscv_prefetch_r(p, offset);
+  __builtin_riscv_prefetch_w(p, offset);
+}
+
+/* { dg-error "second argument to '__builtin_riscv_prefetch_i' must be a constant" "not compile-time constant" { target *-*-* } 7 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_r' must be a constant" "not compile-time constant" { target *-*-* } 8 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_w' must be a constant" "not compile-time constant" { target *-*-* } 9 } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-6.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-6.c
new file mode 100644
index 000000000000..b8deb2d6c807
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-6.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32i_zicbop -mabi=ilp32" } */
+/* { dg-skip-if "" { *-*-* } { "-flto"} } */
+
+int foo (char* p)
+{
+  __builtin_riscv_prefetch_i(p, 1);
+  __builtin_riscv_prefetch_r(p, 1);
+  __builtin_riscv_prefetch_w(p, 1);
+  __builtin_riscv_prefetch_i(p, 16);
+  __builtin_riscv_prefetch_r(p, 16);
+  __builtin_riscv_prefetch_w(p, 16);
+  __builtin_riscv_prefetch_i(p, -2080);
+  __builtin_riscv_prefetch_r(p, -2080);
+  __builtin_riscv_prefetch_w(p, -2080);
+  __builtin_riscv_prefetch_i(p, +2048);
+  __builtin_riscv_prefetch_r(p, +2048);
+  __builtin_riscv_prefetch_w(p, +2048);
+  __builtin_riscv_prefetch_i(p, 0x800000000000000llu);
+  __builtin_riscv_prefetch_r(p, 0x800000000000000llu);
+  __builtin_riscv_prefetch_w(p, 0x800000000000000llu);
+}
+
+/* { dg-error "second argument to '__builtin_riscv_prefetch_i' must be a valid offset for the prefetch instruction" "unaligned offset 1" { target *-*-* }  7 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_r' must be a valid offset for the prefetch instruction" "unaligned offset 1" { target *-*-* }  8 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_w' must be a valid offset for the prefetch instruction" "unaligned offset 1" { target *-*-* }  9 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_i' must be a valid offset for the prefetch instruction" "unaligned offset 2" { target *-*-* } 10 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_r' must be a valid offset for the prefetch instruction" "unaligned offset 2" { target *-*-* } 11 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_w' must be a valid offset for the prefetch instruction" "unaligned offset 2" { target *-*-* } 12 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_i' must be a valid offset for the prefetch instruction" "out of range (negative)" { target *-*-* } 13 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_r' must be a valid offset for the prefetch instruction" "out of range (negative)" { target *-*-* } 14 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_w' must be a valid offset for the prefetch instruction" "out of range (negative)" { target *-*-* } 15 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_i' must be a valid offset for the prefetch instruction" "out of range (positive 1)" { target *-*-* } 16 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_r' must be a valid offset for the prefetch instruction" "out of range (positive 1)" { target *-*-* } 17 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_w' must be a valid offset for the prefetch instruction" "out of range (positive 1)" { target *-*-* } 18 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_i' must be a valid offset for the prefetch instruction" "out of range (positive 2)" { target *-*-* } 19 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_r' must be a valid offset for the prefetch instruction" "out of range (positive 2)" { target *-*-* } 20 } */
+/* { dg-error "second argument to '__builtin_riscv_prefetch_w' must be a valid offset for the prefetch instruction" "out of range (positive 2)" { target *-*-* } 21 } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-1.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-1.c
new file mode 100644
index 000000000000..14b89b8ddf35
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-1.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32i_zicbop -mabi=ilp32" } */
+
+void foo (char *p)
+{
+  __builtin_prefetch (p, 0, 0);
+  __builtin_prefetch (p, 0, 1);
+  __builtin_prefetch (p, 0, 2);
+  __builtin_prefetch (p, 0, 3);
+  __builtin_prefetch (p, 1, 0);
+  __builtin_prefetch (p, 1, 1);
+  __builtin_prefetch (p, 1, 2);
+  __builtin_prefetch (p, 1, 3);
+}
+
+/* { dg-final { scan-assembler-times "prefetch\\.r" 4 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w" 4 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-2.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-2.c
new file mode 100644
index 000000000000..2fa38dccfcb0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-2.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64i_zicbop -mabi=lp64" } */
+
+#include "cmo-zicbop-by-common-1.c"
+
+/* { dg-final { scan-assembler-times "prefetch\\.r" 4 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w" 4 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-3.c b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-3.c
new file mode 100644
index 000000000000..37505d3d13c3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cmo-zicbop-by-common-3.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv32i_zicbop -mabi=ilp32" } */
+
+void foo (char *p)
+{
+  /* Second argument defaults to zero (read).  */
+  __builtin_prefetch (p);
+  __builtin_prefetch (p, 0);
+  __builtin_prefetch (p, 1);
+}
+
+/* { dg-final { scan-assembler-times "prefetch\\.r" 2 } } */
+/* { dg-final { scan-assembler-times "prefetch\\.w" 1 } } */
