@@ -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}
@@ -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;
+})
@@ -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 */
@@ -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);
@@ -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 ()
{
@@ -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")])
new file mode 100644
@@ -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 } } } } */
+
new file mode 100644
@@ -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 } } } */
new file mode 100644
@@ -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 } } } */
new file mode 100644
@@ -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 } } } */
+
new file mode 100644
@@ -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 } } } */
new file mode 100644
@@ -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 } } } */
new file mode 100644
@@ -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 } } } */
new file mode 100644
@@ -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 } } } */
new file mode 100644
@@ -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 } */
new file mode 100644
@@ -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 } */
new file mode 100644
@@ -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; \
+ } \
+ }
+
+
new file mode 100644
@@ -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 } } } */
new file mode 100644
@@ -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 } } } } */
new file mode 100644
@@ -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 } */
new file mode 100644
@@ -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 } } */
+
new file mode 100644
@@ -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 } } */
+
new file mode 100644
@@ -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 } } */
new file mode 100644
@@ -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 } } */
new file mode 100644
@@ -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 } } */
new file mode 100644
@@ -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
new file mode 100644
@@ -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 } } } */