diff mbox

translator mega-patch

Message ID 20170615220501.GA26408@flamenco (mailing list archive)
State New, archived
Headers show

Commit Message

Emilio Cota June 15, 2017, 10:05 p.m. UTC
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; 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 <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

Comments

Lluís Vilanova June 18, 2017, 2:37 p.m. UTC | #1
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
Peter Maydell June 18, 2017, 6:26 p.m. UTC | #2
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
Emilio Cota June 19, 2017, 4:08 a.m. UTC | #3
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
Peter Maydell June 19, 2017, 7:15 a.m. UTC | #4
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 mbox

Patch

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;
+}