Commit e4dc0052 authored by Richard Henderson's avatar Richard Henderson
Browse files

target-sparc: Directly implement easy ldd/std asis

parent 35e94905
Loading
Loading
Loading
Loading
+103 −12
Original line number Diff line number Diff line
@@ -1988,6 +1988,7 @@ typedef enum {
    GET_ASI_HELPER,
    GET_ASI_EXCP,
    GET_ASI_DIRECT,
    GET_ASI_DTWINX,
} ASIType;

typedef struct {
@@ -2046,18 +2047,26 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop)
        switch (asi) {
        case ASI_N:  /* Nucleus */
        case ASI_NL: /* Nucleus LE */
        case ASI_TWINX_N:
        case ASI_TWINX_NL:
            mem_idx = MMU_NUCLEUS_IDX;
            break;
        case ASI_AIUP:  /* As if user primary */
        case ASI_AIUPL: /* As if user primary LE */
        case ASI_TWINX_AIUP:
        case ASI_TWINX_AIUP_L:
            mem_idx = MMU_USER_IDX;
            break;
        case ASI_AIUS:  /* As if user secondary */
        case ASI_AIUSL: /* As if user secondary LE */
        case ASI_TWINX_AIUS:
        case ASI_TWINX_AIUS_L:
            mem_idx = MMU_USER_SECONDARY_IDX;
            break;
        case ASI_S:  /* Secondary */
        case ASI_SL: /* Secondary LE */
        case ASI_TWINX_S:
        case ASI_TWINX_SL:
            if (mem_idx == MMU_USER_IDX) {
                mem_idx = MMU_USER_SECONDARY_IDX;
            } else if (mem_idx == MMU_KERNEL_IDX) {
@@ -2066,6 +2075,8 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop)
            break;
        case ASI_P:  /* Primary */
        case ASI_PL: /* Primary LE */
        case ASI_TWINX_P:
        case ASI_TWINX_PL:
            break;
        }
        switch (asi) {
@@ -2081,6 +2092,18 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop)
        case ASI_PL:
            type = GET_ASI_DIRECT;
            break;
        case ASI_TWINX_N:
        case ASI_TWINX_NL:
        case ASI_TWINX_AIUP:
        case ASI_TWINX_AIUP_L:
        case ASI_TWINX_AIUS:
        case ASI_TWINX_AIUS_L:
        case ASI_TWINX_P:
        case ASI_TWINX_PL:
        case ASI_TWINX_S:
        case ASI_TWINX_SL:
            type = GET_ASI_DTWINX;
            break;
        }
        /* The little-endian asis all have bit 3 set.  */
        if (asi & 8) {
@@ -2100,6 +2123,9 @@ static void gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr,
    switch (da.type) {
    case GET_ASI_EXCP:
        break;
    case GET_ASI_DTWINX: /* Reserved for ldda.  */
        gen_exception(dc, TT_ILL_INSN);
        break;
    case GET_ASI_DIRECT:
        gen_address_mask(dc, addr);
        tcg_gen_qemu_ld_tl(dst, addr, da.mem_idx, da.memop);
@@ -2137,6 +2163,9 @@ static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr,
    switch (da.type) {
    case GET_ASI_EXCP:
        break;
    case GET_ASI_DTWINX: /* Reserved for stda.  */
        gen_exception(dc, TT_ILL_INSN);
        break;
    case GET_ASI_DIRECT:
        gen_address_mask(dc, addr);
        tcg_gen_qemu_st_tl(src, addr, da.mem_idx, da.memop);
@@ -2303,32 +2332,59 @@ static void gen_stf_asi(DisasContext *dc, TCGv addr,
    }
}

static void gen_ldda_asi(DisasContext *dc, TCGv hi, TCGv addr,
                         int insn, int rd)
static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd)
{
    DisasASI da = get_asi(dc, insn, MO_TEQ);
    TCGv_i64 hi = gen_dest_gpr(dc, rd);
    TCGv_i64 lo = gen_dest_gpr(dc, rd + 1);

    switch (da.type) {
    case GET_ASI_EXCP:
        return;

    case GET_ASI_DTWINX:
        gen_check_align(addr, 15);
        gen_address_mask(dc, addr);
        tcg_gen_qemu_ld_i64(hi, addr, da.mem_idx, da.memop);
        tcg_gen_addi_tl(addr, addr, 8);
        tcg_gen_qemu_ld_i64(lo, addr, da.mem_idx, da.memop);
        break;

    case GET_ASI_DIRECT:
        {
            TCGv_i64 tmp = tcg_temp_new_i64();

            gen_address_mask(dc, addr);
            tcg_gen_qemu_ld_i64(tmp, addr, da.mem_idx, da.memop);

            /* Note that LE ldda acts as if each 32-bit register
               result is byte swapped.  Having just performed one
               64-bit bswap, we need now to swap the writebacks.  */
            if ((da.memop & MO_BSWAP) == MO_TE) {
                tcg_gen_extr32_i64(lo, hi, tmp);
            } else {
                tcg_gen_extr32_i64(hi, lo, tmp);
            }
            tcg_temp_free_i64(tmp);
        }
        break;

    default:
        {
            TCGv_i32 r_asi = tcg_const_i32(da.asi);
            TCGv_i64 tmp;

            save_state(dc);
            gen_helper_ldda_asi(cpu_env, addr, r_asi);
            tcg_temp_free_i32(r_asi);

            tmp = gen_dest_gpr(dc, rd);
            tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUSPARCState, qt0.high));
            gen_store_gpr(dc, rd, tmp);
            tmp = gen_dest_gpr(dc, rd + 1);
            tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUSPARCState, qt0.low));
            gen_store_gpr(dc, rd + 1, tmp);
            tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUSPARCState, qt0.high));
            tcg_gen_ld_i64(lo, cpu_env, offsetof(CPUSPARCState, qt0.low));
        }
        break;
    }

    gen_store_gpr(dc, rd, hi);
    gen_store_gpr(dc, rd + 1, lo);
}

