target/avr: Add instruction translation - CPU main translation function
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>
This commit is contained in:
parent
46188cabae
commit
9baade8d3b
@ -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];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user