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
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
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
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 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
© 2016 - 2025 Red Hat, Inc.