Use comparisons vs memory, short branches, and
an indirect memory jump.
Signed-off-by: Richard Henderson <[email protected]>
---
tcg/tcg.c | 1 +
tcg/x86_64/tcg-target.c.inc | 77 ++++++++++++++++++++++++++++++++++++-
2 files changed, 77 insertions(+), 1 deletion(-)
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 671cf8fd0e..24eee302eb 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -61,6 +61,7 @@
#ifdef CONFIG_USER_ONLY
#include "user/guest-base.h"
#endif
+#include "../accel/tcg/tb-hash.h"
/* Forward declarations for functions declared in tcg-target.c.inc and
used here. */
diff --git a/tcg/x86_64/tcg-target.c.inc b/tcg/x86_64/tcg-target.c.inc
index 04c83fbe6b..d21fc2e9c7 100644
--- a/tcg/x86_64/tcg-target.c.inc
+++ b/tcg/x86_64/tcg-target.c.inc
@@ -1532,6 +1532,12 @@ static int tcg_out_cmp(TCGContext *s, TCGCond cond,
TCGArg arg1,
return jz;
}
+static void tgen_cmp_mem_reg(TCGContext *s, int rexw, TCGReg reg,
+ TCGReg base, intptr_t ofs)
+{
+ tcg_out_modrm_offset(s, OPC_CMP_GvEv | rexw, reg, base, ofs);
+}
+
static void tcg_out_brcond(TCGContext *s, int rexw, TCGCond cond,
TCGArg arg1, TCGArg arg2, int const_arg2,
TCGLabel *label, bool small)
@@ -3374,8 +3380,77 @@ static const TCGOutOpLea outop_lea = {
.out = tgen_lea,
};
+static void tgen_goto_jc_cmp_part(TCGContext *s, TCGArg val, bool const_val,
+ TCGReg scratch, const TranslationBlock *tb,
+ TCGReg base, ptrdiff_t ofs)
+{
+ if (!const_val) {
+ /* cmpq ofs(base), %val */
+ tgen_cmp_mem_reg(s, P_REXW, val, base, ofs);
+ } else if (val == (int8_t)val) {
+ tcg_out_modrm_offset(s, OPC_ARITH_EvIb | P_REXW, ARITH_CMP, base, ofs);
+ tcg_out8(s, val);
+ } else if (val == (int32_t)val) {
+ tcg_out_modrm_offset(s, OPC_ARITH_EvIz | P_REXW, ARITH_CMP, base, ofs);
+ tcg_out32(s, val);
+ } else {
+ uintptr_t tb_slot = (uintptr_t)tb + ofs;
+
+ tcg_out_ld(s, TCG_TYPE_I64, scratch, base, ofs);
+ /*
+ * Compare the value vs a rip-relative reference.
+ * If the value is unchanged from the current TB,
+ * we can reuse that. Otherwise drop it in the pool.
+ */
+ if (val == *(uint64_t *)tb_slot) {
+ tcg_out_modrm_sib_offset(s, OPC_CMP_GvEv | P_REXW, scratch,
+ TCG_REG_NONE, TCG_REG_NONE, 0, tb_slot);
+ } else {
+ tcg_out_modrm_pool(s, OPC_CMP_GvEv | P_REXW, scratch);
+ new_pool_label(s, val, R_386_PC32, s->code_ptr - 4, -4);
+ }
+ }
+}
+
+static void tgen_goto_jc(TCGContext *s, TCGReg ptr, TCGReg pc,
+ TCGArg cs, bool const_cs, TCGArg fl, bool const_fl,
+ TCGLabel *label)
+{
+ const TranslationBlock *tb = s->gen_tb;
+ unsigned free = (ALL_GENERAL_REGS & ~s->reserved_regs & ~(1 << ptr)
+ & ~(const_fl ? 0 : 1 << fl));
+ TCGReg scratch = ctz32(free);
+
+ /* ptr is CPUJumpCache */
+
+ /* Validate pc match */
+ tgen_cmp_mem_reg(s, P_REXW, pc, ptr, offsetof(CPUJumpCache, array[0].pc));
+ tcg_out_jxx(s, JCC_JNE, label, true);
+
+ /* Load and validate tb non-null */
+ tcg_out_ld(s, TCG_TYPE_PTR, ptr, ptr, offsetof(CPUJumpCache, array[0].tb));
+ tcg_out_brcond(s, P_REXW, TCG_COND_EQ, ptr, 0, true, label, true);
+
+ /* ptr is now TranslationBlock */
+
+ /* Validate cs_base match */
+ tgen_goto_jc_cmp_part(s, cs, const_cs, scratch, tb, ptr,
+ offsetof(TranslationBlock, cs_base));
+ tcg_out_jxx(s, JCC_JNE, label, true);
+
+ /* Validate cflags:flags match */
+ tgen_goto_jc_cmp_part(s, fl, const_fl, scratch, tb, ptr,
+ offsetof(TranslationBlock, flags));
+ tcg_out_jxx(s, JCC_JNE, label, true);
+
+ /* Jump to tb */
+ tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev,
+ ptr, offsetof(TranslationBlock, tc.ptr));
+}
+
static const TCGOutOpGotoJC outop_goto_jc = {
- .base.static_constraint = C_NotImplemented,
+ .base.static_constraint = C_O0_I4(r, r, ri, ri),
+ .out = tgen_goto_jc,
};
static int const umin_insn[4] = {
--
2.53.0