Commit 303a9ab8 authored by Richard Henderson's avatar Richard Henderson
Browse files

target/s390x: Use atomic operations for COMPARE SWAP

parent 1807aaa5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ DEF_HELPER_3(cxgb, i64, env, s64, i32)
DEF_HELPER_3(celgb, i64, env, i64, i32)
DEF_HELPER_3(cdlgb, i64, env, i64, i32)
DEF_HELPER_3(cxlgb, i64, env, i64, i32)
DEF_HELPER_4(cdsg, void, env, i64, i32, i32)
DEF_HELPER_FLAGS_3(aeb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(adb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_5(axb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64)
+5 −5
Original line number Diff line number Diff line
@@ -239,12 +239,12 @@
    D(0xec7d, CLGIJ,   RIE_c, GIE, r1_o, i2_8u, 0, 0, cj, 0, 1)

/* COMPARE AND SWAP */
    D(0xba00, CS,      RS_a,  Z,   r3_32u, r1_32u, new, r1_32, cs, 0, 0)
    D(0xeb14, CSY,     RSY_a, LD,  r3_32u, r1_32u, new, r1_32, cs, 0, 0)
    D(0xeb30, CSG,     RSY_a, Z,   r3_o, r1_o, new, r1, cs, 0, 1)
    D(0xba00, CS,      RS_a,  Z,   r3_32u, r1_32u, new, r1_32, cs, 0, MO_TEUL)
    D(0xeb14, CSY,     RSY_a, LD,  r3_32u, r1_32u, new, r1_32, cs, 0, MO_TEUL)
    D(0xeb30, CSG,     RSY_a, Z,   r3_o, r1_o, new, r1, cs, 0, MO_TEQ)
/* COMPARE DOUBLE AND SWAP */
    D(0xbb00, CDS,     RS_a,  Z,   r3_D32, r1_D32, new, r1_D32, cs, 0, 1)
    D(0xeb31, CDSY,    RSY_a, LD,  r3_D32, r1_D32, new, r1_D32, cs, 0, 1)
    D(0xbb00, CDS,     RS_a,  Z,   r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ)
    D(0xeb31, CDSY,    RSY_a, LD,  r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ)
    C(0xeb3e, CDSG,    RSY_a, Z,   0, 0, 0, 0, cdsg, 0)

/* COMPARE AND TRAP */
+40 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
#include "qemu/int128.h"

#if !defined(CONFIG_USER_ONLY)
#include "hw/s390x/storage-keys.h"
@@ -844,6 +845,45 @@ uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
    return cc;
}

void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
                  uint32_t r1, uint32_t r3)
{
    uintptr_t ra = GETPC();
    Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]);
    Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
    Int128 oldv;
    bool fail;

    if (parallel_cpus) {
#ifndef CONFIG_ATOMIC128
        cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
#else
        int mem_idx = cpu_mmu_index(env, false);
        TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
        oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra);
        fail = !int128_eq(oldv, cmpv);
#endif
    } else {
        uint64_t oldh, oldl;

        oldh = cpu_ldq_data_ra(env, addr + 0, ra);
        oldl = cpu_ldq_data_ra(env, addr + 8, ra);

        oldv = int128_make128(oldl, oldh);
        fail = !int128_eq(oldv, cmpv);
        if (fail) {
            newv = oldv;
        }

        cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra);
        cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra);
    }

    env->cc_op = fail;
    env->regs[r1] = int128_gethi(oldv);
    env->regs[r1 + 1] = int128_getlo(oldv);
}

#if !defined(CONFIG_USER_ONLY)
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
+14 −69
Original line number Diff line number Diff line
@@ -1943,102 +1943,47 @@ static ExitStatus op_cps(DisasContext *s, DisasOps *o)

