From fe915cfa6730f664ba19836ce9a97f4f9d6daa6d Mon Sep 17 00:00:00 2001 From: cottonvibes Date: Thu, 4 Mar 2010 06:18:31 +0000 Subject: [PATCH] mVU: work-in-progress cache and memory model rewrite. currently not 100% stable, this is mainly a backup before i do more big changes... git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2663 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/x86/microVU.cpp | 170 ++++++++++++++++++++-------------- pcsx2/x86/microVU.h | 49 ++++++---- pcsx2/x86/microVU_Compile.inl | 51 +++------- pcsx2/x86/microVU_Execute.inl | 5 +- pcsx2/x86/microVU_Misc.h | 2 +- 5 files changed, 147 insertions(+), 130 deletions(-) diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index 3aa4d43144..02e57b7eec 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -94,7 +94,6 @@ _f void mVUinit(VURegs* vuRegsPtr, int vuIndex) { mVU->cacheSize = mVUcacheSize; mVU->prog.max = mMaxProg - 1; mVU->prog.prog = (microProgram*)_aligned_malloc(sizeof(microProgram)*(mVU->prog.max+1), 64); - mVU->prog.progList = new int[mMaxProg]; mVU->regAlloc = new microRegAlloc(mVU->regs); mVUprint((vuIndex) ? "microVU1: init" : "microVU0: init"); @@ -108,6 +107,10 @@ _f void mVUinit(VURegs* vuRegsPtr, int vuIndex) { memset(mVU->cache, 0xcc, mVU->cacheSize + 0x1000); memset(mVU->prog.prog, 0, sizeof(microProgram)*(mVU->prog.max+1)); + for (u32 i = 0; i < (mVU->progSize / 2); i++) { + mVU->prog.list[i].list = new deque(); + } + // Setup Entrance/Exit Points x86SetPtr(mVU->cache); mVUdispatcherA(mVU); @@ -139,10 +142,17 @@ _f void mVUreset(mV) { mVU->prog.x86ptr = z; mVU->prog.x86end = (u8*)((uptr)z + (uptr)(mVU->cacheSize - (_1mb * 3))); // 3mb "Safe Zone" + for (u32 i = 0; i < (mVU->progSize / 2); i++) { + mVU->prog.list[i].list->clear(); + mVU->prog.list[i].size = 0; + mVU->prog.list[i].quick = NULL; + mVU->prog.list[i].quickIdx = -1; + } + for (int i = 0; i <= mVU->prog.max; i++) { - if (!mVU->index) mVUclearProg<0>(i); - else mVUclearProg<1>(i); - mVU->prog.progList[i] = i; + if (!mVU->index) mVUclearProg<0>(mVU->prog.prog[i]); + else mVUclearProg<1>(mVU->prog.prog[i]); + mVU->prog.prog[i].idx = i; } } @@ -162,7 +172,9 @@ _f void mVUclose(mV) { } safe_aligned_free(mVU->prog.prog); } - safe_delete_array(mVU->prog.progList); + for (u32 i = 0; i < (mVU->progSize / 2); i++) { + safe_delete(mVU->prog.list[i].list); + } safe_delete(mVU->regAlloc); } @@ -171,6 +183,10 @@ _f void mVUclear(mV, u32 addr, u32 size) { if (!mVU->prog.cleared) { memzero(mVU->prog.lpState); // Clear pipeline state mVU->prog.cleared = 1; // Next execution searches/creates a new microprogram + for (u32 i = 0; i < (mVU->progSize / 2); i++) { + mVU->prog.list[i].quick = NULL; // Clear current quick-reference prog list + mVU->prog.list[i].quickIdx = -1; // Set to 'invalid' index + } } } @@ -179,13 +195,12 @@ _f void mVUclear(mV, u32 addr, u32 size) { //------------------------------------------------------------------ // Clears program data -_mVUt _f void mVUclearProg(int progIndex) { +_mVUt _f void mVUclearProg(microProgram& program) { microVU* mVU = mVUx; - microProgram& program = mVU->prog.prog[progIndex]; - program.used = 0; program.age = isDead; program.frame = mVU->prog.curFrame; + program.startPC = 0x7fffffff; for (int j = 0; j <= program.ranges.max; j++) { program.ranges.range[j][0] = -1; // Set range to program.ranges.range[j][1] = -1; // indeterminable status @@ -196,25 +211,18 @@ _mVUt _f void mVUclearProg(int progIndex) { } } -// Caches Micro Program -_mVUt _f void mVUcacheProg(int progIndex) { - microVU* mVU = mVUx; - if (!vuIndex) memcpy_const(mVU->prog.prog[progIndex].data, mVU->regs->Micro, 0x1000); - else memcpy_const(mVU->prog.prog[progIndex].data, mVU->regs->Micro, 0x4000); - mVUdumpProg(progIndex); +/* +microProgram* mVUcreateProg(int progIndex, int startPC) { + return (microProgram*)_aligned_malloc(sizeof(microProgram), 64); } +*/ -// Sorts the program list (Moves progIndex to Beginning of ProgList) -_f void mVUsortProg(mV, int progIndex) { - int* temp = new int[mVU->prog.max+1]; - int offset = 0; - for (int i = 0; i <=(mVU->prog.max-1); i++) { - if (progIndex == mVU->prog.progList[i]) offset = 1; - temp[i+1] = mVU->prog.progList[i+offset]; - } - temp[0] = progIndex; - delete[] mVU->prog.progList; - mVU->prog.progList = temp; +// Caches Micro Program +_mVUt _f void mVUcacheProg(microProgram& prog) { + microVU* mVU = mVUx; + if (!vuIndex) memcpy_const(prog.data, mVU->regs->Micro, 0x1000); + else memcpy_const(prog.data, mVU->regs->Micro, 0x4000); + mVUdumpProg(prog.idx); } // Finds the least used program, (if program list full clears and returns an old program; if not-full, returns free program) @@ -224,26 +232,37 @@ _mVUt _f int mVUfindLeastUsedProg() { for (int i = 0; i <= mVU->prog.max; i++) { if (mVU->prog.prog[i].age == isDead) { mVU->prog.total++; - mVUcacheProg(i); // Cache Micro Program + mVUcacheProg(mVU->prog.prog[i]); // Cache Micro Program mVU->prog.prog[i].age = isYoung; mVU->prog.prog[i].used = 1; - mVUsortProg(mVU, i); Console.WriteLn(Color_Orange, "microVU%d: Cached MicroPrograms = [%03d] [%03d]", vuIndex, i+1, mVU->prog.total+1); return i; } } + // If we reach this, it means all program slots are used, so delete old ones... static int clearIdx = 0; int pIdx = clearIdx; for (int i = 0; i < ((mVU->prog.max+1)/4); i++) { - mVUclearProg(clearIdx); + if (mVU->prog.prog[i].used) continue; // Don't delete currently executing program(s) + assert(mVU->prog.prog[i].startPC < (mVU->progSize / 2)); + microProgramList& list = mVU->prog.list[mVU->prog.prog[i].startPC]; + deque::iterator it = list.list->begin(); + for ( ; it != list.list->end(); it++) { + if (it[0] == &mVU->prog.prog[i]) { + list.list->erase(it); + list.quick = NULL; + DevCon.WriteLn("Deleting List Reference!"); + break; + } + } + mVUclearProg(mVU->prog.prog[clearIdx]); clearIdx = aWrap(clearIdx+1, mVU->prog.max); } mVU->prog.total -= ((mVU->prog.max+1)/4)-1; - mVUcacheProg(pIdx); // Cache Micro Program + mVUcacheProg(mVU->prog.prog[pIdx]); // Cache Micro Program mVU->prog.prog[pIdx].age = isYoung; mVU->prog.prog[pIdx].used = 1; - mVUsortProg(mVU, pIdx); Console.WriteLn(Color_Orange, "microVU%d: Cached MicroPrograms = [%03d] [%03d]", vuIndex, pIdx+1, mVU->prog.total+1); return pIdx; } @@ -256,28 +275,28 @@ _f void mVUvsyncUpdate(mV) { mVU->prog.prog[i].used = 0; mVU->prog.prog[i].frame = mVU->prog.curFrame; } - else { // Age Micro Program that wasn't used + /*else { // Age Micro Program that wasn't used s32 diff = mVU->prog.curFrame - mVU->prog.prog[i].frame; - if (diff >= (360 * 10)) { + if (diff >= (60 * 1)) { if (i == mVU->prog.cur) continue; // Don't Age/Kill last used program mVU->prog.total--; if (!mVU->index) mVUclearProg<0>(i); else mVUclearProg<1>(i); DevCon.WriteLn("microVU%d: Killing Dead Program [%03d]", mVU->index, i+1); } - elif(diff >= (60 * 1)) { mVU->prog.prog[i].age = isOld; } - elif(diff >= (20 * 1)) { mVU->prog.prog[i].age = isAged; } - } + //elif(diff >= (60 * 1)) { mVU->prog.prog[i].age = isOld; } + //elif(diff >= (20 * 1)) { mVU->prog.prog[i].age = isAged; } + }*/ } mVU->prog.curFrame++; } -_mVUt _f bool mVUcmpPartial(int progIndex) { +_mVUt _f bool mVUcmpPartial(microProgram& prog) { microVU* mVU = mVUx; - for (int i = 0; i <= mVUprogI.ranges.total; i++) { - if ((mVUprogI.ranges.range[i][0] < 0) - || (mVUprogI.ranges.range[i][1] < 0)) { DevCon.Error("microVU%d: Negative Range![%d][%d]", mVU->index, i, mVUprogI.ranges.total); } - if (memcmp_mmx(cmpOffset(mVUprogI.data), cmpOffset(mVU->regs->Micro), ((mVUprogI.ranges.range[i][1] + 8) - mVUprogI.ranges.range[i][0]))) { + for (int i = 0; i <= prog.ranges.total; i++) { + if((prog.ranges.range[i][0] < 0) + || (prog.ranges.range[i][1] < 0)) { DevCon.Error("microVU%d: Negative Range![%d][%d]", mVU->index, i, prog.ranges.total); } + if (memcmp_mmx(cmpOffset(prog.data), cmpOffset(mVU->regs->Micro), ((prog.ranges.range[i][1] + 8) - prog.ranges.range[i][0]))) { return 0; } } @@ -285,46 +304,53 @@ _mVUt _f bool mVUcmpPartial(int progIndex) { } // Compare Cached microProgram to mVU->regs->Micro -_mVUt _f bool mVUcmpProg(int progIndex, const int checkAge, const bool cmpWholeProg) { - microVU* mVU = mVUx; - if (checkAge == mVUprogI.age) { - if ((cmpWholeProg && !memcmp_mmx((u8*)mVUprogI.data, mVU->regs->Micro, mVU->microMemSize)) - || (!cmpWholeProg && mVUcmpPartial(progIndex))) { - mVU->prog.cur = progIndex; - mVU->prog.cleared = 0; - mVU->prog.isSame = cmpWholeProg ? 1 : -1; - mVU->prog.prog[progIndex].used = 1; - mVU->prog.prog[progIndex].age = isYoung; - return 1; - } +_mVUt _f bool mVUcmpProg(microProgram& prog, const bool cmpWholeProg) { + microVU* mVU = mVUx; + if (prog.age == isDead) return 0; + if ((cmpWholeProg && !memcmp_mmx((u8*)prog.data, mVU->regs->Micro, mVU->microMemSize)) + || (!cmpWholeProg && mVUcmpPartial(prog))) { + mVU->prog.cleared = 0; + mVU->prog.cur = prog.idx; + mVU->prog.isSame = cmpWholeProg ? 1 : -1; + prog.used = 1; + prog.age = isYoung; + return 1; } return 0; } -// Searches for Cached Micro Program and sets prog.cur to it (returns 1 if program found, else returns 0) -_mVUt _f int mVUsearchProg() { +// Searches for Cached Micro Program and sets prog.cur to it (returns entry-point to program) +_mVUt _f void* mVUsearchProg(u32 startPC, uptr pState) { microVU* mVU = mVUx; - if (mVU->prog.cleared) { // If cleared, we need to search for new program - for (int i = mVU->prog.max; i >= 0; i--) { - if (mVUcmpProg(mVU->prog.progList[i], 0, 0)) - return 1; // Check Young Programs - } - for (int i = mVU->prog.max; i >= 0; i--) { - if (mVUcmpProg(mVU->prog.progList[i], 1, 0)) - return 1; // Check Aged Programs - } - for (int i = mVU->prog.max; i >= 0; i--) { - if (mVUcmpProg(mVU->prog.progList[i], 2, 0)) - return 1; // Check Old Programs + microProgramList& list = mVU->prog.list[startPC/8]; + if(!list.quick) { // If null, we need to search for new program + deque::iterator it = list.list->begin(); + for ( ; it != list.list->end(); it++) { + if (mVUcmpProg(*it[0], 0)) { + microProgram* t = it[0]; + list.list->erase(it); + list.list->push_front(t); + list.quick = t->block[startPC/8]; + list.quickIdx = t->idx; + return mVUentryGet(mVU, list.quick, startPC, pState); + } } mVU->prog.cur = mVUfindLeastUsedProg(); // If cleared and program not found, make a new program instance - mVU->prog.cleared = 0; - mVU->prog.isSame = 1; - return 0; + mVU->prog.cleared = 0; + mVU->prog.isSame = 1; + mVUcurProg.startPC = startPC / 8; + void* entryPoint = mVUblockFetch(mVU, startPC, pState); + list.quick = mVUcurProg.block[startPC/8]; + list.quickIdx = mVUcurProg.idx; + list.list->push_front(&mVUcurProg); + DevCon.WriteLn("List[%d].Size = %d", startPC/8, list.list->size()); + return entryPoint; } - mVU->prog.prog[mVU->prog.cur].used = 1; - mVU->prog.prog[mVU->prog.cur].age = isYoung; - return 1; // If !cleared, then we're still on the same program as last-time ;) + mVU->prog.cur = list.quickIdx; + mVU->prog.isSame = -1; + mVUcurProg.used = 1; + mVUcurProg.age = isYoung; + return mVUentryGet(mVU, list.quick, startPC, pState); // If list.quick, then we've already found and recompiled the program ;) } //------------------------------------------------------------------ diff --git a/pcsx2/x86/microVU.h b/pcsx2/x86/microVU.h index cef0b63f9e..518bd3a1cd 100644 --- a/pcsx2/x86/microVU.h +++ b/pcsx2/x86/microVU.h @@ -22,6 +22,8 @@ class AsciiFile; using namespace x86Emitter; #include "microVU_IR.h" #include "microVU_Misc.h" +using namespace std; +#include struct microBlockLink { microBlock* block; @@ -119,6 +121,7 @@ enum microProgramAge { }; #define mProgSize (0x4000/4) +#define mMaxProg ((mVU->index)?400:8) // The amount of Micro Programs Recs will 'remember' struct microProgram { u32 data [mProgSize]; // Holds a copy of the VU microProgram microBlockManager* block[mProgSize/2]; // Array of Block Managers @@ -126,23 +129,32 @@ struct microProgram { u32 frame; // Frame # the program was last used on u32 used; // Program was used this frame? int age; // Program age... Young, Aged, Old, or Dead... + int idx; // Program idx in array[mMaxProg] + u32 startPC; // Start PC of this program +}; + +struct microProgramList { + deque* list; // List of microPrograms who start with the same startPC value + microBlockManager* quick; // Quick reference to valid microBlockManager for current startPC + int quickIdx; // Index of the microProgram who is the owner of 'quick' + int size; // Current size of the list... }; -#define mMaxProg ((mVU->index)?400:8) // The amount of Micro Programs Recs will 'remember' struct microProgManager { - microIR IRinfo; // IR information - microProgram* prog; // Store MicroPrograms in memory - int* progList; // List of program indexes ordered by age (ordered from newest to oldest) - int max; // Max Number of MicroPrograms minus 1 - int total; // Total Number of valid MicroPrograms minus 1 - int cur; // Index to Current MicroProgram thats running (-1 = uncached) - int isSame; // Current cached microProgram is Exact Same program as mVU->regs->Micro (-1 = unknown, 0 = No, 1 = Yes) - int cleared; // Micro Program is Indeterminate so must be searched for (and if no matches are found then recompile a new one) - u32 curFrame; // Frame Counter - u8* x86ptr; // Pointer to program's recompilation code - u8* x86start; // Start of program's rec-cache - u8* x86end; // Limit of program's rec-cache - microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution) + microIR IRinfo; // IR information + microProgram* prog; // Cache MicroPrograms in memory (indirect jumps are treated as new programs) + microProgramList list[mProgSize/2]; // List of microProgram references indexed by startPC values + //int* progList; // List of program indexes ordered by age (ordered from newest to oldest) + int max; // Max Number of MicroPrograms minus 1 + int total; // Total Number of valid MicroPrograms minus 1 + int cur; // Index to Current MicroProgram thats running (-1 = uncached) + int isSame; // Current cached microProgram is Exact Same program as mVU->regs->Micro (-1 = unknown, 0 = No, 1 = Yes) + int cleared; // Micro Program is Indeterminate so must be searched for (and if no matches are found then recompile a new one) + u32 curFrame; // Frame Counter + u8* x86ptr; // Pointer to program's recompilation code + u8* x86start; // Start of program's rec-cache + u8* x86end; // Limit of program's rec-cache + microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution) }; #define mVUcacheSize ((mMaxProg < 20) ? (_1mb * 10) : (mMaxProg * (_1mb * 0.5))) // 0.5mb per program @@ -203,11 +215,10 @@ mVUop(mVUopU); mVUop(mVUopL); // Private Functions - _f void mVUsortProg(mV, int progIndex); -_mVUt _f void mVUclearProg(int progIndex); -_mVUt _f void mVUcacheProg(int progIndex); -_mVUt _f int mVUfindLeastUsedProg(microVU* mVU); -_mVUt _f int mVUsearchProg(); +_mVUt _f void mVUclearProg(microProgram& prog); +_mVUt _f void mVUcacheProg(microProgram& prog); +_mVUt _f int mVUfindLeastUsedProg(); +_mVUt _f int mVUsearchProg(u32 startPC); void* __fastcall mVUexecuteVU0(u32 startPC, u32 cycles); void* __fastcall mVUexecuteVU1(u32 startPC, u32 cycles); diff --git a/pcsx2/x86/microVU_Compile.inl b/pcsx2/x86/microVU_Compile.inl index 3e1612056c..f121e8f487 100644 --- a/pcsx2/x86/microVU_Compile.inl +++ b/pcsx2/x86/microVU_Compile.inl @@ -31,42 +31,14 @@ // Helper Functions //------------------------------------------------------------------ -// Used by checkIfSibling below... -bool cmpSibling(mV, int progIndex) { - if (mVUprogI.age == isDead) return 0; - for (int i = 0; i <= mVUprogI.ranges.total; i++) { - if ((mVUprogI.ranges.range[i][0] < 0) - || (mVUprogI.ranges.range[i][1] < 0)) { DevCon.Error("microVU%d: Negative Range![%d][%d]", mVU->index, i, mVUprogI.ranges.total); } - if (memcmp_mmx(cmpOffset(mVUprogI.data), cmpOffset(mVU->regs->Micro), ((mVUprogI.ranges.range[i][1] + 8) - mVUprogI.ranges.range[i][0]))) { - return 0; - } - } - return 1; -} - -// Since we use partial-program comparisons to search cached microPrograms, -// it is possible that another cached microProgram we have also would've -// passed the comparison check. This function checks how often this happens... -_f void checkIfSibling(mV) { - if (mVU->prog.isSame != -1) return; - for (int i = mVU->prog.max; i >= 0; i--) { - if (i != mVU->prog.cur && cmpSibling(mVU, i)) { - if (mVU->prog.prog[i].block[iPC/2] != NULL) { - DevCon.WriteLn("mVU: microProgram Sibling Detected! [%d][%d]", mVU->prog.cur, i); - } - } - } -} - // Used by mVUsetupRange _f void mVUcheckIsSame(mV) { - if (0) checkIfSibling(mVU); if (mVU->prog.isSame == -1) { mVU->prog.isSame = !memcmp_mmx((u8*)mVUcurProg.data, mVU->regs->Micro, mVU->microMemSize); } if (mVU->prog.isSame == 0) { - if (!isVU1) mVUcacheProg<0>(mVU->prog.cur); - else mVUcacheProg<1>(mVU->prog.cur); + if (!isVU1) mVUcacheProg<0>(mVU->prog.prog[mVU->prog.cur]); + else mVUcacheProg<1>(mVU->prog.prog[mVU->prog.cur]); mVU->prog.isSame = 1; } } @@ -456,19 +428,26 @@ _r void* mVUcompile(microVU* mVU, u32 startPC, uptr pState) { return thisPtr; } +// Returns the entry point of the block (compiles it if not found) +_f void* mVUentryGet(microVU* mVU, microBlockManager* block, u32 startPC, uptr pState) { + microBlock* pBlock = block->search((microRegInfo*)pState); + if (pBlock) return pBlock->x86ptrStart; + else return mVUcompile(mVU, startPC, pState); +} + // Search for Existing Compiled Block (if found, return x86ptr; else, compile and return x86ptr) _f void* mVUblockFetch(microVU* mVU, u32 startPC, uptr pState) { if (startPC > mVU->microMemSize-8) { DevCon.Error("microVU%d: invalid startPC [%04x]", mVU->index, startPC); } - startPC &= mVU->microMemSize-8; - + startPC &= mVU->microMemSize-8; + blockCreate(startPC/8); - microBlock* pBlock = mVUblocks[startPC/8]->search((microRegInfo*)pState); - if (pBlock) { return pBlock->x86ptrStart; } - else { return mVUcompile(mVU, startPC, pState); } + return mVUentryGet(mVU, mVUblocks[startPC/8], startPC, pState); } // mVUcompileJIT() - Called By JR/JALR during execution _mVUt void* __fastcall mVUcompileJIT(u32 startPC, uptr pState) { - return mVUblockFetch(mVUx, startPC, pState); + //DevCon.WriteLn("JR/JALR!"); + //return mVUblockFetch(mVUx, startPC, pState); + return mVUsearchProg(startPC, pState); // Find and set correct program } diff --git a/pcsx2/x86/microVU_Execute.inl b/pcsx2/x86/microVU_Execute.inl index dfd54bfe45..4258798b67 100644 --- a/pcsx2/x86/microVU_Execute.inl +++ b/pcsx2/x86/microVU_Execute.inl @@ -105,12 +105,13 @@ _mVUt void* __fastcall mVUexecute(u32 startPC, u32 cycles) { microVU* mVU = mVUx; mVUprint("microVU%x: startPC = 0x%x, cycles = 0x%x", vuIndex, startPC, cycles); - mVUsearchProg(); // Find and set correct program + //mVUsearchProg(startPC, 0); // Find and set correct program mVU->cycles = cycles; mVU->totalCycles = cycles; xSetPtr(mVU->prog.x86ptr); // Set x86ptr to where last program left off - return mVUblockFetch(mVU, startPC, (uptr)&mVU->prog.lpState); + //return mVUblockFetch(mVU, startPC, (uptr)&mVU->prog.lpState); + return mVUsearchProg(startPC, (uptr)&mVU->prog.lpState); // Find and set correct program } //------------------------------------------------------------------ diff --git a/pcsx2/x86/microVU_Misc.h b/pcsx2/x86/microVU_Misc.h index e61713c73d..12180e76c1 100644 --- a/pcsx2/x86/microVU_Misc.h +++ b/pcsx2/x86/microVU_Misc.h @@ -190,7 +190,7 @@ typedef u32 (__fastcall *mVUCall)(void*, void*); #define branchAddr ((xPC + 8 + (_Imm11_ * 8)) & (mVU->microMemSize-8)) #define branchAddrN ((xPC + 16 + (_Imm11_ * 8)) & (mVU->microMemSize-8)) #define shufflePQ (((mVU->p) ? 0xb0 : 0xe0) | ((mVU->q) ? 0x01 : 0x04)) -#define cmpOffset(x) ((u8*)&(((u8*)x)[mVUprogI.ranges.range[i][0]])) +#define cmpOffset(x) ((u8*)&(((u8*)x)[prog.ranges.range[i][0]])) #define Rmem (uptr)&mVU->regs->VI[REG_R].UL #define aWrap(x, m) ((x > m) ? 0 : x) #define shuffleSS(x) ((x==1)?(0x27):((x==2)?(0xc6):((x==4)?(0xe1):(0xe4))))