summaryrefslogtreecommitdiffstats
path: root/target/mips/translate.c
diff options
context:
space:
mode:
authorStefan Markovic2018-08-02 16:16:23 +0200
committerAleksandar Markovic2018-08-24 17:51:59 +0200
commit11d0fc10b7efe3d0404a71e855c0d9f521ce3d66 (patch)
tree09753a31e6b028802c4f981caaf65958bddb142a /target/mips/translate.c
parenttarget/mips: Implement emulation of nanoMIPS LLWP/SCWP pair (diff)
downloadqemu-11d0fc10b7efe3d0404a71e855c0d9f521ce3d66.tar.gz
qemu-11d0fc10b7efe3d0404a71e855c0d9f521ce3d66.tar.xz
qemu-11d0fc10b7efe3d0404a71e855c0d9f521ce3d66.zip
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>
Diffstat (limited to 'target/mips/translate.c')
-rw-r--r--target/mips/translate.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/target/mips/translate.c b/target/mips/translate.c
index 28d9d0c369..cae4ac0819 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -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);