- Move FPU exception handling into helper functions, since they are big.

- Fix FP-conditional branches.
- Check FPU register mode at runtime, not translation time, as the F64
  status bit can change.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2828 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
ths 2007-05-18 11:55:54 +00:00
parent 34ae7b51f5
commit fd4a04ebb2
5 changed files with 793 additions and 636 deletions

View file

@ -491,7 +491,7 @@ FGEN32(gen_op_load_fpr_WTH2, gen_op_load_fpr_WTH2_fpr);
FGEN32(gen_op_store_fpr_WTH2, gen_op_store_fpr_WTH2_fpr);
#define FOP_CONDS(type, fmt) \
static GenOpFunc1 * cond ## type ## _ ## fmt ## _table[16] = { \
static GenOpFunc1 * gen_op_cmp ## type ## _ ## fmt ## _table[16] = { \
gen_op_cmp ## type ## _ ## fmt ## _f, \
gen_op_cmp ## type ## _ ## fmt ## _un, \
gen_op_cmp ## type ## _ ## fmt ## _eq, \
@ -511,7 +511,7 @@ static GenOpFunc1 * cond ## type ## _ ## fmt ## _table[16] = { \
}; \
static inline void gen_cmp ## type ## _ ## fmt(int n, long cc) \
{ \
cond ## type ## _ ## fmt ## _table[n](cc); \
gen_op_cmp ## type ## _ ## fmt ## _table[n](cc); \
}
FOP_CONDS(, d)
@ -525,11 +525,10 @@ typedef struct DisasContext {
struct TranslationBlock *tb;
target_ulong pc, saved_pc;
uint32_t opcode;
uint32_t fp_status, saved_fp_status;
uint32_t fp_status;
/* Routine used to access memory */
int mem_idx;
uint32_t hflags, saved_hflags;
uint32_t CP0_Status;
int bstate;
target_ulong btarget;
} DisasContext;
@ -628,11 +627,21 @@ static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
}
}
static inline void save_fpu_state (DisasContext *ctx)
static inline void restore_cpu_state (CPUState *env, DisasContext *ctx)
{
if (ctx->fp_status != ctx->saved_fp_status) {
gen_op_save_fp_status(ctx->fp_status);
ctx->saved_fp_status = ctx->fp_status;
ctx->saved_hflags = ctx->hflags;
switch (ctx->hflags & MIPS_HFLAG_BMASK) {
case MIPS_HFLAG_BR:
gen_op_restore_breg_target();
break;
case MIPS_HFLAG_B:
ctx->btarget = env->btarget;
break;
case MIPS_HFLAG_BC:
case MIPS_HFLAG_BL:
ctx->btarget = env->btarget;
gen_op_restore_bcond();
break;
}
}
@ -4293,20 +4302,20 @@ static void gen_compute_branch1 (DisasContext *ctx, uint32_t op,
gen_op_save_bcond();
break;
case OPC_BC1FANY2:
gen_op_bc1fany2(cc);
opn = "bc1fany2";
gen_op_bc1any2f(cc);
opn = "bc1any2f";
goto not_likely;
case OPC_BC1TANY2:
gen_op_bc1tany2(cc);
opn = "bc1tany2";
gen_op_bc1any2t(cc);
opn = "bc1any2t";
goto not_likely;
case OPC_BC1FANY4:
gen_op_bc1fany4(cc);
opn = "bc1fany4";
gen_op_bc1any4f(cc);
opn = "bc1any4f";
goto not_likely;
case OPC_BC1TANY4:
gen_op_bc1tany4(cc);
opn = "bc1tany4";
gen_op_bc1any4t(cc);
opn = "bc1any4t";
not_likely:
ctx->hflags |= MIPS_HFLAG_BC;
gen_op_set_bcond();
@ -4323,27 +4332,6 @@ static void gen_compute_branch1 (DisasContext *ctx, uint32_t op,
/* Coprocessor 1 (FPU) */
/* verify if floating point register is valid; an operation is not defined
* if bit 0 of any register specification is set and the FR bit in the
* Status register equals zero, since the register numbers specify an
* even-odd pair of adjacent coprocessor general registers. When the FR bit
* in the Status register equals one, both even and odd register numbers
* are valid. This limitation exists only for 64 bit wide (d,l,ps) registers.
*
* Multiple 64 bit wide registers can be checked by calling
* CHECK_FR(ctx, freg1 | freg2 | ... | fregN);
*
* FIXME: This is broken for R2, it needs to be checked at runtime, not
* at translation time.
*/
#define CHECK_FR(ctx, freg) do { \
if (!((ctx)->CP0_Status & (1 << CP0St_FR)) && ((freg) & 1)) { \
MIPS_INVAL("FPU mode"); \
generate_exception (ctx, EXCP_RI); \
return; \
} \
} while(0)
#define FOP(func, fmt) (((fmt) << 21) | (func))
static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
@ -4388,14 +4376,14 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
opn = "dmtc1";
break;
case OPC_MFHC1:
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_mfhc1();
GEN_STORE_TN_REG(rt, T0);
opn = "mfhc1";
break;
case OPC_MTHC1:
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_REG_TN(T0, rt);
gen_op_mthc1();
GEN_STORE_FTN_FREG(fs, WTH0);
@ -4546,28 +4534,28 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "neg.s";
break;
case FOP(8, 16):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_roundl_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "round.l.s";
break;
case FOP(9, 16):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_truncl_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "trunc.l.s";
break;
case FOP(10, 16):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_ceill_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "ceil.l.s";
break;
case FOP(11, 16):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_floorl_s();
GEN_STORE_FTN_FREG(fd, DT2);
@ -4622,7 +4610,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "movn.s";
break;
case FOP(33, 16):
CHECK_FR(ctx, fd);
gen_op_cp1_registers(fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtd_s();
GEN_STORE_FTN_FREG(fd, DT2);
@ -4635,14 +4623,14 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "cvt.w.s";
break;
case FOP(37, 16):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtl_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.l.s";
break;
case FOP(38, 16):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WT1, fs);
GEN_LOAD_FREG_FTN(WT0, ft);
gen_op_float_cvtps_s();
@ -4676,7 +4664,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
}
break;
case FOP(0, 17):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT1, ft);
gen_op_float_add_d();
@ -4685,7 +4673,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
optype = BINOP;
break;
case FOP(1, 17):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT1, ft);
gen_op_float_sub_d();
@ -4694,7 +4682,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
optype = BINOP;
break;
case FOP(2, 17):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT1, ft);
gen_op_float_mul_d();
@ -4703,7 +4691,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
optype = BINOP;
break;
case FOP(3, 17):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT1, ft);
gen_op_float_div_d();
@ -4712,84 +4700,84 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
optype = BINOP;
break;
case FOP(4, 17):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_sqrt_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "sqrt.d";
break;
case FOP(5, 17):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_abs_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "abs.d";
break;
case FOP(6, 17):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_mov_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "mov.d";
break;
case FOP(7, 17):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_chs_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "neg.d";
break;
case FOP(8, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_roundl_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "round.l.d";
break;
case FOP(9, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_truncl_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "trunc.l.d";
break;
case FOP(10, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_ceill_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "ceil.l.d";
break;
case FOP(11, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_floorl_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "floor.l.d";
break;
case FOP(12, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_roundw_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "round.w.d";
break;
case FOP(13, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_truncw_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "trunc.w.d";
break;
case FOP(14, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_ceilw_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "ceil.w.d";
break;
case FOP(15, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_floorw_d();
GEN_STORE_FTN_FREG(fd, WT2);
@ -4835,7 +4823,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
case FOP(61, 17):
case FOP(62, 17):
case FOP(63, 17):
CHECK_FR(ctx, fs | ft);
gen_op_cp1_registers(fs | ft);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT1, ft);
if (ctx->opcode & (1 << 6)) {
@ -4847,21 +4835,21 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
}
break;
case FOP(32, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvts_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.d";
break;
case FOP(36, 17):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvtw_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.w.d";
break;
case FOP(37, 17):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvtl_d();
GEN_STORE_FTN_FREG(fd, DT2);
@ -4874,21 +4862,21 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "cvt.s.w";
break;
case FOP(33, 20):
CHECK_FR(ctx, fd);
gen_op_cp1_registers(fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtd_w();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.d.w";
break;
case FOP(32, 21):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvts_l();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.l";
break;
case FOP(33, 21):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvtd_l();
GEN_STORE_FTN_FREG(fd, DT2);
@ -4896,7 +4884,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
break;
case FOP(38, 20):
case FOP(38, 21):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_cvtps_pw();
@ -4905,7 +4893,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "cvt.ps.pw";
break;
case FOP(0, 22):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
@ -4916,7 +4904,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "add.ps";
break;
case FOP(1, 22):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
@ -4927,7 +4915,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "sub.ps";
break;
case FOP(2, 22):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
@ -4938,7 +4926,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "mul.ps";
break;
case FOP(5, 22):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_abs_ps();
@ -4947,7 +4935,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "abs.ps";
break;
case FOP(6, 22):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_mov_ps();
@ -4956,7 +4944,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "mov.ps";
break;
case FOP(7, 22):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_chs_ps();
@ -4998,7 +4986,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "movn.ps";
break;
case FOP(24, 22):
CHECK_FR(ctx, fs | fd | ft);
gen_op_cp1_registers(fs | fd | ft);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
@ -5009,14 +4997,14 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "addr.ps";
break;
case FOP(32, 22):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_cvts_pu();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.pu";
break;
case FOP(36, 22):
CHECK_FR(ctx, fs | fd);
gen_op_cp1_registers(fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_cvtpw_ps();
@ -5025,14 +5013,14 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "cvt.pw.ps";
break;
case FOP(40, 22):
CHECK_FR(ctx, fs);
gen_op_cp1_registers(fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvts_pl();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.pl";
break;
case FOP(44, 22):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_pll_ps();
@ -5040,7 +5028,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "pll.ps";
break;
case FOP(45, 22):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_op_float_plu_ps();
@ -5048,7 +5036,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "plu.ps";
break;
case FOP(46, 22):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_pul_ps();
@ -5056,7 +5044,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
opn = "pul.ps";
break;
case FOP(47, 22):
CHECK_FR(ctx, fs | ft | fd);
gen_op_cp1_registers(fs | ft | fd);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_op_float_puu_ps();
@ -5079,7 +5067,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
case FOP(61, 22):
case FOP(62, 22):
case FOP(63, 22):
CHECK_FR(ctx, fs | ft);
gen_op_cp1_registers(fs | ft);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
@ -5166,7 +5154,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, int fd,
const char *opn = "flt3_arith";
/* All of those work only on 64bit FPUs. */
CHECK_FR(ctx, fd | fr | fs | ft);
gen_op_cp1_registers(fd | fr | fs | ft);
switch (opc) {
case OPC_ALNV_PS:
GEN_LOAD_REG_TN(T0, fr);
@ -5874,26 +5862,12 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
ctx.bstate = BS_NONE;
/* Restore delay slot state from the tb context. */
ctx.hflags = tb->flags;
ctx.saved_hflags = ctx.hflags;
switch (ctx.hflags & MIPS_HFLAG_BMASK) {
case MIPS_HFLAG_BR:
gen_op_restore_breg_target();
break;
case MIPS_HFLAG_B:
ctx.btarget = env->btarget;
break;
case MIPS_HFLAG_BC:
case MIPS_HFLAG_BL:
ctx.btarget = env->btarget;
gen_op_restore_bcond();
break;
}
restore_cpu_state(env, &ctx);
#if defined(CONFIG_USER_ONLY)
ctx.mem_idx = 0;
#else
ctx.mem_idx = !((ctx.hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM);
#endif
ctx.CP0_Status = env->CP0_Status;
#ifdef DEBUG_DISAS
if (loglevel & CPU_LOG_TB_CPU) {
fprintf(logfile, "------------------------------------------------\n");