diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc
index 8e1449d3543..b541c55976b 100644
--- a/gcc/common/config/riscv/riscv-common.cc
+++ b/gcc/common/config/riscv/riscv-common.cc
@@ -230,6 +230,7 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
   {"xtheadfmemidx", ISA_SPEC_CLASS_NONE, 1, 0},
   {"xtheadmac", ISA_SPEC_CLASS_NONE, 1, 0},
   {"xtheadmemidx", ISA_SPEC_CLASS_NONE, 1, 0},
+  {"xtheadmempair", ISA_SPEC_CLASS_NONE, 1, 0},
   {"xtheadsync", ISA_SPEC_CLASS_NONE, 1, 0},
 
   /* Terminate the list.  */
@@ -1265,6 +1266,7 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
   {"xtheadfmemidx", &gcc_options::x_riscv_xthead_subext, MASK_XTHEADFMEMIDX},
   {"xtheadmac",     &gcc_options::x_riscv_xthead_subext, MASK_XTHEADMAC},
   {"xtheadmemidx",  &gcc_options::x_riscv_xthead_subext, MASK_XTHEADMEMIDX},
+  {"xtheadmempair", &gcc_options::x_riscv_xthead_subext, MASK_XTHEADMEMPAIR},
   {"xtheadsync",    &gcc_options::x_riscv_xthead_subext, MASK_XTHEADSYNC},
 
   {NULL, NULL, 0}
diff --git a/gcc/config/riscv/peephole.md b/gcc/config/riscv/peephole.md
index d9477f46338..2e1b6fff314 100644
--- a/gcc/config/riscv/peephole.md
+++ b/gcc/config/riscv/peephole.md
@@ -38,3 +38,339 @@ (define_peephole2
 {
   operands[5] = GEN_INT (INTVAL (operands[2]) - INTVAL (operands[5]));
 })
