Queued target/sh4 patches

-----BEGIN PGP SIGNATURE-----
 
 iQJJBAABCgAzFiEEUryGlb40+QrX1Ay4E4jA+JnoM2sFAlo4QvoVHGF1cmVsaWVu
 QGF1cmVsMzIubmV0AAoJEBOIwPiZ6DNrUe4P/3YaM5lsq9OTgE1abOmI5NyqWQE/
 l7YQwToYzt45jdXgcOOG6KqQevFfPuX24SZYmkuOhRj+UvpedFilEc4Nsx3AvejR
 dI5g0b9mrvcS3DjvjQsYgGC+S6BoauuQdqJrZKkWL5xkkUSq22PYNm0K3doBrNU4
 rOGpNXXpZ+8jWACmeDrZ+Hj4HMokd/G35LxIr3fJHaEgN0NK0YjwlVP/LmxTJb48
 gmdIWPyZaevzxPunN3aaZXo3B4u6vrSsAxhHfIYTTj6xpFb/jouSOldFMrHVx764
 RbDvS9qh7FBr3dyv1ekvDZfBNFuqDWbaC6eV1sYbxpvFNjg2wwlUM2wN3afTAvG5
 rWotFOrhNZJ3mV3oQ816JQsqnq1zcGuysp1IsNLz8krv0LNfaVi9dN/ILF5wF8pS
 l2iZXQK1d6UEPB2N7plYXZWsVT8Qs5d4nsZLIm8tiJTTEwa2lwYxOQdktV6rGv0F
 oIIDAQD29J3r3mjxBaLoEteM/n8h488MAWTPUgB/1++2JDFtFIIBFyBRI65RxGgQ
 KdrWSbhUJwqCyiItjdmq+61R/60D2XzIThOLR9brXorTkpMDI7UyxBaKuOrxjzlw
 ir9jHA92SZeZSN7v5apKh08x/EH1Qy3oo8kgkUEscgx5XmFZlYLa4aHgbr5M4RU/
 ci6JWM/ayijrx5Pz
 =aG1u
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/aurel/tags/pull-target-sh4-20171218' into staging

Queued target/sh4 patches

# gpg: Signature made Mon 18 Dec 2017 22:36:42 GMT
# gpg:                using RSA key 0x1388C0F899E8336B
# gpg: Good signature from "Aurelien Jarno <aurelien@aurel32.net>"
# gpg:                 aka "Aurelien Jarno <aurelien@jarno.fr>"
# gpg:                 aka "Aurelien Jarno <aurel32@debian.org>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 7746 2642 A9EF 94FD 0F77  196D BA9C 7806 1DDD 8C9B
#      Subkey fingerprint: 52BC 8695 BE34 F90A D7D4  0CB8 1388 C0F8 99E8 336B

* remotes/aurel/tags/pull-target-sh4-20171218:
  target/sh4: Convert to DisasContextBase
  target/sh4: Do not singlestep after exceptions
  target/sh4: Convert to DisasJumpType
  target/sh4: Use cmpxchg for movco when parallel_cpus
  target/sh4: fix TCG leak during gusa sequence
  target/sh4: add missing tcg_temp_free() in _decode_opc()

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-12-19 19:11:11 +00:00
commit af352675ef
4 changed files with 174 additions and 123 deletions

View File

@ -2679,6 +2679,8 @@ void cpu_loop(CPUSH4State *env)
target_siginfo_t info; target_siginfo_t info;
while (1) { while (1) {
bool arch_interrupt = true;
cpu_exec_start(cs); cpu_exec_start(cs);
trapnr = cpu_exec(cs); trapnr = cpu_exec(cs);
cpu_exec_end(cs); cpu_exec_end(cs);
@ -2710,13 +2712,14 @@ void cpu_loop(CPUSH4State *env)
int sig; int sig;
sig = gdb_handlesig(cs, TARGET_SIGTRAP); sig = gdb_handlesig(cs, TARGET_SIGTRAP);
if (sig) if (sig) {
{
info.si_signo = sig; info.si_signo = sig;
info.si_errno = 0; info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT; info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
} } else {
arch_interrupt = false;
}
} }
break; break;
case 0xa0: case 0xa0:
@ -2727,9 +2730,9 @@ void cpu_loop(CPUSH4State *env)
info._sifields._sigfault._addr = env->tea; info._sifields._sigfault._addr = env->tea;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break; break;
case EXCP_ATOMIC: case EXCP_ATOMIC:
cpu_exec_step_atomic(cs); cpu_exec_step_atomic(cs);
arch_interrupt = false;
break; break;
default: default:
printf ("Unhandled trap: 0x%x\n", trapnr); printf ("Unhandled trap: 0x%x\n", trapnr);
@ -2737,6 +2740,14 @@ void cpu_loop(CPUSH4State *env)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
process_pending_signals (env); process_pending_signals (env);
/* Most of the traps imply an exception or interrupt, which
implies an REI instruction has been executed. Which means
that LDST (aka LOK_ADDR) should be cleared. But there are
a few exceptions for traps internal to QEMU. */
if (arch_interrupt) {
env->lock_addr = -1;
}
} }
} }
#endif #endif

