target/mips: reimplement SC instruction emulation and use cmpxchg

Completely rewrite conditional stores handling. Use cmpxchg.

This eliminates need for separate implementations of SC instruction
emulation for user and system emulation.

Backports commit 33a07fa2db66376e6ee780d4a8b064dc5118cf34 from qemu
This commit is contained in:
Leon Alrae 2019-02-15 17:08:09 -05:00 committed by Lioncash
parent df67716ed5
commit 6099733fc5
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
6 changed files with 45 additions and 118 deletions

View file

@ -875,10 +875,8 @@ struct CPUMIPSState {
/* XXX: Maybe make LLAddr per-TC? */
target_ulong lladdr; /* LL virtual address compared against SC */
target_ulong llval;
target_ulong llnewval;
uint64_t llval_wp;
uint32_t llnewval_wp;
target_ulong llreg;
uint64_t CP0_LLAddr_rw_bitmask;
int CP0_LLAddr_shift;
/*
@ -1158,8 +1156,6 @@ enum {
EXCP_LAST = EXCP_TLBRI,
};
/* Dummy exception for conditional stores. */
#define EXCP_SC 0x100
/*
* This is an internally generated WAKE request line.

View file

@ -1452,10 +1452,8 @@ void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env,
{
CPUState *cs = CPU(mips_env_get_cpu(env));
if (exception < EXCP_SC) {
qemu_log_mask(CPU_LOG_INT, "%s: %d %d\n",
__func__, exception, error_code);
}
cs->exception_index = exception;
env->error_code = error_code;

View file

@ -13,10 +13,8 @@ DEF_HELPER_4(swr, void, env, tl, tl, int)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_3(ll, tl, env, tl, int)
DEF_HELPER_4(sc, tl, env, tl, tl, int)
#ifdef TARGET_MIPS64
DEF_HELPER_3(lld, tl, env, tl, int)
DEF_HELPER_4(scd, tl, env, tl, tl, int)
#endif
#endif

View file

@ -376,31 +376,6 @@ HELPER_LD_ATOMIC(ll, lw, 0x3)
HELPER_LD_ATOMIC(lld, ld, 0x7)
#endif
#undef HELPER_LD_ATOMIC
#define HELPER_ST_ATOMIC(name, ld_insn, st_insn, almask) \
target_ulong helper_##name(CPUMIPSState *env, target_ulong arg1, \
target_ulong arg2, int mem_idx) \
{ \
target_long tmp; \
\
if (arg2 & almask) { \
env->CP0_BadVAddr = arg2; \
do_raise_exception(env, EXCP_AdES, GETPC()); \
} \
if (arg2 == env->lladdr) { \
tmp = do_##ld_insn(env, arg2, mem_idx, GETPC()); \
if (tmp == env->llval) { \
do_##st_insn(env, arg2, arg1, mem_idx, GETPC()); \
return 1; \
} \
} \
return 0; \
}
HELPER_ST_ATOMIC(sc, lw, sw, 0x3)
#ifdef TARGET_MIPS64
HELPER_ST_ATOMIC(scd, ld, sd, 0x7)
#endif
#undef HELPER_ST_ATOMIC
#endif
#ifdef TARGET_WORDS_BIGENDIAN

View file

@ -3333,50 +3333,6 @@ OP_LD_ATOMIC(lld,ld64);
#endif
#undef OP_LD_ATOMIC
#ifdef CONFIG_USER_ONLY
#define OP_ST_ATOMIC(insn,fname,ldname,almask) \
static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, int mem_idx, \
DisasContext *ctx) \
{ \
TCGContext *tcg_ctx = ctx->uc->tcg_ctx; \
TCGv t0 = tcg_temp_new(tcg_ctx); \
TCGLabel *l1 = gen_new_label(tcg_ctx); \
TCGLabel *l2 = gen_new_label(tcg_ctx); \
\
tcg_gen_andi_tl(tcg_ctx, t0, arg2, almask); \
tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_EQ, t0, 0, l1); \
tcg_gen_st_tl(tcg_ctx, arg2, tcg_ctx->cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); \
generate_exception(ctx, EXCP_AdES); \
gen_set_label(tcg_ctx, l1); \
tcg_gen_ld_tl(tcg_ctx, t0, tcg_ctx->cpu_env, offsetof(CPUMIPSState, lladdr)); \
tcg_gen_brcond_tl(tcg_ctx, TCG_COND_NE, arg2, t0, l2); \
tcg_gen_movi_tl(tcg_ctx, t0, rt | ((almask << 3) & 0x20)); \
tcg_gen_st_tl(tcg_ctx, t0, tcg_ctx->cpu_env, offsetof(CPUMIPSState, llreg)); \
tcg_gen_st_tl(tcg_ctx, arg1, tcg_ctx->cpu_env, offsetof(CPUMIPSState, llnewval)); \
generate_exception_end(ctx, EXCP_SC); \
gen_set_label(tcg_ctx, l2); \
tcg_gen_movi_tl(tcg_ctx, t0, 0); \
gen_store_gpr(ctx, t0, rt); \
tcg_temp_free(tcg_ctx, t0); \
}
#else
#define OP_ST_ATOMIC(insn,fname,ldname,almask) \
static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, int mem_idx, \
DisasContext *ctx) \
{ \
TCGContext *tcg_ctx = ctx->uc->tcg_ctx; \
TCGv t0 = tcg_temp_new(tcg_ctx); \
gen_helper_1e2i(tcg_ctx, insn, t0, arg1, arg2, ctx->mem_idx); \
gen_store_gpr(ctx, t0, rt); \
tcg_temp_free(tcg_ctx, t0); \
}
#endif
OP_ST_ATOMIC(sc,st32,ld32s,0x3);
#if defined(TARGET_MIPS64)
OP_ST_ATOMIC(scd,st64,ld64,0x7);
#endif
#undef OP_ST_ATOMIC
static void gen_base_offset_addr (DisasContext *ctx, TCGv addr,
int base, int offset)
{
@ -3693,41 +3649,39 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt,
/* Store conditional */
static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt,
int base, int16_t offset)
static void gen_st_cond(DisasContext *ctx, int rt, int base, int offset,
TCGMemOp tcg_mo, bool eva)
{
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
TCGv t0, t1;
int mem_idx = ctx->mem_idx;
TCGv addr, t0, val;
TCGLabel *l1 = gen_new_label(tcg_ctx);
TCGLabel *done = gen_new_label(tcg_ctx);
#ifdef CONFIG_USER_ONLY
t0 = tcg_temp_local_new(tcg_ctx);
t1 = tcg_temp_local_new(tcg_ctx);
#else
t0 = tcg_temp_new(tcg_ctx);
t1 = tcg_temp_new(tcg_ctx);
#endif
gen_base_offset_addr(ctx, t0, base, offset);
gen_load_gpr(ctx, t1, rt);
switch (opc) {
#if defined(TARGET_MIPS64)
case OPC_SCD:
case R6_OPC_SCD:
op_st_scd(t1, t0, rt, mem_idx, ctx);
break;
#endif
case OPC_SCE:
mem_idx = MIPS_HFLAG_UM;
/* fall through */
case OPC_SC:
case R6_OPC_SC:
op_st_sc(t1, t0, rt, mem_idx, ctx);
break;
}
tcg_temp_free(tcg_ctx, t1);
addr = tcg_temp_new(tcg_ctx);
/* compare the address against that of the preceeding LL */
gen_base_offset_addr(ctx, addr, base, offset);
tcg_gen_brcond_tl(tcg_ctx, TCG_COND_EQ, addr, tcg_ctx->cpu_lladdr, l1);
tcg_temp_free(tcg_ctx, addr);
tcg_gen_movi_tl(tcg_ctx, t0, 0);
gen_store_gpr(ctx, t0, rt);
tcg_gen_br(tcg_ctx, done);
gen_set_label(tcg_ctx, l1);
/* generate cmpxchg */
val = tcg_temp_new(tcg_ctx);
gen_load_gpr(ctx, val, rt);
tcg_gen_atomic_cmpxchg_tl(tcg_ctx, t0, tcg_ctx->cpu_lladdr, tcg_ctx->cpu_llval, val,
eva ? MIPS_HFLAG_UM : ctx->mem_idx, tcg_mo);
tcg_gen_setcond_tl(tcg_ctx, TCG_COND_EQ, t0, t0, tcg_ctx->cpu_llval);
gen_store_gpr(ctx, t0, rt);
tcg_temp_free(tcg_ctx, val);
gen_set_label(tcg_ctx, done);
tcg_temp_free(tcg_ctx, t0);
}
static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset,
uint32_t reg1, uint32_t reg2, bool eva)
{
@ -16967,13 +16921,13 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx)
gen_st(ctx, mips32_op, rt, rs, offset);
break;
case SC:
gen_st_cond(ctx, OPC_SC, rt, rs, offset);
gen_st_cond(ctx, rt, rs, offset, MO_TESL, false);
break;
#if defined(TARGET_MIPS64)
case SCD:
check_insn(ctx, ISA_MIPS3);
check_mips_64(ctx);
gen_st_cond(ctx, OPC_SCD, rt, rs, offset);
gen_st_cond(ctx, rt, rs, offset, MO_TEQ, false);
break;
#endif
case LD_EVA:
@ -17054,7 +17008,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx)
mips32_op = OPC_SHE;
goto do_st_lr;
case SCE:
gen_st_cond(ctx, OPC_SCE, rt, rs, offset);
gen_st_cond(ctx, rt, rs, offset, MO_TESL, true);
break;
case SWE:
mips32_op = OPC_SWE;
@ -21691,7 +21645,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx)
case NM_P_SC:
switch (ctx->opcode & 0x03) {
case NM_SC:
gen_st_cond(ctx, OPC_SC, rt, rs, s);
gen_st_cond(ctx, rt, rs, s, MO_TESL, false);
break;
case NM_SCWP:
check_xnp(ctx);
@ -21794,7 +21748,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx)
check_xnp(ctx);
check_eva(ctx);
check_cp0_enabled(ctx);
gen_st_cond(ctx, OPC_SCE, rt, rs, s);
gen_st_cond(ctx, rt, rs, s, MO_TESL, true);
break;
case NM_SCWPE:
check_xnp(ctx);
@ -26852,7 +26806,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx)
}
break;
case R6_OPC_SC:
gen_st_cond(ctx, op1, rt, rs, imm);
gen_st_cond(ctx, rt, rs, imm, MO_TESL, false);
break;
case R6_OPC_LL:
gen_ld(ctx, op1, rt, rs, imm);
@ -26879,7 +26833,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx)
break;
#if defined(TARGET_MIPS64)
case R6_OPC_SCD:
gen_st_cond(ctx, op1, rt, rs, imm);
gen_st_cond(ctx, rt, rs, imm, MO_TEQ, false);
break;
case R6_OPC_LLD:
gen_ld(ctx, op1, rt, rs, imm);
@ -27737,7 +27691,7 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx)
return;
case OPC_SCE:
check_cp0_enabled(ctx);
gen_st_cond(ctx, op1, rt, rs, imm);
gen_st_cond(ctx, rt, rs, imm, MO_TESL, true);
return;
case OPC_CACHEE:
check_cp0_enabled(ctx);
@ -29359,7 +29313,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx, bool *insn_need_pat
if (ctx->insn_flags & INSN_R5900) {
check_insn_opc_user_only(ctx, INSN_R5900);
}
gen_st_cond(ctx, op, rt, rs, imm);
gen_st_cond(ctx, rt, rs, imm, MO_TESL, false);
break;
case OPC_CACHE:
check_insn_opc_removed(ctx, ISA_MIPS32R6);
@ -29659,7 +29613,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx, bool *insn_need_pat
check_insn_opc_user_only(ctx, INSN_R5900);
}
check_mips_64(ctx);
gen_st_cond(ctx, op, rt, rs, imm);
gen_st_cond(ctx, rt, rs, imm, MO_TEQ, false);
break;
case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC, OPC_DADDI */
if (ctx->insn_flags & ISA_MIPS32R6) {
@ -30037,6 +29991,11 @@ void mips_tcg_init(struct uc_struct *uc)
offsetof(CPUMIPSState, active_fpu.fcr31),
"fcr31");
tcg_ctx->cpu_lladdr = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPUMIPSState, lladdr),
"lladdr");
tcg_ctx->cpu_llval = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPUMIPSState, llval),
"llval");
#if defined(TARGET_MIPS64)
tcg_ctx->cpu_mmr[0] = NULL;
for (i = 1; i < 32; i++) {

View file

@ -903,6 +903,7 @@ struct TCGContext {
TCGv cpu_PC;
TCGv cpu_HI[4], cpu_LO[4]; // MIPS_DSP_ACC = 4 in qemu/target-mips/cpu.h
TCGv cpu_dspctrl;
TCGv cpu_lladdr, cpu_llval;
TCGv btarget;
TCGv bcond;
TCGv_i32 hflags;