diff mbox series

[v35,09/13] target/avr: Add instruction translation - CPU main translation function

Message ID 20191029212430.20617-10-mrolnik@gmail.com (mailing list archive)
State New, archived
Headers show
Series QEMU AVR 8 bit cores | expand

Commit Message

Michael Rolnik Oct. 29, 2019, 9:24 p.m. UTC
Co-developed-by: Richard Henderson <richard.henderson@linaro.org>
Co-developed-by: Michael Rolnik <mrolnik@gmail.com>

Signed-off-by: Michael Rolnik <mrolnik@gmail.com>
---
 target/avr/translate.c | 234 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 234 insertions(+)

Comments

Philippe Mathieu-Daudé Nov. 22, 2019, 11:48 a.m. UTC | #1
On 10/29/19 10:24 PM, Michael Rolnik wrote:
> Co-developed-by: Richard Henderson <richard.henderson@linaro.org>

This misses a:
"Signed-off-by: Richard Henderson <richard.henderson@linaro.org>"

> Co-developed-by: Michael Rolnik <mrolnik@gmail.com>
> 
> Signed-off-by: Michael Rolnik <mrolnik@gmail.com>

Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>

> ---
>   target/avr/translate.c | 234 +++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 234 insertions(+)
> 
> diff --git a/target/avr/translate.c b/target/avr/translate.c
> index 30ba13bdd7..fdf4e11f58 100644
> --- a/target/avr/translate.c
> +++ b/target/avr/translate.c
> @@ -2792,3 +2792,237 @@ static bool trans_WDR(DisasContext *ctx, arg_WDR *a)
>   
>       return true;
>   }
> +
> +
> +void avr_cpu_tcg_init(void)
> +{
> +    int i;
> +
> +#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x)
> +    cpu_pc = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc");
> +    cpu_Cf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf");
> +    cpu_Zf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf");
> +    cpu_Nf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf");
> +    cpu_Vf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf");
> +    cpu_Sf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf");
> +    cpu_Hf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf");
> +    cpu_Tf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf");
> +    cpu_If = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If");
> +    cpu_rampD = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "rampD");
> +    cpu_rampX = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "rampX");
> +    cpu_rampY = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "rampY");
> +    cpu_rampZ = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "rampZ");
> +    cpu_eind = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind");
> +    cpu_sp = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp");
> +    cpu_skip = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(skip), "skip");
> +
> +    for (i = 0; i < NO_CPU_REGISTERS; i++) {
> +        cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]),
> +                                          reg_names[i]);
> +    }
> +#undef AVR_REG_OFFS
> +}
> +
> +static void translate(DisasContext *ctx)
> +{
> +    uint32_t opcode = next_word(ctx);
> +
> +    if (!decode_insn(ctx, opcode)) {
> +        gen_helper_unsupported(cpu_env);
> +        ctx->bstate = DISAS_NORETURN;
> +    }
> +}
> +
> +/* Standardize the cpu_skip condition to NE.  */
> +static bool canonicalize_skip(DisasContext *ctx)
> +{
> +    switch (ctx->skip_cond) {
> +    case TCG_COND_NEVER:
> +        /* Normal case: cpu_skip is known to be false.  */
> +        return false;
> +
> +    case TCG_COND_ALWAYS:
> +        /*
> +         * Breakpoint case: cpu_skip is known to be true, via TB_FLAGS_SKIP.
> +         * The breakpoint is on the instruction being skipped, at the start
> +         * of the TranslationBlock.  No need to update.
> +         */
> +        return false;
> +
> +    case TCG_COND_NE:
> +        if (ctx->skip_var1 == NULL) {
> +            tcg_gen_mov_tl(cpu_skip, ctx->skip_var0);
> +        } else {
> +            tcg_gen_xor_tl(cpu_skip, ctx->skip_var0, ctx->skip_var1);
> +            ctx->skip_var1 = NULL;
> +        }
> +        break;
> +
> +    default:
> +        /* Convert to a NE condition vs 0. */
> +        if (ctx->skip_var1 == NULL) {
> +            tcg_gen_setcondi_tl(ctx->skip_cond, cpu_skip, ctx->skip_var0, 0);
> +        } else {
> +            tcg_gen_setcond_tl(ctx->skip_cond, cpu_skip,
> +                               ctx->skip_var0, ctx->skip_var1);
> +            ctx->skip_var1 = NULL;
> +        }
> +        ctx->skip_cond = TCG_COND_NE;
> +        break;
> +    }
> +    if (ctx->free_skip_var0) {
> +        tcg_temp_free(ctx->skip_var0);
> +        ctx->free_skip_var0 = false;
> +    }
> +    ctx->skip_var0 = cpu_skip;
> +    return true;
> +}
> +
> +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
> +{
> +    CPUAVRState *env = cs->env_ptr;
> +    DisasContext ctx = {
> +        .tb = tb,
> +        .cs = cs,
> +        .env = env,
> +        .memidx = 0,
> +        .bstate = DISAS_NEXT,
> +        .skip_cond = TCG_COND_NEVER,
> +        .singlestep = cs->singlestep_enabled,
> +    };
> +    target_ulong pc_start = tb->pc / 2;
> +    int num_insns = 0;
> +
> +    if (tb->flags & TB_FLAGS_FULL_ACCESS) {
> +        /*
> +         * This flag is set by ST/LD instruction we will regenerate it ONLY
> +         * with mem/cpu memory access instead of mem access
> +         */
> +        max_insns = 1;
> +    }
> +    if (ctx.singlestep) {
> +        max_insns = 1;
> +    }
> +
> +    gen_tb_start(tb);
> +
> +    ctx.npc = pc_start;
> +    if (tb->flags & TB_FLAGS_SKIP) {
> +        ctx.skip_cond = TCG_COND_ALWAYS;
> +        ctx.skip_var0 = cpu_skip;
> +    }
> +
> +    do {
> +        TCGLabel *skip_label = NULL;
> +
> +        /* translate current instruction */
> +        tcg_gen_insn_start(ctx.npc);
> +        num_insns++;
> +
> +        /*
> +         * this is due to some strange GDB behavior
> +         * let's assume main has address 0x100
> +         * b main   - sets breakpoint at address 0x00000100 (code)
> +         * b *0x100 - sets breakpoint at address 0x00800100 (data)
> +         */
> +        if (unlikely(!ctx.singlestep &&
> +                (cpu_breakpoint_test(cs, OFFSET_CODE + ctx.npc * 2, BP_ANY) ||
> +                 cpu_breakpoint_test(cs, OFFSET_DATA + ctx.npc * 2, BP_ANY)))) {
> +            canonicalize_skip(&ctx);
> +            tcg_gen_movi_tl(cpu_pc, ctx.npc);
> +            gen_helper_debug(cpu_env);
> +            goto done_generating;
> +        }
> +
> +        /* Conditionally skip the next instruction, if indicated.  */
> +        if (ctx.skip_cond != TCG_COND_NEVER) {
> +            skip_label = gen_new_label();
> +            if (ctx.skip_var0 == cpu_skip) {
> +                /*
> +                 * Copy cpu_skip so that we may zero it before the branch.
> +                 * This ensures that cpu_skip is non-zero after the label
> +                 * if and only if the skipped insn itself sets a skip.
> +                 */
> +                ctx.free_skip_var0 = true;
> +                ctx.skip_var0 = tcg_temp_new();
> +                tcg_gen_mov_tl(ctx.skip_var0, cpu_skip);
> +                tcg_gen_movi_tl(cpu_skip, 0);
> +            }
> +            if (ctx.skip_var1 == NULL) {
> +                tcg_gen_brcondi_tl(ctx.skip_cond, ctx.skip_var0, 0, skip_label);
> +            } else {
> +                tcg_gen_brcond_tl(ctx.skip_cond, ctx.skip_var0,
> +                                  ctx.skip_var1, skip_label);
> +                ctx.skip_var1 = NULL;
> +            }
> +            if (ctx.free_skip_var0) {
> +                tcg_temp_free(ctx.skip_var0);
> +                ctx.free_skip_var0 = false;
> +            }
> +            ctx.skip_cond = TCG_COND_NEVER;
> +            ctx.skip_var0 = NULL;
> +        }
> +
> +        translate(&ctx);
> +
> +        if (skip_label) {
> +            canonicalize_skip(&ctx);
> +            gen_set_label(skip_label);
> +            if (ctx.bstate == DISAS_NORETURN) {
> +                ctx.bstate = DISAS_CHAIN;
> +            }
> +        }
> +    } while (ctx.bstate == DISAS_NEXT
> +             && num_insns < max_insns
> +             && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4
> +             && !tcg_op_buf_full());
> +
> +    if (tb->cflags & CF_LAST_IO) {
> +        gen_io_end();
> +    }
> +
> +    bool nonconst_skip = canonicalize_skip(&ctx);
> +
> +    switch (ctx.bstate) {
> +    case DISAS_NORETURN:
> +        assert(!nonconst_skip);
> +        break;
> +    case DISAS_NEXT:
> +    case DISAS_TOO_MANY:
> +    case DISAS_CHAIN:
> +        if (!nonconst_skip) {
> +            /* Note gen_goto_tb checks singlestep.  */
> +            gen_goto_tb(&ctx, 1, ctx.npc);
> +            break;
> +        }
> +        tcg_gen_movi_tl(cpu_pc, ctx.npc);
> +        /* fall through */
> +    case DISAS_LOOKUP:
> +        if (!ctx.singlestep) {
> +            tcg_gen_lookup_and_goto_ptr();
> +            break;
> +        }
> +        /* fall through */
> +    case DISAS_EXIT:
> +        if (ctx.singlestep) {
> +            gen_helper_debug(cpu_env);
> +        } else {
> +            tcg_gen_exit_tb(NULL, 0);
> +        }
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }
> +
> +done_generating:
> +    gen_tb_end(tb, num_insns);
> +
> +    tb->size = (ctx.npc - pc_start) * 2;
> +    tb->icount = num_insns;
> +}
> +
> +void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb,
> +                            target_ulong *data)
> +{
> +    env->pc_w = data[0];
> +}
>
diff mbox series

