#include "exec-all.h"
#include "qemu-common.h"
#include "tcg.h"
+#include "hw/hw.h"
+#include "osdep.h"
#if defined(CONFIG_USER_ONLY)
#include <qemu.h>
#endif
#define TARGET_PHYS_ADDR_SPACE_BITS 32
#endif
-TranslationBlock *tbs;
+static TranslationBlock *tbs;
int code_gen_max_blocks;
TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
-int nb_tbs;
+static int nb_tbs;
/* any access to the tbs or the page table must use this lock */
spinlock_t tb_lock = SPIN_LOCK_UNLOCKED;
-uint8_t code_gen_prologue[1024] __attribute__((aligned (32)));
-uint8_t *code_gen_buffer;
-unsigned long code_gen_buffer_size;
+#if defined(__arm__) || defined(__sparc_v9__)
+/* The prologue must be reachable with a direct jump. ARM and Sparc64
+ have limited branch ranges (possibly also PPC) so place it in a
+ section close to code segment. */
+#define code_gen_section \
+ __attribute__((__section__(".gen_code"))) \
+ __attribute__((aligned (32)))
+#else
+#define code_gen_section \
+ __attribute__((aligned (32)))
+#endif
+
+uint8_t code_gen_prologue[1024] code_gen_section;
+static uint8_t *code_gen_buffer;
+static unsigned long code_gen_buffer_size;
/* threshold to flush the translated code buffer */
-unsigned long code_gen_buffer_max_size;
+static unsigned long code_gen_buffer_max_size;
uint8_t *code_gen_ptr;
#if !defined(CONFIG_USER_ONLY)
int phys_ram_fd;
uint8_t *phys_ram_base;
uint8_t *phys_ram_dirty;
+static int in_migration;
static ram_addr_t phys_ram_alloc_offset = 0;
#endif
/* current CPU in the current thread. It is only valid inside
cpu_exec() */
CPUState *cpu_single_env;
+/* 0 = Do not count executed instructions.
+ 1 = Precise 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 */
/* XXX: for system emulation, it could just be an array */
static PageDesc *l1_map[L1_SIZE];
-PhysPageDesc **l1_phys_map;
+static PhysPageDesc **l1_phys_map;
#if !defined(CONFIG_USER_ONLY)
static void io_mem_init(void);
#endif
/* log support */
-char *logfilename = "/tmp/qemu.log";
+static const char *logfilename = "/tmp/qemu.log";
FILE *logfile;
int loglevel;
static int log_append = 0;
#endif
}
-static inline PageDesc *page_find_alloc(target_ulong index)
+static inline PageDesc **page_l1_map(target_ulong index)
{
- PageDesc **lp, *p;
-
#if TARGET_LONG_BITS > 32
/* Host memory outside guest VM. For 32-bit targets we have already
excluded high addresses. */
- if (index > ((target_ulong)L2_SIZE * L1_SIZE * TARGET_PAGE_SIZE))
+ if (index > ((target_ulong)L2_SIZE * L1_SIZE))
return NULL;
#endif
- lp = &l1_map[index >> L2_BITS];
+ return &l1_map[index >> L2_BITS];
+}
+
+static inline PageDesc *page_find_alloc(target_ulong index)
+{
+ PageDesc **lp, *p;
+ lp = page_l1_map(index);
+ if (!lp)
+ return NULL;
+
p = *lp;
if (!p) {
/* allocate if not found */
static inline PageDesc *page_find(target_ulong index)
{
- PageDesc *p;
+ PageDesc **lp, *p;
+ lp = page_l1_map(index);
+ if (!lp)
+ return NULL;
- p = l1_map[index >> L2_BITS];
+ p = *lp;
if (!p)
return 0;
return p + (index & (L2_SIZE - 1));
static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE];
#endif
-void code_gen_alloc(unsigned long tb_size)
+static void code_gen_alloc(unsigned long tb_size)
{
#ifdef USE_STATIC_CODE_GEN_BUFFER
code_gen_buffer = static_code_gen_buffer;
code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
#else
/* XXX: needs ajustments */
- code_gen_buffer_size = (int)(phys_ram_size / 4);
+ code_gen_buffer_size = (unsigned long)(phys_ram_size / 4);
#endif
}
if (code_gen_buffer_size < MIN_CODE_GEN_BUFFER_SIZE)
#if defined(__linux__)
{
int flags;
+ void *start = NULL;
+
flags = MAP_PRIVATE | MAP_ANONYMOUS;
#if defined(__x86_64__)
flags |= MAP_32BIT;
/* Cannot map more than that */
if (code_gen_buffer_size > (800 * 1024 * 1024))
code_gen_buffer_size = (800 * 1024 * 1024);
+#elif defined(__sparc_v9__)
+ // Map the buffer below 2G, so we can use direct calls and branches
+ flags |= MAP_FIXED;
+ start = (void *) 0x60000000UL;
+ if (code_gen_buffer_size > (512 * 1024 * 1024))
+ code_gen_buffer_size = (512 * 1024 * 1024);
+#endif
+ code_gen_buffer = mmap(start, code_gen_buffer_size,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ flags, -1, 0);
+ if (code_gen_buffer == MAP_FAILED) {
+ fprintf(stderr, "Could not allocate dynamic translator buffer\n");
+ exit(1);
+ }
+ }
+#elif defined(__FreeBSD__)
+ {
+ int flags;
+ void *addr = NULL;
+ flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#if defined(__x86_64__)
+ /* FreeBSD doesn't have MAP_32BIT, use MAP_FIXED and assume
+ * 0x40000000 is free */
+ flags |= MAP_FIXED;
+ addr = (void *)0x40000000;
+ /* Cannot map more than that */
+ if (code_gen_buffer_size > (800 * 1024 * 1024))
+ code_gen_buffer_size = (800 * 1024 * 1024);
#endif
- code_gen_buffer = mmap(NULL, code_gen_buffer_size,
+ code_gen_buffer = mmap(addr, code_gen_buffer_size,
PROT_WRITE | PROT_READ | PROT_EXEC,
flags, -1, 0);
if (code_gen_buffer == MAP_FAILED) {
#endif
}
+#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
+
+#define CPU_COMMON_SAVE_VERSION 1
+
+static void cpu_common_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = opaque;
+
+ qemu_put_be32s(f, &env->halted);
+ qemu_put_be32s(f, &env->interrupt_request);
+}
+
+static int cpu_common_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+
+ if (version_id != CPU_COMMON_SAVE_VERSION)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &env->halted);
+ qemu_get_be32s(f, &env->interrupt_request);
+ tlb_flush(env, 1);
+
+ return 0;
+}
+#endif
+
void cpu_exec_init(CPUState *env)
{
CPUState **penv;
env->cpu_index = cpu_index;
env->nb_watchpoints = 0;
*penv = env;
+#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
+ register_savevm("cpu_common", cpu_index, CPU_COMMON_SAVE_VERSION,
+ cpu_common_save, cpu_common_load, env);
+ register_savevm("cpu", cpu_index, CPU_SAVE_VERSION,
+ cpu_save, cpu_load, env);
+#endif
}
static inline void invalidate_page_bitmap(PageDesc *p)
}
}
-void tb_jmp_check(TranslationBlock *tb)
+static void tb_jmp_check(TranslationBlock *tb)
{
TranslationBlock *tb1;
unsigned int n1;
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;
int n, tb_start, tb_end;
TranslationBlock *tb;
- p->code_bitmap = qemu_malloc(TARGET_PAGE_SIZE / 8);
+ p->code_bitmap = qemu_mallocz(TARGET_PAGE_SIZE / 8);
if (!p->code_bitmap)
return;
- memset(p->code_bitmap, 0, TARGET_PAGE_SIZE / 8);
tb = p->first_tb;
while (tb != NULL) {
}
}
-#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;
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;
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
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
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));
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
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
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);
}
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
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
return tb;
}
+void tb_free(TranslationBlock *tb)
+{
+ /* In practice this is mostly used for single use temporary 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,
#if !defined(CONFIG_SOFTMMU)
/* must avoid mmap() usage of glibc by setting a buffer "by hand" */
{
- static uint8_t logfile_buf[4096];
+ static char logfile_buf[4096];
setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
}
#else
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. */
+ be in the middle of a read-modify-write operation. */
env->interrupt_request |= mask;
#if defined(USE_NPTL)
/* FIXME: TB unchaining isn't SMP safe. For now just ignore the
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 = 0xffff;
+#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
}
env->interrupt_request &= ~mask;
}
-CPULogItem cpu_log_items[] = {
+const CPULogItem cpu_log_items[] = {
{ CPU_LOG_TB_OUT_ASM, "out_asm",
"show generated host assembly code for each compiled TB" },
{ CPU_LOG_TB_IN_ASM, "in_asm",
/* takes a comma separated list of log masks. Return 0 if error. */
int cpu_str_to_log_mask(const char *str)
{
- CPULogItem *item;
+ const CPULogItem *item;
int mask;
const char *p, *p1;
}
}
+int cpu_physical_memory_set_dirty_tracking(int enable)
+{
+ in_migration = enable;
+ return 0;
+}
+
+int cpu_physical_memory_get_dirty_tracking(void)
+{
+ return in_migration;
+}
+
static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
{
ram_addr_t ram_addr;
target_ulong end;
target_ulong addr;
+ if (start + len < start)
+ /* we've wrapped around */
+ return -1;
+
end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */
start = start & TARGET_PAGE_MASK;
- if( end < start )
- /* we've wrapped around */
- return -1;
for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
p = page_find(addr >> TARGET_PAGE_BITS);
if( !p )
{
ram_addr_t addr;
if ((phys_ram_alloc_offset + size) > phys_ram_size) {
- fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 "\n",
+ fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 ")\n",
(uint64_t)size, (uint64_t)phys_ram_size);
abort();
}
#ifdef DEBUG_UNASSIGNED
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
-#ifdef TARGET_SPARC
- do_unassigned_access(addr, 0, 0, 0);
-#elif TARGET_CRIS
- do_unassigned_access(addr, 0, 0, 0);
+#if defined(TARGET_SPARC) || defined(TARGET_CRIS)
+ do_unassigned_access(addr, 0, 0, 0, 1);
+#endif
+ return 0;
+}
+
+static uint32_t unassigned_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_CRIS)
+ do_unassigned_access(addr, 0, 0, 0, 2);
+#endif
+ return 0;
+}
+
+static uint32_t unassigned_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_CRIS)
+ do_unassigned_access(addr, 0, 0, 0, 4);
#endif
return 0;
}
#ifdef DEBUG_UNASSIGNED
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
#endif
-#ifdef TARGET_SPARC
- do_unassigned_access(addr, 1, 0, 0);
-#elif TARGET_CRIS
- do_unassigned_access(addr, 1, 0, 0);
+#if defined(TARGET_SPARC) || defined(TARGET_CRIS)
+ do_unassigned_access(addr, 1, 0, 0, 1);
+#endif
+}
+
+static void unassigned_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_CRIS)
+ do_unassigned_access(addr, 1, 0, 0, 2);
+#endif
+}
+
+static void unassigned_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_CRIS)
+ do_unassigned_access(addr, 1, 0, 0, 4);
#endif
}
static CPUReadMemoryFunc *unassigned_mem_read[3] = {
unassigned_mem_readb,
- unassigned_mem_readb,
- unassigned_mem_readb,
+ unassigned_mem_readw,
+ unassigned_mem_readl,
};
static CPUWriteMemoryFunc *unassigned_mem_write[3] = {
unassigned_mem_writeb,
- unassigned_mem_writeb,
- unassigned_mem_writeb,
+ unassigned_mem_writew,
+ unassigned_mem_writel,
};
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,
/* 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,
/* 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] = {
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)) {
io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
} else {
- ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
- (addr & ~TARGET_PAGE_MASK);
+ unsigned long addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ ptr = phys_ram_base + addr1;
stl_p(ptr, val);
+
+ if (unlikely(in_migration)) {
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
+ /* set dirty bit */
+ phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
+ (0xff & ~CODE_DIRTY_FLAG);
+ }
+ }
}
}
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
+ occurred. */
+ 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 preceding
+ 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 faulting 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, ...))
{