Tracking the implementation of branch instructions in MIPS target.
References:
Branches is translated in function gen_compute_branch
:
// target/mips/translate.c
/* Branches (before delay slot) */
static void gen_compute_branch(DisasContext *ctx, uint32_t opc,
int insn_bytes,
int rs, int rt, int32_t offset,
int delayslot_size)
{
target_ulong btgt = -1;
int blink = 0;
int bcond_compute = 0;
TCGv t0 = tcg_temp_new();
TCGv t1 = tcg_temp_new();
// Note: For CHERI btgt is an absolute address not an offset relative
// to PCC.base.
#if defined(TARGET_CHERI)
bool btarget_checked = false;
// Some debug assertions to ensure all branch cases are bounds checked
#define SET_BTARGET_CHECKED(value) \
do { tcg_debug_assert(!btarget_checked); btarget_checked = value; } while (false)
#else
#define SET_BTARGET_CHECKED(value)
#endif // defined(TARGET_CHERI)
if (ctx->hflags & MIPS_HFLAG_BMASK) {
#ifdef MIPS_DEBUG_DISAS
LOG_DISAS("Branch in delay / forbidden slot at PC 0x"
TARGET_FMT_lx "\n", ctx->base.pc_next);
#endif
generate_exception_end(ctx, EXCP_RI);
SET_BTARGET_CHECKED(true); // exception raised -> no need to check
goto out;
}
/* Load needed operands */
switch (opc) {
case OPC_BEQ:
case OPC_BEQL:
case OPC_BNE:
case OPC_BNEL:
/* Compare two registers */
if (rs != rt) {
gen_load_gpr(t0, rs);
gen_load_gpr(t1, rt);
bcond_compute = 1;
}
btgt = ctx->base.pc_next + insn_bytes + offset;
break;
case OPC_BGEZ:
case OPC_BGEZAL:
case OPC_BGEZALL:
case OPC_BGEZL:
case OPC_BGTZ:
case OPC_BGTZL:
case OPC_BLEZ:
case OPC_BLEZL:
case OPC_BLTZ:
case OPC_BLTZAL:
case OPC_BLTZALL:
case OPC_BLTZL:
/* Compare to zero */
if (rs != 0) {
gen_load_gpr(t0, rs);
bcond_compute = 1;
}
btgt = ctx->base.pc_next + insn_bytes + offset;
break;
case OPC_BPOSGE32:
#if defined(TARGET_MIPS64)
case OPC_BPOSGE64:
tcg_gen_andi_tl(t0, cpu_dspctrl, 0x7F);
#else
tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F);
#endif
bcond_compute = 1;
btgt = ctx->base.pc_next + insn_bytes + offset;
break;
case OPC_J:
case OPC_JAL:
#ifndef TARGET_CHERI
case OPC_JALX:
#endif
/* Jump to immediate (taking PCC.base into account) */
btgt = (((ctx->base.pc_next + insn_bytes - pcc_base(ctx)) &
(int32_t)0xF0000000) | (uint32_t)offset) + pcc_base(ctx);
break;
case OPC_JR:
case OPC_JALR:
/* Jump to register */
if (offset != 0 && offset != 16) {
/*
* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the
* others are reserved.
*/
MIPS_INVAL("jump hint");
generate_exception_end(ctx, EXCP_RI);
SET_BTARGET_CHECKED(true); // exception raised -> no need to check
goto out;
}
gen_load_gpr(btarget, rs);
#ifdef TARGET_CHERI
/* Add PCC.base to rs (jr/jalr is relative to PCC) */
tcg_gen_addi_tl(btarget, btarget, ctx->base.pcc_base);
#endif /* TARGET_CHERI */
gen_check_branch_target_dynamic(ctx, btarget);
SET_BTARGET_CHECKED(true);
break;
default:
MIPS_INVAL("branch/jump");
generate_exception_end(ctx, EXCP_RI);
SET_BTARGET_CHECKED(true); // exception raised -> no need to check
goto out;
}
if (bcond_compute == 0) {
/* No condition to be computed */
switch (opc) {
case OPC_BEQ: /* rx == rx */
case OPC_BEQL: /* rx == rx likely */
case OPC_BGEZ: /* 0 >= 0 */
case OPC_BGEZL: /* 0 >= 0 likely */
case OPC_BLEZ: /* 0 <= 0 */
case OPC_BLEZL: /* 0 <= 0 likely */
/* Always take */
ctx->hflags |= MIPS_HFLAG_B;
break;
case OPC_BGEZAL: /* 0 >= 0 */
case OPC_BGEZALL: /* 0 >= 0 likely */
/* Always take and link */
blink = 31;
ctx->hflags |= MIPS_HFLAG_B;
break;
case OPC_BNE: /* rx != rx */
case OPC_BGTZ: /* 0 > 0 */
case OPC_BLTZ: /* 0 < 0 */
/* Treat as NOP. */
SET_BTARGET_CHECKED(true); // not taken -> no need to check
goto out;
case OPC_BLTZAL: /* 0 < 0 */
/*
* Handle as an unconditional branch to get correct delay
* slot checking.
*/
blink = 31;
btgt = ctx->base.pc_next + insn_bytes + delayslot_size;
ctx->hflags |= MIPS_HFLAG_B;
break;
case OPC_BLTZALL: /* 0 < 0 likely */
/* For CHERI, we have to subtract PCC.base from r31 */
tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 8 - pcc_base(ctx));
gen_log_instr_gpr_update(ctx, 31);
/* Skip the instruction in the delay slot */
ctx->base.pc_next += 4;
SET_BTARGET_CHECKED(true); // not taken -> no need to check
goto out;
case OPC_BNEL: /* rx != rx likely */
case OPC_BGTZL: /* 0 > 0 likely */
case OPC_BLTZL: /* 0 < 0 likely */
/* Skip the instruction in the delay slot */
ctx->base.pc_next += 4;
SET_BTARGET_CHECKED(true); // not taken -> no need to check
goto out;
case OPC_J:
ctx->hflags |= MIPS_HFLAG_B;
break;
#ifndef TARGET_CHERI
case OPC_JALX:
ctx->hflags |= MIPS_HFLAG_BX;
/* Fallthrough */
#endif
case OPC_JAL:
blink = 31;
ctx->hflags |= MIPS_HFLAG_B;
break;
case OPC_JR:
ctx->hflags |= MIPS_HFLAG_BR;
break;
case OPC_JALR:
blink = rt;
ctx->hflags |= MIPS_HFLAG_BR;
break;
default:
MIPS_INVAL("branch/jump");
generate_exception_end(ctx, EXCP_RI);
SET_BTARGET_CHECKED(true); // exception raised -> no need to check
goto out;
}
} else {
switch (opc) {
case OPC_BEQ:
tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1);
goto not_likely;
case OPC_BEQL:
tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1);
goto likely;
case OPC_BNE:
tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1);
goto not_likely;
case OPC_BNEL:
tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1);
goto likely;
case OPC_BGEZ:
tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
goto not_likely;
case OPC_BGEZL:
tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
goto likely;
case OPC_BGEZAL:
tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
blink = 31;
goto not_likely;
case OPC_BGEZALL:
tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
blink = 31;
goto likely;
case OPC_BGTZ:
tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0);
goto not_likely;
case OPC_BGTZL:
tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0);
goto likely;
case OPC_BLEZ:
tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0);
goto not_likely;
case OPC_BLEZL:
tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0);
goto likely;
case OPC_BLTZ:
tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
goto not_likely;
case OPC_BLTZL:
tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
goto likely;
case OPC_BPOSGE32:
tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 32);
goto not_likely;
#if defined(TARGET_MIPS64)
case OPC_BPOSGE64:
tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 64);
goto not_likely;
#endif
case OPC_BLTZAL:
tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
blink = 31;
not_likely:
ctx->hflags |= MIPS_HFLAG_BC;
break;
case OPC_BLTZALL:
tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
blink = 31;
likely:
ctx->hflags |= MIPS_HFLAG_BL;
break;
default:
MIPS_INVAL("conditional branch/jump");
generate_exception_end(ctx, EXCP_RI);
SET_BTARGET_CHECKED(true); // not taken -> no need to check
goto out;
}
}
ctx->btarget = btgt;
#ifdef TARGET_CHERI
if (bcond_compute) {
// Check that the conditional branch target is in range (but only if the branch is taken)
tcg_debug_assert(btgt != -1 && "btgt should have been set!");
gen_check_cond_branch_target(ctx, bcond, btgt);
SET_BTARGET_CHECKED(true);
} else if (!btarget_checked) {
tcg_debug_assert(btgt != -1 && "btgt should have been set!");
gen_check_branch_target(ctx, btgt);
SET_BTARGET_CHECKED(true);
}
#endif
switch (delayslot_size) {
case 2:
ctx->hflags |= MIPS_HFLAG_BDS16;
break;
case 4:
ctx->hflags |= MIPS_HFLAG_BDS32;
break;
}
if (blink > 0) {
int post_delay = insn_bytes + delayslot_size;
int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16);
tcg_gen_movi_tl(cpu_gpr[blink],
/* Subtract PCC.base from r[blink] */
ctx->base.pc_next + post_delay + lowbit - pcc_base(ctx));
gen_log_instr_gpr_update(ctx, blink);
}
out:
if (insn_bytes == 2) {
ctx->hflags |= MIPS_HFLAG_B16;
}
tcg_temp_free(t0);
tcg_temp_free(t1);
#ifdef TARGET_CHERI
tcg_debug_assert(btarget_checked);
#endif
}
Call path:
// target/mips/translate.c
mips_tr_translate_insn()
--> decode_opc() // !(ctx->hflags & MIPS_HFLAG_M16)
--> gen_compute_branch() // OPC_{BGEZ, BLTZAL, BGEZAL, BPOSGE64, JAL, BLEZC, BGTZC, BLEZALC, BGTZALC, BEQ, BNE, BC1EQZ, BC1NEZ, BC1, ...}
--> decode_opc_special()
--> gen_compute_branch() // OPC_JALR
--> decode_opc_special_tx79() // default: INSN_R5900
--> gen_compute_branch()
--> decode_opc_special_legacy() // default: else
--> gen_compute_branch()
--> decode_micromips_opc() // ctx->insn_flags & ASE_MICROMIPS
--> decode_micromips32_opc()
--> gen_compute_branch() // POOL32I -> BLTZ/BLTZAL/BLTZALS/BGEZ/BGEZAL/...
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?