From: Matt Turner <[email protected]> At translation time the caller often already has the destination PC in a temp and knows the flags, cflags and cs_base any destination it may reach has to match because they are the same as the ones as the current block being generated. But the promise that the flags are known (or unchanged) is subtle.
So add new entry points with new semantics and --enable-debug-tcg checks against get_tb_cpu_state() at run time. Signed-off-by: Matt Turner <[email protected]> [rth: Split out target changes; add jc3 variant; expand via tcg-op-def.h.inc] Signed-off-by: Richard Henderson <[email protected]> --- accel/tcg/tcg-runtime.h | 4 +++ include/tcg/tcg-op-common.h | 29 +++++++++++++++++++ include/tcg/tcg-op.h | 4 +++ include/tcg/tcg-op-def.h.inc | 2 ++ accel/tcg/cpu-exec.c | 27 ++++++++++++++++++ tcg/tcg-op.c | 55 ++++++++++++++++++++++++++++++++---- 6 files changed, 115 insertions(+), 6 deletions(-) diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 0dda2079fd0..098c2235b5f 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -9,6 +9,10 @@ DEF_HELPER_FLAGS_1(ctpop_i64, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(lookup_tb_ptr, TCG_CALL_NO_WG_SE, cptr, env) +#ifdef CONFIG_DEBUG_TCG +DEF_HELPER_FLAGS_4(goto_jc_check, TCG_CALL_NO_WG, void, env, i64, i64, i32) +#endif + DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env) #ifndef IN_HELPER_PROTO diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 2bcf737aa56..5102f9bb4dd 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -85,6 +85,35 @@ void tcg_gen_goto_tb(unsigned idx); */ void tcg_gen_lookup_and_goto_ptr(void); +/** + * tcg_gen_goto_jc3_i32() - dispatch to the destination TB via the jump cache + * tcg_gen_goto_jc3_i64() - dispatch to the destination TB via the jump cache + * @pc: temp holding the destination guest PC + * @cs_base: temp holding the destination guest cs_base + * @flags: the destination guest flags + * + * The contract is that the values provided inline are exactly the + * cpu state that would be returned by TCGCPUOps::get_tb_cpu_state. + * --enable-debug-tcg checks the contract at runtime. + * + * Using those values, the lookup may be done inline. + * + * Actually declared and defined via tcg-op-def.h.inc. + */ + +/** + * tcg_gen_goto_jc_i32() - dispatch to the destination TB via the jump cache + * tcg_gen_goto_jc_i64() - dispatch to the destination TB via the jump cache + * @pc: temp holding the destination guest PC + * + * Similar, but cs_base and flags are taken from the current TB. + * + * Thus the contract is that no cpu state changes have occured + * that affect the translation block flags. + * + * Actually declared and defined via tcg-op-def.h.inc. + */ + void tcg_gen_plugin_cb(unsigned from); QEMU_ARG_NONNULL void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index a7d001c959c..02732652b8f 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -175,6 +175,8 @@ typedef TCGv_i64 TCGv; #define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i64 #define tcg_gen_dup_tl_vec tcg_gen_dup_i64_vec #define tcg_gen_dup_tl tcg_gen_dup_i64 +#define tcg_gen_goto_jc_tl tcg_gen_goto_jc_i64 +#define tcg_gen_goto_jc3_tl tcg_gen_goto_jc3_i64 #define dup_const_tl dup_const #else #define tcg_gen_movi_tl tcg_gen_movi_i32 @@ -299,6 +301,8 @@ typedef TCGv_i64 TCGv; #define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i32 #define tcg_gen_dup_tl_vec tcg_gen_dup_i32_vec #define tcg_gen_dup_tl tcg_gen_dup_i32 +#define tcg_gen_goto_jc_tl tcg_gen_goto_jc_i32 +#define tcg_gen_goto_jc3_tl tcg_gen_goto_jc3_i32 #define dup_const_tl(VECE, C) \ (__builtin_constant_p(VECE) \ diff --git a/include/tcg/tcg-op-def.h.inc b/include/tcg/tcg-op-def.h.inc index b60edca6e42..97024fcb222 100644 --- a/include/tcg/tcg-op-def.h.inc +++ b/include/tcg/tcg-op-def.h.inc @@ -31,6 +31,8 @@ DEF2(ext16u, TCGV, TCGV) DEF4(extract, TCGV, TCGV, unsigned, unsigned) DEF4(extract2, TCGV, TCGV, TCGV, unsigned) DEF3(eqv, TCGV, TCGV, TCGV) +DEF1(goto_jc, TCGV) +DEF3(goto_jc3, TCGV, TCGv_i64, unsigned) DEF3(ld, TCGV, TCGv_ptr, tcg_target_long) DEF3(ld8s, TCGV, TCGv_ptr, tcg_target_long) DEF3(ld8u, TCGV, TCGv_ptr, tcg_target_long) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 5226cfb7650..1493fe0c963 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -407,6 +407,33 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) return tb->tc.ptr; } +#ifdef CONFIG_DEBUG_TCG +/** + * helper_goto_jc_check: check the contract of tcg_gen_goto_jc_*() + * @env: current cpu state + * @pc: the destination pc the caller passed at translation time + * @cs_base: the cs_base the dispatching block was translated with + * @flags: the flags the dispatching block was translated with + * + * A goto_jc looks the destination up on the caller's @pc with the flags and + * cs_base of the block doing the dispatching, so all three have to be what + * get_tb_cpu_state() reports by the time the dispatch runs. That is a + * property of the translator, not of the generated code, so check it here + * rather than leaving a target that gets it wrong to be debugged as a block + * running with someone else's flags. + */ +void HELPER(goto_jc_check)(CPUArchState *env, uint64_t pc, uint64_t cs_base, + uint32_t flags) +{ + CPUState *cpu = env_cpu(env); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); + + assert(s.pc == pc); + assert(s.flags == flags); + assert(s.cs_base == cs_base); +} +#endif + /* Return the current PC from CPU, which may be cached in TB. */ static vaddr log_pc(CPUState *cpu, const TranslationBlock *tb) { diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index d271d83a7d2..09d9a7963ae 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -277,6 +277,7 @@ void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo) #define DEF6(NAME, T1, T2, T3, T4, T5, T6) \ QEMU_ARG_NONNULL static void glue(gen_,NAME)(TCGType, T1, T2, T3, T4, T5, T6); +#define TCGv_i64 TCGTemp * #define TCGv_ptr TCGTemp * #define TCGV TCGTemp * #define TINT int64_t @@ -286,6 +287,7 @@ void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo) #undef TINT #undef TCGV #undef TCGv_ptr +#undef TCGv_i64 #undef DEF1 #undef DEF2 @@ -1940,20 +1942,61 @@ void tcg_gen_goto_tb(unsigned idx) tcg_gen_op1i(INDEX_op_goto_tb, 0, idx); } +static void gen_lookup_tb_ptr_and_goto(void) +{ + TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); + + gen_helper_lookup_tb_ptr(ptr, tcg_env); + tcg_gen_op1i(INDEX_op_goto_ptr, TCG_TYPE_PTR, tcgv_ptr_arg(ptr)); + tcg_temp_free_ptr(ptr); +} + void tcg_gen_lookup_and_goto_ptr(void) { - TCGv_ptr ptr; - if (tcg_ctx->gen_tb->cflags & CF_NO_GOTO_PTR) { tcg_gen_exit_tb(NULL, 0); return; } plugin_gen_disable_mem_helpers(); - ptr = tcg_temp_ebb_new_ptr(); - gen_helper_lookup_tb_ptr(ptr, tcg_env); - tcg_gen_op1i(INDEX_op_goto_ptr, TCG_TYPE_PTR, tcgv_ptr_arg(ptr)); - tcg_temp_free_ptr(ptr); + gen_lookup_tb_ptr_and_goto(); +} + +static void gen_goto_jc3(TCGType type, TCGTemp *pc, + TCGTemp *cs_base, unsigned flags) +{ + plugin_gen_disable_mem_helpers(); + + /* + * Widen @pc to i64 because the jump cache is keyed on a vaddr; + * for a 32-bit guest PC that is its zero extension. + * Note that cs_base is target-specific, not a vaddr, and always 64-bit. + */ + if (type == TCG_TYPE_I32) { + TCGTemp *pc64 = tcg_temp_new_internal(TCG_TYPE_I64, TEMP_EBB); + gen_extu_i32_i64(pc64, pc); + pc = pc64; + } + +#ifdef CONFIG_DEBUG_TCG + /* + * The caller has asserted that no flags-affecting cpu state changes + * happened between this TB and the destination. Check it. + */ + gen_helper_goto_jc_check(tcg_env, temp_tcgv_i64(pc), + temp_tcgv_i64(cs_base), + tcg_constant_i32(flags)); +#endif + + gen_lookup_tb_ptr_and_goto(); +} + +static void gen_goto_jc(TCGType type, TCGTemp *pc) +{ + const TranslationBlock *tb = tcg_ctx->gen_tb; + TCGTemp *cs_base = tcg_constant_internal(TCG_TYPE_I64, tb->cs_base); + + gen_goto_jc3(type, pc, cs_base, tb->flags); } /* -- 2.53.0
