@@ -220,3 +220,29 @@ (define_memory_constraint "Wdm"
"Vector duplicate memory operand"
(and (match_code "mem")
(match_code "reg" "0")))
+
+;; Vendor ISA extension constraints.
+
+(define_memory_constraint "th_m_mia"
+ "@internal
+ A MEM with a valid address for th.[l|s]*ia instructions."
+ (and (match_code "mem")
+ (match_test "th_memidx_legitimate_modify_p (op, true)")))
+
+(define_memory_constraint "th_m_mib"
+ "@internal
+ A MEM with a valid address for th.[l|s]*ib instructions."
+ (and (match_code "mem")
+ (match_test "th_memidx_legitimate_modify_p (op, false)")))
+
+(define_memory_constraint "th_m_mir"
+ "@internal
+ A MEM with a valid address for th.[l|s]*r* instructions."
+ (and (match_code "mem")
+ (match_test "th_memidx_legitimate_index_p (op, false)")))
+
+(define_memory_constraint "th_m_miu"
+ "@internal
+ A MEM with a valid address for th.[l|s]*ur* instructions."
+ (and (match_code "mem")
+ (match_test "th_memidx_legitimate_index_p (op, true)")))
@@ -43,6 +43,15 @@ enum riscv_symbol_type {
A natural register + offset address. The register satisfies
riscv_valid_base_register_p and the offset is a const_arith_operand.
+ ADDRESS_REG_REG
+ A base register indexed by (optionally scaled) register.
+
+ ADDRESS_REG_UREG
+ A base register indexed by (optionally scaled) zero-extended register.
+
+ ADDRESS_REG_WB
+ A base register indexed by immediate offset with writeback.
+
ADDRESS_LO_SUM
A LO_SUM rtx. The first operand is a valid base register and
the second operand is a symbolic address.
@@ -54,6 +63,9 @@ enum riscv_symbol_type {
A constant symbolic address. */
enum riscv_address_type {
ADDRESS_REG,
+ ADDRESS_REG_REG,
+ ADDRESS_REG_UREG,
+ ADDRESS_REG_WB,
ADDRESS_LO_SUM,
ADDRESS_CONST_INT,
ADDRESS_SYMBOLIC
@@ -67,6 +79,13 @@ enum riscv_address_type {
ADDRESS_REG
REG is the base register and OFFSET is the constant offset.
+ ADDRESS_REG_REG and ADDRESS_REG_UREG
+ REG is the base register and OFFSET is the index register.
+
+ ADDRESS_REG_WB
+ REG is the base register, OFFSET is the constant offset, and
+ shift is the shift amount for the offset.
+
ADDRESS_LO_SUM
REG and OFFSET are the operands to the LO_SUM and SYMBOL_TYPE
is the type of symbol it references.
@@ -78,6 +97,7 @@ struct riscv_address_info {
rtx reg;
rtx offset;
enum riscv_symbol_type symbol_type;
+ int shift;
};
/* Routines implemented in riscv.cc. */
@@ -85,6 +105,7 @@ extern enum riscv_symbol_type riscv_classify_symbolic_expression (rtx);
extern bool riscv_symbolic_constant_p (rtx, enum riscv_symbol_type *);
extern int riscv_float_const_rtx_index_for_fli (rtx);
extern int riscv_regno_mode_ok_for_base_p (int, machine_mode, bool);
+extern bool riscv_valid_base_register_p (rtx, machine_mode, bool);
extern enum reg_class riscv_index_reg_class ();
extern int riscv_regno_ok_for_index_p (int);
extern int riscv_address_insns (rtx, machine_mode, bool);
@@ -603,6 +624,14 @@ extern void th_mempair_save_restore_regs (rtx[4], bool, machine_mode);
#ifdef RTX_CODE
extern const char*
th_mempair_output_move (rtx[4], bool, machine_mode, RTX_CODE);
+extern bool th_memidx_legitimate_modify_p (rtx);
+extern bool th_memidx_legitimate_modify_p (rtx, bool);
+extern bool th_memidx_legitimate_index_p (rtx);
+extern bool th_memidx_legitimate_index_p (rtx, bool);
+extern bool th_classify_address (struct riscv_address_info *,
+ rtx, machine_mode, bool);
+extern const char *th_output_move (rtx, rtx);
+extern bool th_print_operand_address (FILE *, machine_mode, rtx);
#endif
extern bool riscv_use_divmod_expander (void);
@@ -1084,6 +1084,9 @@ riscv_regno_mode_ok_for_base_p (int regno,
enum reg_class
riscv_index_reg_class ()
{
+ if (TARGET_XTHEADMEMIDX)
+ return GR_REGS;
+
return NO_REGS;
}
@@ -1092,15 +1095,18 @@ riscv_index_reg_class ()
but extensions might support that. */
int
-riscv_regno_ok_for_index_p (int)
+riscv_regno_ok_for_index_p (int regno)
{
+ if (TARGET_XTHEADMEMIDX)
+ return riscv_regno_mode_ok_for_base_p (regno, VOIDmode, 1);
+
return 0;
}
/* Return true if X is a valid base register for mode MODE.
STRICT_P is true if REG_OK_STRICT is in effect. */
-static bool
+bool
riscv_valid_base_register_p (rtx x, machine_mode mode, bool strict_p)
{
if (!strict_p && GET_CODE (x) == SUBREG)
@@ -1342,6 +1348,9 @@ static bool
riscv_classify_address (struct riscv_address_info *info, rtx x,
machine_mode mode, bool strict_p)
{
+ if (th_classify_address (info, x, mode, strict_p))
+ return true;
+
switch (GET_CODE (x))
{
case REG:
@@ -3386,6 +3395,10 @@ riscv_output_move (rtx dest, rtx src)
machine_mode mode;
bool dbl_p;
unsigned width;
+ const char *insn;
+
+ if ((insn = th_output_move (dest, src)))
+ return insn;
dest_code = GET_CODE (dest);
src_code = GET_CODE (src);
@@ -5581,6 +5594,9 @@ riscv_print_operand_address (FILE *file, machine_mode mode ATTRIBUTE_UNUSED, rtx
{
struct riscv_address_info addr;
+ if (th_print_operand_address (file, mode, x))
+ return;
+
if (riscv_classify_address (&addr, x, word_mode, true))
switch (addr.type)
{
@@ -5602,7 +5618,11 @@ riscv_print_operand_address (FILE *file, machine_mode mode ATTRIBUTE_UNUSED, rtx
case ADDRESS_SYMBOLIC:
output_addr_const (file, riscv_strip_unspec_address (x));
return;
+
+ default:
+ gcc_unreachable ();
}
+
gcc_unreachable ();
}
@@ -1191,9 +1191,11 @@ extern void riscv_remove_unneeded_save_restore_calls (void);
#define OPTIMIZE_MODE_SWITCHING(ENTITY) (TARGET_VECTOR)
#define NUM_MODES_FOR_MODE_SWITCHING {VXRM_MODE_NONE, riscv_vector::FRM_NONE}
-
/* The size difference between different RVV modes can be up to 64 times.
e.g. RVVMF64BI vs RVVMF1BI on zvl512b, which is [1, 1] vs [64, 64]. */
#define MAX_POLY_VARIANT 64
+#define HAVE_POST_MODIFY_DISP TARGET_XTHEADMEMIDX
+#define HAVE_PRE_MODIFY_DISP TARGET_XTHEADMEMIDX
+
#endif /* ! GCC_RISCV_H */
@@ -1662,7 +1662,7 @@ (define_insn_and_split "*zero_extendsidi2_internal"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(zero_extend:DI
(match_operand:SI 1 "nonimmediate_operand" " r,m")))]
- "TARGET_64BIT && !TARGET_ZBA && !TARGET_XTHEADBB
+ "TARGET_64BIT && !TARGET_ZBA && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX
&& !(register_operand (operands[1], SImode)
&& reg_or_subregno (operands[1]) == VL_REGNUM)"
"@
@@ -1690,7 +1690,7 @@ (define_insn_and_split "*zero_extendhi<GPR:mode>2"
[(set (match_operand:GPR 0 "register_operand" "=r,r")
(zero_extend:GPR
(match_operand:HI 1 "nonimmediate_operand" " r,m")))]
- "!TARGET_ZBB && !TARGET_XTHEADBB"
+ "!TARGET_ZBB && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
"@
#
lhu\t%0,%1"
@@ -1709,11 +1709,17 @@ (define_insn_and_split "*zero_extendhi<GPR:mode>2"
(set_attr "type" "load")
(set_attr "mode" "<GPR:MODE>")])
-(define_insn "zero_extendqi<SUPERQI:mode>2"
+(define_expand "zero_extendqi<SUPERQI:mode>2"
+ [(set (match_operand:SUPERQI 0 "register_operand")
+ (zero_extend:SUPERQI
+ (match_operand:QI 1 "nonimmediate_operand")))]
+ "")
+
+(define_insn "*zero_extendqi<SUPERQI:mode>2_internal"
[(set (match_operand:SUPERQI 0 "register_operand" "=r,r")
(zero_extend:SUPERQI
(match_operand:QI 1 "nonimmediate_operand" " r,m")))]
- ""
+ "!TARGET_XTHEADMEMIDX"
"@
andi\t%0,%1,0xff
lbu\t%0,%1"
@@ -1728,11 +1734,17 @@ (define_insn "zero_extendqi<SUPERQI:mode>2"
;;
;; ....................
-(define_insn "extendsidi2"
+(define_expand "extendsidi2"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(sign_extend:DI
(match_operand:SI 1 "nonimmediate_operand" " r,m")))]
- "TARGET_64BIT"
+ "TARGET_64BIT")
+
+(define_insn "*extendsidi2_internal"
+ [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (sign_extend:DI
+ (match_operand:SI 1 "nonimmediate_operand" " r,m")))]
+ "TARGET_64BIT && !TARGET_XTHEADMEMIDX"
"@
sext.w\t%0,%1
lw\t%0,%1"
@@ -1749,7 +1761,7 @@ (define_insn_and_split "*extend<SHORT:mode><SUPERQI:mode>2"
[(set (match_operand:SUPERQI 0 "register_operand" "=r,r")
(sign_extend:SUPERQI
(match_operand:SHORT 1 "nonimmediate_operand" " r,m")))]
- "!TARGET_ZBB && !TARGET_XTHEADBB"
+ "!TARGET_ZBB && !TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
"@
#
l<SHORT:size>\t%0,%1"
@@ -25,11 +25,16 @@
#include "coretypes.h"
#include "target.h"
#include "backend.h"
+#include "tree.h"
#include "rtl.h"
+#include "explow.h"
#include "memmodel.h"
#include "emit-rtl.h"
+#include "optabs.h"
#include "poly-int.h"
#include "output.h"
+#include "regs.h"
+#include "riscv-protos.h"
/* If MEM is in the form of "base+offset", extract the two parts
of address and set to BASE and OFFSET, otherwise return false
@@ -430,3 +435,450 @@ th_mempair_save_restore_regs (rtx operands[4], bool load_p,
else
th_mempair_save_regs (operands);
}
+
+/* Return true if X can be represented as signed immediate of NBITS bits.
+ The immediate is assumed to be shifted by LSHAMT bits left. */
+
+static bool
+valid_signed_immediate (rtx x, unsigned nbits, unsigned lshamt)
+{
+ if (GET_CODE (x) != CONST_INT)
+ return false;
+
+ HOST_WIDE_INT v = INTVAL (x);
+
+ HOST_WIDE_INT vunshifted = v >> lshamt;
+
+ /* Make sure we did not shift out any bits. */
+ if (vunshifted << lshamt != v)
+ return false;
+
+ unsigned HOST_WIDE_INT imm_reach = 1LL << nbits;
+ return ((unsigned HOST_WIDE_INT) vunshifted + imm_reach/2 < imm_reach);
+}
+
+/* Return the address RTX of a move to/from memory
+ instruction. */
+
+static rtx
+th_get_move_mem_addr (rtx dest, rtx src, bool load)
+{
+ rtx mem;
+
+ if (load)
+ mem = src;
+ else
+ mem = dest;
+
+ gcc_assert (GET_CODE (mem) == MEM);
+ return XEXP (mem, 0);
+}
+
+/* Return true if X is a valid address for T-Head's memory addressing modes
+ with pre/post modification for machine mode MODE.
+ If it is, fill in INFO appropriately (if non-NULL).
+ If STRICT_P is true then REG_OK_STRICT is in effect. */
+
+static bool
+th_memidx_classify_address_modify (struct riscv_address_info *info, rtx x,
+ machine_mode mode, bool strict_p)
+{
+ if (!TARGET_XTHEADMEMIDX)
+ return false;
+
+ if (!TARGET_64BIT && mode == DImode)
+ return false;
+
+ if (!(INTEGRAL_MODE_P (mode) && GET_MODE_SIZE (mode).to_constant () <= 8))
+ return false;
+
+ if (GET_CODE (x) != POST_MODIFY
+ && GET_CODE (x) != PRE_MODIFY)
+ return false;
+
+ rtx reg = XEXP (x, 0);
+ rtx exp = XEXP (x, 1);
+ rtx expreg = XEXP (exp, 0);
+ rtx expoff = XEXP (exp, 1);
+
+ if (GET_CODE (exp) != PLUS
+ || !rtx_equal_p (expreg, reg)
+ || !CONST_INT_P (expoff)
+ || !riscv_valid_base_register_p (reg, mode, strict_p))
+ return false;
+
+ /* The offset is calculated as (sign_extend(imm5) << imm2) */
+ const int shamt_bits = 2;
+ for (int shamt = 0; shamt < (1 << shamt_bits); shamt++)
+ {
+ const int nbits = 5;
+ if (valid_signed_immediate (expoff, nbits, shamt))
+ {
+ if (info)
+ {
+ info->type = ADDRESS_REG_WB;
+ info->reg = reg;
+ info->offset = expoff;
+ info->shift = shamt;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Return TRUE if X is a MEM with a legitimate modify address. */
+
+bool
+th_memidx_legitimate_modify_p (rtx x)
+{
+ if (!MEM_P (x))
+ return false;
+
+ /* Get the mode from the MEM and unpack it. */
+ machine_mode mode = GET_MODE (x);
+ x = XEXP (x, 0);
+
+ return th_memidx_classify_address_modify (NULL, x, mode, reload_completed);
+}
+
+/* Return TRUE if X is a MEM with a legitimate modify address
+ and the address is POST_MODIFY (if POST is true) or a PRE_MODIFY
+ (otherwise). */
+
+bool
+th_memidx_legitimate_modify_p (rtx x, bool post)
+{
+ if (!th_memidx_legitimate_modify_p (x))
+ return false;
+
+ /* Unpack the MEM and check the code. */
+ x = XEXP (x, 0);
+ if (post)
+ return GET_CODE (x) == POST_MODIFY;
+ else
+ return GET_CODE (x) == PRE_MODIFY;
+}
+
+/* Provide a buffer for a th.lXia/th.lXib/th.sXia/th.sXib instruction
+ for the given MODE. If LOAD is true, a load instruction will be
+ provided (otherwise, a store instruction). If X is not suitable
+ return NULL. */
+
+static const char *
+th_memidx_output_modify (rtx dest, rtx src, machine_mode mode, bool load)
+{
+ char format[24];
+ rtx output_operands[2];
+ rtx x = th_get_move_mem_addr (dest, src, load);
+
+ /* Validate x. */
+ if (!th_memidx_classify_address_modify (NULL, x, mode, reload_completed))
+ return NULL;
+
+ int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
+ bool post = GET_CODE (x) == POST_MODIFY;
+
+ const char *const insn[][4] = {
+ {
+ "th.sbi%s\t%%z1,%%0",
+ "th.shi%s\t%%z1,%%0",
+ "th.swi%s\t%%z1,%%0",
+ "th.sdi%s\t%%z1,%%0"
+ },
+ {
+ "th.lbui%s\t%%0,%%1",
+ "th.lhui%s\t%%0,%%1",
+ "th.lwi%s\t%%0,%%1",
+ "th.ldi%s\t%%0,%%1"
+ }
+ };
+
+ snprintf (format, sizeof (format), insn[load][index], post ? "a" : "b");
+ output_operands[0] = dest;
+ output_operands[1] = src;
+ output_asm_insn (format, output_operands);
+ return "";
+}
+
+static bool
+is_memidx_mode (machine_mode mode)
+{
+ if (mode == QImode || mode == HImode || mode == SImode)
+ return true;
+
+ if (mode == DImode && TARGET_64BIT)
+ return true;
+
+ return false;
+}
+
+/* Return true if X is a valid address for T-Head's memory addressing modes
+ with scaled register offsets for machine mode MODE.
+ If it is, fill in INFO appropriately (if non-NULL).
+ If STRICT_P is true then REG_OK_STRICT is in effect. */
+
+static bool
+th_memidx_classify_address_index (struct riscv_address_info *info, rtx x,
+ machine_mode mode, bool strict_p)
+{
+ /* Ensure that the mode is supported. */
+ if (!(TARGET_XTHEADMEMIDX && is_memidx_mode (mode)))
+ return false;
+
+ if (GET_CODE (x) != PLUS)
+ return false;
+
+ rtx reg = XEXP (x, 0);
+ enum riscv_address_type type;
+ rtx offset = XEXP (x, 1);
+ int shift;
+
+ if (!riscv_valid_base_register_p (reg, mode, strict_p))
+ return false;
+
+ /* (reg:X) */
+ if (REG_P (offset)
+ && GET_MODE (offset) == Xmode)
+ {
+ type = ADDRESS_REG_REG;
+ shift = 0;
+ offset = offset;
+ }
+ /* (zero_extend:DI (reg:SI)) */
+ else if (GET_CODE (offset) == ZERO_EXTEND
+ && GET_MODE (offset) == DImode
+ && GET_MODE (XEXP (offset, 0)) == SImode)
+ {
+ type = ADDRESS_REG_UREG;
+ shift = 0;
+ offset = XEXP (offset, 0);
+ }
+ /* (ashift:X (reg:X) (const_int shift)) */
+ else if (GET_CODE (offset) == ASHIFT
+ && GET_MODE (offset) == Xmode
+ && REG_P (XEXP (offset, 0))
+ && GET_MODE (XEXP (offset, 0)) == Xmode
+ && CONST_INT_P (XEXP (offset, 1))
+ && IN_RANGE (INTVAL (XEXP (offset, 1)), 0, 3))
+ {
+ type = ADDRESS_REG_REG;
+ shift = INTVAL (XEXP (offset, 1));
+ offset = XEXP (offset, 0);
+ }
+ /* (ashift:DI (zero_extend:DI (reg:SI)) (const_int shift)) */
+ else if (GET_CODE (offset) == ASHIFT
+ && GET_MODE (offset) == DImode
+ && GET_CODE (XEXP (offset, 0)) == ZERO_EXTEND
+ && GET_MODE (XEXP (offset, 0)) == DImode
+ && GET_MODE (XEXP (XEXP (offset, 0), 0)) == SImode
+ && CONST_INT_P (XEXP (offset, 1))
+ && IN_RANGE(INTVAL (XEXP (offset, 1)), 0, 3))
+ {
+ type = ADDRESS_REG_UREG;
+ shift = INTVAL (XEXP (offset, 1));
+ offset = XEXP (XEXP (offset, 0), 0);
+ }
+ else
+ return false;
+
+ if (!strict_p && GET_CODE (offset) == SUBREG)
+ offset = SUBREG_REG (offset);
+
+ if (!REG_P (offset)
+ || !riscv_regno_mode_ok_for_base_p (REGNO (offset), mode, strict_p))
+ return false;
+
+ if (info)
+ {
+ info->reg = reg;
+ info->type = type;
+ info->offset = offset;
+ info->shift = shift;
+ }
+ return true;
+}
+
+/* Return TRUE if X is a MEM with a legitimate indexed address. */
+
+bool
+th_memidx_legitimate_index_p (rtx x)
+{
+ if (!MEM_P (x))
+ return false;
+
+ /* Get the mode from the MEM and unpack it. */
+ machine_mode mode = GET_MODE (x);
+ x = XEXP (x, 0);
+
+ return th_memidx_classify_address_index (NULL, x, mode, reload_completed);
+}
+
+/* Return TRUE if X is a MEM with a legitimate indexed address
+ and the offset register is zero-extended (if UINDEX is true)
+ or sign-extended (otherwise). */
+
+bool
+th_memidx_legitimate_index_p (rtx x, bool uindex)
+{
+ if (!MEM_P (x))
+ return false;
+
+ /* Get the mode from the MEM and unpack it. */
+ machine_mode mode = GET_MODE (x);
+ x = XEXP (x, 0);
+
+ struct riscv_address_info info;
+ if (!th_memidx_classify_address_index (&info, x, mode, reload_completed))
+ return false;
+
+ if (uindex)
+ return info.type == ADDRESS_REG_UREG;
+ else
+ return info.type == ADDRESS_REG_REG;
+}
+
+/* Provide a buffer for a th.lrX/th.lurX/th.srX/th.surX instruction
+ for the given MODE. If LOAD is true, a load instruction will be
+ provided (otherwise, a store instruction). If X is not suitable
+ return NULL. */
+
+static const char *
+th_memidx_output_index (rtx dest, rtx src, machine_mode mode, bool load)
+{
+ struct riscv_address_info info;
+ char format[24];
+ rtx output_operands[2];
+ rtx x = th_get_move_mem_addr (dest, src, load);
+
+ /* Validate x. */
+ if (!th_memidx_classify_address_index (&info, x, mode, reload_completed))
+ return NULL;
+
+ int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
+ bool uindex = info.type == ADDRESS_REG_UREG;
+
+ const char *const insn[][4] = {
+ {
+ "th.s%srb\t%%z1,%%0",
+ "th.s%srh\t%%z1,%%0",
+ "th.s%srw\t%%z1,%%0",
+ "th.s%srd\t%%z1,%%0"
+ },
+ {
+ "th.l%srbu\t%%0,%%1",
+ "th.l%srhu\t%%0,%%1",
+ "th.l%srw\t%%0,%%1",
+ "th.l%srd\t%%0,%%1"
+ }
+ };
+
+ snprintf (format, sizeof (format), insn[load][index], uindex ? "u" : "");
+ output_operands[0] = dest;
+ output_operands[1] = src;
+ output_asm_insn (format, output_operands);
+ return "";
+}
+
+/* Return true if X is a valid address for T-Head's memory addressing modes
+ for machine mode MODE. If it is, fill in INFO appropriately (if non-NULL).
+ If STRICT_P is true then REG_OK_STRICT is in effect. */
+
+bool
+th_classify_address (struct riscv_address_info *info, rtx x,
+ machine_mode mode, bool strict_p)
+{
+ switch (GET_CODE (x))
+ {
+ case PLUS:
+ if (th_memidx_classify_address_index (info, x, mode, strict_p))
+ return true;
+ break;
+
+ case POST_MODIFY:
+ case PRE_MODIFY:
+ if (th_memidx_classify_address_modify (info, x, mode, strict_p))
+ return true;
+ break;
+
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+/* Provide a string containing a XTheadMemIdx instruction for the given
+ MODE from the provided SRC to the provided DEST.
+ A pointer to a NULL-terminated string containing the instruction will
+ be returned if a suitable instruction is available. Otherwise, this
+ function returns NULL. */
+
+const char *
+th_output_move (rtx dest, rtx src)
+{
+ enum rtx_code dest_code, src_code;
+ machine_mode mode;
+ const char *insn = NULL;
+
+ dest_code = GET_CODE (dest);
+ src_code = GET_CODE (src);
+ mode = GET_MODE (dest);
+
+ if (!(mode == GET_MODE (src) || src == CONST0_RTX (mode)))
+ return NULL;
+
+ if (dest_code == REG && src_code == MEM)
+ {
+ if (GET_MODE_CLASS (mode) == MODE_INT)
+ {
+ if ((insn = th_memidx_output_index (dest, src, mode, true)))
+ return insn;
+ if ((insn = th_memidx_output_modify (dest, src, mode, true)))
+ return insn;
+ }
+ }
+ else if (dest_code == MEM && (src_code == REG || src == CONST0_RTX (mode)))
+ {
+ if (GET_MODE_CLASS (mode) == MODE_INT
+ || src == CONST0_RTX (mode))
+ {
+ if ((insn = th_memidx_output_index (dest, src, mode, false)))
+ return insn;
+ if ((insn = th_memidx_output_modify (dest, src, mode, false)))
+ return insn;
+ }
+ }
+ return NULL;
+}
+
+/* Implement TARGET_PRINT_OPERAND_ADDRESS for XTheadMemIdx. */
+
+bool
+th_print_operand_address (FILE *file, machine_mode mode, rtx x)
+{
+ struct riscv_address_info addr;
+
+ if (!th_classify_address (&addr, x, mode, reload_completed))
+ return false;
+
+ switch (addr.type)
+ {
+ case ADDRESS_REG_REG:
+ case ADDRESS_REG_UREG:
+ fprintf (file, "%s,%s,%u", reg_names[REGNO (addr.reg)],
+ reg_names[REGNO (addr.offset)], addr.shift);
+ return true;
+
+ case ADDRESS_REG_WB:
+ fprintf (file, "(%s),%ld,%u", reg_names[REGNO (addr.reg)],
+ INTVAL (addr.offset) >> addr.shift, addr.shift);
+ return true;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ gcc_unreachable ();
+}
@@ -62,7 +62,7 @@ (define_insn "*extendhi<SUPERQI:mode>2_th_ext"
[(set (match_operand:SUPERQI 0 "register_operand" "=r,r")
(sign_extend:SUPERQI
(match_operand:HI 1 "nonimmediate_operand" "r,m")))]
- "TARGET_XTHEADBB"
+ "TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
"@
th.ext\t%0,%1,15,0
lh\t%0,%1"
@@ -73,7 +73,7 @@ (define_insn "*extendqi<SUPERQI:mode>2_th_ext"
[(set (match_operand:SUPERQI 0 "register_operand" "=r,r")
(sign_extend:SUPERQI
(match_operand:QI 1 "nonimmediate_operand" "r,m")))]
- "TARGET_XTHEADBB"
+ "TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
"@
th.ext\t%0,%1,7,0
lb\t%0,%1"
@@ -96,7 +96,7 @@ (define_insn "*th_extu<mode>4"
(define_insn "*zero_extendsidi2_th_extu"
[(set (match_operand:DI 0 "register_operand" "=r,r")
(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "r,m")))]
- "TARGET_64BIT && TARGET_XTHEADBB"
+ "TARGET_64BIT && TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
"@
th.extu\t%0,%1,31,0
lwu\t%0,%1"
@@ -106,7 +106,7 @@ (define_insn "*zero_extendsidi2_th_extu"
(define_insn "*zero_extendhi<GPR:mode>2_th_extu"
[(set (match_operand:GPR 0 "register_operand" "=r,r")
(zero_extend:GPR (match_operand:HI 1 "nonimmediate_operand" "r,m")))]
- "TARGET_XTHEADBB"
+ "TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
"@
th.extu\t%0,%1,15,0
lhu\t%0,%1"
@@ -387,4 +387,429 @@ (define_insn "*th_mempair_load_zero_extendsidi2"
(set_attr "mode" "DI")
(set_attr "length" "8")])
+;; XTheadMemIdx
+
+;; Help reload to add a displacement for the base register.
+;; In the case `zext(*(uN*)(base+(zext(rN)<<1)))` LRA splits
+;; off two new instructions: a) `new_base = base + disp`, and
+;; b) `index = zext(rN)<<1`. The index calculation has no
+;; corresponding instruction pattern and needs this insn_and_split
+;; to recover.
+
+(define_insn_and_split "*th_memidx_operand"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (ashift:DI
+ (zero_extend:DI (subreg:SI (match_operand:DI 1 "register_operand" "r") 0))
+ (match_operand 2 "const_int_operand" "n")))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX && lra_in_progress"
+ "#"
+ ""
+ [(set (match_dup 0) (zero_extend:DI (subreg:SI (match_dup 1) 0)))
+ (set (match_dup 0) (ashift:DI (match_dup 0) (match_dup 2)))]
+ ""
+ [(set_attr "type" "bitmanip")])
+
+(define_insn "*th_memidx_zero_extendqi<SUPERQI:mode>2"
+ [(set (match_operand:SUPERQI 0 "register_operand" "=r,r,r,r,r,r")
+ (zero_extend:SUPERQI
+ (match_operand:QI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_XTHEADMEMIDX"
+ "@
+ andi\t%0,%1,0xff
+ th.lbuia\t%0,%1
+ th.lbuib\t%0,%1
+ th.lrbu\t%0,%1
+ th.lurbu\t%0,%1
+ lbu\t%0,%1"
+ [(set_attr "move_type" "andi,load,load,load,load,load")
+ (set_attr "mode" "<SUPERQI:MODE>")])
+
+(define_insn "*th_memidx_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r,r")
+ (sign_extend:DI
+ (match_operand:SI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX"
+ "@
+ sext.w\t%0,%1
+ th.lwia\t%0,%1
+ th.lwib\t%0,%1
+ th.lrw\t%0,%1
+ th.lurw\t%0,%1
+ lw\t%0,%1"
+ [(set_attr "move_type" "move,load,load,load,load,load")
+ (set_attr "mode" "DI")])
+
+;; XTheadMemIdx (without XTheadBb)
+
+(define_insn_and_split "*th_memidx_zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r,r")
+ (zero_extend:DI
+ (match_operand:SI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX && !TARGET_XTHEADBB"
+ "@
+ #
+ th.lwuia\t%0,%1
+ th.lwuib\t%0,%1
+ th.lrwu\t%0,%1
+ th.lurwu\t%0,%1
+ lwu\t%0,%1"
+ "&& reload_completed
+ && REG_P (operands[1])
+ && !paradoxical_subreg_p (operands[0])"
+ [(set (match_dup 0)
+ (ashift:DI (match_dup 1) (const_int 32)))
+ (set (match_dup 0)
+ (lshiftrt:DI (match_dup 0) (const_int 32)))]
+ { operands[1] = gen_lowpart (DImode, operands[1]); }
+ [(set_attr "move_type" "shift_shift,load,load,load,load,load")
+ (set_attr "mode" "DI")])
+
+(define_insn_and_split "*th_memidx_zero_extendhi<GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r")
+ (zero_extend:GPR
+ (match_operand:HI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_XTHEADMEMIDX && !TARGET_XTHEADBB"
+ "@
+ #
+ th.lhuia\t%0,%1
+ th.lhuib\t%0,%1
+ th.lrhu\t%0,%1
+ th.lurhu\t%0,%1
+ lhu\t%0,%1"
+ "&& reload_completed
+ && REG_P (operands[1])
+ && !paradoxical_subreg_p (operands[0])"
+ [(set (match_dup 0)
+ (ashift:GPR (match_dup 1) (match_dup 2)))
+ (set (match_dup 0)
+ (lshiftrt:GPR (match_dup 0) (match_dup 2)))]
+ {
+ operands[1] = gen_lowpart (<GPR:MODE>mode, operands[1]);
+ operands[2] = GEN_INT(GET_MODE_BITSIZE(<GPR:MODE>mode) - 16);
+ }
+ [(set_attr "move_type" "shift_shift,load,load,load,load,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn_and_split "*th_memidx_extend<SHORT:mode><SUPERQI:mode>2"
+ [(set (match_operand:SUPERQI 0 "register_operand" "=r,r,r,r,r,r")
+ (sign_extend:SUPERQI
+ (match_operand:SHORT 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_XTHEADMEMIDX && !TARGET_XTHEADBB"
+ "@
+ #
+ th.l<SHORT:size>ia\t%0,%1
+ th.l<SHORT:size>ib\t%0,%1
+ th.lr<SHORT:size>\t%0,%1
+ th.lur<SHORT:size>\t%0,%1
+ l<SHORT:size>\t%0,%1"
+ "&& reload_completed
+ && REG_P (operands[1])
+ && !paradoxical_subreg_p (operands[0])"
+ [(set (match_dup 0) (ashift:SI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))]
+{
+ operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = GEN_INT (GET_MODE_BITSIZE (SImode)
+ - GET_MODE_BITSIZE (<SHORT:MODE>mode));
+}
+ [(set_attr "move_type" "shift_shift,load,load,load,load,load")
+ (set_attr "mode" "SI")])
+
+;; XTheadMemIdx (with XTheadBb)
+
+(define_insn "*th_memidx_bb_zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r,r")
+ (zero_extend:DI
+ (match_operand:SI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
+ "@
+ th.extu\t%0,%1,31,0
+ th.lwuia\t%0,%1
+ th.lwuib\t%0,%1
+ th.lrwu\t%0,%1
+ th.lurwu\t%0,%1
+ lwu\t%0,%1"
+ [(set_attr "move_type" "shift_shift,load,load,load,load,load")
+ (set_attr "mode" "DI")])
+
+(define_insn "*th_memidx_bb_zero_extendhi<GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r")
+ (zero_extend:GPR
+ (match_operand:HI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
+ "@
+ th.extu\t%0,%1,15,0
+ th.lhuia\t%0,%1
+ th.lhuib\t%0,%1
+ th.lrhu\t%0,%1
+ th.lurhu\t%0,%1
+ lhu\t%0,%1"
+ [(set_attr "move_type" "shift_shift,load,load,load,load,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*th_memidx_bb_extendhi<GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r")
+ (sign_extend:GPR
+ (match_operand:HI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
+ "@
+ th.ext\t%0,%1,15,0
+ th.lhia\t%0,%1
+ th.lhib\t%0,%1
+ th.lrh\t%0,%1
+ th.lurh\t%0,%1
+ lh\t%0,%1"
+ [(set_attr "move_type" "shift_shift,load,load,load,load,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*th_memidx_bb_extendqi<SUPERQI:mode>2"
+ [(set (match_operand:SUPERQI 0 "register_operand" "=r,r,r,r,r,r")
+ (sign_extend:SUPERQI
+ (match_operand:QI 1 "nonimmediate_operand"
+ " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
+ "TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
+ "@
+ th.ext\t%0,%1,7,0
+ th.lbia\t%0,%1
+ th.lbib\t%0,%1
+ th.lrb\t%0,%1
+ th.lurb\t%0,%1
+ lb\t%0,%1"
+ [(set_attr "move_type" "shift_shift,load,load,load,load,load")
+ (set_attr "mode" "<SUPERQI:MODE>")])
+
+(define_mode_iterator TH_M_ANYI [(QI "TARGET_XTHEADMEMIDX")
+ (HI "TARGET_XTHEADMEMIDX")
+ (SI "TARGET_XTHEADMEMIDX")
+ (DI "TARGET_64BIT && TARGET_XTHEADMEMIDX")])
+
+;; All non-extension modes that are supported by XTheadMemIdx
+(define_mode_iterator TH_M_NOEXTI [(SI "!TARGET_64BIT && TARGET_XTHEADMEMIDX")
+ (DI "TARGET_64BIT && TARGET_XTHEADMEMIDX")])
+
+;; XTheadMemIdx optimizations
+;; All optimizations attempt to improve the operand utilization of
+;; XTheadMemIdx instructions, where one sign or zero extended
+;; register-index-operand can be shifted left by a 2-bit immediate.
+;;
+;; The basic idea is the following optimization:
+;; (set (reg 0) (op (reg 1) (imm 2)))
+;; (set (reg 3) (mem (plus (reg 0) (reg 4)))
+;; ==>
+;; (set (reg 3) (mem (plus (reg 4) (op2 (reg 1) (imm 2))))
+;; This optimization only valid if (reg 0) has no further uses.
+;;
+;; The three-instruction case is as follows:
+;; (set (reg 0) (op1 (reg 1) (imm 2)))
+;; (set (reg 3) (op2 (reg 0) (imm 4)))
+;; (set (reg 5) (mem (plus (reg 3) (reg 6)))
+;; ==>
+;; (set (reg 5) (mem (plus (reg 6) (op2 (reg 1) (imm 2/4)))))
+;; This optimization is only valid if (reg 0) and (reg 3) have no further uses.
+;;
+;; The optimization cases are:
+;; I) fold 2-bit ashift of register offset into mem-plus RTX
+;; US) fold 32-bit zero-extended (shift) offset into mem-plus
+;; UZ) fold 32-bit zero-extended (zext) offset into mem-plus
+;;
+;; The first optimization case is targeting the th.lr<MODE> instructions.
+;; The other optimization cases are targeting the th.lur<MODE> instructions
+;; and have to consider two forms of zero-extensions:
+;; - ashift-32 + lshiftrt-{29..32} if there are no zero-extension instructions.
+;; Left-shift amounts of 29..31 indicate a left-shifted zero-extended value.
+;; - zero-extend32 if there are zero-extension instructions (XTheadBb or Zbb).
+;;
+;; We always have three peephole passes per optimization case:
+;; a) no-extended (X) word-load
+;; b) any-extend (SUBX) word-load
+;; c) store
+;;
+;; Note, that SHIFTs will be converted to MULTs during combine.
+
+(define_insn_and_split "*th_memidx_I_a"
+ [(set (match_operand:TH_M_NOEXTI 0 "register_operand" "=r")
+ (mem:TH_M_NOEXTI (plus:X
+ (mult:X (match_operand:X 1 "register_operand" "r")
+ (match_operand:QI 2 "immediate_operand" "i"))
+ (match_operand:X 3 "register_operand" "r"))))]
+ "TARGET_XTHEADMEMIDX
+ && CONST_INT_P (operands[2])
+ && pow2p_hwi (INTVAL (operands[2]))
+ && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
+ "#"
+ "&& 1"
+ [(set (match_dup 0)
+ (mem:TH_M_NOEXTI (plus:X
+ (match_dup 3)
+ (ashift:X (match_dup 1) (match_dup 2)))))]
+ { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
+ }
+)
+
+(define_insn_and_split "*th_memidx_I_b"
+ [(set (match_operand:X 0 "register_operand" "=r")
+ (any_extend:X (mem:SUBX (plus:X
+ (mult:X (match_operand:X 1 "register_operand" "r")
+ (match_operand:QI 2 "immediate_operand" "i"))
+ (match_operand:X 3 "register_operand" "r")))))]
+ "TARGET_XTHEADMEMIDX
+ && CONST_INT_P (operands[2])
+ && pow2p_hwi (INTVAL (operands[2]))
+ && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
+ "#"
+ "&& 1"
+ [(set (match_dup 0)
+ (any_extend:X (mem:SUBX (plus:X
+ (match_dup 3)
+ (ashift:X (match_dup 1) (match_dup 2))))))]
+ { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
+ }
+)
+
+(define_insn_and_split "*th_memidx_I_c"
+ [(set (mem:TH_M_ANYI (plus:X
+ (mult:X (match_operand:X 1 "register_operand" "r")
+ (match_operand:QI 2 "immediate_operand" "i"))
+ (match_operand:X 3 "register_operand" "r")))
+ (match_operand:TH_M_ANYI 0 "register_operand" "r"))]
+ "TARGET_XTHEADMEMIDX
+ && CONST_INT_P (operands[2])
+ && pow2p_hwi (INTVAL (operands[2]))
+ && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
+ "#"
+ "&& 1"
+ [(set (mem:TH_M_ANYI (plus:X
+ (match_dup 3)
+ (ashift:X (match_dup 1) (match_dup 2))))
+ (match_dup 0))]
+ { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
+ }
+)
+
+(define_insn_and_split "*th_memidx_US_a"
+ [(set (match_operand:TH_M_NOEXTI 0 "register_operand" "=r")
+ (mem:TH_M_NOEXTI (plus:DI
+ (and:DI
+ (mult:DI (match_operand:DI 1 "register_operand" "r")
+ (match_operand:QI 2 "immediate_operand" "i"))
+ (match_operand:DI 3 "immediate_operand" "i"))
+ (match_operand:DI 4 "register_operand" "r"))))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX
+ && CONST_INT_P (operands[2])
+ && pow2p_hwi (INTVAL (operands[2]))
+ && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
+ && CONST_INT_P (operands[3])
+ && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
+ "#"
+ "&& 1"
+ [(set (match_dup 0)
+ (mem:TH_M_NOEXTI (plus:DI
+ (match_dup 4)
+ (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2)))))]
+ { operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
+ }
+)
+
+(define_insn_and_split "*th_memidx_US_b"
+ [(set (match_operand:X 0 "register_operand" "=r")
+ (any_extend:X (mem:SUBX (plus:DI
+ (and:DI
+ (mult:DI (match_operand:DI 1 "register_operand" "r")
+ (match_operand:QI 2 "immediate_operand" "i"))
+ (match_operand:DI 3 "immediate_operand" "i"))
+ (match_operand:DI 4 "register_operand" "r")))))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX
+ && CONST_INT_P (operands[2])
+ && pow2p_hwi (INTVAL (operands[2]))
+ && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
+ && CONST_INT_P (operands[3])
+ && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
+ "#"
+ "&& 1"
+ [(set (match_dup 0)
+ (any_extend:X (mem:SUBX (plus:DI
+ (match_dup 4)
+ (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2))))))]
+ { operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
+ }
+)
+
+(define_insn_and_split "*th_memidx_US_c"
+ [(set (mem:TH_M_ANYI (plus:DI
+ (and:DI
+ (mult:DI (match_operand:DI 1 "register_operand" "r")
+ (match_operand:QI 2 "immediate_operand" "i"))
+ (match_operand:DI 3 "immediate_operand" "i"))
+ (match_operand:DI 4 "register_operand" "r")))
+ (match_operand:TH_M_ANYI 0 "register_operand" "r"))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX
+ && CONST_INT_P (operands[2])
+ && pow2p_hwi (INTVAL (operands[2]))
+ && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
+ && CONST_INT_P (operands[3])
+ && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
+ "#"
+ "&& 1"
+ [(set (mem:TH_M_ANYI (plus:DI
+ (match_dup 4)
+ (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2))))
+ (match_dup 0))]
+ { operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
+ }
+)
+
+(define_insn_and_split "*th_memidx_UZ_a"
+ [(set (match_operand:TH_M_NOEXTI 0 "register_operand" "=r")
+ (mem:TH_M_NOEXTI (plus:DI
+ (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
+ (match_operand:DI 2 "register_operand" "r"))))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX"
+ "#"
+ "&& 1"
+ [(set (match_dup 0)
+ (mem:TH_M_NOEXTI (plus:DI
+ (match_dup 2)
+ (zero_extend:DI (match_dup 1)))))]
+)
+
+(define_insn_and_split "*th_memidx_UZ_b"
+ [(set (match_operand:X 0 "register_operand" "=r")
+ (any_extend:X (mem:SUBX (plus:DI
+ (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
+ (match_operand:DI 2 "register_operand" "r")))))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX"
+ "#"
+ "&& 1"
+ [(set (match_dup 0)
+ (any_extend:X (mem:SUBX (plus:DI
+ (match_dup 2)
+ (zero_extend:DI (match_dup 1))))))]
+)
+
+(define_insn_and_split "*th_memidx_UZ_c"
+ [(set (mem:TH_M_ANYI (plus:DI
+ (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
+ (match_operand:DI 2 "register_operand" "r")))
+ (match_operand:TH_M_ANYI 0 "register_operand" "r"))]
+ "TARGET_64BIT && TARGET_XTHEADMEMIDX"
+ "#"
+ "&& 1"
+ [(set (mem:TH_M_ANYI (plus:DI
+ (match_dup 2)
+ (zero_extend:DI (match_dup 1))))
+ (match_dup 0))]
+)
+
(include "thead-peephole.md")
new file mode 100644
@@ -0,0 +1,152 @@
+#ifndef XTHEADMEMIDX_HELPERS_H
+#define XTHEADMEMIDX_HELPERS_H
+
+#include <stdint.h>
+
+#define intX_t long
+#define uintX_t unsigned long
+
+#define PRE_DEC_LOAD(T, N) \
+ void \
+ T ## _pre_dec_load_ ## N (T *p) \
+ { \
+ extern void f ## T ## N (T*, uintX_t); \
+ p = p - N; \
+ T x = *p; \
+ f ## T ## N (p, x); \
+ }
+
+#define PRE_INC_LOAD(T, N) \
+ void \
+ T ## _pre_inc_load_ ## N (T *p) \
+ { \
+ extern void f ## T ## N (T*, uintX_t); \
+ p = p + N; \
+ T x = *p; \
+ f ## T ## N (p, x); \
+ }
+
+#define POST_DEC_LOAD(T, N) \
+ void \
+ T ## _post_dec_load_ ## N (T *p) \
+ { \
+ extern void f ## T ## N (T*, uintX_t); \
+ T x = *p; \
+ p = p - N; \
+ f ## T ## N (p, x); \
+ }
+
+#define POST_INC_LOAD(T,N) \
+ void \
+ T ## _post_inc_load_ ## N (T *p) \
+ { \
+ extern void f ## T ## N (T*,uintX_t); \
+ T x = *p; \
+ p = p + N; \
+ f ## T ## N (p, x); \
+ }
+
+#define PRE_DEC_STORE(T, N) \
+ T * \
+ T ## _pre_dec_store_ ## N (T *p, T v) \
+ { \
+ p = p - N; \
+ *p = v; \
+ return p; \
+ }
+
+#define PRE_INC_STORE(T, N) \
+ T * \
+ T ## _pre_inc_store_ ## N (T *p, T v) \
+ { \
+ p = p + N; \
+ *p = v; \
+ return p; \
+ }
+
+#define POST_DEC_STORE(T, N) \
+ T * \
+ T ## _post_dec_store_ ## N (T *p, T v) \
+ { \
+ *p = v; \
+ p = p - N; \
+ return p; \
+ }
+
+#define POST_INC_STORE(T, N) \
+ T * \
+ T ## _post_inc_store_ ## N (T *p, T v) \
+ { \
+ *p = v; \
+ p = p + N; \
+ return p; \
+ }
+
+#define LR_REG_IMM(T, IMM) \
+ intX_t \
+ lr_reg_imm_ ## T ## _ ## IMM (intX_t rs1, intX_t rs2) \
+ { \
+ return *(T*)(rs1 + (rs2 << IMM)); \
+ }
+
+#define SR_REG_IMM(T, IMM) \
+ void \
+ sr_reg_imm_ ## T ## _ ## IMM (intX_t rs1, intX_t rs2, T val) \
+ { \
+ *(T*)(rs1 + (rs2 << IMM)) = val; \
+ }
+
+#define LR_REG_IMM_UPD(T, IMM) \
+ intX_t \
+ lr_reg_imm_upd_ ## T ## _ ## IMM (intX_t *rs1, intX_t rs2) \
+ { \
+ *rs1 = *rs1 + (rs2 << IMM); \
+ return *(T*)(*rs1); \
+ }
+
+#define SR_REG_IMM_UPD(T, IMM) \
+ void \
+ sr_reg_imm_upd_ ## T ## _ ## IMM (intX_t *rs1, intX_t rs2, T val) \
+ { \
+ *rs1 = *rs1 + (rs2 << IMM); \
+ *(T*)(*rs1) = val; \
+ }
+
+#define LRU_REG_IMM(T, IMM) \
+ intX_t \
+ lru_reg_imm_ ## T ## IMM (intX_t rs1, intX_t rs2) \
+ { \
+ rs2 = (uint32_t)rs2; \
+ return *(T*)(rs1 + (rs2 << IMM)); \
+ }
+
+#define SRU_REG_IMM(T, IMM) \
+ void \
+ sr_reg_imm_ ## T ## _ ## IMM (intX_t rs1, intX_t rs2, T val) \
+ { \
+ rs2 = (uint32_t)rs2; \
+ *(T*)(rs1 + (rs2 << IMM)) = val; \
+ }
+
+#define LRU_REG_IMM_UPD(T, IMM) \
+ intX_t \
+ lru_reg_imm_upd_ ## T ## IMM (intX_t rs1, intX_t *rs2) \
+ { \
+ uintX_t rs2_32 = (uint32_t)*rs2; \
+ intX_t t = rs1 + (rs2_32 << IMM); \
+ intX_t v = *(T*)t; \
+ *rs2 = t; \
+ return v; \
+ }
+
+#define SRU_REG_IMM_UPD(T, IMM) \
+ void \
+ sr_reg_imm_upd_ ## T ## _ ## IMM (intX_t rs1, intX_t *rs2, T val) \
+ { \
+ uintX_t rs2_32 = (uint32_t)*rs2; \
+ intX_t t = rs1 + (rs2_32 << IMM); \
+ *(T*)t = val; \
+ *rs2 = t; \
+ }
+
+#endif /* XTHEADMEMIDX_HELPERS_H */
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LR_REG_IMM_UPD(int8_t, 0)
+LR_REG_IMM_UPD(uint8_t, 1)
+LR_REG_IMM_UPD(int16_t, 2)
+LR_REG_IMM_UPD(uint16_t, 3)
+LR_REG_IMM_UPD(int32_t, 0)
+#if __riscv_xlen == 64
+LR_REG_IMM_UPD(uint32_t, 1)
+LR_REG_IMM_UPD(int64_t, 2)
+#endif
+
+SR_REG_IMM_UPD(int8_t, 3)
+SR_REG_IMM_UPD(int16_t, 0)
+SR_REG_IMM_UPD(int32_t, 1)
+#if __riscv_xlen == 64
+SR_REG_IMM_UPD(int64_t, 2)
+#endif
+
+/* If the shifted value is used later, we cannot eliminate it. */
+/* { dg-final { scan-assembler-times {\mslli\M} 5 { target { rv32 } } } } */
+/* { dg-final { scan-assembler-times {\mslli\M} 8 { target { rv64 } } } } */
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_xtheadbb_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadbb_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LR_REG_IMM_UPD(int8_t, 0)
+LR_REG_IMM_UPD(uint8_t, 1)
+LR_REG_IMM_UPD(int16_t, 2)
+LR_REG_IMM_UPD(uint16_t, 3)
+LR_REG_IMM_UPD(int32_t, 0)
+#if __riscv_xlen == 64
+LR_REG_IMM_UPD(uint32_t, 1)
+LR_REG_IMM_UPD(int64_t, 2)
+#endif
+
+SR_REG_IMM_UPD(int8_t, 3)
+SR_REG_IMM_UPD(int16_t, 0)
+SR_REG_IMM_UPD(int32_t, 1)
+#if __riscv_xlen == 64
+SR_REG_IMM_UPD(int64_t, 2)
+#endif
+
+/* If the shifted value is used later, we cannot eliminate it. */
+/* { dg-final { scan-assembler-times {\mslli\M} 5 { target { rv32 } } } } */
+/* { dg-final { scan-assembler-times {\mslli\M} 8 { target { rv64 } } } } */
new file mode 100644
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_xtheadbb_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadbb_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LR_REG_IMM(int8_t, 0)
+/* { dg-final { scan-assembler-times {\mth.lrb\t[^\n\r]*0\M} 1 } } */
+LR_REG_IMM(uint8_t, 1)
+/* { dg-final { scan-assembler-times {\mth.lrbu\t[^\n\r]*1\M} 1 } } */
+LR_REG_IMM(int16_t, 2)
+/* { dg-final { scan-assembler-times {\mth.lrh\t[^\n\r]*2\M} 1 } } */
+LR_REG_IMM(uint16_t, 3)
+/* { dg-final { scan-assembler-times {\mth.lrhu\t[^\n\r]*3\M} 1 } } */
+LR_REG_IMM(int32_t, 0)
+/* { dg-final { scan-assembler-times {\mth.lrw\t[^\n\r]*0\M} 1 } } */
+#if __riscv_xlen == 64
+LR_REG_IMM(uint32_t, 1)
+/* { dg-final { scan-assembler-times {\mth.lrwu\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+LR_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth.lrd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+SR_REG_IMM(int8_t, 3)
+/* { dg-final { scan-assembler-times {\mth.srb\t[^\n\r]*3\M} 1 } } */
+SR_REG_IMM(int16_t, 0)
+/* { dg-final { scan-assembler-times {\mth.srh\t[^\n\r]*0\M} 1 } } */
+SR_REG_IMM(int32_t, 1)
+/* { dg-final { scan-assembler-times {\mth.srw\t[^\n\r]*1\M} 1 } } */
+#if __riscv_xlen == 64
+SR_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth.srd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+/* { dg-final { scan-assembler-not {\mslli\M} } } */
new file mode 100644
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LR_REG_IMM(int8_t, 0)
+/* { dg-final { scan-assembler-times {\mth.lrb\t[^\n\r]*0\M} 1 } } */
+LR_REG_IMM(uint8_t, 1)
+/* { dg-final { scan-assembler-times {\mth.lrbu\t[^\n\r]*1\M} 1 } } */
+LR_REG_IMM(int16_t, 2)
+/* { dg-final { scan-assembler-times {\mth.lrh\t[^\n\r]*2\M} 1 } } */
+LR_REG_IMM(uint16_t, 3)
+/* { dg-final { scan-assembler-times {\mth.lrhu\t[^\n\r]*3\M} 1 } } */
+LR_REG_IMM(int32_t, 0)
+/* { dg-final { scan-assembler-times {\mth.lrw\t[^\n\r]*0\M} 1 } } */
+#if __riscv_xlen == 64
+LR_REG_IMM(uint32_t, 1)
+/* { dg-final { scan-assembler-times {\mth.lrwu\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+LR_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth.lrd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+SR_REG_IMM(int8_t, 3)
+/* { dg-final { scan-assembler-times {\mth.srb\t[^\n\r]*3\M} 1 } } */
+SR_REG_IMM(int16_t, 0)
+/* { dg-final { scan-assembler-times {\mth.srh\t[^\n\r]*0\M} 1 } } */
+SR_REG_IMM(int32_t, 1)
+/* { dg-final { scan-assembler-times {\mth.srw\t[^\n\r]*1\M} 1 } } */
+#if __riscv_xlen == 64
+SR_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth.srd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+/* { dg-final { scan-assembler-not {\mslli\M} } } */
new file mode 100644
@@ -0,0 +1,74 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+/* { dg-options "-march=rv64gc_xtheadbb_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadbb_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+/* We have a simm5 shifted by a imm2.
+ imm2 | range (simm5 << imm2)
+ 0 | -16..-1,0..15
+ 1 | -32..-2,0..30
+ 2 | -64..-4,0..60
+ 3 | -128..-8,0..120 */
+
+POST_INC_LOAD(int8_t, 1)
+/* { dg-final { scan-assembler {\mth.lbia[^\n\r]*1,0\M} } } */
+PRE_DEC_LOAD(int8_t, 32)
+/* { dg-final { scan-assembler {\mth.lbib[^\n\r]*-16,1\M} } } */
+
+POST_DEC_LOAD(uint8_t, 1)
+/* { dg-final { scan-assembler {\mth.lbuia[^\n\r]*-1,0\M} } } */
+PRE_INC_LOAD(uint8_t, 32)
+/* { dg-final { scan-assembler {\mth.lbuib[^\n\r]*8,2\M} } } */
+
+POST_INC_LOAD(int16_t, 1)
+/* { dg-final { scan-assembler {\mth.lhia[^\n\r]*2,0\M} } } */
+POST_DEC_LOAD(int16_t, 64)
+/* { dg-final { scan-assembler {\mth.lhia[^\n\r]*-16,3\M} } } */
+
+POST_DEC_LOAD(uint16_t, 1)
+/* { dg-final { scan-assembler {\mth.lhuia[^\n\r]*-2,0\M} } } */
+POST_INC_LOAD(uint16_t, 60)
+/* { dg-final { scan-assembler {\mth.lhuia[^\n\r]*15,3\M} } } */
+
+POST_INC_LOAD(int32_t, 1)
+/* { dg-final { scan-assembler {\mth.lwia[^\n\r]*4,0\M} } } */
+PRE_DEC_LOAD(int32_t, 32)
+/* { dg-final { scan-assembler {\mth.lwib[^\n\r]*-16,3\M} } } */
+
+#if __riscv_xlen == 64
+POST_DEC_LOAD(uint32_t, 1)
+/* { dg-final { scan-assembler {\mth.lwuia[^\n\r]*-4,0\M} { target { rv64 } } } } */
+PRE_INC_LOAD(uint32_t, 15)
+/* { dg-final { scan-assembler {\mth.lwuib[^\n\r]*15,2\M} { target { rv64 } } } } */
+
+POST_INC_LOAD(int64_t, 1)
+/* { dg-final { scan-assembler {\mth.ldia[^\n\r]*8,0\M} { target { rv64 } } } } */
+PRE_DEC_LOAD(int64_t, 16)
+/* { dg-final { scan-assembler {\mth.ldib[^\n\r]*-16,3\M} { target { rv64 } } } } */
+#endif
+
+POST_DEC_STORE(int8_t, 1)
+/* { dg-final { scan-assembler {\mth.sbia[^\n\r]*-1,0\M} } } */
+PRE_INC_STORE(int8_t, 120)
+/* { dg-final { scan-assembler {\mth.sbib[^\n\r]*15,3\M} } } */
+
+POST_INC_STORE(int16_t, 1)
+/* { dg-final { scan-assembler {\mth.shia[^\n\r]*2,0\M} } } */
+PRE_DEC_STORE(int16_t, 64)
+/* { dg-final { scan-assembler {\mth.shib[^\n\r]*-16,3\M} } } */
+
+POST_DEC_STORE(int32_t, 1)
+/* { dg-final { scan-assembler {\mth.swia[^\n\r]*-4,0\M} } } */
+PRE_INC_STORE(int32_t, 2)
+/* { dg-final { scan-assembler {\mth.swib[^\n\r]*8,0\M} } } */
+
+#if __riscv_xlen == 64
+POST_INC_STORE(int64_t, 1)
+/* { dg-final { scan-assembler {\mth.sdia[^\n\r]*8,0\M} { target { rv64 } } } } */
+PRE_DEC_STORE(int64_t, 8)
+/* { dg-final { scan-assembler {\mth.sdib[^\n\r]*-16,2\M} { target { rv64 } } } } */
+#endif
+
+/* { dg-final { scan-assembler-not {\m\taddi\M} } } */
new file mode 100644
@@ -0,0 +1,74 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+/* { dg-options "-march=rv64gc_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+/* We have a simm5 shifted by a imm2.
+ imm2 | range (simm5 << imm2)
+ 0 | -16..-1,0..15
+ 1 | -32..-2,0..30
+ 2 | -64..-4,0..60
+ 3 | -128..-8,0..120 */
+
+POST_INC_LOAD(int8_t, 1)
+/* { dg-final { scan-assembler {\mth.lbia[^\n\r]*1,0\M} } } */
+PRE_DEC_LOAD(int8_t, 32)
+/* { dg-final { scan-assembler {\mth.lbib[^\n\r]*-16,1\M} } } */
+
+POST_DEC_LOAD(uint8_t, 1)
+/* { dg-final { scan-assembler {\mth.lbuia[^\n\r]*-1,0\M} } } */
+PRE_INC_LOAD(uint8_t, 32)
+/* { dg-final { scan-assembler {\mth.lbuib[^\n\r]*8,2\M} } } */
+
+POST_INC_LOAD(int16_t, 1)
+/* { dg-final { scan-assembler {\mth.lhia[^\n\r]*2,0\M} } } */
+POST_DEC_LOAD(int16_t, 64)
+/* { dg-final { scan-assembler {\mth.lhia[^\n\r]*-16,3\M} } } */
+
+POST_DEC_LOAD(uint16_t, 1)
+/* { dg-final { scan-assembler {\mth.lhuia[^\n\r]*-2,0\M} } } */
+POST_INC_LOAD(uint16_t, 60)
+/* { dg-final { scan-assembler {\mth.lhuia[^\n\r]*15,3\M} } } */
+
+POST_INC_LOAD(int32_t, 1)
+/* { dg-final { scan-assembler {\mth.lwia[^\n\r]*4,0\M} } } */
+PRE_DEC_LOAD(int32_t, 32)
+/* { dg-final { scan-assembler {\mth.lwib[^\n\r]*-16,3\M} } } */
+
+#if __riscv_xlen == 64
+POST_DEC_LOAD(uint32_t, 1)
+/* { dg-final { scan-assembler {\mth.lwuia[^\n\r]*-4,0\M} { target { rv64 } } } } */
+PRE_INC_LOAD(uint32_t, 15)
+/* { dg-final { scan-assembler {\mth.lwuib[^\n\r]*15,2\M} { target { rv64 } } } } */
+
+POST_INC_LOAD(int64_t, 1)
+/* { dg-final { scan-assembler {\mth.ldia[^\n\r]*8,0\M} { target { rv64 } } } } */
+PRE_DEC_LOAD(int64_t, 16)
+/* { dg-final { scan-assembler {\mth.ldib[^\n\r]*-16,3\M} { target { rv64 } } } } */
+#endif
+
+POST_DEC_STORE(int8_t, 1)
+/* { dg-final { scan-assembler {\mth.sbia[^\n\r]*-1,0\M} } } */
+PRE_INC_STORE(int8_t, 120)
+/* { dg-final { scan-assembler {\mth.sbib[^\n\r]*15,3\M} } } */
+
+POST_INC_STORE(int16_t, 1)
+/* { dg-final { scan-assembler {\mth.shia[^\n\r]*2,0\M} } } */
+PRE_DEC_STORE(int16_t, 64)
+/* { dg-final { scan-assembler {\mth.shib[^\n\r]*-16,3\M} } } */
+
+POST_DEC_STORE(int32_t, 1)
+/* { dg-final { scan-assembler {\mth.swia[^\n\r]*-4,0\M} } } */
+PRE_INC_STORE(int32_t, 2)
+/* { dg-final { scan-assembler {\mth.swib[^\n\r]*8,0\M} } } */
+
+#if __riscv_xlen == 64
+POST_INC_STORE(int64_t, 1)
+/* { dg-final { scan-assembler {\mth.sdia[^\n\r]*8,0\M} { target { rv64 } } } } */
+PRE_DEC_STORE(int64_t, 8)
+/* { dg-final { scan-assembler {\mth.sdib[^\n\r]*-16,2\M} { target { rv64 } } } } */
+#endif
+
+/* { dg-final { scan-assembler-not {\m\taddi\M} } } */
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+/* { dg-options "-march=rv64gc_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LRU_REG_IMM_UPD(int8_t, 0)
+LRU_REG_IMM_UPD(uint8_t, 1)
+LRU_REG_IMM_UPD(int16_t, 2)
+LRU_REG_IMM_UPD(uint16_t, 3)
+LRU_REG_IMM_UPD(int32_t, 0)
+#if __riscv_xlen == 64
+LRU_REG_IMM_UPD(uint32_t, 1)
+LRU_REG_IMM_UPD(int64_t, 2)
+#endif
+
+SRU_REG_IMM_UPD(int8_t, 3)
+SRU_REG_IMM_UPD(int16_t, 0)
+SRU_REG_IMM_UPD(int32_t, 1)
+#if __riscv_xlen == 64
+SRU_REG_IMM_UPD(int64_t, 2)
+#endif
+
+/* If the shifted value is used later, we cannot eliminate it. */
+/* { dg-final { scan-assembler-times {\mslli\M} 5 { target { rv32 } } } } */
+/* { dg-final { scan-assembler-times {\mslli\M} 8 { target { rv64 } } } } */
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_xtheadbb_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadbb_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LRU_REG_IMM_UPD(int8_t, 0)
+LRU_REG_IMM_UPD(uint8_t, 1)
+LRU_REG_IMM_UPD(int16_t, 2)
+LRU_REG_IMM_UPD(uint16_t, 3)
+LRU_REG_IMM_UPD(int32_t, 0)
+#if __riscv_xlen == 64
+LRU_REG_IMM_UPD(uint32_t, 1)
+LRU_REG_IMM_UPD(int64_t, 2)
+#endif
+
+SRU_REG_IMM_UPD(int8_t, 3)
+SRU_REG_IMM_UPD(int16_t, 0)
+SRU_REG_IMM_UPD(int32_t, 1)
+#if __riscv_xlen == 64
+SRU_REG_IMM_UPD(int64_t, 2)
+#endif
+
+/* If the shifted value is used later, we cannot eliminate it. */
+/* { dg-final { scan-assembler-times {\mslli\M} 5 { target { rv32 } } } } */
+/* { dg-final { scan-assembler-times {\mslli\M} 8 { target { rv64 } } } } */
new file mode 100644
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_xtheadbb_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadbb_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LRU_REG_IMM(int8_t, 0)
+/* { dg-final { scan-assembler-times {\mth.lurb\t[^\n\r]*0\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.lrb\t[^\n\r]*0\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(uint8_t, 1)
+/* { dg-final { scan-assembler-times {\mth.lurbu\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.lrbu\t[^\n\r]*1\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(int16_t, 2)
+/* { dg-final { scan-assembler-times {\mth.lurh\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.lrh\t[^\n\r]*2\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(uint16_t, 3)
+/* { dg-final { scan-assembler-times {\mth.lurhu\t[^\n\r]*3\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.lrhu\t[^\n\r]*3\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(int32_t, 0)
+/* { dg-final { scan-assembler-times {\mth.lurw\t[^\n\r]*0\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.lrw\t[^\n\r]*0\M} 1 { target { rv32 } } } } */
+#if __riscv_xlen == 64
+LRU_REG_IMM(uint32_t, 1)
+/* { dg-final { scan-assembler-times {\mth.lurwu\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+LRU_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth.lurd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+SRU_REG_IMM(int8_t, 3)
+/* { dg-final { scan-assembler-times {\mth.surb\t[^\n\r]*3\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.srb\t[^\n\r]*3\M} 1 { target { rv32 } } } } */
+SRU_REG_IMM(int16_t, 0)
+/* { dg-final { scan-assembler-times {\mth.surh\t[^\n\r]*0\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.srh\t[^\n\r]*0\M} 1 { target { rv32 } } } } */
+SRU_REG_IMM(int32_t, 1)
+/* { dg-final { scan-assembler-times {\mth.surw\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth.srw\t[^\n\r]*1\M} 1 { target { rv32 } } } } */
+#if __riscv_xlen == 64
+SRU_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth.surd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+/* { dg-final { scan-assembler-not {\mslli\M} } } */
new file mode 100644
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" } } */
+/* { dg-options "-march=rv64gc_xtheadmemidx" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_xtheadmemidx" { target { rv32 } } } */
+
+#include "xtheadmemidx-helpers.h"
+
+LRU_REG_IMM(int8_t, 0)
+/* { dg-final { scan-assembler-times {\mth\.lurb\t[^\n\r]*0\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.lrb\t[^\n\r]*0\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(uint8_t, 1)
+/* { dg-final { scan-assembler-times {\mth\.lurbu\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.lrbu\t[^\n\r]*1\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(int16_t, 2)
+/* { dg-final { scan-assembler-times {\mth\.lurh\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.lrh\t[^\n\r]*2\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(uint16_t, 3)
+/* { dg-final { scan-assembler-times {\mth\.lurhu\t[^\n\r]*3\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.lrhu\t[^\n\r]*3\M} 1 { target { rv32 } } } } */
+LRU_REG_IMM(int32_t, 0)
+/* { dg-final { scan-assembler-times {\mth\.lurw\t[^\n\r]*0\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.lrw\t[^\n\r]*0\M} 1 { target { rv32 } } } } */
+#if __riscv_xlen == 64
+LRU_REG_IMM(uint32_t, 1)
+/* { dg-final { scan-assembler-times {\mth\.lurwu\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+LRU_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth\.lurd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+SRU_REG_IMM(int8_t, 3)
+/* { dg-final { scan-assembler-times {\mth\.surb\t[^\n\r]*3\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.srb\t[^\n\r]*3\M} 1 { target { rv32 } } } } */
+SRU_REG_IMM(int16_t, 0)
+/* { dg-final { scan-assembler-times {\mth\.surh\t[^\n\r]*0\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.srh\t[^\n\r]*0\M} 1 { target { rv32 } } } } */
+SRU_REG_IMM(int32_t, 1)
+/* { dg-final { scan-assembler-times {\mth\.surw\t[^\n\r]*1\M} 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times {\mth\.srw\t[^\n\r]*1\M} 1 { target { rv32 } } } } */
+#if __riscv_xlen == 64
+SRU_REG_IMM(int64_t, 2)
+/* { dg-final { scan-assembler-times {\mth\.surd\t[^\n\r]*2\M} 1 { target { rv64 } } } } */
+#endif
+
+/* { dg-final { scan-assembler-not {\mslli\M} } } */