Message ID | 20170615220501.GA26408@flamenco (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Emilio G Cota writes: > On Mon, Jun 12, 2017 at 17:54:09 +0300, Lluís Vilanova wrote: >> Signed-off-by: Lluís Vilanova <vilanova@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; I don't get it. Isn't that what my series is already doing? Or do you mean explicitly passing DisasContextBase to all the generic translator functions below? I kind of dislike it every time I see container_of, and it makes type checking from the compiler a bit more fragile. > 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 seem to remember we discussed this at some point before I sent the first version, to allow multiple targets on the same binary, but decided against it. Still, I can leave the ops struct in place without even trying to support multiple targets. It should be a little bit slower (using function pointers instead of a "template"), but I don't think performance will suffer that much since we're at the translation path. Any other opinions on this and the point above? > I'll send as a separate, proper patch the gen-icount changes; really > having cpu_env there as a global seems wrong to me. That's an orthogonal issue that can he handled in a separate series. Thanks! Lluis > 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 <cota@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
On 18 June 2017 at 15:37, Lluís Vilanova <vilanova@ac.upc.edu> wrote: > I seem to remember we discussed this at some point before I sent the first > version, to allow multiple targets on the same binary, but decided against it. > > Still, I can leave the ops struct in place without even trying to support > multiple targets. It should be a little bit slower (using function pointers > instead of a "template"), but I don't think performance will suffer that much > since we're at the translation path. > > Any other opinions on this and the point above? Multiple targets in one binary is one of those things that I would like to see us do some day. So while I wouldn't do huge amounts of work purely to support it, if we have two designs which are otherwise more-or-less equivalent and only one has a path available to support multi-target binaries we should probably prefer that one. thanks -- PMM
On Sun, Jun 18, 2017 at 17:37:39 +0300, Lluís Vilanova wrote: > Emilio G Cota writes: > > > On Mon, Jun 12, 2017 at 17:54:09 +0300, Lluís Vilanova wrote: > >> Signed-off-by: Lluís Vilanova <vilanova@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; > > I don't get it. Isn't that what my series is already doing? Or do you mean > explicitly passing DisasContextBase to all the generic translator functions > below? Yes, I mean the latter. > I kind of dislike it every time I see container_of, and it makes type > checking from the compiler a bit more fragile. container_of is *very* useful! You should like it more :-) The pattern of having a struct of function pointers ("ops") + container_of is used in the kernel extensively, and it works very well there. The compiler will catch misuses of container_of, which in my experience is basically all you need. If you want stricter typing, there's TCON ("typed container"), which is really cool: http://ccodearchive.net/info/tcon.html A neat usage example are type-safe linked lists: http://ccodearchive.net/info/tlist2.html > > 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 seem to remember we discussed this at some point before I sent the first > version, to allow multiple targets on the same binary, but decided against it. > > Still, I can leave the ops struct in place without even trying to support > multiple targets. I didn't have this in mind, but it is a nice side effect. > It should be a little bit slower (using function pointers > instead of a "template"), but I don't think performance will suffer that much > since we're at the translation path. Yes performance wouldn't be an issue, even if all we benchmarked was translation--the key is that the function called is always the same so prediction takes care of it. See Agner's comment on this (in the context of C++ though, but it applies here): > The time it takes to call a virtual member function is a few clock > cycles more than it takes to call a non-virtual member function, provided > that the function call statement always calls the same version of the > virtual function. If the version changes then you may get a misprediction > penalty of 10 - 20 clock cycles. http://www.agner.org/optimize/optimizing_cpp.pdf Cheers, Emilio
On 19 June 2017 at 05:08, Emilio G. Cota <cota@braap.org> wrote: > On Sun, Jun 18, 2017 at 17:37:39 +0300, Lluís Vilanova wrote: >> I kind of dislike it every time I see container_of, and it makes type >> checking from the compiler a bit more fragile. > > container_of is *very* useful! You should like it more :-) Or you could use qom objects, where the parent-to-child cast is checked. thanks -- PMM
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; +}