@@ -1036,6 +1036,108 @@ static inline void gen_check_sp_alignment(DisasContext *s)
*/
}
+typedef struct DisasCompare {
+ TCGCond cond;
+ TCGv_i32 value;
+ bool value_global;
+} DisasCompare;
+
+/*
+ * generate a conditional based on ARM condition code cc.
+ * This is common between ARM and Aarch64 targets.
+ */
+static void arm_test_cc(DisasCompare *cmp, int cc)
+{
+ TCGv_i32 value;
+ TCGCond cond;
+ bool global = true;
+
+ switch (cc) {
+ case 0: /* eq: Z */
+ case 1: /* ne: !Z */
+ cond = TCG_COND_EQ;
+ value = cpu_ZF;
+ break;
+
+ case 2: /* cs: C */
+ case 3: /* cc: !C */
+ cond = TCG_COND_NE;
+ value = cpu_CF;
+ break;
+
+ case 4: /* mi: N */
+ case 5: /* pl: !N */
+ cond = TCG_COND_LT;
+ value = cpu_NF;
+ break;
+
+ case 6: /* vs: V */
+ case 7: /* vc: !V */
+ cond = TCG_COND_LT;
+ value = cpu_VF;
+ break;
+
+ case 8: /* hi: C && !Z */
+ case 9: /* ls: !C || Z */
+ cond = TCG_COND_NE;
+ value = tcg_temp_new_i32();
+ global = false;
+ tcg_gen_neg_i32(value, cpu_CF);
+ tcg_gen_and_i32(value, value, cpu_ZF);
+ break;
+
+ case 10: /* ge: N == V -> N ^ V == 0 */
+ case 11: /* lt: N != V -> N ^ V != 0 */
+ cond = TCG_COND_GE;
+ value = tcg_temp_new_i32();
+ global = false;
+ tcg_gen_xor_i32(value, cpu_VF, cpu_NF);
+ break;
+
+ case 12: /* gt: !Z && N == V */
+ case 13: /* le: Z || N != V */
+ cond = TCG_COND_NE;
+ value = tcg_temp_new_i32();
+ global = false;
+ tcg_gen_xor_i32(value, cpu_VF, cpu_NF);
+ tcg_gen_sari_i32(value, value, 31);
+ tcg_gen_andc_i32(value, cpu_ZF, value);
+ break;
+
+ default:
+ fprintf(stderr, "Bad condition code 0x%x\n", cc);
+ abort();
+ }
+
+ if (cc & 1) {
+ cond = tcg_invert_cond(cond);
+ }
+
+ cmp->cond = cond;
+ cmp->value = value;
+ cmp->value_global = global;
+}
+
+static void arm_free_cc(DisasCompare *cmp)
+{
+ if (!cmp->value_global) {
+ tcg_temp_free_i32(cmp->value);
+ }
+}
+
+static void arm_jump_cc(DisasCompare *cmp, TCGLabel *label)
+{
+ tcg_gen_brcondi_i32(cmp->cond, cmp->value, 0, label);
+}
+
+static void arm_gen_test_cc(int cc, TCGLabel *label)
+{
+ DisasCompare cmp;
+ arm_test_cc(&cmp, cc);
+ arm_jump_cc(&cmp, label);
+ arm_free_cc(&cmp);
+}
+
/*
* This provides a simple table based table lookup decoder. It is
* intended to be used when the relevant bits for decode are too
@@ -732,82 +732,106 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b)
}
#undef PAS_OP
+typedef struct DisasCompare {
+ TCGCond cond;
+ TCGv_i32 value;
+ bool value_global;
+} DisasCompare;
+
/*
- * generate a conditional branch based on ARM condition code cc.
+ * generate a conditional based on ARM condition code cc.
* This is common between ARM and Aarch64 targets.
*/
-void arm_gen_test_cc(int cc, TCGLabel *label)
+static void arm_test_cc(DisasCompare *cmp, int cc)
{
- TCGv_i32 tmp;
- TCGLabel *inv;
+ TCGv_i32 value;
+ TCGCond cond;
+ bool global = true;
switch (cc) {
case 0: /* eq: Z */
- tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label);
- break;
case 1: /* ne: !Z */
- tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label);
+ cond = TCG_COND_EQ;
+ value = cpu_ZF;
break;
+
case 2: /* cs: C */
- tcg_gen_brcondi_i32(TCG_COND_NE, cpu_CF, 0, label);
- break;
case 3: /* cc: !C */
- tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label);
+ cond = TCG_COND_NE;
+ value = cpu_CF;
break;
+
case 4: /* mi: N */
- tcg_gen_brcondi_i32(TCG_COND_LT, cpu_NF, 0, label);
- break;
case 5: /* pl: !N */
- tcg_gen_brcondi_i32(TCG_COND_GE, cpu_NF, 0, label);
+ cond = TCG_COND_LT;
+ value = cpu_NF;
break;
+
case 6: /* vs: V */
- tcg_gen_brcondi_i32(TCG_COND_LT, cpu_VF, 0, label);
- break;
case 7: /* vc: !V */
- tcg_gen_brcondi_i32(TCG_COND_GE, cpu_VF, 0, label);
+ cond = TCG_COND_LT;
+ value = cpu_VF;
break;
+
case 8: /* hi: C && !Z */
- inv = gen_new_label();
- tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, inv);
- tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label);
- gen_set_label(inv);
- break;
- case 9: /* ls: !C || Z */
- tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label);
- tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label);
+ case 9: /* ls: !C || Z -> !(C && !Z) */
+ cond = TCG_COND_NE;
+ value = tcg_temp_new_i32();
+ global = false;
+ tcg_gen_neg_i32(value, cpu_CF);
+ tcg_gen_and_i32(value, value, cpu_ZF);
break;
+
case 10: /* ge: N == V -> N ^ V == 0 */
- tmp = tcg_temp_new_i32();
- tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
- tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
- tcg_temp_free_i32(tmp);
- break;
case 11: /* lt: N != V -> N ^ V != 0 */
- tmp = tcg_temp_new_i32();
- tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
- tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
- tcg_temp_free_i32(tmp);
+ cond = TCG_COND_GE;
+ value = tcg_temp_new_i32();
+ global = false;
+ tcg_gen_xor_i32(value, cpu_VF, cpu_NF);
break;
+
case 12: /* gt: !Z && N == V */
- inv = gen_new_label();
- tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, inv);
- tmp = tcg_temp_new_i32();
- tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
- tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
- tcg_temp_free_i32(tmp);
- gen_set_label(inv);
- break;
case 13: /* le: Z || N != V */
- tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label);
- tmp = tcg_temp_new_i32();
- tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
- tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
- tcg_temp_free_i32(tmp);
+ cond = TCG_COND_NE;
+ value = tcg_temp_new_i32();
+ global = false;
+ tcg_gen_xor_i32(value, cpu_VF, cpu_NF);
+ tcg_gen_sari_i32(value, value, 31);
+ tcg_gen_andc_i32(value, cpu_ZF, value);
break;
+
default:
fprintf(stderr, "Bad condition code 0x%x\n", cc);
abort();
}
+
+ if (cc & 1) {
+ cond = tcg_invert_cond(cond);
+ }
+
+ cmp->cond = cond;
+ cmp->value = value;
+ cmp->value_global = global;
+}
+
+static void arm_free_cc(DisasCompare *cmp)
+{
+ if (!cmp->value_global) {
+ tcg_temp_free_i32(cmp->value);
+ }
+}
+
+static void arm_jump_cc(DisasCompare *cmp, TCGLabel *label)
+{
+ tcg_gen_brcondi_i32(cmp->cond, cmp->value, 0, label);
+}
+
+static void arm_gen_test_cc(int cc, TCGLabel *label)
+{
+ DisasCompare cmp;
+ arm_test_cc(&cmp, cc);
+ arm_jump_cc(&cmp, label);
+ arm_free_cc(&cmp);
}
static const uint8_t table_logic_cc[16] = {
@@ -119,6 +119,4 @@ static inline void aarch64_cpu_dump_state(CPUState *cs, FILE *f,
}
#endif
-void arm_gen_test_cc(int cc, TCGLabel *label);
-
#endif /* TARGET_ARM_TRANSLATE_H */
Splitting arm_gen_test_cc into 3 functions, so that it can be reused for non-branch TCG comparisons. Note that this is also a bug fix for aarch64. At present, we have branches using the 32-bit (translate.c) versions of cpu_[NZCV]F, but we set the flags using the 64-bit (translate-a64.c) versions of cpu_[NZCV]F. From the view of the TCG code generator, these are unrelated variables. The bug is hard to see because we currently only read these variables from branches, and upon reaching a branch TCG will first spill live variables and then reload the arguments of the branch. Since the 32-bit versions were never live until reaching the branch, we'd re-read the data that had just been spilled from the 64-bit versions. Accept the code duplication for now, as the 64-bit functions will diverge. Signed-off-by: Richard Henderson <rth@twiddle.net> --- target-arm/translate-a64.c | 102 +++++++++++++++++++++++++++++++++++++++ target-arm/translate.c | 116 +++++++++++++++++++++++++++------------------ target-arm/translate.h | 2 - 3 files changed, 172 insertions(+), 48 deletions(-)