View File

@ -188,7 +188,9 @@ typedef struct CPUSH4State {
tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ tlb_t itlb[ITLB_SIZE]; /* instruction translation table */
tlb_t utlb[UTLB_SIZE]; /* unified translation table */ tlb_t utlb[UTLB_SIZE]; /* unified translation table */
uint32_t ldst; /* LDST = LOCK_ADDR != -1. */
uint32_t lock_addr;
uint32_t lock_value;
/* Fields up to this point are cleared by a CPU reset */ /* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields; struct {} end_reset_fields;

View File

@ -171,6 +171,7 @@ void superh_cpu_do_interrupt(CPUState *cs)
env->spc = env->pc; env->spc = env->pc;
env->sgr = env->gregs[15]; env->sgr = env->gregs[15];
env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB); env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB);
env->lock_addr = -1;
if (env->flags & DELAY_SLOT_MASK) { if (env->flags & DELAY_SLOT_MASK) {
/* Branch instruction should be executed again before delay slot. */ /* Branch instruction should be executed again before delay slot. */

View File

@ -25,28 +25,27 @@
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "tcg-op.h" #include "tcg-op.h"
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "exec/helper-gen.h" #include "exec/helper-gen.h"
#include "exec/translator.h"
#include "trace-tcg.h" #include "trace-tcg.h"
#include "exec/log.h" #include "exec/log.h"
typedef struct DisasContext { typedef struct DisasContext {
struct TranslationBlock *tb; DisasContextBase base;
target_ulong pc;
uint16_t opcode; uint32_t tbflags; /* should stay unmodified during the TB translation */
uint32_t tbflags; /* should stay unmodified during the TB translation */ uint32_t envflags; /* should stay in sync with env->flags using TCG ops */
uint32_t envflags; /* should stay in sync with env->flags using TCG ops */
int bstate;
int memidx; int memidx;
int gbank; int gbank;
int fbank; int fbank;
uint32_t delayed_pc; uint32_t delayed_pc;
int singlestep_enabled;
uint32_t features; uint32_t features;
int has_movcal;
uint16_t opcode;
bool has_movcal;
} DisasContext; } DisasContext;
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
@ -55,21 +54,18 @@ typedef struct DisasContext {
#define IS_USER(ctx) (!(ctx->tbflags & (1u << SR_MD))) #define IS_USER(ctx) (!(ctx->tbflags & (1u << SR_MD)))
#endif #endif
enum { /* Target-specific values for ctx->base.is_jmp. */
BS_NONE = 0, /* We go out of the TB without reaching a branch or an /* We want to exit back to the cpu loop for some reason.
* exception condition Usually this is to recognize interrupts immediately. */
*/ #define DISAS_STOP DISAS_TARGET_0
BS_STOP = 1, /* We want to stop translation for any reason */
BS_BRANCH = 2, /* We reached a branch condition */
BS_EXCP = 3, /* We reached an exception condition */
};
/* global register indexes */ /* global register indexes */
static TCGv cpu_gregs[32]; static TCGv cpu_gregs[32];
static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t; static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t;
static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr; static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr;
static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl; static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl;
static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst; static TCGv cpu_pr, cpu_fpscr, cpu_fpul;
static TCGv cpu_lock_addr, cpu_lock_value;
static TCGv cpu_fregs[32]; static TCGv cpu_fregs[32];
/* internal register indexes */ /* internal register indexes */
@ -147,8 +143,12 @@ void sh4_translate_init(void)
offsetof(CPUSH4State, offsetof(CPUSH4State,
delayed_cond), delayed_cond),
"_delayed_cond_"); "_delayed_cond_");
cpu_ldst = tcg_global_mem_new_i32(cpu_env, cpu_lock_addr = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUSH4State, ldst), "_ldst_"); offsetof(CPUSH4State, lock_addr),
"_lock_addr_");
cpu_lock_value = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUSH4State, lock_value),
"_lock_value_");
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
cpu_fregs[i] = tcg_global_mem_new_i32(cpu_env, cpu_fregs[i] = tcg_global_mem_new_i32(cpu_env,
@ -209,7 +209,7 @@ static void gen_write_sr(TCGv src)
static inline void gen_save_cpu_state(DisasContext *ctx, bool save_pc) static inline void gen_save_cpu_state(DisasContext *ctx, bool save_pc)
{ {
if (save_pc) { if (save_pc) {
tcg_gen_movi_i32(cpu_pc, ctx->pc); tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next);
} }
if (ctx->delayed_pc != (uint32_t) -1) { if (ctx->delayed_pc != (uint32_t) -1) {
tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc); tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc);
@ -227,11 +227,11 @@ static inline bool use_exit_tb(DisasContext *ctx)
static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
{ {
/* Use a direct jump if in same page and singlestep not enabled */ /* Use a direct jump if in same page and singlestep not enabled */
if (unlikely(ctx->singlestep_enabled || use_exit_tb(ctx))) { if (unlikely(ctx->base.singlestep_enabled || use_exit_tb(ctx))) {
return false; return false;
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
#else #else
return true; return true;
#endif #endif
@ -242,10 +242,10 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
if (use_goto_tb(ctx, dest)) { if (use_goto_tb(ctx, dest)) {
tcg_gen_goto_tb(n); tcg_gen_goto_tb(n);
tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_movi_i32(cpu_pc, dest);
tcg_gen_exit_tb((uintptr_t)ctx->tb + n); tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n);
} else { } else {
tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_movi_i32(cpu_pc, dest);
if (ctx->singlestep_enabled) { if (ctx->base.singlestep_enabled) {
gen_helper_debug(cpu_env); gen_helper_debug(cpu_env);
} else if (use_exit_tb(ctx)) { } else if (use_exit_tb(ctx)) {
tcg_gen_exit_tb(0); tcg_gen_exit_tb(0);
@ -253,6 +253,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
tcg_gen_lookup_and_goto_ptr(); tcg_gen_lookup_and_goto_ptr();
} }
} }
ctx->base.is_jmp = DISAS_NORETURN;
} }
static void gen_jump(DisasContext * ctx) static void gen_jump(DisasContext * ctx)
@ -262,13 +263,14 @@ static void gen_jump(DisasContext * ctx)
delayed jump as immediate jump are conditinal jumps */ delayed jump as immediate jump are conditinal jumps */
tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc); tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc);
tcg_gen_discard_i32(cpu_delayed_pc); tcg_gen_discard_i32(cpu_delayed_pc);
if (ctx->singlestep_enabled) { if (ctx->base.singlestep_enabled) {
gen_helper_debug(cpu_env); gen_helper_debug(cpu_env);
} else if (use_exit_tb(ctx)) { } else if (use_exit_tb(ctx)) {
tcg_gen_exit_tb(0); tcg_gen_exit_tb(0);
} else { } else {
tcg_gen_lookup_and_goto_ptr(); tcg_gen_lookup_and_goto_ptr();
} }
ctx->base.is_jmp = DISAS_NORETURN;
} else { } else {
gen_goto_tb(ctx, 0, ctx->delayed_pc); gen_goto_tb(ctx, 0, ctx->delayed_pc);
} }
@ -298,8 +300,8 @@ static void gen_conditional_jump(DisasContext *ctx, target_ulong dest,
tcg_gen_brcondi_i32(cond_not_taken, cpu_sr_t, 0, l1); tcg_gen_brcondi_i32(cond_not_taken, cpu_sr_t, 0, l1);
gen_goto_tb(ctx, 0, dest); gen_goto_tb(ctx, 0, dest);
gen_set_label(l1); gen_set_label(l1);
gen_goto_tb(ctx, 1, ctx->pc + 2); gen_goto_tb(ctx, 1, ctx->base.pc_next + 2);
ctx->bstate = BS_BRANCH; ctx->base.is_jmp = DISAS_NORETURN;
} }
/* Delayed conditional jump (bt or bf) */ /* Delayed conditional jump (bt or bf) */
@ -322,11 +324,12 @@ static void gen_delayed_conditional_jump(DisasContext * ctx)
gen_jump(ctx); gen_jump(ctx);
gen_set_label(l1); gen_set_label(l1);
ctx->base.is_jmp = DISAS_NEXT;
return; return;
} }
tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1); tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1);
gen_goto_tb(ctx, 1, ctx->pc + 2); gen_goto_tb(ctx, 1, ctx->base.pc_next + 2);
gen_set_label(l1); gen_set_label(l1);
gen_jump(ctx); gen_jump(ctx);
} }
@ -463,7 +466,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc); tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc);
ctx->envflags |= DELAY_SLOT_RTE; ctx->envflags |= DELAY_SLOT_RTE;
ctx->delayed_pc = (uint32_t) - 1; ctx->delayed_pc = (uint32_t) - 1;
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
return; return;
case 0x0058: /* sets */ case 0x0058: /* sets */
tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S)); tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S));
@ -474,23 +477,23 @@ static void _decode_opc(DisasContext * ctx)
case 0xfbfd: /* frchg */ case 0xfbfd: /* frchg */
CHECK_FPSCR_PR_0 CHECK_FPSCR_PR_0
tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR); tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR);
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
return; return;
case 0xf3fd: /* fschg */ case 0xf3fd: /* fschg */
CHECK_FPSCR_PR_0 CHECK_FPSCR_PR_0
tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_SZ); tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_SZ);
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
return; return;
case 0xf7fd: /* fpchg */ case 0xf7fd: /* fpchg */
CHECK_SH4A CHECK_SH4A
tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_PR); tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_PR);
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
return; return;
case 0x0009: /* nop */ case 0x0009: /* nop */
return; return;
case 0x001b: /* sleep */ case 0x001b: /* sleep */
CHECK_PRIVILEGED CHECK_PRIVILEGED
tcg_gen_movi_i32(cpu_pc, ctx->pc + 2); tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next + 2);
gen_helper_sleep(cpu_env); gen_helper_sleep(cpu_env);
return; return;
} }
@ -517,23 +520,24 @@ static void _decode_opc(DisasContext * ctx)
/* Detect the start of a gUSA region. If so, update envflags /* Detect the start of a gUSA region. If so, update envflags
and end the TB. This will allow us to see the end of the and end the TB. This will allow us to see the end of the
region (stored in R0) in the next TB. */ region (stored in R0) in the next TB. */
if (B11_8 == 15 && B7_0s < 0 && (tb_cflags(ctx->tb) & CF_PARALLEL)) { if (B11_8 == 15 && B7_0s < 0 &&
(tb_cflags(ctx->base.tb) & CF_PARALLEL)) {
ctx->envflags = deposit32(ctx->envflags, GUSA_SHIFT, 8, B7_0s); ctx->envflags = deposit32(ctx->envflags, GUSA_SHIFT, 8, B7_0s);
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
} }
#endif #endif
tcg_gen_movi_i32(REG(B11_8), B7_0s); tcg_gen_movi_i32(REG(B11_8), B7_0s);
return; return;
case 0x9000: /* mov.w @(disp,PC),Rn */ case 0x9000: /* mov.w @(disp,PC),Rn */
{ {
TCGv addr = tcg_const_i32(ctx->pc + 4 + B7_0 * 2); TCGv addr = tcg_const_i32(ctx->base.pc_next + 4 + B7_0 * 2);
tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW);
tcg_temp_free(addr); tcg_temp_free(addr);
} }
return; return;
case 0xd000: /* mov.l @(disp,PC),Rn */ case 0xd000: /* mov.l @(disp,PC),Rn */
{ {
TCGv addr = tcg_const_i32((ctx->pc + 4 + B7_0 * 4) & ~3); TCGv addr = tcg_const_i32((ctx->base.pc_next + 4 + B7_0 * 4) & ~3);
tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL); tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
tcg_temp_free(addr); tcg_temp_free(addr);
} }
@ -543,13 +547,13 @@ static void _decode_opc(DisasContext * ctx)
return; return;
case 0xa000: /* bra disp */ case 0xa000: /* bra disp */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2; ctx->delayed_pc = ctx->base.pc_next + 4 + B11_0s * 2;
ctx->envflags |= DELAY_SLOT; ctx->envflags |= DELAY_SLOT;
return; return;
case 0xb000: /* bsr disp */ case 0xb000: /* bsr disp */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
tcg_gen_movi_i32(cpu_pr, ctx->pc + 4); tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4);
ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2; ctx->delayed_pc = ctx->base.pc_next + 4 + B11_0s * 2;
ctx->envflags |= DELAY_SLOT; ctx->envflags |= DELAY_SLOT;
return; return;
} }
@ -601,6 +605,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_subi_i32(addr, REG(B11_8), 4);
tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL); tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL);
tcg_gen_mov_i32(REG(B11_8), addr); tcg_gen_mov_i32(REG(B11_8), addr);
tcg_temp_free(addr);
} }
return; return;
case 0x6004: /* mov.b @Rm+,Rn */ case 0x6004: /* mov.b @Rm+,Rn */
@ -1176,22 +1181,22 @@ static void _decode_opc(DisasContext * ctx)
return; return;
case 0x8b00: /* bf label */ case 0x8b00: /* bf label */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
gen_conditional_jump(ctx, ctx->pc + 4 + B7_0s * 2, false); gen_conditional_jump(ctx, ctx->base.pc_next + 4 + B7_0s * 2, false);
return; return;
case 0x8f00: /* bf/s label */ case 0x8f00: /* bf/s label */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
tcg_gen_xori_i32(cpu_delayed_cond, cpu_sr_t, 1); tcg_gen_xori_i32(cpu_delayed_cond, cpu_sr_t, 1);
ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2; ctx->delayed_pc = ctx->base.pc_next + 4 + B7_0s * 2;
ctx->envflags |= DELAY_SLOT_CONDITIONAL; ctx->envflags |= DELAY_SLOT_CONDITIONAL;
return; return;
case 0x8900: /* bt label */ case 0x8900: /* bt label */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
gen_conditional_jump(ctx, ctx->pc + 4 + B7_0s * 2, true); gen_conditional_jump(ctx, ctx->base.pc_next + 4 + B7_0s * 2, true);
return; return;
case 0x8d00: /* bt/s label */ case 0x8d00: /* bt/s label */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
tcg_gen_mov_i32(cpu_delayed_cond, cpu_sr_t); tcg_gen_mov_i32(cpu_delayed_cond, cpu_sr_t);
ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2; ctx->delayed_pc = ctx->base.pc_next + 4 + B7_0s * 2;
ctx->envflags |= DELAY_SLOT_CONDITIONAL; ctx->envflags |= DELAY_SLOT_CONDITIONAL;
return; return;
case 0x8800: /* cmp/eq #imm,R0 */ case 0x8800: /* cmp/eq #imm,R0 */
@ -1278,7 +1283,8 @@ static void _decode_opc(DisasContext * ctx)
} }
return; return;
case 0xc700: /* mova @(disp,PC),R0 */ case 0xc700: /* mova @(disp,PC),R0 */
tcg_gen_movi_i32(REG(0), ((ctx->pc & 0xfffffffc) + 4 + B7_0 * 4) & ~3); tcg_gen_movi_i32(REG(0), ((ctx->base.pc_next & 0xfffffffc) +
4 + B7_0 * 4) & ~3);
return; return;
case 0xcb00: /* or #imm,R0 */ case 0xcb00: /* or #imm,R0 */
tcg_gen_ori_i32(REG(0), REG(0), B7_0); tcg_gen_ori_i32(REG(0), REG(0), B7_0);
@ -1304,7 +1310,7 @@ static void _decode_opc(DisasContext * ctx)
imm = tcg_const_i32(B7_0); imm = tcg_const_i32(B7_0);
gen_helper_trapa(cpu_env, imm); gen_helper_trapa(cpu_env, imm);
tcg_temp_free(imm); tcg_temp_free(imm);
ctx->bstate = BS_EXCP; ctx->base.is_jmp = DISAS_NORETURN;
} }
return; return;
case 0xc800: /* tst #imm,R0 */ case 0xc800: /* tst #imm,R0 */
@ -1372,13 +1378,13 @@ static void _decode_opc(DisasContext * ctx)
switch (ctx->opcode & 0xf0ff) { switch (ctx->opcode & 0xf0ff) {
case 0x0023: /* braf Rn */ case 0x0023: /* braf Rn */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
tcg_gen_addi_i32(cpu_delayed_pc, REG(B11_8), ctx->pc + 4); tcg_gen_addi_i32(cpu_delayed_pc, REG(B11_8), ctx->base.pc_next + 4);
ctx->envflags |= DELAY_SLOT; ctx->envflags |= DELAY_SLOT;
ctx->delayed_pc = (uint32_t) - 1; ctx->delayed_pc = (uint32_t) - 1;
return; return;
case 0x0003: /* bsrf Rn */ case 0x0003: /* bsrf Rn */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
tcg_gen_movi_i32(cpu_pr, ctx->pc + 4); tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4);
tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr); tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr);
ctx->envflags |= DELAY_SLOT; ctx->envflags |= DELAY_SLOT;
ctx->delayed_pc = (uint32_t) - 1; ctx->delayed_pc = (uint32_t) - 1;
@ -1401,7 +1407,7 @@ static void _decode_opc(DisasContext * ctx)
return; return;
case 0x400b: /* jsr @Rn */ case 0x400b: /* jsr @Rn */
CHECK_NOT_DELAY_SLOT CHECK_NOT_DELAY_SLOT
tcg_gen_movi_i32(cpu_pr, ctx->pc + 4); tcg_gen_movi_i32(cpu_pr, ctx->base.pc_next + 4);
tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8)); tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8));
ctx->envflags |= DELAY_SLOT; ctx->envflags |= DELAY_SLOT;
ctx->delayed_pc = (uint32_t) - 1; ctx->delayed_pc = (uint32_t) - 1;
@ -1413,7 +1419,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3); tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3);
gen_write_sr(val); gen_write_sr(val);
tcg_temp_free(val); tcg_temp_free(val);
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
} }
return; return;
case 0x4007: /* ldc.l @Rm+,SR */ case 0x4007: /* ldc.l @Rm+,SR */
@ -1425,7 +1431,7 @@ static void _decode_opc(DisasContext * ctx)
gen_write_sr(val); gen_write_sr(val);
tcg_temp_free(val); tcg_temp_free(val);
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
} }
return; return;
case 0x0002: /* stc SR,Rn */ case 0x0002: /* stc SR,Rn */
@ -1487,7 +1493,7 @@ static void _decode_opc(DisasContext * ctx)
case 0x406a: /* lds Rm,FPSCR */ case 0x406a: /* lds Rm,FPSCR */
CHECK_FPU_ENABLED CHECK_FPU_ENABLED
gen_helper_ld_fpscr(cpu_env, REG(B11_8)); gen_helper_ld_fpscr(cpu_env, REG(B11_8));
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
return; return;
case 0x4066: /* lds.l @Rm+,FPSCR */ case 0x4066: /* lds.l @Rm+,FPSCR */
CHECK_FPU_ENABLED CHECK_FPU_ENABLED
@ -1497,7 +1503,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
gen_helper_ld_fpscr(cpu_env, addr); gen_helper_ld_fpscr(cpu_env, addr);
tcg_temp_free(addr); tcg_temp_free(addr);
ctx->bstate = BS_STOP; ctx->base.is_jmp = DISAS_STOP;
} }
return; return;
case 0x006a: /* sts FPSCR,Rn */ case 0x006a: /* sts FPSCR,Rn */
@ -1524,6 +1530,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TEUL); tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TEUL);
gen_helper_movcal(cpu_env, REG(B11_8), val); gen_helper_movcal(cpu_env, REG(B11_8), val);
tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
tcg_temp_free(val);
} }
ctx->has_movcal = 1; ctx->has_movcal = 1;
return; return;
@ -1547,31 +1554,64 @@ static void _decode_opc(DisasContext * ctx)
return; return;
case 0x0073: case 0x0073:
/* MOVCO.L /* MOVCO.L
LDST -> T * LDST -> T
If (T == 1) R0 -> (Rn) * If (T == 1) R0 -> (Rn)
0 -> LDST * 0 -> LDST
*/ *
* The above description doesn't work in a parallel context.
* Since we currently support no smp boards, this implies user-mode.
* But we can still support the official mechanism while user-mode
* is single-threaded. */
CHECK_SH4A CHECK_SH4A
{ {
TCGLabel *label = gen_new_label(); TCGLabel *fail = gen_new_label();
tcg_gen_mov_i32(cpu_sr_t, cpu_ldst); TCGLabel *done = gen_new_label();
tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label);
tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); if ((tb_cflags(ctx->base.tb) & CF_PARALLEL)) {
gen_set_label(label); TCGv tmp;
tcg_gen_movi_i32(cpu_ldst, 0);
return; tcg_gen_brcond_i32(TCG_COND_NE, REG(B11_8),
cpu_lock_addr, fail);
tmp = tcg_temp_new();
tcg_gen_atomic_cmpxchg_i32(tmp, REG(B11_8), cpu_lock_value,
REG(0), ctx->memidx, MO_TEUL);
tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, tmp, cpu_lock_value);
tcg_temp_free(tmp);
} else {
tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_lock_addr, -1, fail);
tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
tcg_gen_movi_i32(cpu_sr_t, 1);
}
tcg_gen_br(done);
gen_set_label(fail);
tcg_gen_movi_i32(cpu_sr_t, 0);
gen_set_label(done);
tcg_gen_movi_i32(cpu_lock_addr, -1);
} }
return;
case 0x0063: case 0x0063:
/* MOVLI.L @Rm,R0 /* MOVLI.L @Rm,R0
1 -> LDST * 1 -> LDST
(Rm) -> R0 * (Rm) -> R0
When interrupt/exception * When interrupt/exception
occurred 0 -> LDST * occurred 0 -> LDST
*/ *
* In a parallel context, we must also save the loaded value
* for use with the cmpxchg that we'll use with movco.l. */
CHECK_SH4A CHECK_SH4A
tcg_gen_movi_i32(cpu_ldst, 0); if ((tb_cflags(ctx->base.tb) & CF_PARALLEL)) {
tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL); TCGv tmp = tcg_temp_new();
tcg_gen_movi_i32(cpu_ldst, 1); tcg_gen_mov_i32(tmp, REG(B11_8));
tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL);
tcg_gen_mov_i32(cpu_lock_value, REG(0));
tcg_gen_mov_i32(cpu_lock_addr, tmp);
tcg_temp_free(tmp);
} else {
tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL);
tcg_gen_movi_i32(cpu_lock_addr, 0);
}
return; return;
case 0x0093: /* ocbi @Rn */ case 0x0093: /* ocbi @Rn */
{ {
@ -1789,7 +1829,7 @@ static void _decode_opc(DisasContext * ctx)
} }
#if 0 #if 0
fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n", fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n",
ctx->opcode, ctx->pc); ctx->opcode, ctx->base.pc_next);
fflush(stderr); fflush(stderr);
#endif #endif
do_illegal: do_illegal:
@ -1801,7 +1841,7 @@ static void _decode_opc(DisasContext * ctx)
gen_save_cpu_state(ctx, true); gen_save_cpu_state(ctx, true);
gen_helper_raise_illegal_instruction(cpu_env); gen_helper_raise_illegal_instruction(cpu_env);
} }
ctx->bstate = BS_EXCP; ctx->base.is_jmp = DISAS_NORETURN;
return; return;
do_fpu_disabled: do_fpu_disabled:
@ -1811,7 +1851,7 @@ static void _decode_opc(DisasContext * ctx)
} else { } else {
gen_helper_raise_fpu_disable(cpu_env); gen_helper_raise_fpu_disable(cpu_env);
} }
ctx->bstate = BS_EXCP; ctx->base.is_jmp = DISAS_NORETURN;
return; return;
} }
@ -1837,7 +1877,6 @@ static void decode_opc(DisasContext * ctx)
ctx->envflags &= ~GUSA_MASK; ctx->envflags &= ~GUSA_MASK;
tcg_gen_movi_i32(cpu_flags, ctx->envflags); tcg_gen_movi_i32(cpu_flags, ctx->envflags);
ctx->bstate = BS_BRANCH;
if (old_flags & DELAY_SLOT_CONDITIONAL) { if (old_flags & DELAY_SLOT_CONDITIONAL) {
gen_delayed_conditional_jump(ctx); gen_delayed_conditional_jump(ctx);
} else { } else {
@ -1864,8 +1903,8 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns)
int mv_src, mt_dst, st_src, st_mop; int mv_src, mt_dst, st_src, st_mop;
TCGv op_arg; TCGv op_arg;
uint32_t pc = ctx->pc; uint32_t pc = ctx->base.pc_next;
uint32_t pc_end = ctx->tb->cs_base; uint32_t pc_end = ctx->base.tb->cs_base;
int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8); int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8);
int max_insns = (pc_end - pc) / 2; int max_insns = (pc_end - pc) / 2;
int i; int i;
@ -2189,13 +2228,13 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns)
} }
/* If op_src is not a valid register, then op_arg was a constant. */ /* If op_src is not a valid register, then op_arg was a constant. */
if (op_src < 0) { if (op_src < 0 && !TCGV_IS_UNUSED(op_arg)) {
tcg_temp_free_i32(op_arg); tcg_temp_free_i32(op_arg);
} }
/* The entire region has been translated. */ /* The entire region has been translated. */
ctx->envflags &= ~GUSA_MASK; ctx->envflags &= ~GUSA_MASK;
ctx->pc = pc_end; ctx->base.pc_next = pc_end;
return max_insns; return max_insns;
fail: fail:
@ -2208,13 +2247,13 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns)
ctx->envflags |= GUSA_EXCLUSIVE; ctx->envflags |= GUSA_EXCLUSIVE;
gen_save_cpu_state(ctx, false); gen_save_cpu_state(ctx, false);
gen_helper_exclusive(cpu_env); gen_helper_exclusive(cpu_env);
ctx->bstate = BS_EXCP; ctx->base.is_jmp = DISAS_NORETURN;
/* We're not executing an instruction, but we must report one for the /* We're not executing an instruction, but we must report one for the
purposes of accounting within the TB. We might as well report the purposes of accounting within the TB. We might as well report the
entire region consumed via ctx->pc so that it's immediately available entire region consumed via ctx->base.pc_next so that it's immediately
in the disassembly dump. */ available in the disassembly dump. */
ctx->pc = pc_end; ctx->base.pc_next = pc_end;
return 1; return 1;
} }
#endif #endif
@ -2228,16 +2267,16 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
int max_insns; int max_insns;
pc_start = tb->pc; pc_start = tb->pc;
ctx.pc = pc_start; ctx.base.pc_next = pc_start;
ctx.tbflags = (uint32_t)tb->flags; ctx.tbflags = (uint32_t)tb->flags;
ctx.envflags = tb->flags & TB_FLAG_ENVFLAGS_MASK; ctx.envflags = tb->flags & TB_FLAG_ENVFLAGS_MASK;
ctx.bstate = BS_NONE; ctx.base.is_jmp = DISAS_NEXT;
ctx.memidx = (ctx.tbflags & (1u << SR_MD)) == 0 ? 1 : 0; ctx.memidx = (ctx.tbflags & (1u << SR_MD)) == 0 ? 1 : 0;
/* We don't know if the delayed pc came from a dynamic or static branch, /* We don't know if the delayed pc came from a dynamic or static branch,
so assume it is a dynamic branch. */ so assume it is a dynamic branch. */
ctx.delayed_pc = -1; /* use delayed pc from env pointer */ ctx.delayed_pc = -1; /* use delayed pc from env pointer */
ctx.tb = tb; ctx.base.tb = tb;
ctx.singlestep_enabled = cs->singlestep_enabled; ctx.base.singlestep_enabled = cs->singlestep_enabled;
ctx.features = env->features; ctx.features = env->features;
ctx.has_movcal = (ctx.tbflags & TB_FLAG_PENDING_MOVCA); ctx.has_movcal = (ctx.tbflags & TB_FLAG_PENDING_MOVCA);
ctx.gbank = ((ctx.tbflags & (1 << SR_MD)) && ctx.gbank = ((ctx.tbflags & (1 << SR_MD)) &&
@ -2252,11 +2291,11 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
/* Since the ISA is fixed-width, we can bound by the number /* Since the ISA is fixed-width, we can bound by the number
of instructions remaining on the page. */ of instructions remaining on the page. */
num_insns = -(ctx.pc | TARGET_PAGE_MASK) / 2; num_insns = -(ctx.base.pc_next | TARGET_PAGE_MASK) / 2;
max_insns = MIN(max_insns, num_insns); max_insns = MIN(max_insns, num_insns);
/* Single stepping means just that. */ /* Single stepping means just that. */
if (ctx.singlestep_enabled || singlestep) { if (ctx.base.singlestep_enabled || singlestep) {
max_insns = 1; max_insns = 1;
} }
@ -2269,22 +2308,22 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
} }
#endif #endif
while (ctx.bstate == BS_NONE while (ctx.base.is_jmp == DISAS_NEXT
&& num_insns < max_insns && num_insns < max_insns
&& !tcg_op_buf_full()) { && !tcg_op_buf_full()) {
tcg_gen_insn_start(ctx.pc, ctx.envflags); tcg_gen_insn_start(ctx.base.pc_next, ctx.envflags);
num_insns++; num_insns++;
if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) {
/* We have hit a breakpoint - make sure PC is up-to-date */ /* We have hit a breakpoint - make sure PC is up-to-date */
gen_save_cpu_state(&ctx, true); gen_save_cpu_state(&ctx, true);
gen_helper_debug(cpu_env); gen_helper_debug(cpu_env);
ctx.bstate = BS_EXCP; ctx.base.is_jmp = DISAS_NORETURN;
/* The address covered by the breakpoint must be included in /* The address covered by the breakpoint must be included in
[tb->pc, tb->pc + tb->size) in order to for it to be [tb->pc, tb->pc + tb->size) in order to for it to be
properly cleared -- thus we increment the PC here so that properly cleared -- thus we increment the PC here so that
the logic setting tb->size below does the right thing. */ the logic setting tb->size below does the right thing. */
ctx.pc += 2; ctx.base.pc_next += 2;
break; break;
} }
@ -2292,9 +2331,9 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
gen_io_start(); gen_io_start();
} }
ctx.opcode = cpu_lduw_code(env, ctx.pc); ctx.opcode = cpu_lduw_code(env, ctx.base.pc_next);
decode_opc(&ctx); decode_opc(&ctx);
ctx.pc += 2; ctx.base.pc_next += 2;
} }
if (tb_cflags(tb) & CF_LAST_IO) { if (tb_cflags(tb) & CF_LAST_IO) {
gen_io_end(); gen_io_end();
@ -2305,30 +2344,28 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
ctx.envflags &= ~GUSA_MASK; ctx.envflags &= ~GUSA_MASK;
} }
if (cs->singlestep_enabled) { switch (ctx.base.is_jmp) {
case DISAS_STOP:
gen_save_cpu_state(&ctx, true); gen_save_cpu_state(&ctx, true);
gen_helper_debug(cpu_env); if (ctx.base.singlestep_enabled) {
} else { gen_helper_debug(cpu_env);
switch (ctx.bstate) { } else {
case BS_STOP:
gen_save_cpu_state(&ctx, true);
tcg_gen_exit_tb(0); tcg_gen_exit_tb(0);
break; }
case BS_NONE: break;
gen_save_cpu_state(&ctx, false); case DISAS_NEXT:
gen_goto_tb(&ctx, 0, ctx.pc); gen_save_cpu_state(&ctx, false);
break; gen_goto_tb(&ctx, 0, ctx.base.pc_next);
case BS_EXCP: break;
/* fall through */ case DISAS_NORETURN:
case BS_BRANCH: break;
default: default:
break; g_assert_not_reached();
}
} }
gen_tb_end(tb, num_insns); gen_tb_end(tb, num_insns);
tb->size = ctx.pc - pc_start; tb->size = ctx.base.pc_next - pc_start;
tb->icount = num_insns; tb->icount = num_insns;
#ifdef DEBUG_DISAS #ifdef DEBUG_DISAS
@ -2336,7 +2373,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
&& qemu_log_in_addr_range(pc_start)) { && qemu_log_in_addr_range(pc_start)) {
qemu_log_lock(); qemu_log_lock();
qemu_log("IN:\n"); /* , lookup_symbol(pc_start)); */ qemu_log("IN:\n"); /* , lookup_symbol(pc_start)); */
log_target_disas(cs, pc_start, ctx.pc - pc_start); log_target_disas(cs, pc_start, ctx.base.pc_next - pc_start);
qemu_log("\n"); qemu_log("\n");
qemu_log_unlock(); qemu_log_unlock();
} }