target/mips: Add emulation of nanoMIPS 32-bit branch instructions
Add emulation of various flavors of nanoMIPS 32-bit branch instructions. Reviewed-by: Aleksandar Markovic <amarkovic@wavecomp.com> Signed-off-by: Yongbok Kim <yongbok.kim@mips.com> Signed-off-by: Aleksandar Markovic <amarkovic@wavecomp.com> Signed-off-by: Stefan Markovic <smarkovic@wavecomp.com>
This commit is contained in:
parent
0b16dcd180
commit
11d0fc10b7
@ -17217,6 +17217,321 @@ static void gen_pool32axf_nanomips_insn(CPUMIPSState *env, DisasContext *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/* Immediate Value Compact Branches */
|
||||
static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc,
|
||||
int rt, int32_t imm, int32_t offset)
|
||||
{
|
||||
TCGCond cond;
|
||||
int bcond_compute = 0;
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv t1 = tcg_temp_new();
|
||||
|
||||
gen_load_gpr(t0, rt);
|
||||
tcg_gen_movi_tl(t1, imm);
|
||||
ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset);
|
||||
|
||||
/* Load needed operands and calculate btarget */
|
||||
switch (opc) {
|
||||
case NM_BEQIC:
|
||||
if (rt == 0 && imm == 0) {
|
||||
/* Unconditional branch */
|
||||
} else if (rt == 0 && imm != 0) {
|
||||
/* Treat as NOP */
|
||||
goto out;
|
||||
} else {
|
||||
bcond_compute = 1;
|
||||
cond = TCG_COND_EQ;
|
||||
}
|
||||
break;
|
||||
case NM_BBEQZC:
|
||||
case NM_BBNEZC:
|
||||
if (imm >= 32 && !(ctx->hflags & MIPS_HFLAG_64)) {
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
goto out;
|
||||
} else if (rt == 0 && opc == NM_BBEQZC) {
|
||||
/* Unconditional branch */
|
||||
} else if (rt == 0 && opc == NM_BBNEZC) {
|
||||
/* Treat as NOP */
|
||||
goto out;
|
||||
} else {
|
||||
tcg_gen_shri_tl(t0, t0, imm);
|
||||
tcg_gen_andi_tl(t0, t0, 1);
|
||||
tcg_gen_movi_tl(t1, 0);
|
||||
bcond_compute = 1;
|
||||
if (opc == NM_BBEQZC) {
|
||||
cond = TCG_COND_EQ;
|
||||
} else {
|
||||
cond = TCG_COND_NE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NM_BNEIC:
|
||||
if (rt == 0 && imm == 0) {
|
||||
/* Treat as NOP */
|
||||
goto out;
|
||||
} else if (rt == 0 && imm != 0) {
|
||||
/* Unconditional branch */
|
||||
} else {
|
||||
bcond_compute = 1;
|
||||
cond = TCG_COND_NE;
|
||||
}
|
||||
break;
|
||||
case NM_BGEIC:
|
||||
if (rt == 0 && imm == 0) {
|
||||
/* Unconditional branch */
|
||||
} else {
|
||||
bcond_compute = 1;
|
||||
cond = TCG_COND_GE;
|
||||
}
|
||||
break;
|
||||
case NM_BLTIC:
|
||||
bcond_compute = 1;
|
||||
cond = TCG_COND_LT;
|
||||
break;
|
||||
case NM_BGEIUC:
|
||||
if (rt == 0 && imm == 0) {
|
||||
/* Unconditional branch */
|
||||
} else {
|
||||
bcond_compute = 1;
|
||||
cond = TCG_COND_GEU;
|
||||
}
|
||||
break;
|
||||
case NM_BLTIUC:
|
||||
bcond_compute = 1;
|
||||
cond = TCG_COND_LTU;
|
||||
break;
|
||||
default:
|
||||
MIPS_INVAL("Immediate Value Compact branch");
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bcond_compute == 0) {
|
||||
/* Uncoditional compact branch */
|
||||
gen_goto_tb(ctx, 0, ctx->btarget);
|
||||
} else {
|
||||
/* Conditional compact branch */
|
||||
TCGLabel *fs = gen_new_label();
|
||||
|
||||
tcg_gen_brcond_tl(tcg_invert_cond(cond), t0, t1, fs);
|
||||
|
||||
gen_goto_tb(ctx, 1, ctx->btarget);
|
||||
gen_set_label(fs);
|
||||
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next + 4);
|
||||
}
|
||||
|
||||
out:
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
}
|
||||
|
||||
/* P.BALRSC type nanoMIPS R6 branches: BALRSC and BRSC */
|
||||
static void gen_compute_nanomips_pbalrsc_branch(DisasContext *ctx, int rs,
|
||||
int rt)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv t1 = tcg_temp_new();
|
||||
|
||||
/* load rs */
|
||||
gen_load_gpr(t0, rs);
|
||||
|
||||
/* link */
|
||||
if (rt != 0) {
|
||||
tcg_gen_movi_tl(cpu_gpr[rt], ctx->base.pc_next + 4);
|
||||
}
|
||||
|
||||
/* calculate btarget */
|
||||
tcg_gen_shli_tl(t0, t0, 1);
|
||||
tcg_gen_movi_tl(t1, ctx->base.pc_next + 4);
|
||||
gen_op_addr_add(ctx, btarget, t1, t0);
|
||||
|
||||
/* unconditional branch to register */
|
||||
tcg_gen_mov_tl(cpu_PC, btarget);
|
||||
tcg_gen_lookup_and_goto_ptr();
|
||||
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
}
|
||||
|
||||
/* nanoMIPS Branches */
|
||||
static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc,
|
||||
int rs, int rt, int32_t offset)
|
||||
{
|
||||
int bcond_compute = 0;
|
||||
TCGv t0 = tcg_temp_new();
|
||||
TCGv t1 = tcg_temp_new();
|
||||
|
||||
/* Load needed operands and calculate btarget */
|
||||
switch (opc) {
|
||||
/* compact branch */
|
||||
case OPC_BGEC:
|
||||
case OPC_BLTC:
|
||||
gen_load_gpr(t0, rs);
|
||||
gen_load_gpr(t1, rt);
|
||||
bcond_compute = 1;
|
||||
ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset);
|
||||
break;
|
||||
case OPC_BGEUC:
|
||||
case OPC_BLTUC:
|
||||
if (rs == 0 || rs == rt) {
|
||||
/* OPC_BLEZALC, OPC_BGEZALC */
|
||||
/* OPC_BGTZALC, OPC_BLTZALC */
|
||||
tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4);
|
||||
}
|
||||
gen_load_gpr(t0, rs);
|
||||
gen_load_gpr(t1, rt);
|
||||
bcond_compute = 1;
|
||||
ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset);
|
||||
break;
|
||||
case OPC_BC:
|
||||
ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset);
|
||||
break;
|
||||
case OPC_BEQZC:
|
||||
if (rs != 0) {
|
||||
/* OPC_BEQZC, OPC_BNEZC */
|
||||
gen_load_gpr(t0, rs);
|
||||
bcond_compute = 1;
|
||||
ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset);
|
||||
} else {
|
||||
/* OPC_JIC, OPC_JIALC */
|
||||
TCGv tbase = tcg_temp_new();
|
||||
TCGv toffset = tcg_temp_new();
|
||||
|
||||
gen_load_gpr(tbase, rt);
|
||||
tcg_gen_movi_tl(toffset, offset);
|
||||
gen_op_addr_add(ctx, btarget, tbase, toffset);
|
||||
tcg_temp_free(tbase);
|
||||
tcg_temp_free(toffset);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MIPS_INVAL("Compact branch/jump");
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bcond_compute == 0) {
|
||||
/* Uncoditional compact branch */
|
||||
switch (opc) {
|
||||
case OPC_BC:
|
||||
gen_goto_tb(ctx, 0, ctx->btarget);
|
||||
break;
|
||||
default:
|
||||
MIPS_INVAL("Compact branch/jump");
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* Conditional compact branch */
|
||||
TCGLabel *fs = gen_new_label();
|
||||
|
||||
switch (opc) {
|
||||
case OPC_BGEUC:
|
||||
if (rs == 0 && rt != 0) {
|
||||
/* OPC_BLEZALC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs);
|
||||
} else if (rs != 0 && rt != 0 && rs == rt) {
|
||||
/* OPC_BGEZALC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs);
|
||||
} else {
|
||||
/* OPC_BGEUC */
|
||||
tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs);
|
||||
}
|
||||
break;
|
||||
case OPC_BLTUC:
|
||||
if (rs == 0 && rt != 0) {
|
||||
/* OPC_BGTZALC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs);
|
||||
} else if (rs != 0 && rt != 0 && rs == rt) {
|
||||
/* OPC_BLTZALC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs);
|
||||
} else {
|
||||
/* OPC_BLTUC */
|
||||
tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs);
|
||||
}
|
||||
break;
|
||||
case OPC_BGEC:
|
||||
if (rs == 0 && rt != 0) {
|
||||
/* OPC_BLEZC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs);
|
||||
} else if (rs != 0 && rt != 0 && rs == rt) {
|
||||
/* OPC_BGEZC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs);
|
||||
} else {
|
||||
/* OPC_BGEC */
|
||||
tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs);
|
||||
}
|
||||
break;
|
||||
case OPC_BLTC:
|
||||
if (rs == 0 && rt != 0) {
|
||||
/* OPC_BGTZC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs);
|
||||
} else if (rs != 0 && rt != 0 && rs == rt) {
|
||||
/* OPC_BLTZC */
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs);
|
||||
} else {
|
||||
/* OPC_BLTC */
|
||||
tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs);
|
||||
}
|
||||
break;
|
||||
case OPC_BEQZC:
|
||||
tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs);
|
||||
break;
|
||||
default:
|
||||
MIPS_INVAL("Compact conditional branch/jump");
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Generating branch here as compact branches don't have delay slot */
|
||||
gen_goto_tb(ctx, 1, ctx->btarget);
|
||||
gen_set_label(fs);
|
||||
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next + 4);
|
||||
}
|
||||
|
||||
out:
|
||||
tcg_temp_free(t0);
|
||||
tcg_temp_free(t1);
|
||||
}
|
||||
|
||||
|
||||
/* nanoMIPS CP1 Branches */
|
||||
static void gen_compute_branch_cp1_nm(DisasContext *ctx, uint32_t op,
|
||||
int32_t ft, int32_t offset)
|
||||
{
|
||||
target_ulong btarget;
|
||||
TCGv_i64 t0 = tcg_temp_new_i64();
|
||||
|
||||
gen_load_fpr64(ctx, t0, ft);
|
||||
tcg_gen_andi_i64(t0, t0, 1);
|
||||
|
||||
btarget = addr_add(ctx, ctx->base.pc_next + 4, offset);
|
||||
|
||||
switch (op) {
|
||||
case NM_BC1EQZC:
|
||||
tcg_gen_xori_i64(t0, t0, 1);
|
||||
ctx->hflags |= MIPS_HFLAG_BC;
|
||||
break;
|
||||
case NM_BC1NEZC:
|
||||
/* t0 already set */
|
||||
ctx->hflags |= MIPS_HFLAG_BC;
|
||||
break;
|
||||
default:
|
||||
MIPS_INVAL("cp1 cond branch");
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tcg_gen_trunc_i64_tl(bcond, t0);
|
||||
|
||||
ctx->btarget = btarget;
|
||||
|
||||
out:
|
||||
tcg_temp_free_i64(t0);
|
||||
}
|
||||
|
||||
|
||||
static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt)
|
||||
{
|
||||
@ -18276,16 +18591,129 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx)
|
||||
}
|
||||
break;
|
||||
case NM_MOVE_BALC:
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
int32_t s = sextract32(ctx->opcode, 0, 1) << 21 |
|
||||
extract32(ctx->opcode, 1, 20) << 1;
|
||||
rd = (extract32(ctx->opcode, 24, 1)) == 0 ? 4 : 5;
|
||||
rt = decode_gpr_gpr4_zero(extract32(ctx->opcode, 25, 1) << 3 |
|
||||
extract32(ctx->opcode, 21, 3));
|
||||
gen_load_gpr(t0, rt);
|
||||
tcg_gen_mov_tl(cpu_gpr[rd], t0);
|
||||
gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s);
|
||||
tcg_temp_free(t0);
|
||||
}
|
||||
break;
|
||||
case NM_P_BAL:
|
||||
{
|
||||
int32_t s = sextract32(ctx->opcode, 0, 1) << 25 |
|
||||
extract32(ctx->opcode, 1, 24) << 1;
|
||||
|
||||
if ((extract32(ctx->opcode, 25, 1)) == 0) {
|
||||
/* BC */
|
||||
gen_compute_branch_nm(ctx, OPC_BEQ, 4, 0, 0, s);
|
||||
} else {
|
||||
/* BALC */
|
||||
gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NM_P_J:
|
||||
switch (extract32(ctx->opcode, 12, 4)) {
|
||||
case NM_JALRC:
|
||||
case NM_JALRC_HB:
|
||||
gen_compute_branch_nm(ctx, OPC_JALR, 4, rs, rt, 0);
|
||||
break;
|
||||
case NM_P_BALRSC:
|
||||
gen_compute_nanomips_pbalrsc_branch(ctx, rs, rt);
|
||||
break;
|
||||
default:
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NM_P_BR1:
|
||||
{
|
||||
int32_t s = sextract32(ctx->opcode, 0, 1) << 14 |
|
||||
extract32(ctx->opcode, 1, 13) << 1;
|
||||
switch (extract32(ctx->opcode, 14, 2)) {
|
||||
case NM_BEQC:
|
||||
gen_compute_branch_nm(ctx, OPC_BEQ, 4, rs, rt, s);
|
||||
break;
|
||||
case NM_P_BR3A:
|
||||
s = sextract32(ctx->opcode, 0, 1) << 14 |
|
||||
extract32(ctx->opcode, 1, 13) << 1;
|
||||
check_cp1_enabled(ctx);
|
||||
switch (extract32(ctx->opcode, 16, 5)) {
|
||||
case NM_BC1EQZC:
|
||||
gen_compute_branch_cp1_nm(ctx, OPC_BC1EQZ, rt, s);
|
||||
break;
|
||||
case NM_BC1NEZC:
|
||||
gen_compute_branch_cp1_nm(ctx, OPC_BC1NEZ, rt, s);
|
||||
break;
|
||||
default:
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NM_BGEC:
|
||||
if (rs == rt) {
|
||||
gen_compute_compact_branch_nm(ctx, OPC_BC, rs, rt, s);
|
||||
} else {
|
||||
gen_compute_compact_branch_nm(ctx, OPC_BGEC, rs, rt, s);
|
||||
}
|
||||
break;
|
||||
case NM_BGEUC:
|
||||
if (rs == rt || rt == 0) {
|
||||
gen_compute_compact_branch_nm(ctx, OPC_BC, 0, 0, s);
|
||||
} else if (rs == 0) {
|
||||
gen_compute_compact_branch_nm(ctx, OPC_BEQZC, rt, 0, s);
|
||||
} else {
|
||||
gen_compute_compact_branch_nm(ctx, OPC_BGEUC, rs, rt, s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NM_P_BR2:
|
||||
{
|
||||
int32_t s = sextract32(ctx->opcode, 0, 1) << 14 |
|
||||
extract32(ctx->opcode, 1, 13) << 1;
|
||||
switch (extract32(ctx->opcode, 14, 2)) {
|
||||
case NM_BNEC:
|
||||
gen_compute_branch_nm(ctx, OPC_BNE, 4, rs, rt, s);
|
||||
break;
|
||||
case NM_BLTC:
|
||||
if (rs != 0 && rt != 0 && rs == rt) {
|
||||
/* NOP */
|
||||
ctx->hflags |= MIPS_HFLAG_FBNSLOT;
|
||||
} else {
|
||||
gen_compute_compact_branch_nm(ctx, OPC_BLTC, rs, rt, s);
|
||||
}
|
||||
break;
|
||||
case NM_BLTUC:
|
||||
if (rs == 0 || rs == rt) {
|
||||
/* NOP */
|
||||
ctx->hflags |= MIPS_HFLAG_FBNSLOT;
|
||||
} else {
|
||||
gen_compute_compact_branch_nm(ctx, OPC_BLTUC, rs, rt, s);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NM_P_BRI:
|
||||
{
|
||||
int32_t s = sextract32(ctx->opcode, 0, 1) << 11 |
|
||||
extract32(ctx->opcode, 1, 10) << 1;
|
||||
uint32_t u = extract32(ctx->opcode, 11, 7);
|
||||
|
||||
gen_compute_imm_branch(ctx, extract32(ctx->opcode, 18, 3),
|
||||
rt, u, s);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
generate_exception_end(ctx, EXCP_RI);
|
||||
|
Loading…
Reference in New Issue
Block a user