target/ppc: Use atomic cmpxchg for STQCX
When running in a parallel context, we must use a helper in order to perform the 128-bit atomic operation. When running in a serial context, do the compare before the store. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
f89ced5f55
commit
4a9b3c5dd3
@ -807,4 +807,6 @@ DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG,
|
||||
void, env, tl, i64, i64, i32)
|
||||
DEF_HELPER_FLAGS_5(stq_be_parallel, TCG_CALL_NO_WG,
|
||||
void, env, tl, i64, i64, i32)
|
||||
DEF_HELPER_5(stqcx_le_parallel, i32, env, tl, i64, i64, i32)
|
||||
DEF_HELPER_5(stqcx_be_parallel, i32, env, tl, i64, i64, i32)
|
||||
#endif
|
||||
|
@ -245,6 +245,44 @@ void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr,
|
||||
Int128 val = int128_make128(lo, hi);
|
||||
helper_atomic_sto_be_mmu(env, addr, val, opidx, GETPC());
|
||||
}
|
||||
|
||||
uint32_t helper_stqcx_le_parallel(CPUPPCState *env, target_ulong addr,
|
||||
uint64_t new_lo, uint64_t new_hi,
|
||||
uint32_t opidx)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (likely(addr == env->reserve_addr)) {
|
||||
Int128 oldv, cmpv, newv;
|
||||
|
||||
cmpv = int128_make128(env->reserve_val2, env->reserve_val);
|
||||
newv = int128_make128(new_lo, new_hi);
|
||||
oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv,
|
||||
opidx, GETPC());
|
||||
success = int128_eq(oldv, cmpv);
|
||||
}
|
||||
env->reserve_addr = -1;
|
||||
return env->so + success * CRF_EQ_BIT;
|
||||
}
|
||||
|
||||
uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr,
|
||||
uint64_t new_lo, uint64_t new_hi,
|
||||
uint32_t opidx)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
if (likely(addr == env->reserve_addr)) {
|
||||
Int128 oldv, cmpv, newv;
|
||||
|
||||
cmpv = int128_make128(env->reserve_val2, env->reserve_val);
|
||||
newv = int128_make128(new_lo, new_hi);
|
||||
oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv,
|
||||
opidx, GETPC());
|
||||
success = int128_eq(oldv, cmpv);
|
||||
}
|
||||
env->reserve_addr = -1;
|
||||
return env->so + success * CRF_EQ_BIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -3332,50 +3332,77 @@ static void gen_lqarx(DisasContext *ctx)
|
||||
/* stqcx. */
|
||||
static void gen_stqcx_(DisasContext *ctx)
|
||||
{
|
||||
TCGv EA;
|
||||
int reg = rS(ctx->opcode);
|
||||
int len = 16;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
TCGLabel *l1;
|
||||
TCGv gpr1, gpr2;
|
||||
#endif
|
||||
int rs = rS(ctx->opcode);
|
||||
TCGv EA, hi, lo;
|
||||
|
||||
if (unlikely((rD(ctx->opcode) & 1))) {
|
||||
if (unlikely(rs & 1)) {
|
||||
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
gen_set_access_type(ctx, ACCESS_RES);
|
||||
EA = tcg_temp_local_new();
|
||||
EA = tcg_temp_new();
|
||||
gen_addr_reg_index(ctx, EA);
|
||||
if (len > 1) {
|
||||
gen_check_align(ctx, EA, (len) - 1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
gen_conditional_store(ctx, EA, reg, 16);
|
||||
/* Note that the low part is always in RS+1, even in LE mode. */
|
||||
lo = cpu_gpr[rs + 1];
|
||||
hi = cpu_gpr[rs];
|
||||
|
||||
if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
|
||||
TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16);
|
||||
#ifdef CONFIG_ATOMIC128
|
||||
if (ctx->le_mode) {
|
||||
gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi);
|
||||
} else {
|
||||
gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi);
|
||||
}
|
||||
#else
|
||||
tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
|
||||
l1 = gen_new_label();
|
||||
tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1);
|
||||
tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
|
||||
|
||||
if (unlikely(ctx->le_mode)) {
|
||||
gpr1 = cpu_gpr[reg + 1];
|
||||
gpr2 = cpu_gpr[reg];
|
||||
} else {
|
||||
gpr1 = cpu_gpr[reg];
|
||||
gpr2 = cpu_gpr[reg + 1];
|
||||
}
|
||||
tcg_gen_qemu_st_tl(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q));
|
||||
gen_addr_add(ctx, EA, EA, 8);
|
||||
tcg_gen_qemu_st_tl(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q));
|
||||
|
||||
gen_set_label(l1);
|
||||
tcg_gen_movi_tl(cpu_reserve, -1);
|
||||
/* Restart with exclusive lock. */
|
||||
gen_helper_exit_atomic(cpu_env);
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
#endif
|
||||
tcg_temp_free(EA);
|
||||
}
|
||||
tcg_temp_free(EA);
|
||||
tcg_temp_free_i32(oi);
|
||||
} else {
|
||||
TCGLabel *lab_fail = gen_new_label();
|
||||
TCGLabel *lab_over = gen_new_label();
|
||||
TCGv_i64 t0 = tcg_temp_new_i64();
|
||||
TCGv_i64 t1 = tcg_temp_new_i64();
|
||||
|
||||
tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lab_fail);
|
||||
tcg_temp_free(EA);
|
||||
|
||||
gen_qemu_ld64_i64(ctx, t0, cpu_reserve);
|
||||
tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode
|
||||
? offsetof(CPUPPCState, reserve_val2)
|
||||
: offsetof(CPUPPCState, reserve_val)));
|
||||
tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail);
|
||||
|
||||
tcg_gen_addi_i64(t0, cpu_reserve, 8);
|
||||
gen_qemu_ld64_i64(ctx, t0, t0);
|
||||
tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode
|
||||
? offsetof(CPUPPCState, reserve_val)
|
||||
: offsetof(CPUPPCState, reserve_val2)));
|
||||
tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail);
|
||||
|
||||
/* Success */
|
||||
gen_qemu_st64_i64(ctx, ctx->le_mode ? lo : hi, cpu_reserve);
|
||||
tcg_gen_addi_i64(t0, cpu_reserve, 8);
|
||||
gen_qemu_st64_i64(ctx, ctx->le_mode ? hi : lo, t0);
|
||||
|
||||
tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
|
||||
tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
|
||||
tcg_gen_br(lab_over);
|
||||
|
||||
gen_set_label(lab_fail);
|
||||
tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
|
||||
|
||||
gen_set_label(lab_over);
|
||||
tcg_gen_movi_tl(cpu_reserve, -1);
|
||||
tcg_temp_free_i64(t0);
|
||||
tcg_temp_free_i64(t1);
|
||||
}
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
|
||||
/* sync */
|
||||
|
Loading…
Reference in New Issue
Block a user