public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Christoph Muellner <christoph.muellner@vrull.eu>
To: gcc-patches@gcc.gnu.org, Kito Cheng <kito.cheng@sifive.com>,
	Jim Wilson <jim.wilson.gcc@gmail.com>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Andrew Waterman <andrew@sifive.com>,
	Philipp Tomsich <philipp.tomsich@vrull.eu>,
	Cooper Qu <cooper.qu@linux.alibaba.com>,
	Lifang Xia <lifang_xia@linux.alibaba.com>,
	Yunhai Shang <yunhai@linux.alibaba.com>,
	Zhiwei Liu <zhiwei_liu@linux.alibaba.com>
Cc: "moiz.hussain" <moiz.hussain@vrull.eu>,
	"M . Moiz Hussain" <muhammad.hussain@vrull.eu>
Subject: [RFC PATCH] riscv: thead: Add support for XTheadMemPair ISA extension
Date: Sun, 13 Nov 2022 23:00:57 +0100	[thread overview]
Message-ID: <20221113220057.2753718-1-christoph.muellner@vrull.eu> (raw)

From: "moiz.hussain" <moiz.hussain@vrull.eu>

The XTheadMacXTheadMemPair ISA extension provides load/store
pair instructions:
* th.ldd
* th.sdd
* th.lwd
* th.lwud
* th.swd

We added the following unnamed patterns to the
peephole.md stage:
* load/store pair patterns for 4 instructions
* load/store pair patterns for 2 instructions

It was also required to add define_insn patterns to thead.md:
* th_mov_mempair_<GPR:MODE>
* th_mov_mempair_di_si_zero_ext
* th_mov_mempair_di_si_sign_ext
* th_mov_mempair_si_si_zero_ext
* th_mov_mempair_si_si_sign_ext

Much of the code has been inspired by the MIPS and the aarch64 backend.
Also the new test cases were inspired by other architectures.

The patch is meant to apply on top of the XThead* series
that has been posted here:
  https://gcc.gnu.org/pipermail/gcc-patches/2022-November/605980.html

This patch is in RFC mode as we still need to sort out some failing
tests. However, we'd like to get some feedback from the RISC-V
maintainers on the approach that we have taken. Especially since the
patch is already quite big.

Signed-off-by: M. Moiz Hussain <muhammad.hussain@vrull.eu>
---
 gcc/common/config/riscv/riscv-common.cc       |   2 +
 gcc/config/riscv/peephole.md                  | 336 +++++++++
 gcc/config/riscv/riscv-opts.h                 |   4 +-
 gcc/config/riscv/riscv-protos.h               |   9 +
 gcc/config/riscv/riscv.cc                     | 701 ++++++++++++++++++
 gcc/config/riscv/thead.md                     |  83 +++
 .../gcc.target/riscv/xtheadmempair-1.c        |  39 +
 .../gcc.target/riscv/xtheadmempair-10.c       |  35 +
 .../gcc.target/riscv/xtheadmempair-11.c       |  22 +
 .../gcc.target/riscv/xtheadmempair-12.c       |  23 +
 .../gcc.target/riscv/xtheadmempair-13.c       |  31 +
 .../gcc.target/riscv/xtheadmempair-14.c       |  36 +
 .../gcc.target/riscv/xtheadmempair-15.c       |  20 +
 .../gcc.target/riscv/xtheadmempair-16.c       |  24 +
 .../gcc.target/riscv/xtheadmempair-17.c       |  16 +
 .../gcc.target/riscv/xtheadmempair-18.c       |  67 ++
 .../gcc.target/riscv/xtheadmempair-18.h       |  59 ++
 .../gcc.target/riscv/xtheadmempair-19.c       |  86 +++
 .../gcc.target/riscv/xtheadmempair-2.c        |  24 +
 .../gcc.target/riscv/xtheadmempair-20.c       |  23 +
 .../gcc.target/riscv/xtheadmempair-3.c        |  35 +
 .../gcc.target/riscv/xtheadmempair-4.c        |  25 +
 .../gcc.target/riscv/xtheadmempair-5.c        |  23 +
 .../gcc.target/riscv/xtheadmempair-6.c        |  22 +
 .../gcc.target/riscv/xtheadmempair-7.c        |  22 +
 .../gcc.target/riscv/xtheadmempair-8.c        |  31 +
 .../gcc.target/riscv/xtheadmempair-9.c        |  36 +
 27 files changed, 1833 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-10.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-11.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-12.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-13.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-14.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-15.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-16.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-17.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-18.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-18.h
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-19.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-20.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-6.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-7.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-8.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/xtheadmempair-9.c

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 } } } */
-- 
2.38.1


                 reply	other threads:[~2022-11-13 22:01 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221113220057.2753718-1-christoph.muellner@vrull.eu \
    --to=christoph.muellner@vrull.eu \
    --cc=andrew@sifive.com \
    --cc=cooper.qu@linux.alibaba.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jim.wilson.gcc@gmail.com \
    --cc=kito.cheng@sifive.com \
    --cc=lifang_xia@linux.alibaba.com \
    --cc=moiz.hussain@vrull.eu \
    --cc=muhammad.hussain@vrull.eu \
    --cc=palmer@dabbelt.com \
    --cc=philipp.tomsich@vrull.eu \
    --cc=yunhai@linux.alibaba.com \
    --cc=zhiwei_liu@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).