+
+;;
+;;  ....................
+;;
+;;	T-HEAD EXTENSION MEMPAIR - 4 instr LOADS
+;;
+;;  ....................
+
+;; LOAD
+;; T-HEAD: Four DI loads, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 8 "r")
+   (set (match_operand:DI 0 "register_operand" "")
+	(match_operand:DI 1 "memory_operand" ""))
+   (set (match_operand:DI 2 "register_operand" "")
+	(match_operand:DI 3 "memory_operand" ""))
+   (set (match_operand:DI 4 "register_operand" "")
+	(match_operand:DI 5 "memory_operand" ""))
+   (set (match_operand:DI 6 "register_operand" "")
+	(match_operand:DI 7 "memory_operand" ""))
+   (match_dup 8)]
+  "TARGET_XTHEADMEMPAIR && TARGET_64BIT
+  && riscv_load_store_bonding_p_4instr (operands, DImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, DImode,
+    SIGN_EXTEND, true))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: Four SI unsigned loads, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 8 "r")
+   (set (match_operand:DI 0 "register_operand" "")
+	(zero_extend:DI (match_operand:SI 1 "memory_operand" "")))
+   (set (match_operand:DI 2 "register_operand" "")
+	(zero_extend:DI (match_operand:SI 3 "memory_operand" "")))
+   (set (match_operand:DI 4 "register_operand" "")
+	(zero_extend:DI (match_operand:SI 5 "memory_operand" "")))
+   (set (match_operand:DI 6 "register_operand" "")
+	(zero_extend:DI (match_operand:SI 7 "memory_operand" "")))
+   (match_dup 8)]
+  "TARGET_XTHEADMEMPAIR
+  && riscv_load_store_bonding_p_4instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    ZERO_EXTEND, true))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: Four SI signed loads, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 8 "r")
+   (set (match_operand:DI 0 "register_operand" "")
+	(sign_extend:DI (match_operand:SI 1 "memory_operand" "")))
+   (set (match_operand:DI 2 "register_operand" "")
+	(sign_extend:DI (match_operand:SI 3 "memory_operand" "")))
+   (set (match_operand:DI 4 "register_operand" "")
+	(sign_extend:DI (match_operand:SI 5 "memory_operand" "")))
+   (set (match_operand:DI 6 "register_operand" "")
+	(sign_extend:DI (match_operand:SI 7 "memory_operand" "")))
+   (match_dup 8)]
+  "TARGET_XTHEADMEMPAIR
+  && riscv_load_store_bonding_p_4instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    SIGN_EXTEND, true))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: Four SI loads, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 8 "r")
+   (set (match_operand:SI 0 "register_operand" "")
+	(match_operand:SI 1 "memory_operand" ""))
+   (set (match_operand:SI 2 "register_operand" "")
+	(match_operand:SI 3 "memory_operand" ""))
+   (set (match_operand:SI 4 "register_operand" "")
+	(match_operand:SI 5 "memory_operand" ""))
+   (set (match_operand:SI 6 "register_operand" "")
+	(match_operand:SI 7 "memory_operand" ""))
+   (match_dup 8)]
+  "TARGET_XTHEADMEMPAIR
+  && riscv_load_store_bonding_p_4instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    SIGN_EXTEND, true))
+    DONE;
+  else
+    FAIL;
+})
+
+;;
+;;  ....................
+;;
+;;	T-HEAD EXTENSION MEMPAIR - 4 instr STORES
+;;
+;;  ....................
+
+;; STORE
+;; T-HEAD: Four DI stores, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 8 "r")
+   (set (match_operand:DI 0 "memory_operand" "")
+	(match_operand:DI 1 "register_operand" ""))
+   (set (match_operand:DI 2 "memory_operand" "")
+	(match_operand:DI 3 "register_operand" ""))
+   (set (match_operand:DI 4 "memory_operand" "")
+	(match_operand:DI 5 "register_operand" ""))
+   (set (match_operand:DI 6 "memory_operand" "")
+	(match_operand:DI 7 "register_operand" ""))
+   (match_dup 8)]
+  "TARGET_XTHEADMEMPAIR && TARGET_64BIT
+  && riscv_load_store_bonding_p_4instr (operands, DImode, false)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, false, DImode,
+    SIGN_EXTEND, true))
+    DONE;
+  else
+    FAIL;
+})
+
+;; STORE
+;; T-HEAD: Four SI stores, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 8 "r")
+   (set (match_operand:SI 0 "memory_operand" "")
+	(match_operand:SI 1 "register_operand" ""))
+   (set (match_operand:SI 2 "memory_operand" "")
+	(match_operand:SI 3 "register_operand" ""))
+   (set (match_operand:SI 4 "memory_operand" "")
+	(match_operand:SI 5 "register_operand" ""))
+   (set (match_operand:SI 6 "memory_operand" "")
+	(match_operand:SI 7 "register_operand" ""))
+   (match_dup 8)]
+  "TARGET_XTHEADMEMPAIR && !TARGET_64BIT
+  && riscv_load_store_bonding_p_4instr (operands, SImode, false)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, false, SImode,
+    SIGN_EXTEND, true))
+    DONE;
+  else
+    FAIL;
+})
+
+;;
+;;  ....................
+;;
+;;	T-HEAD EXTENSION MEMPAIR - 2 instr LOADS
+;;
+;;  ....................
+
+;; LOAD
+;; T-HEAD: A pair of two DI loads, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:DI 0 "register_operand" "")
+	(match_operand:DI 1 "memory_operand" ""))
+   (set (match_operand:DI 2 "register_operand" "")
+	(match_operand:DI 3 "memory_operand" ""))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR && TARGET_64BIT
+  && riscv_load_store_bonding_p_2instr (operands, DImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, DImode,
+    SIGN_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: A pair of two DI extend unsigned SI loads,
+;; with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:DI 0 "register_operand" "")
+	(zero_extend:DI (match_operand:SI 1 "memory_operand" "")))
+   (set (match_operand:DI 2 "register_operand" "")
+	(zero_extend:DI (match_operand:SI 3 "memory_operand" "")))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR
+  && riscv_load_store_bonding_p_2instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    ZERO_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: A pair of two DI extend signed SI loads,
+;; with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:DI 0 "register_operand" "")
+	(sign_extend:DI (match_operand:SI 1 "memory_operand" "")))
+   (set (match_operand:DI 2 "register_operand" "")
+	(sign_extend:DI (match_operand:SI 3 "memory_operand" "")))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR
+  && riscv_load_store_bonding_p_2instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    SIGN_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: A pair of two SI extend unsigned SI loads,
+;; with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:SI 0 "register_operand" "")
+	(zero_extend:SI (match_operand:SI 1 "memory_operand" "")))
+   (set (match_operand:SI 2 "register_operand" "")
+	(zero_extend:SI (match_operand:SI 3 "memory_operand" "")))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR && !TARGET_64BIT
+  && riscv_load_store_bonding_p_2instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    ZERO_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: A pair of two SI extend signed SI loads,
+;; with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:SI 0 "register_operand" "")
+	(sign_extend:SI (match_operand:SI 1 "memory_operand" "")))
+   (set (match_operand:SI 2 "register_operand" "")
+	(sign_extend:SI (match_operand:SI 3 "memory_operand" "")))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR && !TARGET_64BIT
+  && riscv_load_store_bonding_p_2instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    SIGN_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
+
+;; LOAD
+;; T-HEAD: A pair of two SI loads, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:SI 0 "register_operand" "")
+	(match_operand:SI 1 "memory_operand" ""))
+   (set (match_operand:SI 2 "register_operand" "")
+	(match_operand:SI 3 "memory_operand" ""))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR && !TARGET_64BIT
+  && riscv_load_store_bonding_p_2instr (operands, SImode, true)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, true, SImode,
+    SIGN_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
+
+;;
+;;  ....................
+;;
+;;	T-HEAD EXTENSION MEMPAIR - 2 instr STORES
+;;
+;;  ....................
+
+;; STORE
+;; T-HEAD: A pair of two DI stores, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:DI 0 "memory_operand" "")
+	(match_operand:DI 1 "register_operand" ""))
+   (set (match_operand:DI 2 "memory_operand" "")
+	(match_operand:DI 3 "register_operand" ""))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR && TARGET_64BIT
+  && riscv_load_store_bonding_p_2instr (operands, DImode, false)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, false, DImode,
+   SIGN_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
+
+;; STORE
+;; T-HEAD: A pair of two SI stores, with non-adjusted offset
+(define_peephole2
+  [(match_scratch:P 4 "r")
+   (set (match_operand:SI 0 "memory_operand" "")
+	(match_operand:SI 1 "register_operand" ""))
+   (set (match_operand:SI 2 "memory_operand" "")
+	(match_operand:SI 3 "register_operand" ""))
+   (match_dup 4)]
+  "TARGET_XTHEADMEMPAIR
+  && riscv_load_store_bonding_p_2instr (operands, SImode, false)"
+  [(const_int 0)]
+{
+  if (th_riscv_gen_adjusted_mempair (operands, false, SImode,
+   SIGN_EXTEND, false))
+    DONE;
+  else
+    FAIL;
+})
diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h
index 18daac40dbd..626de51d644 100644
--- a/gcc/config/riscv/riscv-opts.h
+++ b/gcc/config/riscv/riscv-opts.h
@@ -205,7 +205,9 @@ enum stack_protector_guard {
 #define TARGET_XTHEADMAC	((riscv_xthead_subext & MASK_XTHEADMAC) != 0)
 #define MASK_XTHEADMEMIDX	(1 << 7)
 #define TARGET_XTHEADMEMIDX	((riscv_xthead_subext & MASK_XTHEADMEMIDX) != 0)
-#define MASK_XTHEADSYNC		(1 << 8)
+#define MASK_XTHEADMEMPAIR	 (1 << 8)
+#define TARGET_XTHEADMEMPAIR ((riscv_xthead_subext & MASK_XTHEADMEMPAIR) != 0)
+#define MASK_XTHEADSYNC		(1 << 9)
 #define TARGET_XTHEADSYNC	((riscv_xthead_subext & MASK_XTHEADSYNC) != 0)
 
 #endif /* ! GCC_RISCV_OPTS_H */
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 5a718bb62b4..9161bef50a6 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -54,7 +54,16 @@ extern bool riscv_split_64bit_move_p (rtx, rtx);
 extern void riscv_split_doubleword_move (rtx, rtx);
 extern const char *riscv_output_move (rtx, rtx);
 extern const char *riscv_output_return ();
+extern bool riscv_load_store_bonding_p_2instr (rtx*, machine_mode, bool);
+extern bool riscv_load_store_bonding_p_4instr (rtx*, machine_mode, bool);
+extern int riscv_ldrstr_offset_compare (const void *, const void *);
+extern void riscv_swap_ldrstr_operands (rtx*, bool);
+extern bool extract_base_offset_in_addr (rtx mem, rtx *base, rtx *offset);
 #ifdef RTX_CODE
+extern const char *th_riscv_output_mempair_move (rtx*, machine_mode,
+  enum rtx_code);
+extern bool th_riscv_gen_adjusted_mempair (rtx*, bool, machine_mode,
+  enum rtx_code, bool);
 extern void riscv_expand_int_scc (rtx, enum rtx_code, rtx, rtx);
 extern void riscv_expand_float_scc (rtx, enum rtx_code, rtx, rtx);
 extern void riscv_expand_conditional_branch (rtx, enum rtx_code, rtx, rtx);
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 5a0a2b0c2db..8cd24c29fc1 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "output.h"
 #include "alias.h"
 #include "tree.h"
+#include "fold-const.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "varasm.h"
@@ -55,6 +56,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "predict.h"
 #include "tree-pass.h"
+#include "tree-dfa.h"
 #include "opts.h"
 #include "tm-constrs.h"
 #include "rtl-iter.h"
@@ -2835,6 +2837,705 @@ riscv_output_move (rtx dest, rtx src)
   gcc_unreachable ();
 }
 
