diff mbox series

[1/1] RISC-V: Add support for XCVmem extension in CV32E40P

Message ID 20231109124219.966619-2-mary.bennett@embecosm.com
State New
Headers show
Series RISC-V: Support CORE-V XCVMEM extension | expand

Commit Message

Mary Bennett Nov. 9, 2023, 12:42 p.m. UTC
XCVmem adds more loads and stores. To prevent non-XCVmem loads and
stores from generating illegal XCVmem specific operands, constraint
'm' was redefined. 'm' does not accept POST_MODIFY or reg + reg
addresses.

Spec: github.com/openhwgroup/core-v-sw/blob/master/specifications/corev-builtin-spec.md

Contributors:
  Mary Bennett <mary.bennett@embecosm.com>
  Nandni Jamnadas <nandni.jamnadas@embecosm.com>
  Pietra Ferreira <pietra.ferreira@embecosm.com>
  Charlie Keaney
  Jessica Mills
  Craig Blackmore <craig.blackmore@embecosm.com>
  Simon Cook <simon.cook@embecosm.com>
  Jeremy Bennett <jeremy.bennett@embecosm.com>
  Helene Chelin <helene.chelin@embecosm.com>

gcc/ChangeLog:
	* common/config/riscv/riscv-common.cc: Add the XCVmem
	  extension.
	* config/riscv/riscv.opt: Likewise.
	* config/riscv/corev.md: Likewise.
	* config/riscv/predicates.md: Likewise.
	* config/riscv/riscv-protos.h: Likewise.
	* config/riscv/riscv.cc: Add POST_MODIFY.
	* config/riscv/riscv.h: Likewise.
	* config/riscv/riscv.md: Prevent XCVmem operands being
	  used in non-XCVmem loads and stores.
        * config/riscv/constraints.md: Likewise.
	* config/riscv/predicates.md: Likewise.
	* doc/sourcebuild.texi: Add XCVmem documentation.

gcc/testsuite/ChangeLog:
	* gcc.target/riscv/cv-mem-operand-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-operand-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-operand-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-operand-compile-4.c: New test.
	* gcc.target/riscv/cv-mem-operand-compile-5.c: New test.
	* gcc.target/riscv/cv-mem-operand-compile-6.c: New test.
	* gcc.target/riscv/cv-mem-operand-compile-7.c: New test.
        * gcc.target/riscv/cv-mem-operand-compile-8.c: New test.
	* gcc.target/riscv/cv-mem-lb-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-lb-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-lb-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-lbu-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-lbu-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-lbu-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-lh-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-lh-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-lh-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-lhu-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-lhu-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-lhu-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-lw-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-lw-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-lw-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-sb-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-sb-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-sb-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-sh-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-sh-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-sh-compile-3.c: New test.
	* gcc.target/riscv/cv-mem-sw-compile-1.c: New test.
	* gcc.target/riscv/cv-mem-sw-compile-2.c: New test.
	* gcc.target/riscv/cv-mem-sw-compile-3.c: New test.
	* lib/target-supports.exp: Add proc for XCVmem.
---
 gcc/common/config/riscv/riscv-common.cc       |   2 +
 gcc/config/riscv/constraints.md               |  28 +++
 gcc/config/riscv/corev.md                     | 227 ++++++++++++++++++
 gcc/config/riscv/predicates.md                |  20 +-
 gcc/config/riscv/riscv-protos.h               |  12 +-
 gcc/config/riscv/riscv.cc                     |  48 +++-
 gcc/config/riscv/riscv.h                      |   6 +-
 gcc/config/riscv/riscv.md                     |  46 ++--
 gcc/config/riscv/riscv.opt                    |   2 +
 gcc/doc/sourcebuild.texi                      |   3 +
 .../gcc.target/riscv/cv-mem-lb-compile-1.c    |  23 ++
 .../gcc.target/riscv/cv-mem-lb-compile-2.c    |  24 ++
 .../gcc.target/riscv/cv-mem-lb-compile-3.c    |  16 ++
 .../gcc.target/riscv/cv-mem-lbu-compile-1.c   |  23 ++
 .../gcc.target/riscv/cv-mem-lbu-compile-2.c   |  24 ++
 .../gcc.target/riscv/cv-mem-lbu-compile-3.c   |  16 ++
 .../gcc.target/riscv/cv-mem-lh-compile-1.c    |  23 ++
 .../gcc.target/riscv/cv-mem-lh-compile-2.c    |  24 ++
 .../gcc.target/riscv/cv-mem-lh-compile-3.c    |  16 ++
 .../gcc.target/riscv/cv-mem-lhu-compile-1.c   |  23 ++
 .../gcc.target/riscv/cv-mem-lhu-compile-2.c   |  24 ++
 .../gcc.target/riscv/cv-mem-lhu-compile-3.c   |  16 ++
 .../gcc.target/riscv/cv-mem-lw-compile-1.c    |  38 +++
 .../gcc.target/riscv/cv-mem-lw-compile-2.c    |  38 +++
 .../gcc.target/riscv/cv-mem-lw-compile-3.c    |  22 ++
 .../riscv/cv-mem-operand-compile-1.c          |  19 ++
 .../riscv/cv-mem-operand-compile-2.c          |  20 ++
 .../riscv/cv-mem-operand-compile-3.c          |  28 +++
 .../riscv/cv-mem-operand-compile-4.c          |  21 ++
 .../riscv/cv-mem-operand-compile-5.c          |  25 ++
 .../riscv/cv-mem-operand-compile-6.c          |  21 ++
 .../riscv/cv-mem-operand-compile-7.c          |  24 ++
 .../riscv/cv-mem-operand-compile-8.c          |  18 ++
 .../gcc.target/riscv/cv-mem-sb-compile-1.c    |  36 +++
 .../gcc.target/riscv/cv-mem-sb-compile-2.c    |  38 +++
 .../gcc.target/riscv/cv-mem-sb-compile-3.c    |  30 +++
 .../gcc.target/riscv/cv-mem-sh-compile-1.c    |  36 +++
 .../gcc.target/riscv/cv-mem-sh-compile-2.c    |  38 +++
 .../gcc.target/riscv/cv-mem-sh-compile-3.c    |  30 +++
 .../gcc.target/riscv/cv-mem-sw-compile-1.c    |  36 +++
 .../gcc.target/riscv/cv-mem-sw-compile-2.c    |  38 +++
 .../gcc.target/riscv/cv-mem-sw-compile-3.c    |  30 +++
 gcc/testsuite/lib/target-supports.exp         |  14 ++
 43 files changed, 1216 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-6.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-7.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-8.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-3.c

Comments

