diff mbox

[3/4] target: [tcg] Add generic translation framework

Message ID 146859913647.13167.16063406348810399647.stgit@fimbulvetr.bsc.es (mailing list archive)
State New, archived
Headers show

Commit Message

Lluís Vilanova July 15, 2016, 4:12 p.m. UTC
Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
---
 include/exec/translate-all_template.h |   58 ++++++++++++
 include/qom/cpu.h                     |   21 ++++
 translate-all_template.h              |  160 +++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 include/exec/translate-all_template.h
 create mode 100644 translate-all_template.h

Comments

Richard Henderson July 18, 2016, 1:10 p.m. UTC | #1
On 07/15/2016 09:42 PM, Lluís Vilanova wrote:
> Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
> ---
>  include/exec/translate-all_template.h |   58 ++++++++++++
>  include/qom/cpu.h                     |   21 ++++
>  translate-all_template.h              |  160 +++++++++++++++++++++++++++++++++
>  3 files changed, 239 insertions(+)
>  create mode 100644 include/exec/translate-all_template.h
>  create mode 100644 translate-all_template.h
>
> diff --git a/include/exec/translate-all_template.h b/include/exec/translate-all_template.h
> new file mode 100644
> index 0000000..9e0c361
> --- /dev/null
> +++ b/include/exec/translate-all_template.h
> @@ -0,0 +1,58 @@
> +/*
> + * Generic intermediate code generation.
> + *
> + * Copyright (C) 2016 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"
> +
> +
> +/**
> + * 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;

I think you might as well add the common cases: exit tb via exception, exit via 
goto_tb, exit via indirect jump (pc updated), exit for state change (pc not 
updated).

See the set used for alpha.

> +void gen_intermediate_code(CPUState *cpu, struct TranslationBlock *tb)
> +{
> +    CPUArchState *env = cpu->env_ptr;
> +    DisasContext dc1, *dc = &dc1;
> +    int num_insns;
> +    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;
> +    gen_intermediate_code_target_init_disas_context(dc, env);
> +
> +    /* Target-specific globals */
> +    gen_intermediate_code_target_init_globals(dc, env);
> +
> +    /* Instruction counting */
> +    num_insns = 0;
> +    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;
> +    }

I've started adding the singlestep check here, outside the loop, setting 
max_insns to 1.

> +
> +    /* Start translating */
> +    gen_tb_start(dc->base.tb);
> +
> +    while (true) {
> +        CPUBreakpoint *bp;
> +
> +        tcg_gen_insn_start(dc->base.pc_next, dc->cc_op);

You've probably discovered that this will have to be its own hook.

> +        num_insns++;
> +
> +        /* Pass breakpoint hits to target for further processing */
> +        bp = NULL;
> +        do {
> +            bp = cpu_breakpoint_get(cpu, dc->base.pc_next, bp);
> +            if (unlikely(bp)) {
> +                if (gen_intermediate_code_target_breakpoint_hit(dc, env, bp)) {
> +                    goto done_generating;
> +                }
> +            }
> +        } while (bp != NULL);

Why would you need to loop here?


r~
Lluís Vilanova July 18, 2016, 1:55 p.m. UTC | #2
Richard Henderson writes:

> On 07/15/2016 09:42 PM, Lluís Vilanova wrote:
>> Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
>> ---
>> include/exec/translate-all_template.h |   58 ++++++++++++
>> include/qom/cpu.h                     |   21 ++++
>> translate-all_template.h              |  160 +++++++++++++++++++++++++++++++++
>> 3 files changed, 239 insertions(+)
>> create mode 100644 include/exec/translate-all_template.h
>> create mode 100644 translate-all_template.h
>> 
>> diff --git a/include/exec/translate-all_template.h b/include/exec/translate-all_template.h
>> new file mode 100644
>> index 0000000..9e0c361
>> --- /dev/null
>> +++ b/include/exec/translate-all_template.h
>> @@ -0,0 +1,58 @@
>> +/*
>> + * Generic intermediate code generation.
>> + *
>> + * Copyright (C) 2016 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"
>> +
>> +
>> +/**
>> + * 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;

> I think you might as well add the common cases: exit tb via exception, exit via
> goto_tb, exit via indirect jump (pc updated), exit for state change (pc not
> updated).

> See the set used for alpha.

Do you mean the "DISAS_*" values in "exec-all.h"? I looked into it, but could
not fully understand when they are supposed to be used (seem inconsistent across
targets), so I decided to defer this to the target.


>> +void gen_intermediate_code(CPUState *cpu, struct TranslationBlock *tb)
>> +{
>> +    CPUArchState *env = cpu->env_ptr;
>> +    DisasContext dc1, *dc = &dc1;
>> +    int num_insns;
>> +    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;
>> +    gen_intermediate_code_target_init_disas_context(dc, env);
>> +
>> +    /* Target-specific globals */
>> +    gen_intermediate_code_target_init_globals(dc, env);
>> +
>> +    /* Instruction counting */
>> +    num_insns = 0;
>> +    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;
>> +    }

> I've started adding the singlestep check here, outside the loop, setting
> max_insns to 1.

That makes sense, and could eliminate one field in DisasContextBase.