+/* If MEM is in the form of "base+offset", extract the two parts
+   of address and set to BASE and OFFSET, otherwise return false
+   after clearing BASE and OFFSET.  */
+
+bool
+extract_base_offset_in_addr (rtx mem, rtx *base, rtx *offset)
+{
+  rtx addr;
+
+  gcc_assert (MEM_P (mem));
+
+  addr = XEXP (mem, 0);
+
+  if (REG_P (addr))
+    {
+      *base = addr;
+      *offset = const0_rtx;
+      return true;
+    }
+
+  if (GET_CODE (addr) == PLUS
+      && REG_P (XEXP (addr, 0)) && CONST_INT_P (XEXP (addr, 1)))
+    {
+      *base = XEXP (addr, 0);
+      *offset = XEXP (addr, 1);
+      return true;
+    }
+
+  *base = NULL_RTX;
+  *offset = NULL_RTX;
+
+  return false;
+}
+
+#ifdef RTX_CODE
+/* Return the appropriate instructions to move SRC into DEST.  */
+
+/* If REVERSED is null, return true if memory reference *MEM2 comes
+   immediately after memory reference *MEM1.  Do not change the references
+   in this case.
+
+   Otherwise, check if *MEM1 and *MEM2 are consecutive memory references and,
+   if they are, try to make them use constant offsets from the same base
+   register.  Return true on success.  When returning true, set *REVERSED
+   to true if *MEM1 comes after *MEM2, false if *MEM1 comes before *MEM2.  */
+static bool
+riscv_check_consecutive_mems (rtx *mem1, rtx *mem2, bool *reversed)
+{
+  if (reversed)
+    *reversed = false;
+
+  if (GET_RTX_CLASS (GET_CODE (XEXP (*mem1, 0))) == RTX_AUTOINC
+      || GET_RTX_CLASS (GET_CODE (XEXP (*mem2, 0))) == RTX_AUTOINC)
+    return false;
+
+  if (!MEM_SIZE_KNOWN_P (*mem1) || !MEM_SIZE_KNOWN_P (*mem2))
+    return false;
+
+  auto size1 = MEM_SIZE (*mem1);
+  auto size2 = MEM_SIZE (*mem2);
+
+  rtx base1, base2, offset1, offset2;
+  extract_base_offset_in_addr (*mem1, &base1, &offset1);
+  extract_base_offset_in_addr (*mem2, &base2, &offset2);
+
+  /* Make sure at least one memory is in base+offset form.  */
+  if (!(base1 && offset1) && !(base2 && offset2))
+    return false;
+
+  /* If both mems already use the same base register, just check the
+     offsets.  */
+  if (base1 && base2 && rtx_equal_p (base1, base2))
+    {
+      if (!offset1 || !offset2)
+	return false;
+
+      if (known_eq (UINTVAL (offset1) + size1, UINTVAL (offset2)))
+	return true;
+
+      if (known_eq (UINTVAL (offset2) + size2, UINTVAL (offset1)) && reversed)
+	{
+	  *reversed = true;
+	  return true;
+	}
+
+      return false;
+    }
+
+  /* Otherwise, check whether the MEM_EXPRs and MEM_OFFSETs together
+     guarantee that the values are consecutive.  */
+  if (MEM_EXPR (*mem1)
+      && MEM_EXPR (*mem2)
+      && MEM_OFFSET_KNOWN_P (*mem1)
+      && MEM_OFFSET_KNOWN_P (*mem2))
+    {
+      poly_int64 expr_offset1;
+      poly_int64 expr_offset2;
+      tree expr_base1 = get_addr_base_and_unit_offset (MEM_EXPR (*mem1),
+						       &expr_offset1);
+      tree expr_base2 = get_addr_base_and_unit_offset (MEM_EXPR (*mem2),
+						       &expr_offset2);
+      if (!expr_base1
+	  || !expr_base2
+	  || !DECL_P (expr_base1)
+	  || !operand_equal_p (expr_base1, expr_base2, OEP_ADDRESS_OF))
+	return false;
+
+      expr_offset1 += MEM_OFFSET (*mem1);
+      expr_offset2 += MEM_OFFSET (*mem2);
+
+      if (known_eq (expr_offset1 + size1, expr_offset2))
+	;
+      else if (known_eq (expr_offset2 + size2, expr_offset1) && reversed)
+	*reversed = true;
+      else
+	return false;
+
+      if (reversed)
+	{
+	  if (base2)
+	    {
+	      rtx addr1 = plus_constant (Pmode, XEXP (*mem2, 0),
+					 expr_offset1 - expr_offset2);
+	      *mem1 = replace_equiv_address_nv (*mem1, addr1);
+	    }
+	  else
+	    {
+	      rtx addr2 = plus_constant (Pmode, XEXP (*mem1, 0),
+					 expr_offset2 - expr_offset1);
+	      *mem2 = replace_equiv_address_nv (*mem2, addr2);
+	    }
+	}
+      return true;
+    }
+
+  return false;
+}
+
+/* Given OPERANDS of consecutive load/store that can be merged,
+   swap them if they are not in ascending order.  */
+void
+riscv_swap_ldrstr_operands (rtx* operands, bool load)
+{
+  int mem_op = load ? 1 : 0;
+  bool reversed = false;
+  if (!riscv_check_consecutive_mems (operands + mem_op,
+				       operands + mem_op + 2, &reversed))
+    gcc_unreachable ();
+
+  if (reversed)
+    {
+      /* Irrespective of whether this is a load or a store,
+	 we do the same swap.  */
+      std::swap (operands[0], operands[2]);
+      std::swap (operands[1], operands[3]);
+    }
+}
+
+/* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
+   and *OFFSET_PTR.  Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise.  */
+
+static void
+riscv_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
+{
+  if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
+    {
+      *base_ptr = XEXP (x, 0);
+      *offset_ptr = INTVAL (XEXP (x, 1));
+    }
+  else
+    {
+      *base_ptr = x;
+      *offset_ptr = 0;
+    }
+}
+
+const char *
+th_riscv_output_mempair_move (rtx *operands, machine_mode mode,
+	enum rtx_code code)
+{
+  unsigned width;
+  rtx reg1, reg2, mem1, mem2, base1, base2;
+  HOST_WIDE_INT offset1, offset2;
+  rtx output_operands[5];
+
+  width = GET_MODE_SIZE (mode).to_constant ();
+
+  // LOAD
+  if (which_alternative == 0)
+    {
+      reg1 = copy_rtx (operands[0]);
+      reg2 = copy_rtx (operands[2]);
+      mem1 = copy_rtx (operands[1]);
+      mem2 = copy_rtx (operands[3]);
+    }
+  // STORE
+  else if (which_alternative == 1)
+    {
+      reg1 = copy_rtx (operands[1]);
+      reg2 = copy_rtx (operands[3]);
+      mem1 = copy_rtx (operands[0]);
+      mem2 = copy_rtx (operands[2]);
+    }
+
+  riscv_split_plus (XEXP (mem1, 0), &base1, &offset1);
+  riscv_split_plus (XEXP (mem2, 0), &base2, &offset2);
+
+  // LOAD
+  if (which_alternative == 0)
+	{
+    switch (width)
+		{
+      case 4:
+      {
+	gcc_assert (!(offset1 % 8));
+
+	output_operands[0] = copy_rtx (reg1);
+	output_operands[1] = copy_rtx (reg2);
+	output_operands[2] = copy_rtx (base1);
+	output_operands[3] = gen_rtx_CONST_INT (mode, (offset1 >> 3));
+	output_operands[4] = gen_rtx_CONST_INT (mode, 3);
+
+	if (code == ZERO_EXTEND)
+		output_asm_insn ("th.lwud %0, %1, (%2), %3, %4",
+			output_operands);
+	else if (code == SIGN_EXTEND)
+		output_asm_insn ("th.lwd %0, %1, (%2), %3, %4",
+			output_operands);
+	else
+		abort ();
+	break;
+      }
+      case 8:
+      {
+	gcc_assert (!(offset1 % 16));
+
+	output_operands[0] = copy_rtx (reg1);
+	output_operands[1] = copy_rtx (reg2);
+	output_operands[2] = copy_rtx (base1);
+	output_operands[3] = gen_rtx_CONST_INT (mode, (offset1 >> 4));
+	output_operands[4] = gen_rtx_CONST_INT (mode, 4);
+
+	output_asm_insn ("th.ldd %0, %1, (%2), %3, %4", output_operands);
+	break;
+      }
+      default:
+	abort ();
+		}
+  }
+  // STORE
+  else if (which_alternative == 1)
+	{
+    switch (width)
+		{
+      case 4:
+      {
+	gcc_assert (!(offset1 % 8));
+
+	output_operands[0] = copy_rtx (reg1);
+	output_operands[1] = copy_rtx (reg2);
+	output_operands[2] = copy_rtx (base1);
+	output_operands[3] = gen_rtx_CONST_INT (mode, (offset1 >> 3));
+	output_operands[4] = gen_rtx_CONST_INT (mode, 3);
+
+	output_asm_insn ("th.swd %z0, %z1, (%2), %3, %4", output_operands);
+	break;
+      }
+      case 8:
+      {
+	gcc_assert (!(offset1 % 16));
+
+	output_operands[0] = copy_rtx (reg1);
+	output_operands[1] = copy_rtx (reg2);
+	output_operands[2] = copy_rtx (base1);
+	output_operands[3] = gen_rtx_CONST_INT (mode, (offset1 >> 4));
+	output_operands[4] = gen_rtx_CONST_INT (mode, 4);
+
+	output_asm_insn ("th.sdd %z0, %z1, (%2), %3, %4", output_operands);
+	break;
+      }
+      default:
+	abort ();
+		}
+  }
+  // UNKNOWN
+  else
+	{
+    abort ();
+  }
+  return "";
+}
+#endif
+
+/* Given OPERANDS of consecutive load/store, check if we can merge
+   them into load-pair or store-pair instructions by adjusting the
+	 offset.  LOAD is true if they are load instructions.
+	 MODE is the mode of memory operands.
+
+   Given below consecutive stores:
+
+     sd  a2, 0x100 (a1)
+     sd  a3, 0x108 (a1)
+     sd  a4, 0x110 (a1)
+     sd  a5, 0x118 (a1)
+
+   Though the offsets are out of the range supported by stp, we can
+   still pair them after adjusting the offset, like:
+
+     addi t0, a1, 0x100
+     th.sdd  a2, a3, 0 (t0), 0, 4
+     th.sdd  a4, a5, 16 (t0), 0, 4
+
+   The peephole patterns detecting this opportunity should guarantee
+   the scratch register is avaliable.
+
+   The function works for 4 consecutive load/store pairs.  */
+bool
+riscv_load_store_bonding_p_4instr (rtx *operands, machine_mode mode,
+bool load_p)
+{
+  HOST_WIDE_INT msize;
+  msize = GET_MODE_SIZE (mode).to_constant ();
+
+  constexpr int NUM_INSTR = 4;
+
+  rtx reg[NUM_INSTR], mem[NUM_INSTR], base[NUM_INSTR];
+	rtx	temp_operands[2*NUM_INSTR];
+
+  enum reg_class rc[NUM_INSTR];
+  HOST_WIDE_INT offset[NUM_INSTR];
+
+  /* We make changes on a copy as we may still bail out.  */
+  for (int i = 0; i < (2*NUM_INSTR); i++)
+    temp_operands[i] = copy_rtx (operands[i]);
+
+  /* Sort the operands.  */
+  gcc_stablesort (temp_operands, NUM_INSTR, 2 * sizeof (rtx *),
+    riscv_ldrstr_offset_compare);
+
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    reg[i] = (load_p)? temp_operands[2*i] : temp_operands[(2*i) + 1];
+    mem[i] = (load_p)? temp_operands[(2*i) + 1] : temp_operands[(2*i)];
+  }
+
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    riscv_split_plus (XEXP (mem[i], 0), &base[i], &offset[i]);
+    rc[i] = REGNO_REG_CLASS (REGNO (reg[i]));
+  }
+
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    /* All bases are reg.  */
+    if (!REG_P (base[i]))
+		{
+      return false;
+    }
+    /* The mems cannot be volatile.  */
+    if (MEM_VOLATILE_P (mem[i]))
+		{
+      return false;
+    }
+    /* Base regs do not match.  */
+    if (!rtx_equal_p (base[i], base[(i+1) % NUM_INSTR]))
+		{
+      return false;
+    }
+  }
+
+/* Either of the loads is clobbering base register.  It is legitimate to
+	bond loads if second load clobbers base register.  However, hardware
+	does not support such bonding.  */
+    if (load_p
+			&& (REGNO (reg[0]) == REGNO (base[0])
+				|| (REGNO (reg[1]) == REGNO (base[0])))
+			&& (REGNO (reg[2]) == REGNO (base[0])
+				|| (REGNO (reg[3]) == REGNO (base[3])))
+			&& (REGNO (reg[1]) == REGNO (base[1])
+				|| (REGNO (reg[2]) == REGNO (base[2]))))
+		{
+      return false;
+    }
+
+    /* Loading in same registers.  */
+    if (load_p
+	&& (REGNO (reg[0]) == REGNO (reg[1]))
+	&& (REGNO (reg[1]) == REGNO (reg[2]))
+	&& (REGNO (reg[2]) == REGNO (reg[3])))
+		{
+      return false;
+    }
+
+    /* The loads/stores are not of same type.  */
+    if (rc[0] != rc[1]
+	&& rc[1] != rc[2]
+	&& rc[2] != rc[3]
+	&& !reg_class_subset_p (rc[0], rc[1])
+	&& !reg_class_subset_p (rc[1], rc[0])
+	&& !reg_class_subset_p (rc[2], rc[3])
+	&& !reg_class_subset_p (rc[3], rc[2])
+	&& !reg_class_subset_p (rc[1], rc[2])
+	&& !reg_class_subset_p (rc[2], rc[3]))
+		{
+      return false;
+    }
+
+    if ((abs (offset[0] - offset[1]) != msize)
+	|| (abs (offset[2] - offset[3]) != msize)
+	|| (abs (offset[1] - offset[2]) != msize))
+		{
+      return false;
+    }
+    return true;
+}
+
+/* Given OPERANDS of consecutive load/store, check if we can merge
+   them into load-pair or store-pair instructions by adjusting the
+	 offset.  LOAD is true if they are load instructions.
+	 MODE is the mode of memory operands.
+
+   Given below consecutive stores:
+
+     sd  a2, 0x100 (a1)
+     sd  a3, 0x108 (a1)
+     sd  a4, 0x110 (a1)
+     sd  a5, 0x118 (a1)
+
+   Though the offsets are out of the range supported by stp, we can
+   still pair them after adjusting the offset, like:
+
+     addi t0, a1, 0x100
+     th.sdd  a2, a3, 0 (t0), 0, 4
+     th.sdd  a4, a5, 16 (t0), 0, 4
+
+   The peephole patterns detecting this opportunity should guarantee
+   the scratch register is avaliable.
+
+   The function works for 2 consecutive load/store pairs.  */
+bool
+riscv_load_store_bonding_p_2instr (rtx *operands, machine_mode mode,
+bool load_p)
+{
+  HOST_WIDE_INT msize;
+  msize = GET_MODE_SIZE (mode).to_constant ();
+
+  constexpr int NUM_INSTR = 2;
+
+  rtx reg[NUM_INSTR], mem[NUM_INSTR], base[NUM_INSTR];
+	rtx	temp_operands[2*NUM_INSTR];
+
+  enum reg_class rc[NUM_INSTR];
+  HOST_WIDE_INT offset[NUM_INSTR];
+
+  /* We make changes on a copy as we may still bail out.  */
+  for (int i = 0; i < (2*NUM_INSTR); i++)
+    temp_operands[i] = copy_rtx (operands[i]);
+
+  /* Sort the operands.  */
+  gcc_stablesort (temp_operands, NUM_INSTR, 2 * sizeof (rtx *),
+    riscv_ldrstr_offset_compare);
+
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    reg[i] = (load_p)? temp_operands[2*i] : temp_operands[(2*i) + 1];
+    mem[i] = (load_p)? temp_operands[(2*i) + 1] : temp_operands[(2*i)];
+  }
+
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    riscv_split_plus (XEXP (mem[i], 0), &base[i], &offset[i]);
+    rc[i] = REGNO_REG_CLASS (REGNO (reg[i]));
+  }
+
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    /* All bases are reg.  */
+    if (!REG_P (base[i]))
+		{
+      return false;
+    }
+    /* The mems cannot be volatile.  */
+    if (MEM_VOLATILE_P (mem[i]))
+		{
+      return false;
+    }
+    /* Base regs do not match.  */
+    if (!rtx_equal_p (base[i], base[(i+1) % NUM_INSTR]))
+		{
+      return false;
+    }
+  }
+
+/* Either of the loads is clobbering base register.  It is legitimate to
+	bond loads if second load clobbers base register.  However, hardware
+	does not support such bonding.  */
+    if (load_p
+	&& (REGNO (reg[0]) == REGNO (base[0])
+	|| (REGNO (reg[1]) == REGNO (base[0]))))
+    {
+      return false;
+    }
+
+    /* Loading in same registers.  */
+    if (load_p
+				&& REGNO (reg[0]) == REGNO (reg[1]))
+    {
+      return false;
+    }
+
+    /* The loads/stores are not of same type.  */
+    if (rc[0] != rc[1]
+	&& !reg_class_subset_p (rc[0], rc[1])
+	&& !reg_class_subset_p (rc[1], rc[0]))
+    {
+      return false;
+    }
+
+    if (abs (offset[0] - offset[1]) != msize)
+    {
+      return false;
+    }
+    return true;
+}
+
+/* Taking X and Y to be pairs of RTX, one pointing to a MEM rtx and the
+   other pointing to a REG rtx containing an offset, compare the offsets
+   of the two pairs.
+
+   Return:
+
+	1 iff offset (X) > offset (Y)
+	0 iff offset (X) == offset (Y)
+	-1 iff offset (X) < offset (Y)  */
+int
+riscv_ldrstr_offset_compare (const void *x, const void *y)
+{
+  const rtx * operands_1 = (const rtx *) x;
+  const rtx * operands_2 = (const rtx *) y;
+  rtx mem_1, mem_2, base, offset_1, offset_2;
+
+  if (MEM_P (operands_1[0]))
+    mem_1 = operands_1[0];
+  else
+    mem_1 = operands_1[1];
+
+  if (MEM_P (operands_2[0]))
+    mem_2 = operands_2[0];
+  else
+    mem_2 = operands_2[1];
+
+  /* Extract the offsets.  */
+  extract_base_offset_in_addr (mem_1, &base, &offset_1);
+  extract_base_offset_in_addr (mem_2, &base, &offset_2);
+
+  gcc_assert (offset_1 != NULL_RTX && offset_2 != NULL_RTX);
+
+  return wi::cmps (INTVAL (offset_1), INTVAL (offset_2));
+}
+
+/* Given OPERANDS of consecutive load/store, this function pairs them
+   into LDP/STP after adjusting the offset.  It depends on the fact
+   that the operands can be sorted so the offsets are correct for STP.
+   MODE is the mode of memory operands.  CODE is the rtl operator
+   which should be applied to all memory operands, it's SIGN_EXTEND,
+   ZERO_EXTEND or UNKNOWN.  */
+
+bool
+th_riscv_gen_adjusted_mempair (rtx *operands, bool load,
+machine_mode mode, enum rtx_code code, bool is_four_insns)
+{
+  rtx base, offset_1, t1, t2, scratch;
+  HOST_WIDE_INT off_val_1, base_off, new_off_1,
+    off_upper_limit, off_lower_limit, msize;
+
+  int NUM_INSTR = 2;
+  if (is_four_insns)
+    NUM_INSTR = 4;
+
+  rtx temp_operands[2*NUM_INSTR], mem[NUM_INSTR];
+  bool emit_adjust_insn = false;
+  bool misaligned_offset = false;
+
+  scratch = copy_rtx (operands[2*NUM_INSTR]);
+  msize = GET_MODE_SIZE (mode).to_constant ();
+
+  /* Sort the mem operands.  */
+  gcc_stablesort (operands, NUM_INSTR, 2 * sizeof (rtx *),
+      riscv_ldrstr_offset_compare);
+
+  /* We make changes on a copy as we may still bail out.  */
+  for (int i = 0; i < (2*NUM_INSTR); i++)
+    temp_operands[i] = copy_rtx (operands[i]);
+
+  for (int i = 0; i < NUM_INSTR; i++)
+    mem[i] = copy_rtx (load? temp_operands[(2*i) + 1] : temp_operands[2*i]);
+
+  extract_base_offset_in_addr (mem[0], &base, &offset_1);
+  gcc_assert (base != NULL_RTX && offset_1 != NULL_RTX);
+
+  off_val_1 = INTVAL (offset_1);
+
+  switch (msize)
+	{
+    case 4:
+    {
+      off_upper_limit = 3 << 3;
+      off_lower_limit = 0;
+      misaligned_offset = (off_val_1 % 8)? true : false;
+      break;
+    }
+    case 8:
+    {
+      off_upper_limit = 3 << 4;
+      off_lower_limit = 0;
+      misaligned_offset = (off_val_1 % 16)? true : false;
+      break;
+    }
+    default:
+      abort ();
+  }
+
+  /* Offset of the first STP/LDP.  */
+  if ((off_val_1 < off_lower_limit)
+    || (off_val_1 > off_upper_limit)
+    || misaligned_offset)
+  {
+    emit_adjust_insn = true;
+    new_off_1 = 0;
+    base_off = abs (new_off_1 - off_val_1);
+  }
+
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    if (emit_adjust_insn)
+		{
+      replace_equiv_address_nv (mem[i], plus_constant (Pmode, scratch,
+	(new_off_1 + (i*msize))), true);
+    }
+    operands[2*i] = (load)? temp_operands[2*i] : mem[i];
+    operands[(2*i) + 1] = (load)? mem[i] : temp_operands[(2*i) + 1];
+  }
+
+  if (is_four_insns)
+  {
+    if (!riscv_load_store_bonding_p_4instr (operands, mode, load))
+    {
+      return false;
+    }
+  }
+  else
+  {
+    if (!riscv_load_store_bonding_p_2instr (operands, mode, load))
+    {
+      return false;
+    }
+  }
+
+  /* Sign extension for loads.  */
+  for (int i = 0; i < NUM_INSTR; i++)
+	{
+    if (load && GET_MODE_SIZE (GET_MODE (mem[i])).to_constant () == 4)
+		{
+      if (code == ZERO_EXTEND)
+			{
+	mem[i] = gen_rtx_ZERO_EXTEND (Pmode, mem[i]);
+      }
+      else if (code == SIGN_EXTEND)
+			{
+	mem[i] = gen_rtx_SIGN_EXTEND (Pmode, mem[i]);
+      }
+      else
+			{
+	abort ();
+      }
+    }
+    operands[2*i] = (load)? temp_operands[2*i] : mem[i];
+    operands[(2*i) + 1] = (load)? mem[i] : temp_operands[(2*i) + 1];
+  }
+
+  /* Emit adjusting instruction.  */
+  if (emit_adjust_insn)
+	{
+    emit_insn (gen_rtx_SET (scratch, plus_constant (Pmode, base, base_off)));
+  }
+
+  /* Emit ld/sd paired instructions.  */
+  t1 = gen_rtx_SET (operands[0], operands[1]);
+  t2 = gen_rtx_SET (operands[2], operands[3]);
+  emit_insn (gen_rtx_PARALLEL (mode, gen_rtvec (2, t1, t2)));
+  if (is_four_insns)
+	{
+    t1 = gen_rtx_SET (operands[4], operands[5]);
+    t2 = gen_rtx_SET (operands[6], operands[7]);
+    emit_insn (gen_rtx_PARALLEL (mode, gen_rtvec (2, t1, t2)));
+  }
+  return true;
+}
+
 const char *
 riscv_output_return ()
 {
diff --git a/gcc/config/riscv/thead.md b/gcc/config/riscv/thead.md
index 995703ff349..4414f8690d6 100644
--- a/gcc/config/riscv/thead.md
+++ b/gcc/config/riscv/thead.md
@@ -250,3 +250,86 @@ (define_insn "*th_sextw_msubhisi4"
    (set_attr "mode" "SI")]
 )
 