Kito Cheng Nov. 19, 2023, 7:55 a.m. UTC | #1
> diff --git a/gcc/config/riscv/constraints.md b/gcc/config/riscv/constraints.md
> index 718b4bd77df..f67bff0940f 100644
> --- a/gcc/config/riscv/constraints.md
> +++ b/gcc/config/riscv/constraints.md
> @@ -35,6 +35,17 @@
>
>  ;; General constraints
>
> +(define_memory_constraint "m"
> +  "An address that is not base reg + index reg or post modify."
> +  (and (match_code "mem")
> +       (and (match_test "memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0),
> +                                                 MEM_ADDR_SPACE (op))")
> +            (not (match_test "((GET_CODE (XEXP (op, 0)) == PLUS
> +                && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
> +                && GET_CODE (XEXP (XEXP (op, 0), 1)) == REG)
> +                || GET_CODE (XEXP (op, 0)) == POST_MODIFY)
> +               && TARGET_XCVMEM")))))
> +

I would suggest not overriding `m` and using the same move pattern
instead, like th_output_move.

I believe that should be good for both code gen and maintainability.

For codegen, IRA/LRA would like to see all possible solutions put
within the move pattern.

>  (define_constraint "I"
>    "An I-type 12-bit signed immediate."
>    (and (match_code "const_int")
> diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
> index 1e9813b4f39..185e7d6ce23 100644
> --- a/gcc/config/riscv/riscv.h
> +++ b/gcc/config/riscv/riscv.h
> @@ -44,6 +44,8 @@ along with GCC; see the file COPYING3.  If not see
>  #define RISCV_TUNE_STRING_DEFAULT "rocket"
>  #endif
>
> +#define TARGET_MEM_CONSTRAINT 'w'

Maybe I missed something but I didn't see it used anywhere?

> +
>  extern const char *riscv_expand_arch (int argc, const char **argv);
>  extern const char *riscv_expand_arch_from_cpu (int argc, const char **argv);
>  extern const char *riscv_default_mtune (int argc, const char **argv);
> diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
> index 168c8665a7a..5c9ea62d943 100644
> --- a/gcc/config/riscv/riscv.md
> +++ b/gcc/config/riscv/riscv.md
> @@ -1663,13 +1663,13 @@
>
>  (define_expand "zero_extendsidi2"
>    [(set (match_operand:DI 0 "register_operand")
> -       (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand")))]
> +       (zero_extend:DI (match_operand:SI 1 "nonimmediate_nonpostinc")))]

Does Core-V support RV64? Does it xcvmem support cv.lwu if it supports RV64?

Don't change this if it does not support RV64, and see comment of
zero_extendhi if
it supports.

>    "TARGET_64BIT")
>
>  (define_insn_and_split "*zero_extendsidi2_internal"
>    [(set (match_operand:DI     0 "register_operand"     "=r,r")
>         (zero_extend:DI
> -           (match_operand:SI 1 "nonimmediate_operand" " r,m")))]
> +           (match_operand:SI 1 "nonimmediate_nonpostinc" " r,m")))]
>    "TARGET_64BIT && !TARGET_ZBA && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX
>     && !(register_operand (operands[1], SImode)
>          && reg_or_subregno (operands[1]) == VL_REGNUM)"

> @@ -1691,13 +1691,13 @@
>  (define_expand "zero_extendhi<GPR:mode>2"
>    [(set (match_operand:GPR    0 "register_operand")
>         (zero_extend:GPR
> -           (match_operand:HI 1 "nonimmediate_operand")))]
> +           (match_operand:HI 1 "nonimmediate_nonpostinc")))]


I would like to prevent this change if possible.

>    "")
>
>  (define_insn_and_split "*zero_extendhi<GPR:mode>2"
>    [(set (match_operand:GPR    0 "register_operand"     "=r,r")
>         (zero_extend:GPR
> -           (match_operand:HI 1 "nonimmediate_operand" " r,m")))]
> +           (match_operand:HI 1 "nonimmediate_nonpostinc" " r,m")))]
>    "!TARGET_ZBB && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
>    "@
>     #

Use riscv_output_move for outputting lhu, then it should be able to naturally
support cv.lhu as well.

Same comment for all other zero_extend* and extend* patterns.

