Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

cpu_jit_buffer.c

/*
 * $Id: cpu_jit_buffer.c,v 1.104 2009-01-22 16:30:17 potyra Exp $
 *
 * Copyright (c) 2005-2009 FAUmachine Team <info@faumachine.org>
 * Copyright (c) 2003 Fabrice Bellard
 *
 * Derived from QEMU sources.
 * Modified for FAUmachine by Volkmar Sieh.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 * USA
 */

#ifdef _WIN32
#include <windows.h>
#else
#include <sys/types.h>
#endif

#include "assert.h"
#include "stdio.h"
#include "string.h"

#include <sys/types.h>
#include <sys/mman.h>

#include <stdlib.h>

#include "exec-all.h"
#include "arch_gen_cpu_x86_state.h"
#include "cpu_jit.h"
#if CONFIG_CPU == 80286
#include "cpu_286_jit_op.h"
#elif CONFIG_CPU == 80386
#include "cpu_386_jit_op.h"
#elif CONFIG_CPU == 80686
#if defined(CONFIG_CPU_LM_SUPPORT)
#include "cpu_x86_64_jit_op.h"
#else
#include "cpu_686_jit_op.h"
#endif
#endif
#include "../arch_gen_cpu_x86_mmu.h"

/*
 * Functions
 */
#if defined(USE_DIRECT_JUMP)

#if defined(__powerpc__)
static inline void
tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
{
        uint32_t val, *ptr;

        /* patch the branch destination */
        ptr = (uint32_t *)jmp_addr;
        val = *ptr;
        val = (val & ~0x03fffffc) | ((addr - jmp_addr) & 0x03fffffc);
        *ptr = val;
        /* flush icache */
        asm volatile ("dcbst 0,%0" : : "r"(ptr) : "memory");
        asm volatile ("sync" : : : "memory");
        asm volatile ("icbi 0,%0" : : "r"(ptr) : "memory");
        asm volatile ("sync" : : : "memory");
        asm volatile ("isync" : : : "memory");
}
#elif defined(__i386__) || defined(__x86_64__)
static inline void
tb_set_jmp_target1(uint8_t *jmp_addr, uint8_t *addr)
{
        /* patch the branch destination */
        *(uint32_t *) jmp_addr = addr - (jmp_addr + 4);
        /* no need to flush icache explicitely */
}
#endif

static inline void
tb_set_jmp_target(TranslationBlock *tb, int n, uint8_t *addr)
{
        unsigned long offset;

        offset = tb->tb_jmp_offset[n];
        tb_set_jmp_target1(tb->tc_ptr + offset, addr);
        offset = tb->tb_jmp_offset[n + 2];
        if (offset != 0xffff)
                tb_set_jmp_target1(tb->tc_ptr + offset, addr);
}

#else

/* set the jump target */
static inline void
tb_set_jmp_target(TranslationBlock *tb, int n, uint8_t *addr)
{
        tb->tb_next[n] = addr;
}

#endif

static inline void
tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb_next)
{
      /* NOTE: this test is only needed for thread safety */
      if (!tb->jmp_next[n]) {
            /* patch the native jump address */
            tb_set_jmp_target(tb, n, tb_next->tc_ptr);

            /* add in TB jmp circular list */
            tb->jmp_next[n] = tb_next->jmp_first;
            tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
      }
}

/*
 * Reset the jump entry 'n' of a TB so that it is not chained to
 * another TB.
 */
static inline void
tb_reset_jump(TranslationBlock *tb, int n)
{
      tb_set_jmp_target(tb, n, tb->tc_ptr + tb->tb_next_offset[n]);
}

static PageDesc *
page_find_alloc(unsigned int index_)
{
    PageDesc **lp, *p;

    lp = &env->l1_map[index_ >> L2_BITS];
    p = *lp;
    if (!p) {
        /* allocate if not found */
        p = malloc(sizeof(PageDesc) * L2_SIZE);
      assert(p != NULL);
        memset(p, 0, sizeof(PageDesc) * L2_SIZE);
        *lp = p;
    }
    return p + (index_ & (L2_SIZE - 1));
}

static PageDesc *
page_find(unsigned int index_)
{
    PageDesc *p;

    p = env->l1_map[index_ >> L2_BITS];
    if (!p)
        return 0;
    return p + (index_ & (L2_SIZE - 1));
}

