target-sparc: Implement ldqf and stqf inline

At the same time, fix a problem with stqf_asi, when
a write might access two pages.

Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Richard Henderson 2016-07-14 11:47:08 -07:00
parent 918d9a2c9d
commit f939ffe5a0
3 changed files with 67 additions and 111 deletions

View File

@ -38,8 +38,6 @@ DEF_HELPER_3(tsubcctv, tl, env, tl, tl)
DEF_HELPER_FLAGS_3(sdivx, TCG_CALL_NO_WG, s64, env, s64, s64) DEF_HELPER_FLAGS_3(sdivx, TCG_CALL_NO_WG, s64, env, s64, s64)
DEF_HELPER_FLAGS_3(udivx, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(udivx, TCG_CALL_NO_WG, i64, env, i64, i64)
#endif #endif
DEF_HELPER_FLAGS_3(ldqf, TCG_CALL_NO_WG, void, env, tl, int)
DEF_HELPER_FLAGS_3(stqf, TCG_CALL_NO_WG, void, env, tl, int)
#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32) DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32)
DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32) DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32)

View File

@ -254,18 +254,6 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb,
#endif #endif
#if defined(TARGET_SPARC64) || defined(CONFIG_USER_ONLY)
static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr)
{
#ifdef TARGET_SPARC64
if (AM_CHECK(env1)) {
addr &= 0xffffffffULL;
}
#endif
return addr;
}
#endif
#ifdef TARGET_SPARC64 #ifdef TARGET_SPARC64
/* returns true if access using this ASI is to have address translated by MMU /* returns true if access using this ASI is to have address translated by MMU
otherwise access is to raw physical address */ otherwise access is to raw physical address */
@ -290,14 +278,21 @@ static inline int is_translating_asi(int asi)
} }
} }
static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr)
{
if (AM_CHECK(env1)) {
addr &= 0xffffffffULL;
}
return addr;
}
static inline target_ulong asi_address_mask(CPUSPARCState *env, static inline target_ulong asi_address_mask(CPUSPARCState *env,
int asi, target_ulong addr) int asi, target_ulong addr)
{ {
if (is_translating_asi(asi)) { if (is_translating_asi(asi)) {
return address_mask(env, addr); addr = address_mask(env, addr);
} else {
return addr;
} }
return addr;
} }
#endif #endif
@ -1601,78 +1596,6 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
#endif /* CONFIG_USER_ONLY */ #endif /* CONFIG_USER_ONLY */
#endif /* TARGET_SPARC64 */ #endif /* TARGET_SPARC64 */
void helper_ldqf(CPUSPARCState *env, target_ulong addr, int mem_idx)
{
/* XXX add 128 bit load */
CPU_QuadU u;
do_check_align(env, addr, 7, GETPC());
#if !defined(CONFIG_USER_ONLY)
switch (mem_idx) {
case MMU_USER_IDX:
u.ll.upper = cpu_ldq_user(env, addr);
u.ll.lower = cpu_ldq_user(env, addr + 8);
QT0 = u.q;
break;
case MMU_KERNEL_IDX:
u.ll.upper = cpu_ldq_kernel(env, addr);
u.ll.lower = cpu_ldq_kernel(env, addr + 8);
QT0 = u.q;
break;
#ifdef TARGET_SPARC64
case MMU_HYPV_IDX:
u.ll.upper = cpu_ldq_hypv(env, addr);
u.ll.lower = cpu_ldq_hypv(env, addr + 8);
QT0 = u.q;
break;
#endif
default:
DPRINTF_MMU("helper_ldqf: need to check MMU idx %d\n", mem_idx);
break;
}
#else
u.ll.upper = cpu_ldq_data(env, address_mask(env, addr));
u.ll.lower = cpu_ldq_data(env, address_mask(env, addr + 8));
QT0 = u.q;
#endif
}
void helper_stqf(CPUSPARCState *env, target_ulong addr, int mem_idx)
{
/* XXX add 128 bit store */
CPU_QuadU u;
do_check_align(env, addr, 7, GETPC());
#if !defined(CONFIG_USER_ONLY)
switch (mem_idx) {
case MMU_USER_IDX:
u.q = QT0;
cpu_stq_user(env, addr, u.ll.upper);
cpu_stq_user(env, addr + 8, u.ll.lower);
break;
case MMU_KERNEL_IDX:
u.q = QT0;
cpu_stq_kernel(env, addr, u.ll.upper);
cpu_stq_kernel(env, addr + 8, u.ll.lower);
break;
#ifdef TARGET_SPARC64
case MMU_HYPV_IDX:
u.q = QT0;
cpu_stq_hypv(env, addr, u.ll.upper);
cpu_stq_hypv(env, addr + 8, u.ll.lower);
break;
#endif
default:
DPRINTF_MMU("helper_stqf: need to check MMU idx %d\n", mem_idx);
break;
}
#else
u.q = QT0;
cpu_stq_data(env, address_mask(env, addr), u.ll.upper);
cpu_stq_data(env, address_mask(env, addr + 8), u.ll.lower);
#endif
}
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
#ifndef TARGET_SPARC64 #ifndef TARGET_SPARC64
void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr,

View File

