macemu/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp

824 lines
22 KiB
C++

/*
* ppc-cpu.cpp - PowerPC CPU definition
*
* Kheperix (C) 2003-2005 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include <stdlib.h>
#include <assert.h>
#include "vm_alloc.h"
#include "cpu/vm.hpp"
#include "cpu/ppc/ppc-cpu.hpp"
#ifndef SHEEPSHAVER
#include "basic-kernel.hpp"
#endif
#if PPC_ENABLE_JIT
#include "cpu/jit/dyngen-exec.h"
#endif
#if ENABLE_MON
#include "mon.h"
#include "mon_disass.h"
#endif
#define DEBUG 0
#include "debug.h"
#if PPC_PROFILE_GENERIC_CALLS
uint32 powerpc_cpu::generic_calls_count[PPC_I(MAX)];
static int generic_calls_ids[PPC_I(MAX)];
const int generic_calls_top_ten = 20;
int generic_calls_compare(const void *e1, const void *e2)
{
const int id1 = *(const int *)e1;
const int id2 = *(const int *)e2;
return powerpc_cpu::generic_calls_count[id2] - powerpc_cpu::generic_calls_count[id1];
}
#endif
#if PPC_PROFILE_REGS_USE
int register_info_compare(const void *e1, const void *e2)
{
const powerpc_cpu::register_info *ri1 = (powerpc_cpu::register_info *)e1;
const powerpc_cpu::register_info *ri2 = (powerpc_cpu::register_info *)e2;
return ri2->count - ri1->count;
}
#endif
static int ppc_refcount = 0;
void powerpc_cpu::set_register(int id, any_register const & value)
{
if (id >= powerpc_registers::GPR(0) && id <= powerpc_registers::GPR(31)) {
gpr(id - powerpc_registers::GPR_BASE) = value.i;
return;
}
if (id >= powerpc_registers::FPR(0) && id <= powerpc_registers::FPR(31)) {
fpr(id - powerpc_registers::FPR_BASE) = value.d;
return;
}
switch (id) {
case powerpc_registers::CR: cr().set(value.i); break;
case powerpc_registers::FPSCR: fpscr() = value.i; break;
case powerpc_registers::XER: xer().set(value.i); break;
case powerpc_registers::LR: lr() = value.i; break;
case powerpc_registers::CTR: ctr() = value.i; break;
case basic_registers::PC:
case powerpc_registers::PC: pc() = value.i; break;
case basic_registers::SP:
case powerpc_registers::SP: gpr(1)= value.i; break;
default: abort(); break;
}
}
any_register powerpc_cpu::get_register(int id)
{
any_register value;
if (id >= powerpc_registers::GPR(0) && id <= powerpc_registers::GPR(31)) {
value.i = gpr(id - powerpc_registers::GPR_BASE);
return value;
}
if (id >= powerpc_registers::FPR(0) && id <= powerpc_registers::FPR(31)) {
value.d = fpr(id - powerpc_registers::FPR_BASE);
return value;
}
switch (id) {
case powerpc_registers::CR: value.i = cr().get(); break;
case powerpc_registers::FPSCR: value.i = fpscr(); break;
case powerpc_registers::XER: value.i = xer().get(); break;
case powerpc_registers::LR: value.i = lr(); break;
case powerpc_registers::CTR: value.i = ctr(); break;
case basic_registers::PC:
case powerpc_registers::PC: value.i = pc(); break;
case basic_registers::SP:
case powerpc_registers::SP: value.i = gpr(1); break;
default: abort(); break;
}
return value;
}
#if KPX_MAX_CPUS != 1
uint32 powerpc_registers::reserve_valid = 0;
uint32 powerpc_registers::reserve_addr = 0;
uint32 powerpc_registers::reserve_data = 0;
#endif
void powerpc_cpu::init_registers()
{
assert((((uintptr)&vr(0)) % 16) == 0);
for (int i = 0; i < 32; i++) {
gpr(i) = 0;
fpr(i) = 0;
}
cr().set(0);
fpscr() = 0;
xer().set(0);
lr() = 0;
ctr() = 0;
pc() = 0;
}
void powerpc_cpu::init_flight_recorder()
{
#if PPC_FLIGHT_RECORDER
log_ptr = 0;
log_ptr_wrapped = false;
#endif
}
void powerpc_cpu::do_record_step(uint32 pc, uint32 opcode)
{
#if PPC_FLIGHT_RECORDER
log[log_ptr].pc = pc;
log[log_ptr].opcode = opcode;
#ifdef SHEEPSHAVER
log[log_ptr].sp = gpr(1);
log[log_ptr].r24 = gpr(24);
#endif
#if PPC_FLIGHT_RECORDER >= 2
for (int i = 0; i < 32; i++) {
log[log_ptr].r[i] = gpr(i);
log[log_ptr].fr[i] = fpr(i);
}
log[log_ptr].lr = lr();
log[log_ptr].ctr = ctr();
log[log_ptr].cr = cr().get();
log[log_ptr].xer = xer().get();
log[log_ptr].fpscr = fpscr();
#endif
log_ptr++;
if (log_ptr == LOG_SIZE) {
log_ptr = 0;
log_ptr_wrapped = true;
}
#endif
}
#if PPC_FLIGHT_RECORDER
void powerpc_cpu::start_log()
{
logging = true;
invalidate_cache();
}
void powerpc_cpu::stop_log()
{
logging = false;
invalidate_cache();
}
void powerpc_cpu::dump_log(const char *filename)
{
if (filename == NULL)
filename = "ppc.log";
FILE *f = fopen(filename, "w");
if (f == NULL)
return;
int start_ptr = 0;
int log_size = log_ptr;
if (log_ptr_wrapped) {
start_ptr = log_ptr;
log_size = LOG_SIZE;
}
for (int i = 0; i < log_size; i++) {
int j = (i + start_ptr) % LOG_SIZE;
#if PPC_FLIGHT_RECORDER >= 2
fprintf(f, " pc %08x lr %08x ctr %08x cr %08x xer %08x ", log[j].pc, log[j].lr, log[j].ctr, log[j].cr, log[j].xer);
fprintf(f, " r0 %08x r1 %08x r2 %08x r3 %08x ", log[j].r[0], log[j].r[1], log[j].r[2], log[j].r[3]);
fprintf(f, " r4 %08x r5 %08x r6 %08x r7 %08x ", log[j].r[4], log[j].r[5], log[j].r[6], log[j].r[7]);
fprintf(f, " r8 %08x r9 %08x r10 %08x r11 %08x ", log[j].r[8], log[j].r[9], log[j].r[10], log[j].r[11]);
fprintf(f, "r12 %08x r13 %08x r14 %08x r15 %08x ", log[j].r[12], log[j].r[13], log[j].r[14], log[j].r[15]);
fprintf(f, "r16 %08x r17 %08x r18 %08x r19 %08x ", log[j].r[16], log[j].r[17], log[j].r[18], log[j].r[19]);
fprintf(f, "r20 %08x r21 %08x r22 %08x r23 %08x ", log[j].r[20], log[j].r[21], log[j].r[22], log[j].r[23]);
fprintf(f, "r24 %08x r25 %08x r26 %08x r27 %08x ", log[j].r[24], log[j].r[25], log[j].r[26], log[j].r[27]);
fprintf(f, "r28 %08x r29 %08x r30 %08x r31 %08x\n", log[j].r[28], log[j].r[29], log[j].r[30], log[j].r[31]);
fprintf(f, "opcode %08x\n", log[j].opcode);
#else
fprintf(f, " pc %08x opc %08x", log[j].pc, log[j].opcode);
#ifdef SHEEPSHAVER
fprintf(f, " sp %08x r24 %08x", log[j].sp, log[j].r24);
#endif
fprintf(f, "| ");
#if !ENABLE_MON
fprintf(f, "\n");
#endif
#endif
#if ENABLE_MON
disass_ppc(f, log[j].pc, log[j].opcode);
#endif
}
fclose(f);
}
#endif
#if ENABLE_MON
static uint32 mon_read_byte_ppc(uintptr addr)
{
return *((uint8 *)addr);
}
static void mon_write_byte_ppc(uintptr addr, uint32 b)
{
uint8 *m = (uint8 *)addr;
*m = b;
}
#endif
void powerpc_cpu::initialize()
{
#ifdef SHEEPSHAVER
printf("PowerPC CPU emulator by Gwenole Beauchesne\n");
#endif
#if PPC_PROFILE_REGS_USE
reginfo = new register_info[32];
for (int i = 0; i < 32; i++) {
reginfo[i].id = i;
reginfo[i].count = 0;
}
#endif
init_flight_recorder();
init_decoder();
init_registers();
init_decode_cache();
execute_depth = 0;
// Initialize block lookup table
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
my_block_cache.initialize();
#endif
// Init cache range invalidate recorder
cache_range.start = cache_range.end = 0;
// Init syscalls handler
execute_do_syscall = NULL;
// Init field2mask
for (int i = 0; i < 256; i++) {
uint32 mask = 0;
if (i & 0x01) mask |= 0x0000000f;
if (i & 0x02) mask |= 0x000000f0;
if (i & 0x04) mask |= 0x00000f00;
if (i & 0x08) mask |= 0x0000f000;
if (i & 0x10) mask |= 0x000f0000;
if (i & 0x20) mask |= 0x00f00000;
if (i & 0x40) mask |= 0x0f000000;
if (i & 0x80) mask |= 0xf0000000;
field2mask[i] = mask;
}
#if ENABLE_MON
mon_init();
mon_read_byte = mon_read_byte_ppc;
mon_write_byte = mon_write_byte_ppc;
#endif
#if PPC_PROFILE_COMPILE_TIME
compile_count = 0;
compile_time = 0;
emul_start_time = clock();
#endif
}
#if PPC_ENABLE_JIT
void powerpc_cpu::enable_jit(uint32 cache_size)
{
use_jit = true;
if (cache_size)
codegen.set_cache_size(cache_size);
codegen.initialize();
}
#endif
// Memory allocator returning powerpc_cpu objects aligned on 16-byte boundaries
// FORMAT: [ alignment ] magic identifier, offset to malloc'ed data, powerpc_cpu data
void *powerpc_cpu::operator new(size_t size)
{
const int ALIGN = 16;
// Allocate enough space for powerpc_cpu data + signature + align pad
uint8 *ptr = (uint8 *)malloc(size + ALIGN * 2);
if (ptr == NULL)
throw std::bad_alloc();
// Align memory
int ofs = 0;
while ((((uintptr)ptr) % ALIGN) != 0)
ofs++, ptr++;
// Insert signature and offset
struct aligned_block_t {
uint32 pad[(ALIGN - 8) / 4];
uint32 signature;
uint32 offset;
uint8 data[sizeof(powerpc_cpu)];
};
aligned_block_t *blk = (aligned_block_t *)ptr;
blk->signature = 0x53435055; /* 'SCPU' */
blk->offset = ofs + (&blk->data[0] - (uint8 *)blk);
assert((((uintptr)&blk->data) % ALIGN) == 0);
return &blk->data[0];
}
void powerpc_cpu::operator delete(void *p)
{
uint32 *blk = (uint32 *)p;
assert(blk[-2] == 0x53435055); /* 'SCPU' */
void *ptr = (void *)(((uintptr)p) - blk[-1]);
free(ptr);
}
#ifdef SHEEPSHAVER
powerpc_cpu::powerpc_cpu()
#if PPC_ENABLE_JIT
: codegen(this)
#endif
#else
powerpc_cpu::powerpc_cpu(task_struct *parent_task)
: basic_cpu(parent_task)
#if PPC_ENABLE_JIT
, codegen(this)
#endif
#endif
{
#if PPC_ENABLE_JIT
use_jit = false;
#endif
++ppc_refcount;
initialize();
}
powerpc_cpu::~powerpc_cpu()
{
--ppc_refcount;
#if PPC_PROFILE_COMPILE_TIME
clock_t emul_end_time = clock();
const char *type = NULL;
#if PPC_ENABLE_JIT
if (use_jit)
type = "compile";
#endif
#if PPC_DECODE_CACHE
if (!type)
type = "predecode";
#endif
if (type) {
printf("### Statistics for block %s\n", type);
printf("Total block %s count : %d\n", type, compile_count);
uint32 emul_time = emul_end_time - emul_start_time;
printf("Total emulation time : %.1f sec\n",
double(emul_time) / double(CLOCKS_PER_SEC));
printf("Total %s time : %.1f sec (%.1f%%)\n", type,
double(compile_time) / double(CLOCKS_PER_SEC),
100.0 * double(compile_time) / double(emul_time));
printf("\n");
}
#endif
#if PPC_PROFILE_GENERIC_CALLS
if (use_jit && ppc_refcount == 0) {
uint64 total_generic_calls_count = 0;
for (int i = 0; i < PPC_I(MAX); i++) {
generic_calls_ids[i] = i;
total_generic_calls_count += generic_calls_count[i];
}
qsort(generic_calls_ids, PPC_I(MAX), sizeof(int), generic_calls_compare);
printf("Rank Count Ratio Name\n");
for (int i = 0; i < generic_calls_top_ten; i++) {
uint32 mnemo = generic_calls_ids[i];
uint32 count = generic_calls_count[mnemo];
const instr_info_t *ii = powerpc_ii_table;
while (ii->mnemo != mnemo)
ii++;
printf("%03d: %10lu %2.1f%% %s\n", i, count, 100.0*double(count)/double(total_generic_calls_count), ii->name);
}
}
#endif
#if PPC_PROFILE_REGS_USE
printf("\n### Statistics for register usage\n");
uint64 tot_reg_count = 0;
for (int i = 0; i < 32; i++)
tot_reg_count += reginfo[i].count;
qsort(reginfo, 32, sizeof(register_info), register_info_compare);
uint64 cum_reg_count = 0;
for (int i = 0; i < 32; i++) {
cum_reg_count += reginfo[i].count;
printf("r%-2d : %16llu %2.1f%% [%3.1f%%]\n",
reginfo[i].id, reginfo[i].count,
100.0*double(reginfo[i].count)/double(tot_reg_count),
100.0*double(cum_reg_count)/double(tot_reg_count));
}
delete[] reginfo;
#endif
kill_decode_cache();
#if ENABLE_MON
mon_exit();
#endif
}
void powerpc_cpu::dump_registers()
{
fprintf(stderr, " r0 %08x r1 %08x r2 %08x r3 %08x\n", gpr(0), gpr(1), gpr(2), gpr(3));
fprintf(stderr, " r4 %08x r5 %08x r6 %08x r7 %08x\n", gpr(4), gpr(5), gpr(6), gpr(7));
fprintf(stderr, " r8 %08x r9 %08x r10 %08x r11 %08x\n", gpr(8), gpr(9), gpr(10), gpr(11));
fprintf(stderr, "r12 %08x r13 %08x r14 %08x r15 %08x\n", gpr(12), gpr(13), gpr(14), gpr(15));
fprintf(stderr, "r16 %08x r17 %08x r18 %08x r19 %08x\n", gpr(16), gpr(17), gpr(18), gpr(19));
fprintf(stderr, "r20 %08x r21 %08x r22 %08x r23 %08x\n", gpr(20), gpr(21), gpr(22), gpr(23));
fprintf(stderr, "r24 %08x r25 %08x r26 %08x r27 %08x\n", gpr(24), gpr(25), gpr(26), gpr(27));
fprintf(stderr, "r28 %08x r29 %08x r30 %08x r31 %08x\n", gpr(28), gpr(29), gpr(30), gpr(31));
fprintf(stderr, " f0 %02.5f f1 %02.5f f2 %02.5f f3 %02.5f\n", fpr(0), fpr(1), fpr(2), fpr(3));
fprintf(stderr, " f4 %02.5f f5 %02.5f f6 %02.5f f7 %02.5f\n", fpr(4), fpr(5), fpr(6), fpr(7));
fprintf(stderr, " f8 %02.5f f9 %02.5f f10 %02.5f f11 %02.5f\n", fpr(8), fpr(9), fpr(10), fpr(11));
fprintf(stderr, "f12 %02.5f f13 %02.5f f14 %02.5f f15 %02.5f\n", fpr(12), fpr(13), fpr(14), fpr(15));
fprintf(stderr, "f16 %02.5f f17 %02.5f f18 %02.5f f19 %02.5f\n", fpr(16), fpr(17), fpr(18), fpr(19));
fprintf(stderr, "f20 %02.5f f21 %02.5f f22 %02.5f f23 %02.5f\n", fpr(20), fpr(21), fpr(22), fpr(23));
fprintf(stderr, "f24 %02.5f f25 %02.5f f26 %02.5f f27 %02.5f\n", fpr(24), fpr(25), fpr(26), fpr(27));
fprintf(stderr, "f28 %02.5f f29 %02.5f f30 %02.5f f31 %02.5f\n", fpr(28), fpr(29), fpr(30), fpr(31));
fprintf(stderr, " lr %08x ctr %08x cr %08x xer %08x\n", lr(), ctr(), cr().get(), xer().get());
fprintf(stderr, " pc %08x fpscr %08x\n", pc(), fpscr());
fflush(stderr);
}
void powerpc_cpu::dump_instruction(uint32 opcode)
{
fprintf(stderr, "[%08x]-> %08x\n", pc(), opcode);
}
void powerpc_cpu::fake_dump_registers(uint32)
{
dump_registers();
}
void powerpc_registers::interrupt_copy(powerpc_registers &oregs, powerpc_registers const &iregs)
{
for (int i = 0; i < 32; i++) {
oregs.gpr[i] = iregs.gpr[i];
oregs.fpr[i] = iregs.fpr[i];
}
oregs.cr = iregs.cr;
oregs.fpscr = iregs.fpscr;
oregs.xer = iregs.xer;
oregs.lr = iregs.lr;
oregs.ctr = iregs.ctr;
oregs.pc = iregs.pc;
uint32 vrsave = iregs.vrsave;
oregs.vrsave = vrsave;
if (vrsave) {
for (int i = 31; i >= 0; i--) {
if (vrsave & 1)
oregs.vr[i] = iregs.vr[i];
vrsave >>= 1;
}
}
}
bool powerpc_cpu::check_spcflags()
{
if (spcflags().test(SPCFLAG_CPU_EXEC_RETURN)) {
spcflags().clear(SPCFLAG_CPU_EXEC_RETURN);
return false;
}
#ifdef SHEEPSHAVER
if (spcflags().test(SPCFLAG_CPU_HANDLE_INTERRUPT)) {
spcflags().clear(SPCFLAG_CPU_HANDLE_INTERRUPT);
static bool processing_interrupt = false;
if (!processing_interrupt) {
processing_interrupt = true;
powerpc_registers r;
powerpc_registers::interrupt_copy(r, regs());
HandleInterrupt(&r);
powerpc_registers::interrupt_copy(regs(), r);
processing_interrupt = false;
}
}
if (spcflags().test(SPCFLAG_CPU_TRIGGER_INTERRUPT)) {
spcflags().clear(SPCFLAG_CPU_TRIGGER_INTERRUPT);
spcflags().set(SPCFLAG_CPU_HANDLE_INTERRUPT);
}
#endif
if (spcflags().test(SPCFLAG_CPU_ENTER_MON)) {
spcflags().clear(SPCFLAG_CPU_ENTER_MON);
#if ENABLE_MON
// Start up mon in real-mode
const char *arg[] = {
"mon",
#ifdef SHEEPSHAVER
"-m",
#endif
"-r",
NULL
};
mon(sizeof(arg)/sizeof(arg[0]) - 1, arg);
#endif
}
return true;
}
#if DYNGEN_DIRECT_BLOCK_CHAINING
void *powerpc_cpu::compile_chain_block(block_info *sbi)
{
// Block index is stuffed into the source basic block pointer,
// which is aligned at least on 4-byte boundaries
const int n = ((uintptr)sbi) & 3;
sbi = (block_info *)(((uintptr)sbi) & ~3L);
const uint32 tpc = sbi->li[n].jmp_pc;
block_info *tbi = my_block_cache.find(tpc);
if (tbi == NULL)
tbi = compile_block(tpc);
assert(tbi && tbi->pc == tpc);
dg_set_jmp_target(sbi->li[n].jmp_addr, tbi->entry_point);
return tbi->entry_point;
}
#endif
void powerpc_cpu::execute(uint32 entry)
{
bool invalidated_cache = false;
pc() = entry;
#if PPC_EXECUTE_DUMP_STATE
const bool dump_state = true;
#endif
execute_depth++;
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
if (execute_depth == 1 || (PPC_ENABLE_JIT && PPC_REENTRANT_JIT)) {
#if PPC_ENABLE_JIT
if (use_jit) {
block_info *bi = my_block_cache.find(pc());
if (bi == NULL)
bi = compile_block(pc());
for (;;) {
// Execute all cached blocks
for (;;) {
codegen.execute(bi->entry_point);
if (!spcflags().empty()) {
if (!check_spcflags())
goto return_site;
// Force redecoding if cache was invalidated
if (spcflags().test(SPCFLAG_JIT_EXEC_RETURN)) {
spcflags().clear(SPCFLAG_JIT_EXEC_RETURN);
invalidated_cache = true;
break;
}
}
// Don't check for backward branches here as this
// is now done by generated code. Besides, we will
// get here if the fast cache lookup failed too.
if ((bi = my_block_cache.find(pc())) == NULL)
break;
}
// Compile new block
bi = compile_block(pc());
}
}
#endif
#if PPC_DECODE_CACHE
block_info *bi = my_block_cache.find(pc());
if (bi != NULL)
goto pdi_execute;
for (;;) {
#if PPC_PROFILE_COMPILE_TIME
compile_count++;
clock_t start_time;
start_time = clock();
#endif
bi = my_block_cache.new_blockinfo();
bi->init(pc());
// Predecode a new block
block_info::decode_info *di;
const instr_info_t *ii;
uint32 dpc;
di = bi->di = decode_cache_p;
dpc = pc() - 4;
do {
uint32 opcode = vm_read_memory_4(dpc += 4);
ii = decode(opcode);
#if PPC_EXECUTE_DUMP_STATE
if (dump_state) {
di->opcode = opcode;
di->execute = nv_mem_fun(&powerpc_cpu::dump_instruction);
di++;
}
#endif
#if PPC_FLIGHT_RECORDER
if (is_logging()) {
di->opcode = opcode;
di->execute = nv_mem_fun(&powerpc_cpu::record_step);
di++;
}
#endif
di->opcode = opcode;
di->execute = ii->execute;
di++;
#if PPC_EXECUTE_DUMP_STATE
if (dump_state) {
di->opcode = 0;
di->execute = nv_mem_fun(&powerpc_cpu::fake_dump_registers);
di++;
}
#endif
if (di >= decode_cache_end_p) {
// Invalidate cache and move current code to start
invalidate_cache();
const int blocklen = di - bi->di;
memmove(decode_cache_p, bi->di, blocklen * sizeof(*di));
bi->di = decode_cache_p;
di = bi->di + blocklen;
}
} while ((ii->cflow & CFLOW_END_BLOCK) == 0);
bi->end_pc = dpc;
bi->min_pc = dpc;
bi->max_pc = entry;
bi->size = di - bi->di;
my_block_cache.add_to_cl_list(bi);
my_block_cache.add_to_active_list(bi);
decode_cache_p += bi->size;
#if PPC_PROFILE_COMPILE_TIME
compile_time += (clock() - start_time);
#endif
// Execute all cached blocks
pdi_execute:
for (;;) {
const int r = bi->size % 4;
di = bi->di + r;
int n = (bi->size + 3) / 4;
switch (r) {
case 0: do {
di += 4;
di[-4].execute(this, di[-4].opcode);
case 3: di[-3].execute(this, di[-3].opcode);
case 2: di[-2].execute(this, di[-2].opcode);
case 1: di[-1].execute(this, di[-1].opcode);
} while (--n > 0);
}
if (!spcflags().empty()) {
if (!check_spcflags())
goto return_site;
// Force redecoding if cache was invalidated
if (spcflags().test(SPCFLAG_JIT_EXEC_RETURN)) {
spcflags().clear(SPCFLAG_JIT_EXEC_RETURN);
invalidated_cache = true;
break;
}
}
if ((bi->pc != pc()) && ((bi = my_block_cache.find(pc())) == NULL))
break;
}
}
#else
goto do_interpret;
#endif
}
#endif
do_interpret:
for (;;) {
uint32 opcode = vm_read_memory_4(pc());
const instr_info_t *ii = decode(opcode);
#if PPC_EXECUTE_DUMP_STATE
if (dump_state)
dump_instruction(opcode);
#endif
#if PPC_FLIGHT_RECORDER
if (is_logging())
record_step(opcode);
#endif
assert(ii->execute.ptr() != 0);
ii->execute(this, opcode);
#if PPC_EXECUTE_DUMP_STATE
if (dump_state)
dump_registers();
#endif
if (!spcflags().empty() && !check_spcflags())
goto return_site;
}
return_site:
// Tell upper level we invalidated cache?
if (invalidated_cache)
spcflags().set(SPCFLAG_JIT_EXEC_RETURN);
--execute_depth;
}
void powerpc_cpu::execute()
{
execute(pc());
}
void powerpc_cpu::init_decode_cache()
{
#if PPC_DECODE_CACHE
decode_cache = (block_info::decode_info *)vm_acquire(DECODE_CACHE_SIZE);
if (decode_cache == VM_MAP_FAILED) {
fprintf(stderr, "powerpc_cpu: Could not allocate decode cache\n");
abort();
}
D(bug("powerpc_cpu: Allocated decode cache: %d KB at %p\n", DECODE_CACHE_SIZE / 1024, decode_cache));
decode_cache_p = decode_cache;
decode_cache_end_p = decode_cache + DECODE_CACHE_MAX_ENTRIES;
#if FLIGHT_RECORDER
// Leave enough room to last call to record_step()
decode_cache_end_p -= 2;
#endif
#if PPC_EXECUTE_DUMP_STATE
// Leave enough room to last calls to dump state functions
decode_cache_end_p -= 2;
#endif
#endif
}
void powerpc_cpu::kill_decode_cache()
{
#if PPC_DECODE_CACHE
vm_release(decode_cache, DECODE_CACHE_SIZE);
#endif
}
void powerpc_cpu::invalidate_cache()
{
D(bug("Invalidate all cache blocks\n"));
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
my_block_cache.clear();
my_block_cache.initialize();
spcflags().set(SPCFLAG_JIT_EXEC_RETURN);
#endif
#if PPC_ENABLE_JIT
codegen.invalidate_cache();
#endif
#if PPC_DECODE_CACHE
decode_cache_p = decode_cache;
#endif
}
void powerpc_block_info::invalidate()
{
#if PPC_DECODE_CACHE
// Don't do anything if this is a predecoded block
if (di)
return;
#endif
#if DYNGEN_DIRECT_BLOCK_CHAINING
for (int i = 0; i < MAX_TARGETS; i++) {
link_info * const tli = &li[i];
uint32 tpc = tli->jmp_pc;
// For any jump within page boundaries, reset the jump address
// to the target block resolver (trampoline)
if (tpc != INVALID_PC && ((tpc ^ pc) >> 12) == 0)
dg_set_jmp_target(tli->jmp_addr, tli->jmp_resolve_addr);
}
#endif
}
void powerpc_cpu::invalidate_cache_range(uintptr start, uintptr end)
{
D(bug("Invalidate cache block [%08x - %08x]\n", start, end));
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
#if DYNGEN_DIRECT_BLOCK_CHAINING
if (use_jit) {
// Invalidate on page boundaries
start &= -4096;
end = (end + 4095) & -4096;
D(bug(" at page boundaries [%08x - %08x]\n", start, end));
}
#endif
spcflags().set(SPCFLAG_JIT_EXEC_RETURN);
my_block_cache.clear_range(start, end);
#endif
}