Lots of work on JIT (Implemented "unlimited instruction cache" CPU emulation. Works in JIT and JIT IL). Implemented correct PowerPC instruction cache (IC) emulation in the interpreter mode (including HID0 cache management stuff). Removed the "dcbi->dcbf" hack (again... this time it shouldn't break anything ;) )
Fixes issue 917. Fixes issue 1183. Fixes issue 1190. (those are really need to be verified) Maybe fixes something else. Maybe breaks something. I can't test everything, so please report if you find out something. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4357 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
98e1dd6dc4
commit
b843378636
|
@ -963,6 +963,14 @@
|
|||
RelativePath=".\Src\PowerPC\PPCAnalyst.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\PowerPC\PPCCache.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\PowerPC\PPCCache.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\PowerPC\PPCSymbolDB.cpp"
|
||||
>
|
||||
|
|
|
@ -529,16 +529,81 @@ bool AreMemoryBreakpointsActivated()
|
|||
u32 Read_Instruction(const u32 em_address)
|
||||
{
|
||||
UGeckoInstruction inst = ReadUnchecked_U32(em_address);
|
||||
if (inst.OPCD == 0)
|
||||
inst.hex = jit.GetBlockCache()->GetOriginalCode(em_address);
|
||||
if (inst.OPCD == 1)
|
||||
return HLE::GetOrigInstruction(em_address);
|
||||
else
|
||||
return inst.hex;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT(const u32 _Address)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
//return Memory::ReadUnchecked_U32(_Address);
|
||||
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000)
|
||||
{
|
||||
PanicAlert("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
return 0;
|
||||
}
|
||||
u8* iCache = jit.GetBlockCache()->GetICache();
|
||||
u32 addr = _Address & JIT_ICACHE_MASK;
|
||||
jit.GetBlockCache()->GetICache();
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
{
|
||||
u32 block_start = addr & ~0x1f;
|
||||
u8 *pMem = Memory::GetPointer(block_start);
|
||||
memcpy(iCache + block_start, pMem, 32);
|
||||
inst = *(u32*)(iCache + addr);
|
||||
}
|
||||
inst = Common::swap32(inst);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit.GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
//PanicAlert("Read from %x. res = %x. mem=%x", _Address, inst, Memory::Read_U32(_Address));
|
||||
return inst;
|
||||
}
|
||||
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
//return Memory::ReadUnchecked_U32(_Address);
|
||||
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000)
|
||||
{
|
||||
PanicAlert("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
|
||||
return 0;
|
||||
}
|
||||
u8* iCache = jit.GetBlockCache()->GetICache();
|
||||
u32 addr = _Address & JIT_ICACHE_MASK;
|
||||
jit.GetBlockCache()->GetICache();
|
||||
u32 inst = *(u32*)(iCache + addr);
|
||||
if (inst == JIT_ICACHE_INVALID_WORD)
|
||||
inst = Memory::ReadUnchecked_U32(_Address);
|
||||
else
|
||||
inst = Common::swap32(inst);
|
||||
#else
|
||||
u32 inst = Memory::ReadUnchecked_U32(_Address);
|
||||
#endif
|
||||
if ((inst & 0xfc000000) == 0)
|
||||
{
|
||||
inst = jit.GetBlockCache()->GetOriginalFirstOp(inst);
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
// WARNING! No checks!
|
||||
// We assume that _Address is cached
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
*(u32*)(jit.GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
|
||||
#else
|
||||
Memory::WriteUnchecked_U32(_Value, _Address);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
|
|
|
@ -90,7 +90,6 @@ namespace Memory
|
|||
void InitHWMemFuncs();
|
||||
void InitHWMemFuncsWii();
|
||||
|
||||
u32 Read_Instruction(const u32 _Address);
|
||||
bool IsRAMAddress(const u32 addr, bool allow_locked_cache = false);
|
||||
writeFn32 GetHWWriteFun32(const u32 _Address);
|
||||
|
||||
|
@ -105,7 +104,17 @@ namespace Memory
|
|||
#endif
|
||||
}
|
||||
|
||||
// used by interpreter to read instructions, uses iCache
|
||||
u32 Read_Opcode(const u32 _Address);
|
||||
// used by JIT to read instructions, uses iCacheJIT
|
||||
u32 Read_Opcode_JIT(const u32 _Address);
|
||||
// used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode
|
||||
u32 Read_Opcode_JIT_LC(const u32 _Address);
|
||||
void Write_Opcode_JIT(const u32 _Address, const u32 _Value);
|
||||
// this is used by Debugger a lot.
|
||||
// For now, just reads from memory!
|
||||
u32 Read_Instruction(const u32 _Address);
|
||||
|
||||
|
||||
// For use by emulator
|
||||
|
||||
|
|
|
@ -294,9 +294,10 @@ u32 Read_Opcode(const u32 _Address)
|
|||
return 0x00000000;
|
||||
}
|
||||
|
||||
u32 _var = 0;
|
||||
/*u32 _var = 0;
|
||||
ReadFromHardware<u32>(_var, _Address, _Address, FLAG_OPCODE);
|
||||
return _var;
|
||||
return _var;*/
|
||||
return PowerPC::ppcState.iCache.ReadInstruction(_Address);
|
||||
}
|
||||
|
||||
u8 Read_U8(const u32 _Address)
|
||||
|
|
|
@ -391,6 +391,45 @@ union UReg_FPSCR
|
|||
UReg_FPSCR() { Hex = 0;}
|
||||
};
|
||||
|
||||
// Hardware Implementation-Dependent Register 0
|
||||
union UReg_HID0
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned NOOPTI : 1;
|
||||
unsigned : 1;
|
||||
unsigned BHT : 1;
|
||||
unsigned ABE : 1;
|
||||
unsigned : 1;
|
||||
unsigned BTIC : 1;
|
||||
unsigned DCFA : 1;
|
||||
unsigned SGE : 1;
|
||||
unsigned IFEM : 1;
|
||||
unsigned SPD : 1;
|
||||
unsigned DCFI : 1;
|
||||
unsigned ICFI : 1;
|
||||
unsigned DLOCK : 1;
|
||||
unsigned ILOCK : 1;
|
||||
unsigned DCE : 1;
|
||||
unsigned ICE : 1;
|
||||
unsigned NHR : 1;
|
||||
unsigned : 3;
|
||||
unsigned DPM : 1;
|
||||
unsigned SLEEP : 1;
|
||||
unsigned NAP : 1;
|
||||
unsigned DOZE : 1;
|
||||
unsigned PAR : 1;
|
||||
unsigned ECLK : 1;
|
||||
unsigned : 1;
|
||||
unsigned BCLK : 1;
|
||||
unsigned EBD : 1;
|
||||
unsigned EBA : 1;
|
||||
unsigned DBP : 1;
|
||||
unsigned EMCP : 1;
|
||||
};
|
||||
u32 Hex;
|
||||
};
|
||||
|
||||
// Hardware Implementation-Dependent Register 2
|
||||
union UReg_HID2
|
||||
{
|
||||
|
@ -590,6 +629,8 @@ enum
|
|||
SPR_DBAT3L = 542,
|
||||
SPR_DBAT3U = 543,
|
||||
SPR_GQR0 = 912,
|
||||
SPR_HID0 = 1008,
|
||||
SPR_HID1 = 1009,
|
||||
SPR_HID2 = 920,
|
||||
SPR_WPAR = 921,
|
||||
SPR_DMAU = 922,
|
||||
|
|
|
@ -317,14 +317,6 @@ void dcbtst(UGeckoInstruction _inst)
|
|||
|
||||
void dcbz(UGeckoInstruction _inst)
|
||||
{
|
||||
// hack to prevent clearing of memory cached in the CPU instruction cache
|
||||
// needed to run WiiWare games
|
||||
// 0x81330c2c
|
||||
u32 NextOpcode = Memory::Read_U32(PC+4);
|
||||
if (NextOpcode == 0x7C0400AC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// HACK but works... we think
|
||||
Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32);
|
||||
}
|
||||
|
@ -347,17 +339,8 @@ void eieio(UGeckoInstruction _inst)
|
|||
void icbi(UGeckoInstruction _inst)
|
||||
{
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
// block size seems to be 0x20
|
||||
address &= ~0x1f;
|
||||
|
||||
// this comment is slightly outdated but still relevant:
|
||||
// Inform the JIT to kill off this area of code NOW
|
||||
// VERY IMPORTANT when we start linking blocks
|
||||
// There are a TON of these so hopefully we can make this mechanism
|
||||
// fast in the JIT
|
||||
// ector said that this isn't needed anymore, and that making
|
||||
// a jit version of this instruction would be easy anyway
|
||||
//jit.GetBlockCache()->InvalidateCodeRange(address, 0x20);
|
||||
PowerPC::ppcState.iCache.Invalidate(address);
|
||||
jit.GetBlockCache()->InvalidateICache(address);
|
||||
}
|
||||
|
||||
void lbzux(UGeckoInstruction _inst)
|
||||
|
|
|
@ -318,6 +318,28 @@ void mtspr(UGeckoInstruction _inst)
|
|||
TU = m_GPR[_inst.RD];
|
||||
break;
|
||||
|
||||
case SPR_HID0: // HID0
|
||||
{
|
||||
UReg_HID0 old_hid0;
|
||||
old_hid0.Hex = oldValue;
|
||||
if (HID0.ICE != old_hid0.ICE)
|
||||
{
|
||||
NOTICE_LOG(POWERPC, "Instruction Cache Enable (HID0.ICE) = %d", (int)HID0.ICE);
|
||||
}
|
||||
if (HID0.ILOCK != old_hid0.ILOCK)
|
||||
{
|
||||
NOTICE_LOG(POWERPC, "Instruction Cache Lock (HID0.ILOCK) = %d", (int)HID0.ILOCK);
|
||||
}
|
||||
if (HID0.ICFI)
|
||||
{
|
||||
HID0.ICFI = 0;
|
||||
NOTICE_LOG(POWERPC, "Flush Instruction Cache! ICE=%d", (int)HID0.ICE);
|
||||
// this is rather slow
|
||||
// most games do it only once during initialization
|
||||
PowerPC::ppcState.iCache.Reset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SPR_HID2: // HID2
|
||||
{
|
||||
UReg_HID2 old_hid2;
|
||||
|
|
|
@ -280,7 +280,7 @@ static GekkoOPTemplate table31[] =
|
|||
|
||||
{4, Interpreter::tw, {"tw", OPTYPE_SYSTEM, 0, 1}},
|
||||
{598, Interpreter::sync, {"sync", OPTYPE_SYSTEM, 0, 2}},
|
||||
{982, Interpreter::icbi, {"icbi", OPTYPE_SYSTEM, 0, 3}},
|
||||
{982, Interpreter::icbi, {"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}},
|
||||
|
||||
// Unused instructions on GC
|
||||
{310, Interpreter::eciwx, {"eciwx", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
|
|
|
@ -27,15 +27,10 @@
|
|||
// * A flush simply does a conditional write to the appropriate CRx.
|
||||
// * If flag available, branch code can become absolutely trivial.
|
||||
|
||||
|
||||
|
||||
// Settings
|
||||
// ----------
|
||||
#define JIT_OFF_OPTIONS // Compile with JIT off options
|
||||
|
||||
|
||||
|
||||
|
||||
// Include
|
||||
// ----------
|
||||
#if JITTEST
|
||||
|
@ -55,9 +50,6 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// Declarations and definitions
|
||||
// ----------
|
||||
|
||||
|
@ -308,6 +300,8 @@ public:
|
|||
|
||||
void lmw(UGeckoInstruction inst);
|
||||
void stmw(UGeckoInstruction inst);
|
||||
|
||||
void icbi(UGeckoInstruction inst);
|
||||
};
|
||||
|
||||
extern Jit64 jit;
|
||||
|
|
|
@ -86,12 +86,23 @@ void AsmRoutineManager::Generate()
|
|||
dispatcherNoCheck = GetCodePtr();
|
||||
MOV(32, R(EAX), M(&PowerPC::ppcState.pc));
|
||||
dispatcherPcInEAX = GetCodePtr();
|
||||
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
AND(32, R(EAX), Imm32(JIT_ICACHE_MASK));
|
||||
#ifdef _M_IX86
|
||||
MOV(32, R(EAX), MDisp(EAX, (u32)jit.GetBlockCache()->GetICache()));
|
||||
#else
|
||||
MOV(64, R(RSI), Imm64((u64)jit.GetBlockCache()->GetICache()));
|
||||
MOV(32, R(EAX), MComplex(RSI, EAX, SCALE_1, 0));
|
||||
#endif
|
||||
#else
|
||||
#ifdef _M_IX86
|
||||
AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));
|
||||
MOV(32, R(EBX), Imm32((u32)Memory::base));
|
||||
MOV(32, R(EAX), MComplex(EBX, EAX, SCALE_1, 0));
|
||||
#else
|
||||
MOV(32, R(EAX), MComplex(RBX, RAX, SCALE_1, 0));
|
||||
#endif
|
||||
#endif
|
||||
TEST(32, R(EAX), Imm32(0xFC));
|
||||
FixupBranch notfound = J_CC(CC_NZ);
|
||||
|
|
|
@ -509,3 +509,9 @@ void Jit64::stmw(UGeckoInstruction inst)
|
|||
gpr.UnlockAllX();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Jit64::icbi(UGeckoInstruction inst)
|
||||
{
|
||||
Default(inst);
|
||||
WriteExit(js.compilerPC + 4, 0);
|
||||
}
|
|
@ -294,6 +294,8 @@ public:
|
|||
|
||||
void lmw(UGeckoInstruction inst);
|
||||
void stmw(UGeckoInstruction inst);
|
||||
|
||||
void icbi(UGeckoInstruction inst);
|
||||
};
|
||||
|
||||
extern Jit64 jit;
|
||||
|
@ -303,4 +305,3 @@ void Jit(u32 em_address);
|
|||
void ProfiledReJit();
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -88,6 +88,16 @@ void AsmRoutineManager::Generate()
|
|||
dispatcherNoCheck = GetCodePtr();
|
||||
MOV(32, R(EAX), M(&PowerPC::ppcState.pc));
|
||||
dispatcherPcInEAX = GetCodePtr();
|
||||
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
AND(32, R(EAX), Imm32(JIT_ICACHE_MASK));
|
||||
#ifdef _M_IX86
|
||||
MOV(32, R(EAX), MDisp(EAX, (u32)jit.GetBlockCache()->GetICache()));
|
||||
#else
|
||||
MOV(64, R(RSI), Imm64((u64)jit.GetBlockCache()->GetICache()));
|
||||
MOV(32, R(EAX), MComplex(RSI, EAX, SCALE_1, 0));
|
||||
#endif
|
||||
#else
|
||||
#ifdef _M_IX86
|
||||
AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));
|
||||
MOV(32, R(EBX), Imm32((u32)Memory::base));
|
||||
|
@ -95,6 +105,8 @@ void AsmRoutineManager::Generate()
|
|||
#else
|
||||
MOV(32, R(EAX), MComplex(RBX, RAX, SCALE_1, 0));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TEST(32, R(EAX), Imm32(0xFC));
|
||||
FixupBranch notfound = J_CC(CC_NZ);
|
||||
BSWAP(32, EAX);
|
||||
|
|
|
@ -196,3 +196,9 @@ void Jit64::stmw(UGeckoInstruction inst)
|
|||
addr = ibuild.EmitAdd(addr, ibuild.EmitIntConst(4));
|
||||
}
|
||||
}
|
||||
|
||||
void Jit64::icbi(UGeckoInstruction inst)
|
||||
{
|
||||
Default(inst);
|
||||
ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4));
|
||||
}
|
|
@ -60,7 +60,6 @@ using namespace Gen;
|
|||
|
||||
#define INVALID_EXIT 0xFFFFFFFF
|
||||
|
||||
|
||||
bool JitBlock::ContainsAddress(u32 em_address)
|
||||
{
|
||||
// WARNING - THIS DOES NOT WORK WITH INLINING ENABLED.
|
||||
|
@ -86,7 +85,21 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
#endif
|
||||
blocks = new JitBlock[MAX_NUM_BLOCKS];
|
||||
blockCodePointers = new const u8*[MAX_NUM_BLOCKS];
|
||||
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if (iCache == 0)
|
||||
{
|
||||
iCache = new u8[JIT_ICACHE_SIZE];
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("JitBlockCache::Init() - iCache is already initialized");
|
||||
}
|
||||
if (iCache == 0)
|
||||
{
|
||||
PanicAlert("JitBlockCache::Init() - unable to allocate iCache");
|
||||
}
|
||||
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
|
||||
#endif
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
@ -94,6 +107,11 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
{
|
||||
delete [] blocks;
|
||||
delete [] blockCodePointers;
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
if (iCache != 0)
|
||||
delete [] iCache;
|
||||
iCache = 0;
|
||||
#endif
|
||||
blocks = 0;
|
||||
blockCodePointers = 0;
|
||||
num_blocks = 0;
|
||||
|
@ -107,17 +125,17 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
void JitBlockCache::Clear()
|
||||
{
|
||||
Core::DisplayMessage("Cleared code cache.", 3000);
|
||||
// Is destroying the blocks really necessary?
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
DestroyBlock(i, false);
|
||||
}
|
||||
links_to.clear();
|
||||
block_map.clear();
|
||||
num_blocks = 0;
|
||||
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
|
||||
}
|
||||
|
||||
void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
|
||||
/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
|
@ -126,7 +144,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
DestroyBlock(i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void JitBlockCache::Reset()
|
||||
{
|
||||
|
@ -161,7 +179,6 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
JitBlock &b = blocks[num_blocks];
|
||||
b.invalid = false;
|
||||
b.originalAddress = em_address;
|
||||
b.originalFirstOpcode = Memory::ReadFast32(em_address);
|
||||
b.exitAddress[0] = INVALID_EXIT;
|
||||
b.exitAddress[1] = INVALID_EXIT;
|
||||
b.exitPtrs[0] = 0;
|
||||
|
@ -177,7 +194,9 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
{
|
||||
blockCodePointers[block_num] = code_ptr;
|
||||
JitBlock &b = blocks[block_num];
|
||||
Memory::WriteUnchecked_U32((JIT_OPCODE << 26) | block_num, blocks[block_num].originalAddress);
|
||||
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
|
||||
Memory::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
|
||||
block_map[std::make_pair(b.originalAddress + b.originalSize - 1, b.originalAddress)] = block_num;
|
||||
if (block_link)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
|
@ -204,51 +223,52 @@ bool JitBlock::ContainsAddress(u32 em_address)
|
|||
return blockCodePointers;
|
||||
}
|
||||
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
u8 *JitBlockCache::GetICache()
|
||||
{
|
||||
return iCache;
|
||||
}
|
||||
#endif
|
||||
|
||||
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
|
||||
{
|
||||
if (!blocks)
|
||||
return -1;
|
||||
u32 code = Memory::ReadFast32(addr);
|
||||
if ((code >> 26) == JIT_OPCODE)
|
||||
{
|
||||
// Jitted code.
|
||||
unsigned int block = code & 0x03FFFFFF;
|
||||
if (block >= (unsigned int)num_blocks) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (blocks[block].originalAddress != addr)
|
||||
{
|
||||
//_assert_msg_(DYNA_REC, 0, "GetBlockFromAddress %08x - No match - This is BAD", addr);
|
||||
return -1;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
u32 inst = *(u32*)(iCache + (addr & JIT_ICACHE_MASK));
|
||||
inst = Common::swap32(inst);
|
||||
#else
|
||||
u32 inst = Memory::ReadFast32(addr);
|
||||
#endif
|
||||
if (inst & 0xfc000000) // definitely not a JIT block
|
||||
return -1;
|
||||
}
|
||||
if (inst >= num_blocks)
|
||||
return -1;
|
||||
if (blocks[inst].originalAddress != addr)
|
||||
return -1;
|
||||
return inst;
|
||||
}
|
||||
|
||||
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
|
||||
{
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
if (blocks[i].ContainsAddress(em_address))
|
||||
block_numbers->push_back(i);
|
||||
}
|
||||
|
||||
u32 JitBlockCache::GetOriginalCode(u32 address)
|
||||
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
|
||||
{
|
||||
int num = GetBlockNumberFromStartAddress(address);
|
||||
if (num == -1)
|
||||
return Memory::ReadUnchecked_U32(address);
|
||||
else
|
||||
return blocks[num].originalFirstOpcode;
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
if (blocks[i].ContainsAddress(em_address))
|
||||
block_numbers->push_back(i);
|
||||
}
|
||||
|
||||
u32 JitBlockCache::GetOriginalFirstOp(u32 block_num)
|
||||
{
|
||||
if (block_num >= num_blocks)
|
||||
{
|
||||
//PanicAlert("JitBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
|
||||
return block_num;
|
||||
}
|
||||
return blocks[block_num].originalFirstOpcode;
|
||||
}
|
||||
|
||||
CompiledCode JitBlockCache::GetCompiledCodeFromBlock(int blockNumber)
|
||||
{
|
||||
return (CompiledCode)blockCodePointers[blockNumber];
|
||||
return (CompiledCode)blocks[blockNumber].normalEntry;
|
||||
}
|
||||
|
||||
//Block linker
|
||||
|
@ -301,52 +321,64 @@ void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int>
|
|||
|
||||
void JitBlockCache::DestroyBlock(int blocknum, bool invalidate)
|
||||
{
|
||||
u32 codebytes = (JIT_OPCODE << 26) | blocknum; //generate from i
|
||||
if (blocknum < 0 || blocknum >= num_blocks)
|
||||
{
|
||||
PanicAlert("DestroyBlock: Invalid block number %d", blocknum);
|
||||
return;
|
||||
}
|
||||
JitBlock &b = blocks[blocknum];
|
||||
b.invalid = 1;
|
||||
if (codebytes == Memory::ReadFast32(b.originalAddress))
|
||||
if (b.invalid)
|
||||
{
|
||||
//nobody has changed it, good
|
||||
if (invalidate)
|
||||
PanicAlert("Invalidating invalid block %d", blocknum);
|
||||
return;
|
||||
}
|
||||
b.invalid = true;
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode);
|
||||
#else
|
||||
if (Memory::ReadFast32(b.originalAddress) == blocknum)
|
||||
Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
|
||||
}
|
||||
else if (!invalidate)
|
||||
{
|
||||
//PanicAlert("Detected code overwrite");
|
||||
//else, we may be in trouble, since we apparently know of this block but it's been
|
||||
//overwritten. We should have thrown it out before, on instruction cache invalidate or something.
|
||||
//Not ne cessarily bad though , if a game has simply thrown away a lot of code and is now using the space
|
||||
//for something else, then it's fine.
|
||||
DEBUG_LOG(MASTER_LOG, "WARNING - ClearCache detected code overwrite @ %08x", blocks[blocknum].originalAddress);
|
||||
}
|
||||
#endif
|
||||
|
||||
// We don't unlink blocks, we just send anyone who tries to run them back to the dispatcher.
|
||||
// Not entirely ideal, but .. pretty good.
|
||||
|
||||
// TODO - make sure that the below stuff really is safe.
|
||||
|
||||
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
||||
XEmitter emit((u8 *)b.checkedEntry);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(asm_routines.dispatcher, true);
|
||||
|
||||
// this is not needed really
|
||||
/*
|
||||
emit.SetCodePtr((u8 *)blockCodePointers[blocknum]);
|
||||
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
|
||||
emit.JMP(asm_routines.dispatcher, true);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void JitBlockCache::InvalidateCodeRange(u32 address, u32 length)
|
||||
void JitBlockCache::InvalidateICache(u32 address)
|
||||
{
|
||||
if (!jit.jo.enableBlocklink)
|
||||
return;
|
||||
return;
|
||||
//This is slow but should be safe (zelda needs it for block linking)
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
address &= ~0x1f;
|
||||
// destroy JIT blocks
|
||||
// !! this works correctly under assumption that any two overlapping blocks end at the same address
|
||||
std::map<pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(address, 0)), it2 = it1, it;
|
||||
while (it2 != block_map.end() && it2->first.second < address + 0x20)
|
||||
{
|
||||
if (RangeIntersect(blocks[i].originalAddress, blocks[i].originalAddress + blocks[i].originalSize,
|
||||
address, address + length))
|
||||
{
|
||||
DestroyBlock(i, true);
|
||||
}
|
||||
DestroyBlock(it2->second, true);
|
||||
it2++;
|
||||
}
|
||||
if (it1 != it2)
|
||||
{
|
||||
block_map.erase(it1, it2);
|
||||
}
|
||||
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
// invalidate iCache
|
||||
if ((address & ~JIT_ICACHE_MASK) != 0x80000000 && (address & ~JIT_ICACHE_MASK) != 0x00000000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
u32 cacheaddr = address & JIT_ICACHE_MASK;
|
||||
memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE, 32);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -28,6 +28,17 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// emulate CPU with unlimited instruction cache
|
||||
// the only way to invalidate a region is the "icbi" instruction
|
||||
#define JIT_UNLIMITED_ICACHE
|
||||
|
||||
#define JIT_ICACHE_SIZE 0x2000000
|
||||
#define JIT_ICACHE_MASK 0x1ffffff
|
||||
// this corresponds to opcode 5 which is invalid in PowerPC
|
||||
#define JIT_ICACHE_INVALID_BYTE 0x14
|
||||
#define JIT_ICACHE_INVALID_WORD 0x14141414
|
||||
|
||||
|
||||
enum BlockFlag
|
||||
{
|
||||
BLOCK_USE_GQR0 = 0x1, BLOCK_USE_GQR1 = 0x2, BLOCK_USE_GQR2 = 0x4, BLOCK_USE_GQR3 = 0x8,
|
||||
|
@ -71,6 +82,10 @@ class JitBlockCache
|
|||
JitBlock *blocks;
|
||||
int num_blocks;
|
||||
std::multimap<u32, int> links_to;
|
||||
std::map<std::pair<u32,u32>, u32> block_map; // (end_addr, start_addr) -> number
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
u8 *iCache;
|
||||
#endif
|
||||
int MAX_NUM_BLOCKS;
|
||||
|
||||
bool RangeIntersect(int s1, int e1, int s2, int e2) const;
|
||||
|
@ -94,6 +109,9 @@ public:
|
|||
JitBlock *GetBlock(int block_num);
|
||||
int GetNumBlocks() const;
|
||||
const u8 **GetCodePointers();
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
u8 *GetICache();
|
||||
#endif
|
||||
|
||||
// Fast way to get a block. Only works on the first ppc instruction of a block.
|
||||
int GetBlockNumberFromStartAddress(u32 em_address);
|
||||
|
@ -104,15 +122,15 @@ public:
|
|||
// This one is slow so should only be used for one-shots from the debugger UI, not for anything during runtime.
|
||||
void GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers);
|
||||
|
||||
u32 GetOriginalCode(u32 address);
|
||||
u32 GetOriginalFirstOp(u32 block_num);
|
||||
CompiledCode GetCompiledCodeFromBlock(int blockNumber);
|
||||
|
||||
// DOES NOT WORK CORRECTLY WITH INLINING
|
||||
void InvalidateCodeRange(u32 em_address, u32 length);
|
||||
void InvalidateICache(u32 em_address);
|
||||
void DestroyBlock(int blocknum, bool invalidate);
|
||||
|
||||
// Not currently used
|
||||
void DestroyBlocksWithFlag(BlockFlag death_flag);
|
||||
//void DestroyBlocksWithFlag(BlockFlag death_flag);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -319,7 +319,7 @@ static GekkoOPTemplate table31[] =
|
|||
|
||||
{4, &Jit64::Default}, //"tw", OPTYPE_SYSTEM, 0, 1}},
|
||||
{598, &Jit64::DoNothing}, //"sync", OPTYPE_SYSTEM, 0, 2}},
|
||||
{982, &Jit64::Default}, //"icbi", OPTYPE_SYSTEM, 0, 3}},
|
||||
{982, &Jit64::icbi}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}},
|
||||
|
||||
// Unused instructions on GC
|
||||
{310, &Jit64::Default}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}},
|
||||
|
|
|
@ -287,8 +287,7 @@ bool CanSwapAdjacentOps(const CodeOp &a, const CodeOp &b)
|
|||
bool Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, BlockRegStats *fpa, CodeBuffer *buffer)
|
||||
{
|
||||
memset(st, 0, sizeof(st));
|
||||
|
||||
UGeckoInstruction previnst = Memory::Read_Instruction(address - 4);
|
||||
UGeckoInstruction previnst = Memory::Read_Opcode_JIT_LC(address - 4);
|
||||
if (previnst.hex == 0x4e800020)
|
||||
st->isFirstBlockOfFunction = true;
|
||||
|
||||
|
@ -309,10 +308,9 @@ bool Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, Blo
|
|||
{
|
||||
memset(&code[i], 0, sizeof(CodeOp));
|
||||
code[i].address = address;
|
||||
UGeckoInstruction inst = Memory::Read_Instruction(code[i].address);
|
||||
UGeckoInstruction untouched_op = Memory::ReadUnchecked_U32(code[i].address);
|
||||
if (untouched_op.OPCD == 1) // Do handle HLE instructions.
|
||||
inst = untouched_op;
|
||||
|
||||
UGeckoInstruction inst = Memory::Read_Opcode_JIT(code[i].address);
|
||||
|
||||
_assert_msg_(POWERPC, inst.hex != 0, "Zero Op - Error flattening %08x op %08x", address + i*4, inst.hex);
|
||||
code[i].inst = inst;
|
||||
code[i].branchTo = -1;
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// 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, version 2.0.
|
||||
|
||||
// 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 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "PPCCache.h"
|
||||
#include "../HW/Memmap.h"
|
||||
#include "PowerPC.h"
|
||||
|
||||
namespace PowerPC
|
||||
{
|
||||
|
||||
u32 plru_mask[8] = {11,11,19,19,37,37,69,69};
|
||||
u32 plru_value[8] = {11,3,17,1,36,4,64,0};
|
||||
|
||||
InstructionCache::InstructionCache()
|
||||
{
|
||||
for (u32 m = 0; m < 0xff; m++)
|
||||
{
|
||||
u32 w = 0;
|
||||
while (m & (1<<w)) w++;
|
||||
way_from_valid[m] = w;
|
||||
}
|
||||
|
||||
for (u32 m = 0; m < 128; m++)
|
||||
{
|
||||
u32 b[7];
|
||||
for (int i = 0; i < 7; i++) b[i] = m & (1<<i);
|
||||
u32 w;
|
||||
if (b[0])
|
||||
if (b[2])
|
||||
if (b[6])
|
||||
w = 7;
|
||||
else
|
||||
w = 6;
|
||||
else
|
||||
if (b[5])
|
||||
w = 5;
|
||||
else
|
||||
w = 4;
|
||||
else
|
||||
if (b[1])
|
||||
if (b[4])
|
||||
w = 3;
|
||||
else
|
||||
w = 2;
|
||||
else
|
||||
if (b[3])
|
||||
w = 1;
|
||||
else
|
||||
w = 0;
|
||||
way_from_plru[m] = w;
|
||||
}
|
||||
}
|
||||
|
||||
void InstructionCache::Reset()
|
||||
{
|
||||
memset(valid, 0, sizeof(valid));
|
||||
memset(plru, 0, sizeof(plru));
|
||||
#ifdef FAST_ICACHE
|
||||
memset(lookup_table, 0xff, sizeof(lookup_table));
|
||||
#endif
|
||||
}
|
||||
|
||||
void InstructionCache::Invalidate(u32 addr)
|
||||
{
|
||||
if (!HID0.ICE)
|
||||
return;
|
||||
// invalidates the whole set
|
||||
u32 set = (addr >> 5) & 0x7f;
|
||||
#ifdef FAST_ICACHE
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (valid[set] & (1<<i))
|
||||
{
|
||||
lookup_table[((tags[set][i] << 7) | set) & 0xfffff] = 0xff;
|
||||
}
|
||||
#endif
|
||||
valid[set] = 0;
|
||||
}
|
||||
|
||||
u32 InstructionCache::ReadInstruction(u32 addr)
|
||||
{
|
||||
if (!HID0.ICE) // instuction cache is disabled
|
||||
return Memory::ReadUnchecked_U32(addr);
|
||||
u32 set = (addr >> 5) & 0x7f;
|
||||
u32 tag = addr >> 12;
|
||||
#ifdef FAST_ICACHE
|
||||
u32 t = lookup_table[(addr>>5) & 0xfffff];
|
||||
#else
|
||||
u32 t = 0xff;
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
if (tags[set][i] == tag && (valid[set] & (1<<i)))
|
||||
{
|
||||
t = i;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (t == 0xff) // load to the cache
|
||||
{
|
||||
if (HID0.ILOCK) // instruction cache is locked
|
||||
return Memory::ReadUnchecked_U32(addr);
|
||||
// select a way
|
||||
if (valid[set] != 0xff)
|
||||
t = way_from_valid[valid[set]];
|
||||
else
|
||||
t = way_from_plru[plru[set]];
|
||||
// load
|
||||
u8 *p = Memory::GetPointer(addr & ~0x1f);
|
||||
memcpy(data[set][t], p, 32);
|
||||
#ifdef FAST_ICACHE
|
||||
if (valid[set] & (1<<t))
|
||||
lookup_table[((tags[set][t] << 7) | set) & 0xfffff] = 0xff;
|
||||
lookup_table[(addr>>5) & 0xfffff] = t;
|
||||
#endif
|
||||
tags[set][t] = tag;
|
||||
valid[set] |= 1<<t;
|
||||
}
|
||||
// update plru
|
||||
plru[set] = (plru[set] & ~plru_mask[t]) | plru_value[t];
|
||||
return Common::swap32(data[set][t][(addr>>2)&7]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// 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, version 2.0.
|
||||
|
||||
// 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 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _PPCCACHE_H
|
||||
#define _PPCCACHE_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#define FAST_ICACHE
|
||||
|
||||
namespace PowerPC
|
||||
{
|
||||
|
||||
const u32 ICACHE_SETS = 128;
|
||||
const u32 ICACHE_WAYS = 8;
|
||||
// size of an instruction cache block in words
|
||||
const u32 ICACHE_BLOCK_SIZE = 8;
|
||||
|
||||
struct InstructionCache
|
||||
{
|
||||
u32 data[ICACHE_SETS][ICACHE_WAYS][ICACHE_BLOCK_SIZE];
|
||||
u32 tags[ICACHE_SETS][ICACHE_WAYS];
|
||||
u32 plru[ICACHE_SETS];
|
||||
u32 valid[ICACHE_SETS];
|
||||
|
||||
u32 way_from_valid[255];
|
||||
u32 way_from_plru[128];
|
||||
|
||||
#ifdef FAST_ICACHE
|
||||
u8 lookup_table[1<<20];
|
||||
#endif
|
||||
|
||||
InstructionCache();
|
||||
void Reset();
|
||||
u32 ReadInstruction(u32 addr);
|
||||
void Invalidate(u32 addr);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -127,6 +127,8 @@ void Init()
|
|||
// ... but start as interpreter by default.
|
||||
mode = MODE_INTERPRETER;
|
||||
state = CPU_STEPPING;
|
||||
|
||||
ppcState.iCache.Reset();
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Gekko.h"
|
||||
#include "BreakPoints.h"
|
||||
#include "../Debugger/PPCDebugInterface.h"
|
||||
#include "PPCCache.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
|
@ -64,6 +65,9 @@ struct GC_ALIGNED64(PowerPCState)
|
|||
// special purpose registers - controlls quantizers, DMA, and lots of other misc extensions.
|
||||
// also for power management, but we don't care about that.
|
||||
u32 spr[1024];
|
||||
|
||||
InstructionCache iCache;
|
||||
// JIT-mode instruction cache. Managed by JitCache
|
||||
};
|
||||
|
||||
enum CPUState
|
||||
|
@ -101,6 +105,7 @@ void OnIdle(u32 _uThreadAddr);
|
|||
void OnIdleIL();
|
||||
|
||||
// Easy register access macros.
|
||||
#define HID0 ((UReg_HID0&)PowerPC::ppcState.spr[SPR_HID0])
|
||||
#define HID2 ((UReg_HID2&)PowerPC::ppcState.spr[SPR_HID2])
|
||||
#define DMAU (*(UReg_DMAU*)&PowerPC::ppcState.spr[SPR_DMAU])
|
||||
#define DMAL (*(UReg_DMAL*)&PowerPC::ppcState.spr[SPR_DMAL])
|
||||
|
|
|
@ -72,6 +72,7 @@ files = ["ActionReplay.cpp",
|
|||
"PowerPC/PowerPC.cpp",
|
||||
"PowerPC/PPCAnalyst.cpp",
|
||||
"PowerPC/PPCTables.cpp",
|
||||
"PowerPC/PPCCache.cpp",
|
||||
"PowerPC/Profiler.cpp",
|
||||
"PowerPC/SignatureDB.cpp",
|
||||
"PowerPC/PPCSymbolDB.cpp",
|
||||
|
|
|
@ -24,7 +24,11 @@
|
|||
#include "CoreTiming.h"
|
||||
#include "HW/HW.h"
|
||||
#include "PowerPC/PowerPC.h"
|
||||
#ifdef JITTEST
|
||||
#include "PowerPC/Jit64IL/Jit.h"
|
||||
#else
|
||||
#include "PowerPC/Jit64/Jit.h"
|
||||
#endif
|
||||
|
||||
#include "PluginManager.h"
|
||||
|
||||
|
@ -91,6 +95,9 @@ void DoState(PointerWrap &p)
|
|||
PowerPC::DoState(p);
|
||||
HW::DoState(p);
|
||||
CoreTiming::DoState(p);
|
||||
#ifdef JIT_UNLIMITED_ICACHE
|
||||
p.DoVoid(jit.GetBlockCache()->GetICache(), JIT_ICACHE_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoadBufferStateCallback(u64 userdata, int cyclesLate)
|
||||
|
|
Loading…
Reference in New Issue