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 +++++++++++++++++++++++++++++++++
4 files changed, 300 insertions(+), 1 deletion(-)
create mode 100644 include/exec/translate-all_template.h
create mode 100644 translate-all_template.h
diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h
index 62d462e494..547c979629 100644
--- a/include/exec/gen-icount.h
+++ b/include/exec/gen-icount.h
@@ -44,7 +44,7 @@ static inline void gen_tb_start(TranslationBlock *tb)
tcg_temp_free_i32(count);
}
-static void gen_tb_end(TranslationBlock *tb, int num_insns)
+static inline void gen_tb_end(TranslationBlock *tb, int num_insns)
{
if (tb->cflags & CF_USE_ICOUNT) {
/* Update the num_insn immediate parameter now that we know
diff --git a/include/exec/translate-all_template.h b/include/exec/translate-all_template.h
new file mode 100644
index 0000000000..51c8a43b80
--- /dev/null
+++ b/include/exec/translate-all_template.h
@@ -0,0 +1,73 @@
+/*
+ * Generic intermediate code generation.
+ *
+ * Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef EXEC__TRANSLATE_ALL_TEMPLATE_H
+#define EXEC__TRANSLATE_ALL_TEMPLATE_H
+
+/*
+ * Include this header from a target-specific file, and add a
+ *
+ * DisasContextBase base;
+ *
+ * member in your target-specific DisasContext.
+ */
+
+
+#include "exec/exec-all.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;
+
+#endif /* EXEC__TRANSLATE_ALL_TEMPLATE_H */
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 89ddb686fb..d46e8df756 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -982,6 +982,28 @@ static inline bool cpu_breakpoint_test(CPUState *cpu, vaddr pc, int mask)
return false;
}
+/* Get first breakpoint matching a PC */
+static inline CPUBreakpoint *cpu_breakpoint_get(CPUState *cpu, vaddr pc,
+ CPUBreakpoint *bp)
+{
+ if (likely(bp == NULL)) {
+ if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
+ QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
+ if (bp->pc == pc) {
+ return bp;
+ }
+ }
+ }
+ } else {
+ QTAILQ_FOREACH_CONTINUE(bp, entry) {
+ if (bp->pc == pc) {
+ return bp;
+ }
+ }
+ }
+ return NULL;
+}
+
int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
int flags, CPUWatchpoint **watchpoint);
int cpu_watchpoint_remove(CPUState *cpu, vaddr addr,
diff --git a/translate-all_template.h b/translate-all_template.h
new file mode 100644
index 0000000000..3e3f0816f7
--- /dev/null
+++ b/translate-all_template.h
@@ -0,0 +1,204 @@
+/*
+ * Generic intermediate code generation.
+ *
+ * Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef TRANSLATE_ALL_TEMPLATE_H
+#define TRANSLATE_ALL_TEMPLATE_H
+
+/*
+ * Include this header from a target-specific file, which must define the
+ * target-specific functions declared below.
+ *
+ * These must be paired with instructions in "exec/translate-all_template.h".
+ */
+
+
+#include "cpu.h"
+#include "qemu/error-report.h"
+
+
+static void gen_intermediate_code_target_init_disas_context(
+ DisasContext *dc, CPUArchState *env);
+
+static void gen_intermediate_code_target_init_globals(
+ DisasContext *dc, CPUArchState *env);
+
+static void gen_intermediate_code_target_tb_start(
+ DisasContext *dc, CPUArchState *env);
+
+static void gen_intermediate_code_target_insn_start(
+ DisasContext *dc, CPUArchState *env);
+
+static BreakpointHitType gen_intermediate_code_target_breakpoint_hit(
+ DisasContext *dc, CPUArchState *env,
+ const CPUBreakpoint *bp);
+
+static target_ulong gen_intermediate_code_target_disas_insn(
+ DisasContext *dc, CPUArchState *env);
+
+static DisasJumpType gen_intermediate_code_target_stop_check(
+ DisasContext *dc, CPUArchState *env);
+
+static void gen_intermediate_code_target_stop(
+ DisasContext *dc, CPUArchState *env);
+
+static int gen_intermediate_code_target_get_disas_flags(
+ const DisasContext *dc);
+
+
+static inline void gen_intermediate_code_tcg_check(const DisasContext *dc)
+{
+ if (tcg_check_temp_count()) {
+ error_report("warning: TCG temporary leaks before "TARGET_FMT_lx,
+ dc->base.pc_next);
+ }
+}
+
+void gen_intermediate_code(CPUState *cpu, struct TranslationBlock *tb)
+{
+ CPUArchState *env = cpu->env_ptr;
+ DisasContext dc1, *dc = &dc1;
+ int max_insns;
+
+ /* Initialize DisasContext */
+ dc->base.tb = tb;
+ dc->base.singlestep_enabled = cpu->singlestep_enabled;
+ dc->base.pc_first = tb->pc;
+ dc->base.pc_next = dc->base.pc_first;
+ dc->base.jmp_type = DJ_NEXT;
+ dc->base.num_insns = 0;
+ gen_intermediate_code_target_init_disas_context(dc, env);
+
+ /* Initialize globals */
+ gen_intermediate_code_target_init_globals(dc, env);
+ tcg_clear_temp_count();
+
+ /* Instruction counting */
+ max_insns = dc->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 (dc->base.singlestep_enabled || singlestep) {
+ max_insns = 1;
+ }
+
+ /* Start translating */
+ gen_tb_start(dc->base.tb);
+ gen_intermediate_code_target_tb_start(dc, env);
+
+ while (true) {
+ CPUBreakpoint *bp;
+
+ dc->base.num_insns++;
+ gen_intermediate_code_target_insn_start(dc, env);
+
+ /* Early exit before breakpoint checks */
+ if (unlikely(dc->base.jmp_type != DJ_NEXT)) {
+ break;
+ }
+
+ /* Pass breakpoint hits to target for further processing */
+ bp = NULL;
+ do {
+ bp = cpu_breakpoint_get(cpu, dc->base.pc_next, bp);
+ if (unlikely(bp)) {
+ BreakpointHitType bh =
+ gen_intermediate_code_target_breakpoint_hit(dc, 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 (dc->base.num_insns == max_insns &&
+ (dc->base.tb->cflags & CF_LAST_IO)) {
+ gen_io_start();
+ }
+
+ /* Disassemble one instruction */
+ dc->base.pc_next = gen_intermediate_code_target_disas_insn(dc, env);
+
+ /**************************************************/
+ /* Conditions to stop translation */
+ /**************************************************/
+
+ /* Disassembly already set a stop condition */
+ if (dc->base.jmp_type >= DJ_TARGET) {
+ break;
+ }
+
+ /* Target-specific conditions */
+ dc->base.jmp_type = gen_intermediate_code_target_stop_check(dc, env);
+ if (dc->base.jmp_type >= DJ_TARGET) {
+ break;
+ }
+
+ /* Too many instructions */
+ if (tcg_op_buf_full() || dc->base.num_insns >= max_insns) {
+ dc->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 ((dc->base.pc_first & TARGET_PAGE_MASK)
+ != (dc->base.pc_next & TARGET_PAGE_MASK)) {
+ dc->base.jmp_type = DJ_TOO_MANY;
+ break;
+ }
+
+ gen_intermediate_code_tcg_check(dc);
+ }
+
+ gen_intermediate_code_target_stop(dc, env);
+
+ if (dc->base.tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+
+done_generating:
+ gen_tb_end(dc->base.tb, dc->base.num_insns);
+
+ gen_intermediate_code_tcg_check(dc);
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) &&
+ qemu_log_in_addr_range(dc->base.pc_first)) {
+ qemu_log_lock();
+ qemu_log("----------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first));
+ log_target_disas(cpu, dc->base.pc_first,
+ dc->base.pc_next - dc->base.pc_first,
+ gen_intermediate_code_target_get_disas_flags(dc));
+ qemu_log("\n");
+ qemu_log_unlock();
+ }
+#endif
+
+ dc->base.tb->size = dc->base.pc_next - dc->base.pc_first;
+ dc->base.tb->icount = dc->base.num_insns;
+}
+
+#endif /* TRANSLATE_ALL_TEMPLATE_H */
Some minor nits below.
On Mon, Jun 12, 2017 at 17:54:09 +0300, Lluís Vilanova wrote:
> Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
> ---
> +/**
> + * 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;
BH_MISS reads out loud to "Breakpoint Hit Miss"; that's quite counterintuitive.
Similarly for the others (e.g. "breakpoint Hit Hit -- ??".
Can we just do BP_{MISS,HIT,etc}? Thinking about it, perhaps BP_NONE is
better than BP_MISS.
(snip)
> +/**
> + * 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;
- @pc_next: I'd stick with @pc, it's shorter, it's everywhere already, and
with the documentation it's very clear what it is for.
- @jmp_type: missing doc :-)
Thanks,
E.
On Thu, Jun 15, 2017 at 18:19:11 -0400, Emilio G. Cota wrote:
> (snip)
> > +/**
> > + * 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;
>
> - @pc_next: I'd stick with @pc, it's shorter, it's everywhere already, and
> with the documentation it's very clear what it is for.
> - @jmp_type: missing doc :-)
Also, consider keeping the @is_jmp name instead of renaming it to
@jmp_type. (@jmp would be shorter but it would be confusing though,
e.g. cris has both dc->jmp and dc->is_jmp.)
E.
On Thu, Jun 15, 2017 at 19:25:07 -0400, Emilio G. Cota wrote: > Also, consider keeping the @is_jmp name instead of renaming it to > @jmp_type. (@jmp would be shorter but it would be confusing though, > e.g. cris has both dc->jmp and dc->is_jmp.) It turns out that keeping the original names also makes the diff's a lot more bearable. I have a WIP branch with further modifications to your patches; the goal is to break down the patchset to make it more easily reviewable, which hopefully will also prevent us from introducing bugs. You can see the WIP tree at: https://github.com/cota/qemu/tree/trloop Note that I may rebase this branch any time. Cheers, Emilio
Emilio G Cota writes: > On Thu, Jun 15, 2017 at 19:25:07 -0400, Emilio G. Cota wrote: >> Also, consider keeping the @is_jmp name instead of renaming it to >> @jmp_type. (@jmp would be shorter but it would be confusing though, >> e.g. cris has both dc->jmp and dc->is_jmp.) > It turns out that keeping the original names also makes the diff's > a lot more bearable. > I have a WIP branch with further modifications to your patches; the goal > is to break down the patchset to make it more easily reviewable, which > hopefully will also prevent us from introducing bugs. > You can see the WIP tree at: > https://github.com/cota/qemu/tree/trloop > Note that I may rebase this branch any time. Ok, I'll do that for both arm and x86. Thanks a lot, Lluis
Emilio G Cota writes:
> On Thu, Jun 15, 2017 at 18:19:11 -0400, Emilio G. Cota wrote:
>> (snip)
>> > +/**
>> > + * 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;
>>
>> - @pc_next: I'd stick with @pc, it's shorter, it's everywhere already, and
>> with the documentation it's very clear what it is for.
>> - @jmp_type: missing doc :-)
> Also, consider keeping the @is_jmp name instead of renaming it to
> @jmp_type. (@jmp would be shorter but it would be confusing though,
> e.g. cris has both dc->jmp and dc->is_jmp.)
I just figured that this series could also take the chance of trying to rename a
few common variables I'm changing to something more readable.
But if you feel very strongly about keeping the original names (and minimizing
the diffs as you say later), I'll revert the name changes.
Thanks,
Lluis
Lluís Vilanova writes:
> Emilio G Cota writes:
>> On Thu, Jun 15, 2017 at 18:19:11 -0400, Emilio G. Cota wrote:
>>> (snip)
>>> > +/**
>>> > + * 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;
>>>
>>> - @pc_next: I'd stick with @pc, it's shorter, it's everywhere already, and
>>> with the documentation it's very clear what it is for.
>>> - @jmp_type: missing doc :-)
>> Also, consider keeping the @is_jmp name instead of renaming it to
>> @jmp_type. (@jmp would be shorter but it would be confusing though,
>> e.g. cris has both dc->jmp and dc->is_jmp.)
> I just figured that this series could also take the chance of trying to rename a
> few common variables I'm changing to something more readable.
> But if you feel very strongly about keeping the original names (and minimizing
> the diffs as you say later), I'll revert the name changes.
Also, going through the changes to break them down into smaller pieces, I saw
that TranslationBlock (at least in i386) already has a "pc" member, so using
"pc_next" in DisasContextBase makes it even clearer it's a different variable.
You comments still apply to "is_jmp" vs "jmp_type" though. Unless you or anybody
else feels strongly against it, I'll keep "jmp_type", since I'm already changing
all lines that reference "is_jmp" to use DisasContextBase (instead of
DisasContext).
Thanks,
Lluis
Lluís Vilanova writes:
> Lluís Vilanova writes:
>> Emilio G Cota writes:
>>> On Thu, Jun 15, 2017 at 18:19:11 -0400, Emilio G. Cota wrote:
>>>> (snip)
>>>> > +/**
>>>> > + * 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;
>>>>
>>>> - @pc_next: I'd stick with @pc, it's shorter, it's everywhere already, and
>>>> with the documentation it's very clear what it is for.
>>>> - @jmp_type: missing doc :-)
>>> Also, consider keeping the @is_jmp name instead of renaming it to
>>> @jmp_type. (@jmp would be shorter but it would be confusing though,
>>> e.g. cris has both dc->jmp and dc->is_jmp.)
>> I just figured that this series could also take the chance of trying to rename a
>> few common variables I'm changing to something more readable.
>> But if you feel very strongly about keeping the original names (and minimizing
>> the diffs as you say later), I'll revert the name changes.
> Also, going through the changes to break them down into smaller pieces, I saw
> that TranslationBlock (at least in i386) already has a "pc" member, so using
> "pc_next" in DisasContextBase makes it even clearer it's a different variable.
> You comments still apply to "is_jmp" vs "jmp_type" though. Unless you or anybody
> else feels strongly against it, I'll keep "jmp_type", since I'm already changing
> all lines that reference "is_jmp" to use DisasContextBase (instead of
> DisasContext).
Aha, just checked your proposed patches more closely and it totally makes sense
to keep "is_jmp" to simplify the diffs, so I'll go for that one.
Thanks!
Lluis
On Mon, Jun 19, 2017 at 00:54:05 +0300, Lluís Vilanova wrote: > Aha, just checked your proposed patches more closely and it totally makes sense > to keep "is_jmp" to simplify the diffs, so I'll go for that one. Also I think it's important to break down the changes to each architecture into separate patches, otherwise the diff becomes impossible to read. I went quite a long way with arm/translate.c on Friday. E.
Emilio G Cota writes: > On Mon, Jun 19, 2017 at 00:54:05 +0300, Lluís Vilanova wrote: >> Aha, just checked your proposed patches more closely and it totally makes sense >> to keep "is_jmp" to simplify the diffs, so I'll go for that one. > Also I think it's important to break down the changes to each > architecture into separate patches, otherwise the diff becomes > impossible to read. I went quite a long way with arm/translate.c > on Friday. Yes, I'm onto it, thanks :) Lluis
Emilio G Cota writes:
> Some minor nits below.
> On Mon, Jun 12, 2017 at 17:54:09 +0300, Lluís Vilanova wrote:
>> Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
>> ---
>> +/**
>> + * 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;
> BH_MISS reads out loud to "Breakpoint Hit Miss"; that's quite counterintuitive.
> Similarly for the others (e.g. "breakpoint Hit Hit -- ??".
> Can we just do BP_{MISS,HIT,etc}? Thinking about it, perhaps BP_NONE is
> better than BP_MISS.
[...]
Will do.
Cheers,
Lluis
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.