X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fmupen64plus-core%2Fsrc%2Fr4300%2Fx86_64%2Fregcache.c;fp=source%2Fmupen64plus-core%2Fsrc%2Fr4300%2Fx86_64%2Fregcache.c;h=34cd3296667f22d369c1b72a0a955456e0be20a9;hb=451ab91e3827a6384981b3300e2a7000d2eaba58;hp=0000000000000000000000000000000000000000;hpb=a2ab25365b5b0dddbee476d695d8a31151407581;p=mupen64plus-pandora.git diff --git a/source/mupen64plus-core/src/r4300/x86_64/regcache.c b/source/mupen64plus-core/src/r4300/x86_64/regcache.c new file mode 100644 index 0000000..34cd329 --- /dev/null +++ b/source/mupen64plus-core/src/r4300/x86_64/regcache.c @@ -0,0 +1,657 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - regcache.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2007 Richard Goedeken (Richard42) * + * Copyright (C) 2002 Hacktarux * + * * + * 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., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include + +#include "regcache.h" + +#include "api/m64p_types.h" +#include "api/callbacks.h" +#include "r4300/recomp.h" +#include "r4300/r4300.h" +#include "r4300/recomph.h" + +static unsigned long long * reg_content[8]; +static precomp_instr* last_access[8]; +static precomp_instr* free_since[8]; +static int dirty[8]; +static int is64bits[8]; +static unsigned long long *r0; + +void init_cache(precomp_instr* start) +{ + int i; + for (i=0; i<8; i++) + { + reg_content[i] = NULL; + last_access[i] = NULL; + free_since[i] = start; + dirty[i] = 0; + is64bits[i] = 0; + } + r0 = (unsigned long long *) reg; +} + +void free_all_registers(void) +{ +#if defined(PROFILE_R4300) + int freestart = code_length; + int flushed = 0; +#endif + + int i; + for (i=0; i<8; i++) + { +#if defined(PROFILE_R4300) + if (last_access[i] && dirty[i]) flushed = 1; +#endif + if (last_access[i]) + { + free_register(i); + } + else + { + while (free_since[i] <= dst) + { + free_since[i]->reg_cache_infos.needed_registers[i] = NULL; + free_since[i]++; + } + } + } + +#if defined(PROFILE_R4300) + if (flushed == 1) + { + long long x86addr = (long long) ((*inst_pointer) + freestart); + int mipsop = -5; + if (fwrite(&mipsop, 1, 4, pfProfile) != 4 || /* -5 = regcache flushing */ + fwrite(&x86addr, 1, sizeof(char *), pfProfile) != sizeof(char *)) // write pointer to start of register cache flushing instructions + DebugMessage(M64MSG_ERROR, "Error writing R4300 instruction address profiling data"); + x86addr = (long long) ((*inst_pointer) + code_length); + if (fwrite(&src, 1, 4, pfProfile) != 4 || // write 4-byte MIPS opcode for current instruction + fwrite(&x86addr, 1, sizeof(char *), pfProfile) != sizeof(char *)) // write pointer to dynamically generated x86 code for this MIPS instruction + DebugMessage(M64MSG_ERROR, "Error writing R4300 instruction address profiling data"); + } +#endif +} + +static void simplify_access(void) +{ + int i; + dst->local_addr = code_length; + for(i=0; i<8; i++) dst->reg_cache_infos.needed_registers[i] = NULL; +} + +void free_registers_move_start(void) +{ + /* flush all dirty registers and clear needed_registers table */ + free_all_registers(); + + /* now move the start of the new instruction down past the flushing instructions */ + simplify_access(); + +} + +// this function frees a specific X86 GPR +void free_register(int reg) +{ + precomp_instr *last; + + if (last_access[reg] != NULL) + last = last_access[reg]+1; + else + last = free_since[reg]; + + while (last <= dst) + { + if (last_access[reg] != NULL && dirty[reg]) + last->reg_cache_infos.needed_registers[reg] = reg_content[reg]; + else + last->reg_cache_infos.needed_registers[reg] = NULL; + last++; + } + if (last_access[reg] == NULL) + { + free_since[reg] = dst+1; + return; + } + + if (dirty[reg]) + { + if (is64bits[reg]) + { + mov_m64rel_xreg64((unsigned long long *) reg_content[reg], reg); + } + else + { + movsxd_reg64_reg32(reg, reg); + mov_m64rel_xreg64((unsigned long long *) reg_content[reg], reg); + } + } + + last_access[reg] = NULL; + free_since[reg] = dst+1; +} + +int lru_register(void) +{ + unsigned long long oldest_access = 0xFFFFFFFFFFFFFFFFULL; + int i, reg = 0; + for (i=0; i<8; i++) + { + if (i != ESP && (unsigned long long) last_access[i] < oldest_access) + { + oldest_access = (unsigned long long) last_access[i]; + reg = i; + } + } + return reg; +} + +int lru_base_register(void) /* EBP cannot be used as a base register for SIB addressing byte */ +{ + unsigned long long oldest_access = 0xFFFFFFFFFFFFFFFFULL; + int i, reg = 0; + for (i=0; i<8; i++) + { + if (i != ESP && i != EBP && (unsigned long long) last_access[i] < oldest_access) + { + oldest_access = (unsigned long long) last_access[i]; + reg = i; + } + } + return reg; +} + +void set_register_state(int reg, unsigned int *addr, int _dirty, int _is64bits) +{ + if (addr == NULL) + last_access[reg] = NULL; + else + last_access[reg] = dst; + reg_content[reg] = (unsigned long long *) addr; + is64bits[reg] = _is64bits; + dirty[reg] = _dirty; +} + +int lock_register(int reg) +{ + free_register(reg); + last_access[reg] = (precomp_instr *) 0xFFFFFFFFFFFFFFFFULL; + reg_content[reg] = NULL; + return reg; +} + +void unlock_register(int reg) +{ + last_access[reg] = NULL; +} + +// this function finds a register to put the data contained in addr, +// if there was another value before it's cleanly removed of the +// register cache. After that, the register number is returned. +// If data are already cached, the function only returns the register number +int allocate_register_32(unsigned int *addr) +{ + int reg = 0, i; + + // is it already cached ? + if (addr != NULL) + { + for (i = 0; i < 8; i++) + { + if (last_access[i] != NULL && (unsigned int *) reg_content[i] == addr) + { + precomp_instr *last = last_access[i]+1; + + while (last <= dst) + { + last->reg_cache_infos.needed_registers[i] = reg_content[i]; + last++; + } + last_access[i] = dst; + is64bits[i] = 0; + return i; + } + } + } + + // it's not cached, so take the least recently used register + reg = lru_register(); + + if (last_access[reg]) + free_register(reg); + else + { + while (free_since[reg] <= dst) + { + free_since[reg]->reg_cache_infos.needed_registers[reg] = NULL; + free_since[reg]++; + } + } + + last_access[reg] = dst; + reg_content[reg] = (unsigned long long *) addr; + dirty[reg] = 0; + is64bits[reg] = 0; + + if (addr != NULL) + { + if (addr == (unsigned int *) r0) + xor_reg32_reg32(reg, reg); + else + mov_xreg32_m32rel(reg, addr); + } + + return reg; +} + +// this function is similar to allocate_register except it loads +// a 64 bits value, and return the register number of the LSB part +int allocate_register_64(unsigned long long *addr) +{ + int reg, i; + + // is it already cached? + if (addr != NULL) + { + for (i = 0; i < 8; i++) + { + if (last_access[i] != NULL && reg_content[i] == addr) + { + precomp_instr *last = last_access[i]+1; + + while (last <= dst) + { + last->reg_cache_infos.needed_registers[i] = reg_content[i]; + last++; + } + last_access[i] = dst; + if (is64bits[i] == 0) + { + movsxd_reg64_reg32(i, i); + is64bits[i] = 1; + } + return i; + } + } + } + + // it's not cached, so take the least recently used register + reg = lru_register(); + + if (last_access[reg]) + free_register(reg); + else + { + while (free_since[reg] <= dst) + { + free_since[reg]->reg_cache_infos.needed_registers[reg] = NULL; + free_since[reg]++; + } + } + + last_access[reg] = dst; + reg_content[reg] = addr; + dirty[reg] = 0; + is64bits[reg] = 1; + + if (addr != NULL) + { + if (addr == r0) + xor_reg64_reg64(reg, reg); + else + mov_xreg64_m64rel(reg, addr); + } + + return reg; +} + +// this function checks if the data located at addr are cached in a register +// and then, it returns 1 if it's a 64 bit value +// 0 if it's a 32 bit value +// -1 if it's not cached +int is64(unsigned int *addr) +{ + int i; + for (i = 0; i < 8; i++) + { + if (last_access[i] != NULL && reg_content[i] == (unsigned long long *) addr) + { + return is64bits[i]; + } + } + return -1; +} + +int allocate_register_32_w(unsigned int *addr) +{ + int reg = 0, i; + + // is it already cached ? + for (i = 0; i < 8; i++) + { + if (last_access[i] != NULL && reg_content[i] == (unsigned long long *) addr) + { + precomp_instr *last = last_access[i] + 1; + + while (last <= dst) + { + last->reg_cache_infos.needed_registers[i] = NULL; + last++; + } + last_access[i] = dst; + dirty[i] = 1; + is64bits[i] = 0; + return i; + } + } + + // it's not cached, so take the least recently used register + reg = lru_register(); + + if (last_access[reg]) + free_register(reg); + else + { + while (free_since[reg] <= dst) + { + free_since[reg]->reg_cache_infos.needed_registers[reg] = NULL; + free_since[reg]++; + } + } + + last_access[reg] = dst; + reg_content[reg] = (unsigned long long *) addr; + dirty[reg] = 1; + is64bits[reg] = 0; + + return reg; +} + +int allocate_register_64_w(unsigned long long *addr) +{ + int reg, i; + + // is it already cached? + for (i = 0; i < 8; i++) + { + if (last_access[i] != NULL && reg_content[i] == addr) + { + precomp_instr *last = last_access[i] + 1; + + while (last <= dst) + { + last->reg_cache_infos.needed_registers[i] = NULL; + last++; + } + last_access[i] = dst; + is64bits[i] = 1; + dirty[i] = 1; + return i; + } + } + + // it's not cached, so take the least recently used register + reg = lru_register(); + + if (last_access[reg]) + free_register(reg); + else + { + while (free_since[reg] <= dst) + { + free_since[reg]->reg_cache_infos.needed_registers[reg] = NULL; + free_since[reg]++; + } + } + + last_access[reg] = dst; + reg_content[reg] = addr; + dirty[reg] = 1; + is64bits[reg] = 1; + + return reg; +} + +void allocate_register_32_manually(int reg, unsigned int *addr) +{ + int i; + + /* check if we just happen to already have this r4300 reg cached in the requested x86 reg */ + if (last_access[reg] != NULL && reg_content[reg] == (unsigned long long *) addr) + { + precomp_instr *last = last_access[reg] + 1; + while (last <= dst) + { + last->reg_cache_infos.needed_registers[reg] = reg_content[reg]; + last++; + } + last_access[reg] = dst; + /* we won't touch is64bits or dirty; the register returned is "read-only" */ + return; + } + + /* otherwise free up the requested x86 register */ + if (last_access[reg]) + free_register(reg); + else + { + while (free_since[reg] <= dst) + { + free_since[reg]->reg_cache_infos.needed_registers[reg] = NULL; + free_since[reg]++; + } + } + + /* if the r4300 register is already cached in a different x86 register, then copy it to the requested x86 register */ + for (i=0; i<8; i++) + { + if (last_access[i] != NULL && reg_content[i] == (unsigned long long *) addr) + { + precomp_instr *last = last_access[i]+1; + while (last <= dst) + { + last->reg_cache_infos.needed_registers[i] = reg_content[i]; + last++; + } + last_access[i] = dst; + if (is64bits[i]) + mov_reg64_reg64(reg, i); + else + mov_reg32_reg32(reg, i); + last_access[reg] = dst; + is64bits[reg] = is64bits[i]; + dirty[reg] = dirty[i]; + reg_content[reg] = reg_content[i]; + /* free the previous x86 register used to cache this r4300 register */ + free_since[i] = dst + 1; + last_access[i] = NULL; + return; + } + } + + /* otherwise just load the 32-bit value into the requested register */ + last_access[reg] = dst; + reg_content[reg] = (unsigned long long *) addr; + dirty[reg] = 0; + is64bits[reg] = 0; + + if ((unsigned long long *) addr == r0) + xor_reg32_reg32(reg, reg); + else + mov_xreg32_m32rel(reg, addr); +} + +void allocate_register_32_manually_w(int reg, unsigned int *addr) +{ + int i; + + /* check if we just happen to already have this r4300 reg cached in the requested x86 reg */ + if (last_access[reg] != NULL && reg_content[reg] == (unsigned long long *) addr) + { + precomp_instr *last = last_access[reg]+1; + while (last <= dst) + { + last->reg_cache_infos.needed_registers[reg] = NULL; + last++; + } + last_access[reg] = dst; + is64bits[reg] = 0; + dirty[reg] = 1; + return; + } + + /* otherwise free up the requested x86 register */ + if (last_access[reg]) + free_register(reg); + else + { + while (free_since[reg] <= dst) + { + free_since[reg]->reg_cache_infos.needed_registers[reg] = NULL; + free_since[reg]++; + } + } + + /* if the r4300 register is already cached in a different x86 register, then free it and bind to the requested x86 register */ + for (i = 0; i < 8; i++) + { + if (last_access[i] != NULL && reg_content[i] == (unsigned long long *) addr) + { + precomp_instr *last = last_access[i] + 1; + while (last <= dst) + { + last->reg_cache_infos.needed_registers[i] = NULL; + last++; + } + last_access[reg] = dst; + reg_content[reg] = reg_content[i]; + dirty[reg] = 1; + is64bits[reg] = 0; + /* free the previous x86 register used to cache this r4300 register */ + free_since[i] = dst+1; + last_access[i] = NULL; + return; + } + } + + /* otherwise just set up the requested register as 32-bit */ + last_access[reg] = dst; + reg_content[reg] = (unsigned long long *) addr; + dirty[reg] = 1; + is64bits[reg] = 0; +} + + +// 0x48 0x83 0xEC 0x8 sub rsp, byte 8 +// 0x48 0xA1 0xXXXXXXXXXXXXXXXX mov rax, qword (&code start) +// 0x48 0x05 0xXXXXXXXX add rax, dword (local_addr) +// 0x48 0x89 0x04 0x24 mov [rsp], rax +// 0x48 0xB8 0xXXXXXXXXXXXXXXXX mov rax, ®[0] +// 0x48 0x8B (reg<<3)|0x80 0xXXXXXXXX mov rdi, [rax + XXXXXXXX] +// 0x48 0x8B (reg<<3)|0x80 0xXXXXXXXX mov rsi, [rax + XXXXXXXX] +// 0x48 0x8B (reg<<3)|0x80 0xXXXXXXXX mov rbp, [rax + XXXXXXXX] +// 0x48 0x8B (reg<<3)|0x80 0xXXXXXXXX mov rdx, [rax + XXXXXXXX] +// 0x48 0x8B (reg<<3)|0x80 0xXXXXXXXX mov rcx, [rax + XXXXXXXX] +// 0x48 0x8B (reg<<3)|0x80 0xXXXXXXXX mov rbx, [rax + XXXXXXXX] +// 0x48 0x8B (reg<<3)|0x80 0xXXXXXXXX mov rax, [rax + XXXXXXXX] +// 0xC3 ret +// total : 84 bytes + +static void build_wrapper(precomp_instr *instr, unsigned char* pCode, precomp_block* block) +{ + int i; + +#if defined(PROFILE_R4300) + long long x86addr = (long long) pCode; + int mipsop = -4; + if (fwrite(&mipsop, 1, 4, pfProfile) != 4 || // write 4-byte MIPS opcode + fwrite(&x86addr, 1, sizeof(char *), pfProfile) != sizeof(char *)) // write pointer to dynamically generated x86 code for this MIPS instruction + DebugMessage(M64MSG_ERROR, "Error writing R4300 instruction address profiling data"); +#endif + + *pCode++ = 0x48; + *pCode++ = 0x83; + *pCode++ = 0xEC; + *pCode++ = 0x08; + + *pCode++ = 0x48; + *pCode++ = 0xA1; + *((unsigned long long *) pCode) = (unsigned long long) (&block->code); + pCode += 8; + + *pCode++ = 0x48; + *pCode++ = 0x05; + *((unsigned int *) pCode) = (unsigned int) instr->local_addr; + pCode += 4; + + *pCode++ = 0x48; + *pCode++ = 0x89; + *pCode++ = 0x04; + *pCode++ = 0x24; + + *pCode++ = 0x48; + *pCode++ = 0xB8; + *((unsigned long long *) pCode) = (unsigned long long) ®[0]; + pCode += 8; + + for (i=7; i>=0; i--) + { + long long riprel; + if (instr->reg_cache_infos.needed_registers[i] != NULL) + { + *pCode++ = 0x48; + *pCode++ = 0x8B; + *pCode++ = 0x80 | (i << 3); + riprel = (long long) ((unsigned char *) instr->reg_cache_infos.needed_registers[i] - (unsigned char *) ®[0]); + *((int *) pCode) = (int) riprel; + pCode += 4; + if (riprel >= 0x7fffffffLL || riprel < -0x80000000LL) + { + DebugMessage(M64MSG_ERROR, "build_wrapper error: reg[%i] offset too big for relative address from %p to %p", + i, (®[0]), instr->reg_cache_infos.needed_registers[i]); + asm(" int $3; "); + } + } + } + *pCode++ = 0xC3; +} + +void build_wrappers(precomp_instr *instr, int start, int end, precomp_block* block) +{ + int i, reg; + for (i=start; i