Patch

diff --git a/target/avr/translate.c b/target/avr/translate.c
index 30ba13bdd7..fdf4e11f58 100644
--- a/target/avr/translate.c
+++ b/target/avr/translate.c
@@ -2792,3 +2792,237 @@  static bool trans_WDR(DisasContext *ctx, arg_WDR *a)
 
     return true;
 }
+
+
+void avr_cpu_tcg_init(void)
+{
+    int i;
+
+#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x)
+    cpu_pc = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc");
+    cpu_Cf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf");
+    cpu_Zf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf");
+    cpu_Nf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf");
+    cpu_Vf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf");
+    cpu_Sf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf");
+    cpu_Hf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf");
+    cpu_Tf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf");
+    cpu_If = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If");
+    cpu_rampD = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "rampD");
+    cpu_rampX = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "rampX");
+    cpu_rampY = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "rampY");
+    cpu_rampZ = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "rampZ");
+    cpu_eind = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind");
+    cpu_sp = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp");
+    cpu_skip = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(skip), "skip");
+
+    for (i = 0; i < NO_CPU_REGISTERS; i++) {
+        cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]),
+                                          reg_names[i]);
+    }
+#undef AVR_REG_OFFS
+}
+
+static void translate(DisasContext *ctx)
+{
+    uint32_t opcode = next_word(ctx);
+
+    if (!decode_insn(ctx, opcode)) {
+        gen_helper_unsupported(cpu_env);
+        ctx->bstate = DISAS_NORETURN;
+    }
+}
+
+/* Standardize the cpu_skip condition to NE.  */
+static bool canonicalize_skip(DisasContext *ctx)
+{
+    switch (ctx->skip_cond) {
+    case TCG_COND_NEVER:
+        /* Normal case: cpu_skip is known to be false.  */
+        return false;
+
+    case TCG_COND_ALWAYS:
+        /*
+         * Breakpoint case: cpu_skip is known to be true, via TB_FLAGS_SKIP.
+         * The breakpoint is on the instruction being skipped, at the start
+         * of the TranslationBlock.  No need to update.
+         */
+        return false;
+
+    case TCG_COND_NE:
+        if (ctx->skip_var1 == NULL) {
+            tcg_gen_mov_tl(cpu_skip, ctx->skip_var0);
+        } else {
+            tcg_gen_xor_tl(cpu_skip, ctx->skip_var0, ctx->skip_var1);
+            ctx->skip_var1 = NULL;
+        }
+        break;
+
+    default:
+        /* Convert to a NE condition vs 0. */
+        if (ctx->skip_var1 == NULL) {
+            tcg_gen_setcondi_tl(ctx->skip_cond, cpu_skip, ctx->skip_var0, 0);
+        } else {
+            tcg_gen_setcond_tl(ctx->skip_cond, cpu_skip,
+                               ctx->skip_var0, ctx->skip_var1);
+            ctx->skip_var1 = NULL;
+        }
+        ctx->skip_cond = TCG_COND_NE;
+        break;
+    }
+    if (ctx->free_skip_var0) {
+        tcg_temp_free(ctx->skip_var0);
+        ctx->free_skip_var0 = false;
+    }
+    ctx->skip_var0 = cpu_skip;
+    return true;
+}
+
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
+{
+    CPUAVRState *env = cs->env_ptr;
+    DisasContext ctx = {
+        .tb = tb,
+        .cs = cs,
+        .env = env,
+        .memidx = 0,
+        .bstate = DISAS_NEXT,
+        .skip_cond = TCG_COND_NEVER,
+        .singlestep = cs->singlestep_enabled,
+    };
+    target_ulong pc_start = tb->pc / 2;
+    int num_insns = 0;
+
+    if (tb->flags & TB_FLAGS_FULL_ACCESS) {
+        /*
+         * This flag is set by ST/LD instruction we will regenerate it ONLY
+         * with mem/cpu memory access instead of mem access
+         */
+        max_insns = 1;
+    }
+    if (ctx.singlestep) {
+        max_insns = 1;
+    }
+
+    gen_tb_start(tb);
+
+    ctx.npc = pc_start;
+    if (tb->flags & TB_FLAGS_SKIP) {
+        ctx.skip_cond = TCG_COND_ALWAYS;
+        ctx.skip_var0 = cpu_skip;
+    }
+
+    do {
+        TCGLabel *skip_label = NULL;
+
+        /* translate current instruction */
+        tcg_gen_insn_start(ctx.npc);
+        num_insns++;
+
+        /*
+         * this is due to some strange GDB behavior
+         * let's assume main has address 0x100
+         * b main   - sets breakpoint at address 0x00000100 (code)
+         * b *0x100 - sets breakpoint at address 0x00800100 (data)
+         */
+        if (unlikely(!ctx.singlestep &&
+                (cpu_breakpoint_test(cs, OFFSET_CODE + ctx.npc * 2, BP_ANY) ||
+                 cpu_breakpoint_test(cs, OFFSET_DATA + ctx.npc * 2, BP_ANY)))) {
+            canonicalize_skip(&ctx);
+            tcg_gen_movi_tl(cpu_pc, ctx.npc);
+            gen_helper_debug(cpu_env);
+            goto done_generating;
+        }
+
+        /* Conditionally skip the next instruction, if indicated.  */
+        if (ctx.skip_cond != TCG_COND_NEVER) {
+            skip_label = gen_new_label();
+            if (ctx.skip_var0 == cpu_skip) {
+                /*
+                 * Copy cpu_skip so that we may zero it before the branch.
+                 * This ensures that cpu_skip is non-zero after the label
+                 * if and only if the skipped insn itself sets a skip.
+                 */
+                ctx.free_skip_var0 = true;
+                ctx.skip_var0 = tcg_temp_new();
+                tcg_gen_mov_tl(ctx.skip_var0, cpu_skip);
+                tcg_gen_movi_tl(cpu_skip, 0);
+            }
+            if (ctx.skip_var1 == NULL) {
+                tcg_gen_brcondi_tl(ctx.skip_cond, ctx.skip_var0, 0, skip_label);
+            } else {
+                tcg_gen_brcond_tl(ctx.skip_cond, ctx.skip_var0,
+                                  ctx.skip_var1, skip_label);
+                ctx.skip_var1 = NULL;
+            }
+            if (ctx.free_skip_var0) {
+                tcg_temp_free(ctx.skip_var0);
+                ctx.free_skip_var0 = false;
+            }
+            ctx.skip_cond = TCG_COND_NEVER;
+            ctx.skip_var0 = NULL;
+        }
+
+        translate(&ctx);
+
+        if (skip_label) {
+            canonicalize_skip(&ctx);
+            gen_set_label(skip_label);
+            if (ctx.bstate == DISAS_NORETURN) {
+                ctx.bstate = DISAS_CHAIN;
+            }
+        }
+    } while (ctx.bstate == DISAS_NEXT
+             && num_insns < max_insns
+             && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4
+             && !tcg_op_buf_full());
+
+    if (tb->cflags & CF_LAST_IO) {
+        gen_io_end();
+    }
+
+    bool nonconst_skip = canonicalize_skip(&ctx);
+
+    switch (ctx.bstate) {
+    case DISAS_NORETURN:
+        assert(!nonconst_skip);
+        break;
+    case DISAS_NEXT:
+    case DISAS_TOO_MANY:
+    case DISAS_CHAIN:
+        if (!nonconst_skip) {
+            /* Note gen_goto_tb checks singlestep.  */
+            gen_goto_tb(&ctx, 1, ctx.npc);
+            break;
+        }
+        tcg_gen_movi_tl(cpu_pc, ctx.npc);
+        /* fall through */
+    case DISAS_LOOKUP:
+        if (!ctx.singlestep) {
+            tcg_gen_lookup_and_goto_ptr();
+            break;
+        }
+        /* fall through */
+    case DISAS_EXIT:
+        if (ctx.singlestep) {
+            gen_helper_debug(cpu_env);
+        } else {
+            tcg_gen_exit_tb(NULL, 0);
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+done_generating:
+    gen_tb_end(tb, num_insns);
+
+    tb->size = (ctx.npc - pc_start) * 2;
+    tb->icount = num_insns;
+}
+
+void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb,
+                            target_ulong *data)
+{
+    env->pc_w = data[0];
+}