/*
 * Allocate a new translation block. Flush the translation buffer if
 * too many translation blocks or too much generated code.
 */
static TranslationBlock *
tb_alloc(target_ulong pc)
{
      TranslationBlock *tb;

      if (env->nb_tbs >= CODE_GEN_MAX_BLOCKS
       || CODE_GEN_BUFFER_MAX_SIZE <= env->code_gen_ptr - env->code_gen_buffer) {
            return (TranslationBlock *) 0;
      }
      tb = &env->tbs[env->nb_tbs++];
      tb->pc = pc;
      tb->cflags = 0;
      return tb;
}

static inline void
invalidate_page_bitmap(PageDesc *p)
{
        if (p->code_bitmap) {
                free(p->code_bitmap);
                p->code_bitmap = NULL;
        }
        p->code_write_count = 0;
}

/*
 * Set to NULL all the 'first_tb' fields in all PageDescs.
 */
static void
page_flush_tb(void)
{
        int i, j;
        PageDesc *p;

        for (i = 0; i < L1_SIZE; i++) {
                p = env->l1_map[i];
                if (p) {
                        for (j = 0; j < L2_SIZE; j++) {
                                p->first_tb = NULL;
                                invalidate_page_bitmap(p);
                                p++;
                        }
                }
        }
}

/*
 * Flush all the translation blocks.
 */
/* XXX: tb_flush is currently not thread safe */
static void
tb_flush(void)
{
      env->nb_tbs = 0;

      memset(env->tb_jmp_cache, 0, sizeof(env->tb_jmp_cache));

      memset(env->tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *));
      page_flush_tb();

      env->code_gen_ptr = env->code_gen_buffer;

      /*
       * XXX: flush processor icache at this point if cache flush is
       * expensive
       */
}

static inline unsigned int
tb_hash_func(unsigned long pc)
{
      return pc & (CODE_GEN_HASH_SIZE - 1);
}

static inline unsigned int
tb_phys_hash_func(unsigned long pc)
{
      return pc & (CODE_GEN_PHYS_HASH_SIZE - 1);
}

/*
 * Add the tb in the target page and protect it if necessary.
 */
static inline void
tb_alloc_page(TranslationBlock *tb, unsigned int n, unsigned int page_addr)
{
      PageDesc *p;
      TranslationBlock *last_first_tb;

      tb->page_addr[n] = page_addr;
      p = page_find_alloc(page_addr >> TARGET_PAGE_BITS);
      tb->page_next[n] = p->first_tb;
      last_first_tb = p->first_tb;
      p->first_tb = (TranslationBlock *)((long)tb | n);
      invalidate_page_bitmap(p);

      /*
       * If some code is already present, then the pages are already
       * protected. So we handle the case where only the first TB is
       * allocated in a physical page.
       */
      if (! last_first_tb) {
            NAME_(tlb_protect)(page_addr);        
      }
}

/*
 * 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.
 */
static void
tb_link_phys(
      TranslationBlock *tb,
      target_ulong phys_pc,
      target_ulong phys_page2
)
{
      unsigned int h;
      TranslationBlock **ptb;

      /* Add in the physical hash table. */
      h = tb_phys_hash_func(phys_pc);
      ptb = &env->tb_phys_hash[h];
      tb->phys_hash_next = *ptb;
      *ptb = tb;

      /* Add in the page list. */
      tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK);
      if (phys_page2 != -1) {
            tb_alloc_page(tb, 1, phys_page2);
      } else {
            tb->page_addr[1] = -1;
      }

      tb->jmp_first = (TranslationBlock *)((long)tb | 2);
      tb->jmp_next[0] = NULL;
      tb->jmp_next[1] = NULL;

      /* Init original jump addresses. */
      if (tb->tb_next_offset[0] != 0xffff)
            tb_reset_jump(tb, 0);
      if (tb->tb_next_offset[1] != 0xffff)
            tb_reset_jump(tb, 1);
}

/*
 * Find a translation block in the translation cache. If not found,
 * return NULL and the pointer to the last element of the list in pptb.
 */