> @@ -1827,7 +1827,7 @@
>  })
>
>  (define_insn "*movhf_hardfloat"
> -  [(set (match_operand:HF 0 "nonimmediate_operand" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
> +  [(set (match_operand:HF 0 "nonimmediate_nonpostinc" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")

Just repeat here, I really want to prevent using separated patterns to
handle postinc.

>         (match_operand:HF 1 "move_operand"         " f,zfli,G,m,f,G,*r,*f,*G*r,*m,*r"))]
>    "TARGET_ZFHMIN
>     && (register_operand (operands[0], HFmode)
diff mbox series

Patch

diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index 04631e007f0..862674f921b 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -314,6 +314,7 @@  static const struct riscv_ext_version riscv_ext_version_table[] =
   {"xcvalu", ISA_SPEC_CLASS_NONE, 1, 0},
   {"xcvelw", ISA_SPEC_CLASS_NONE, 1, 0},
   {"xcvbi", ISA_SPEC_CLASS_NONE, 1, 0},
+  {"xcvmem", ISA_SPEC_CLASS_NONE, 1, 0},
 
   {"xtheadba", ISA_SPEC_CLASS_NONE, 1, 0},
   {"xtheadbb", ISA_SPEC_CLASS_NONE, 1, 0},
@@ -1671,6 +1672,7 @@  static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
   {"xcvalu",        &gcc_options::x_riscv_xcv_subext, MASK_XCVALU},
   {"xcvelw",        &gcc_options::x_riscv_xcv_subext, MASK_XCVELW},
   {"xcvbi",         &gcc_options::x_riscv_xcv_subext, MASK_XCVBI},
+  {"xcvmem",        &gcc_options::x_riscv_xcv_subext, MASK_XCVMEM},
 
   {"xtheadba",      &gcc_options::x_riscv_xthead_subext, MASK_XTHEADBA},
   {"xtheadbb",      &gcc_options::x_riscv_xthead_subext, MASK_XTHEADBB},
diff --git a/gcc/config/riscv/constraints.md b/gcc/config/riscv/constraints.md
index 718b4bd77df..f67bff0940f 100644
--- a/gcc/config/riscv/constraints.md
+++ b/gcc/config/riscv/constraints.md
@@ -35,6 +35,17 @@ 
 
 ;; General constraints
 
+(define_memory_constraint "m"
+  "An address that is not base reg + index reg or post modify."
+  (and (match_code "mem")
+       (and (match_test "memory_address_addr_space_p (GET_MODE (op), XEXP (op, 0),
+                                                 MEM_ADDR_SPACE (op))")
+            (not (match_test "((GET_CODE (XEXP (op, 0)) == PLUS
+                && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
+                && GET_CODE (XEXP (XEXP (op, 0), 1)) == REG)
+                || GET_CODE (XEXP (op, 0)) == POST_MODIFY)
+		&& TARGET_XCVMEM")))))
+
 (define_constraint "I"
   "An I-type 12-bit signed immediate."
   (and (match_code "const_int")
@@ -253,3 +264,20 @@ 
    A 5-bit signed immediate for CORE-V Immediate Branch."
   (and (match_code "const_int")
        (match_test "IN_RANGE (ival, -16, 15)")))
+
+(define_constraint "CV_mem_plus"
+  "@internal
+   An address for reg+reg stores and loads"
+  (and (match_code "mem")
+       (match_test "GET_CODE (XEXP (op, 0)) == PLUS
+                && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
+                && GET_CODE (XEXP (XEXP (op, 0), 1)) == REG")))
+
+(define_constraint "CV_mem_post"
+  "@internal
+   An address for post-modify or reg+reg stores and loads"
+  (and (match_code "mem")
+       (match_test "(GET_CODE (XEXP (op, 0)) == PLUS
+                && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
+                && GET_CODE (XEXP (XEXP (op, 0), 1)) == REG)
+		|| GET_CODE (XEXP (op, 0)) == POST_MODIFY")))
diff --git a/gcc/config/riscv/corev.md b/gcc/config/riscv/corev.md
index 7d7b952d817..fef7666e5dd 100644
--- a/gcc/config/riscv/corev.md
+++ b/gcc/config/riscv/corev.md
@@ -720,3 +720,230 @@ 
   "cv.b%C1imm\t%2,%3,%0"
   [(set_attr "type" "branch")
    (set_attr "mode" "none")])
+
+;;CORE-V Post Increment Load/ Store
+;; Post Increment Register-Immediate and Register-Register Load/Store
+(define_insn "cv_load<mode>_postinc"
+   [(set (match_operand:ANYI 0 "register_operand" "=r")
+         (match_operand:ANYI 1 "mem_post_inc" "CV_mem_post"))]
+  "TARGET_XCVMEM && riscv_legitimate_xcvmem_address_p (<MODE>mode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))"
+  "cv.<load>\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "cv_load_<optab><SHORT:mode>_postinc"
+   [(set (match_operand:SI 0 "register_operand" "=r")
+     (any_extend:SI (match_operand:SHORT 1 "mem_post_inc" "CV_mem_post")))]
+  "TARGET_XCVMEM && riscv_legitimate_xcvmem_address_p (<MODE>mode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))"
+  "cv.<load><u>\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "cv_loadsf_postinc_hardfloat"
+   [(set (match_operand:SF 0 "register_operand" "=r")
+         (match_operand:SF 1 "mem_post_inc" "CV_mem_post"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.lw\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_loadsf_postinc_softfloat"
+   [(set (match_operand:SF 0 "register_operand" "=r")
+         (match_operand:SF 1 "mem_post_inc" "CV_mem_post"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.lw\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_loadhf_postinc_hardfloat"
+   [(set (match_operand:HF 0 "register_operand" "=r")
+         (match_operand:HF 1 "mem_post_inc" "CV_mem_post"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.lh\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "HF")])
+
+(define_insn "cv_loadhf_postinc_softfloat"
+   [(set (match_operand:HF 0 "register_operand" "=r")
+         (match_operand:HF 1 "mem_post_inc" "CV_mem_post"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.lh\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "HF")])
+
+(define_insn "cv_store<mode>_postinc"
+   [(set (match_operand:ANYI 0 "mem_post_inc" "=CV_mem_post")
+         (match_operand:ANYI 1 "register_operand" "r"))]
+  "TARGET_XCVMEM && riscv_legitimate_xcvmem_address_p (<MODE>mode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))"
+  "cv.<store>\t%1,%0"
+  [(set_attr "type" "store")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "cv_storesf_postinc_hardfloat"
+   [(set (match_operand:SF 0 "mem_post_inc" "=CV_mem_post")
+         (match_operand:SF 1 "register_operand" "r"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.sw\t%1,%0"
+  [(set_attr "type" "store")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_storesf_postinc_softfloat"
+   [(set (match_operand:SF 0 "mem_post_inc" "=CV_mem_post")
+         (match_operand:SF 1 "register_operand" "r"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.sw\t%1,%0"
+  [(set_attr "type" "store")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_storehf_postinc_hardfloat"
+   [(set (match_operand:HF 0 "mem_post_inc" "=CV_mem_post")
+         (match_operand:HF 1 "register_operand" "r"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.sh\t%1,%0"
+  [(set_attr "type" "store")
+   (set_attr "mode" "HF")])
+
+(define_insn "cv_storehf_postinc_softfloat"
+   [(set (match_operand:HF 0 "mem_post_inc" "=CV_mem_post")
+         (match_operand:HF 1 "register_operand" "r"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.sh\t%1,%0"
+  [(set_attr "type" "store")
+   (set_attr "mode" "HF")])
+
+;; Normal Register-Register Load/Store
+(define_insn "cv_load<mode>"
+   [(set (match_operand:ANYI 0 "register_operand" "=r")
+         (match_operand:ANYI 1 "mem_plus_reg" "CV_mem_plus"))]
+  "TARGET_XCVMEM && riscv_legitimate_xcvmem_address_p (<MODE>mode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))"
+  "cv.<load>\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "cv_load_<optab><SHORT:mode>"
+   [(set (match_operand:SI 0 "register_operand" "=r")
+     (any_extend:SI (match_operand:SHORT 1 "mem_plus_reg" "CV_mem_plus")))]
+  "TARGET_XCVMEM && riscv_legitimate_xcvmem_address_p (<MODE>mode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))"
+  "cv.<load><u>\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "cv_loadsf_hardfloat"
+   [(set (match_operand:SF 0 "register_operand" "=r")
+         (match_operand:SF 1 "mem_plus_reg" "CV_mem_plus"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.lw\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_loadsf_softfloat"
+   [(set (match_operand:SF 0 "register_operand" "=r")
+         (match_operand:SF 1 "mem_plus_reg" "CV_mem_plus"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.lw\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_loadhf_hardfloat"
+   [(set (match_operand:HF 0 "register_operand" "=r")
+         (match_operand:HF 1 "mem_plus_reg" "CV_mem_plus"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.lh\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "HF")])
+
+(define_insn "cv_loadhf_softfloat"
+   [(set (match_operand:HF 0 "register_operand" "=r")
+         (match_operand:HF 1 "mem_plus_reg" "CV_mem_plus"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[1], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.lh\t%0,%1"
+  [(set_attr "type" "load")
+   (set_attr "mode" "HF")])
+
+(define_insn "cv_store<mode>"
+   [(set (match_operand:ANYI 0 "mem_plus_reg" "=CV_mem_plus")
+     (match_operand:ANYI 1 "register_operand" "r"))]
+  "TARGET_XCVMEM && riscv_legitimate_xcvmem_address_p (<MODE>mode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))"
+  "cv.<store>\t%1,%0"
+  [(set_attr "type" "store")
+   (set_attr "mode" "<MODE>")])
+
+(define_insn "cv_storesf_hardfloat"
+  [(set (match_operand:SF 0 "mem_plus_reg"     "=CV_mem_plus")
+        (match_operand:SF 1 "register_operand" " r"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.sw\t%1,%0"
+  [(set_attr "move_type" "store")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_storesf_softfloat"
+  [(set (match_operand:SF 0 "mem_plus_reg"     "=CV_mem_plus")
+        (match_operand:SF 1 "register_operand" " r"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (SFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], SFmode)
+       || reg_or_0_operand (operands[1], SFmode))"
+  "cv.sw\t%1,%0"
+  [(set_attr "move_type" "store")
+   (set_attr "mode" "SF")])
+
+(define_insn "cv_storehf_hardfloat"
+  [(set (match_operand:HF 0 "mem_plus_reg"     "=CV_mem_plus")
+        (match_operand:HF 1 "register_operand" " r"))]
+  "TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.sh\t%1,%0"
+  [(set_attr "move_type" "store")
+   (set_attr "mode" "HF")])
+
+(define_insn "cv_storehf_softfloat"
+  [(set (match_operand:HF 0 "mem_plus_reg"     "=CV_mem_plus")
+        (match_operand:HF 1 "register_operand" " r"))]
+  "!TARGET_HARD_FLOAT && TARGET_XCVMEM
+   && riscv_legitimate_xcvmem_address_p (HFmode, XEXP (operands[0], 0), (lra_in_progress || reload_completed))
+   && (register_operand (operands[0], HFmode)
+       || reg_or_0_operand (operands[1], HFmode))"
+  "cv.sh\t%1,%0"
+  [(set_attr "move_type" "store")
+   (set_attr "mode" "HF")])
diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
index 69a6319c2c8..b7ccc6c2239 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -234,8 +234,22 @@ 
   return false;
 })
 
+(define_predicate "mem_post_inc"
+  (and (match_code "mem")
+       (match_test "TARGET_XCVMEM && GET_CODE (XEXP (op, 0)) == POST_MODIFY
+                    && GET_MODE_SIZE (GET_MODE (op)).to_constant () <= 4")))
+
+(define_predicate "mem_plus_reg"
+  (and (match_code "mem")
+       (match_test "TARGET_XCVMEM && GET_CODE (XEXP (op, 0)) == PLUS
+		    && GET_MODE_SIZE (GET_MODE (op)).to_constant () <= 4
+		    && REG_P (XEXP (XEXP (op, 0), 1))
+		    && REG_P (XEXP (XEXP (op, 0), 0))")))
+
 (define_predicate "move_operand"
-  (match_operand 0 "general_operand")
+  (and (match_operand 0 "general_operand")
+  (and (not (match_operand 0 "mem_post_inc"))
+       (not (match_operand 0 "mem_plus_reg"))))
 {
   enum riscv_symbol_type symbol_type;
 
@@ -404,6 +418,10 @@ 
   (and (match_code "const_int")
        (match_test "IN_RANGE (INTVAL (op), -16, 15)")))
 
+(define_predicate "nonimmediate_nonpostinc"
+  (and (match_operand 0 "nonimmediate_operand")
+	(not (match_operand 0 "mem_post_inc"))))
+
 ;; Predicates for the V extension.
 (define_special_predicate "vector_length_operand"
   (ior (match_operand 0 "pmode_register_operand")
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index d322b7e80ec..d7085d0338c 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -68,7 +68,8 @@  enum riscv_address_type {
   ADDRESS_REG_WB,
   ADDRESS_LO_SUM,
   ADDRESS_CONST_INT,
-  ADDRESS_SYMBOLIC
+  ADDRESS_SYMBOLIC,
+  ADDRESS_REG_INC
 };
 
 /* Information about an address described by riscv_address_type.
@@ -91,7 +92,13 @@  enum riscv_address_type {
        is the type of symbol it references.
 
    ADDRESS_SYMBOLIC
-       SYMBOL_TYPE is the type of symbol that the address references.  */
+       SYMBOL_TYPE is the type of symbol that the address references.
+
+   ADDRESS_REG_INC:
+       A base register + offset address access with post modify side-effect
+       (base register += offset).  The offset is an immediate or index
+       register.
+       */
 struct riscv_address_info {
   enum riscv_address_type type;
   rtx reg;
@@ -101,6 +108,7 @@  struct riscv_address_info {
 };
 
 /* Routines implemented in riscv.cc.  */
+bool riscv_legitimate_xcvmem_address_p (machine_mode, rtx, bool);
 extern enum riscv_symbol_type riscv_classify_symbolic_expression (rtx);
 extern bool riscv_symbolic_constant_p (rtx, enum riscv_symbol_type *);
 extern int riscv_float_const_rtx_index_for_fli (rtx);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 08ff05dcc3f..b701e1fb08f 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -1084,7 +1084,7 @@  riscv_regno_mode_ok_for_base_p (int regno,
 enum reg_class
 riscv_index_reg_class ()
 {
-  if (TARGET_XTHEADMEMIDX || TARGET_XTHEADFMEMIDX)
+  if (TARGET_XTHEADMEMIDX || TARGET_XTHEADFMEMIDX || TARGET_XCVMEM)
     return GR_REGS;
 
   return NO_REGS;
@@ -1360,6 +1360,16 @@  riscv_classify_address (struct riscv_address_info *info, rtx x,
       info->offset = const0_rtx;
       return riscv_valid_base_register_p (info->reg, mode, strict_p);
 
+    case POST_MODIFY:
+      /* For instructions using post inc, the offset can either be register
+       * or 12-bit immediate. */
+       info->type = ADDRESS_REG_INC;
+       info->reg = XEXP (x, 0);
+       info->offset = XEXP ((XEXP (x, 1)), 1);
+       return (riscv_valid_base_register_p (info->reg, mode, strict_p)
+       && (riscv_valid_base_register_p (info->offset, mode, strict_p)
+	       || riscv_valid_offset_p (info->offset, mode)));
+
     case PLUS:
       /* RVV load/store disallow any offset.  */
       if (riscv_v_ext_mode_p (mode))
@@ -1369,7 +1379,8 @@  riscv_classify_address (struct riscv_address_info *info, rtx x,
       info->reg = XEXP (x, 0);
       info->offset = XEXP (x, 1);
       return (riscv_valid_base_register_p (info->reg, mode, strict_p)
-	      && riscv_valid_offset_p (info->offset, mode));
+	      && (riscv_valid_offset_p (info->offset, mode)
+		  || (TARGET_XCVMEM && riscv_valid_base_register_p (info->offset, mode, strict_p))));
 
     case LO_SUM:
       /* RVV load/store disallow LO_SUM.  */
@@ -2485,6 +2496,30 @@  riscv_v_adjust_scalable_frame (rtx target, poly_int64 offset, bool epilogue)
   REG_NOTES (insn) = dwarf;
 }
 
+/* Check if post inc instructions are valid. If not, make the address
+ * vaild. */
+bool
+riscv_legitimate_xcvmem_address_p (machine_mode mode, rtx x, bool strict_p)
+{
+  struct riscv_address_info addr;
+
+  switch (GET_CODE (x))
+    {
+    case POST_MODIFY:
+      if (riscv_classify_address (&addr, x, mode, strict_p))
+	return addr.type == ADDRESS_REG_INC;
+      return false;
+
+    case PLUS:
+      if (REG_P (XEXP (x, 0)) && REG_P (XEXP (x, 1)) && riscv_classify_address (&addr, x, mode, strict_p))
+        return addr.type == ADDRESS_REG;
+      return false;
+
+    default:
+      return false;
+    }
+}
+
 /* If (set DEST SRC) is not a valid move instruction, emit an equivalent
    sequence that is valid.  */
 
@@ -5601,7 +5636,8 @@  riscv_print_operand_address (FILE *file, machine_mode mode ATTRIBUTE_UNUSED, rtx
     switch (addr.type)
       {
       case ADDRESS_REG:
-	output_addr_const (file, riscv_strip_unspec_address (addr.offset));
+	if (REG_P (addr.offset)) fprintf (file, "%s", reg_names[REGNO (addr.offset)]);
+	else output_addr_const (file, riscv_strip_unspec_address (addr.offset));
 	fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
 	return;
 
@@ -5619,6 +5655,12 @@  riscv_print_operand_address (FILE *file, machine_mode mode ATTRIBUTE_UNUSED, rtx
 	output_addr_const (file, riscv_strip_unspec_address (x));
 	return;
 
+      case ADDRESS_REG_INC:
+	fprintf (file, "(%s),", reg_names[REGNO (addr.reg)]);
+	if (REG_P (addr.offset)) fprintf (file, "%s", reg_names[REGNO (addr.offset)]);
+	else output_addr_const (file, addr.offset);
+	return;
+
       default:
 	gcc_unreachable ();
       }
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 1e9813b4f39..185e7d6ce23 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -44,6 +44,8 @@  along with GCC; see the file COPYING3.  If not see
 #define RISCV_TUNE_STRING_DEFAULT "rocket"
 #endif
 
+#define TARGET_MEM_CONSTRAINT 'w'
+
 extern const char *riscv_expand_arch (int argc, const char **argv);
 extern const char *riscv_expand_arch_from_cpu (int argc, const char **argv);
 extern const char *riscv_default_mtune (int argc, const char **argv);
@@ -1197,7 +1199,9 @@  extern void riscv_remove_unneeded_save_restore_calls (void);
    e.g. RVVMF64BI vs RVVMF1BI on zvl512b, which is [1, 1] vs [64, 64].  */
 #define MAX_POLY_VARIANT 64
 
-#define HAVE_POST_MODIFY_DISP TARGET_XTHEADMEMIDX
+#define HAVE_POST_MODIFY_DISP (TARGET_XTHEADMEMIDX || TARGET_XCVMEM)
 #define HAVE_PRE_MODIFY_DISP  TARGET_XTHEADMEMIDX
 
+#define HAVE_POST_MODIFY_REG TARGET_XCVMEM
+
 #endif /* ! GCC_RISCV_H */
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 168c8665a7a..5c9ea62d943 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -1663,13 +1663,13 @@ 
 
 (define_expand "zero_extendsidi2"
   [(set (match_operand:DI 0 "register_operand")
-	(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand")))]
+	(zero_extend:DI (match_operand:SI 1 "nonimmediate_nonpostinc")))]
   "TARGET_64BIT")
 
 (define_insn_and_split "*zero_extendsidi2_internal"
   [(set (match_operand:DI     0 "register_operand"     "=r,r")
 	(zero_extend:DI
-	    (match_operand:SI 1 "nonimmediate_operand" " r,m")))]
+	    (match_operand:SI 1 "nonimmediate_nonpostinc" " r,m")))]
   "TARGET_64BIT && !TARGET_ZBA && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX
    && !(register_operand (operands[1], SImode)
         && reg_or_subregno (operands[1]) == VL_REGNUM)"
@@ -1691,13 +1691,13 @@ 
 (define_expand "zero_extendhi<GPR:mode>2"
   [(set (match_operand:GPR    0 "register_operand")
 	(zero_extend:GPR
-	    (match_operand:HI 1 "nonimmediate_operand")))]
+	    (match_operand:HI 1 "nonimmediate_nonpostinc")))]
   "")
 
 (define_insn_and_split "*zero_extendhi<GPR:mode>2"
   [(set (match_operand:GPR    0 "register_operand"     "=r,r")
 	(zero_extend:GPR
-	    (match_operand:HI 1 "nonimmediate_operand" " r,m")))]
+	    (match_operand:HI 1 "nonimmediate_nonpostinc" " r,m")))]
   "!TARGET_ZBB && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
   "@
    #
@@ -1720,13 +1720,13 @@ 
 (define_expand "zero_extendqi<SUPERQI:mode>2"
   [(set (match_operand:SUPERQI    0 "register_operand")
 	(zero_extend:SUPERQI
-	    (match_operand:QI 1 "nonimmediate_operand")))]
+	    (match_operand:QI 1 "nonimmediate_nonpostinc")))]
   "")
 
 (define_insn "*zero_extendqi<SUPERQI:mode>2_internal"
   [(set (match_operand:SUPERQI 0 "register_operand"    "=r,r")
 	(zero_extend:SUPERQI
-	    (match_operand:QI 1 "nonimmediate_operand" " r,m")))]
+	    (match_operand:QI 1 "nonimmediate_nonpostinc" " r,m")))]
   "!TARGET_XTHEADMEMIDX"
   "@
    andi\t%0,%1,0xff
@@ -1745,13 +1745,13 @@ 
 (define_expand "extendsidi2"
   [(set (match_operand:DI     0 "register_operand"     "=r,r")
 	(sign_extend:DI
-	    (match_operand:SI 1 "nonimmediate_operand" " r,m")))]
+	    (match_operand:SI 1 "nonimmediate_nonpostinc" " r,m")))]
   "TARGET_64BIT")
 
 (define_insn "*extendsidi2_internal"
   [(set (match_operand:DI     0 "register_operand"     "=r,r")
 	(sign_extend:DI
-	    (match_operand:SI 1 "nonimmediate_operand" " r,m")))]
+	    (match_operand:SI 1 "nonimmediate_nonpostinc" " r,m")))]
   "TARGET_64BIT && !TARGET_XTHEADMEMIDX"
   "@
    sext.w\t%0,%1
@@ -1762,13 +1762,13 @@ 
 
 (define_expand "extend<SHORT:mode><SUPERQI:mode>2"
   [(set (match_operand:SUPERQI 0 "register_operand")
-	(sign_extend:SUPERQI (match_operand:SHORT 1 "nonimmediate_operand")))]
+	(sign_extend:SUPERQI (match_operand:SHORT 1 "nonimmediate_nonpostinc")))]
   "")
 
 (define_insn_and_split "*extend<SHORT:mode><SUPERQI:mode>2"
   [(set (match_operand:SUPERQI   0 "register_operand"     "=r,r")
 	(sign_extend:SUPERQI
-	    (match_operand:SHORT 1 "nonimmediate_operand" " r,m")))]
+	    (match_operand:SHORT 1 "nonimmediate_nonpostinc" " r,m")))]
   "!TARGET_ZBB && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
   "@
    #
@@ -1827,7 +1827,7 @@ 
 })
 
 (define_insn "*movhf_hardfloat"
-  [(set (match_operand:HF 0 "nonimmediate_operand" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
+  [(set (match_operand:HF 0 "nonimmediate_nonpostinc" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
 	(match_operand:HF 1 "move_operand"         " f,zfli,G,m,f,G,*r,*f,*G*r,*m,*r"))]
   "TARGET_ZFHMIN
    && (register_operand (operands[0], HFmode)
@@ -1838,7 +1838,7 @@ 
    (set_attr "mode" "HF")])
 
 (define_insn "*movhf_softfloat"
-  [(set (match_operand:HF 0 "nonimmediate_operand" "=f, r,r,m,*f,*r")
+  [(set (match_operand:HF 0 "nonimmediate_nonpostinc" "=f, r,r,m,*f,*r")
 	(match_operand:HF 1 "move_operand"         " f,Gr,m,r,*r,*f"))]
   "!TARGET_ZFHMIN
    && (register_operand (operands[0], HFmode)
@@ -2062,7 +2062,7 @@ 
 })
 
 (define_insn "*movdi_32bit"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,m,  *f,*f,*r,*f,*m,r")
+  [(set (match_operand:DI 0 "nonimmediate_nonpostinc" "=r,r,r,m,  *f,*f,*r,*f,*m,r")
 	(match_operand:DI 1 "move_operand"         " r,i,m,r,*J*r,*m,*f,*f,*f,vp"))]
   "!TARGET_64BIT
    && (register_operand (operands[0], DImode)
@@ -2074,7 +2074,7 @@ 
    (set_attr "ext" "base,base,base,base,d,d,d,d,d,vector")])
 
 (define_insn "*movdi_64bit"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r, m,  *f,*f,*r,*f,*m,r")
+  [(set (match_operand:DI 0 "nonimmediate_nonpostinc" "=r,r,r, m,  *f,*f,*r,*f,*m,r")
 	(match_operand:DI 1 "move_operand"         " r,T,m,rJ,*r*J,*m,*f,*f,*f,vp"))]
   "TARGET_64BIT
    && (register_operand (operands[0], DImode)
@@ -2097,7 +2097,7 @@ 
 })
 
 (define_insn "*movsi_internal"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r, m,  *f,*f,*r,*m,r")
+  [(set (match_operand:SI 0 "nonimmediate_nonpostinc" "=r,r,r, m,  *f,*f,*r,*m,r")
 	(match_operand:SI 1 "move_operand"         " r,T,m,rJ,*r*J,*m,*f,*f,vp"))]
   "(register_operand (operands[0], SImode)
     || reg_or_0_operand (operands[1], SImode))
@@ -2126,7 +2126,7 @@ 
 })
 
 (define_insn "*movhi_internal"
-  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r, m,  *f,*r,r")
+  [(set (match_operand:HI 0 "nonimmediate_nonpostinc" "=r,r,r, m,  *f,*r,r")
 	(match_operand:HI 1 "move_operand"	   " r,T,m,rJ,*r*J,*f,vp"))]
   "(register_operand (operands[0], HImode)
     || reg_or_0_operand (operands[1], HImode))"
@@ -2170,7 +2170,7 @@ 
 })
 
 (define_insn "*movqi_internal"
-  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,r, m,  *f,*r,r")
+  [(set (match_operand:QI 0 "nonimmediate_nonpostinc" "=r,r,r, m,  *f,*r,r")
 	(match_operand:QI 1 "move_operand"         " r,I,m,rJ,*r*J,*f,vp"))]
   "(register_operand (operands[0], QImode)
     || reg_or_0_operand (operands[1], QImode))"
@@ -2192,7 +2192,7 @@ 
 })
 
 (define_insn "*movsf_hardfloat"
-  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
+  [(set (match_operand:SF 0 "nonimmediate_nonpostinc" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
 	(match_operand:SF 1 "move_operand"         " f,zfli,G,m,f,G,*r,*f,*G*r,*m,*r"))]
   "TARGET_HARD_FLOAT
    && (register_operand (operands[0], SFmode)
@@ -2203,7 +2203,7 @@ 
    (set_attr "mode" "SF")])
 
 (define_insn "*movsf_softfloat"
-  [(set (match_operand:SF 0 "nonimmediate_operand" "= r,r,m")
+  [(set (match_operand:SF 0 "nonimmediate_nonpostinc" "= r,r,m")
 	(match_operand:SF 1 "move_operand"         " Gr,m,r"))]
   "!TARGET_HARD_FLOAT
    && (register_operand (operands[0], SFmode)
@@ -2228,7 +2228,7 @@ 
 ;; In RV32, we lack fmv.x.d and fmv.d.x.  Go through memory instead.
 ;; (However, we can still use fcvt.d.w to zero a floating-point register.)
 (define_insn "*movdf_hardfloat_rv32"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,   f,f,f,m,m,*zmvf,*zmvr,  *r,*r,*m")
+  [(set (match_operand:DF 0 "nonimmediate_nonpostinc" "=f,   f,f,f,m,m,*zmvf,*zmvr,  *r,*r,*m")
 	(match_operand:DF 1 "move_operand"         " f,zfli,G,m,f,G,*zmvr,*zmvf,*r*G,*m,*r"))]
   "!TARGET_64BIT && TARGET_DOUBLE_FLOAT
    && (register_operand (operands[0], DFmode)
@@ -2239,7 +2239,7 @@ 
    (set_attr "mode" "DF")])
 
 (define_insn "*movdf_hardfloat_rv64"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
+  [(set (match_operand:DF 0 "nonimmediate_nonpostinc" "=f,   f,f,f,m,m,*f,*r,  *r,*r,*m")
 	(match_operand:DF 1 "move_operand"         " f,zfli,G,m,f,G,*r,*f,*r*G,*m,*r"))]
   "TARGET_64BIT && TARGET_DOUBLE_FLOAT
    && (register_operand (operands[0], DFmode)
@@ -2250,7 +2250,7 @@ 
    (set_attr "mode" "DF")])
 
 (define_insn "*movdf_softfloat"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "= r,r, m")
+  [(set (match_operand:DF 0 "nonimmediate_nonpostinc" "= r,r, m")
 	(match_operand:DF 1 "move_operand"         " rG,m,rG"))]
   "!TARGET_DOUBLE_FLOAT
    && (register_operand (operands[0], DFmode)
@@ -2297,7 +2297,7 @@ 
    (set_attr "mode" "DF")])
 
 (define_split
-  [(set (match_operand:MOVE64 0 "nonimmediate_operand")
+  [(set (match_operand:MOVE64 0 "nonimmediate_nonpostinc")
 	(match_operand:MOVE64 1 "move_operand"))]
   "reload_completed
    && riscv_split_64bit_move_p (operands[0], operands[1])"
diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
index d06c0f8f416..5076d049d9f 100644
--- a/gcc/config/riscv/riscv.opt
+++ b/gcc/config/riscv/riscv.opt
@@ -415,6 +415,8 @@  Mask(XCVELW) Var(riscv_xcv_subext)
 
 Mask(XCVBI) Var(riscv_xcv_subext)
 
+Mask(XCVMEM) Var(riscv_xcv_subext)
+
 TargetVariable
 int riscv_xthead_subext
 
diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index 6fee1144238..cd3e58a2508 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -2487,6 +2487,9 @@  Test system has support for the CORE-V ELW extension.
 @item cv_bi
 Test system has support for the CORE-V BI extension.
 
+@item cv_mem
+Test system has support for the CORE-V MEM extension.
+
 @end table
 
 @subsubsection Other hardware attributes
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-1.c
new file mode 100644
index 00000000000..1850eaa8f92
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-1.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-immediate loads.
+ */
+
+int
+fooQIsigned (signed char* array_char, int n)
+{
+  int char_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    char_sum += array_char[i];
+  }
+
+  return char_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lb\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),1" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-2.c
new file mode 100644
index 00000000000..3d3ad6d43a1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-2.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register loads.
+ */
+
+int
+fooQIsigned (signed char* array_char, int n, int j)
+{
+  int char_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    char_sum += *array_char;
+    array_char+=j*sizeof(array_char);
+  }
+
+  return char_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lb\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-3.c
new file mode 100644
index 00000000000..358e34455d4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lb-compile-3.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register loads.
+ */
+
+int
+fooQIsigned (signed char* array_char, int i, int j)
+{
+  return array_char[i+j];
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lb\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-1.c
new file mode 100644
index 00000000000..1edc4402fa4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-1.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-immediate loads.
+ */
+
+int
+fooQIunsigned (unsigned char* array_uchar, int n)
+{
+  int uns_char_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    uns_char_sum += array_uchar[i];
+  }
+
+  return uns_char_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lbu\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),1" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-2.c
new file mode 100644
index 00000000000..182e7e6ae96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-2.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register loads.
+ */
+
+int
+fooQIunsigned (unsigned char* array_uchar, int n, int j)
+{
+  int uns_char_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    uns_char_sum += *array_uchar;
+    array_uchar+=j*sizeof(array_uchar);
+  }
+
+  return uns_char_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lbu\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-3.c
new file mode 100644
index 00000000000..00099c15b6a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lbu-compile-3.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register loads.
+ */
+
+int
+fooQIunsigned (unsigned char* array_uchar, int i, int j)
+{
+  return array_uchar[i+j];
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lbu\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-1.c
new file mode 100644
index 00000000000..a7e74e8978e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-1.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-immediate loads.
+ */
+
+int
+fooHIsigned (signed short int* array_short, int n)
+{
+  int short_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    short_sum += array_short[i];
+  }
+
+  return short_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lh\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),2" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-2.c
new file mode 100644
index 00000000000..4ec9672d6ad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-2.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register loads.
+ */
+
+int
+fooHIsigned (signed short int* array_short, int n, int j)
+{
+  int short_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    short_sum += *array_short;
+    array_short+=j*sizeof(array_short);
+  }
+
+  return short_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lh\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-3.c
new file mode 100644
index 00000000000..3a9b1a1deb8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lh-compile-3.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register loads.
+ */
+
+int
+fooHIsigned (signed short int* array_short, int i, int j)
+{
+  return array_short[i+j];
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lh\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-1.c
new file mode 100644
index 00000000000..19963fa706e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-1.c
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-immediate loads.
+ */
+
+int
+fooHIunsigned (unsigned short int* array_ushort, int n)
+{
+  int uns_short_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    uns_short_sum += array_ushort[i];
+  }
+
+  return uns_short_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lhu\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),2" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-2.c
new file mode 100644
index 00000000000..9669e575511
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-2.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register loads.
+ */
+
+int
+fooHIunsigned (unsigned short int* array_ushort, int n, int j)
+{
+  int uns_short_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    uns_short_sum += *array_ushort;
+    array_ushort+=j*sizeof(array_ushort);
+  }
+
+  return uns_short_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lhu\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-3.c
new file mode 100644
index 00000000000..baa2731aba2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lhu-compile-3.c
@@ -0,0 +1,16 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register loads.
+ */
+
+int
+fooHIunsigned (unsigned short int* array_ushort, int i, int j)
+{
+  return array_ushort[i+j];
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lhu\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-1.c
new file mode 100644
index 00000000000..3d61fc0f01c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-1.c
@@ -0,0 +1,38 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* We don't generate for some optimization levels. TODO: should support for Os,
+   Oz and Og */
+/* { dg-skip-if "" { *-*-* }  { "-O0" "-Os" "-Oz" "-Og" } { "" } } */
+
+/*
+ * Test for post-inc register-immediate loads.
+ */
+
+int
+fooSIsigned (signed int* array_int, int n)
+{
+  int int_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    int_sum += array_int[i];
+  }
+
+  return int_sum;
+}
+
+int
+fooSIunsigned (unsigned int* array_uint, int n)
+{
+  int uns_int_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    uns_int_sum += array_uint[i];
+  }
+
+  return uns_int_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lw\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),4" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-2.c
new file mode 100644
index 00000000000..9909981c373
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-2.c
@@ -0,0 +1,38 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register loads.
+ */
+
+int
+fooSIsigned (signed int* array_int, int n, int j)
+{
+  int int_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    int_sum += *array_int;
+    array_int+=j*sizeof(array_int);
+  }
+
+  return int_sum;
+}
+
+int
+fooSIunsigned (unsigned int* array_uint, int n, int j)
+{
+  int uns_int_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    uns_int_sum += *array_uint;
+    array_uint+=j*sizeof(array_uint);
+  }
+
+  return uns_int_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lw\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-3.c
new file mode 100644
index 00000000000..8864927de9c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-lw-compile-3.c
@@ -0,0 +1,22 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register loads.
+ */
+
+int
+fooSIsigned (signed int* array_int, int i, int j)
+{
+  return array_int[i+j];
+}
+
+int
+fooSIunsigned (unsigned int* array_uint, int i, int j)
+{
+  return array_uint[i+j];
+}
+
+/* { dg-final { scan-assembler-times "cv\\.lw\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-1.c
new file mode 100644
index 00000000000..05204b41573
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-1.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -g -O2" } */
+
+/*
+ * Test for illegal XCVmem specific operand.
+ * Pattern: `(mem:DF (plus:SI (reg reg)))`.
+ */
+
+struct {
+  double a[3];
+} * b;
+int c;
+
+int
+foo (void)
+{
+  b[0].a[c] -= b[0].a[c];
+}
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-2.c
new file mode 100644
index 00000000000..373915f73d6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-2.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -g -O2" } */
+
+/*
+ * Test for illegal XCVmem specific operand.
+ * Pattern: `(mem:DF (post_modify:SI (reg reg)))`.
+ */
+
+int bar (double);
+
+int
+foo (void)
+{
+  double *b;
+  int c = 0;
+  for (;; c++)
+    bar (b[c]);
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-3.c
new file mode 100644
index 00000000000..c622d60cd2a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-3.c
@@ -0,0 +1,28 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -g -O2 -Wno-int-conversion" } */
+
+/*
+ * Test for illegal XCVmem specific operand in loads.
+ * Instruction `lbu reg,reg(reg)`.
+ */
+
+int a;
+int bar (char);
+
+int
+foo (void)
+{
+  short *d;
+  char *e = (char *)foo;
+  for (;;) {
+    char c = d++;
+    bar (c);
+    short b = e[0] + b;
+    if (b)
+      a = 5;
+    e += 2;
+  }
+}
+
+/* { dg-final { scan-assembler-not "lbu\t\[a-z\]\[0-99\],\[a-z\]\[0-99\]\\(\[a-z\]\[0-99\]\\)" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-4.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-4.c
new file mode 100644
index 00000000000..4922f9667d5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-4.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32if_xcvmem" } */
+
+/*
+ * Test for illegal XCVmem specific operand in flw.
+ * Error: illegal operands `flw fa5,a5(a4)'
+ */
+
+int b, e;
+float *m;
+
+int
+foo (void) {
+  m[1] = m[b];
+  m[b] = e;
+
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "flw\tfa5,a5\\(a4\\)" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-5.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-5.c
new file mode 100644
index 00000000000..140486ce20b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-5.c
@@ -0,0 +1,25 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32" } */
+
+/*
+ * Tests that stores can generate regular reg:imm operands.
+ */
+
+int a[1];
+int b, c, d, e, f;
+float g;
+
+int
+foo (void)
+{
+  int h;
+  for (;; d++)
+    switch (c) {
+    case 8:
+      g = e == f;
+      a[h + d] = g;
+      g = e < f;
+      b = g;
+    }
+}
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-6.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-6.c
new file mode 100644
index 00000000000..8d6113971a6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-6.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+
+/*
+ * Test for illegal XCVmem specific operands in lw.
+ */
+
+int
+fooSIsigned (signed int* array_int)
+{
+  return array_int [3];
+}
+
+int
+fooSIunsigned (unsigned int* array_uint)
+{
+  return array_uint [3];
+}
+
+/* { dg-final { scan-assembler-not "cv\\.lw\t" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-7.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-7.c
new file mode 100644
index 00000000000..dc61d859988
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-7.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for illegal XCVmem specific operands in register-immediate sw.
+ */
+
+int
+fooSIsigned (signed int* array_int, int i)
+{
+  array_int [3] = i;
+  return array_int [3];
+}
+
+int
+fooSIunsigned (unsigned int* array_uint, int i)
+{
+  array_uint [3] = i;
+  return array_uint [3];
+}
+
+/* { dg-final { scan-assembler-not "cv\\.sw\t" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-8.c b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-8.c
new file mode 100644
index 00000000000..da7f5166fe7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-operand-compile-8.c
@@ -0,0 +1,18 @@ 
+/* { dg-do assemble } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem" } */
+
+/*
+ * Test for illegal XCVmem specific operand in sw.
+ */
+
+int a;
+float *b;
+
+int
+foo (void)
+{
+  int c;
+  for (c = 0; c < 10; c++)
+    b[c] = a;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-1.c
new file mode 100644
index 00000000000..596283f41e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-1.c
@@ -0,0 +1,36 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops -fno-tree-vectorize" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-immediate saves.
+ */
+
+int
+fooQIsigned (signed char* array_char, int n)
+{
+  int char_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    array_char[i] += char_sum;
+  }
+
+  return char_sum;
+}
+
+int
+fooQIunsigned (unsigned char* array_uchar, int n)
+{
+  int uns_char_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    array_uchar[i] += uns_char_sum;
+  }
+
+  return uns_char_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sb\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),1" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-2.c
new file mode 100644
index 00000000000..f907eca2fbf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-2.c
@@ -0,0 +1,38 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops -fno-tree-vectorize" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register saves.
+ */
+
+int
+fooQIsigned (signed char* array_char, int n, int j)
+{
+  int char_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    *array_char += char_sum;
+    array_char+=j*sizeof(array_char);
+  }
+
+  return char_sum;
+}
+
+int
+fooQIunsigned (unsigned char* array_uchar, int n, int j)
+{
+  int uns_char_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    *array_uchar += uns_char_sum;
+    array_uchar+=j*sizeof(array_uchar);
+  }
+
+  return uns_char_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sb\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-3.c
new file mode 100644
index 00000000000..822573d13af
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sb-compile-3.c
@@ -0,0 +1,30 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops -fno-tree-vectorize" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register saves.
+ */
+
+int
+fooQIsigned (signed char* array_char, int i, int j)
+{
+  int char_sum = 1;
+
+  array_char[i+j] += char_sum;
+
+  return char_sum;
+}
+
+int
+fooQIunsigned (unsigned char* array_uchar, int i, int j)
+{
+  int uns_char_sum = 1;
+
+  array_uchar[i+j] += uns_char_sum;
+
+  return uns_char_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sb\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-1.c
new file mode 100644
index 00000000000..036836cc419
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-1.c
@@ -0,0 +1,36 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-immediate saves.
+ */
+
+int
+fooHIsigned (signed short int* array_short, int n)
+{
+  int short_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    array_short[i] += short_sum;
+  }
+
+  return short_sum;
+}
+
+int
+fooHIunsigned (unsigned short int* array_ushort, int n)
+{
+  int uns_short_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    array_ushort[i] += uns_short_sum;
+  }
+
+  return uns_short_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sh\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),2" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-2.c
new file mode 100644
index 00000000000..c761137395f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-2.c
@@ -0,0 +1,38 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register saves.
+ */
+
+int
+fooHIsigned (signed short int* array_short, int n, int j)
+{
+  int short_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    *array_short += short_sum;
+    array_short+=j*sizeof(array_short);
+  }
+
+  return short_sum;
+}
+
+int
+fooHIunsigned (unsigned short int* array_ushort, int n, int j)
+{
+  int uns_short_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    *array_ushort += uns_short_sum;
+    array_ushort+=j*sizeof(array_ushort);
+  }
+
+  return uns_short_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sh\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-3.c
new file mode 100644
index 00000000000..3d8e07f9093
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sh-compile-3.c
@@ -0,0 +1,30 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register saves.
+ */
+
+int
+fooHIsigned (signed short int* array_short, int i, int j)
+{
+  int short_sum = 1;
+
+  array_short[i+j] = short_sum;
+
+  return short_sum;
+}
+
+int
+fooHIunsigned (unsigned short int* array_ushort, int i, int j)
+{
+  int uns_short_sum = 1;
+
+  array_ushort[i+j] += uns_short_sum;
+
+  return uns_short_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sh\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-1.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-1.c
new file mode 100644
index 00000000000..230022761cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-1.c
@@ -0,0 +1,36 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-immediate saves.
+ */
+
+int
+fooSIsigned (signed int* array_int, int n)
+{
+  int int_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    array_int[i] += int_sum;
+  }
+
+  return int_sum;
+}
+
+int
+fooSIunsigned (unsigned int* array_uint, int n)
+{
+  int uns_int_sum = 1;
+
+  for(int i=0; i<n; i++)
+  {
+    array_uint[i] += uns_int_sum;
+  }
+
+  return uns_int_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sw\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),4" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-2.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-2.c
new file mode 100644
index 00000000000..fc5fcba22ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-2.c
@@ -0,0 +1,38 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Oz" "-Og" } } */
+
+/*
+ * Test for post-inc register-register saves.
+ */
+
+int
+fooSIsigned (signed int* array_int, int n, int j)
+{
+  int int_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    *array_int += int_sum;
+    array_int+=j*sizeof(array_int);
+  }
+
+  return int_sum;
+}
+
+int
+fooSIunsigned (unsigned int* array_uint, int n, int j)
+{
+  int uns_int_sum = 1;
+
+  for(int i=0; i<n; i+=j)
+  {
+    *array_uint += uns_int_sum;
+    array_uint+=j*sizeof(array_uint);
+  }
+
+  return uns_int_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sw\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\),\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-3.c b/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-3.c
new file mode 100644
index 00000000000..c709f2e5fc5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/cv-mem-sw-compile-3.c
@@ -0,0 +1,30 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target cv_mem } */
+/* { dg-options "-march=rv32i_xcvmem -mabi=ilp32 -fno-unroll-loops" } */
+/* { dg-skip-if "" { *-*-* }  { "-O0" } { "" } } */
+
+/*
+ * Test for normal register-register saves.
+ */
+
+int
+fooSIsigned (signed int* array_int, int i, int j)
+{
+  int int_sum = 1;
+
+  array_int[i+j] = int_sum;
+
+  return int_sum;
+}
+
+int
+fooSIunsigned (unsigned int* array_uint, int i, int j)
+{
+  int uns_int_sum = 1;
+
+  array_uint[i+j] = uns_int_sum;
+
+  return uns_int_sum;
+}
+
+/* { dg-final { scan-assembler-times "cv\\.sw\t\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\),(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\(\(\?\:t\[0-6\]\|a\[0-7\]\|s\[1-11\]\)\\)" 2 } } */
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 0eae746e848..949fd413ff5 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -13111,6 +13111,20 @@  proc check_effective_target_cv_bi { } {
     } "-march=rv32i_xcvbi" ]
 }
 
+# Return 1 if the CORE-V MEM extension is available.
+proc check_effective_target_cv_mem { } {
+    if { !([istarget riscv*-*-*]) } {
+         return 0
+     }
+    return [check_no_compiler_messages cv_mem object {
+        void foo (void)
+        {
+          asm ("cv.lw t0, (t1), 4");
+        }
+    } "-march=rv32i_xcvmem" ]
+}
+
+
 proc check_effective_target_loongarch_sx { } {
     return [check_no_compiler_messages loongarch_lsx assembly {
        #if !defined(__loongarch_sx)