Skip to content

Commit

Permalink
target/avr: Add instruction translation - CPU main translation function
Browse files Browse the repository at this point in the history
Add the core of translation mechanism.

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>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Aleksandar Markovic <aleksandar.m.mail@gmail.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Aleksandar Markovic <aleksandar.m.mail@gmail.com>
Signed-off-by: Thomas Huth <huth@tuxfamily.org>
Message-Id: <20200705140315.260514-17-huth@tuxfamily.org>
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
  • Loading branch information
michaelrolnik authored and philmd committed Jul 11, 2020
1 parent 46188ca commit 9baade8
Showing 1 changed file with 213 additions and 0 deletions.
213 changes: 213 additions & 0 deletions target/avr/translate.c
Expand Up @@ -2805,3 +2805,216 @@ static bool trans_WDR(DisasContext *ctx, arg_WDR *a)

return true;
}

/*
* Core translation mechanism functions:
*
* - translate()
* - canonicalize_skip()
* - gen_intermediate_code()
* - restore_state_to_opc()
*
*/
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];
}

0 comments on commit 9baade8

Please sign in to comment.