On Mon, Jun 12, 2017 at 17:54:09 +0300, Lluís Vilanova wrote: > Signed-off-by: Lluís Vilanova <vilan...@ac.upc.edu> > --- > include/exec/gen-icount.h | 2 > include/exec/translate-all_template.h | 73 ++++++++++++ > include/qom/cpu.h | 22 ++++ > translate-all_template.h | 204 > +++++++++++++++++++++++++++++++++
I think this concept of "template" is quite painful. Find appended something that I find more palatable: it embeds DisasContextBase in DisasContext, so that we can have a standalone object with all generic code; target-specific code is called via an "ops" struct with function pointers that targets fill in. The target-specific DisasContext struct can then be retrieved from the base struct with container_of(). I'll send as a separate, proper patch the gen-icount changes; really having cpu_env there as a global seems wrong to me. What do you think? Emilio PS. Apply with `git am --scissors'. --- 8< --- Warning: INCOMPLETE, do not even think of merging! This is just to show an alternative approach to including C code from the target translators (ugh!). Only arm and aarch64 have been converted. This applies on top of this series: https://lists.gnu.org/archive/html/qemu-devel/2017-06/msg02833.html Signed-off-by: Emilio G. Cota <c...@braap.org> --- Makefile.target | 2 +- include/exec/exec-all.h | 2 +- include/exec/gen-icount.h | 6 +- include/exec/translator.h | 74 +++++++++++++++++++ target/arm/translate-a64.c | 130 ++++++++++++++++++---------------- target/arm/translate.c | 173 +++++++++++++++++++++++---------------------- target/arm/translate.h | 11 +-- translator.c | 170 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 411 insertions(+), 157 deletions(-) create mode 100644 include/exec/translator.h create mode 100644 translator.c diff --git a/Makefile.target b/Makefile.target index ce8dfe4..ef2d538 100644 --- a/Makefile.target +++ b/Makefile.target @@ -88,7 +88,7 @@ all: $(PROGS) stap ######################################################### # cpu emulator library -obj-y = exec.o translate-all.o cpu-exec.o +obj-y = exec.o translate-all.o cpu-exec.o translator.o obj-y += translate-common.o obj-y += cpu-exec-common.o obj-y += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 6ad31a8..d376546 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -22,6 +22,7 @@ #include "qemu-common.h" #include "exec/tb-context.h" +#include "exec/translator.h" /* allow to see translation results - the slowdown should be negligible, so we leave it */ #define DEBUG_DISAS @@ -37,7 +38,6 @@ typedef ram_addr_t tb_page_addr_t; /* is_jmp field values */ /* TODO: delete after all targets are transitioned to generic translation */ -#include "exec/translate-all_template.h" #define DISAS_NEXT DJ_NEXT /* next instruction can be analyzed */ #define DISAS_JUMP (DJ_TARGET + 0) /* only pc was modified dynamically */ #define DISAS_UPDATE (DJ_TARGET + 1) /* cpu state was modified dynamically */ diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index 547c979..f4ad610 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -8,7 +8,7 @@ static int icount_start_insn_idx; static TCGLabel *exitreq_label; -static inline void gen_tb_start(TranslationBlock *tb) +static inline void gen_tb_start(TranslationBlock *tb, TCGv_env cpu_env) { TCGv_i32 count, imm; @@ -59,14 +59,14 @@ static inline void gen_tb_end(TranslationBlock *tb, int num_insns) tcg_ctx.gen_op_buf[tcg_ctx.gen_op_buf[0].prev].next = 0; } -static inline void gen_io_start(void) +static inline void gen_io_start(TCGv_env cpu_env) { TCGv_i32 tmp = tcg_const_i32(1); tcg_gen_st_i32(tmp, cpu_env, -ENV_OFFSET + offsetof(CPUState, can_do_io)); tcg_temp_free_i32(tmp); } -static inline void gen_io_end(void) +static inline void gen_io_end(TCGv_env cpu_env) { TCGv_i32 tmp = tcg_const_i32(0); tcg_gen_st_i32(tmp, cpu_env, -ENV_OFFSET + offsetof(CPUState, can_do_io)); diff --git a/include/exec/translator.h b/include/exec/translator.h new file mode 100644 index 0000000..f2da424 --- /dev/null +++ b/include/exec/translator.h @@ -0,0 +1,74 @@ +#ifndef EXEC_TRANSLATOR_H +#define EXEC_TRANSLATOR_H + +#include "exec/exec-all.h" +#include "tcg.h" + +/** + * BreakpointHitType: + * @BH_MISS: No hit + * @BH_HIT_INSN: Hit, but continue translating instruction + * @BH_HIT_TB: Hit, stop translating TB + * + * How to react to a breakpoint hit. + */ +typedef enum BreakpointHitType { + BH_MISS, + BH_HIT_INSN, + BH_HIT_TB, +} BreakpointHitType; + +/** + * DisasJumpType: + * @DJ_NEXT: Next instruction in program order + * @DJ_TOO_MANY: Too many instructions executed + * @DJ_TARGET: Start of target-specific conditions + * + * What instruction to disassemble next. + */ +typedef enum DisasJumpType { + DJ_NEXT, + DJ_TOO_MANY, + DJ_TARGET, +} DisasJumpType; + +/** + * DisasContextBase: + * @tb: Translation block for this disassembly. + * @pc_first: Address of first guest instruction in this TB. + * @pc_next: Address of next guest instruction in this TB (current during + * disassembly). + * @num_insns: Number of translated instructions (including current). + * @singlestep_enabled: "Hardware" single stepping enabled. + * + * Architecture-agnostic disassembly context. + */ +typedef struct DisasContextBase { + TranslationBlock *tb; + target_ulong pc_first; + target_ulong pc_next; + DisasJumpType jmp_type; + unsigned int num_insns; + bool singlestep_enabled; +} DisasContextBase; + +/* all void-returning ops are optional, i.e. can be NULL */ +struct translator_ops { + void (*init_context)(DisasContextBase *, CPUArchState *); + void (*init_globals)(DisasContextBase *, CPUArchState *); + void (*tb_start)(DisasContextBase *, CPUArchState *); + void (*insn_start)(DisasContextBase *, CPUArchState *); + BreakpointHitType (*breakpoint_hit)(DisasContextBase *, CPUArchState *, + const CPUBreakpoint *); + target_ulong (*disas_insn)(DisasContextBase *, CPUArchState *); + DisasJumpType (*stop_check)(DisasContextBase *, CPUArchState *); + void (*stop)(DisasContextBase *, CPUArchState *); + int (*disas_flags)(const DisasContextBase *); +}; + +typedef struct translator_ops TranslatorOps; + +void translator_gen(const TranslatorOps *tr, DisasContextBase *base, + CPUState *cpu, TranslationBlock *tb, TCGv_env cpu_env); + +#endif /* EXEC_TRANSLATOR_H */ diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 508a016..c486d04 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -1561,7 +1561,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, } if ((s->base.tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - gen_io_start(); + gen_io_start(cpu_env); } tcg_rt = cpu_reg(s, rt); @@ -1593,7 +1593,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, if ((s->base.tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { /* I/O operations must end the TB here (whether read or write) */ - gen_io_end(); + gen_io_end(cpu_env); s->base.jmp_type = DJ_UPDATE; } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { /* We default to ending the TB on a coprocessor register write, @@ -11191,16 +11191,10 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) free_tmp_a64(s); } - - -/* Use separate top-level templates for each architecture */ -#define gen_intermediate_code gen_intermediate_code_aarch64 -#include "translate-all_template.h" -#undef gen_intermediate_code - -static void gen_intermediate_code_target_init_disas_context( - DisasContext *dc, CPUArchState *env) +static void a64_tr_init_dc(DisasContextBase *base, CPUArchState *env) { + DisasContext *dc = container_of(base, DisasContext, base); + dc->condjmp = 0; dc->aarch64 = 1; @@ -11211,17 +11205,17 @@ static void gen_intermediate_code_target_init_disas_context( !arm_el_is_aa64(env, 3); dc->thumb = 0; dc->sctlr_b = 0; - dc->be_data = ARM_TBFLAG_BE_DATA(dc->base.tb->flags) ? MO_BE : MO_LE; + dc->be_data = ARM_TBFLAG_BE_DATA(base->tb->flags) ? MO_BE : MO_LE; dc->condexec_mask = 0; dc->condexec_cond = 0; - dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(dc->base.tb->flags)); - dc->tbi0 = ARM_TBFLAG_TBI0(dc->base.tb->flags); - dc->tbi1 = ARM_TBFLAG_TBI1(dc->base.tb->flags); + dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(base->tb->flags)); + dc->tbi0 = ARM_TBFLAG_TBI0(base->tb->flags); + dc->tbi1 = ARM_TBFLAG_TBI1(base->tb->flags); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif - dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags); + dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(base->tb->flags); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_env_get_cpu(env)->cp_regs; @@ -11242,43 +11236,35 @@ static void gen_intermediate_code_target_init_disas_context( * emit code to generate a software step exception * end the TB */ - dc->ss_active = ARM_TBFLAG_SS_ACTIVE(dc->base.tb->flags); - dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(dc->base.tb->flags); + dc->ss_active = ARM_TBFLAG_SS_ACTIVE(base->tb->flags); + dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(base->tb->flags); dc->is_ldex = false; dc->ss_same_el = (arm_debug_target_el(env) == dc->current_el); init_tmp_a64_array(dc); dc->next_page_start = - (dc->base.pc_first & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; -} - -static void gen_intermediate_code_target_init_globals( - DisasContext *dc, CPUArchState *env) -{ + (base->pc_first & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; } -static void gen_intermediate_code_target_tb_start( - DisasContext *dc, CPUArchState *env) +static void a64_tr_insn_start(DisasContextBase *base, CPUArchState *env) { -} + DisasContext *dc = container_of(base, DisasContext, base); -static void gen_intermediate_code_target_insn_start( - DisasContext *dc, CPUArchState *env) -{ dc->insn_start_idx = tcg_op_buf_count(); - tcg_gen_insn_start(dc->base.pc_next, 0, 0); + tcg_gen_insn_start(base->pc_next, 0, 0); } -static BreakpointHitType gen_intermediate_code_target_breakpoint_hit( - DisasContext *dc, CPUArchState *env, - const CPUBreakpoint *bp) +static BreakpointHitType +a64_tr_bp_hit(DisasContextBase *b, CPUArchState *env, const CPUBreakpoint *bp) { + DisasContext *dc = container_of(b, DisasContext, base); + if (bp->flags & BP_CPU) { - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_set_pc_im(b->pc_next); gen_helper_check_breakpoints(cpu_env); /* End the TB early; it likely won't be executed */ - dc->base.jmp_type = DJ_UPDATE; + b->jmp_type = DJ_UPDATE; return BH_HIT_INSN; } else { gen_exception_internal_insn(dc, 0, EXCP_DEBUG); @@ -11287,14 +11273,15 @@ static BreakpointHitType gen_intermediate_code_target_breakpoint_hit( to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ - dc->base.pc_next += 4; + b->pc_next += 4; return BH_HIT_TB; } } -static target_ulong gen_intermediate_code_target_disas_insn( - DisasContext *dc, CPUArchState *env) +static target_ulong a64_tr_disas_insn(DisasContextBase *base, CPUArchState *env) { + DisasContext *dc = container_of(base, DisasContext, base); + if (dc->ss_active && !dc->pstate_ss) { /* Singlestep state is Active-pending. * If we're in this state at the start of a TB then either @@ -11306,19 +11293,21 @@ static target_ulong gen_intermediate_code_target_disas_insn( * "did not step an insn" case, and so the syndrome ISV and EX * bits should be zero. */ - assert(dc->base.num_insns == 1); + assert(base->num_insns == 1); gen_exception(EXCP_UDEF, syn_swstep(dc->ss_same_el, 0, 0), default_exception_el(dc)); - dc->base.jmp_type = DJ_EXC; + base->jmp_type = DJ_EXC; } else { disas_a64_insn(env, dc); } - return dc->base.pc_next; + return base->pc_next; } -static DisasJumpType gen_intermediate_code_target_stop_check( - DisasContext *dc, CPUArchState *env) +static DisasJumpType +a64_tr_stop_check(DisasContextBase *base, CPUArchState *env) { + DisasContext *dc = container_of(base, DisasContext, base); + /* Translation stops when a conditional branch is encountered. * Otherwise the subsequent code could get translated several times. * Also stop translation when a page boundary is reached. This @@ -11327,41 +11316,42 @@ static DisasJumpType gen_intermediate_code_target_stop_check( if (dc->ss_active) { return DJ_SS; } else { - return dc->base.jmp_type; + return base->jmp_type; } } -static void gen_intermediate_code_target_stop( - DisasContext *dc, CPUArchState *env) +static void a64_tr_stop(DisasContextBase *base, CPUArchState *env) { - if (unlikely(dc->base.singlestep_enabled || dc->ss_active) - && dc->base.jmp_type != DJ_EXC) { + DisasContext *dc = container_of(base, DisasContext, base); + + if (unlikely(base->singlestep_enabled || dc->ss_active) + && base->jmp_type != DJ_EXC) { /* Note that this means single stepping WFI doesn't halt the CPU. * For conditional branch insns this is harmless unreachable code as * gen_goto_tb() has already handled emitting the debug exception * (and thus a tb-jump is not possible when singlestepping). */ - assert(dc->base.jmp_type != DJ_TB_JUMP); - if (dc->base.jmp_type != DJ_JUMP) { - gen_a64_set_pc_im(dc->base.pc_next); + assert(base->jmp_type != DJ_TB_JUMP); + if (base->jmp_type != DJ_JUMP) { + gen_a64_set_pc_im(base->pc_next); } - if (dc->base.singlestep_enabled) { + if (base->singlestep_enabled) { gen_exception_internal(EXCP_DEBUG); } else { gen_step_complete_exception(dc); } } else { /* Cast because target-specific values are not in generic enum */ - unsigned int jt = (unsigned int)dc->base.jmp_type; + unsigned int jt = (unsigned int)base->jmp_type; switch (jt) { case DJ_NEXT: case DJ_TOO_MANY: /* target set DJ_NEXT */ - gen_goto_tb(dc, 1, dc->base.pc_next); + gen_goto_tb(dc, 1, base->pc_next); break; default: case DJ_UPDATE: - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_set_pc_im(base->pc_next); /* fall through */ case DJ_JUMP: tcg_gen_lookup_and_goto_ptr(cpu_pc); @@ -11375,18 +11365,18 @@ static void gen_intermediate_code_target_stop( /* nothing to generate */ break; case DJ_WFE: - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_set_pc_im(base->pc_next); gen_helper_wfe(cpu_env); break; case DJ_YIELD: - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_set_pc_im(base->pc_next); gen_helper_yield(cpu_env); break; case DJ_WFI: /* This is a special case because we don't want to just halt the CPU * if trying to debug across a WFI. */ - gen_a64_set_pc_im(dc->base.pc_next); + gen_a64_set_pc_im(base->pc_next); gen_helper_wfi(cpu_env); /* The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. @@ -11397,8 +11387,26 @@ static void gen_intermediate_code_target_stop( } } -static int gen_intermediate_code_target_get_disas_flags( - const DisasContext *dc) +static int a64_tr_disas_flags(const DisasContextBase *base) { + DisasContext *dc = container_of(base, DisasContext, base); + return 4 | (bswap_code(dc->sctlr_b) ? 2 : 0); } + +static const TranslatorOps a64_tr = { + .init_context = a64_tr_init_dc, + .insn_start = a64_tr_insn_start, + .breakpoint_hit = a64_tr_bp_hit, + .disas_insn = a64_tr_disas_insn, + .stop_check = a64_tr_stop_check, + .stop = a64_tr_stop, + .disas_flags = a64_tr_disas_flags, +}; + +void gen_intermediate_code_a64(CPUState *cpu, struct TranslationBlock *tb) +{ + DisasContext dc; + + translator_gen(&a64_tr, &dc.base, cpu, tb, cpu_env); +} diff --git a/target/arm/translate.c b/target/arm/translate.c index 06f207a..5ea9952 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -7655,7 +7655,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) } if ((s->base.tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - gen_io_start(); + gen_io_start(cpu_env); } if (isread) { @@ -7747,7 +7747,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) if ((s->base.tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { /* I/O operations must end the TB here (whether read or write) */ - gen_io_end(); + gen_io_end(cpu_env); gen_lookup_tb(s); } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { /* We default to ending the TB on a coprocessor register write, @@ -11864,31 +11864,10 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, } } - - -/* Use separate top-level templates for each architecture */ -#define gen_intermediate_code gen_intermediate_code_arm -#include "translate-all_template.h" -#undef gen_intermediate_code - -#if !defined(TARGET_AARCH64) -void gen_intermediate_code_aarch64(CPUState *cpu, struct TranslationBlock *tb) -{ -} -#endif - -void gen_intermediate_code(CPUState *cpu, struct TranslationBlock *tb) +static void arm_tr_init_dc(DisasContextBase *base, CPUARMState *env) { - if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { - gen_intermediate_code_aarch64(cpu, tb); - } else { - gen_intermediate_code_arm(cpu, tb); - } -} + DisasContext *dc = container_of(base, DisasContext, base); -static void gen_intermediate_code_target_init_disas_context( - DisasContext *dc, CPUARMState *env) -{ dc->condjmp = 0; dc->aarch64 = 0; @@ -11897,23 +11876,23 @@ static void gen_intermediate_code_target_init_disas_context( */ dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); - dc->thumb = ARM_TBFLAG_THUMB(dc->base.tb->flags); - dc->sctlr_b = ARM_TBFLAG_SCTLR_B(dc->base.tb->flags); - dc->be_data = ARM_TBFLAG_BE_DATA(dc->base.tb->flags) ? MO_BE : MO_LE; - dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(dc->base.tb->flags) & 0xf) << 1; - dc->condexec_cond = ARM_TBFLAG_CONDEXEC(dc->base.tb->flags) >> 4; - dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(dc->base.tb->flags)); + dc->thumb = ARM_TBFLAG_THUMB(base->tb->flags); + dc->sctlr_b = ARM_TBFLAG_SCTLR_B(base->tb->flags); + dc->be_data = ARM_TBFLAG_BE_DATA(base->tb->flags) ? MO_BE : MO_LE; + dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(base->tb->flags) & 0xf) << 1; + dc->condexec_cond = ARM_TBFLAG_CONDEXEC(base->tb->flags) >> 4; + dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(base->tb->flags)); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif - dc->ns = ARM_TBFLAG_NS(dc->base.tb->flags); - dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags); - dc->vfp_enabled = ARM_TBFLAG_VFPEN(dc->base.tb->flags); - dc->vec_len = ARM_TBFLAG_VECLEN(dc->base.tb->flags); - dc->vec_stride = ARM_TBFLAG_VECSTRIDE(dc->base.tb->flags); - dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(dc->base.tb->flags); - dc->v7m_handler_mode = ARM_TBFLAG_HANDLER(dc->base.tb->flags); + dc->ns = ARM_TBFLAG_NS(base->tb->flags); + dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(base->tb->flags); + dc->vfp_enabled = ARM_TBFLAG_VFPEN(base->tb->flags); + dc->vec_len = ARM_TBFLAG_VECLEN(base->tb->flags); + dc->vec_stride = ARM_TBFLAG_VECSTRIDE(base->tb->flags); + dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(base->tb->flags); + dc->v7m_handler_mode = ARM_TBFLAG_HANDLER(base->tb->flags); dc->cp_regs = arm_env_get_cpu(env)->cp_regs; dc->features = env->features; @@ -11932,17 +11911,16 @@ static void gen_intermediate_code_target_init_disas_context( * emit code to generate a software step exception * end the TB */ - dc->ss_active = ARM_TBFLAG_SS_ACTIVE(dc->base.tb->flags); - dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(dc->base.tb->flags); + dc->ss_active = ARM_TBFLAG_SS_ACTIVE(base->tb->flags); + dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(base->tb->flags); dc->is_ldex = false; dc->ss_same_el = false; /* Can't be true since EL_d must be AArch64 */ dc->next_page_start = - (dc->base.pc_first & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + (base->pc_first & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; } -static void gen_intermediate_code_target_init_globals( - DisasContext *dc, CPUARMState *env) +static void arm_tr_init_globals(DisasContextBase *base, CPUARMState *env) { cpu_F0s = tcg_temp_new_i32(); cpu_F1s = tcg_temp_new_i32(); @@ -11954,8 +11932,7 @@ static void gen_intermediate_code_target_init_globals( cpu_M0 = tcg_temp_new_i64(); } -static void gen_intermediate_code_target_tb_start( - DisasContext *dc, CPUARMState *env) +static void arm_tr_tb_start(DisasContextBase *base, CPUARMState *env) { /* A note on handling of the condexec (IT) bits: * @@ -11986,6 +11963,7 @@ static void gen_intermediate_code_target_tb_start( * we don't need to care about whether CPUARMState is correct in the * middle of a TB. */ + DisasContext *dc = container_of(base, DisasContext, base); /* * Reset the conditional execution bits immediately. This avoids @@ -11998,43 +11976,45 @@ static void gen_intermediate_code_target_tb_start( } } -static void gen_intermediate_code_target_insn_start( - DisasContext *dc, CPUARMState *env) +static void arm_tr_insn_start(DisasContextBase *base, CPUARMState *env) { + DisasContext *dc = container_of(base, DisasContext, base); + dc->insn_start_idx = tcg_op_buf_count(); - tcg_gen_insn_start(dc->base.pc_next, + tcg_gen_insn_start(base->pc_next, (dc->condexec_cond << 4) | (dc->condexec_mask >> 1), 0); #ifdef CONFIG_USER_ONLY /* Intercept jump to the magic kernel page. */ - if (dc->base.pc_next >= 0xffff0000) { + if (base->pc_next >= 0xffff0000) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception_internal(EXCP_KERNEL_TRAP); - dc->base.jmp_type = DJ_EXC; + base->jmp_type = DJ_EXC; } #else - if (dc->base.pc_next >= 0xfffffff0 && arm_dc_feature(dc, ARM_FEATURE_M)) { + if (base->pc_next >= 0xfffffff0 && arm_dc_feature(dc, ARM_FEATURE_M)) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception_internal(EXCP_EXCEPTION_EXIT); - dc->base.jmp_type = DJ_EXC; + base->jmp_type = DJ_EXC; } #endif } -static BreakpointHitType gen_intermediate_code_target_breakpoint_hit( - DisasContext *dc, CPUARMState *env, - const CPUBreakpoint *bp) +static BreakpointHitType +arm_tr_bp_hit(DisasContextBase *base, CPUARMState *env, const CPUBreakpoint *bp) { + DisasContext *dc = container_of(base, DisasContext, base); + if (bp->flags & BP_CPU) { gen_set_condexec(dc); - gen_set_pc_im(dc, dc->base.pc_next); + gen_set_pc_im(dc, base->pc_next); gen_helper_check_breakpoints(cpu_env); /* End the TB early; it's likely not going to be executed */ - dc->base.jmp_type = DJ_UPDATE; + base->jmp_type = DJ_UPDATE; return BH_HIT_INSN; } else { gen_exception_internal_insn(dc, 0, EXCP_DEBUG); @@ -12045,14 +12025,15 @@ static BreakpointHitType gen_intermediate_code_target_breakpoint_hit( tb->size below does the right thing. */ /* TODO: Advance PC by correct instruction length to avoid * disassembler error messages */ - dc->base.pc_next += 2; + base->pc_next += 2; return BH_HIT_TB; } } -static target_ulong gen_intermediate_code_target_disas_insn( - DisasContext *dc, CPUArchState *env) +static target_ulong arm_tr_disas_insn(DisasContextBase *base, CPUArchState *env) { + DisasContext *dc = container_of(base, DisasContext, base); + if (dc->ss_active && !dc->pstate_ss) { /* Singlestep state is Active-pending. * If we're in this state at the start of a TB then either @@ -12064,11 +12045,11 @@ static target_ulong gen_intermediate_code_target_disas_insn( * "did not step an insn" case, and so the syndrome ISV and EX * bits should be zero. */ - assert(dc->base.num_insns == 1); + assert(base->num_insns == 1); gen_exception(EXCP_UDEF, syn_swstep(dc->ss_same_el, 0, 0), default_exception_el(dc)); - dc->base.jmp_type = DJ_SKIP; - return dc->base.pc_next; + base->jmp_type = DJ_SKIP; + return base->pc_next; } if (dc->thumb) { @@ -12082,30 +12063,32 @@ static target_ulong gen_intermediate_code_target_disas_insn( } } } else { - unsigned int insn = arm_ldl_code(env, dc->base.pc_next, dc->sctlr_b); - dc->base.pc_next += 4; + unsigned int insn = arm_ldl_code(env, base->pc_next, dc->sctlr_b); + base->pc_next += 4; disas_arm_insn(dc, insn); } - if (dc->condjmp && !dc->base.jmp_type) { + if (dc->condjmp && !base->jmp_type) { gen_set_label(dc->condlabel); dc->condjmp = 0; } - return dc->base.pc_next; + return base->pc_next; } -static DisasJumpType gen_intermediate_code_target_stop_check( - DisasContext *dc, CPUARMState *env) +static DisasJumpType arm_tr_stop_check(DisasContextBase *base, CPUARMState *env) { + DisasContext *dc = container_of(base, DisasContext, base); + /* Translation stops when a conditional branch is encountered. * Otherwise the subsequent code could get translated several times. * Also stop translation when a page boundary is reached. This * ensures prefetch aborts occur at the right place. */ + dc = container_of(base, DisasContext, base); if (is_singlestepping(dc)) { return DJ_SS; - } else if ((dc->base.pc_next >= dc->next_page_start - 3) + } else if ((base->pc_next >= dc->next_page_start - 3) && insn_crosses_page(env, dc)) { /* * Generic code already checked if the next insn starts in a new @@ -12122,21 +12105,21 @@ static DisasJumpType gen_intermediate_code_target_stop_check( */ return DJ_PAGE_CROSS; } else { - return dc->base.jmp_type; + return base->jmp_type; } } -static void gen_intermediate_code_target_stop( - DisasContext *dc, CPUARMState *env) +static void arm_tr_stop(DisasContextBase *base, CPUARMState *env) { + DisasContext *dc = container_of(base, DisasContext, base); /* Cast because target-specific values are not in generic enum */ - unsigned int jt = (unsigned int)dc->base.jmp_type; + unsigned int jt = (unsigned int)base->jmp_type; if (jt == DJ_SKIP) { return; } - if ((dc->base.tb->cflags & CF_LAST_IO) && dc->condjmp) { + if ((base->tb->cflags & CF_LAST_IO) && dc->condjmp) { /* FIXME: This can theoretically happen with self-modifying code. */ cpu_abort(ENV_GET_CPU(env), "IO on conditional branch instruction"); } @@ -12145,7 +12128,7 @@ static void gen_intermediate_code_target_stop( instruction was a conditional branch or trap, and the PC has already been written. */ gen_set_condexec(dc); - if (dc->base.jmp_type == DJ_BX_EXCRET) { + if (base->jmp_type == DJ_BX_EXCRET) { /* Exception return branches need some special case code at the * end of the TB, which is complex enough that it has to * handle the single-step vs not and the condition-failed @@ -12171,7 +12154,7 @@ static void gen_intermediate_code_target_stop( case DJ_NEXT: case DJ_TOO_MANY: /* target set DJ_NEXT */ case DJ_UPDATE: - gen_set_pc_im(dc, dc->base.pc_next); + gen_set_pc_im(dc, base->pc_next); /* fall through */ default: /* FIXME: Single stepping a WFI insn will not halt the CPU. */ @@ -12191,10 +12174,10 @@ static void gen_intermediate_code_target_stop( switch (jt) { case DJ_NEXT: case DJ_TOO_MANY: /* target set DJ_NEXT */ - gen_goto_tb(dc, 1, dc->base.pc_next); + gen_goto_tb(dc, 1, base->pc_next); break; case DJ_UPDATE: - gen_set_pc_im(dc, dc->base.pc_next); + gen_set_pc_im(dc, base->pc_next); /* fall through */ case DJ_JUMP: gen_goto_ptr(); @@ -12238,16 +12221,40 @@ static void gen_intermediate_code_target_stop( gen_set_label(dc->condlabel); gen_set_condexec(dc); if (unlikely(is_singlestepping(dc))) { - gen_set_pc_im(dc, dc->base.pc_next); + gen_set_pc_im(dc, base->pc_next); gen_singlestep_exception(dc); } else { - gen_goto_tb(dc, 1, dc->base.pc_next); + gen_goto_tb(dc, 1, base->pc_next); } } } -static int gen_intermediate_code_target_get_disas_flags( - const DisasContext *dc) +static int arm_tr_disas_flags(const DisasContextBase *base) { + DisasContext *dc = container_of(base, DisasContext, base); + return dc->thumb | (dc->sctlr_b << 1); } + +static const TranslatorOps arm_tr = { + .init_context = arm_tr_init_dc, + .init_globals = arm_tr_init_globals, + .tb_start = arm_tr_tb_start, + .insn_start = arm_tr_insn_start, + .breakpoint_hit = arm_tr_bp_hit, + .disas_insn = arm_tr_disas_insn, + .stop_check = arm_tr_stop_check, + .stop = arm_tr_stop, + .disas_flags = arm_tr_disas_flags, +}; + +void gen_intermediate_code(CPUState *cpu, struct TranslationBlock *tb) +{ + DisasContext dc; + + if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { + gen_intermediate_code_a64(cpu, tb); + } else { + translator_gen(&arm_tr, &dc.base, cpu, tb, cpu_env); + } +} diff --git a/target/arm/translate.h b/target/arm/translate.h index 5473994..1aa5d49 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -1,8 +1,7 @@ #ifndef TARGET_ARM_TRANSLATE_H #define TARGET_ARM_TRANSLATE_H -#include "exec/translate-all_template.h" - +#include "exec/translator.h" /* internal defines */ typedef struct DisasContext { @@ -122,7 +121,6 @@ static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) } /* Target-specific values for DisasContextBase::jmp_type */ -#include "exec/translate-all_template.h" #define DJ_JUMP (DJ_TARGET + 0) #define DJ_UPDATE (DJ_TARGET + 1) #define DJ_TB_JUMP (DJ_TARGET + 2) @@ -153,13 +151,10 @@ static void disas_set_insn_syndrome(DisasContext *s, uint32_t syn) #define DJ_PAGE_CROSS (DJ_TARGET + 13) #define DJ_SKIP (DJ_TARGET + 14) -void gen_intermediate_code_arm(CPUState *cpu, struct TranslationBlock *tb); -void gen_intermediate_code_aarch64(CPUState *cpu, struct TranslationBlock *tb); - #ifdef TARGET_AARCH64 void init_tmp_a64_array(DisasContext *s); void a64_translate_init(void); -void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb); +void gen_intermediate_code_a64(CPUState *cpu, TranslationBlock *tb); void gen_a64_set_pc_im(uint64_t val); void aarch64_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags); @@ -172,7 +167,7 @@ static inline void a64_translate_init(void) { } -static inline void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb) +static inline void gen_intermediate_code_a64(CPUState *cpu, TranslationBlock *tb) { } diff --git a/translator.c b/translator.c new file mode 100644 index 0000000..2248b52 --- /dev/null +++ b/translator.c @@ -0,0 +1,170 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "disas/disas.h" +#include "cpu.h" +#include "tcg.h" +#include "tcg-op.h" +#include "exec/exec-all.h" +#include "exec/translator.h" +#include "exec/gen-icount.h" +#include "exec/log.h" + +static inline void check_tcg(const DisasContextBase *base) +{ + if (tcg_check_temp_count()) { + error_report("warning: TCG temporary leaks before "TARGET_FMT_lx, + base->pc_next); + } +} + +void translator_gen(const TranslatorOps *tr, DisasContextBase *base, + CPUState *cpu, TranslationBlock *tb, TCGv_env cpu_env) +{ + CPUArchState *env = cpu->env_ptr; + int max_insns; + + /* Initialize DisasContextBase */ + base->tb = tb; + base->singlestep_enabled = cpu->singlestep_enabled; + base->pc_first = tb->pc; + base->pc_next = base->pc_first; + base->jmp_type = DJ_NEXT; + base->num_insns = 0; + if (tr->init_context) { + tr->init_context(base, env); + } + + /* Initialize globals */ + if (tr->init_globals) { + tr->init_globals(base, env); + } + tcg_clear_temp_count(); + + /* Instruction counting */ + max_insns = base->tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + if (base->singlestep_enabled || singlestep) { + max_insns = 1; + } + + /* Start translating */ + gen_tb_start(base->tb, cpu_env); + if (tr->tb_start) { + tr->tb_start(base, env); + } + + while (true) { + CPUBreakpoint *bp; + + base->num_insns++; + if (tr->insn_start) { + tr->insn_start(base, env); + } + + /* Early exit before breakpoint checks */ + if (unlikely(base->jmp_type != DJ_NEXT)) { + break; + } + + /* Pass breakpoint hits to target for further processing */ + bp = NULL; + do { + bp = cpu_breakpoint_get(cpu, base->pc_next, bp); + if (unlikely(bp)) { + BreakpointHitType bh = tr->breakpoint_hit(base, env, bp); + if (bh == BH_HIT_INSN) { + /* Hit, keep translating */ + /* + * TODO: if we're never going to have more than one BP in a + * single address, we can simply use a bool here. + */ + break; + } else if (bh == BH_HIT_TB) { + goto done_generating; + } + } + } while (bp != NULL); + + /* Accept I/O on last instruction */ + if (base->num_insns == max_insns && + (base->tb->cflags & CF_LAST_IO)) { + gen_io_start(cpu_env); + } + + /* Disassemble one instruction */ + base->pc_next = tr->disas_insn(base, env); + + /**************************************************/ + /* Conditions to stop translation */ + /**************************************************/ + + /* Disassembly already set a stop condition */ + if (base->jmp_type >= DJ_TARGET) { + break; + } + + /* Target-specific conditions */ + base->jmp_type = tr->stop_check(base, env); + if (base->jmp_type >= DJ_TARGET) { + break; + } + + /* Too many instructions */ + if (tcg_op_buf_full() || base->num_insns >= max_insns) { + base->jmp_type = DJ_TOO_MANY; + break; + } + + /* + * Check if next instruction is on next page, which can cause an + * exception. + * + * NOTE: Target-specific code must check a single instruction does not + * cross page boundaries; the first in the TB is always allowed to + * cross pages (never goes through this check). + */ + if ((base->pc_first & TARGET_PAGE_MASK) + != (base->pc_next & TARGET_PAGE_MASK)) { + base->jmp_type = DJ_TOO_MANY; + break; + } + + check_tcg(base); + } + + if (tr->stop) { + tr->stop(base, env); + } + + if (base->tb->cflags & CF_LAST_IO) { + gen_io_end(cpu_env); + } + + done_generating: + gen_tb_end(base->tb, base->num_insns); + + check_tcg(base); + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && + qemu_log_in_addr_range(base->pc_first)) { + qemu_log_lock(); + qemu_log("----------------\n"); + qemu_log("IN: %s\n", lookup_symbol(base->pc_first)); + log_target_disas(cpu, base->pc_first, + base->pc_next - base->pc_first, + tr->disas_flags(base)); + qemu_log("\n"); + qemu_log_unlock(); + } +#endif + + base->tb->size = base->pc_next - base->pc_first; + base->tb->icount = base->num_insns; +} -- 2.7.4