static ExitStatus op_cs(DisasContext *s, DisasOps *o)
{
    /* FIXME: needs an atomic solution for CONFIG_USER_ONLY.  */
    int d2 = get_field(s->fields, d2);
    int b2 = get_field(s->fields, b2);
    int is_64 = s->insn->data;
    TCGv_i64 addr, mem, cc, z;
    TCGv_i64 addr, cc;

    /* Note that in1 = R3 (new value) and
       in2 = (zero-extended) R1 (expected value).  */

    /* Load the memory into the (temporary) output.  While the PoO only talks
       about moving the memory to R1 on inequality, if we include equality it
       means that R1 is equal to the memory in all conditions.  */
    addr = get_address(s, 0, b2, d2);
    if (is_64) {
        tcg_gen_qemu_ld64(o->out, addr, get_mem_index(s));
    } else {
        tcg_gen_qemu_ld32u(o->out, addr, get_mem_index(s));
    }
    tcg_gen_atomic_cmpxchg_i64(o->out, addr, o->in2, o->in1,
                               get_mem_index(s), s->insn->data | MO_ALIGN);
    tcg_temp_free_i64(addr);

    /* Are the memory and expected values (un)equal?  Note that this setcond
       produces the output CC value, thus the NE sense of the test.  */
    cc = tcg_temp_new_i64();
    tcg_gen_setcond_i64(TCG_COND_NE, cc, o->in2, o->out);

    /* If the memory and expected values are equal (CC==0), copy R3 to MEM.
       Recall that we are allowed to unconditionally issue the store (and
       thus any possible write trap), so (re-)store the original contents
       of MEM in case of inequality.  */
    z = tcg_const_i64(0);
    mem = tcg_temp_new_i64();
    tcg_gen_movcond_i64(TCG_COND_EQ, mem, cc, z, o->in1, o->out);
    if (is_64) {
        tcg_gen_qemu_st64(mem, addr, get_mem_index(s));
    } else {
        tcg_gen_qemu_st32(mem, addr, get_mem_index(s));
    }
    tcg_temp_free_i64(z);
    tcg_temp_free_i64(mem);
    tcg_temp_free_i64(addr);

    /* Store CC back to cc_op.  Wait until after the store so that any
       exception gets the old cc_op value.  */
    tcg_gen_extrl_i64_i32(cc_op, cc);
    tcg_temp_free_i64(cc);
    set_cc_static(s);

    return NO_EXIT;
}

static ExitStatus op_cdsg(DisasContext *s, DisasOps *o)
{
    /* FIXME: needs an atomic solution for CONFIG_USER_ONLY.  */
    int r1 = get_field(s->fields, r1);
    int r3 = get_field(s->fields, r3);
    int d2 = get_field(s->fields, d2);
    int b2 = get_field(s->fields, b2);
    TCGv_i64 addrh, addrl, memh, meml, outh, outl, cc, z;
    TCGv_i64 addr;
    TCGv_i32 t_r1, t_r3;

    /* Note that R1:R1+1 = expected value and R3:R3+1 = new value.  */
    addr = get_address(s, 0, b2, d2);
    t_r1 = tcg_const_i32(r1);
    t_r3 = tcg_const_i32(r3);
    gen_helper_cdsg(cpu_env, addr, t_r1, t_r3);
    tcg_temp_free_i64(addr);
    tcg_temp_free_i32(t_r1);
    tcg_temp_free_i32(t_r3);

    addrh = get_address(s, 0, b2, d2);
    addrl = get_address(s, 0, b2, d2 + 8);
    outh = tcg_temp_new_i64();
    outl = tcg_temp_new_i64();

    tcg_gen_qemu_ld64(outh, addrh, get_mem_index(s));
    tcg_gen_qemu_ld64(outl, addrl, get_mem_index(s));

    /* Fold the double-word compare with arithmetic.  */
    cc = tcg_temp_new_i64();
    z = tcg_temp_new_i64();
    tcg_gen_xor_i64(cc, outh, regs[r1]);
    tcg_gen_xor_i64(z, outl, regs[r1 + 1]);
    tcg_gen_or_i64(cc, cc, z);
    tcg_gen_movi_i64(z, 0);
    tcg_gen_setcond_i64(TCG_COND_NE, cc, cc, z);

    memh = tcg_temp_new_i64();
    meml = tcg_temp_new_i64();
    tcg_gen_movcond_i64(TCG_COND_EQ, memh, cc, z, regs[r3], outh);
    tcg_gen_movcond_i64(TCG_COND_EQ, meml, cc, z, regs[r3 + 1], outl);
    tcg_temp_free_i64(z);

    tcg_gen_qemu_st64(memh, addrh, get_mem_index(s));
    tcg_gen_qemu_st64(meml, addrl, get_mem_index(s));
    tcg_temp_free_i64(memh);
    tcg_temp_free_i64(meml);
    tcg_temp_free_i64(addrh);
    tcg_temp_free_i64(addrl);

    /* Save back state now that we've passed all exceptions.  */
    tcg_gen_mov_i64(regs[r1], outh);
    tcg_gen_mov_i64(regs[r1 + 1], outl);
    tcg_gen_extrl_i64_i32(cc_op, cc);
    tcg_temp_free_i64(outh);
    tcg_temp_free_i64(outl);
    tcg_temp_free_i64(cc);
    set_cc_static(s);
    return NO_EXIT;
}