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;
// Clear current register mapping structure
// Clear allocation counter
void _initXMMregs() {
memzero( xmmregs );
g_xmmAllocCounter = 0;
s_xmmchecknext = 0;
}
// Get a pointer to the physical register (GPR / FPU / VU etc..)
__fi void* _XMMGetAddr(int type, int reg, VURegs *VU)
{
switch (type) {
@ -79,6 +82,14 @@ __fi void* _XMMGetAddr(int type, int reg, VURegs *VU)
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 i, tempi;
@ -140,6 +151,7 @@ int _getFreeXMMreg()
throw Exception::FailedToAllocateRegister();
}
// Reserve a XMM register for temporary operation.
int _allocTempXMMreg(XMMSSEType type, int xmmreg) {
if (xmmreg == -1)
xmmreg = _getFreeXMMreg();
@ -155,6 +167,7 @@ int _allocTempXMMreg(XMMSSEType type, int xmmreg) {
return xmmreg;
}
#ifndef DISABLE_SVU
int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode) {
int i;
int readfromreg = -1;
@ -208,7 +221,14 @@ int _allocVFtoXMMreg(VURegs *VU, int xmmreg, int vfreg, int mode) {
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 i;
@ -238,6 +258,7 @@ int _checkXMMreg(int type, int reg, int mode)
return -1;
}
#ifndef DISABLE_SVU
int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode) {
int i;
int readfromreg = -1;
@ -294,7 +315,15 @@ int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode) {
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 i;
@ -332,6 +361,8 @@ int _allocFPtoXMMreg(int xmmreg, int fpreg, int mode) {
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 i;
@ -439,6 +470,8 @@ int _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode)
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 i;
@ -477,6 +510,7 @@ int _allocFPACCtoXMMreg(int xmmreg, int mode)
return xmmreg;
}
#ifndef DISABLE_SVU
void _addNeededVFtoXMMreg(int vfreg) {
int i;
@ -489,7 +523,10 @@ void _addNeededVFtoXMMreg(int vfreg) {
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)
{
int i;
@ -505,6 +542,7 @@ void _addNeededGPRtoXMMreg(int gprreg)
}
}
#ifndef DISABLE_SVU
void _addNeededACCtoXMMreg() {
int i;
@ -517,7 +555,10 @@ void _addNeededACCtoXMMreg() {
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) {
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() {
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() {
int i;
@ -564,6 +609,7 @@ void _clearNeededXMMregs() {
}
}
#ifndef DISABLE_SVU
void _deleteVFtoXMMreg(int reg, int vu, int flush)
{
int i;
@ -634,7 +680,9 @@ void _deleteVFtoXMMreg(int reg, int vu, int flush)
}
}
}
#endif
#if 0
void _deleteACCtoXMMreg(int vu, int flush)
{
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)
{
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)
{
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)
{
pxAssert( xmmreg < iREGCNT_XMM );
@ -871,6 +928,7 @@ void _freeXMMreg(u32 xmmreg)
xmmregs[xmmreg].inuse = 0;
}
// Return the number of inuse XMM register that have the MODE_WRITE flag
int _getNumXMMwrite()
{
int num = 0, i;
@ -881,6 +939,9 @@ int _getNumXMMwrite()
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()
{
int i;
@ -911,6 +972,7 @@ u8 _hasFreeXMMreg()
return 0;
}
#if 0
void _moveXMMreg(int xmmreg)
{
int i;
@ -931,7 +993,9 @@ void _moveXMMreg(int xmmreg)
xmmregs[xmmreg].inuse = 0;
xMOVDQA(xRegisterSSE(i), xRegisterSSE(xmmreg));
}
#endif
// Flush in memory all inuse registers but registers are still valid
void _flushXMMregs()
{
int i;
@ -949,6 +1013,7 @@ void _flushXMMregs()
}
}
// Flush in memory all inuse registers. All registers are invalid
void _freeXMMregs()
{
int i;
@ -1004,6 +1069,8 @@ int _signExtendXMMtoM(uptr to, x86SSERegType from, int candestroy)
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)
{
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);
}
// 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)
{
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;
}
#if 0
u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg)
{
u32 i, inst = 1;
@ -1068,6 +1138,7 @@ u32 _recIsRegUsed(EEINST* pinst, int size, u8 xmmtype, u8 reg)
return 0;
}
#endif
void _recFillRegister(EEINST& pinst, int type, int reg, int write)
{

View File

@ -163,19 +163,14 @@ struct _xmmregs {
void _initXMMregs();
int _getFreeXMMreg();
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 _allocGPRtoXMMreg(int xmmreg, int gprreg, int mode);
int _allocACCtoXMMreg(VURegs *VU, int xmmreg, int mode);
int _allocFPACCtoXMMreg(int xmmreg, int mode);
int _checkXMMreg(int type, int reg, int mode);
void _addNeededVFtoXMMreg(int vfreg);
void _addNeededACCtoXMMreg();
void _addNeededFPtoXMMreg(int fpreg);
void _addNeededFPACCtoXMMreg();
void _addNeededGPRtoXMMreg(int gprreg);
void _clearNeededXMMregs();
void _deleteVFtoXMMreg(int reg, int vu, int flush);
//void _deleteACCtoXMMreg(int vu, int flush);
void _deleteGPRtoXMMreg(int reg, int flush);
void _deleteFPtoXMMreg(int reg, int flush);
@ -185,6 +180,13 @@ void _flushXMMregs();
u8 _hasFreeXMMreg();
void _freeXMMregs();
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);
@ -196,6 +198,18 @@ int _signExtendXMMtoM(uptr to, x86SSERegType from, int candestroy); // returns t
//////////////////////
// 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_LIVE2 4 // if cur var's next 64 bits are needed
#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)
extern u32 _recIsRegWritten(EEINST* pinst, int size, u8 xmmtype, u8 reg);
// 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);
static __fi bool EEINST_ISLIVE64(u32 reg) { return !!(g_pCurInstInfo->regs[reg] & (EEINST_LIVE0)); }