+;; MEMPAIR load/store 64/32 bit
+(define_insn "*th_mov_mempair_<GPR:MODE>"
+  [(set (match_operand:GPR 0 "nonimmediate_operand" "=r, m")
+	(match_operand:GPR 1 "move_operand" "m, r"))
+   (set (match_operand:GPR 2 "nonimmediate_operand" "=r, m")
+	(match_operand:GPR 3 "move_operand" "m, r"))]
+  "TARGET_XTHEADMEMPAIR && reload_completed
+   && (register_operand (operands[0], <GPR:MODE>mode)
+   || reg_or_0_operand (operands[1], <GPR:MODE>mode))
+   && (register_operand (operands[2], <GPR:MODE>mode)
+   || reg_or_0_operand (operands[3], <GPR:MODE>mode))"
+  { return th_riscv_output_mempair_move (operands, <GPR:MODE>mode,
+    SIGN_EXTEND); }
+  [(set_attr "move_type" "load, store")
+   (set_attr "mode" "<GPR:MODE>")])
+
+;; MEMPAIR load DI extended unsigned SI
+(define_insn "*th_mov_mempair_di_si_zero_ext"
+  [(set (match_operand 0 "nonimmediate_operand" "=r")
+	(zero_extend:DI (match_operand 1 "move_operand" "m")))
+   (set (match_operand 2 "nonimmediate_operand" "=r")
+	(zero_extend:DI (match_operand 3 "move_operand" "m")))]
+  "TARGET_XTHEADMEMPAIR && reload_completed
+   && (register_operand (operands[0], DImode)
+   || reg_or_0_operand (operands[1], SImode))
+   && (register_operand (operands[2], DImode)
+   || reg_or_0_operand (operands[3], SImode))"
+  { return th_riscv_output_mempair_move (operands, SImode,
+    ZERO_EXTEND); }
+  [(set_attr "move_type" "load")
+   (set_attr "mode" "DI")
+   (set_attr "length" "8")])
+
+;; MEMPAIR load DI extended signed SI
+(define_insn "*th_mov_mempair_di_si_sign_ext"
+  [(set (match_operand 0 "nonimmediate_operand" "=r")
+	(sign_extend:DI (match_operand 1 "move_operand" "m")))
+   (set (match_operand 2 "nonimmediate_operand" "=r")
+	(sign_extend:DI (match_operand 3 "move_operand" "m")))]
+  "TARGET_XTHEADMEMPAIR && reload_completed
+   && (register_operand (operands[0], DImode)
+   || reg_or_0_operand (operands[1], SImode))
+   && (register_operand (operands[2], DImode)
+   || reg_or_0_operand (operands[3], SImode))"
+  { return th_riscv_output_mempair_move (operands, SImode,
+    SIGN_EXTEND); }
+  [(set_attr "move_type" "load")
+   (set_attr "mode" "DI")
+   (set_attr "length" "8")])
+
+;; MEMPAIR load SI extended unsigned SI
+(define_insn "*th_mov_mempair_si_si_zero_ext"
+  [(set (match_operand 0 "nonimmediate_operand" "=r")
+	(zero_extend:SI (match_operand 1 "move_operand" "m")))
+   (set (match_operand 2 "nonimmediate_operand" "=r")
+	(zero_extend:SI (match_operand 3 "move_operand" "m")))]
+  "TARGET_XTHEADMEMPAIR && reload_completed
+   && (register_operand (operands[0], SImode)
+   || reg_or_0_operand (operands[1], SImode))
+   && (register_operand (operands[2], SImode)
+   || reg_or_0_operand (operands[3], SImode))"
+  { return th_riscv_output_mempair_move (operands, SImode,
+    ZERO_EXTEND); }
+  [(set_attr "move_type" "load")
+   (set_attr "mode" "SI")
+   (set_attr "length" "4")])
+
+;; MEMPAIR load SI extended signed SI
+(define_insn "*th_mov_mempair_si_si_sign_ext"
+  [(set (match_operand 0 "nonimmediate_operand" "=r")
+	(sign_extend:SI (match_operand 1 "move_operand" "m")))
+   (set (match_operand 2 "nonimmediate_operand" "=r")
+	(sign_extend:SI (match_operand 3 "move_operand" "m")))]
+  "TARGET_XTHEADMEMPAIR && reload_completed
+   && (register_operand (operands[0], SImode)
+   || reg_or_0_operand (operands[1], SImode))
+   && (register_operand (operands[2], SImode)
+   || reg_or_0_operand (operands[3], SImode))"
+  { return th_riscv_output_mempair_move (operands, SImode,
+    SIGN_EXTEND); }
+  [(set_attr "move_type" "load")
+   (set_attr "mode" "SI")
+   (set_attr "length" "4")])
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-1.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-1.c
new file mode 100644
index 00000000000..e26b1d82c75
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-1.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+#include <stdint.h>
+
+#define A_SIZE 32
+
+unsigned long long array[A_SIZE];
+unsigned long long temp[A_SIZE];
+
+void bar (void);
+
+// Type your code here, or load an example.
+int 
+foo (void) {
+  for (int i=0; i<A_SIZE; i++)
+    array[i] = 0xF;
+  
+  bar();
+
+  for (int i=0; i<A_SIZE; i++)
+    temp[i] = array[i];
+
+  return 1;
+}
+
+/* { dg-final { scan-assembler-times "th.sdd\t" 2  { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times "th.ldd\t" 2  { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times "th.sdd\t" 2  { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4  { target { rv32 } } } } */
+/* { dg-final { scan-assembler-times "th.lwd\t" 4  { target { rv32 } } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4  { target { rv32 } } } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-10.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-10.c
new file mode 100644
index 00000000000..1fc4d0af626
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-10.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+typedef unsigned __attribute__((mode(DI))) uint64_t;
+
+struct S0 {
+  uint64_t f1;
+  uint64_t f2;
+  uint64_t f3;
+  uint64_t f4;
+  uint64_t f5;
+} a;
+struct S2 {
+  uint64_t f0;
+  uint64_t f2;
+  struct S0 f3;
+};
+
+void fn1 (struct S2 b) {
+  a = b.f3;
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.ldd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+
+/* { dg-final { scan-assembler-times "addi\t" 1 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.lwd\t" 4 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-11.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-11.c
new file mode 100644
index 00000000000..0af8ab34cbe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-11.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+extern void abort (void);
+
+int arr[4][4] = {{0, 1, 1, -1}, {-1, -1, 1, -1}, {1, -1, 1, 1}, {1, -1, -1, 0}};
+long long
+foo ()
+{
+  long long ll = 0;
+  ll += arr[1][0];
+  ll += arr[1][1];
+  return ll;
+}
+
+/* { dg-final { scan-assembler-times "th.lwd\t" 1 { target { rv32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-12.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-12.c
new file mode 100644
index 00000000000..a9beb342e07
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-12.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+extern void abort (void);
+
+unsigned int arr[4][4] = {{0, 1, 1, 2}, {2, 2, 1, 2}, {1, 2, 1, 1}, {1, 2, 2, 0}};
+unsigned long long
+foo ()
+{
+  unsigned long long ll = 0;
+  ll += arr[1][0];
+  ll += arr[1][1];
+  return ll;
+}
+
+/* { dg-final { scan-assembler-times "th.lwud\t" 1 { target { rv32 } } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-13.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-13.c
new file mode 100644
index 00000000000..83a949dc83f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-13.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+typedef float __attribute__ ((vector_size (8))) fvec;
+typedef int __attribute__ ((vector_size (8))) ivec;
+
+struct vec_pair
+{
+  fvec a;
+  ivec b;
+};
+
+void ldp (fvec *a, ivec *b, struct vec_pair *p)
+{
+  *a = p->a;
+  *b = p->b;
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.sdd\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.ldd\t" 1 { target { rv64 } } } */
+
+/* { dg-final { scan-assembler-times "addi\t" 2 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.lwd\t" 2 { target { rv32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-14.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-14.c
new file mode 100644
index 00000000000..446bce83a64
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-14.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+void
+store (int *arr, int x, int y, int z)
+{
+  arr[400] = x;
+  arr[401] = y;
+
+  arr[500] = z;
+  arr[501] = x;
+}
+
+
+void
+store_long (long long int *arr, long long int x, long long int y)
+{
+  arr[400] = x;
+  arr[401] = y;
+
+  arr[403] = y;
+  arr[404] = x;
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2 { target { rv64 } } } */
+
+/* { dg-final { scan-assembler-times "addi\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 6 { target { rv32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-15.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-15.c
new file mode 100644
index 00000000000..bdc2e0bd0ea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-15.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+long long
+load_long (long long int *arr)
+{
+  return arr[400] << 1 + arr[401] << 1 + arr[403] << 1 + arr[404] << 1;
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.ldd\t" 2 { target { rv64 } } } */
+
+/* { dg-final { scan-assembler-times "addi\t" 1 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.lwd\t" 4 { target { rv32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-16.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-16.c
new file mode 100644
index 00000000000..2c9ffbc33b4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-16.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+void
+store_offset (int *array, int x, int y)
+{
+  array[1085] = x;
+  array[1084] = y;
+
+  array[1086] = y;
+  array[1087] = 5;
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.ldd\t" 2 { target { rv64 } } } */
+
+/* { dg-final { scan-assembler-times "addi\t" 1 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.lwd\t" 4 { target { rv32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-17.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-17.c
new file mode 100644
index 00000000000..8d0fa88547c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-17.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+int
+load (int *arr)
+{
+  return arr[527] << 1 + arr[400] << 1 + arr[401] << 1 + arr[528] << 1;
+}
+
+/* { dg-final { scan-assembler-times "th.lwd\t" 2 } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-18.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-18.c
new file mode 100644
index 00000000000..05685720fab
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-18.c
@@ -0,0 +1,67 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+#include "xtheadmempair-18.h"
+
+CONST_FN (2, int32_t, 0);
+/* { dg-final { scan-assembler-times "th.swd\t" 1 } */
+
+CONST_FN (4, int32_t, 0);
+/* { dg-final { scan-assembler-times "th.swd\t" 2 } */
+
+CONST_FN (8, int32_t, 0);
+/* { dg-final { scan-assembler-times "th.swd\t" 4 } */
+
+CONST_FN (16, int32_t, 0);
+/* { dg-final { scan-assembler-times "th.swd\t" 8 } */
+
+CONST_FN (2, int32_t, 1);
+/* { dg-final { scan-assembler-times "th.swd\t" 1 } */
+
+CONST_FN (4, int32_t, 1);
+/* { dg-final { scan-assembler-times "th.swd\t" 2 } */
+
+CONST_FN (8, int32_t, 1);
+/* { dg-final { scan-assembler-times "th.swd\t" 4 } */
+
+DUP_FN (2, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 1 } */
+
+DUP_FN (4, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 2 } */
+
+DUP_FN (8, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 4 } */
+
+CONS2_FN (1, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 1 } */
+
+CONS2_FN (2, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 2 } */
+
+CONS2_FN (4, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 4 } */
+
+CONS2_FN (8, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 8 } */
+
+CONS2_FN (16, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 16 } */
+
+CONS4_FN (1, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 2 } */
+
+CONS4_FN (2, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 4 } */
+
+CONS4_FN (4, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 8 } */
+
+CONS4_FN (8, int32_t);
+/* { dg-final { scan-assembler-times "th.swd\t" 16 } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-18.h b/gcc/testsuite/gcc.target/riscv/xtheadmempair-18.h
new file mode 100644
index 00000000000..dc41ec7a571
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-18.h
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+#include <stdint.h>
+
+#define PRAGMA(X) _Pragma (#X)
+#define UNROLL(COUNT) PRAGMA (GCC unroll (COUNT))
+
+#define CONST_FN(COUNT, TYPE, VAL)		\
+  void						\
+  const_##COUNT##_##TYPE##_##VAL (TYPE *x)	\
+  {						\
+    UNROLL (COUNT)				\
+    for (int i = 0; i < COUNT; ++i)		\
+      x[i] = VAL;				\
+  }
+
+#define DUP_FN(COUNT, TYPE)			\
+  void						\
+  dup_##COUNT##_##TYPE (TYPE *x, TYPE val)	\
+  {						\
+    UNROLL (COUNT)				\
+    for (int i = 0; i < COUNT; ++i)		\
+      x[i] = val;				\
+  }
+
+#define CONS2_FN(COUNT, TYPE)					\
+  void								\
+  cons2_##COUNT##_##TYPE (TYPE *x, TYPE val0, TYPE val1)	\
+  {								\
+    UNROLL (COUNT)						\
+    for (int i = 0; i < COUNT * 2; i += 2)			\
+      {								\
+	x[i + 0] = val0;					\
+	x[i + 1] = val1;					\
+      }								\
+  }
+
+#define CONS4_FN(COUNT, TYPE)					\
+  void								\
+  cons4_##COUNT##_##TYPE (TYPE *x, TYPE val0, TYPE val1,	\
+			  TYPE val2, TYPE val3)			\
+  {								\
+    UNROLL (COUNT)						\
+    for (int i = 0; i < COUNT * 4; i += 4)			\
+      {								\
+	x[i + 0] = val0;					\
+	x[i + 1] = val1;					\
+	x[i + 2] = val2;					\
+	x[i + 3] = val3;					\
+      }								\
+  }
+
+
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-19.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-19.c
new file mode 100644
index 00000000000..1b8866daeae
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-19.c
@@ -0,0 +1,86 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+#include "xtheadmempair-18.h"
+
+CONST_FN (2, int64_t, 0);
+/* { dg-final { scan-assembler-times "th.sdd\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2 { target { rv32 } } } */
+
+CONST_FN (4, int64_t, 0);
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
+
+CONST_FN (8, int64_t, 0);
+/* { dg-final { scan-assembler-times "th.sdd\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 8 { target { rv32 } } } */
+
+CONST_FN (16, int64_t, 0);
+/* { dg-final { scan-assembler-times "th.sdd\t" 8 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 16 { target { rv32 } } } */
+
+CONST_FN (2, int64_t, 1);
+/* { dg-final { scan-assembler-times "th.sdd\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2 { target { rv32 } } } */
+
+CONST_FN (4, int64_t, 1);
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
+
+CONST_FN (8, int64_t, 1);
+/* { dg-final { scan-assembler-times "th.sdd\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 8 { target { rv32 } } } */
+
+DUP_FN (2, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2 { target { rv32 } } } */
+
+DUP_FN (4, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
+
+DUP_FN (8, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 8 { target { rv32 } } } */
+
+CONS2_FN (1, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 1 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2 { target { rv32 } } } */
+
+CONS2_FN (2, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
+
+CONS2_FN (4, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 8 { target { rv32 } } } */
+
+CONS2_FN (8, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 8 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 16 { target { rv32 } } } */
+
+CONS2_FN (16, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 16 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 32 { target { rv32 } } } */
+
+CONS4_FN (1, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
+
+CONS4_FN (2, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 8 { target { rv32 } } } */
+
+CONS4_FN (4, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 8 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 16 { target { rv32 } } } */
+
+CONS4_FN (8, int64_t);
+/* { dg-final { scan-assembler-times "th.sdd\t" 16 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 32 { target { rv32 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-2.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-2.c
new file mode 100644
index 00000000000..e784419025a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-2.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+#include <stdint.h>
+
+__int128 a;
+
+void
+foo (int e)
+{
+	a = 25 << 52 + 10 + e;
+	uint64_t c, d;
+	c = a >> 64;
+	d = a;
+}
+
+/* { dg-final { scan-assembler-times "th.sdd\t" 1  { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2  { target { rv32 } } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-20.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-20.c
new file mode 100644
index 00000000000..531fba41090
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-20.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+typedef struct
+{
+  int a, b, c, d, e;
+} S;
+
+void foo (S *);
+
+void test (int x)
+{
+  S s = { .a = x };
+  foo (&s);
+}
+
+/* { dg-final { scan-assembler-times "th.swd\t" 2 } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-3.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-3.c
new file mode 100644
index 00000000000..e59da57722b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-3.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+#include <stdint.h>
+
+int arr[4][4];
+
+void
+foo (int x, int y)
+{
+	arr[0][1] = x;
+	arr[1][0] = y;
+	arr[2][0] = x;
+	arr[1][1] = y;
+	arr[0][2] = x;
+	arr[0][3] = y;
+	arr[1][2] = x;
+	arr[2][1] = y;
+	arr[3][0] = x;
+	arr[3][1] = y;
+	arr[2][2] = x;
+	arr[1][3] = y;
+	arr[2][3] = x;
+	arr[3][2] = y;
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 2 } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 2 } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-4.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-4.c
new file mode 100644
index 00000000000..c920c475b68
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-4.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+int x[4] = {-4,-5,6,7};
+
+int 
+foo() 
+{
+	int a,b,c,d;
+	        
+	a = x[0];
+	b = x[1];
+	c = x[2];
+	d = x[3];
+	return a+b+c+d;
+}
+
+/* { dg-final { scan-assembler-times "th.lwd\t" 2 } } */
+
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-5.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-5.c
new file mode 100644
index 00000000000..780e76cbc21
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-5.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+int x[4] = {-4,-5,6,7};
+int y[4];
+
+unsigned int* 
+foo() 
+{
+	y[0] = (unsigned int) x[0];
+	y[2] = (unsigned int) x[1];
+	y[1] = (unsigned int) x[2];
+	y[3] = (unsigned int) x[3];
+	return y;
+}
+
+/* { dg-final { scan-assembler-times "th.lwud\t" 2 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-6.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-6.c
new file mode 100644
index 00000000000..c19433cf20f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-6.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+int x[4] = {-4,-5,6,7};
+
+int 
+foo() 
+{
+  int a,b;
+  
+  a = x[0];
+  b = x[1];
+  return a+b;
+}
+
+/* { dg-final { scan-assembler-times "th.lwd\t" 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-7.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-7.c
new file mode 100644
index 00000000000..af3ee1b3739
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-7.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+int x[4] = {-4,-5,6,7};
+int y[4];
+
+unsigned int* foo() {
+	y[2] = (unsigned int) x[1];
+	y[1] = (unsigned int) x[2];
+	y[3] = (unsigned int) x[3];
+	return y;
+}
+
+/* { dg-final { scan-assembler-times "th.lwud\t" 1 } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 1 } } */
+/* { dg-final { scan-assembler-times "addi\t" 5 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-8.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-8.c
new file mode 100644
index 00000000000..76d83d7f9d7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-8.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+typedef int VINT32 __attribute__ ((vector_size((16))));
+
+void
+memory_operation (void * __restrict src, void * __restrict dest, int num)
+{
+  VINT32 *vsrc = (VINT32 *) src;
+  VINT32 *vdest = (VINT32 *) dest;
+  int i;
+
+  for (i = 0; i < num - 1; i += 2)
+  {
+    vdest[i] = vdest[i] + vsrc[i];
+    vdest[i + 1] = vdest[i + 1] + vsrc[i + 1];
+  }
+}
+
+/* { dg-final { scan-assembler-times "th.lwd\t" 8 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "addi\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.lwd\t" 8 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "addi\t" 4 { target { rv32 } } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/xtheadmempair-9.c b/gcc/testsuite/gcc.target/riscv/xtheadmempair-9.c
new file mode 100644
index 00000000000..f8ed6663777
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/xtheadmempair-9.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Oz" "-Os"} } */
+/* { dg-options "-march=rv64gc_xtheadmempair" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmempair" { target { rv32 } } } */
+
+#ifndef __riscv_xtheadmempair
+#error Feature macro not defined
+#endif
+
+typedef unsigned __attribute__((mode(DI))) uint64_t;
+
+struct S0 {
+  uint64_t f1;
+  uint64_t f2;
+  uint64_t f3;
+  uint64_t f4;
+  uint64_t f5;
+} a;
+struct S2 {
+  uint64_t f0;
+  uint64_t f2;
+  struct S0 f3;
+};
+
+void fn1 () {
+  struct S2 b = {0, 1, 7, 4073709551611, 4, 8, 7};
+  a = b.f3;
+}
+
+/* { dg-final { scan-assembler-times "addi\t" 4 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.ldd\t" 2 { target { rv64 } } } */
+/* { dg-final { scan-assembler-times "th.sdd\t" 2 { target { rv64 } } } */
+
+/* { dg-final { scan-assembler-times "addi\t" 4 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.lwd\t" 4 { target { rv32 } } } */
+/* { dg-final { scan-assembler-times "th.swd\t" 4 { target { rv32 } } } */
