recompiler: document the register allocation

This commit is contained in:
Gregory Hainaut 2016-02-05 00:26:19 +01:00
parent 224370e866
commit 6ac76c86d7
2 changed files with 92 additions and 7 deletions

View File

@ -47,12 +47,15 @@ _x86regs x86regs[iREGCNT_GPR], s_saveX86regs[iREGCNT_GPR];
static int s_xmmchecknext = 0; static int s_xmmchecknext = 0;
// Clear current register mapping structure
// Clear allocation counter
void _initXMMregs() { void _initXMMregs() {
memzero( xmmregs ); memzero( xmmregs );
g_xmmAllocCounter = 0; g_xmmAllocCounter = 0;
s_xmmchecknext = 0; s_xmmchecknext = 0;
} }
// Get a pointer to the physical register (GPR / FPU / VU etc..)
__fi void* _XMMGetAddr(int type, int reg, VURegs *VU) __fi void* _XMMGetAddr(int type, int reg, VURegs *VU)
{ {
switch (type) { switch (type) {
@ -79,6 +82,14 @@ __fi void* _XMMGetAddr(int type, int reg, VURegs *VU)
return NULL; return NULL;
} }
// Get the index of a free register
// Step1: check any available register (inuse == 0)
// Step2: check registers that are not live (both EEINST_LIVE* are cleared)
// Step3: check registers that won't use SSE in the future (likely broken as EEINST_XMM isn't set properly)
// Step4: take a randome register
//
// Note: I don't understand why we don't check register that aren't useful anymore
// (i.e EEINST_USED is cleared)
int _getFreeXMMreg() int _getFreeXMMreg()
{ {
int i, tempi; int i, tempi;
@ -140,6 +151,7 @@ int _getFreeXMMreg()
throw Exception::FailedToAllocateRegister(); throw Exception::FailedToAllocateRegister();
} }
// Reserve a XMM register for temporary operation.
int _allocTempXMMreg(XMMSSEType type, int xmmreg) { int _allocTempXMMreg(XMMSSEType type, int xmmreg) {
if (xmmreg == -1) if (xmmreg == -1)
xmmreg = _getFreeXMMreg(); xmmreg = _getFreeXMMreg();
@ -155,6 +167,7 @@ int _allocTempXMMreg(XMMSSEType type, int xmmreg) {
return xmmreg; return xmmreg;
} }
#ifndef DISABLE_SVU
int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode) { int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode) {
int i; int i;
int readfromreg = -1; int readfromreg = -1;
@ -208,7 +221,14 @@ int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode) {
return xmmreg; return xmmreg;
} }
#endif
// Search register "reg" of type "type" which is inuse
// If register doesn't have the read flag but mode is read
// then populate the register from the memory
// Note: There is a special HALF mode (to handle low 64 bits copy) but it seems to be unused
//
// So basically it is mostly used to set the mode of the register, and load value if we need to read it
int _checkXMMreg(int type, int reg, int mode) int _checkXMMreg(int type, int reg, int mode)
{ {
int i; int i;
@ -238,6 +258,7 @@ int _checkXMMreg(int type, int reg, int mode)
return -1; return -1;
} }
#ifndef DISABLE_SVU
int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode) { int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode) {
int i; int i;
int readfromreg = -1; int readfromreg = -1;
@ -294,7 +315,15 @@ int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode) {
return xmmreg; return xmmreg;
} }
#endif
// Fully allocate a FPU register
// first trial:
// search an already reserved reg then populate it if we read it
// Second trial:
// reserve a new reg, then populate it if we read it
//
// Note: FPU are always in XMM register
int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode) { int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode) {
int i; int i;
@ -332,6 +361,8 @@ int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode) {
return xmmreg; return xmmreg;
} }
// In short try to allocate a GPR register. Code is an uterly mess
// due to XMM/MMX/X86 crazyness !
int _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode) int _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode)
{ {
int i; int i;
@ -439,6 +470,8 @@ int _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode)
return xmmreg; return xmmreg;
} }
// Same code as _allocFPtoXMMreg but for the FPU ACC register
// (seriously boy you could have factorized it)
int _allocFPACCtoXMMreg(int xmmreg, int mode) int _allocFPACCtoXMMreg(int xmmreg, int mode)
{ {
int i; int i;
@ -477,6 +510,7 @@ int _allocFPACCtoXMMreg(int xmmreg, int mode)
return xmmreg; return xmmreg;
} }
#ifndef DISABLE_SVU
void _addNeededVFtoXMMreg(int vfreg) { void _addNeededVFtoXMMreg(int vfreg) {
int i; int i;
@ -489,7 +523,10 @@ void _addNeededVFtoXMMreg(int vfreg) {
xmmregs[i].needed = 1; xmmregs[i].needed = 1;
} }
} }
#endif
// Mark reserved GPR reg as needed. It won't be evicted anymore.
// You must use _clearNeededXMMregs to clear the flag
void _addNeededGPRtoXMMreg(int gprreg) void _addNeededGPRtoXMMreg(int gprreg)
{ {
int i; int i;
@ -505,6 +542,7 @@ void _addNeededGPRtoXMMreg(int gprreg)
} }
} }
#ifndef DISABLE_SVU
void _addNeededACCtoXMMreg() { void _addNeededACCtoXMMreg() {
int i; int i;
@ -517,7 +555,10 @@ void _addNeededACCtoXMMreg() {
break; break;
} }
} }
#endif
// Mark reserved FPU reg as needed. It won't be evicted anymore.
// You must use _clearNeededXMMregs to clear the flag
void _addNeededFPtoXMMreg(int fpreg) { void _addNeededFPtoXMMreg(int fpreg) {
int i; int i;
@ -532,6 +573,8 @@ void _addNeededFPtoXMMreg(int fpreg) {
} }
} }
// Mark reserved FPU ACC reg as needed. It won't be evicted anymore.
// You must use _clearNeededXMMregs to clear the flag
void _addNeededFPACCtoXMMreg() { void _addNeededFPACCtoXMMreg() {
int i; int i;
@ -545,6 +588,8 @@ void _addNeededFPACCtoXMMreg() {
} }
} }
// Clear needed flags of all registers
// Written register will set MODE_READ (aka data is valid, no need to load it)
void _clearNeededXMMregs() { void _clearNeededXMMregs() {
int i; int i;
@ -564,6 +609,7 @@ void _clearNeededXMMregs() {
} }
} }
#ifndef DISABLE_SVU
void _deleteVFtoXMMreg(int reg, int vu, int flush) void _deleteVFtoXMMreg(int reg, int vu, int flush)
{ {
int i; int i;
@ -634,7 +680,9 @@ void _deleteVFtoXMMreg(int reg, int vu, int flush)
} }
} }
} }
#endif
#if 0
void _deleteACCtoXMMreg(int vu, int flush) void _deleteACCtoXMMreg(int vu, int flush)
{ {
int i; int i;
@ -695,8 +743,11 @@ void _deleteACCtoXMMreg(int vu, int flush)
} }
} }
} }
#endif
// when flush is 1 or 2, only commits the reg to mem (still leave its xmm entry) // Flush is 0: _freeXMMreg. Flush in memory if MODE_WRITE. Clear inuse
// Flush is 1: Flush in memory. But register is still valid
// Flush is 2: like 0 ...
void _deleteGPRtoXMMreg(int reg, int flush) void _deleteGPRtoXMMreg(int reg, int flush)
{ {
int i; int i;
@ -731,6 +782,9 @@ void _deleteGPRtoXMMreg(int reg, int flush)
} }
} }
// Flush is 0: _freeXMMreg. Flush in memory if MODE_WRITE. Clear inuse
// Flush is 1: Flush in memory. But register is still valid
// Flush is 2: drop register content
void _deleteFPtoXMMreg(int reg, int flush) void _deleteFPtoXMMreg(int reg, int flush)
{ {
int i; int i;
@ -758,6 +812,9 @@ void _deleteFPtoXMMreg(int reg, int flush)
} }
} }
// Free cached register
// Step 1: flush content in memory if MODE_WRITE
// Step 2: clear 'inuse' field
void _freeXMMreg(u32 xmmreg) void _freeXMMreg(u32 xmmreg)
{ {
pxAssert( xmmreg < iREGCNT_XMM ); pxAssert( xmmreg < iREGCNT_XMM );
@ -871,6 +928,7 @@ void _freeXMMreg(u32 xmmreg)
xmmregs[xmmreg].inuse = 0; xmmregs[xmmreg].inuse = 0;
} }
// Return the number of inuse XMM register that have the MODE_WRITE flag
int _getNumXMMwrite() int _getNumXMMwrite()
{ {
int num = 0, i; int num = 0, i;
@ -881,6 +939,9 @@ int _getNumXMMwrite()
return num; return num;
} }
// Step1: check any available register (inuse == 0)
// Step2: check registers that are not live (both EEINST_LIVE* are cleared)
// Step3: check registers that are not useful anymore (EEINST_USED cleared)
u8 _hasFreeXMMreg() u8 _hasFreeXMMreg()
{ {
int i; int i;
@ -911,6 +972,7 @@ u8 _hasFreeXMMreg()
return 0; return 0;
} }
#if 0
void _moveXMMreg(int xmmreg) void _moveXMMreg(int xmmreg)
{ {
int i; int i;
@ -931,7 +993,9 @@ void _moveXMMreg(int xmmreg)
xmmregs[xmmreg].inuse = 0; xmmregs[xmmreg].inuse = 0;
xMOVDQA(xRegisterSSE(i), xRegisterSSE(xmmreg)); xMOVDQA(xRegisterSSE(i), xRegisterSSE(xmmreg));
} }
#endif
// Flush in memory all inuse registers but registers are still valid
void _flushXMMregs() void _flushXMMregs()
{ {
int i; int i;
@ -949,6 +1013,7 @@ void _flushXMMregs()
} }
} }
// Flush in memory all inuse registers. All registers are invalid
void _freeXMMregs() void _freeXMMregs()
{ {
int i; int i;
@ -1004,6 +1069,8 @@ int _signExtendXMMtoM(uptr to, x86SSERegType from, int candestroy)
pxAssume( false ); pxAssume( false );
} }
// Seem related to the mix between XMM/x86 in order to avoid a couple of move
// But it is quite obscure !!!
int _allocCheckGPRtoXMM(EEINST* pinst, int gprreg, int mode) int _allocCheckGPRtoXMM(EEINST* pinst, int gprreg, int mode)
{ {
if( pinst->regs[gprreg] & EEINST_XMM ) return _allocGPRtoXMMreg(-1, gprreg, mode); if( pinst->regs[gprreg] & EEINST_XMM ) return _allocGPRtoXMMreg(-1, gprreg, mode);
@ -1011,6 +1078,8 @@ int _allocCheckGPRtoXMM(EEINST* pinst, int gprreg, int mode)
return _checkXMMreg(XMMTYPE_GPRREG, gprreg, mode); return _checkXMMreg(XMMTYPE_GPRREG, gprreg, mode);
} }
// Seem related to the mix between XMM/x86 in order to avoid a couple of move
// But it is quite obscure !!!
int _allocCheckFPUtoXMM(EEINST* pinst, int fpureg, int mode) int _allocCheckFPUtoXMM(EEINST* pinst, int fpureg, int mode)
{ {
if( pinst->fpuregs[fpureg] & EEINST_XMM ) return _allocFPtoXMMreg(-1, fpureg, mode); if( pinst->fpuregs[fpureg] & EEINST_XMM ) return _allocFPtoXMMreg(-1, fpureg, mode);
@ -1050,6 +1119,7 @@ u32 _recIsRegWritten(EEINST* pinst, int size, u8 xmmtype, u8 reg)
return 0; return 0;
} }
#if 0
u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg) u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg)
{ {
u32 i, inst = 1; u32 i, inst = 1;
@ -1068,6 +1138,7 @@ u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg)
return 0; return 0;
} }
#endif
void _recFillRegister(EEINST& pinst, int type, int reg, int write) void _recFillRegister(EEINST& pinst, int type, int reg, int write)
{ {

View File

@ -163,19 +163,14 @@ struct _xmmregs {
void _initXMMregs(); void _initXMMregs();
int _getFreeXMMreg(); int _getFreeXMMreg();
int _allocTempXMMreg(XMMSSEType type, int xmmreg); int _allocTempXMMreg(XMMSSEType type, int xmmreg);
int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode);
int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode); int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode);
int _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode); int _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode);
int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode);
int _allocFPACCtoXMMreg(int xmmreg, int mode); int _allocFPACCtoXMMreg(int xmmreg, int mode);
int _checkXMMreg(int type, int reg, int mode); int _checkXMMreg(int type, int reg, int mode);
void _addNeededVFtoXMMreg(int vfreg);
void _addNeededACCtoXMMreg();
void _addNeededFPtoXMMreg(int fpreg); void _addNeededFPtoXMMreg(int fpreg);
void _addNeededFPACCtoXMMreg(); void _addNeededFPACCtoXMMreg();
void _addNeededGPRtoXMMreg(int gprreg); void _addNeededGPRtoXMMreg(int gprreg);
void _clearNeededXMMregs(); void _clearNeededXMMregs();
void _deleteVFtoXMMreg(int reg, int vu, int flush);
//void _deleteACCtoXMMreg(int vu, int flush); //void _deleteACCtoXMMreg(int vu, int flush);
void _deleteGPRtoXMMreg(int reg, int flush); void _deleteGPRtoXMMreg(int reg, int flush);
void _deleteFPtoXMMreg(int reg, int flush); void _deleteFPtoXMMreg(int reg, int flush);
@ -185,6 +180,13 @@ void _flushXMMregs();
u8 _hasFreeXMMreg(); u8 _hasFreeXMMreg();
void _freeXMMregs(); void _freeXMMregs();
int _getNumXMMwrite(); int _getNumXMMwrite();
#ifndef DISABLE_SVU
int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode);
int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode);
void _addNeededVFtoXMMreg(int vfreg);
void _addNeededACCtoXMMreg();
void _deleteVFtoXMMreg(int reg, int vu, int flush);
#endif
void _signExtendSFtoM(uptr mem); void _signExtendSFtoM(uptr mem);
@ -196,6 +198,18 @@ int _signExtendXMMtoM(uptr to, x86SSERegType from, int candestroy); // returns t
////////////////////// //////////////////////
// Instruction Info // // Instruction Info //
////////////////////// //////////////////////
// Liveness information for the noobs :)
// Let's take I instructions that read from RN register set and write to
// WN register set.
// 1/ EEINST_USED will be set in register N of instruction I1, if and only if RN or WN is used in the insruction I2 with I2 >= I1.
// In others words, it will be set on [I0, ILast] with ILast the last instruction that use the register.
// 2/ EEINST_LASTUSE will be set in register N the last instruction that use the register.
// Note: EEINST_USED will be cleared after EEINST_LASTUSE
// My guess: both variable allow to detect register that can be flushed for free
//
// 3/ EEINST_LIVE* is cleared when register is written. And set again when register is read.
// My guess: the purpose is to detect the usage hole in the flow
#define EEINST_LIVE0 1 // if var is ever used (read or write) #define EEINST_LIVE0 1 // if var is ever used (read or write)
#define EEINST_LIVE2 4 // if cur var's next 64 bits are needed #define EEINST_LIVE2 4 // if cur var's next 64 bits are needed
#define EEINST_LASTUSE 8 // if var isn't written/read anymore #define EEINST_LASTUSE 8 // if var isn't written/read anymore
@ -227,7 +241,7 @@ extern void _recClearInst(EEINST* pinst);
// returns the number of insts + 1 until written (0 if not written) // returns the number of insts + 1 until written (0 if not written)
extern u32 _recIsRegWritten(EEINST* pinst, int size, u8 xmmtype, u8 reg); extern u32 _recIsRegWritten(EEINST* pinst, int size, u8 xmmtype, u8 reg);
// returns the number of insts + 1 until used (0 if not used) // returns the number of insts + 1 until used (0 if not used)
extern u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg); //extern u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg);
extern void _recFillRegister(EEINST& pinst, int type, int reg, int write); extern void _recFillRegister(EEINST& pinst, int type, int reg, int write);
static __fi bool EEINST_ISLIVE64(u32 reg) { return !!(g_pCurInstInfo->regs[reg] & (EEINST_LIVE0)); } static __fi bool EEINST_ISLIVE64(u32 reg) { return !!(g_pCurInstInfo->regs[reg] & (EEINST_LIVE0)); }