target-mips: implement forbidden slot

When conditional compact branch is encountered decode one more instruction in
current translation block - that will be forbidden slot. Instruction in
forbidden slot will be executed only if conditional compact branch is not taken.

Any control transfer instruction (CTI) which are branches, jumps, ERET,
DERET, WAIT and PAUSE will generate RI exception if executed in forbidden or
delay slot.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
Reviewed-by: Yongbok Kim <yongbok.kim@imgtec.com>
This commit is contained in:
Leon Alrae 2014-07-11 16:11:33 +01:00
parent faf1f68ba1
commit 339cd2a82a
2 changed files with 76 additions and 36 deletions

View File

@ -488,7 +488,7 @@ struct CPUMIPSState {
* the delay slot, record what type of branch it is so that we can * the delay slot, record what type of branch it is so that we can
* resume translation properly. It might be possible to reduce * resume translation properly. It might be possible to reduce
* this from three bits to two. */ * this from three bits to two. */
#define MIPS_HFLAG_BMASK_BASE 0x03800 #define MIPS_HFLAG_BMASK_BASE 0x803800
#define MIPS_HFLAG_B 0x00800 /* Unconditional branch */ #define MIPS_HFLAG_B 0x00800 /* Unconditional branch */
#define MIPS_HFLAG_BC 0x01000 /* Conditional branch */ #define MIPS_HFLAG_BC 0x01000 /* Conditional branch */
#define MIPS_HFLAG_BL 0x01800 /* Likely branch */ #define MIPS_HFLAG_BL 0x01800 /* Likely branch */
@ -507,6 +507,7 @@ struct CPUMIPSState {
/* Extra flag about HWREna register. */ /* Extra flag about HWREna register. */
#define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */ #define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */
#define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */ #define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */
#define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot */
target_ulong btarget; /* Jump / branch target */ target_ulong btarget; /* Jump / branch target */
target_ulong bcond; /* Branch condition (if needed) */ target_ulong bcond; /* Branch condition (if needed) */

View File

@ -4136,7 +4136,8 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
if (ctx->hflags & MIPS_HFLAG_BMASK) { if (ctx->hflags & MIPS_HFLAG_BMASK) {
#ifdef MIPS_DEBUG_DISAS #ifdef MIPS_DEBUG_DISAS
LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); LOG_DISAS("Branch in delay / forbidden slot at PC 0x"
TARGET_FMT_lx "\n", ctx->pc);
#endif #endif
generate_exception(ctx, EXCP_RI); generate_exception(ctx, EXCP_RI);
goto out; goto out;
@ -7638,12 +7639,22 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt,
case OPC_ERET: case OPC_ERET:
opn = "eret"; opn = "eret";
check_insn(ctx, ISA_MIPS2); check_insn(ctx, ISA_MIPS2);
if ((ctx->insn_flags & ISA_MIPS32R6) &&
(ctx->hflags & MIPS_HFLAG_BMASK)) {
MIPS_DEBUG("CTI in delay / forbidden slot");
goto die;
}
gen_helper_eret(cpu_env); gen_helper_eret(cpu_env);
ctx->bstate = BS_EXCP; ctx->bstate = BS_EXCP;
break; break;
case OPC_DERET: case OPC_DERET:
opn = "deret"; opn = "deret";
check_insn(ctx, ISA_MIPS32); check_insn(ctx, ISA_MIPS32);
if ((ctx->insn_flags & ISA_MIPS32R6) &&
(ctx->hflags & MIPS_HFLAG_BMASK)) {
MIPS_DEBUG("CTI in delay / forbidden slot");
goto die;
}
if (!(ctx->hflags & MIPS_HFLAG_DM)) { if (!(ctx->hflags & MIPS_HFLAG_DM)) {
MIPS_INVAL(opn); MIPS_INVAL(opn);
generate_exception(ctx, EXCP_RI); generate_exception(ctx, EXCP_RI);
@ -7655,6 +7666,11 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt,
case OPC_WAIT: case OPC_WAIT:
opn = "wait"; opn = "wait";
check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); check_insn(ctx, ISA_MIPS3 | ISA_MIPS32);
if ((ctx->insn_flags & ISA_MIPS32R6) &&
(ctx->hflags & MIPS_HFLAG_BMASK)) {
MIPS_DEBUG("CTI in delay / forbidden slot");
goto die;
}
/* If we get an exception, we want to restart at next instruction */ /* If we get an exception, we want to restart at next instruction */
ctx->pc += 4; ctx->pc += 4;
save_cpu_state(ctx, 1); save_cpu_state(ctx, 1);
@ -7681,6 +7697,12 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op,
const char *opn = "cp1 cond branch"; const char *opn = "cp1 cond branch";
TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i32 t0 = tcg_temp_new_i32();
if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) {
MIPS_DEBUG("CTI in delay / forbidden slot");
generate_exception(ctx, EXCP_RI);
goto out;
}
if (cc != 0) if (cc != 0)
check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); check_insn(ctx, ISA_MIPS4 | ISA_MIPS32);
@ -7797,7 +7819,8 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op,
if (ctx->hflags & MIPS_HFLAG_BMASK) { if (ctx->hflags & MIPS_HFLAG_BMASK) {
#ifdef MIPS_DEBUG_DISAS #ifdef MIPS_DEBUG_DISAS
LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx
"\n", ctx->pc);
#endif #endif
generate_exception(ctx, EXCP_RI); generate_exception(ctx, EXCP_RI);
goto out; goto out;
@ -10251,6 +10274,10 @@ static void gen_branch(DisasContext *ctx, int insn_bytes)
save_cpu_state(ctx, 0); save_cpu_state(ctx, 0);
/* FIXME: Need to clear can_do_io. */ /* FIXME: Need to clear can_do_io. */
switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) {
case MIPS_HFLAG_FBNSLOT:
MIPS_DEBUG("forbidden slot");
gen_goto_tb(ctx, 0, ctx->pc + insn_bytes);
break;
case MIPS_HFLAG_B: case MIPS_HFLAG_B:
/* unconditional branch */ /* unconditional branch */
MIPS_DEBUG("unconditional branch"); MIPS_DEBUG("unconditional branch");
@ -15533,7 +15560,8 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc,
if (ctx->hflags & MIPS_HFLAG_BMASK) { if (ctx->hflags & MIPS_HFLAG_BMASK) {
#ifdef MIPS_DEBUG_DISAS #ifdef MIPS_DEBUG_DISAS
LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx
"\n", ctx->pc);
#endif #endif
generate_exception(ctx, EXCP_RI); generate_exception(ctx, EXCP_RI);
goto out; goto out;
@ -15626,56 +15654,56 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc,
gen_branch(ctx, 4); gen_branch(ctx, 4);
} else { } else {
/* Conditional compact branch */ /* Conditional compact branch */
int l1 = gen_new_label(); int fs = gen_new_label();
save_cpu_state(ctx, 0); save_cpu_state(ctx, 0);
switch (opc) { switch (opc) {
case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */
if (rs == 0 && rt != 0) { if (rs == 0 && rt != 0) {
/* OPC_BLEZALC */ /* OPC_BLEZALC */
tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs);
} else if (rs != 0 && rt != 0 && rs == rt) { } else if (rs != 0 && rt != 0 && rs == rt) {
/* OPC_BGEZALC */ /* OPC_BGEZALC */
tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs);
} else { } else {
/* OPC_BGEUC */ /* OPC_BGEUC */
tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1); tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs);
} }
break; break;
case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */
if (rs == 0 && rt != 0) { if (rs == 0 && rt != 0) {
/* OPC_BGTZALC */ /* OPC_BGTZALC */
tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs);
} else if (rs != 0 && rt != 0 && rs == rt) { } else if (rs != 0 && rt != 0 && rs == rt) {
/* OPC_BLTZALC */ /* OPC_BLTZALC */
tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs);
} else { } else {
/* OPC_BLTUC */ /* OPC_BLTUC */
tcg_gen_brcond_tl(TCG_COND_LTU, t0, t1, l1); tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs);
} }
break; break;
case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */
if (rs == 0 && rt != 0) { if (rs == 0 && rt != 0) {
/* OPC_BLEZC */ /* OPC_BLEZC */
tcg_gen_brcondi_tl(TCG_COND_LE, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs);
} else if (rs != 0 && rt != 0 && rs == rt) { } else if (rs != 0 && rt != 0 && rs == rt) {
/* OPC_BGEZC */ /* OPC_BGEZC */
tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs);
} else { } else {
/* OPC_BGEC */ /* OPC_BGEC */
tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1); tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs);
} }
break; break;
case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */
if (rs == 0 && rt != 0) { if (rs == 0 && rt != 0) {
/* OPC_BGTZC */ /* OPC_BGTZC */
tcg_gen_brcondi_tl(TCG_COND_GT, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs);
} else if (rs != 0 && rt != 0 && rs == rt) { } else if (rs != 0 && rt != 0 && rs == rt) {
/* OPC_BLTZC */ /* OPC_BLTZC */
tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs);
} else { } else {
/* OPC_BLTC */ /* OPC_BLTC */
tcg_gen_brcond_tl(TCG_COND_LT, t0, t1, l1); tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs);
} }
break; break;
case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */
@ -15704,10 +15732,10 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc,
tcg_gen_or_tl(t4, t4, input_overflow); tcg_gen_or_tl(t4, t4, input_overflow);
if (opc == OPC_BOVC) { if (opc == OPC_BOVC) {
/* OPC_BOVC */ /* OPC_BOVC */
tcg_gen_brcondi_tl(TCG_COND_NE, t4, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t4, 0, fs);
} else { } else {
/* OPC_BNVC */ /* OPC_BNVC */
tcg_gen_brcondi_tl(TCG_COND_EQ, t4, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t4, 0, fs);
} }
tcg_temp_free(input_overflow); tcg_temp_free(input_overflow);
tcg_temp_free(t4); tcg_temp_free(t4);
@ -15717,27 +15745,27 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc,
/* OPC_BEQZALC, OPC_BNEZALC */ /* OPC_BEQZALC, OPC_BNEZALC */
if (opc == OPC_BEQZALC) { if (opc == OPC_BEQZALC) {
/* OPC_BEQZALC */ /* OPC_BEQZALC */
tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t1, 0, fs);
} else { } else {
/* OPC_BNEZALC */ /* OPC_BNEZALC */
tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t1, 0, fs);
} }
} else { } else {
/* OPC_BEQC, OPC_BNEC */ /* OPC_BEQC, OPC_BNEC */
if (opc == OPC_BEQC) { if (opc == OPC_BEQC) {
/* OPC_BEQC */ /* OPC_BEQC */
tcg_gen_brcond_tl(TCG_COND_EQ, t0, t1, l1); tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_EQ), t0, t1, fs);
} else { } else {
/* OPC_BNEC */ /* OPC_BNEC */
tcg_gen_brcond_tl(TCG_COND_NE, t0, t1, l1); tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_NE), t0, t1, fs);
} }
} }
break; break;
case OPC_BEQZC: case OPC_BEQZC:
tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs);
break; break;
case OPC_BNEZC: case OPC_BNEZC:
tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1); tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t0, 0, fs);
break; break;
default: default:
MIPS_INVAL("Compact conditional branch/jump"); MIPS_INVAL("Compact conditional branch/jump");
@ -15746,12 +15774,11 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc,
} }
/* Generating branch here as compact branches don't have delay slot */ /* Generating branch here as compact branches don't have delay slot */
/* TODO: implement forbidden slot */ gen_goto_tb(ctx, 1, ctx->btarget);
gen_goto_tb(ctx, 1, ctx->pc + 4); gen_set_label(fs);
gen_set_label(l1);
gen_goto_tb(ctx, 0, ctx->btarget); ctx->hflags |= MIPS_HFLAG_FBNSLOT;
MIPS_DEBUG("Compact conditional branch"); MIPS_DEBUG("Compact conditional branch");
ctx->bstate = BS_BRANCH;
} }
out: out:
@ -15969,6 +15996,16 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx)
op1 = MASK_SPECIAL(ctx->opcode); op1 = MASK_SPECIAL(ctx->opcode);
switch (op1) { switch (op1) {
case OPC_SLL: /* Shift with immediate */ case OPC_SLL: /* Shift with immediate */
if (sa == 5 && rd == 0 &&
rs == 0 && rt == 0) { /* PAUSE */
if ((ctx->insn_flags & ISA_MIPS32R6) &&
(ctx->hflags & MIPS_HFLAG_BMASK)) {
MIPS_DEBUG("CTI in delay / forbidden slot");
generate_exception(ctx, EXCP_RI);
break;
}
}
/* Fallthrough */
case OPC_SRA: case OPC_SRA:
gen_shift_imm(ctx, op1, rd, rt, sa); gen_shift_imm(ctx, op1, rd, rt, sa);
break; break;
@ -17567,7 +17604,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
int num_insns; int num_insns;
int max_insns; int max_insns;
int insn_bytes; int insn_bytes;
int is_delay; int is_slot;
if (search_pc) if (search_pc)
qemu_log("search pc %d\n", search_pc); qemu_log("search pc %d\n", search_pc);
@ -17632,7 +17669,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
gen_io_start(); gen_io_start();
is_delay = ctx.hflags & MIPS_HFLAG_BMASK; is_slot = ctx.hflags & MIPS_HFLAG_BMASK;
if (!(ctx.hflags & MIPS_HFLAG_M16)) { if (!(ctx.hflags & MIPS_HFLAG_M16)) {
ctx.opcode = cpu_ldl_code(env, ctx.pc); ctx.opcode = cpu_ldl_code(env, ctx.pc);
insn_bytes = 4; insn_bytes = 4;
@ -17650,12 +17687,14 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
} }
if (ctx.hflags & MIPS_HFLAG_BMASK) { if (ctx.hflags & MIPS_HFLAG_BMASK) {
if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32))) { if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 |
is_delay = 1; MIPS_HFLAG_FBNSLOT))) {
/* force to generate branch as no delay slot is required */ /* force to generate branch as there is neither delay nor
forbidden slot */
is_slot = 1;
} }
} }
if (is_delay) { if (is_slot) {
gen_branch(&ctx, insn_bytes); gen_branch(&ctx, insn_bytes);
} }
ctx.pc += insn_bytes; ctx.pc += insn_bytes;