Commit a5f53390 authored by Leon Alrae's avatar Leon Alrae
Browse files

target-mips: fix hflags modified in delay / forbidden slot



All instructions which may change hflags terminate tb. However, this doesn't
work if such an instruction is placed in delay or forbidden slot.
gen_branch() clears MIPS_HFLAG_BMASK in ctx->hflags and then generates code
to overwrite hflags with ctx->hflags, consequently we loose any execution-time
hflags modifications. For example, in the following scenario hflag related to
Status.CU1 will not be updated:
    /* Set Status.CU1 in delay slot */
    mfc0  $24, $12, 0
    lui   $25, 0x2000
    or    $25, $25, $24
    b     check_Status_CU1
    mtc0  $25, $12, 0

With this change we clear MIPS_HFLAG_BMASK in execution-time hflags if
instruction in delay or forbidden slot wants to terminate tb for some reason
(i.e. ctx->bstate != BS_NONE).

Also, die early and loudly if "unknown branch" is encountered as this should
never happen.

Signed-off-by: default avatarLeon Alrae <leon.alrae@imgtec.com>
parent 62c68869
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -10531,14 +10531,25 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd)
    tcg_temp_free(t0);
}
static inline void clear_branch_hflags(DisasContext *ctx)
{
    ctx->hflags &= ~MIPS_HFLAG_BMASK;
    if (ctx->bstate == BS_NONE) {
        save_cpu_state(ctx, 0);
    } else {
        /* it is not safe to save ctx->hflags as hflags may be changed
           in execution time by the instruction in delay / forbidden slot. */
        tcg_gen_andi_i32(hflags, hflags, ~MIPS_HFLAG_BMASK);
    }
}
static void gen_branch(DisasContext *ctx, int insn_bytes)
{
    if (ctx->hflags & MIPS_HFLAG_BMASK) {
        int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK;
        /* Branches completion */
        ctx->hflags &= ~MIPS_HFLAG_BMASK;
        clear_branch_hflags(ctx);
        ctx->bstate = BS_BRANCH;
        save_cpu_state(ctx, 0);
        /* FIXME: Need to clear can_do_io.  */
        switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) {
        case MIPS_HFLAG_FBNSLOT:
@@ -10596,8 +10607,8 @@ static void gen_branch(DisasContext *ctx, int insn_bytes)
            tcg_gen_exit_tb(0);
            break;
        default:
            MIPS_DEBUG("unknown branch");
            break;
            fprintf(stderr, "unknown branch 0x%x\n", proc_hflags);
            abort();
        }
    }
}