static inline TranslationBlock *
tb_find_fast(
        target_ulong pc,
        target_ulong cs_base,
        unsigned int flags
)
{
        TranslationBlock *tb;

      tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
      if (__builtin_expect(! tb || tb->pc != pc
                  || tb->cs_base != cs_base || tb->flags != flags, 0)) {
            return 0;
      } else {
            return tb;
      }
}

static void
tb_gen_code(target_ulong pc, target_ulong cs_base, int flags)
{
    TranslationBlock *tb;
    uint8_t *tc_ptr;
    target_ulong phys_pc, phys_page2, virt_page2;
    int code_gen_size;

    phys_pc = NAME_(tlb_virt_to_phys)(pc);
    tb = tb_alloc(pc);
    if (!tb) {
        /* flush must be done */
        tb_flush();
        /* cannot fail at this point */
        tb = tb_alloc(pc);
    }
    tc_ptr = env->code_gen_ptr;
    tb->tc_ptr = tc_ptr;
    tb->cs_base = cs_base;
    tb->flags = flags;
    tb->cflags = CF_SINGLE_INSN;
    NAME_(gen_code)(tb, &code_gen_size);
    env->code_gen_ptr = (void *)(((unsigned long)env->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 = NAME_(tlb_virt_to_phys)(virt_page2);
    }
    tb_link_phys(tb, phys_pc, phys_page2);
}

static TranslationBlock *
tb_find_slow(
      target_ulong pc,
      target_ulong cs_base,
      unsigned int flags
)
{
      TranslationBlock *tb;
      uint8_t *tc_ptr;
      int code_gen_size;
      TranslationBlock **ptb1;
      unsigned int h;
      target_ulong phys_pc, phys_page1, phys_page2, virt_page2;

      env->tb_invalidated_flag = 0;

      /* find translated block using physical mappings */
      phys_pc = NAME_(tlb_virt_to_phys)(pc);
      phys_page1 = phys_pc & TARGET_PAGE_MASK;
      phys_page2 = -1;
      h = tb_phys_hash_func(phys_pc);
      ptb1 = &env->tb_phys_hash[h];
      for(;;) {
            tb = *ptb1;
            if (!tb)
                  goto not_found;
            if (tb->pc == pc
             && tb->page_addr[0] == phys_page1
             && tb->cs_base == cs_base
             && tb->flags == flags) {
                  /* check next page if needed */
                  if (tb->page_addr[1] != -1) {
                        virt_page2 = (pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
                        phys_page2 = NAME_(tlb_virt_to_phys)(virt_page2);
                        if (tb->page_addr[1] == phys_page2)
                              goto found;
                  } else {
                        goto found;
                  }
            }
            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();
            /* cannot fail at this point */
            tb = tb_alloc(pc);
            /* don't forget to invalidate previous TB info */
            env->tb_invalidated_flag = 1;
      }
      tc_ptr = env->code_gen_ptr;
      tb->tc_ptr = tc_ptr;
      tb->cs_base = cs_base;
      tb->flags = flags;
      NAME_(gen_code)(tb, &code_gen_size);
      env->code_gen_ptr = (void *)(((unsigned long)env->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 = NAME_(tlb_virt_to_phys)(virt_page2);
      }
      tb_link_phys(tb, phys_pc, phys_page2);

found:  ;
      /* we add the TB in the virtual pc hash table */
      env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;

      return tb;
}

TranslationBlock *
NAME_(tb_get)(
      target_ulong pc,
      target_ulong cs_base,
      unsigned int flags,
      unsigned long t0
)
{
      TranslationBlock *tb;

#if 0
      static int old = 0;

      if (old != loglevel) {
            tb_flush();
            old = loglevel;
      }
#endif

      tb = tb_find_fast(pc, cs_base, flags);
      if (! tb) {
            tb = tb_find_slow(pc, cs_base, flags);
            if (env->tb_invalidated_flag) {
                  /*
                   * As some TB could have been invalidated because
                   * of memory exceptions while generating the code, we
                   * mustn't connect last executed tb with this
                   * tb.
                   */
                  t0 = 0;
            }
      }

      /*
       * see if we can patch the calling TB. When the TB
       * spans two pages, we cannot safely do a direct
       * jump.
       */
      if (t0 != 0
       && tb->page_addr[1] == -1) {
            tb_add_jump((TranslationBlock *)(long)(t0 & ~3), t0 & 3, tb);
      }

      return tb;
}

/* invalidate one TB */
static inline void
tb_remove(TranslationBlock **ptb, TranslationBlock *tb, int next_offset)
{
    TranslationBlock *tb1;
    for(;;) {
        tb1 = *ptb;
        if (tb1 == tb) {
            *ptb = *(TranslationBlock **)((char *)tb1 + next_offset);
            break;
        }
        ptb = (TranslationBlock **)((char *)tb1 + next_offset);
    }
}

static inline void
tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb)
{
    TranslationBlock *tb1;
    unsigned int n1;

    for(;;) {
        tb1 = *ptb;
        n1 = (long)tb1 & 3;
        tb1 = (TranslationBlock *)((long)tb1 & ~3);
        if (tb1 == tb) {
            *ptb = tb1->page_next[n1];
            break;
        }
        ptb = &tb1->page_next[n1];
    }
}

static void
tb_jmp_remove(TranslationBlock *tb, int n)
{
    TranslationBlock *tb1, **ptb;
    unsigned int n1;

    ptb = &tb->jmp_next[n];
    tb1 = *ptb;
    if (tb1) {
        /* find tb(n) in circular list */
        for(;;) {
            tb1 = *ptb;
            n1 = (long)tb1 & 3;
            tb1 = (TranslationBlock *)((long)tb1 & ~3);
            if (n1 == n && tb1 == tb)
                break;
            if (n1 == 2) {
                ptb = &tb1->jmp_first;
            } else {
                ptb = &tb1->jmp_next[n1];
            }
        }
        /* now we can suppress tb(n) from the list */
        *ptb = tb->jmp_next[n];

        tb->jmp_next[n] = NULL;
    }
}

static void
tb_phys_invalidate(TranslationBlock *tb)
{
    PageDesc *p;
    unsigned int h, n1;
    target_ulong phys_pc;
    TranslationBlock *tb1, *tb2;
    
    /* remove the TB from the hash list */
    phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
    h = tb_phys_hash_func(phys_pc);
    tb_remove(&env->tb_phys_hash[h], tb, 
              offsetof(TranslationBlock, phys_hash_next));

    /* remove the TB from the page list */
    if (tb->page_addr[0] != -1) {
        p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS);
        tb_page_remove(&p->first_tb, tb);
        invalidate_page_bitmap(p);
    }
    if (tb->page_addr[1] != -1) {
        p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS);
        tb_page_remove(&p->first_tb, tb);
        invalidate_page_bitmap(p);
    }

    env->tb_invalidated_flag = 1;

    /* remove the TB from the hash list */
    h = tb_jmp_cache_hash_func(tb->pc);
    if (env->tb_jmp_cache[h] == tb) {
          env->tb_jmp_cache[h] = NULL;
    }

    /* suppress this TB from the two jump lists */
    tb_jmp_remove(tb, 0);
    tb_jmp_remove(tb, 1);

    /* suppress any remaining jumps to this TB */
    tb1 = tb->jmp_first;
    for(;;) {
        n1 = (long)tb1 & 3;
        if (n1 == 2)
            break;
        tb1 = (TranslationBlock *)((long)tb1 & ~3);
        tb2 = tb1->jmp_next[n1];
        tb_reset_jump(tb1, n1);
        tb1->jmp_next[n1] = NULL;
        tb1 = tb2;
    }
    tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */
}

