From 801d71f7f00d4715610bd0f236af79da9bfbfb3c Mon Sep 17 00:00:00 2001 From: ramapcsx2 Date: Fri, 6 Mar 2009 17:14:30 +0000 Subject: [PATCH] Fixes for the new block manager optimizations by Pseudonym. There's a bit more speed even :) git-svn-id: http://pcsx2.googlecode.com/svn/trunk@700 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/x86/BaseblockEx.cpp | 180 +++++++--------------------- pcsx2/x86/BaseblockEx.h | 72 +++++++---- pcsx2/x86/iR3000A.cpp | 190 +++++++++++++++-------------- pcsx2/x86/iR3000A.h | 2 +- pcsx2/x86/iR5900.h | 4 +- pcsx2/x86/ix86-32/iR5900-32.cpp | 206 ++++++++++++++++---------------- 6 files changed, 300 insertions(+), 354 deletions(-) diff --git a/pcsx2/x86/BaseblockEx.cpp b/pcsx2/x86/BaseblockEx.cpp index 67855133c3..6a72251330 100644 --- a/pcsx2/x86/BaseblockEx.cpp +++ b/pcsx2/x86/BaseblockEx.cpp @@ -19,143 +19,51 @@ #include "PrecompiledHeader.h" #include "BaseblockEx.h" -#include - -using namespace std; - -struct BASEBLOCKS +BASEBLOCKEX* BaseBlocks::New(u32 startpc) { - // 0 - ee, 1 - iop - void Add(BASEBLOCKEX*); - void Remove(BASEBLOCKEX*); - int Get(u32 startpc); - void Reset(); - - BASEBLOCKEX** GetAll(int* pnum); - - vector blocks; -}; - -void BASEBLOCKS::Add(BASEBLOCKEX* pex) -{ - assert( pex != NULL ); - - switch(blocks.size()) { - case 0: - blocks.push_back(pex); - return; - case 1: - assert( blocks.front()->startpc != pex->startpc ); - - if( blocks.front()->startpc < pex->startpc ) { - blocks.push_back(pex); - } - else blocks.insert(blocks.begin(), pex); - - return; - - default: - { - int imin = 0, imax = blocks.size(), imid; - - while(imin < imax) { - imid = (imin+imax)>>1; - - if( blocks[imid]->startpc > pex->startpc ) imax = imid; - else imin = imid+1; - } - - assert( imin == blocks.size() || blocks[imin]->startpc > pex->startpc ); - blocks.insert(blocks.begin()+imin, pex); - - return; - } - } -} - -int BASEBLOCKS::Get(u32 startpc) -{ - switch(blocks.size()) { - case 0: - return -1; - case 1: - if (blocks.front()->startpc + blocks.front()->size*4 <= startpc) - return -1; - else - return 0; - /*case 2: - return blocks.front()->startpc < startpc;*/ - - default: - { - int imin = 0, imax = blocks.size()-1, imid; - - while(imin < imax) { - imid = (imin+imax)>>1; - - if( blocks[imid]->startpc > startpc ) imax = imid; - else if( blocks[imid]->startpc == startpc ) return imid; - else imin = imid+1; - } - - //assert( blocks[imin]->startpc == startpc ); - if (startpc < blocks[imin]->startpc || - startpc >= blocks[imin]->startpc + blocks[imin]->size*4) - return -1; - else - return imin; - } - } -} - -void BASEBLOCKS::Remove(BASEBLOCKEX* pex) -{ - assert( pex != NULL ); - int i = Get(pex->startpc); - assert( blocks[i] == pex ); - blocks.erase(blocks.begin()+i); -} - -void BASEBLOCKS::Reset() -{ - blocks.resize(0); - blocks.reserve(512); -} - -BASEBLOCKEX** BASEBLOCKS::GetAll(int* pnum) -{ - assert( pnum != NULL ); - *pnum = blocks.size(); - return &blocks[0]; -} - -static BASEBLOCKS s_vecBaseBlocksEx[2]; - -void AddBaseBlockEx(BASEBLOCKEX* pex, int cpu) -{ - s_vecBaseBlocksEx[cpu].Add(pex); -} - -BASEBLOCKEX* GetBaseBlockEx(u32 startpc, int cpu) -{ - int i = s_vecBaseBlocksEx[cpu].Get(startpc); - if (i < 0) + if (blocks.size() == size) return 0; + + BASEBLOCKEX newblock; + std::vector::iterator iter; + memset(&newblock, 0, sizeof newblock); + newblock.startpc = startpc; + + int imin = 0, imax = blocks.size(), imid; + + while (imin < imax) { + imid = (imin+imax)>>1; + + if (blocks[imid].startpc > startpc) + imax = imid; + else + imin = imid + 1; + } + + assert(imin == blocks.size() || blocks[imin].startpc > startpc); + iter = blocks.insert(blocks.begin() + imin, newblock); + return &*iter; +} + +int BaseBlocks::Index(u32 startpc) const +{ + if (0 == blocks.size()) + return -1; + + int imin = 0, imax = blocks.size() - 1, imid; + + while(imin != imax) { + imid = (imin+imax+1)>>1; + + if (blocks[imid].startpc > startpc) + imax = imid - 1; + else + imin = imid; + } + + if (startpc < blocks[imin].startpc || + startpc >= blocks[imin].startpc + blocks[imin].size*4) + return -1; else - return s_vecBaseBlocksEx[cpu].blocks[i]; -} - -void RemoveBaseBlockEx(BASEBLOCKEX* pex, int cpu) -{ - s_vecBaseBlocksEx[cpu].Remove(pex); -} - -void ResetBaseBlockEx(int cpu) -{ - s_vecBaseBlocksEx[cpu].Reset(); -} - -BASEBLOCKEX** GetAllBaseBlocks(int* pnum, int cpu) -{ - return s_vecBaseBlocksEx[cpu].GetAll(pnum); + return imin; } diff --git a/pcsx2/x86/BaseblockEx.h b/pcsx2/x86/BaseblockEx.h index e058453a7d..7c00110589 100644 --- a/pcsx2/x86/BaseblockEx.h +++ b/pcsx2/x86/BaseblockEx.h @@ -16,8 +16,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef _BASEBLOCKEX_H_ -#define _BASEBLOCKEX_H_ +#pragma once + +#include "PrecompiledHeader.h" +#include // used to keep block information #define BLOCKTYPE_DELAYSLOT 1 // if bit set, delay slot @@ -51,28 +53,58 @@ struct BASEBLOCKEX }; -// This is an asinine macro that bases indexing on sizeof(BASEBLOCK) for no reason. (air) +class BaseBlocks +{ +private: + std::vector blocks; + unsigned long size; + +public: + BaseBlocks(unsigned long max) : + size(max), + blocks(0) + { + blocks.reserve(size); + } + + BASEBLOCKEX* New(u32 startpc); + int Index (u32 startpc) const; + + inline BASEBLOCKEX* operator[](int idx) + { + if (idx < 0 || idx >= (int)blocks.size()) + return 0; + return &blocks[idx]; + } + + inline BASEBLOCKEX* Get(u32 startpc) + { + return (*this)[Index(startpc)]; + } + + inline void Remove(int idx) + { + blocks.erase(blocks.begin() + idx); + } + + inline void Reset() + { + blocks.clear(); + } +}; + #define GET_BLOCKTYPE(b) ((b)->Type) -// x * (sizeof(BASEBLOCK) / 4) sacrifices safety for speed compared to -// x / 4 * sizeof(BASEBLOCK) or a higher level approach. #define PC_GETBLOCK_(x, reclut) ((BASEBLOCK*)(reclut[((u32)(x)) >> 16] + (x)*(sizeof(BASEBLOCK)/4))) -static void recLUT_SetPage( uptr reclut[0x10000], uint page, void* mapping ) +static void recLUT_SetPage(uptr reclut[0x10000], uptr hwlut[0x10000], + BASEBLOCK *mapbase, uint pagebase, uint pageidx, uint mappage) { + uint page = pagebase + pageidx; + jASSUME( page < 0x10000 ); - reclut[page] = ((uptr)mapping) - ((page << 14) * sizeof(BASEBLOCK)); + reclut[page] = (uptr)&mapbase[(mappage - page) << 14]; + if (hwlut) + hwlut[page] = 0u - (pagebase << 16); } -// This is needed because of the retarded GETBLOCK macro above. -C_ASSERT( sizeof(BASEBLOCK) == 8 ); - -// 0 - ee, 1 - iop -extern void AddBaseBlockEx(BASEBLOCKEX*, int cpu); -extern void RemoveBaseBlockEx(BASEBLOCKEX*, int cpu); -extern BASEBLOCKEX* GetBaseBlockEx(u32 startpc, int cpu); -extern void ResetBaseBlockEx(int cpu); - -extern BASEBLOCKEX** GetAllBaseBlocks(int* pnum, int cpu); - - -#endif \ No newline at end of file +C_ASSERT( sizeof(BASEBLOCK) == 8 ); \ No newline at end of file diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index fb3d958975..9d3ed2cbd8 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -45,6 +45,9 @@ u32 g_psxMaxRecMem = 0; u32 s_psxrecblocks[] = {0}; uptr psxRecLUT[0x10000]; +uptr psxhwLUT[0x10000]; + +#define HWADDR(mem) (psxhwLUT[mem >> 16] + (mem)) #define PSX_NUMBLOCKS (1<<12) #define MAPBASE 0x48000000 @@ -59,7 +62,7 @@ static u8 *recMem = NULL; // the recompiled blocks will be here static BASEBLOCK *recRAM = NULL; // and the ptr to the blocks here static BASEBLOCK *recROM = NULL; // and here static BASEBLOCK *recROM1 = NULL; // also here -static BASEBLOCKEX *recBlocks = NULL; +static BaseBlocks recBlocks(PSX_NUMBLOCKS); static u8 *recPtr = NULL; u32 psxpc; // recompiler psxpc int psxbranch; // set for branch @@ -71,9 +74,7 @@ static u32 s_nInstCacheSize = 0; static BASEBLOCK* s_pCurBlock = NULL; static BASEBLOCKEX* s_pCurBlockEx = NULL; -static u32 s_nEndBlock = 0; // what psxpc the current block ends - -static u32 s_nNextBlock = 0; // next free block in recBlocks +static u32 s_nEndBlock = 0; // what psxpc the current block ends static u32 s_ConstGPRreg; static u32 s_saveConstGPRreg = 0, s_saveHasConstReg = 0, s_saveFlushedConstReg = 0; @@ -88,7 +89,7 @@ void psxRecompileNextInstruction(int delayslot); extern void (*rpsxBSC[64])(); void rpsxpropBSC(EEINST* prev, EEINST* pinst); -static void iopInitRecLUT(BASEBLOCK* base, int count); +static void iopClearRecLUT(BASEBLOCK* base, int count); #ifdef _DEBUG u32 psxdump = 0; @@ -98,21 +99,9 @@ u32 psxdump = 0; #define PSX_GETBLOCK(x) PC_GETBLOCK_(x, psxRecLUT) -#define PSXREC_CLEARM(mem) { \ - if ((mem) < g_psxMaxRecMem && (psxRecLUT[(mem) >> 16] + mem)) { \ - psxRecClearMem(mem); \ - } \ -} \ - -BASEBLOCKEX* PSX_GETBLOCKEX(BASEBLOCK* p) -{ -// BASEBLOCKEX* pex = *(BASEBLOCKEX**)(p+1); -// if( pex >= recBlocks && pex < recBlocks+PSX_NUMBLOCKS ) -// return pex; - - // otherwise, use the sorted list - return GetBaseBlockEx(p->GetStartPC(), 1); -} +#define PSXREC_CLEARM(mem) \ + (((mem) < g_psxMaxRecMem && (psxRecLUT[(mem) >> 16] + (mem))) ? \ + psxRecClearMem(mem) : 4) //////////////////////////////////////////////////// #ifdef _DEBUG @@ -528,8 +517,7 @@ void psxRecompileCodeConst3(R3000AFNPTR constcode, R3000AFNPTR_INFO constscode, static u8* m_recBlockAlloc = NULL; static const uint m_recBlockAllocSize = - (((Ps2MemSize::IopRam + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK)) - + (PSX_NUMBLOCKS*sizeof(BASEBLOCKEX)); // recBlocks + (((Ps2MemSize::IopRam + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK)); static void recAlloc() { @@ -557,7 +545,6 @@ static void recAlloc() recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::IopRam / 4) * sizeof(BASEBLOCK); recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK); recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK); - recBlocks = (BASEBLOCKEX*)curpos; // curpos += sizeof(BASEBLOCKEX)*EE_NUMBLOCKS; if( s_pInstCache == NULL ) { @@ -574,45 +561,45 @@ static void recAlloc() void recResetIOP() { // calling recResetIOP without first calling recInit is bad mojo. - jASSUME( psxRecLUT != NULL ); jASSUME( recMem != NULL ); jASSUME( m_recBlockAlloc != NULL ); DevCon::Status( "iR3000A Resetting recompiler memory and structures" ); - memzero_ptr( psxRecLUT ); memset_8<0xcd,RECMEM_SIZE>( recMem ); - memzero_ptr(recBlocks); - iopInitRecLUT((BASEBLOCK*)m_recBlockAlloc, + iopClearRecLUT((BASEBLOCK*)m_recBlockAlloc, (((Ps2MemSize::IopRam + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4))); + for (int i = 0; i < 0x10000; i++) + recLUT_SetPage(psxRecLUT, 0, 0, 0, i, 0); + // We're only mapping 20 pages here in 4 places. // 0x80 comes from : (Ps2MemSize::IopRam / 0x10000) * 4 for (int i=0; i<0x80; i++) { - recLUT_SetPage(psxRecLUT, i + 0x0000, &recRAM[(i & 0x1f) << 14]); - recLUT_SetPage(psxRecLUT, i + 0x8000, &recRAM[(i & 0x1f) << 14]); - recLUT_SetPage(psxRecLUT, i + 0xa000, &recRAM[(i & 0x1f) << 14]); + recLUT_SetPage(psxRecLUT, psxhwLUT, recRAM, 0x0000, i, i & 0x1f); + recLUT_SetPage(psxRecLUT, psxhwLUT, recRAM, 0x8000, i, i & 0x1f); + recLUT_SetPage(psxRecLUT, psxhwLUT, recRAM, 0xa000, i, i & 0x1f); } - for (int i=0; i<(Ps2MemSize::Rom / 0x10000); i++) + for (int i=0x1fc0; i<0x2000; i++) { - recLUT_SetPage(psxRecLUT, i + 0x1fc0, &recROM[i << 14]); - recLUT_SetPage(psxRecLUT, i + 0x9fc0, &recROM[i << 14]); - recLUT_SetPage(psxRecLUT, i + 0xbfc0, &recROM[i << 14]); + recLUT_SetPage(psxRecLUT, psxhwLUT, recROM, 0x0000, i, i - 0x1fc0); + recLUT_SetPage(psxRecLUT, psxhwLUT, recROM, 0x8000, i, i - 0x1fc0); + recLUT_SetPage(psxRecLUT, psxhwLUT, recROM, 0xa000, i, i - 0x1fc0); } - for (int i=0; i<(Ps2MemSize::Rom1 / 0x10000); i++) + for (int i=0x1e00; i<0x1e04; i++) { - recLUT_SetPage(psxRecLUT, i + 0x1e00, &recROM1[i << 14]); - recLUT_SetPage(psxRecLUT, i + 0x9e00, &recROM1[i << 14]); - recLUT_SetPage(psxRecLUT, i + 0xbe00, &recROM1[i << 14]); + recLUT_SetPage(psxRecLUT, psxhwLUT, recROM1, 0x0000, i, i - 0x1fc0); + recLUT_SetPage(psxRecLUT, psxhwLUT, recROM1, 0x8000, i, i - 0x1fc0); + recLUT_SetPage(psxRecLUT, psxhwLUT, recROM1, 0xa000, i, i - 0x1fc0); } if( s_pInstCache ) memset( s_pInstCache, 0, sizeof(EEINST)*s_nInstCacheSize ); - ResetBaseBlockEx(1); + recBlocks.Reset(); g_psxMaxRecMem = 0; recPtr = recMem; @@ -782,7 +769,7 @@ static __declspec(naked) void iopDispatcherReg() } #endif // _MSC_VER -static void iopInitRecLUT(BASEBLOCK* base, int count) +static void iopClearRecLUT(BASEBLOCK* base, int count) { for (int i = 0; i < count; i++) { base[i].SetFnptr((uptr)iopJITCompile); @@ -845,59 +832,95 @@ static s32 recExecuteBlock( s32 eeCycles ) static void recClear(u32 Addr, u32 Size) { - u32 i; - for(i = 0; i < Size; ++i, Addr+=4) { - PSXREC_CLEARM(Addr); - } + u32 pc = Addr; + while (pc < Addr + Size*4) + pc += PSXREC_CLEARM(pc); } -#define IOP_MIN_BLOCK_BYTES 15 - +// not used and not right for now +#if 0 void rpsxMemConstClear(u32 mem) { // NOTE! This assumes recLUT never changes its mapping if( !(psxRecLUT[mem>>16] + mem) ) return; - CMP32ItoM((uptr)PSX_GETBLOCK(mem), 0); + CMP32ItoM((uptr)PSX_GETBLOCK(mem), iopJITCompile); j8Ptr[6] = JE8(0); _callFunctionArg1((uptr)psxRecClearMem, MEM_CONSTTAG, mem); x86SetJ8(j8Ptr[6]); } +#endif -void psxRecClearMem(u32 pc) +// Returns the offset to the next instruction after any cleared memory +u32 psxRecClearMem(u32 pc) { BASEBLOCKEX* pexblock; - BASEBLOCK* pstart; - BASEBLOCK* p; + BASEBLOCK* pblock; + + pblock = PSX_GETBLOCK(pc); + // if ((u8*)iopJITCompile == pblock->GetFnptr()) + if (!pblock->GetStartPC()) + return 4; + + pc = HWADDR(pc); + + u32 lowerextent = pc, upperextent = pc + 4; + int blockidx = recBlocks.Index(pc); + + jASSUME(blockidx != -1); + + while (pexblock = recBlocks[blockidx - 1]) { + if (pexblock->startpc + pexblock->size * 4 <= lowerextent) + break; + + lowerextent = min(lowerextent, pexblock->startpc); + blockidx--; + } - p= PSX_GETBLOCK(pc); - pc = p->GetStartPC(); - if (!pc) - return; - pexblock = GetBaseBlockEx(pc, 1); - if (!pexblock) - return; - pstart = PSX_GETBLOCK(pexblock->startpc); + while (pexblock = recBlocks[blockidx]) { + if (pexblock->startpc >= upperextent) + break; - for (int i = 0; i < pexblock->size; i++) { - x86Ptr[0] = (u8*)pstart[i].GetFnptr(); - if (x86Ptr[0] == (u8*)iopJITCompile) - continue; + pblock = PSX_GETBLOCK(pexblock->startpc); + x86Ptr[_EmitterId_] = (u8*)pblock->GetFnptr(); + jASSUME((u8*)iopJITCompile != x86Ptr[_EmitterId_]); + // jASSUME((u8*)iopJITCompileInside != x86Ptr[_EmitterId_]); + + // This is breaking things currently, rather than figure it out + // I'm just using DispatcherReg, it's fast enough now. + // Actually, if we want to do this at all maybe keeping a hash + // table of const jumps and modifying the jumps straight from + // here is the way to go. +#if 0 // there is a small problem: mem can be ored with 0xa<<28 or 0x8<<28, and don't know which - MOV32ItoR(EDX, pexblock->startpc + i*4); - assert( (uptr)x86Ptr[0] <= 0xffffffff ); - PUSH32I((uptr)x86Ptr[0]); // will be replaced by JMP32 - JMP32((uptr)iopDispatcherClear - ( (uptr)x86Ptr[0] + 5 )); + MOV32ItoR(EDX, pexblock->startpc); + assert((uptr)x86Ptr[_EmitterId_] <= 0xffffffff); + PUSH32I((uptr)x86Ptr[_EmitterId_]); // will be replaced by JMP32 + JMP32((uptr)iopDispatcherClear - ((uptr)x86Ptr[_EmitterId_] + 5)); +#else + MOV32ItoM((uptr)&psxRegs.pc, pexblock->startpc); + JMP32((uptr)iopDispatcherReg - ((uptr)x86Ptr[_EmitterId_] + 5)); +#endif + + lowerextent = min(lowerextent, pexblock->startpc); + upperextent = max(upperextent, pexblock->startpc + pexblock->size * 4); + recBlocks.Remove(blockidx); } - iopInitRecLUT(pstart, pexblock->size); +#ifdef PCSX2_DEVBUILD + for (int i = 0; pexblock = recBlocks[i]; i++) + if (pc >= pexblock->startpc && pc < pexblock->startpc + pexblock->size * 4) { + Console::Error("Impossible block clearing failure"); + jASSUME(0); + } +#endif - RemoveBaseBlockEx(pexblock, 1); - pexblock->size = 0; - pexblock->startpc = 0; + iopClearRecLUT(PSX_GETBLOCK(lowerextent), (upperextent - lowerextent) / 4); + + return upperextent - pc; } void psxSetBranchReg(u32 reg) @@ -1067,7 +1090,7 @@ void psxRecompileNextInstruction(int delayslot) if( !delayslot && psxpc == pblock->GetStartPC() ) { // code already in place, so jump to it and exit recomp - assert( PSX_GETBLOCKEX(pblock)->startpc == pblock->GetStartPC() ); + assert( recBlocks.Get(HWADDR(psxpc))->startpc == HWADDR(psxpc) ); _psxFlushCall(FLUSH_EVERYTHING); MOV32ItoM((uptr)&psxRegs.pc, psxpc); @@ -1197,10 +1220,7 @@ void iopRecRecompile(u32 startpc) // if recPtr reached the mem limit reset whole mem if (((uptr)recPtr - (uptr)recMem) >= (RECMEM_SIZE - 0x10000)) - { - // This is getting called pretty often in Linux. (21 times in the course of getting to the starting screen of KH1) --arcum42 recResetIOP(); - } s_pCurBlock = PSX_GETBLOCK(startpc); @@ -1211,27 +1231,17 @@ void iopRecRecompile(u32 startpc) } if( s_pCurBlock->GetStartPC() == startpc ) { - s_pCurBlockEx = PSX_GETBLOCKEX(s_pCurBlock); - assert( s_pCurBlockEx->startpc == startpc ); + s_pCurBlockEx = recBlocks.Get(HWADDR(startpc)); + assert( s_pCurBlockEx->startpc == HWADDR(startpc) ); } else { - s_pCurBlockEx = NULL; - for(i = 0; i < PSX_NUMBLOCKS; ++i) { - if( recBlocks[(i+s_nNextBlock)%PSX_NUMBLOCKS].size == 0 ) { - s_pCurBlockEx = recBlocks+(i+s_nNextBlock)%PSX_NUMBLOCKS; - s_nNextBlock = (i+s_nNextBlock+1)%PSX_NUMBLOCKS; - break; - } - } + s_pCurBlockEx = recBlocks.New(HWADDR(startpc)); if( s_pCurBlockEx == NULL ) { DevCon::WriteLn("IOP Recompiler data reset"); recResetIOP(); - s_nNextBlock = 0; - s_pCurBlockEx = recBlocks; + s_pCurBlockEx = recBlocks.New(HWADDR(startpc)); } - - s_pCurBlockEx->startpc = startpc; } x86SetPtr( recPtr ); @@ -1385,9 +1395,6 @@ StartRecomp: } #endif - // set the block ptr - AddBaseBlockEx(s_pCurBlockEx, 1); - if( !(psxpc&0x10000000) ) g_psxMaxRecMem = std::max( (psxpc&~0xa0000000), g_psxMaxRecMem ); @@ -1426,7 +1433,6 @@ StartRecomp: } } - assert( x86Ptr[0] >= (u8*)s_pCurBlock->GetFnptr() + IOP_MIN_BLOCK_BYTES ); assert( x86Ptr[0] < recMem+RECMEM_SIZE ); recPtr = x86Ptr[0]; diff --git a/pcsx2/x86/iR3000A.h b/pcsx2/x86/iR3000A.h index 24267ba922..e5dc2f963c 100644 --- a/pcsx2/x86/iR3000A.h +++ b/pcsx2/x86/iR3000A.h @@ -70,7 +70,7 @@ void psxLoadBranchState(); void psxSetBranchReg(u32 reg); void psxSetBranchImm( u32 imm ); void psxRecompileNextInstruction(int delayslot); -void psxRecClearMem(u32 p); +u32 psxRecClearMem(u32 p); //////////////////////////////////////////////////////////////////// // IOP Constant Propagation Defines, Vars, and API - From here down! diff --git a/pcsx2/x86/iR5900.h b/pcsx2/x86/iR5900.h index 57bb8c092b..ad102f4801 100644 --- a/pcsx2/x86/iR5900.h +++ b/pcsx2/x86/iR5900.h @@ -92,8 +92,8 @@ extern GPR_reg64 s_ConstGPRreg; // Used to clear recompiled code blocks during memory/dma write operations. -void recClearMem(u32 pc); -void REC_CLEARM( u32 mem ); +u32 recClearMem(u32 pc); +u32 REC_CLEARM( u32 mem ); // used when processing branches void SaveBranchState(); diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index e65f671f09..1b13c9b094 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -53,6 +53,9 @@ bool g_EEFreezeRegs = false; u32 maxrecmem = 0; uptr recLUT[0x10000]; +uptr hwLUT[0x10000]; + +#define HWADDR(mem) (hwLUT[mem >> 16] + (mem)) u32 s_nBlockCycles = 0; // cycles of current block recompiling //u8* dyna_block_discard_recmem=0; @@ -77,7 +80,7 @@ static u8* recStack = NULL; // stack mem static BASEBLOCK *recRAM = NULL; // and the ptr to the blocks here static BASEBLOCK *recROM = NULL; // and here static BASEBLOCK *recROM1 = NULL; // also here -static BASEBLOCKEX *recBlocks = NULL; +static BaseBlocks recBlocks(EE_NUMBLOCKS); static u8* recPtr = NULL, *recStackPtr = NULL; static EEINST* s_pInstCache = NULL; static u32 s_nInstCacheSize = 0; @@ -87,8 +90,6 @@ static BASEBLOCKEX* s_pCurBlockEx = NULL; static u32 s_nEndBlock = 0; // what pc the current block ends static u32 s_nHasDelay = 0; -static u32 s_nNextBlock = 0; // next free block in recBlocks - // save states for branches static u16 s_savex86FpuState, s_saveiCWstate; static u32 s_saveHasConstReg = 0, s_saveFlushedConstReg = 0, s_saveRegHasLive1 = 0, s_saveRegHasSignExt = 0; @@ -111,17 +112,7 @@ static u32 dumplog = 0; #endif static void iBranchTest(u32 newpc, bool noDispatch=false); -static void InitRecLUT(BASEBLOCK* base, int count); - -BASEBLOCKEX* PC_GETBLOCKEX(u32 pc) -{ -// BASEBLOCKEX* pex = *(BASEBLOCKEX**)(p+1); -// if( pex >= recBlocks && pex < recBlocks+EE_NUMBLOCKS ) -// return pex; - - // otherwise, use the sorted list - return GetBaseBlockEx(pc, 0); -} +static void ClearRecLUT(BASEBLOCK* base, int count); //////////////////////////////////////////////////// static void iDumpBlock( int startpc, u8 * ptr ) @@ -464,7 +455,6 @@ static u8* m_recBlockAlloc = NULL; static const uint m_recBlockAllocSize = (((Ps2MemSize::Base + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4) * sizeof(BASEBLOCK)) -+ (EE_NUMBLOCKS*sizeof(BASEBLOCKEX)) // recBlocks + RECSTACK_SIZE; // recStack static void recAlloc() @@ -507,7 +497,6 @@ static void recAlloc() recRAM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Base / 4) * sizeof(BASEBLOCK); recROM = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom / 4) * sizeof(BASEBLOCK); recROM1 = (BASEBLOCK*)curpos; curpos += (Ps2MemSize::Rom1 / 4) * sizeof(BASEBLOCK); - recBlocks = (BASEBLOCKEX*)curpos; curpos += sizeof(BASEBLOCKEX)*EE_NUMBLOCKS; recStack = (u8*)curpos; if( s_pInstCache == NULL ) @@ -531,19 +520,17 @@ void recResetEE( void ) { DbgCon::Status( "iR5900-32 > Resetting recompiler memory and structures." ); - s_nNextBlock = 0; maxrecmem = 0; memset_8<0xcd, REC_CACHEMEM>(recMem); memzero_ptr( m_recBlockAlloc ); - memzero_ptr(recBlocks); - InitRecLUT((BASEBLOCK*)m_recBlockAlloc, + ClearRecLUT((BASEBLOCK*)m_recBlockAlloc, (((Ps2MemSize::Base + Ps2MemSize::Rom + Ps2MemSize::Rom1) / 4))); if( s_pInstCache ) memset( s_pInstCache, 0, sizeof(EEINST)*s_nInstCacheSize ); - ResetBaseBlockEx(0); + recBlocks.Reset(); mmap_ResetBlockTracking(); #ifdef _MSC_VER @@ -552,32 +539,35 @@ void recResetEE( void ) __asm__("emms"); #endif - memzero_ptr( recLUT ); + #define GET_HWADDR(mem) + + for (int i = 0; i < 0x10000; i++) + recLUT_SetPage(recLUT, 0, 0, 0, i, 0); for ( int i = 0x0000; i < 0x0200; i++ ) { - recLUT_SetPage(recLUT, i + 0x0000, &recRAM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0x2000, &recRAM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0x3000, &recRAM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0x8000, &recRAM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0xa000, &recRAM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0xb000, &recRAM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0xc000, &recRAM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0xd000, &recRAM[ i << 14 ]); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0x0000, i, i); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0x2000, i, i); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0x3000, i, i); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0x8000, i, i); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0xa000, i, i); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0xb000, i, i); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0xc000, i, i); + recLUT_SetPage(recLUT, hwLUT, recRAM, 0xd000, i, i); } - for ( int i = 0x0000; i < 0x0040; i++ ) + for ( int i = 0x1fc0; i < 0x2000; i++ ) { - recLUT_SetPage(recLUT, i + 0x1fc0, &recROM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0x9fc0, &recROM[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0xbfc0, &recROM[ i << 14 ]); + recLUT_SetPage(recLUT, hwLUT, recROM, 0x0000, i, i - 0x1fc0); + recLUT_SetPage(recLUT, hwLUT, recROM, 0x8000, i, i - 0x1fc0); + recLUT_SetPage(recLUT, hwLUT, recROM, 0xa000, i, i - 0x1fc0); } - for ( int i = 0x0000; i < 0x0004; i++ ) + for ( int i = 0x1e00; i < 0x1e04; i++ ) { - recLUT_SetPage(recLUT, i + 0x1e00, &recROM1[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0x9e00, &recROM1[ i << 14 ]); - recLUT_SetPage(recLUT, i + 0xbe00, &recROM1[ i << 14 ]); + recLUT_SetPage(recLUT, hwLUT, recROM1, 0x0000, i, i - 0x1e00); + recLUT_SetPage(recLUT, hwLUT, recROM1, 0x8000, i, i - 0x1e00); + recLUT_SetPage(recLUT, hwLUT, recROM1, 0xa000, i, i - 0x1e00); } // drk||Raziel says this is useful but I'm not sure why. Something to do with forward jumps. @@ -603,12 +593,11 @@ void recResetEE( void ) static void recShutdown( void ) { ProfilerTerminateSource( "EERec" ); - ResetBaseBlockEx(0); + recBlocks.Reset(); SafeSysMunmap( recMem, REC_CACHEMEM ); safe_aligned_free( m_recBlockAlloc ); recRAM = recROM = recROM1 = NULL; - recBlocks = NULL; recStack = NULL; safe_free( s_pInstCache ); @@ -880,21 +869,23 @@ void recBREAK( void ) { } } } // end namespace R5900::Dynarec::OpcodeImpl //////////////////////////////////////////////////// -static void REC_CLEARM( u32 mem ) +static u32 REC_CLEARM( u32 mem ) { if ((mem) < maxrecmem && (recLUT[(mem) >> 16] + mem)) - recClearMem(mem); + return recClearMem(mem); + else + return 4; } void recClear( u32 Addr, u32 Size ) { - u32 i; - for(i = 0; i < Size; ++i, Addr+=4) - REC_CLEARM(Addr); + u32 pc = Addr; + while (pc < Addr + Size*4) + pc += REC_CLEARM(pc); } // Clears the recLUT table so that all blocks are mapped to the JIT recompiler by default. -static void InitRecLUT(BASEBLOCK* base, int count) +static void ClearRecLUT(BASEBLOCK* base, int count) { for (int i = 0; i < count; i++) { @@ -904,52 +895,74 @@ static void InitRecLUT(BASEBLOCK* base, int count) } } -void recClearMem(u32 pc) +// Returns the offset to the next instruction after any cleared memory +u32 recClearMem(u32 pc) { BASEBLOCKEX* pexblock; - BASEBLOCK* pstart; - BASEBLOCK* p; - - p= PC_GETBLOCK(pc); - pc = p->GetStartPC(); - if (!pc) - return; - pexblock = PC_GETBLOCKEX(pc); - if (!pexblock) - return; - pstart = PC_GETBLOCK(pexblock->startpc); + BASEBLOCK* pblock; - // necessary since recompiler doesn't call femms/emms -#ifdef __INTEL_COMPILER - __asm__("emms"); -#else - #ifdef _MSC_VER - if (cpucaps.has3DNOWInstructionExtensions) __asm femms; - else __asm emms; - #else - if( cpucaps.has3DNOWInstructionExtensions )__asm__("femms"); - else - __asm__("emms"); - #endif -#endif + pblock = PC_GETBLOCK(pc); + // if ((u8*)JITCompile == pblock->GetFnptr()) + if (!pblock->GetStartPC()) + return 4; - for (int i = 0; i < pexblock->size; i++) - { - x86Ptr[0] = (u8*)pstart[i].GetFnptr(); - if (x86Ptr[0] == (u8*)JITCompile) - continue; + pc = HWADDR(pc); - // there is a small problem: mem can be ored with 0xa<<28 or 0x8<<28, and don't know which - MOV32ItoR(EDX, pexblock->startpc + i*4); - PUSH32I((u32)x86Ptr[0]); // will be replaced by JMP32 - JMP32((u32)DispatcherClear - ( (u32)x86Ptr[0] + 5 )); + u32 lowerextent = pc, upperextent = pc + 4; + int blockidx = recBlocks.Index(pc); + + jASSUME(blockidx != -1); + + while (pexblock = recBlocks[blockidx - 1]) { + if (pexblock->startpc + pexblock->size*4 <= lowerextent) + break; + + lowerextent = min(lowerextent, pexblock->startpc); + blockidx--; } - InitRecLUT(pstart, pexblock->size); + while (pexblock = recBlocks[blockidx]) { + if (pexblock->startpc >= upperextent) + break; - RemoveBaseBlockEx(pexblock, 0); - pexblock->size = 0; - pexblock->startpc = 0; + pblock = PC_GETBLOCK(pexblock->startpc); + x86Ptr[_EmitterId_] = (u8*)pblock->GetFnptr(); + + jASSUME((u8*)JITCompile != x86Ptr[_EmitterId_]); + // jASSUME((u8*)JITCompileInside != x86Ptr[_EmitterId_]); + + // This is breaking things currently, rather than figure it out + // I'm just using DispatcherReg, it's fast enough now. + // Actually, if we want to do this at all maybe keeping a hash + // table of const jumps and modifying the jumps straight from + // here is the way to go. +#if 0 + // there is a small problem: mem can be ored with 0xa<<28 or 0x8<<28, and don't know which + MOV32ItoR(EDX, pexblock->startpc); + assert((uptr)x86Ptr[_EmitterId_] <= 0xffffffff); + PUSH32I((uptr)x86Ptr[_EmitterId_]); // will be replaced by JMP32 + JMP32((uptr)DispatcherClear - ((uptr)x86Ptr[_EmitterId_] + 5)); +#else + MOV32ItoM((uptr)&cpuRegs.pc, pexblock->startpc); + JMP32((uptr)DispatcherReg - ((uptr)x86Ptr[_EmitterId_] + 5)); +#endif + + lowerextent = min(lowerextent, pexblock->startpc); + upperextent = max(upperextent, pexblock->startpc + pexblock->size * 4); + recBlocks.Remove(blockidx); + } + +#ifdef PCSX2_DEVBUILD + for (int i = 0; pexblock = recBlocks[i]; i++) + if (pc >= pexblock->startpc && pc < pexblock->startpc + pexblock->size * 4) { + Console::Error("Impossible block clearing failure"); + jASSUME(0); + } +#endif + + ClearRecLUT(PC_GETBLOCK(lowerextent), (upperextent - lowerextent) / 4); + + return upperextent - pc; } // check for end of bios @@ -1269,7 +1282,7 @@ void recompileNextInstruction(int delayslot) if( !delayslot && pc == pblock->GetStartPC() ) { // code already in place, so jump to it and exit recomp - assert( PC_GETBLOCKEX(pc)->startpc == pblock->GetStartPC() ); + assert( recBlocks.Get(HWADDR(pc))->startpc == HWADDR(pc) ); iFlushCall(FLUSH_EVERYTHING); MOV32ItoM((uptr)&cpuRegs.pc, pc); @@ -1501,27 +1514,17 @@ void recRecompile( const u32 startpc ) } if( s_pCurBlock->GetStartPC() == startpc ) { - s_pCurBlockEx = PC_GETBLOCKEX(startpc); - assert( s_pCurBlockEx->startpc == startpc ); + s_pCurBlockEx = recBlocks.Get(HWADDR(startpc)); + assert( s_pCurBlockEx->startpc == HWADDR(startpc) ); } else { - s_pCurBlockEx = NULL; - for(i = 0; i < EE_NUMBLOCKS; ++i) { - if( recBlocks[(i+s_nNextBlock)%EE_NUMBLOCKS].size == 0 ) { - s_pCurBlockEx = recBlocks+(i+s_nNextBlock)%EE_NUMBLOCKS; - s_nNextBlock = (i+s_nNextBlock+1)%EE_NUMBLOCKS; - break; - } - } + s_pCurBlockEx = recBlocks.New(HWADDR(startpc)); if( s_pCurBlockEx == NULL ) { //SysPrintf("ee reset (blocks)\n"); recResetEE(); - s_nNextBlock = 0; - s_pCurBlockEx = recBlocks; + s_pCurBlockEx = recBlocks.New(HWADDR(startpc)); } - - s_pCurBlockEx->startpc = startpc; } x86SetPtr( recPtr ); @@ -1896,9 +1899,9 @@ StartRecomp: assert( (pc-startpc)>>2 <= 0xffff ); s_pCurBlockEx->size = (pc-startpc)>>2; - for(i = 1; i < (u32)s_pCurBlockEx->size; ++i) { + for(i = 1; i < (u32)s_pCurBlockEx->size; i++) { if (!s_pCurBlock[i].GetStartPC()) - s_pCurBlock[i].SetStartPC( startpc ); + s_pCurBlock[i].SetStartPC(startpc); } @@ -1920,9 +1923,6 @@ StartRecomp: } #endif - // set the block ptr - AddBaseBlockEx(s_pCurBlockEx, 0); - if( !(pc&0x10000000) ) maxrecmem = std::max( (pc&~0xa0000000), maxrecmem );