>> +
>> +    /* Start translating */
>> +    gen_tb_start(dc->base.tb);
>> +
>> +    while (true) {
>> +        CPUBreakpoint *bp;
>> +
>> +        tcg_gen_insn_start(dc->base.pc_next, dc->cc_op);

> You've probably discovered that this will have to be its own hook.

Yes, sorry about not fixing it before sending.


>> +        num_insns++;
>> +
>> +        /* Pass breakpoint hits to target for further processing */
>> +        bp = NULL;
>> +        do {
>> +            bp = cpu_breakpoint_get(cpu, dc->base.pc_next, bp);
>> +            if (unlikely(bp)) {
>> +                if (gen_intermediate_code_target_breakpoint_hit(dc, env, bp)) {
>> +                    goto done_generating;
>> +                }
>> +            }
>> +        } while (bp != NULL);

> Why would you need to loop here?

cpu_breakpoint_get() just returns breakpoints matching a PC, and delays other
checks (like flags) to target code. That's why cpu_breakpoint_get() is now used
as a looping construct (thus the change in qlist to continue a previous
foreach).


Thanks,
  Lluis
diff mbox

Patch

diff --git a/include/exec/translate-all_template.h b/include/exec/translate-all_template.h
new file mode 100644
index 0000000..9e0c361
--- /dev/null
+++ b/include/exec/translate-all_template.h
@@ -0,0 +1,58 @@ 
+/*
+ * Generic intermediate code generation.
+ *
+ * Copyright (C) 2016 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"
+
+
+/**
+ * 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.
+ * @singlestep_enabled: "Hardware" single stepping enabled.
+ * @pc_first: Address of first guest instruction in this TB.
+ * @pc_next: Address of next guest instruction in this TB.
+ *
+ * Architecture-agnostic disassembly context.
+ */
+typedef struct DisasContextBase
+{
+    TranslationBlock *tb;
+    bool singlestep_enabled;
+    target_ulong pc_first;
+    target_ulong pc_next;
+    DisasJumpType jmp_type;
+} DisasContextBase;
+
+#endif  /* EXEC__TRANSLATE_ALL_TEMPLATE_H */
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index b7a10f7..de20927 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -808,6 +808,27 @@  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 0000000..afbd088
--- /dev/null
+++ b/translate-all_template.h
@@ -0,0 +1,160 @@ 
+/*
+ * Generic intermediate code generation.
+ *
+ * Copyright (C) 2016 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"
+
+
+static void gen_intermediate_code_target_init_disas_context(
+    DisasContext * restrict dc, const CPUArchState * restrict env);
+
+static void gen_intermediate_code_target_init_globals(
+    DisasContext * restrict dc, const CPUArchState * restrict env);
+
+static bool gen_intermediate_code_target_breakpoint_hit(
+    DisasContext * restrict dc, const CPUArchState * restrict env,
+    const CPUBreakpoint * restrict bp);
+
+static DisasJumpType gen_intermediate_code_target_stop_check(
+    DisasContext * restrict dc, const CPUArchState * restrict env);
+
+static void gen_intermediate_code_target_stop(
+    DisasContext * restrict dc, const CPUArchState * restrict env);
+
+static int gen_intermediate_code_target_get_disas_flags(
+    const DisasContext *dc);
+
+
+void gen_intermediate_code(CPUState *cpu, struct TranslationBlock *tb)
+{
+    CPUArchState *env = cpu->env_ptr;
+    DisasContext dc1, *dc = &dc1;
+    int num_insns;
+    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;
+    gen_intermediate_code_target_init_disas_context(dc, env);
+
+    /* Target-specific globals */
+    gen_intermediate_code_target_init_globals(dc, env);
+
+    /* Instruction counting */
+    num_insns = 0;
+    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;
+    }
+
+    /* Start translating */
+    gen_tb_start(dc->base.tb);
+
+    while (true) {
+        CPUBreakpoint *bp;
+
+        tcg_gen_insn_start(dc->base.pc_next, dc->cc_op);
+        num_insns++;
+
+        /* Pass breakpoint hits to target for further processing */
+        bp = NULL;
+        do {
+            bp = cpu_breakpoint_get(cpu, dc->base.pc_next, bp);
+            if (unlikely(bp)) {
+                if (gen_intermediate_code_target_breakpoint_hit(dc, env, bp)) {
+                    goto done_generating;
+                }
+            }
+        } while (bp != NULL);
+
+        /* Accept I/O on last instruction */
+        if (num_insns == max_insns && (dc->base.tb->cflags & CF_LAST_IO)) {
+            gen_io_start();
+        }
+
+        /* Disassemble one instruction */
+        dc->base.pc_next = disas_insn(env, dc, dc->base.pc_next);
+
+        /**************************************************/
+        /* Conditions to stop translation                 */
+        /**************************************************/
+
+        /* Too many instructions */
+        if (tcg_op_buf_full() || num_insns >= max_insns) {
+            dc->base.jmp_type = DJ_TOO_MANY;
+            break;
+        }
+
+        /* Single-stepping */
+        if (dc->base.singlestep_enabled || singlestep) {
+            dc->base.jmp_type = DJ_TOO_MANY;
+            break;
+        }
+
+        /*
+         * 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 (already
+         *       disassembled), is always allowed to cross pages.
+         */
+        if ((dc->base.pc_first & TARGET_PAGE_MASK)
+            != (dc->base.pc_next & TARGET_PAGE_MASK)) {
+            dc->base.jmp_type = DJ_TOO_MANY;
+            break;
+        }
+
+        /* Target-specific conditions */
+        dc->base.jmp_type = gen_intermediate_code_target_stop_check(dc, env);
+        if (dc->base.jmp_type >= DJ_TARGET) {
+            break;
+        }
+    }
+
+    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, num_insns);
+
+#ifdef DEBUG_DISAS
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) &&
+        qemu_log_in_addr_range(dc->base.pc_first)) {
+        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");
+    }
+#endif
+
+    dc->base.tb->size = dc->base.pc_next - dc->base.pc_first;
+    dc->base.tb->icount = num_insns;
+}
+
+#endif  /* TRANSLATE_ALL_TEMPLATE_H */