static void
set_bits(uint8_t *tab, int start, int len)
{
    int end, mask, end1;

    end = start + len;
    tab += start >> 3;
    mask = 0xff << (start & 7);
    if ((start & ~7) == (end & ~7)) {
        if (start < end) {
            mask &= ~(0xff << (end & 7));
            *tab |= mask;
        }
    } else {
        *tab++ |= mask;
        start = (start + 8) & ~7;
        end1 = end & ~7;
        while (start < end1) {
            *tab++ = 0xff;
            start += 8;
        }
        if (start < end) {
            mask = ~(0xff << (end & 7));
            *tab |= mask;
        }
    }
}

static void
build_page_bitmap(PageDesc *p)
{
    int n, tb_start, tb_end;
    TranslationBlock *tb;
    
    p->code_bitmap = malloc(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) {
        n = (long)tb & 3;
        tb = (TranslationBlock *)((long)tb & ~3);
        /* NOTE: this is subtle as a TB may span two physical pages */
        if (n == 0) {
            /* NOTE: tb_end may be after the end of the page, but
               it is not a problem */
            tb_start = tb->pc & ~TARGET_PAGE_MASK;
            tb_end = tb_start + tb->size;
            if (tb_end > TARGET_PAGE_SIZE)
                tb_end = TARGET_PAGE_SIZE;
        } else {
            tb_start = 0;
            tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
        }
        set_bits(p->code_bitmap, tb_start, tb_end - tb_start);
        tb = tb->page_next[n];
    }
}

