A common helper for all conditional branches, using DisasCompare64.
Signed-off-by: Richard Henderson <[email protected]>
---
target/arm/tcg/translate-a64.c | 142 ++++++++++++++++++---------------
1 file changed, 79 insertions(+), 63 deletions(-)
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index c8d1251f13..e66ccdff6f 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -584,6 +584,50 @@ static void gen_goto_tb(DisasContext *s, unsigned
tb_slot_idx, int64_t diff)
}
}
+static bool gen_cbranch(DisasContext *s, const DisasCompare64 *c,
+ int64_t taken_diff)
+{
+ int64_t not_taken_diff = 4;
+ bool may_goto_t = use_goto_tb(s, s->pc_curr + taken_diff);
+ bool may_goto_f = use_goto_tb(s, s->pc_curr + not_taken_diff);
+
+ /*
+ * If there is a choice, place a real goto_tb path first.
+ * If neither path can use goto_tb, convert to an indirect branch.
+ */
+ if (may_goto_f) {
+ DisasLabel l = gen_disas_label(s);
+
+ tcg_gen_brcond_i64(c->cond, c->v1, c->v2, l.label);
+ gen_goto_tb_int(s, 0, not_taken_diff);
+
+ set_disas_label(s, l);
+ if (may_goto_t) {
+ gen_goto_tb_int(s, 1, taken_diff);
+ s->base.is_jmp = DISAS_NORETURN;
+ return true;
+ }
+ gen_a64_update_pc(s, taken_diff);
+ } else if (may_goto_t) {
+ DisasLabel l = gen_disas_label(s);
+
+ tcg_gen_brcond_i64(tcg_invert_cond(c->cond), c->v1, c->v2, l.label);
+ gen_goto_tb_int(s, 1, taken_diff);
+
+ set_disas_label(s, l);
+ gen_a64_update_pc(s, not_taken_diff);
+ } else {
+ TCGv_i64 pc_t = tcg_temp_new_i64();
+ TCGv_i64 pc_f = tcg_temp_new_i64();
+
+ gen_pc_plus_diff(s, pc_t, taken_diff);
+ gen_pc_plus_diff(s, pc_f, not_taken_diff);
+ tcg_gen_movcond_i64(c->cond, cpu_pc, c->v1, c->v2, pc_t, pc_f);
+ s->pc_save = -1;
+ }
+ return gen_indirect_branch(s);
+}
+
/*
* Register access functions
*
@@ -1758,38 +1802,26 @@ static bool trans_BL(DisasContext *s, arg_i *a)
static bool trans_CBZ(DisasContext *s, arg_cbz *a)
{
- DisasLabel match;
- TCGv_i64 tcg_cmp;
+ DisasCompare64 c = {
+ .cond = a->nz ? TCG_COND_NE : TCG_COND_EQ,
+ .v1 = read_cpu_reg(s, a->rt, a->sf),
+ .v2 = tcg_constant_i64(0),
+ };
- tcg_cmp = read_cpu_reg(s, a->rt, a->sf);
reset_btype(s);
-
- match = gen_disas_label(s);
- tcg_gen_brcondi_i64(a->nz ? TCG_COND_NE : TCG_COND_EQ,
- tcg_cmp, 0, match.label);
- gen_goto_tb(s, 0, 4);
- set_disas_label(s, match);
- gen_goto_tb(s, 1, a->imm);
- return true;
+ return gen_cbranch(s, &c, a->imm);
}
static bool trans_TBZ(DisasContext *s, arg_tbz *a)
{
- DisasLabel match;
- TCGv_i64 tcg_cmp;
-
- tcg_cmp = tcg_temp_new_i64();
- tcg_gen_andi_i64(tcg_cmp, cpu_reg(s, a->rt), 1ULL << a->bitpos);
+ DisasCompare64 c = {
+ .cond = a->nz ? TCG_COND_TSTNE : TCG_COND_TSTEQ,
+ .v1 = cpu_reg(s, a->rt),
+ .v2 = tcg_constant_i64(1ULL << a->bitpos),
+ };
reset_btype(s);
-
- match = gen_disas_label(s);
- tcg_gen_brcondi_i64(a->nz ? TCG_COND_NE : TCG_COND_EQ,
- tcg_cmp, 0, match.label);
- gen_goto_tb(s, 0, 4);
- set_disas_label(s, match);
- gen_goto_tb(s, 1, a->imm);
- return true;
+ return gen_cbranch(s, &c, a->imm);
}
static bool trans_B_cond(DisasContext *s, arg_B_cond *a)
@@ -1801,11 +1833,10 @@ static bool trans_B_cond(DisasContext *s, arg_B_cond *a)
reset_btype(s);
if (a->cond < 0x0e) {
/* genuinely conditional branches */
- DisasLabel match = gen_disas_label(s);
- arm_gen_test_cc(a->cond, match.label);
- gen_goto_tb(s, 0, 4);
- set_disas_label(s, match);
- gen_goto_tb(s, 1, a->imm);
+ DisasCompare64 c;
+
+ a64_test_cc(&c, a->cond);
+ return gen_cbranch(s, &c, a->imm);
} else {
/* 0xe and 0xf are both "always" conditions */
gen_goto_tb(s, 0, a->imm);
@@ -1825,35 +1856,27 @@ static bool trans_CB_cond(DisasContext *s, arg_CB_cond
*a)
[6] = TCG_COND_EQ,
[7] = TCG_COND_NE,
};
- TCGCond cond = cb_cond[a->cc];
- TCGv_i64 t, m;
- DisasLabel match;
+ DisasCompare64 c = { .cond = cb_cond[a->cc] };
- if (!dc_isar_feature(aa64_cmpbr, s) || cond == TCG_COND_NEVER) {
+ if (!dc_isar_feature(aa64_cmpbr, s) || c.cond == TCG_COND_NEVER) {
return false;
}
- t = cpu_reg(s, a->rt);
- m = cpu_reg(s, a->rm);
+ c.v1 = cpu_reg(s, a->rt);
+ c.v1 = cpu_reg(s, a->rm);
if (a->esz != MO_64) {
- MemOp mop = a->esz | (is_signed_cond(cond) ? MO_SIGN : 0);
+ MemOp mop = a->esz | (is_signed_cond(c.cond) ? MO_SIGN : 0);
TCGv_i64 tt = tcg_temp_new_i64();
TCGv_i64 tm = tcg_temp_new_i64();
- tcg_gen_ext_i64(tt, t, mop);
- tcg_gen_ext_i64(tm, m, mop);
- t = tt;
- m = tm;
+ tcg_gen_ext_i64(tt, c.v1, mop);
+ tcg_gen_ext_i64(tm, c.v1, mop);
+ c.v1 = tt;
+ c.v1 = tm;
}
reset_btype(s);
- match = gen_disas_label(s);
-
- tcg_gen_brcond_i64(cond, t, m, match.label);
- gen_goto_tb(s, 0, 4);
- set_disas_label(s, match);
- gen_goto_tb(s, 1, a->imm);
- return true;
+ return gen_cbranch(s, &c, a->imm);
}
static bool trans_CB_cond_imm(DisasContext *s, arg_CB_cond_imm *a)
@@ -1869,34 +1892,27 @@ static bool trans_CB_cond_imm(DisasContext *s,
arg_CB_cond_imm *a)
[6] = TCG_COND_EQ,
[7] = TCG_COND_NE,
};
- TCGCond cond = cb_cond[a->cc];
- TCGv_i64 t;
- DisasLabel match;
+ DisasCompare64 c = { .cond = cb_cond[a->cc] };
- if (!dc_isar_feature(aa64_cmpbr, s) || cond == TCG_COND_NEVER) {
+ if (!dc_isar_feature(aa64_cmpbr, s) || c.cond == TCG_COND_NEVER) {
return false;
}
- t = cpu_reg(s, a->rt);
+ c.v1 = cpu_reg(s, a->rt);
if (!a->sf) {
TCGv_i64 tt = tcg_temp_new_i64();
- if (is_signed_cond(cond)) {
- tcg_gen_ext32s_i64(tt, t);
+ if (is_signed_cond(c.cond)) {
+ tcg_gen_ext32s_i64(tt, c.v1);
} else {
- tcg_gen_ext32u_i64(tt, t);
+ tcg_gen_ext32u_i64(tt, c.v1);
}
- t = tt;
+ c.v1 = tt;
}
+ c.v2 = tcg_constant_i64(a->imm6);
reset_btype(s);
- match = gen_disas_label(s);
-
- tcg_gen_brcondi_i64(cond, t, a->imm6, match.label);
- gen_goto_tb(s, 0, 4);
- set_disas_label(s, match);
- gen_goto_tb(s, 1, a->imm9);
- return true;
+ return gen_cbranch(s, &c, a->imm9);
}
static void set_btype_for_br(DisasContext *s, int rn)
--
2.53.0