[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 33/34] tcg/sparc: Support unaligned access for user-only
From: |
Richard Henderson |
Subject: |
[PULL 33/34] tcg/sparc: Support unaligned access for user-only |
Date: |
Fri, 11 Feb 2022 12:30:58 +1100 |
This is kinda sorta the opposite of the other tcg hosts, where
we get (normal) alignment checks for free with host SIGBUS and
need to add code to support unaligned accesses.
This inline code expansion is somewhat large, but it takes quite
a few instructions to make a function call to a helper anyway.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
tcg/sparc/tcg-target.c.inc | 219 +++++++++++++++++++++++++++++++++++--
1 file changed, 211 insertions(+), 8 deletions(-)
diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc
index 646bb462c3..72d9552fd0 100644
--- a/tcg/sparc/tcg-target.c.inc
+++ b/tcg/sparc/tcg-target.c.inc
@@ -211,6 +211,7 @@ static const int tcg_target_call_oarg_regs[] = {
#define ARITH_ADD (INSN_OP(2) | INSN_OP3(0x00))
#define ARITH_ADDCC (INSN_OP(2) | INSN_OP3(0x10))
#define ARITH_AND (INSN_OP(2) | INSN_OP3(0x01))
+#define ARITH_ANDCC (INSN_OP(2) | INSN_OP3(0x11))
#define ARITH_ANDN (INSN_OP(2) | INSN_OP3(0x05))
#define ARITH_OR (INSN_OP(2) | INSN_OP3(0x02))
#define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12))
@@ -1025,6 +1026,38 @@ static void build_trampolines(TCGContext *s)
tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0);
}
}
+#else
+static const tcg_insn_unit *qemu_unalign_ld_trampoline;
+static const tcg_insn_unit *qemu_unalign_st_trampoline;
+
+static void build_trampolines(TCGContext *s)
+{
+ for (int ld = 0; ld < 2; ++ld) {
+ void *helper;
+
+ while ((uintptr_t)s->code_ptr & 15) {
+ tcg_out_nop(s);
+ }
+
+ if (ld) {
+ helper = helper_unaligned_ld;
+ qemu_unalign_ld_trampoline = tcg_splitwx_to_rx(s->code_ptr);
+ } else {
+ helper = helper_unaligned_st;
+ qemu_unalign_st_trampoline = tcg_splitwx_to_rx(s->code_ptr);
+ }
+
+ if (!SPARC64 && TARGET_LONG_BITS == 64) {
+ /* Install the high part of the address. */
+ tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O2, 32, SHIFT_SRLX);
+ }
+
+ /* Tail call. */
+ tcg_out_jmpl_const(s, helper, true, true);
+ /* delay slot -- set the env argument */
+ tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0);
+ }
+}
#endif
/* Generate global QEMU prologue and epilogue code */
@@ -1075,9 +1108,7 @@ static void tcg_target_qemu_prologue(TCGContext *s)
/* delay slot */
tcg_out_movi_imm13(s, TCG_REG_O0, 0);
-#ifdef CONFIG_SOFTMMU
build_trampolines(s);
-#endif
}
static void tcg_out_nop_fill(tcg_insn_unit *p, int count)
@@ -1162,18 +1193,22 @@ static TCGReg tcg_out_tlb_load(TCGContext *s, TCGReg
addr, int mem_index,
static const int qemu_ld_opc[(MO_SSIZE | MO_BSWAP) + 1] = {
[MO_UB] = LDUB,
[MO_SB] = LDSB,
+ [MO_UB | MO_LE] = LDUB,
+ [MO_SB | MO_LE] = LDSB,
[MO_BEUW] = LDUH,
[MO_BESW] = LDSH,
[MO_BEUL] = LDUW,
[MO_BESL] = LDSW,
[MO_BEUQ] = LDX,
+ [MO_BESQ] = LDX,
[MO_LEUW] = LDUH_LE,
[MO_LESW] = LDSH_LE,
[MO_LEUL] = LDUW_LE,
[MO_LESL] = LDSW_LE,
[MO_LEUQ] = LDX_LE,
+ [MO_LESQ] = LDX_LE,
};
static const int qemu_st_opc[(MO_SIZE | MO_BSWAP) + 1] = {
@@ -1192,11 +1227,12 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data,
TCGReg addr,
MemOpIdx oi, bool is_64)
{
MemOp memop = get_memop(oi);
+ tcg_insn_unit *label_ptr;
+
#ifdef CONFIG_SOFTMMU
unsigned memi = get_mmuidx(oi);
TCGReg addrz, param;
const tcg_insn_unit *func;
- tcg_insn_unit *label_ptr;
addrz = tcg_out_tlb_load(s, addr, memi, memop,
offsetof(CPUTLBEntry, addr_read));
@@ -1260,13 +1296,99 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data,
TCGReg addr,
*label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr));
#else
+ TCGReg index = (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0);
+ unsigned a_bits = get_alignment_bits(memop);
+ unsigned s_bits = memop & MO_SIZE;
+ unsigned t_bits;
+
if (SPARC64 && TARGET_LONG_BITS == 32) {
tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL);
addr = TCG_REG_T1;
}
- tcg_out_ldst_rr(s, data, addr,
- (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0),
+
+ /*
+ * Normal case: alignment equal to access size.
+ */
+ if (a_bits == s_bits) {
+ tcg_out_ldst_rr(s, data, addr, index,
+ qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]);
+ return;
+ }
+
+ /*
+ * Test for at least natural alignment, and assume most accesses
+ * will be aligned -- perform a straight load in the delay slot.
+ * This is required to preserve atomicity for aligned accesses.
+ */
+ t_bits = MAX(a_bits, s_bits);
+ tcg_debug_assert(t_bits < 13);
+ tcg_out_arithi(s, TCG_REG_G0, addr, (1u << t_bits) - 1, ARITH_ANDCC);
+
+ /* beq,a,pt %icc, label */
+ label_ptr = s->code_ptr;
+ tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT | BPCC_ICC, 0);
+ /* delay slot */
+ tcg_out_ldst_rr(s, data, addr, index,
qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]);
+
+ if (a_bits >= s_bits) {
+ /*
+ * Overalignment: A successful alignment test will perform the memory
+ * operation in the delay slot, and failure need only invoke the
+ * handler for SIGBUS.
+ */
+ TCGReg arg_low = TCG_REG_O1 + (!SPARC64 && TARGET_LONG_BITS == 64);
+ tcg_out_call_nodelay(s, qemu_unalign_ld_trampoline, false);
+ /* delay slot -- move to low part of argument reg */
+ tcg_out_mov_delay(s, arg_low, addr);
+ } else {
+ /* Underalignment: load by pieces of minimum alignment. */
+ int ld_opc, a_size, s_size, i;
+
+ /*
+ * Force full address into T1 early; avoids problems with
+ * overlap between @addr and @data.
+ */
+ tcg_out_arith(s, TCG_REG_T1, addr, index, ARITH_ADD);
+
+ a_size = 1 << a_bits;
+ s_size = 1 << s_bits;
+ if ((memop & MO_BSWAP) == MO_BE) {
+ ld_opc = qemu_ld_opc[a_bits | MO_BE | (memop & MO_SIGN)];
+ tcg_out_ldst(s, data, TCG_REG_T1, 0, ld_opc);
+ ld_opc = qemu_ld_opc[a_bits | MO_BE];
+ for (i = a_size; i < s_size; i += a_size) {
+ tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, ld_opc);
+ tcg_out_arithi(s, data, data, a_size, SHIFT_SLLX);
+ tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR);
+ }
+ } else if (a_bits == 0) {
+ ld_opc = LDUB;
+ tcg_out_ldst(s, data, TCG_REG_T1, 0, ld_opc);
+ for (i = a_size; i < s_size; i += a_size) {
+ if ((memop & MO_SIGN) && i == s_size - a_size) {
+ ld_opc = LDSB;
+ }
+ tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, ld_opc);
+ tcg_out_arithi(s, TCG_REG_T2, TCG_REG_T2, i * 8, SHIFT_SLLX);
+ tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR);
+ }
+ } else {
+ ld_opc = qemu_ld_opc[a_bits | MO_LE];
+ tcg_out_ldst_rr(s, data, TCG_REG_T1, TCG_REG_G0, ld_opc);
+ for (i = a_size; i < s_size; i += a_size) {
+ tcg_out_arithi(s, TCG_REG_T1, TCG_REG_T1, a_size, ARITH_ADD);
+ if ((memop & MO_SIGN) && i == s_size - a_size) {
+ ld_opc = qemu_ld_opc[a_bits | MO_LE | MO_SIGN];
+ }
+ tcg_out_ldst_rr(s, TCG_REG_T2, TCG_REG_T1, TCG_REG_G0, ld_opc);
+ tcg_out_arithi(s, TCG_REG_T2, TCG_REG_T2, i * 8, SHIFT_SLLX);
+ tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR);
+ }
+ }
+ }
+
+ *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr));
#endif /* CONFIG_SOFTMMU */
}
@@ -1274,11 +1396,12 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data,
TCGReg addr,
MemOpIdx oi)
{
MemOp memop = get_memop(oi);
+ tcg_insn_unit *label_ptr;
+
#ifdef CONFIG_SOFTMMU
unsigned memi = get_mmuidx(oi);
TCGReg addrz, param;
const tcg_insn_unit *func;
- tcg_insn_unit *label_ptr;
addrz = tcg_out_tlb_load(s, addr, memi, memop,
offsetof(CPUTLBEntry, addr_write));
@@ -1315,13 +1438,93 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data,
TCGReg addr,
*label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr));
#else
+ TCGReg index = (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0);
+ unsigned a_bits = get_alignment_bits(memop);
+ unsigned s_bits = memop & MO_SIZE;
+ unsigned t_bits;
+
if (SPARC64 && TARGET_LONG_BITS == 32) {
tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL);
addr = TCG_REG_T1;
}
- tcg_out_ldst_rr(s, data, addr,
- (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0),
+
+ /*
+ * Normal case: alignment equal to access size.
+ */
+ if (a_bits == s_bits) {
+ tcg_out_ldst_rr(s, data, addr, index,
+ qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]);
+ return;
+ }
+
+ /*
+ * Test for at least natural alignment, and assume most accesses
+ * will be aligned -- perform a straight store in the delay slot.
+ * This is required to preserve atomicity for aligned accesses.
+ */
+ t_bits = MAX(a_bits, s_bits);
+ tcg_debug_assert(t_bits < 13);
+ tcg_out_arithi(s, TCG_REG_G0, addr, (1u << t_bits) - 1, ARITH_ANDCC);
+
+ /* beq,a,pt %icc, label */
+ label_ptr = s->code_ptr;
+ tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT | BPCC_ICC, 0);
+ /* delay slot */
+ tcg_out_ldst_rr(s, data, addr, index,
qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]);
+
+ if (a_bits >= s_bits) {
+ /*
+ * Overalignment: A successful alignment test will perform the memory
+ * operation in the delay slot, and failure need only invoke the
+ * handler for SIGBUS.
+ */
+ TCGReg arg_low = TCG_REG_O1 + (!SPARC64 && TARGET_LONG_BITS == 64);
+ tcg_out_call_nodelay(s, qemu_unalign_st_trampoline, false);
+ /* delay slot -- move to low part of argument reg */
+ tcg_out_mov_delay(s, arg_low, addr);
+ } else {
+ /* Underalignment: store by pieces of minimum alignment. */
+ int st_opc, a_size, s_size, i;
+
+ /*
+ * Force full address into T1 early; avoids problems with
+ * overlap between @addr and @data.
+ */
+ tcg_out_arith(s, TCG_REG_T1, addr, index, ARITH_ADD);
+
+ a_size = 1 << a_bits;
+ s_size = 1 << s_bits;
+ if ((memop & MO_BSWAP) == MO_BE) {
+ st_opc = qemu_st_opc[a_bits | MO_BE];
+ for (i = 0; i < s_size; i += a_size) {
+ TCGReg d = data;
+ int shift = (s_size - a_size - i) * 8;
+ if (shift) {
+ d = TCG_REG_T2;
+ tcg_out_arithi(s, d, data, shift, SHIFT_SRLX);
+ }
+ tcg_out_ldst(s, d, TCG_REG_T1, i, st_opc);
+ }
+ } else if (a_bits == 0) {
+ tcg_out_ldst(s, data, TCG_REG_T1, 0, STB);
+ for (i = 1; i < s_size; i++) {
+ tcg_out_arithi(s, TCG_REG_T2, data, i * 8, SHIFT_SRLX);
+ tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, STB);
+ }
+ } else {
+ /* Note that ST*A with immediate asi must use indexed address. */
+ st_opc = qemu_st_opc[a_bits + MO_LE];
+ tcg_out_ldst_rr(s, data, TCG_REG_T1, TCG_REG_G0, st_opc);
+ for (i = a_size; i < s_size; i += a_size) {
+ tcg_out_arithi(s, TCG_REG_T2, data, i * 8, SHIFT_SRLX);
+ tcg_out_arithi(s, TCG_REG_T1, TCG_REG_T1, a_size, ARITH_ADD);
+ tcg_out_ldst_rr(s, TCG_REG_T2, TCG_REG_T1, TCG_REG_G0, st_opc);
+ }
+ }
+ }
+
+ *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr));
#endif /* CONFIG_SOFTMMU */
}
--
2.25.1
- [PULL 20/34] tcg/arm: Check alignment for ldrd and strd, (continued)
- [PULL 20/34] tcg/arm: Check alignment for ldrd and strd, Richard Henderson, 2022/02/10
- [PULL 22/34] tcg/arm: Reserve a register for guest_base, Richard Henderson, 2022/02/10
- [PULL 23/34] tcg/arm: Support raising sigbus for user-only, Richard Henderson, 2022/02/10
- [PULL 24/34] tcg/mips: Support unaligned access for user-only, Richard Henderson, 2022/02/10
- [PULL 25/34] tcg/mips: Support unaligned access for softmmu, Richard Henderson, 2022/02/10
- [PULL 28/34] tcg/sparc: Add scratch argument to tcg_out_movi_int, Richard Henderson, 2022/02/10
- [PULL 29/34] tcg/sparc: Improve code gen for shifted 32-bit constants, Richard Henderson, 2022/02/10
- [PULL 27/34] tcg/sparc: Split out tcg_out_movi_imm32, Richard Henderson, 2022/02/10
- [PULL 30/34] tcg/sparc: Convert patch_reloc to return bool, Richard Henderson, 2022/02/10
- [PULL 32/34] tcg/sparc: Add tcg_out_jmpl_const for better tail calls, Richard Henderson, 2022/02/10
- [PULL 33/34] tcg/sparc: Support unaligned access for user-only,
Richard Henderson <=
- [PULL 31/34] tcg/sparc: Use the constant pool for 64-bit constants, Richard Henderson, 2022/02/10
- [PULL 26/34] tcg/sparc: Use tcg_out_movi_imm13 in tcg_out_addsub2_i64, Richard Henderson, 2022/02/10
- [PULL 34/34] tests/tcg/multiarch: Add sigbus.c, Richard Henderson, 2022/02/10
- Re: [PULL 00/34] tcg patch queue, Peter Maydell, 2022/02/14