/* invalidate all TBs which intersect with the target physical page
   starting in range [start;end[. NOTE: start and end must refer to
   the same physical page. */
static void
tb_invalidate_phys_page_range(target_ulong start, target_ulong end)
{
    int n, current_tb_modified, current_tb_not_found, current_flags;
    PageDesc *p;
    TranslationBlock *tb, *tb_next, *current_tb, *saved_tb;
    target_ulong tb_start, tb_end;
    target_ulong current_pc, current_cs_base;

    p = page_find(start >> TARGET_PAGE_BITS);
    if (!p) 
        return;
    if (!p->code_bitmap && ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) {
        /* build code bitmap */
        build_page_bitmap(p);
    }

    /* we remove all the TBs in the range [start, end[ */
    /* XXX: see if in some cases it could be faster to invalidate all the code */
    current_tb_not_found = 1;
    current_tb_modified = 0;
    current_tb = NULL; /* avoid warning */
    current_pc = 0; /* avoid warning */
    current_cs_base = 0; /* avoid warning */
    current_flags = 0; /* avoid warning */
    tb = p->first_tb;
    while (tb != NULL) {
        n = (long)tb & 3;
        tb = (TranslationBlock *)((long)tb & ~3);
        tb_next = tb->page_next[n];
        /* NOTE: this is subtle as a TB may span two physical pages */
        if (n == 0) {
            /* NOTE: tb_end may be after the end of the page, but
               it is not a problem */
            tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
            tb_end = tb_start + tb->size;
        } else {
            tb_start = tb->page_addr[1];
            tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
        }
        if (!(tb_end <= start || tb_start >= end)) {
            if (current_tb_not_found) {
                current_tb_not_found = 0;
                current_tb = NULL;
                if (env->mem_write_pc) {
                    /* now we have a real cpu fault */
                    current_tb = NAME_(tb_find_pc)(env->mem_write_pc);
                }
            }
            if (current_tb == tb &&
                !(current_tb->cflags & CF_SINGLE_INSN)) {
                /* 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
                would require a specialized function to partially
                restore the CPU state */
                
                current_tb_modified = 1;
                NAME_(restore_state)(current_tb, env->mem_write_pc);
                current_flags = env->hflags;
                current_flags |= (env->eflags & (CPU_IOPL_MASK | CPU_TF_MASK | CPU_VM_MASK));
                current_cs_base = (target_ulong)env->segs[R_CS].base;
                current_pc = current_cs_base + env->eip;
            }
            saved_tb = env->current_tb;
            env->current_tb = NULL;
            tb_phys_invalidate(tb);
            env->current_tb = saved_tb;
            if (env->interrupt_request && env->current_tb)
                NAME_(interrupt)();
        }
        tb = tb_next;
    }
    /* if no code remaining, no need to continue to use slow writes */
    if (! p->first_tb) {
        invalidate_page_bitmap(p);
        NAME_(tlb_unprotect)(start);
    }
    if (current_tb_modified) {
        /* we generate a block containing just the instruction
           modifying the memory. It will ensure that it cannot modify
           itself */
        env->current_tb = NULL;
        tb_gen_code(current_pc, current_cs_base, current_flags);
        NAME_(resume_from_signal)(NULL);
    }
}

