SH: Improve movca.l/ocbi emulation.

Author: Vladimir Prus <vladimir@codesourcery.com>

    Fix movcal.l/ocbi emulation.

        * target-sh4/cpu.h (memory_content): New.
        (CPUSH4State): New fields movcal_backup and movcal_backup_tail.
        * target-sh4/helper.h (helper_movcal)
        (helper_discard_movcal_backup, helper_ocbi): New.
        * target-sh4/op_helper.c (helper_movcal)
        (helper_discard_movcal_backup, helper_ocbi): New.
        * target-sh4/translate.c (DisasContext): New field has_movcal.
        (sh4_defs): Update CVS for SH7785.
        (cpu_sh4_init): Initialize env->movcal_backup_tail.
        (_decode_opc): Discard movca.l-backup.
        Make use of helper_movcal and helper_ocbi.
        (gen_intermediate_code_internal): Initialize has_movcal to 1.

Thanks to Shin-ichiro KAWASAKI and Paul Mundt for valuable feedback.

Acked-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6966 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
edgar_igl 2009-04-01 23:10:46 +00:00
parent 714fa308a3
commit 852d481faf
5 changed files with 156 additions and 5 deletions

View file

@ -50,6 +50,7 @@ typedef struct DisasContext {
uint32_t delayed_pc;
int singlestep_enabled;
uint32_t features;
int has_movcal;
} DisasContext;
#if defined(CONFIG_USER_ONLY)
@ -283,6 +284,7 @@ CPUSH4State *cpu_sh4_init(const char *cpu_model)
env = qemu_mallocz(sizeof(CPUSH4State));
env->features = def->features;
cpu_exec_init(env);
env->movcal_backup_tail = &(env->movcal_backup);
sh4_translate_init();
env->cpu_model_str = cpu_model;
cpu_sh4_reset(env);
@ -495,6 +497,37 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
static void _decode_opc(DisasContext * ctx)
{
/* This code tries to make movcal emulation sufficiently
accurate for Linux purposes. This instruction writes
memory, and prior to that, always allocates a cache line.
It is used in two contexts:
- in memcpy, where data is copied in blocks, the first write
of to a block uses movca.l for performance.
- in arch/sh/mm/cache-sh4.c, movcal.l + ocbi combination is used
to flush the cache. Here, the data written by movcal.l is never
written to memory, and the data written is just bogus.
To simulate this, we simulate movcal.l, we store the value to memory,
but we also remember the previous content. If we see ocbi, we check
if movcal.l for that address was done previously. If so, the write should
not have hit the memory, so we restore the previous content.
When we see an instruction that is neither movca.l
nor ocbi, the previous content is discarded.
To optimize, we only try to flush stores when we're at the start of
TB, or if we already saw movca.l in this TB and did not flush stores
yet. */
if (ctx->has_movcal)
{
int opcode = ctx->opcode & 0xf0ff;
if (opcode != 0x0093 /* ocbi */
&& opcode != 0x00c3 /* movca.l */)
{
gen_helper_discard_movcal_backup ();
ctx->has_movcal = 0;
}
}
#if 0
fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode);
#endif
@ -1545,7 +1578,13 @@ static void _decode_opc(DisasContext * ctx)
}
return;
case 0x00c3: /* movca.l R0,@Rm */
tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
{
TCGv val = tcg_temp_new();
tcg_gen_qemu_ld32u(val, REG(B11_8), ctx->memidx);
gen_helper_movcal (REG(B11_8), val);
tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
}
ctx->has_movcal = 1;
return;
case 0x40a9:
/* MOVUA.L @Rm,R0 (Rm) -> R0
@ -1594,9 +1633,7 @@ static void _decode_opc(DisasContext * ctx)
break;
case 0x0093: /* ocbi @Rn */
{
TCGv dummy = tcg_temp_new();
tcg_gen_qemu_ld32s(dummy, REG(B11_8), ctx->memidx);
tcg_temp_free(dummy);
gen_helper_ocbi (REG(B11_8));
}
return;
case 0x00a3: /* ocbp @Rn */
@ -1876,6 +1913,7 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
ctx.tb = tb;
ctx.singlestep_enabled = env->singlestep_enabled;
ctx.features = env->features;
ctx.has_movcal = (tb->flags & TB_FLAG_PENDING_MOVCA);
#ifdef DEBUG_DISAS
qemu_log_mask(CPU_LOG_TB_CPU,