diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-03-06 23:23:54 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-03-06 23:23:54 +0000 |
commit | 7d13299d07a9c3c42277207ae7a691f0501a70b2 (patch) | |
tree | 981b791299c674712bacc00292de4afc6cc436ec /exec-i386.c | |
parent | 1017ebe9cb38ae034b0e7c6c449abe2c9b5284fb (diff) |
added translation cache
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@25 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'exec-i386.c')
-rw-r--r-- | exec-i386.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/exec-i386.c b/exec-i386.c new file mode 100644 index 0000000000..c067685095 --- /dev/null +++ b/exec-i386.c @@ -0,0 +1,213 @@ +/* + * i386 emulator main execution loop + * + * Copyright (c) 2003 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "exec-i386.h" + +#define DEBUG_EXEC +#define DEBUG_FLUSH + +/* main execution loop */ + +/* maximum total translate dcode allocated */ +#define CODE_GEN_BUFFER_SIZE (2048 * 1024) +//#define CODE_GEN_BUFFER_SIZE (128 * 1024) +#define CODE_GEN_MAX_SIZE 65536 +#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ + +/* threshold to flush the translated code buffer */ +#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) + +#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) +#define CODE_GEN_HASH_BITS 15 +#define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) +typedef struct TranslationBlock { + unsigned long pc; /* simulated PC corresponding to this block */ + uint8_t *tc_ptr; /* pointer to the translated code */ + struct TranslationBlock *hash_next; /* next matching block */ +} TranslationBlock; + +TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; +TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; +int nb_tbs; + +uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; +uint8_t *code_gen_ptr; + +#ifdef DEBUG_EXEC +static const char *cc_op_str[] = { + "DYNAMIC", + "EFLAGS", + "MUL", + "ADDB", + "ADDW", + "ADDL", + "ADCB", + "ADCW", + "ADCL", + "SUBB", + "SUBW", + "SUBL", + "SBBB", + "SBBW", + "SBBL", + "LOGICB", + "LOGICW", + "LOGICL", + "INCB", + "INCW", + "INCL", + "DECB", + "DECW", + "DECL", + "SHLB", + "SHLW", + "SHLL", + "SARB", + "SARW", + "SARL", +}; + +static void cpu_x86_dump_state(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + eflags |= (DF & DIRECTION_FLAG); + fprintf(logfile, + "EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n" + "ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n" + "CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n", + env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX], + env->regs[R_ESI], env->regs[R_EDI], env->regs[R_EBP], env->regs[R_ESP], + env->cc_src, env->cc_dst, cc_op_str[env->cc_op], + eflags & DIRECTION_FLAG ? 'D' : '-', + eflags & CC_O ? 'O' : '-', + eflags & CC_S ? 'S' : '-', + eflags & CC_Z ? 'Z' : '-', + eflags & CC_A ? 'A' : '-', + eflags & CC_P ? 'P' : '-', + eflags & CC_C ? 'C' : '-' + ); +#if 1 + fprintf(logfile, "ST0=%f ST1=%f ST2=%f ST3=%f\n", + (double)ST0, (double)ST1, (double)ST(2), (double)ST(3)); +#endif +} + +#endif + +void cpu_x86_tblocks_init(void) +{ + if (!code_gen_ptr) { + code_gen_ptr = code_gen_buffer; + } +} + +/* flush all the translation blocks */ +static void tb_flush(void) +{ + int i; +#ifdef DEBUG_FLUSH + printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", + code_gen_ptr - code_gen_buffer, + nb_tbs, + (code_gen_ptr - code_gen_buffer) / nb_tbs); +#endif + nb_tbs = 0; + for(i = 0;i < CODE_GEN_HASH_SIZE; i++) + tb_hash[i] = NULL; + code_gen_ptr = code_gen_buffer; + /* XXX: flush processor icache at this point */ +} + +/* find a translation block in the translation cache. If not found, + allocate a new one */ +static inline TranslationBlock *tb_find_and_alloc(unsigned long pc) +{ + TranslationBlock **ptb, *tb; + unsigned int h; + + h = pc & (CODE_GEN_HASH_SIZE - 1); + ptb = &tb_hash[h]; + for(;;) { + tb = *ptb; + if (!tb) + break; + if (tb->pc == pc) + return tb; + ptb = &tb->hash_next; + } + if (nb_tbs >= CODE_GEN_MAX_BLOCKS || + (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) + tb_flush(); + tb = &tbs[nb_tbs++]; + *ptb = tb; + tb->pc = pc; + tb->tc_ptr = NULL; + tb->hash_next = NULL; + return tb; +} + +int cpu_x86_exec(CPUX86State *env1) +{ + int saved_T0, saved_T1, saved_A0; + CPUX86State *saved_env; + int code_gen_size, ret; + void (*gen_func)(void); + TranslationBlock *tb; + uint8_t *tc_ptr; + + /* first we save global registers */ + saved_T0 = T0; + saved_T1 = T1; + saved_A0 = A0; + saved_env = env; + env = env1; + + /* prepare setjmp context for exception handling */ + if (setjmp(env->jmp_env) == 0) { + for(;;) { +#ifdef DEBUG_EXEC + if (loglevel) { + cpu_x86_dump_state(); + } +#endif + tb = tb_find_and_alloc((unsigned long)env->pc); + tc_ptr = tb->tc_ptr; + if (!tb->tc_ptr) { + /* if no translated code available, then translate it now */ + tc_ptr = code_gen_ptr; + cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, + &code_gen_size, (uint8_t *)env->pc); + tb->tc_ptr = tc_ptr; + code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); + } + /* execute the generated code */ + gen_func = (void *)tc_ptr; + gen_func(); + } + } + ret = env->exception_index; + + /* restore global registers */ + T0 = saved_T0; + T1 = saved_T1; + A0 = saved_A0; + env = saved_env; + return ret; +} |