static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr,
@@ -2340,6 +2396,33 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr,
    switch (da.type) {
    case GET_ASI_EXCP:
        break;

    case GET_ASI_DTWINX:
        gen_check_align(addr, 15);
        gen_address_mask(dc, addr);
        tcg_gen_qemu_st_i64(hi, addr, da.mem_idx, da.memop);
        tcg_gen_addi_tl(addr, addr, 8);
        tcg_gen_qemu_st_i64(lo, addr, da.mem_idx, da.memop);
        break;

    case GET_ASI_DIRECT:
        {
            TCGv_i64 t64 = tcg_temp_new_i64();

            /* Note that LE stda acts as if each 32-bit register result is
               byte swapped.  We will perform one 64-bit LE store, so now
               we must swap the order of the construction.  */
            if ((da.memop & MO_BSWAP) == MO_TE) {
                tcg_gen_concat32_i64(t64, lo, hi);
            } else {
                tcg_gen_concat32_i64(t64, hi, lo);
            }
            gen_address_mask(dc, addr);
            tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop);
            tcg_temp_free_i64(t64);
        }
        break;

    default:
        {
            TCGv_i32 r_asi = tcg_const_i32(da.asi);
@@ -2379,14 +2462,14 @@ static void gen_casx_asi(DisasContext *dc, TCGv addr, TCGv val2,
}

#elif !defined(CONFIG_USER_ONLY)
static void gen_ldda_asi(DisasContext *dc, TCGv hi, TCGv addr,
                         int insn, int rd)
static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd)
{
    /* ??? Work around an apparent bug in Ubuntu gcc 4.8.2-10ubuntu2+12,
       whereby "rd + 1" elicits "error: array subscript is above array".
       Since we have already asserted that rd is even, the semantics
       are unchanged.  */
    TCGv lo = gen_dest_gpr(dc, rd | 1);
    TCGv hi = gen_dest_gpr(dc, rd);
    TCGv_i64 t64 = tcg_temp_new_i64();
    DisasASI da = get_asi(dc, insn, MO_TEQ);

@@ -2394,6 +2477,10 @@ static void gen_ldda_asi(DisasContext *dc, TCGv hi, TCGv addr,
    case GET_ASI_EXCP:
        tcg_temp_free_i64(t64);
        return;
    case GET_ASI_DIRECT:
        gen_address_mask(dc, addr);
        tcg_gen_qemu_ld_i64(t64, addr, da.mem_idx, da.memop);
        break;
    default:
        {
            TCGv_i32 r_asi = tcg_const_i32(da.asi);
@@ -2427,6 +2514,10 @@ static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr,
    switch (da.type) {
    case GET_ASI_EXCP:
        break;
    case GET_ASI_DIRECT:
        gen_address_mask(dc, addr);
        tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop);
        break;
    default:
        {
            TCGv_i32 r_asi = tcg_const_i32(da.asi);
@@ -4918,7 +5009,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
                    if (rd & 1) {
                        goto illegal_insn;
                    }
                    gen_ldda_asi(dc, cpu_val, cpu_addr, insn, rd);
                    gen_ldda_asi(dc, cpu_addr, insn, rd);
                    goto skip_move;
                case 0x19:      /* ldsba, load signed byte alternate */
                    gen_ld_asi(dc, cpu_val, cpu_addr, insn, MO_SB);