Source to ./ppc32_jit.h
/*
* Cisco router simulation platform.
* Copyright (c) 2005,2006 Christophe Fillot ([email protected])
*
* PPC32 JIT compiler.
*/
#ifndef __PPC32_JIT_H__
#define __PPC32_JIT_H__
#include "utils.h"
#include "sbox.h"
/* Size of executable page area (in Mb) */
#ifndef __CYGWIN__
#define PPC_EXEC_AREA_SIZE 64
#else
#define PPC_EXEC_AREA_SIZE 16
#endif
/* Buffer size for JIT code generation */
#define PPC_JIT_BUFSIZE 32768
/* Maximum number of X86 chunks */
#define PPC_JIT_MAX_CHUNKS 64
/* Size of hash for virtual address lookup */
#define PPC_JIT_VIRT_HASH_BITS 17
#define PPC_JIT_VIRT_HASH_MASK ((1 << PPC_JIT_VIRT_HASH_BITS) - 1)
#define PPC_JIT_VIRT_HASH_SIZE (1 << PPC_JIT_VIRT_HASH_BITS)
/* Size of hash for physical lookup */
#define PPC_JIT_PHYS_HASH_BITS 16
#define PPC_JIT_PHYS_HASH_MASK ((1 << PPC_JIT_PHYS_HASH_BITS) - 1)
#define PPC_JIT_PHYS_HASH_SIZE (1 << PPC_JIT_PHYS_HASH_BITS)
#define PPC_JIT_TARGET_BITMAP_INDEX(x) (((x) >> 7) & 0x1F)
#define PPC_JIT_TARGET_BITMAP_POS(x) (((x) >> 2) & 0x1F)
/* Instruction jump patch */
struct ppc32_insn_patch {
struct ppc32_insn_patch *next;
u_char *jit_insn;
m_uint32_t ppc_ia;
};
/* Instruction patch table */
#define PPC32_INSN_PATCH_TABLE_SIZE 32
struct ppc32_jit_patch_table {
struct ppc32_jit_patch_table *next;
struct ppc32_insn_patch patches[PPC32_INSN_PATCH_TABLE_SIZE];
u_int cur_patch;
};
#define PPC32_JIT_TCB_FLAG_SMC 0x1 /* Self-modifying code */
/* PPC32 translated code block */
struct ppc32_jit_tcb {
u_int flags;
m_uint32_t start_ia;
m_uint32_t exec_state;
u_char **jit_insn_ptr;
m_uint64_t acc_count;
ppc_insn_t *ppc_code;
u_int ppc_trans_pos;
u_int jit_chunk_pos;
u_char *jit_ptr;
insn_exec_page_t *jit_buffer;
insn_exec_page_t *jit_chunks[PPC_JIT_MAX_CHUNKS];
struct ppc32_jit_patch_table *patch_table;
ppc32_jit_tcb_t *prev,*next;
/* Physical page information */
m_uint32_t phys_page;
m_uint32_t phys_hash;
ppc32_jit_tcb_t **phys_pprev,*phys_next;
/* 1024 instructions per page, one bit per instruction */
m_uint32_t target_bitmap[32];
m_uint32_t target_undef_cnt;
#if DEBUG_BLOCK_TIMESTAMP
m_uint64_t tm_first_use,tm_last_use;
#endif
};
/* PPC instruction recognition */
struct ppc32_insn_tag {
int (*emit)(cpu_ppc_t *cpu,ppc32_jit_tcb_t *,ppc_insn_t);
m_uint32_t mask,value;
};
/* Mark the specified IA as a target for further recompiling */
static inline void
ppc32_jit_tcb_set_target_bit(ppc32_jit_tcb_t *b,m_uint32_t ia)
{
int index,pos;
index = PPC_JIT_TARGET_BITMAP_INDEX(ia);
pos = PPC_JIT_TARGET_BITMAP_POS(ia);
b->target_bitmap[index] |= 1 << pos;
}
/* Returns TRUE if the specified IA is in the target bitmap */
static inline int
ppc32_jit_tcb_get_target_bit(ppc32_jit_tcb_t *b,m_uint32_t ia)
{
int index,pos;
index = PPC_JIT_TARGET_BITMAP_INDEX(ia);
pos = PPC_JIT_TARGET_BITMAP_POS(ia);
return(b->target_bitmap[index] & (1 << pos));
}
/* Get the JIT instruction pointer in a translated block */
static forced_inline
u_char *ppc32_jit_tcb_get_host_ptr(ppc32_jit_tcb_t *tcb,m_uint32_t vaddr)
{
m_uint32_t offset;
offset = (vaddr & PPC32_MIN_PAGE_IMASK) >> 2;
return(tcb->jit_insn_ptr[offset]);
}
/* Check if the specified address belongs to the specified block */
static forced_inline
int ppc32_jit_tcb_local_addr(ppc32_jit_tcb_t *tcb,m_uint32_t vaddr,
u_char **jit_addr)
{
if ((vaddr & PPC32_MIN_PAGE_MASK) == tcb->start_ia) {
*jit_addr = ppc32_jit_tcb_get_host_ptr(tcb,vaddr);
return(1);
}
return(0);
}
/* Check if PC register matches the compiled block virtual address */
static forced_inline
int ppc32_jit_tcb_match(cpu_ppc_t *cpu,ppc32_jit_tcb_t *tcb)
{
m_uint32_t vpage;
vpage = cpu->ia & PPC32_MIN_PAGE_MASK;
return((tcb->start_ia == vpage) && (tcb->exec_state == cpu->exec_state));
}
/* Compute the hash index for the specified virtual address */
static forced_inline m_uint32_t ppc32_jit_get_virt_hash(m_uint32_t vaddr)
{
m_uint32_t page_hash;
page_hash = sbox_u32(vaddr >> PPC32_MIN_PAGE_SHIFT);
return((page_hash ^ (page_hash >> 14)) & PPC_JIT_VIRT_HASH_MASK);
}
/* Compute the hash index for the specified physical page */
static forced_inline m_uint32_t ppc32_jit_get_phys_hash(m_uint32_t phys_page)
{
m_uint32_t page_hash;
page_hash = sbox_u32(phys_page);
return((page_hash ^ (page_hash >> 12)) & PPC_JIT_PHYS_HASH_MASK);
}
/* Find a JIT block matching a physical page */
static inline ppc32_jit_tcb_t *
ppc32_jit_find_by_phys_page(cpu_ppc_t *cpu,m_uint32_t phys_page)
{
m_uint32_t page_hash = ppc32_jit_get_phys_hash(phys_page);
ppc32_jit_tcb_t *tcb;
for(tcb=cpu->tcb_phys_hash[page_hash];tcb;tcb=tcb->phys_next)
if (tcb->phys_page == phys_page)
return tcb;
return NULL;
}
/* ======================================================================== */
/* JIT emit operations (generic). */
/* ======================================================================== */
/* Indicate registers modified by ppc32_update_cr() functions */
extern void ppc32_update_cr_set_altered_hreg(cpu_ppc_t *cpu);
/* Set opcode */
static inline void ppc32_op_set(cpu_ppc_t *cpu,jit_op_t *op)
{
cpu_gen_t *c = cpu->gen;
*c->jit_op_current = op;
c->jit_op_current = &op->next;
}
/* EMIT_BASIC_OPCODE */
static inline void ppc32_op_emit_basic_opcode(cpu_ppc_t *cpu,u_int opcode)
{
jit_op_t *op = jit_op_get(cpu->gen,0,opcode);
ppc32_op_set(cpu,op);
}
/* Trash the specified host register */
static inline void ppc32_op_emit_alter_host_reg(cpu_ppc_t *cpu,int host_reg)
{
jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_ALTER_HOST_REG);
op->param[0] = host_reg;
ppc32_op_set(cpu,op);
}
/* EMIT_INSN_OUTPUT */
static inline jit_op_t *
ppc32_op_emit_insn_output(cpu_ppc_t *cpu,u_int size_index,char *insn_name)
{
jit_op_t *op = jit_op_get(cpu->gen,size_index,JIT_OP_INSN_OUTPUT);
op->arg_ptr = NULL;
op->insn_name = insn_name;
ppc32_op_set(cpu,op);
return op;
}
/* EMIT_LOAD_GPR */
static inline
void ppc32_op_emit_load_gpr(cpu_ppc_t *cpu,int host_reg,int ppc_reg)
{
jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_LOAD_GPR);
op->param[0] = host_reg;
op->param[1] = ppc_reg;
op->param[2] = host_reg;
ppc32_op_set(cpu,op);
}
/* EMIT_STORE_GPR */
static inline
void ppc32_op_emit_store_gpr(cpu_ppc_t *cpu,int ppc_reg,int host_reg)
{
jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_STORE_GPR);
op->param[0] = host_reg;
op->param[1] = ppc_reg;
op->param[2] = host_reg;
ppc32_op_set(cpu,op);
}
/* EMIT_UPDATE_FLAGS */
static inline
void ppc32_op_emit_update_flags(cpu_ppc_t *cpu,int field,int is_signed)
{
jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_UPDATE_FLAGS);
op->param[0] = field;
op->param[1] = is_signed;
ppc32_op_set(cpu,op);
ppc32_update_cr_set_altered_hreg(cpu);
}
/* EMIT_REQUIRE_FLAGS */
static inline void ppc32_op_emit_require_flags(cpu_ppc_t *cpu,int field)
{
jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_REQUIRE_FLAGS);
op->param[0] = field;
ppc32_op_set(cpu,op);
}
/* EMIT_BRANCH_TARGET */
static inline void ppc32_op_emit_branch_target(cpu_ppc_t *cpu,
ppc32_jit_tcb_t *b,
m_uint32_t ia)
{
cpu_gen_t *c = cpu->gen;
jit_op_t *op = jit_op_get(c,0,JIT_OP_BRANCH_TARGET);
u_int pos;
if ((ia & PPC32_MIN_PAGE_MASK) == b->start_ia) {
pos = (ia & PPC32_MIN_PAGE_IMASK) >> 2;
/* Insert in head */
op->next = c->jit_op_array[pos];
c->jit_op_array[pos] = op;
}
}
/* EMIT_SET_HOST_REG_IMM32 */
static inline void
ppc32_op_emit_set_host_reg_imm32(cpu_ppc_t *cpu,int reg,m_uint32_t val)
{
jit_op_t *op = jit_op_get(cpu->gen,0,JIT_OP_SET_HOST_REG_IMM32);
op->param[0] = reg;
op->param[1] = val;
ppc32_op_set(cpu,op);
}
/* ======================================================================== */
/* JIT operations with implementations specific to target CPU */
void ppc32_op_insn_output(ppc32_jit_tcb_t *b,jit_op_t *op);
void ppc32_op_load_gpr(ppc32_jit_tcb_t *b,jit_op_t *op);
void ppc32_op_store_gpr(ppc32_jit_tcb_t *b,jit_op_t *op);
void ppc32_op_update_flags(ppc32_jit_tcb_t *b,jit_op_t *op);
void ppc32_op_move_host_reg(ppc32_jit_tcb_t *b,jit_op_t *op);
void ppc32_op_set_host_reg_imm32(ppc32_jit_tcb_t *b,jit_op_t *op);
/* Set the Instruction Address (IA) register */
void ppc32_set_ia(u_char **ptr,m_uint32_t new_ia);
/* Jump to the next page */
void ppc32_set_page_jump(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b);
/* Increment the number of executed instructions (performance debugging) */
void ppc32_inc_perf_counter(cpu_ppc_t *cpu);
/* ======================================================================== */
/* Virtual Breakpoint */
void ppc32_emit_breakpoint(cpu_ppc_t *cpu,ppc32_jit_tcb_t *b);
/* Initialize instruction lookup table */
void ppc32_jit_create_ilt(void);
/* Initialize the JIT structure */
int ppc32_jit_init(cpu_ppc_t *cpu);
/* Flush the JIT */
u_int ppc32_jit_flush(cpu_ppc_t *cpu,u_int threshold);
/* Shutdown the JIT */
void ppc32_jit_shutdown(cpu_ppc_t *cpu);
/* Fetch a PowerPC instruction and emit corresponding translated code */
struct ppc32_insn_tag *ppc32_jit_fetch_and_emit(cpu_ppc_t *cpu,
ppc32_jit_tcb_t *block);
/* Record a patch to apply in a compiled block */
int ppc32_jit_tcb_record_patch(ppc32_jit_tcb_t *block,jit_op_t *iop,
u_char *jit_ptr,m_uint32_t vaddr);
/* Mark a block as containing self-modifying code */
void ppc32_jit_mark_smc(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block);
/* Free an instruction block */
void ppc32_jit_tcb_free(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block,
int list_removal);
/* Check if the specified address belongs to the specified block */
int ppc32_jit_tcb_local_addr(ppc32_jit_tcb_t *block,m_uint32_t vaddr,
u_char **jit_addr);
/* Recompile a page */
int ppc32_jit_tcb_recompile(cpu_ppc_t *cpu,ppc32_jit_tcb_t *block);
/* Execute compiled PowerPC code */
void *ppc32_jit_run_cpu(cpu_gen_t *gen);
/* Start register allocation sequence */
void ppc32_jit_start_hreg_seq(cpu_ppc_t *cpu,char *insn);
/* Close register allocation sequence */
void ppc32_jit_close_hreg_seq(cpu_ppc_t *cpu);
/* Insert a reg map as head of list (as MRU element) */
void ppc32_jit_insert_hreg_mru(cpu_ppc_t *cpu,struct hreg_map *map);
/* Allocate an host register */
int ppc32_jit_alloc_hreg(cpu_ppc_t *cpu,int ppc_reg);
/* Force allocation of an host register */
int ppc32_jit_alloc_hreg_forced(cpu_ppc_t *cpu,int hreg);
/* Initialize register mapping */
void ppc32_jit_init_hreg_mapping(cpu_ppc_t *cpu);
#endif