aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2008-06-29 01:03:05 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2008-06-29 01:03:05 +0000
commit2e70f6efa8b960d3b5401373ad6fa98747bb9578 (patch)
tree4864a691a4d52324fe4626261e202525dd3a8659
parentf6e5889e7f581d286cd2153a71c386795a7070ac (diff)
Add instruction counter.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4799 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--cpu-all.h2
-rw-r--r--cpu-defs.h34
-rw-r--r--cpu-exec.c93
-rw-r--r--exec-all.h33
-rw-r--r--exec.c147
-rw-r--r--hw/mips_timer.c5
-rw-r--r--qemu-doc.texi11
-rw-r--r--softmmu_template.h22
-rw-r--r--target-alpha/cpu.h2
-rw-r--r--target-alpha/translate.c33
-rw-r--r--target-arm/cpu.h2
-rw-r--r--target-arm/translate.c31
-rw-r--r--target-cris/cpu.h2
-rw-r--r--target-cris/translate.c21
-rw-r--r--target-i386/cpu.h2
-rw-r--r--target-i386/translate.c65
-rw-r--r--target-m68k/cpu.h2
-rw-r--r--target-m68k/translate.c21
-rw-r--r--target-mips/cpu.h6
-rw-r--r--target-mips/translate.c51
-rw-r--r--target-ppc/cpu.h3
-rw-r--r--target-ppc/helper.c1
-rw-r--r--target-ppc/translate.c32
-rw-r--r--target-sh4/cpu.h5
-rw-r--r--target-sh4/translate.c31
-rw-r--r--target-sparc/cpu.h5
-rw-r--r--target-sparc/translate.c20
-rw-r--r--translate-all.c9
-rw-r--r--vl.c256
29 files changed, 830 insertions, 117 deletions
diff --git a/cpu-all.h b/cpu-all.h
index 6a16125362..0b19ac9e33 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -782,6 +782,8 @@ void cpu_abort(CPUState *env, const char *fmt, ...)
__attribute__ ((__noreturn__));
extern CPUState *first_cpu;
extern CPUState *cpu_single_env;
+extern int64_t qemu_icount;
+extern int use_icount;
#define CPU_INTERRUPT_EXIT 0x01 /* wants exit from main loop */
#define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */
diff --git a/cpu-defs.h b/cpu-defs.h
index e197686040..9621b947d3 100644
--- a/cpu-defs.h
+++ b/cpu-defs.h
@@ -130,17 +130,29 @@ typedef struct CPUTLBEntry {
sizeof(target_phys_addr_t))];
} CPUTLBEntry;
+#ifdef WORDS_BIGENDIAN
+typedef struct icount_decr_u16 {
+ uint16_t high;
+ uint16_t low;
+} icount_decr_u16;
+#else
+typedef struct icount_decr_u16 {
+ uint16_t low;
+ uint16_t high;
+} icount_decr_u16;
+#endif
+
#define CPU_TEMP_BUF_NLONGS 128
#define CPU_COMMON \
struct TranslationBlock *current_tb; /* currently executing TB */ \
/* soft mmu support */ \
- /* in order to avoid passing too many arguments to the memory \
- write helpers, we store some rarely used information in the CPU \
+ /* in order to avoid passing too many arguments to the MMIO \
+ helpers, we store some rarely used information in the CPU \
context) */ \
- unsigned long mem_write_pc; /* host pc at which the memory was \
- written */ \
- target_ulong mem_write_vaddr; /* target virtual addr at which the \
- memory was written */ \
+ unsigned long mem_io_pc; /* host pc at which the memory was \
+ accessed */ \
+ target_ulong mem_io_vaddr; /* target virtual addr at which the \
+ memory was accessed */ \
int halted; /* TRUE if the CPU is in suspend state */ \
/* The meaning of the MMU modes is defined in the target code. */ \
CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \
@@ -149,6 +161,16 @@ typedef struct CPUTLBEntry {
/* buffer for temporaries in the code generator */ \
long temp_buf[CPU_TEMP_BUF_NLONGS]; \
\
+ int64_t icount_extra; /* Instructions until next timer event. */ \
+ /* Number of cycles left, with interrupt flag in high bit. \
+ This allows a single read-compare-cbranch-write sequence to test \
+ for both decrementer underflow and exceptions. */ \
+ union { \
+ uint32_t u32; \
+ icount_decr_u16 u16; \
+ } icount_decr; \
+ uint32_t can_do_io; /* nonzero if memory mapped IO is safe. */ \
+ \
/* from this point: preserved by CPU reset */ \
/* ice debug support */ \
target_ulong breakpoints[MAX_BREAKPOINTS]; \
diff --git a/cpu-exec.c b/cpu-exec.c
index 9d00cffb5e..08e10f4322 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -82,15 +82,40 @@ void cpu_resume_from_signal(CPUState *env1, void *puc)
longjmp(env->jmp_env, 1);
}
+/* Execute the code without caching the generated code. An interpreter
+ could be used if available. */
+static void cpu_exec_nocache(int max_cycles, TranslationBlock *orig_tb)
+{
+ unsigned long next_tb;
+ TranslationBlock *tb;
+
+ /* Should never happen.
+ We only end up here when an existing TB is too long. */
+ if (max_cycles > CF_COUNT_MASK)
+ max_cycles = CF_COUNT_MASK;
+
+ tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
+ max_cycles);
+ env->current_tb = tb;
+ /* execute the generated code */
+ next_tb = tcg_qemu_tb_exec(tb->tc_ptr);
+
+ if ((next_tb & 3) == 2) {
+ /* Restore PC. This may happen if async event occurs before
+ the TB starts executing. */
+ CPU_PC_FROM_TB(env, tb);
+ }
+ tb_phys_invalidate(tb, -1);
+ tb_free(tb);
+}
+
static TranslationBlock *tb_find_slow(target_ulong pc,
target_ulong cs_base,
uint64_t flags)
{
TranslationBlock *tb, **ptb1;
- int code_gen_size;
unsigned int h;
target_ulong phys_pc, phys_page1, phys_page2, virt_page2;
- uint8_t *tc_ptr;
tb_invalidated_flag = 0;
@@ -124,30 +149,8 @@ static TranslationBlock *tb_find_slow(target_ulong pc,
ptb1 = &tb->phys_hash_next;
}
not_found:
- /* if no translated code available, then translate it now */
- tb = tb_alloc(pc);
- if (!tb) {
- /* flush must be done */
- tb_flush(env);
- /* cannot fail at this point */
- tb = tb_alloc(pc);
- /* don't forget to invalidate previous TB info */
- tb_invalidated_flag = 1;
- }
- tc_ptr = code_gen_ptr;
- tb->tc_ptr = tc_ptr;
- tb->cs_base = cs_base;
- tb->flags = flags;
- cpu_gen_code(env, tb, &code_gen_size);
- code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
-
- /* check next page if needed */
- virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
- phys_page2 = -1;
- if ((pc & TARGET_PAGE_MASK) != virt_page2) {
- phys_page2 = get_phys_addr_code(env, virt_page2);
- }
- tb_link_phys(tb, phys_pc, phys_page2);
+ /* if no translated code available, then translate it now */
+ tb = tb_gen_code(env, pc, cs_base, flags, 0);
found:
/* we add the TB in the virtual pc hash table */
@@ -583,6 +586,7 @@ int cpu_exec(CPUState *env1)
of memory exceptions while generating the code, we
must recompute the hash index here */
next_tb = 0;
+ tb_invalidated_flag = 0;
}
#ifdef DEBUG_EXEC
if ((loglevel & CPU_LOG_EXEC)) {
@@ -604,16 +608,45 @@ int cpu_exec(CPUState *env1)
}
}
spin_unlock(&tb_lock);
- tc_ptr = tb->tc_ptr;
env->current_tb = tb;
+ while (env->current_tb) {
+ tc_ptr = tb->tc_ptr;
/* execute the generated code */
#if defined(__sparc__) && !defined(HOST_SOLARIS)
#undef env
- env = cpu_single_env;
+ env = cpu_single_env;
#define env cpu_single_env
#endif
- next_tb = tcg_qemu_tb_exec(tc_ptr);
- env->current_tb = NULL;
+ next_tb = tcg_qemu_tb_exec(tc_ptr);
+ env->current_tb = NULL;
+ if ((next_tb & 3) == 2) {
+ /* Instruction counter exired. */
+ int insns_left;
+ tb = (TranslationBlock *)(long)(next_tb & ~3);
+ /* Restore PC. */
+ CPU_PC_FROM_TB(env, tb);
+ insns_left = env->icount_decr.u32;
+ if (env->icount_extra && insns_left >= 0) {
+ /* Refill decrementer and continue execution. */
+ env->icount_extra += insns_left;
+ if (env->icount_extra > 0xffff) {
+ insns_left = 0xffff;
+ } else {
+ insns_left = env->icount_extra;
+ }
+ env->icount_extra -= insns_left;
+ env->icount_decr.u16.low = insns_left;
+ } else {
+ if (insns_left > 0) {
+ /* Execute remaining instructions. */
+ cpu_exec_nocache(insns_left, tb);
+ }
+ env->exception_index = EXCP_INTERRUPT;
+ next_tb = 0;
+ cpu_loop_exit();
+ }
+ }
+ }
/* reset soft MMU for next block (it can currently
only be set by a memory fault) */
#if defined(USE_KQEMU)
diff --git a/exec-all.h b/exec-all.h
index 62a9394873..b169370002 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -27,7 +27,7 @@
#define DISAS_UPDATE 2 /* cpu state was modified dynamically */
#define DISAS_TB_JUMP 3 /* only pc was modified statically */
-struct TranslationBlock;
+typedef struct TranslationBlock TranslationBlock;
/* XXX: make safe guess about sizes */
#define MAX_OP_PER_INSTR 64
@@ -48,6 +48,7 @@ extern target_ulong gen_opc_pc[OPC_BUF_SIZE];
extern target_ulong gen_opc_npc[OPC_BUF_SIZE];
extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
+extern uint16_t gen_opc_icount[OPC_BUF_SIZE];
extern target_ulong gen_opc_jump_pc[2];
extern uint32_t gen_opc_hflags[OPC_BUF_SIZE];
@@ -75,6 +76,10 @@ int cpu_restore_state_copy(struct TranslationBlock *tb,
CPUState *env, unsigned long searched_pc,
void *puc);
void cpu_resume_from_signal(CPUState *env1, void *puc);
+void cpu_io_recompile(CPUState *env, void *retaddr);
+TranslationBlock *tb_gen_code(CPUState *env,
+ target_ulong pc, target_ulong cs_base, int flags,
+ int cflags);
void cpu_exec_init(CPUState *env);
int page_unprotect(target_ulong address, unsigned long pc, void *puc);
void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end,
@@ -117,16 +122,15 @@ static inline int tlb_set_page(CPUState *env1, target_ulong vaddr,
#define USE_DIRECT_JUMP
#endif
-typedef struct TranslationBlock {
+struct TranslationBlock {
target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */
target_ulong cs_base; /* CS base for this block */
uint64_t flags; /* flags defining in which context the code was generated */
uint16_t size; /* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */
uint16_t cflags; /* compile flags */
-#define CF_TB_FP_USED 0x0002 /* fp ops are used in the TB */
-#define CF_FP_USED 0x0004 /* fp ops are used in the TB or in a chained TB */
-#define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */
+#define CF_COUNT_MASK 0x7fff
+#define CF_LAST_IO 0x8000 /* Last insn may be an IO access. */
uint8_t *tc_ptr; /* pointer to the translated code */
/* next matching tb for physical address. */
@@ -150,7 +154,8 @@ typedef struct TranslationBlock {
jmp_first */
struct TranslationBlock *jmp_next[2];
struct TranslationBlock *jmp_first;
-} TranslationBlock;
+ uint32_t icount;
+};
static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc)
{
@@ -173,9 +178,11 @@ static inline unsigned int tb_phys_hash_func(unsigned long pc)
}
TranslationBlock *tb_alloc(target_ulong pc);
+void tb_free(TranslationBlock *tb);
void tb_flush(CPUState *env);
void tb_link_phys(TranslationBlock *tb,
target_ulong phys_pc, target_ulong phys_page2);
+void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr);
extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
extern uint8_t *code_gen_ptr;
@@ -364,6 +371,20 @@ static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr)
}
return addr + env1->tlb_table[mmu_idx][page_index].addend - (unsigned long)phys_ram_base;
}
+
+/* Deterministic execution requires that IO only be performaed on the last
+ instruction of a TB so that interrupts take effect immediately. */
+static inline int can_do_io(CPUState *env)
+{
+ if (!use_icount)
+ return 1;
+
+ /* If not executing code then assume we are ok. */
+ if (!env->current_tb)
+ return 1;
+
+ return env->can_do_io != 0;
+}
#endif
#ifdef USE_KQEMU
diff --git a/exec.c b/exec.c
index 48600e49a9..06d92538f7 100644
--- a/exec.c
+++ b/exec.c
@@ -107,6 +107,13 @@ CPUState *first_cpu;
/* current CPU in the current thread. It is only valid inside
cpu_exec() */
CPUState *cpu_single_env;
+/* 0 = Do not count executed instructions.
+ 1 = Precice instruction counting.
+ 2 = Adaptive rate instruction counting. */
+int use_icount = 0;
+/* Current instruction counter. While executing translated code this may
+ include some instructions that have not yet been executed. */
+int64_t qemu_icount;
typedef struct PageDesc {
/* list of TBs intersecting this ram page */
@@ -633,7 +640,7 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n)
tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n]));
}
-static inline void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr)
+void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr)
{
CPUState *env;
PageDesc *p;
@@ -746,11 +753,9 @@ static void build_page_bitmap(PageDesc *p)
}
}
-#ifdef TARGET_HAS_PRECISE_SMC
-
-static void tb_gen_code(CPUState *env,
- target_ulong pc, target_ulong cs_base, int flags,
- int cflags)
+TranslationBlock *tb_gen_code(CPUState *env,
+ target_ulong pc, target_ulong cs_base,
+ int flags, int cflags)
{
TranslationBlock *tb;
uint8_t *tc_ptr;
@@ -764,6 +769,8 @@ static void tb_gen_code(CPUState *env,
tb_flush(env);
/* cannot fail at this point */
tb = tb_alloc(pc);
+ /* Don't forget to invalidate previous TB info. */
+ tb_invalidated_flag = 1;
}
tc_ptr = code_gen_ptr;
tb->tc_ptr = tc_ptr;
@@ -780,8 +787,8 @@ static void tb_gen_code(CPUState *env,
phys_page2 = get_phys_addr_code(env, virt_page2);
}
tb_link_phys(tb, phys_pc, phys_page2);
+ return tb;
}
-#endif
/* invalidate all TBs which intersect with the target physical page
starting in range [start;end[. NOTE: start and end must refer to
@@ -836,13 +843,13 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
if (current_tb_not_found) {
current_tb_not_found = 0;
current_tb = NULL;
- if (env->mem_write_pc) {
+ if (env->mem_io_pc) {
/* now we have a real cpu fault */
- current_tb = tb_find_pc(env->mem_write_pc);
+ current_tb = tb_find_pc(env->mem_io_pc);
}
}
if (current_tb == tb &&
- !(current_tb->cflags & CF_SINGLE_INSN)) {
+ (current_tb->cflags & CF_COUNT_MASK) != 1) {
/* If we are modifying the current TB, we must stop
its execution. We could be more precise by checking
that the modification is after the current PC, but it
@@ -851,7 +858,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
current_tb_modified = 1;
cpu_restore_state(current_tb, env,
- env->mem_write_pc, NULL);
+ env->mem_io_pc, NULL);
#if defined(TARGET_I386)
current_flags = env->hflags;
current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
@@ -883,7 +890,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
if (!p->first_tb) {
invalidate_page_bitmap(p);
if (is_cpu_write_access) {
- tlb_unprotect_code_phys(env, start, env->mem_write_vaddr);
+ tlb_unprotect_code_phys(env, start, env->mem_io_vaddr);
}
}
#endif
@@ -893,8 +900,7 @@ void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t
modifying the memory. It will ensure that it cannot modify
itself */
env->current_tb = NULL;
- tb_gen_code(env, current_pc, current_cs_base, current_flags,
- CF_SINGLE_INSN);
+ tb_gen_code(env, current_pc, current_cs_base, current_flags, 1);
cpu_resume_from_signal(env, NULL);
}
#endif
@@ -909,7 +915,7 @@ static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int le
if (1) {
if (loglevel) {
fprintf(logfile, "modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
- cpu_single_env->mem_write_vaddr, len,
+ cpu_single_env->mem_io_vaddr, len,
cpu_single_env->eip,
cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base);
}
@@ -961,7 +967,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr,
tb = (TranslationBlock *)((long)tb & ~3);
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb == tb &&
- !(current_tb->cflags & CF_SINGLE_INSN)) {
+ (current_tb->cflags & CF_COUNT_MASK) != 1) {
/* If we are modifying the current TB, we must stop
its execution. We could be more precise by checking
that the modification is after the current PC, but it
@@ -990,8 +996,7 @@ static void tb_invalidate_phys_page(target_phys_addr_t addr,
modifying the memory. It will ensure that it cannot modify
itself */
env->current_tb = NULL;
- tb_gen_code(env, current_pc, current_cs_base, current_flags,
- CF_SINGLE_INSN);
+ tb_gen_code(env, current_pc, current_cs_base, current_flags, 1);
cpu_resume_from_signal(env, puc);
}
#endif
@@ -1068,6 +1073,17 @@ TranslationBlock *tb_alloc(target_ulong pc)
return tb;
}
+void tb_free(TranslationBlock *tb)
+{
+ /* In practice this is mostly used for single use temorary TB
+ Ignore the hard cases and just back up if this TB happens to
+ be the last one generated. */
+ if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) {
+ code_gen_ptr = tb->tc_ptr;
+ nb_tbs--;
+ }
+}
+
/* add a new TB and link it to the physical page tables. phys_page2 is
(-1) to indicate that only one page contains the TB. */
void tb_link_phys(TranslationBlock *tb,
@@ -1369,7 +1385,9 @@ void cpu_interrupt(CPUState *env, int mask)
TranslationBlock *tb;
static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED;
#endif
+ int old_mask;
+ old_mask = env->interrupt_request;
/* FIXME: This is probably not threadsafe. A different thread could
be in the mittle of a read-modify-write operation. */
env->interrupt_request |= mask;
@@ -1379,13 +1397,25 @@ void cpu_interrupt(CPUState *env, int mask)
emulation this often isn't actually as bad as it sounds. Often
signals are used primarily to interrupt blocking syscalls. */
#else
- /* if the cpu is currently executing code, we must unlink it and
- all the potentially executing TB */
- tb = env->current_tb;
- if (tb && !testandset(&interrupt_lock)) {
- env->current_tb = NULL;
- tb_reset_jump_recursive(tb);
- resetlock(&interrupt_lock);
+ if (use_icount) {
+ env->icount_decr.u16.high = 0x8000;
+#ifndef CONFIG_USER_ONLY
+ /* CPU_INTERRUPT_EXIT isn't a real interrupt. It just means
+ an async event happened and we need to process it. */
+ if (!can_do_io(env)
+ && (mask & ~(old_mask | CPU_INTERRUPT_EXIT)) != 0) {
+ cpu_abort(env, "Raised interrupt while not in I/O function");
+ }
+#endif
+ } else {
+ tb = env->current_tb;
+ /* if the cpu is currently executing code, we must unlink it and
+ all the potentially executing TB */
+ if (tb && !testandset(&interrupt_lock)) {
+ env->current_tb = NULL;
+ tb_reset_jump_recursive(tb);
+ resetlock(&interrupt_lock);
+ }
}
#endif
}
@@ -2227,7 +2257,7 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr,
/* we remove the notdirty callback only if the code has been
flushed */
if (dirty_flags == 0xff)
- tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr);
+ tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
}
static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr,
@@ -2252,7 +2282,7 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr,
/* we remove the notdirty callback only if the code has been
flushed */
if (dirty_flags == 0xff)
- tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr);
+ tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
}
static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr,
@@ -2277,7 +2307,7 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr,
/* we remove the notdirty callback only if the code has been
flushed */
if (dirty_flags == 0xff)
- tlb_set_dirty(cpu_single_env, cpu_single_env->mem_write_vaddr);
+ tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
}
static CPUReadMemoryFunc *error_mem_read[3] = {
@@ -2299,7 +2329,7 @@ static void check_watchpoint(int offset, int flags)
target_ulong vaddr;
int i;
- vaddr = (env->mem_write_vaddr & TARGET_PAGE_MASK) + offset;
+ vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset;
for (i = 0; i < env->nb_watchpoints; i++) {
if (vaddr == env->watchpoint[i].vaddr
&& (env->watchpoint[i].type & flags)) {
@@ -2967,6 +2997,65 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
return 0;
}
+/* in deterministic execution mode, instructions doing device I/Os
+ must be at the end of the TB */
+void cpu_io_recompile(CPUState *env, void *retaddr)
+{
+ TranslationBlock *tb;
+ uint32_t n, cflags;
+ target_ulong pc, cs_base;
+ uint64_t flags;
+
+ tb = tb_find_pc((unsigned long)retaddr);
+ if (!tb) {
+ cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p",
+ retaddr);
+ }
+ n = env->icount_decr.u16.low + tb->icount;
+ cpu_restore_state(tb, env, (unsigned long)retaddr, NULL);
+ /* Calculate how many instructions had been executed before the fault
+ occured. */
+ n = n - env->icount_decr.u16.low;
+ /* Generate a new TB ending on the I/O insn. */
+ n++;
+ /* On MIPS and SH, delay slot instructions can only be restarted if
+ they were already the first instruction in the TB. If this is not
+ the first instruction in a TB then re-execute the preceeding
+ branch. */
+#if defined(TARGET_MIPS)
+ if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) {
+ env->active_tc.PC -= 4;
+ env->icount_decr.u16.low++;
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ }
+#elif defined(TARGET_SH4)
+ if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
+ && n > 1) {
+ env->pc -= 2;
+ env->icount_decr.u16.low++;
+ env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
+ }
+#endif
+ /* This should never happen. */
+ if (n > CF_COUNT_MASK)
+ cpu_abort(env, "TB too big during recompile");
+
+ cflags = n | CF_LAST_IO;
+ pc = tb->pc;
+ cs_base = tb->cs_base;
+ flags = tb->flags;
+ tb_phys_invalidate(tb, -1);
+ /* FIXME: In theory this could raise an exception. In practice
+ we have already translated the block once so it's probably ok. */
+ tb_gen_code(env, pc, cs_base, flags, cflags);
+ /* TODO: If env->pc != tb->pc (i.e. the failuting instruction was not
+ the first in the TB) then we end up generating a whole new TB and
+ repeating the fault, which is horribly inefficient.
+ Better would be to execute just this insn uncached, or generate a
+ second new TB. */
+ cpu_resume_from_signal(env, NULL);
+}
+
void dump_exec_info(FILE *f,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
{
diff --git a/hw/mips_timer.c b/hw/mips_timer.c
index 5b172ea424..5549e2483a 100644
--- a/hw/mips_timer.c
+++ b/hw/mips_timer.c
@@ -91,7 +91,12 @@ static void mips_timer_cb (void *opaque)
if (env->CP0_Cause & (1 << CP0Ca_DC))
return;
+ /* ??? This callback should occur when the counter is exactly equal to
+ the comparator value. Offset the count by one to avoid immediately
+ retriggering the callback before any virtual time has passed. */
+ env->CP0_Count++;
cpu_mips_timer_update(env);
+ env->CP0_Count--;
if (env->insn_flags & ISA_MIPS32R2)
env->CP0_Cause |= 1 << CP0Ca_TI;
qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
diff --git a/qemu-doc.texi b/qemu-doc.texi
index e8641178db..057b9f6551 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -965,6 +965,17 @@ On M68K this implements the "ColdFire GDB" interface used by libgloss.
Note that this allows guest direct access to the host filesystem,
so should only be used with trusted guest OS.
+
+@item -icount [N|auto]
+Enable virtual instruction counter. The virtual cpu will execute one
+instruction every 2^N ns of virtual time. If @code{auto} is specified
+then the virtual cpu speed will be automatically adjusted to keep virtual
+time within a few seconds of real time.
+
+Note that while this option can give deterministic behavior, it does not
+provide cycle accurate emulation. Modern CPUs contain superscalar out of
+order cores with complex cache heirachies. The number of instructions
+executed often has little or no correlation with actual performance.
@end table
@c man end
diff --git a/softmmu_template.h b/softmmu_template.h
index 934df5286f..98dd378816 100644
--- a/softmmu_template.h
+++ b/softmmu_template.h
@@ -51,12 +51,18 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
int mmu_idx,
void *retaddr);
static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,
- target_ulong addr)
+ target_ulong addr,
+ void *retaddr)
{
DATA_TYPE res;
int index;
index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
+ env->mem_io_pc = (unsigned long)retaddr;
+ if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
+ && !can_do_io(env)) {
+ cpu_io_recompile(env, retaddr);
+ }
#if SHIFT <= 2
res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr);
@@ -95,8 +101,9 @@ DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
/* IO access */
if ((addr & (DATA_SIZE - 1)) != 0)
goto do_unaligned_access;
+ retaddr = GETPC();
addend = env->iotlb[mmu_idx][index];
- res = glue(io_read, SUFFIX)(addend, addr);
+ res = glue(io_read, SUFFIX)(addend, addr, retaddr);
} else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
/* slow unaligned access (it spans two pages or IO) */
do_unaligned_access:
@@ -148,8 +155,9 @@ static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
/* IO access */
if ((addr & (DATA_SIZE - 1)) != 0)
goto do_unaligned_access;
+ retaddr = GETPC();
addend = env->iotlb[mmu_idx][index];
- res = glue(io_read, SUFFIX)(addend, addr);
+ res = glue(io_read, SUFFIX)(addend, addr, retaddr);
} else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
do_unaligned_access:
/* slow unaligned access (it spans two pages) */
@@ -194,9 +202,13 @@ static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,
int index;
index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
+ if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
+ && !can_do_io(env)) {
+ cpu_io_recompile(env, retaddr);
+ }
- env->mem_write_vaddr = addr;
- env->mem_write_pc = (unsigned long)retaddr;
+ env->mem_io_vaddr = addr;
+ env->mem_io_pc = (unsigned long)retaddr;
#if SHIFT <= 2
io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val);
#else
diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h
index e9b2b07571..bdd0ae6124 100644
--- a/target-alpha/cpu.h
+++ b/target-alpha/cpu.h
@@ -415,4 +415,6 @@ void cpu_loop_exit (void);
void pal_init (CPUState *env);
void call_pal (CPUState *env, int palcode);
+#define CPU_PC_FROM_TB(env, tb) env->pc = tb->pc
+
#endif /* !defined (__CPU_ALPHA_H__) */
diff --git a/target-alpha/translate.c b/target-alpha/translate.c
index a3991022e3..4a741573d7 100644
--- a/target-alpha/translate.c
+++ b/target-alpha/translate.c
@@ -43,6 +43,19 @@ struct DisasContext {
uint32_t amask;
};
+TCGv cpu_env;
+
+#include "gen-icount.h"
+
+void alpha_translate_init()
+{
+ static int done_init = 0;
+ if (done_init)
+ return;
+ cpu_env = tcg_global_reg_new(TCG_TYPE_PTR, TCG_AREG0, "env");
+ done_init = 1;
+}
+
static always_inline void gen_op_nop (void)
{
#if defined(GENERATE_NOP)
@@ -1970,6 +1983,8 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
uint16_t *gen_opc_end;
int j, lj = -1;
int ret;
+ int num_insns;
+ int max_insns;
pc_start = tb->pc;
gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
@@ -1981,6 +1996,12 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
ctx.mem_idx = ((env->ps >> 3) & 3);
ctx.pal_mode = env->ipr[IPR_EXC_ADDR] & 1;
#endif
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
for (ret = 0; ret == 0;) {
if (env->nb_breakpoints > 0) {
for(j = 0; j < env->nb_breakpoints; j++) {
@@ -1998,8 +2019,11 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
gen_opc_instr_start[lj++] = 0;
gen_opc_pc[lj] = ctx.pc;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
#if defined ALPHA_DEBUG_DISAS
insn_count++;
if (logfile != NULL) {
@@ -2014,6 +2038,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
fprintf(logfile, "opcode %08x %d\n", insn, insn_count);
}
#endif
+ num_insns++;
ctx.pc += 4;
ret = translate_one(ctxp, insn);
if (ret != 0)
@@ -2022,7 +2047,8 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
* generation
*/
if (((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) ||
- (env->singlestep_enabled)) {
+ (env->singlestep_enabled) ||
+ num_insns >= max_insns) {
break;
}
#if defined (DO_SINGLE_STEP)
@@ -2035,8 +2061,11 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
#if defined (DO_TB_FLUSH)
gen_op_tb_flush();
#endif
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
/* Generate the return instruction */
tcg_gen_exit_tb(0);
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
if (search_pc) {
j = gen_opc_ptr - gen_opc_buf;
@@ -2045,6 +2074,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
gen_opc_instr_start[lj++] = 0;
} else {
tb->size = ctx.pc - pc_start;
+ tb->icount = num_insns;
}
#if defined ALPHA_DEBUG_DISAS
if (loglevel & CPU_LOG_TB_CPU) {
@@ -2079,6 +2109,7 @@ CPUAlphaState * cpu_alpha_init (const char *cpu_model)
if (!env)
return NULL;
cpu_exec_init(env);
+ alpha_translate_init();
tlb_flush(env, 1);
/* XXX: should not be hardcoded */
env->implver = IMPLVER_2106x;
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 5005bf6dd3..6b16e5d04d 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -417,6 +417,8 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
}
#endif
+#define CPU_PC_FROM_TB(env, tb) env->regs[15] = tb->pc
+
#include "cpu-all.h"
#endif
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 91328c8133..701745225a 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -84,6 +84,9 @@ static TCGv cpu_V0, cpu_V1, cpu_M0;
static TCGv cpu_T[2];
static TCGv cpu_F0s, cpu_F1s, cpu_F0d, cpu_F1d;
+#define ICOUNT_TEMP cpu_T[0]
+#include "gen-icount.h"
+
/* initialize TCG globals. */
void arm_translate_init(void)
{
@@ -8539,6 +8542,8 @@ static inline int gen_intermediate_code_internal(CPUState *env,
int j, lj;
target_ulong pc_start;
uint32_t next_page_start;
+ int num_insns;
+ int max_insns;
/* generate intermediate code */
num_temps = 0;
@@ -8575,6 +8580,12 @@ static inline int gen_intermediate_code_internal(CPUState *env,
cpu_M0 = tcg_temp_new(TCG_TYPE_I64);
next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
/* Reset the conditional execution bits immediately. This avoids
complications trying to do it at the end of the block. */
if (env->condexec_bits)
@@ -8625,8 +8636,12 @@ static inline int gen_intermediate_code_internal(CPUState *env,
}
gen_opc_pc[lj] = dc->pc;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
+
if (env->thumb) {
disas_thumb_insn(env, dc);
if (dc->condexec_mask) {
@@ -8659,9 +8674,20 @@ static inline int gen_intermediate_code_internal(CPUState *env,
* Otherwise the subsequent code could get translated several times.
* Also stop translation when a page boundary is reached. This
* ensures prefech aborts occur at the right place. */
+ num_insns ++;
} while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
!env->singlestep_enabled &&
- dc->pc < next_page_start);
+ dc->pc < next_page_start &&
+ num_insns < max_insns);
+
+ if (tb->cflags & CF_LAST_IO) {
+ if (dc->condjmp) {
+ /* FIXME: This can theoretically happen with self-modifying
+ code. */
+ cpu_abort(env, "IO on conditional branch instruction");
+ }
+ gen_io_end();
+ }
/* At this stage dc->condjmp will only be set when the skipped
instruction was a conditional branch or trap, and the PC has
@@ -8726,7 +8752,9 @@ static inline int gen_intermediate_code_internal(CPUState *env,
dc->condjmp = 0;
}
}
+
done_generating:
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
#ifdef DEBUG_DISAS
@@ -8744,6 +8772,7 @@ done_generating:
gen_opc_instr_start[lj++] = 0;
} else {
tb->size = dc->pc - pc_start;
+ tb->icount = num_insns;
}
return 0;
}
diff --git a/target-cris/cpu.h b/target-cris/cpu.h
index 447e780b28..d086221d3f 100644
--- a/target-cris/cpu.h
+++ b/target-cris/cpu.h
@@ -238,5 +238,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
#define SFR_RW_MM_TLB_LO env->pregs[PR_SRS]][5
#define SFR_RW_MM_TLB_HI env->pregs[PR_SRS]][6
+#define CPU_PC_FROM_TB(env, tb) env->pc = tb->pc
+
#include "cpu-all.h"
#endif
diff --git a/target-cris/translate.c b/target-cris/translate.c
index cd0c2e15fa..c3b8adef87 100644
--- a/target-cris/translate.c
+++ b/target-cris/translate.c
@@ -77,6 +77,8 @@ TCGv env_btaken;
TCGv env_btarget;
TCGv env_pc;
+#include "gen-icount.h"
+
/* This is the state at translation time. */
typedef struct DisasContext {
CPUState *env;
@@ -3032,6 +3034,8 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
struct DisasContext *dc = &ctx;
uint32_t next_page_start;
target_ulong npc;
+ int num_insns;
+ int max_insns;
if (!logfile)
logfile = stderr;
@@ -3092,6 +3096,12 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
do
{
check_breakpoint(env, dc);
@@ -3108,6 +3118,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
else
gen_opc_pc[lj] = dc->pc;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
/* Pretty disas. */
@@ -3116,6 +3127,8 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
DIS(fprintf(logfile, "%x ", dc->pc));
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
dc->clear_x = 1;
if (unlikely(loglevel & CPU_LOG_TB_OP))
tcg_gen_debug_insn_start(dc->pc);
@@ -3125,6 +3138,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
if (dc->clear_x)
cris_clear_x_flag(dc);
+ num_insns++;
/* Check for delayed branches here. If we do it before
actually genereating any host code, the simulator will just
loop doing nothing for on this program location. */
@@ -3151,12 +3165,15 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
if (!(tb->pc & 1) && env->singlestep_enabled)
break;
} while (!dc->is_jmp && gen_opc_ptr < gen_opc_end
- && (dc->pc < next_page_start));
+ && (dc->pc < next_page_start)
+ && num_insns < max_insns);
npc = dc->pc;
if (dc->jmp == JMP_DIRECT && !dc->delayed_branch)
npc = dc->jmp_pc;
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
/* Force an update if the per-tb cpu state has changed. */
if (dc->is_jmp == DISAS_NEXT
&& (dc->cpustate_changed || !dc->flagx_known
@@ -3194,6 +3211,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
break;
}
}
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
if (search_pc) {
j = gen_opc_ptr - gen_opc_buf;
@@ -3202,6 +3220,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
gen_opc_instr_start[lj++] = 0;
} else {
tb->size = dc->pc - pc_start;
+ tb->icount = num_insns;
}
#ifdef DEBUG_DISAS
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index d80ffb8718..285b5a7500 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -753,6 +753,8 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
}
#endif
+#define CPU_PC_FROM_TB(env, tb) env->eip = tb->pc - tb->cs_base
+
#include "cpu-all.h"
#include "svm.h"
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 6f9c25672f..ea0933ef5c 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -65,6 +65,8 @@ static TCGv cpu_T[2], cpu_T3;
static TCGv cpu_tmp0, cpu_tmp1_i64, cpu_tmp2_i32, cpu_tmp3_i32, cpu_tmp4, cpu_ptr0, cpu_ptr1;
static TCGv cpu_tmp5, cpu_tmp6;
+#include "gen-icount.h"
+
#ifdef TARGET_X86_64
static int x86_64_hregs;
#endif
@@ -1203,6 +1205,8 @@ static inline void gen_cmps(DisasContext *s, int ot)
static inline void gen_ins(DisasContext *s, int ot)
{
+ if (use_icount)
+ gen_io_start();
gen_string_movl_A0_EDI(s);
/* Note: we must do this dummy write first to be restartable in
case of page fault. */
@@ -1215,10 +1219,14 @@ static inline void gen_ins(DisasContext *s, int ot)
gen_op_st_T0_A0(ot + s->mem_index);
gen_op_movl_T0_Dshift(ot);
gen_op_add_reg_T0(s->aflag, R_EDI);
+ if (use_icount)
+ gen_io_end();
}
static inline void gen_outs(DisasContext *s, int ot)
{
+ if (use_icount)
+ gen_io_start();
gen_string_movl_A0_ESI(s);
gen_op_ld_T0_A0(ot + s->mem_index);
@@ -1230,6 +1238,8 @@ static inline void gen_outs(DisasContext *s, int ot)
gen_op_movl_T0_Dshift(ot);
gen_op_add_reg_T0(s->aflag, R_ESI);
+ if (use_icount)
+ gen_io_end();
}
/* same method as Valgrind : we generate jumps to current or next
@@ -5570,6 +5580,9 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
} else {
gen_ins(s, ot);
+ if (use_icount) {
+ gen_jmp(s, s->pc - s->cs_base);
+ }
}
break;
case 0x6e: /* outsS */
@@ -5586,6 +5599,9 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
} else {
gen_outs(s, ot);
+ if (use_icount) {
+ gen_jmp(s, s->pc - s->cs_base);
+ }
}
break;
@@ -5602,9 +5618,15 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_op_movl_T0_im(val);
gen_check_io(s, ot, pc_start - s->cs_base,
SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes));
+ if (use_icount)
+ gen_io_start();
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_helper_1_1(helper_in_func[ot], cpu_T[1], cpu_tmp2_i32);
gen_op_mov_reg_T1(ot, R_EAX);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
break;
case 0xe6:
case 0xe7:
@@ -5618,10 +5640,16 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
svm_is_rep(prefixes));
gen_op_mov_TN_reg(ot, 1, R_EAX);
+ if (use_icount)
+ gen_io_start();
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]);
tcg_gen_helper_0_2(helper_out_func[ot], cpu_tmp2_i32, cpu_tmp3_i32);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
break;
case 0xec:
case 0xed:
@@ -5633,9 +5661,15 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_op_andl_T0_ffff();
gen_check_io(s, ot, pc_start - s->cs_base,
SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes));
+ if (use_icount)
+ gen_io_start();
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_helper_1_1(helper_in_func[ot], cpu_T[1], cpu_tmp2_i32);
gen_op_mov_reg_T1(ot, R_EAX);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
break;
case 0xee:
case 0xef:
@@ -5649,10 +5683,16 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
svm_is_rep(prefixes));
gen_op_mov_TN_reg(ot, 1, R_EAX);
+ if (use_icount)
+ gen_io_start();
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]);
tcg_gen_helper_0_2(helper_out_func[ot], cpu_tmp2_i32, cpu_tmp3_i32);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
break;
/************************/
@@ -7109,6 +7149,8 @@ static inline int gen_intermediate_code_internal(CPUState *env,
uint64_t flags;
target_ulong pc_start;
target_ulong cs_base;
+ int num_insns;
+ int max_insns;
/* generate intermediate code */
pc_start = tb->pc;
@@ -7179,7 +7221,12 @@ static inline int gen_intermediate_code_internal(CPUState *env,
dc->is_jmp = DISAS_NEXT;
pc_ptr = pc_start;
lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+ gen_icount_start();
for(;;) {
if (env->nb_breakpoints > 0) {
for(j = 0; j < env->nb_breakpoints; j++) {
@@ -7199,8 +7246,13 @@ static inline int gen_intermediate_code_internal(CPUState *env,
gen_opc_pc[lj] = pc_ptr;
gen_opc_cc_op[lj] = dc->cc_op;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
+
pc_ptr = disas_insn(dc, pc_ptr);
+ num_insns++;
/* stop translation if indicated */
if (dc->is_jmp)
break;
@@ -7210,20 +7262,23 @@ static inline int gen_intermediate_code_internal(CPUState *env,
the flag and abort the translation to give the irqs a
change to be happen */
if (dc->tf || dc->singlestep_enabled ||
- (flags & HF_INHIBIT_IRQ_MASK) ||
- (cflags & CF_SINGLE_INSN)) {
+ (flags & HF_INHIBIT_IRQ_MASK)) {
gen_jmp_im(pc_ptr - dc->cs_base);
gen_eob(dc);
break;
}
/* if too long translation, stop generation too */
if (gen_opc_ptr >= gen_opc_end ||
- (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32)) {
+ (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32) ||
+ num_insns >= max_insns) {
gen_jmp_im(pc_ptr - dc->cs_base);
gen_eob(dc);
break;
}
}
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
/* we don't forget to fill the last values */
if (search_pc) {
@@ -7252,8 +7307,10 @@ static inline int gen_intermediate_code_internal(CPUState *env,
}
#endif
- if (!search_pc)
+ if (!search_pc) {
tb->size = pc_ptr - pc_start;
+ tb->icount = num_insns;
+ }
return 0;
}
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h
index cfeafc003e..aaf9e42632 100644
--- a/target-m68k/cpu.h
+++ b/target-m68k/cpu.h
@@ -235,6 +235,8 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
}
#endif
+#define CPU_PC_FROM_TB(env, tb) env->pc = tb->pc
+
#include "cpu-all.h"
#endif
diff --git a/target-m68k/translate.c b/target-m68k/translate.c
index 0d603bdf58..a920d213b0 100644
--- a/target-m68k/translate.c
+++ b/target-m68k/translate.c
@@ -63,6 +63,8 @@ static TCGv NULL_QREG;
/* Used to distinguish stores from bad addressing modes. */
static TCGv store_dummy;
+#include "gen-icount.h"
+
void m68k_tcg_init(void)
{
char *p;
@@ -2919,6 +2921,8 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
target_ulong pc_start;
int pc_offset;
int last_cc_op;
+ int num_insns;
+ int max_insns;
/* generate intermediate code */
pc_start = tb->pc;
@@ -2937,6 +2941,12 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
dc->is_mem = 0;
dc->mactmp = NULL_QREG;
lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
do {
pc_offset = dc->pc - pc_start;
gen_throws_exception = NULL;
@@ -2960,10 +2970,14 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
}
gen_opc_pc[lj] = dc->pc;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
last_cc_op = dc->cc_op;
dc->insn_pc = dc->pc;
disas_m68k_insn(env, dc);
+ num_insns++;
/* Terminate the TB on memory ops if watchpoints are present. */
/* FIXME: This should be replacd by the deterministic execution
@@ -2972,8 +2986,11 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
break;
} while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
!env->singlestep_enabled &&
- (pc_offset) < (TARGET_PAGE_SIZE - 32));
+ (pc_offset) < (TARGET_PAGE_SIZE - 32) &&
+ num_insns < max_insns);
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
if (__builtin_expect(env->singlestep_enabled, 0)) {
/* Make sure the pc is updated, and raise a debug exception. */
if (!dc->is_jmp) {
@@ -2999,6 +3016,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
break;
}
}
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
#ifdef DEBUG_DISAS
@@ -3016,6 +3034,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
gen_opc_instr_start[lj++] = 0;
} else {
tb->size = dc->pc - pc_start;
+ tb->icount = num_insns;
}
//optimize_flags();
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 78851ed932..8e21f6b4a8 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -572,4 +572,10 @@ CPUMIPSState *cpu_mips_init(const char *cpu_model);
uint32_t cpu_mips_get_clock (void);
int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
+#define CPU_PC_FROM_TB(env, tb) do { \
+ env->active_tc.PC = tb->pc; \
+ env->hflags &= ~MIPS_HFLAG_BMASK; \
+ env->hflags |= tb->flags & MIPS_HFLAG_BMASK; \
+ } while (0)
+
#endif /* !defined (__MIPS_CPU_H__) */
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 9c97b9c486..67fbbedf7e 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -428,6 +428,8 @@ static TCGv cpu_env, current_fpu;
/* FPU TNs, global for now. */
static TCGv fpu32_T[3], fpu64_T[3], fpu32h_T[3];
+#include "gen-icount.h"
+
static inline void tcg_gen_helper_0_i(void *func, TCGv arg)
{
TCGv tmp = tcg_const_i32(arg);
@@ -3061,7 +3063,14 @@ static void gen_mfc0 (CPUState *env, DisasContext *ctx, TCGv t0, int reg, int se
case 9:
switch (sel) {
case 0:
+ /* Mark as an IO operation because we read the time. */
+ if (use_icount)
+ gen_io_start();
tcg_gen_helper_1_0(do_mfc0_count, t0);
+ if (use_icount) {
+ gen_io_end();
+ ctx->bstate = BS_STOP;
+ }
rn = "Count";
break;
/* 6,7 are implementation dependent */
@@ -3422,6 +3431,9 @@ static void gen_mtc0 (CPUState *env, DisasContext *ctx, TCGv t0, int reg, int se
if (sel != 0)
check_insn(env, ctx, ISA_MIPS32);
+ if (use_icount)
+ gen_io_start();
+
switch (reg) {
case 0:
switch (sel) {
@@ -4004,6 +4016,11 @@ static void gen_mtc0 (CPUState *env, DisasContext *ctx, TCGv t0, int reg, int se
rn, reg, sel);
}
#endif
+ /* For simplicitly assume that all writes can cause interrupts. */
+ if (use_icount) {
+ gen_io_end();
+ ctx->bstate = BS_STOP;
+ }
return;
die:
@@ -4238,7 +4255,14 @@ static void gen_dmfc0 (CPUState *env, DisasContext *ctx, TCGv t0, int reg, int s
case 9:
switch (sel) {
case 0:
+ /* Mark as an IO operation because we read the time. */
+ if (use_icount)
+ gen_io_start();
tcg_gen_helper_1_0(do_mfc0_count, t0);
+ if (use_icount) {
+ gen_io_end();
+ ctx->bstate = BS_STOP;
+ }
rn = "Count";
break;
/* 6,7 are implementation dependent */
@@ -4591,6 +4615,9 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv t0, int reg, int s
if (sel != 0)
check_insn(env, ctx, ISA_MIPS64);
+ if (use_icount)
+ gen_io_start();
+
switch (reg) {
case 0:
switch (sel) {
@@ -5161,6 +5188,11 @@ static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv t0, int reg, int s
}
#endif
tcg_temp_free(t0);
+ /* For simplicitly assume that all writes can cause interrupts. */
+ if (use_icount) {
+ gen_io_end();
+ ctx->bstate = BS_STOP;
+ }
return;
die:
@@ -7760,6 +7792,7 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
ctx->hflags &= ~MIPS_HFLAG_BMASK;
ctx->bstate = BS_BRANCH;
save_cpu_state(ctx, 0);
+ /* FIXME: Need to clear can_do_io. */
switch (hflags) {
case MIPS_HFLAG_B:
/* unconditional branch */
@@ -7807,6 +7840,8 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
target_ulong pc_start;
uint16_t *gen_opc_end;
int j, lj = -1;
+ int num_insns;
+ int max_insns;
if (search_pc && loglevel)
fprintf (logfile, "search pc %d\n", search_pc);
@@ -7826,6 +7861,11 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
#else
ctx.mem_idx = ctx.hflags & MIPS_HFLAG_KSU;
#endif
+ num_insns = 0;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
#ifdef DEBUG_DISAS
if (loglevel & CPU_LOG_TB_CPU) {
fprintf(logfile, "------------------------------------------------\n");
@@ -7838,6 +7878,7 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
fprintf(logfile, "\ntb %p idx %d hflags %04x\n",
tb, ctx.mem_idx, ctx.hflags);
#endif
+ gen_icount_start();
while (ctx.bstate == BS_NONE) {
if (env->nb_breakpoints > 0) {
for(j = 0; j < env->nb_breakpoints; j++) {
@@ -7863,10 +7904,14 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
gen_opc_pc[lj] = ctx.pc;
gen_opc_hflags[lj] = ctx.hflags & MIPS_HFLAG_BMASK;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
ctx.opcode = ldl_code(ctx.pc);
decode_opc(env, &ctx);
ctx.pc += 4;
+ num_insns++;
if (env->singlestep_enabled)
break;
@@ -7880,10 +7925,14 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
if (gen_opc_ptr >= gen_opc_end)
break;
+ if (num_insns >= max_insns)
+ break;
#if defined (MIPS_SINGLE_STEP)
break;
#endif
}
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
if (env->singlestep_enabled) {
save_cpu_state(&ctx, ctx.bstate == BS_NONE);
tcg_gen_helper_0_i(do_raise_exception, EXCP_DEBUG);
@@ -7907,6 +7956,7 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
}
}
done_generating:
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
if (search_pc) {
j = gen_opc_ptr - gen_opc_buf;
@@ -7915,6 +7965,7 @@ done_generating:
gen_opc_instr_start[lj++] = 0;
} else {
tb->size = ctx.pc - pc_start;
+ tb->icount = num_insns;
}
#ifdef DEBUG_DISAS
#if defined MIPS_DEBUG_DISAS
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index d08bbba995..d9dcf58ef3 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -697,6 +697,7 @@ struct mmu_ctx_t {
/*****************************************************************************/
CPUPPCState *cpu_ppc_init (const char *cpu_model);
+void ppc_translate_init(void);
int cpu_ppc_exec (CPUPPCState *s);
void cpu_ppc_close (CPUPPCState *s);
/* you can call this signal handler from your SIGBUS and SIGSEGV
@@ -833,6 +834,8 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
}
#endif
+#define CPU_PC_FROM_TB(env, tb) env->nip = tb->pc
+
#include "cpu-all.h"
/*****************************************************************************/
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 2a52dc69a2..c6ac6cb0e0 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -2977,6 +2977,7 @@ CPUPPCState *cpu_ppc_init (const char *cpu_model)
if (!env)
return NULL;
cpu_exec_init(env);
+ ppc_translate_init();
env->cpu_model_str = cpu_model;
cpu_ppc_register_internal(env, def);
cpu_ppc_reset(env);
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 28cb6abdda..b98ba35bd3 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -43,6 +43,19 @@
/*****************************************************************************/
/* Code translation helpers */
+static TCGv cpu_env;
+
+#include "gen-icount.h"
+
+void ppc_translate_init(void)
+{
+ int done_init = 0;
+ if (done_init)
+ return;
+ cpu_env = tcg_global_reg_new(TCG_TYPE_PTR, TCG_AREG0, "env");
+ done_init = 1;
+}
+
#if defined(OPTIMIZE_FPRF_UPDATE)
static uint16_t *gen_fprf_buf[OPC_BUF_SIZE];
static uint16_t **gen_fprf_ptr;
@@ -6168,6 +6181,8 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
uint16_t *gen_opc_end;
int supervisor, little_endian;
int j, lj = -1;
+ int num_insns;
+ int max_insns;
pc_start = tb->pc;
gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
@@ -6211,6 +6226,12 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
/* Single step trace mode */
msr_se = 1;
#endif
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
/* Set env in case of segfault during code fetch */
while (ctx.exception == POWERPC_EXCP_NONE && gen_opc_ptr < gen_opc_end) {
if (unlikely(env->nb_breakpoints > 0)) {
@@ -6230,6 +6251,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
gen_opc_instr_start[lj++] = 0;
gen_opc_pc[lj] = ctx.nip;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
}
#if defined PPC_DEBUG_DISAS
@@ -6239,6 +6261,8 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
ctx.nip, supervisor, (int)msr_ir);
}
#endif
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
if (unlikely(little_endian)) {
ctx.opcode = bswap32(ldl_code(ctx.nip));
} else {
@@ -6253,6 +6277,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
#endif
ctx.nip += 4;
table = env->opcodes;
+ num_insns++;
handler = table[opc1(ctx.opcode)];
if (is_indirect_opcode(handler)) {
table = ind_table(handler);
@@ -6306,7 +6331,8 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
ctx.exception != POWERPC_EXCP_BRANCH)) {
GEN_EXCP(ctxp, POWERPC_EXCP_TRACE, 0);
} else if (unlikely(((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) ||
- (env->singlestep_enabled))) {
+ (env->singlestep_enabled) ||
+ num_insns >= max_insns)) {
/* if we reach a page boundary or are single stepping, stop
* generation
*/
@@ -6316,6 +6342,8 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
break;
#endif
}
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
if (ctx.exception == POWERPC_EXCP_NONE) {
gen_goto_tb(&ctx, 0, ctx.nip);
} else if (ctx.exception != POWERPC_EXCP_BRANCH) {
@@ -6326,6 +6354,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
/* Generate the return instruction */
tcg_gen_exit_tb(0);
}
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
if (unlikely(search_pc)) {
j = gen_opc_ptr - gen_opc_buf;
@@ -6334,6 +6363,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
gen_opc_instr_start[lj++] = 0;
} else {
tb->size = ctx.nip - pc_start;
+ tb->icount = num_insns;
}
#if defined(DEBUG_DISAS)
if (loglevel & CPU_LOG_TB_CPU) {
diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h
index c349bc8862..014ecd3422 100644
--- a/target-sh4/cpu.h
+++ b/target-sh4/cpu.h
@@ -152,6 +152,11 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
}
#endif
+#define CPU_PC_FROM_TB(env, tb) do { \
+ env->pc = tb->pc; \
+ env->flags = tb->flags; \
+ } while (0)
+
#include "cpu-all.h"
/* Memory access type */
diff --git a/target-sh4/translate.c b/target-sh4/translate.c
index 3564a67613..8e3f324f3e 100644
--- a/target-sh4/translate.c
+++ b/target-sh4/translate.c
@@ -56,6 +56,19 @@ enum {
BS_EXCP = 3, /* We reached an exception condition */
};
+static TCGv cpu_env;
+
+#include "gen-icount.h"
+
+void sh4_translate_init()
+{
+ static int done_init = 0;
+ if (done_init)
+ return;
+ cpu_env = tcg_global_reg_new(TCG_TYPE_PTR, TCG_AREG0, "env");
+ done_init = 1;
+}
+
#ifdef CONFIG_USER_ONLY
#define GEN_OP_LD(width, reg) \
@@ -143,6 +156,7 @@ CPUSH4State *cpu_sh4_init(const char *cpu_model)
if (!env)
return NULL;
cpu_exec_init(env);
+ sh4_translate_init();
cpu_sh4_reset(env);
tlb_flush(env, 1);
return env;
@@ -1189,6 +1203,8 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
target_ulong pc_start;
static uint16_t *gen_opc_end;
int i, ii;
+ int num_insns;
+ int max_insns;
pc_start = tb->pc;
gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
@@ -1213,6 +1229,11 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
#endif
ii = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+ gen_icount_start();
while (ctx.bstate == BS_NONE && gen_opc_ptr < gen_opc_end) {
if (env->nb_breakpoints > 0) {
for (i = 0; i < env->nb_breakpoints; i++) {
@@ -1235,22 +1256,30 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
gen_opc_pc[ii] = ctx.pc;
gen_opc_hflags[ii] = ctx.flags;
gen_opc_instr_start[ii] = 1;
+ gen_opc_icount[ii] = num_insns;
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
#if 0
fprintf(stderr, "Loading opcode at address 0x%08x\n", ctx.pc);
fflush(stderr);
#endif
ctx.opcode = lduw_code(ctx.pc);
decode_opc(&ctx);
+ num_insns++;
ctx.pc += 2;
if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0)
break;
if (env->singlestep_enabled)
break;
+ if (num_insns >= max_insns)
+ break;
#ifdef SH4_SINGLE_STEP
break;
#endif
}
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
if (env->singlestep_enabled) {
gen_op_debug();
} else {
@@ -1274,6 +1303,7 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
}
}
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
if (search_pc) {
i = gen_opc_ptr - gen_opc_buf;
@@ -1282,6 +1312,7 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
gen_opc_instr_start[ii++] = 0;
} else {
tb->size = ctx.pc - pc_start;
+ tb->icount = num_insns;
}
#ifdef DEBUG_DISAS
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index b56dc91ff5..03bc2dfd09 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -437,6 +437,11 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
}
#endif
+#define CPU_PC_FROM_TB(env, tb) do { \
+ env->pc = tb->pc; \
+ env->npc = tb->cs_base; \
+ } while(0)
+
#include "cpu-all.h"
#endif
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index 98c6291502..49f998e90b 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -48,6 +48,8 @@ static TCGv cpu_xcc;
/* local register indexes (only used inside old micro ops) */
static TCGv cpu_tmp0, cpu_tmp32, cpu_tmp64;
+#include "gen-icount.h"
+
typedef struct DisasContext {
target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */
target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */
@@ -4719,6 +4721,8 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb,
uint16_t *gen_opc_end;
DisasContext dc1, *dc = &dc1;
int j, lj = -1;
+ int num_insns;
+ int max_insns;
memset(dc, 0, sizeof(DisasContext));
dc->tb = tb;
@@ -4747,6 +4751,11 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb,
cpu_val = tcg_temp_local_new(TCG_TYPE_TL);
cpu_addr = tcg_temp_local_new(TCG_TYPE_TL);
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+ gen_icount_start();
do {
if (env->nb_breakpoints > 0) {
for(j = 0; j < env->nb_breakpoints; j++) {
@@ -4771,10 +4780,14 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb,
gen_opc_pc[lj] = dc->pc;
gen_opc_npc[lj] = dc->npc;
gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
}
}
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
last_pc = dc->pc;
disas_sparc_insn(dc);
+ num_insns++;
if (dc->is_br)
break;
@@ -4793,7 +4806,8 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb,
break;
}
} while ((gen_opc_ptr < gen_opc_end) &&
- (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32));
+ (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32) &&
+ num_insns < max_insns);
exit_gen_loop:
tcg_temp_free(cpu_addr);
@@ -4802,6 +4816,8 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb,
tcg_temp_free(cpu_tmp64);
tcg_temp_free(cpu_tmp32);
tcg_temp_free(cpu_tmp0);
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
if (!dc->is_br) {
if (dc->pc != DYNAMIC_PC &&
(dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) {
@@ -4814,6 +4830,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb,
tcg_gen_exit_tb(0);
}
}
+ gen_icount_end(tb, num_insns);
*gen_opc_ptr = INDEX_op_end;
if (spc) {
j = gen_opc_ptr - gen_opc_buf;
@@ -4829,6 +4846,7 @@ static inline int gen_intermediate_code_internal(TranslationBlock * tb,
gen_opc_jump_pc[1] = dc->jump_pc[1];
} else {
tb->size = last_pc + 4 - pc_start;
+ tb->icount = num_insns;
}
#ifdef DEBUG_DISAS
if (loglevel & CPU_LOG_TB_IN_ASM) {
diff --git a/translate-all.c b/translate-all.c
index 8b8b6a2813..263649578b 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -38,6 +38,7 @@ uint16_t gen_opc_buf[OPC_BUF_SIZE];
TCGArg gen_opparam_buf[OPPARAM_BUF_SIZE];
target_ulong gen_opc_pc[OPC_BUF_SIZE];
+uint16_t gen_opc_icount[OPC_BUF_SIZE];
uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
#if defined(TARGET_I386)
uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
@@ -158,6 +159,13 @@ int cpu_restore_state(TranslationBlock *tb,
if (gen_intermediate_code_pc(env, tb) < 0)
return -1;
+ if (use_icount) {
+ /* Reset the cycle counter to the start of the block. */
+ env->icount_decr.u16.low += tb->icount;
+ /* Clear the IO flag. */
+ env->can_do_io = 0;
+ }
+
/* find opc index corresponding to search_pc */
tc_ptr = (unsigned long)tb->tc_ptr;
if (searched_pc < tc_ptr)
@@ -177,6 +185,7 @@ int cpu_restore_state(TranslationBlock *tb,
/* now find start of instruction before */
while (gen_opc_instr_start[j] == 0)
j--;
+ env->icount_decr.u16.low -= gen_opc_icount[j];
gen_pc_load(env, tb, searched_pc, j, puc);
diff --git a/vl.c b/vl.c
index 908aa39223..72ce67b02d 100644
--- a/vl.c
+++ b/vl.c
@@ -239,6 +239,14 @@ struct drive_opt {
static CPUState *cur_cpu;
static CPUState *next_cpu;
static int event_pending = 1;
+/* Conversion factor from emulated instrctions to virtual clock ticks. */
+static int icount_time_shift;
+/* Arbitrarily pick 1MIPS as the minimum alowable speed. */
+#define MAX_ICOUNT_SHIFT 10
+/* Compensate for varying guest execution speed. */
+static int64_t qemu_icount_bias;
+QEMUTimer *icount_rt_timer;
+QEMUTimer *icount_vm_timer;
#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
@@ -733,9 +741,22 @@ static int64_t get_clock(void)
return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
}
}
-
#endif
+/* Return the virtual CPU time, based on the instruction counter. */
+static int64_t cpu_get_icount(void)
+{
+ int64_t icount;
+ CPUState *env = cpu_single_env;;
+ icount = qemu_icount;
+ if (env) {
+ if (!can_do_io(env))
+ fprintf(stderr, "Bad clock read\n");
+ icount -= (env->icount_decr.u16.low + env->icount_extra);
+ }
+ return qemu_icount_bias + (icount << icount_time_shift);
+}
+
/***********************************************************/
/* guest cycle counter */
@@ -747,6 +768,9 @@ static int cpu_ticks_enabled;
/* return the host CPU cycle counter and handle stop/restart */
int64_t cpu_get_ticks(void)
{
+ if (use_icount) {
+ return cpu_get_icount();
+ }
if (!cpu_ticks_enabled) {
return cpu_ticks_offset;
} else {
@@ -878,6 +902,71 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t);
#endif /* _WIN32 */
+/* Correlation between real and virtual time is always going to be
+ farly approximate, so ignore small variation.
+ When the guest is idle real and virtual time will be aligned in
+ the IO wait loop. */
+#define ICOUNT_WOBBLE (QEMU_TIMER_BASE / 10)
+
+static void icount_adjust(void)
+{
+ int64_t cur_time;
+ int64_t cur_icount;
+ int64_t delta;
+ static int64_t last_delta;
+ /* If the VM is not running, then do nothing. */
+ if (!vm_running)
+ return;
+
+ cur_time = cpu_get_clock();
+ cur_icount = qemu_get_clock(vm_clock);
+ delta = cur_icount - cur_time;
+ /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
+ if (delta > 0
+ && last_delta + ICOUNT_WOBBLE < delta * 2
+ && icount_time_shift > 0) {
+ /* The guest is getting too far ahead. Slow time down. */
+ icount_time_shift--;
+ }
+ if (delta < 0
+ && last_delta - ICOUNT_WOBBLE > delta * 2
+ && icount_time_shift < MAX_ICOUNT_SHIFT) {
+ /* The guest is getting too far behind. Speed time up. */
+ icount_time_shift++;
+ }
+ last_delta = delta;
+ qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift);
+}
+
+static void icount_adjust_rt(void * opaque)
+{
+ qemu_mod_timer(icount_rt_timer,
+ qemu_get_clock(rt_clock) + 1000);
+ icount_adjust();
+}
+
+static void icount_adjust_vm(void * opaque)
+{
+ qemu_mod_timer(icount_vm_timer,
+ qemu_get_clock(vm_clock) + QEMU_TIMER_BASE / 10);
+ icount_adjust();
+}
+
+static void init_icount_adjust(void)
+{
+ /* Have both realtime and virtual time triggers for speed adjustment.
+ The realtime trigger catches emulated time passing too slowly,
+ the virtual time trigger catches emulated time passing too fast.
+ Realtime triggers occur even when idle, so use them less frequently
+ than VM triggers. */
+ icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL);
+ qemu_mod_timer(icount_rt_timer,
+ qemu_get_clock(rt_clock) + 1000);
+ icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL);
+ qemu_mod_timer(icount_vm_timer,
+ qemu_get_clock(vm_clock) + QEMU_TIMER_BASE / 10);
+}
+
static struct qemu_alarm_timer alarm_timers[] = {
#ifndef _WIN32
#ifdef __linux__
@@ -914,6 +1003,7 @@ static void configure_alarms(char const *opt)
int count = (sizeof(alarm_timers) / sizeof(*alarm_timers)) - 1;
char *arg;
char *name;
+ struct qemu_alarm_timer tmp;
if (!strcmp(opt, "?")) {
show_available_alarms();
@@ -925,8 +1015,6 @@ static void configure_alarms(char const *opt)
/* Reorder the array */
name = strtok(arg, ",");
while (name) {
- struct qemu_alarm_timer tmp;
-
for (i = 0; i < count && alarm_timers[i].name; i++) {
if (!strcmp(alarm_timers[i].name, name))
break;
@@ -954,7 +1042,7 @@ next:
free(arg);
if (cur) {
- /* Disable remaining timers */
+ /* Disable remaining timers */
for (i = cur; i < count; i++)
alarm_timers[i].name = NULL;
} else {
@@ -1039,9 +1127,15 @@ void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
*pt = ts;
/* Rearm if necessary */
- if ((alarm_timer->flags & ALARM_FLAG_EXPIRED) == 0 &&
- pt == &active_timers[ts->clock->type])
- qemu_rearm_alarm_timer(alarm_timer);
+ if (pt == &active_timers[ts->clock->type]) {
+ if ((alarm_timer->flags & ALARM_FLAG_EXPIRED) == 0) {
+ qemu_rearm_alarm_timer(alarm_timer);
+ }
+ /* Interrupt execution to force deadline recalculation. */
+ if (use_icount && cpu_single_env) {
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+ }
+ }
}
int qemu_timer_pending(QEMUTimer *ts)
@@ -1085,7 +1179,11 @@ int64_t qemu_get_clock(QEMUClock *clock)
return get_clock() / 1000000;
default:
case QEMU_TIMER_VIRTUAL:
- return cpu_get_clock();
+ if (use_icount) {
+ return cpu_get_icount();
+ } else {
+ return cpu_get_clock();
+ }
}
}
@@ -1184,8 +1282,9 @@ static void host_alarm_handler(int host_signum)
}
#endif
if (alarm_has_dynticks(alarm_timer) ||
- qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
- qemu_get_clock(vm_clock)) ||
+ (!use_icount &&
+ qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
+ qemu_get_clock(vm_clock))) ||
qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
qemu_get_clock(rt_clock))) {
#ifdef _WIN32
@@ -1209,28 +1308,45 @@ static void host_alarm_handler(int host_signum)
}
}
-static uint64_t qemu_next_deadline(void)
+static int64_t qemu_next_deadline(void)
{
- int64_t nearest_delta_us = INT64_MAX;
- int64_t vmdelta_us;
-
- if (active_timers[QEMU_TIMER_REALTIME])
- nearest_delta_us = (active_timers[QEMU_TIMER_REALTIME]->expire_time -
- qemu_get_clock(rt_clock))*1000;
+ int64_t delta;
if (active_timers[QEMU_TIMER_VIRTUAL]) {
- /* round up */
- vmdelta_us = (active_timers[QEMU_TIMER_VIRTUAL]->expire_time -
- qemu_get_clock(vm_clock)+999)/1000;
- if (vmdelta_us < nearest_delta_us)
- nearest_delta_us = vmdelta_us;
+ delta = active_timers[QEMU_TIMER_VIRTUAL]->expire_time -
+ qemu_get_clock(vm_clock);
+ } else {
+ /* To avoid problems with overflow limit this to 2^32. */
+ delta = INT32_MAX;
}
- /* Avoid arming the timer to negative, zero, or too low values */
- if (nearest_delta_us <= MIN_TIMER_REARM_US)
- nearest_delta_us = MIN_TIMER_REARM_US;
+ if (delta < 0)
+ delta = 0;
- return nearest_delta_us;
+ return delta;
+}
+
+static uint64_t qemu_next_deadline_dyntick(void)
+{
+ int64_t delta;
+ int64_t rtdelta;
+
+ if (use_icount)
+ delta = INT32_MAX;
+ else
+ delta = (qemu_next_deadline() + 999) / 1000;
+
+ if (active_timers[QEMU_TIMER_REALTIME]) {
+ rtdelta = (active_timers[QEMU_TIMER_REALTIME]->expire_time -
+ qemu_get_clock(rt_clock))*1000;
+ if (rtdelta < delta)
+ delta = rtdelta;
+ }
+
+ if (delta < MIN_TIMER_REARM_US)
+ delta = MIN_TIMER_REARM_US;
+
+ return delta;
}
#ifndef _WIN32
@@ -1386,7 +1502,7 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
!active_timers[QEMU_TIMER_VIRTUAL])
return;
- nearest_delta_us = qemu_next_deadline();
+ nearest_delta_us = qemu_next_deadline_dyntick();
/* check whether a timer is already running */
if (timer_gettime(host_timer, &timeout)) {
@@ -1513,7 +1629,7 @@ static void win32_rearm_timer(struct qemu_alarm_timer *t)
!active_timers[QEMU_TIMER_VIRTUAL])
return;
- nearest_delta_us = qemu_next_deadline();
+ nearest_delta_us = qemu_next_deadline_dyntick();
nearest_delta_us /= 1000;
timeKillEvent(data->timerId);
@@ -7068,10 +7184,33 @@ static int main_loop(void)
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
+ if (use_icount) {
+ int64_t count;
+ int decr;
+ qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
+ env->icount_decr.u16.low = 0;
+ env->icount_extra = 0;
+ count = qemu_next_deadline();
+ count = (count + (1 << icount_time_shift) - 1)
+ >> icount_time_shift;
+ qemu_icount += count;
+ decr = (count > 0xffff) ? 0xffff : count;
+ count -= decr;
+ env->icount_decr.u16.low = decr;
+ env->icount_extra = count;
+ }
ret = cpu_exec(env);
#ifdef CONFIG_PROFILER
qemu_time += profile_getclock() - ti;
#endif
+ if (use_icount) {
+ /* Fold pending instructions back into the
+ instruction counter, and clear the interrupt flag. */
+ qemu_icount -= (env->icount_decr.u16.low
+ + env->icount_extra);
+ env->icount_decr.u32 = 0;
+ env->icount_extra = 0;
+ }
next_cpu = env->next_cpu ?: first_cpu;
if (event_pending && likely(ret != EXCP_DEBUG)) {
ret = EXCP_INTERRUPT;
@@ -7115,10 +7254,46 @@ static int main_loop(void)
}
/* If all cpus are halted then wait until the next IRQ */
/* XXX: use timeout computed from timers */
- if (ret == EXCP_HALTED)
- timeout = 10;
- else
+ if (ret == EXCP_HALTED) {
+ if (use_icount) {
+ int64_t add;
+ int64_t delta;
+ /* Advance virtual time to the next event. */
+ if (use_icount == 1) {
+ /* When not using an adaptive execution frequency
+ we tend to get badly out of sync with real time,
+ so just delay for a resonable amount of time. */
+ delta = 0;
+ } else {
+ delta = cpu_get_icount() - cpu_get_clock();
+ }
+ if (delta > 0) {
+ /* If virtual time is ahead of real time then just
+ wait for IO. */
+ timeout = (delta / 1000000) + 1;
+ } else {
+ /* Wait for either IO to occur or the next
+ timer event. */
+ add = qemu_next_deadline();
+ /* We advance the timer before checking for IO.
+ Limit the amount we advance so that early IO
+ activity won't get the guest too far ahead. */
+ if (add > 10000000)
+ add = 10000000;
+ delta += add;
+ add = (add + (1 << icount_time_shift) - 1)
+ >> icount_time_shift;
+ qemu_icount += add;
+ timeout = delta / 1000000;
+ if (timeout < 0)
+ timeout = 0;
+ }
+ } else {
+ timeout = 10;
+ }
+ } else {
timeout = 0;
+ }
} else {
timeout = 10;
}
@@ -7270,6 +7445,8 @@ static void help(int exitcode)
"-clock force the use of the given methods for timer alarm.\n"
" To see what timers are available use -clock ?\n"
"-startdate select initial date of the clock\n"
+ "-icount [N|auto]\n"
+ " Enable virtual instruction counter with 2^N clock ticks per instructon\n"
"\n"
"During emulation, the following keys are useful:\n"
"ctrl-alt-f toggle full screen\n"
@@ -7374,6 +7551,7 @@ enum {
QEMU_OPTION_clock,
QEMU_OPTION_startdate,
QEMU_OPTION_tb_size,
+ QEMU_OPTION_icount,
};
typedef struct QEMUOption {
@@ -7486,6 +7664,7 @@ const QEMUOption qemu_options[] = {
{ "clock", HAS_ARG, QEMU_OPTION_clock },
{ "startdate", HAS_ARG, QEMU_OPTION_startdate },
{ "tb-size", HAS_ARG, QEMU_OPTION_tb_size },
+ { "icount", HAS_ARG, QEMU_OPTION_icount },
{ NULL },
};
@@ -8310,6 +8489,14 @@ int main(int argc, char **argv)
if (tb_size < 0)
tb_size = 0;
break;
+ case QEMU_OPTION_icount:
+ use_icount = 1;
+ if (strcmp(optarg, "auto") == 0) {
+ icount_time_shift = -1;
+ } else {
+ icount_time_shift = strtol(optarg, NULL, 0);
+ }
+ break;
}
}
}
@@ -8395,6 +8582,13 @@ int main(int argc, char **argv)
init_timers();
init_timer_alarm();
qemu_aio_init();
+ if (use_icount && icount_time_shift < 0) {
+ use_icount = 2;
+ /* 125MIPS seems a reasonable initial guess at the guest speed.
+ It will be corrected fairly quickly anyway. */
+ icount_time_shift = 3;
+ init_icount_adjust();
+ }
#ifdef _WIN32
socket_init();