/* len must be <= 8 and start must be a multiple of len */
void
NAME_(tb_invalidate_phys_page_fast)(target_ulong start, int len)
{
    PageDesc *p;
    int offset, b;

    p = page_find(start >> TARGET_PAGE_BITS);
    if (!p) 
        return;
    if (p->code_bitmap) {
        offset = start & ~TARGET_PAGE_MASK;
        b = p->code_bitmap[offset >> 3] >> (offset & 7);
        if (b & ((1 << len) - 1))
            goto do_invalidate;
    } else {
    do_invalidate:
        tb_invalidate_phys_page_range(start, start + len);
    }
}

TranslationBlock *
NAME_(tb_find_alloc)(target_ulong pd)
{
      PageDesc *p1;

      p1 = page_find_alloc(pd >> TARGET_PAGE_BITS);

      return p1->first_tb;
}

/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr <
   tb[1].tc_ptr. Return NULL if not found */
TranslationBlock *
NAME_(tb_find_pc)(unsigned long tc_ptr)
{
    int m_min, m_max, m;
    unsigned long v;
    TranslationBlock *tb;

    if (env->nb_tbs <= 0)
        return NULL;
    if (tc_ptr < (unsigned long) env->code_gen_buffer
     || (unsigned long) env->code_gen_ptr <= tc_ptr)
        return NULL;
    /* binary search (cf Knuth) */
    m_min = 0;
    m_max = env->nb_tbs - 1;
    while (m_min <= m_max) {
        m = (m_min + m_max) >> 1;
        tb = &env->tbs[m];
        v = (unsigned long)tb->tc_ptr;
        if (v == tc_ptr)
            return tb;
        else if (tc_ptr < v) {
            m_max = m - 1;
        } else {
            m_min = m + 1;
        }
    } 
    return &env->tbs[m_max];
}

/*forward*/ static void tb_reset_jump_recursive(TranslationBlock *tb);

static void
tb_reset_jump_recursive2(TranslationBlock *tb, int n)
{
    TranslationBlock *tb1, *tb_next, **ptb;
    unsigned int n1;

    tb1 = tb->jmp_next[n];
    if (tb1 != NULL) {
        /* find head of list */
        for(;;) {
            n1 = (long)tb1 & 3;
            tb1 = (TranslationBlock *)((long)tb1 & ~3);
            if (n1 == 2)
                break;
            tb1 = tb1->jmp_next[n1];
        }
        /* we are now sure now that tb jumps to tb1 */
        tb_next = tb1;

        /* remove tb from the jmp_first list */
        ptb = &tb_next->jmp_first;
        for(;;) {
            tb1 = *ptb;
            n1 = (long)tb1 & 3;
            tb1 = (TranslationBlock *)((long)tb1 & ~3);
            if (n1 == n && tb1 == tb)
                break;
            ptb = &tb1->jmp_next[n1];
        }
        *ptb = tb->jmp_next[n];
        tb->jmp_next[n] = NULL;
        
        /* suppress the jump to next tb in generated code */
        tb_reset_jump(tb, n);

        /* suppress jumps in the tb on which we could have jumped */
        tb_reset_jump_recursive(tb_next);
    }
}

static void
tb_reset_jump_recursive(TranslationBlock *tb)
{
      tb_reset_jump_recursive2(tb, 0);
      tb_reset_jump_recursive2(tb, 1);
}

void
NAME_(interrupt)(void)
{
      TranslationBlock *tb;

      /*
       * If the cpu is currently executing code, we must unlink it and
       * all the potentially executing TB.
       */
      tb = env->current_tb;
      if (tb) {
            env->current_tb = NULL;
            tb_reset_jump_recursive(tb);
      }
}

void
NAME_(jit_buffer_reset)(void)
{
      env->nb_tbs = 0;

      memset(env->tb_jmp_cache, 0, sizeof(env->tb_jmp_cache));

      memset(env->tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *));
      page_flush_tb();

      env->code_gen_ptr = env->code_gen_buffer;
}

void
NAME_(jit_buffer_init)(struct cpu *css)
{
      css->code_gen_ptr = css->code_gen_buffer;

      memset(css->tlb_read, 0, sizeof(css->tlb_read));
      memset(css->tlb_write, 0, sizeof(css->tlb_write));
      memset(css->tlb_code, 0, sizeof(css->tlb_code));
      memset(css->tb_jmp_cache, 0, sizeof(css->tb_jmp_cache));
}

Generated by  Doxygen 1.6.0   Back to index