Commit 2e70f6ef authored by Paul Brook's avatar Paul Brook
Browse files

Add instruction counter.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4799 c046a42c-6fe2-441c-8c8c-71466251a162
parent f6e5889e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 */
+28 −6
Original line number Diff line number Diff line
@@ -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];                          \
+63 −30
Original line number Diff line number Diff line
@@ -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;

@@ -125,29 +150,7 @@ static TranslationBlock *tb_find_slow(target_ulong pc,
    }
 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);
    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,8 +608,9 @@ 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
@@ -614,6 +619,34 @@ int cpu_exec(CPUState *env1)
#endif
                    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)
+27 −6
Original line number Diff line number Diff line
@@ -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
+118 −29
Original line number Diff line number Diff line
@@ -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,14 +1397,26 @@ 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 (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 */
    tb = env->current_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, ...))
{
Loading