aboutsummaryrefslogtreecommitdiff
path: root/target-sparc
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2015-09-03 13:13:35 -0700
committerRichard Henderson <rth@twiddle.net>2016-07-12 11:02:35 -0700
commite4dc0052a40d3e7b00ca0b008f345e2ed644aa20 (patch)
tree6e49f0ce50366c5926c39508eca0ca12b8322e6c /target-sparc
parent35e94905ce4b39b358a673995f9bee11f46ec8be (diff)
target-sparc: Directly implement easy ldd/std asis
Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'target-sparc')
-rw-r--r--target-sparc/translate.c115
1 files changed, 103 insertions, 12 deletions
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index e7120f89d1..0d5149d104 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -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);