@ -242,7 +242,29 @@ static void gen_op_store_QT0_fpr(unsigned int dst)
offsetof(CPU_QuadU, ll.lower)); offsetof(CPU_QuadU, ll.lower));
} }
static void gen_store_fpr_Q(DisasContext *dc, unsigned int dst,
TCGv_i64 v1, TCGv_i64 v2)
{
dst = QFPREG(dst);
tcg_gen_mov_i64(cpu_fpr[dst / 2], v1);
tcg_gen_mov_i64(cpu_fpr[dst / 2 + 1], v2);
gen_update_fprs_dirty(dc, dst);
}
#ifdef TARGET_SPARC64 #ifdef TARGET_SPARC64
static TCGv_i64 gen_load_fpr_Q0(DisasContext *dc, unsigned int src)
{
src = QFPREG(src);
return cpu_fpr[src / 2];
}
static TCGv_i64 gen_load_fpr_Q1(DisasContext *dc, unsigned int src)
{
src = QFPREG(src);
return cpu_fpr[src / 2 + 1];
}
static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs) static void gen_move_Q(DisasContext *dc, unsigned int rd, unsigned int rs)
{ {
rd = QFPREG(rd); rd = QFPREG(rd);
@ -2557,10 +2579,17 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr,
tcg_gen_qemu_st_i32(d32, addr, da.mem_idx, da.memop); tcg_gen_qemu_st_i32(d32, addr, da.mem_idx, da.memop);
break; break;
case 8: case 8:
/* ??? Only 4-byte alignment required. However, it is legal
for the cpu to signal the alignment fault, and the OS trap
handler is required to fix it up. */
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop);
break; break;
case 16: case 16:
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop); /* Only 4-byte alignment required. See above. Requiring
16-byte alignment here avoids having to probe the second
page before performing the first write. */
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx,
da.memop | MO_ALIGN_16);
tcg_gen_addi_tl(addr, addr, 8); tcg_gen_addi_tl(addr, addr, 8);
tcg_gen_qemu_st_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop); tcg_gen_qemu_st_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop);
break; break;
@ -5426,17 +5455,16 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
gen_helper_ldfsr(cpu_fsr, cpu_env, cpu_fsr, cpu_dst_32); gen_helper_ldfsr(cpu_fsr, cpu_env, cpu_fsr, cpu_dst_32);
break; break;
case 0x22: /* ldqf, load quad fpreg */ case 0x22: /* ldqf, load quad fpreg */
{ CHECK_FPU_FEATURE(dc, FLOAT128);
TCGv_i32 r_const; gen_address_mask(dc, cpu_addr);
cpu_src1_64 = tcg_temp_new_i64();
CHECK_FPU_FEATURE(dc, FLOAT128); tcg_gen_qemu_ld64(cpu_src1_64, cpu_addr, dc->mem_idx);
r_const = tcg_const_i32(dc->mem_idx); tcg_gen_addi_tl(cpu_addr, cpu_addr, 8);
gen_address_mask(dc, cpu_addr); cpu_src2_64 = tcg_temp_new_i64();
gen_helper_ldqf(cpu_env, cpu_addr, r_const); tcg_gen_qemu_ld64(cpu_src2_64, cpu_addr, dc->mem_idx);
tcg_temp_free_i32(r_const); gen_store_fpr_Q(dc, rd, cpu_src1_64, cpu_src2_64);
gen_op_store_QT0_fpr(QFPREG(rd)); tcg_temp_free_i64(cpu_src1_64);
gen_update_fprs_dirty(dc, QFPREG(rd)); tcg_temp_free_i64(cpu_src2_64);
}
break; break;
case 0x23: /* lddf, load double fpreg */ case 0x23: /* lddf, load double fpreg */
gen_address_mask(dc, cpu_addr); gen_address_mask(dc, cpu_addr);
@ -5537,16 +5565,20 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
case 0x26: case 0x26:
#ifdef TARGET_SPARC64 #ifdef TARGET_SPARC64
/* V9 stqf, store quad fpreg */ /* V9 stqf, store quad fpreg */
{ CHECK_FPU_FEATURE(dc, FLOAT128);
TCGv_i32 r_const; gen_address_mask(dc, cpu_addr);
/* ??? While stqf only requires 4-byte alignment, it is
CHECK_FPU_FEATURE(dc, FLOAT128); legal for the cpu to signal the unaligned exception.
gen_op_load_fpr_QT0(QFPREG(rd)); The OS trap handler is then required to fix it up.
r_const = tcg_const_i32(dc->mem_idx); For qemu, this avoids having to probe the second page
gen_address_mask(dc, cpu_addr); before performing the first write. */
gen_helper_stqf(cpu_env, cpu_addr, r_const); cpu_src1_64 = gen_load_fpr_Q0(dc, rd);
tcg_temp_free_i32(r_const); tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr,
} dc->mem_idx, MO_TEQ | MO_ALIGN_16);
tcg_gen_addi_tl(cpu_addr, cpu_addr, 8);
cpu_src2_64 = gen_load_fpr_Q1(dc, rd);
tcg_gen_qemu_st_i64(cpu_src1_64, cpu_addr,
dc->mem_idx, MO_TEQ);
break; break;
#else /* !TARGET_SPARC64 */ #else /* !TARGET_SPARC64 */
/* stdfq, store floating point queue */ /* stdfq, store floating point queue */
@ -5562,6 +5594,9 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
#endif #endif
#endif #endif
case 0x27: /* stdf, store double fpreg */ case 0x27: /* stdf, store double fpreg */
/* ??? Only 4-byte alignment required. However, it is
legal for the cpu to signal the alignment fault, and
the OS trap handler is required to fix it up. */
gen_address_mask(dc, cpu_addr); gen_address_mask(dc, cpu_addr);
cpu_src1_64 = gen_load_fpr_D(dc, rd); cpu_src1_64 = gen_load_fpr_D(dc, rd);
tcg_gen_qemu_st64(cpu_src1_64, cpu_addr, dc->mem_idx); tcg_gen_qemu_st64(cpu_src1_64, cpu_addr, dc->mem_idx);