pcsxr/libpcsxcore/ppc/reguse.c

424 lines
15 KiB
C

#if defined (__ppc__) || defined (__ppc64__) || defined (__powerpc__) || defined (__powerpc64__) || defined (__POWERPC__)
#include "../psxcommon.h"
#include "reguse.h"
#include "../r3000a.h"
//#define SAME_CYCLE_MODE
static const int useBSC[64] = {
/*recSPECIAL*/ REGUSE_SUB | REGUSE_SPECIAL,
/*recREGIMM*/ REGUSE_SUB | REGUSE_REGIMM,
/*recJ*/ REGUSE_JUMP,
/*recJAL*/ REGUSE_JUMP | REGUSE_R31_W,
/*recBEQ*/ REGUSE_BRANCH | REGUSE_RS_R | REGUSE_RT_R,
/*recBNE*/ REGUSE_BRANCH | REGUSE_RS_R | REGUSE_RT_R,
/*recBLEZ*/ REGUSE_BRANCH | REGUSE_RS_R,
/*recBGTZ*/ REGUSE_BRANCH | REGUSE_RS_R,
/*recADDI*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_W,
/*recADDIU*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_W,
/*recSLTI*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_W,
/*recSLTIU*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_W,
/*recANDI*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_RT_W,
/*recORI*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_RT_W,
/*recXORI*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_RT_W,
/*recLUI*/ REGUSE_ACC | REGUSE_RT_W,
/*recCOP0*/ REGUSE_SUB | REGUSE_COP0,
REGUSE_NONE,
/*recCOP2*/ REGUSE_SUB | REGUSE_COP2,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recLB*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_RT_W,
/*recLH*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_RT_W,
/*recLWL*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_RT,
/*recLW*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_RT_W,
/*recLBU*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_RT_W,
/*recLHU*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_RT_W,
/*recLWR*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_RT,
REGUSE_NONE,
/*recSB*/ REGUSE_MEM_W | REGUSE_RS_R | REGUSE_RT_R,
/*recSH*/ REGUSE_MEM_W | REGUSE_RS_R | REGUSE_RT_R,
/*recSWL*/ REGUSE_MEM | REGUSE_RS_R | REGUSE_RT_R,
/*recSW*/ REGUSE_MEM_W | REGUSE_RS_R | REGUSE_RT_R,
REGUSE_NONE, REGUSE_NONE,
/*recSWR*/ REGUSE_MEM | REGUSE_RS_R | REGUSE_RT_R,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recLWC2*/ REGUSE_MEM_R | REGUSE_RS_R | REGUSE_COP2_RT_W,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE,
/*recSWC2*/ REGUSE_MEM_W | REGUSE_RS_R | REGUSE_COP2_RT_R,
/*recHLE*/ REGUSE_UNKNOWN, // TODO: can this be done in a better way
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE
};
static const int useSPC[64] = {
/*recSLL*/ REGUSE_ACC | REGUSE_RT_R | REGUSE_RD_W,
REGUSE_NONE,
/*recSRL*/ REGUSE_ACC | REGUSE_RT_R | REGUSE_RD_W,
/*recSRA*/ REGUSE_ACC | REGUSE_RT_R | REGUSE_RD_W,
/*recSLLV*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
REGUSE_NONE,
/*recSRLV*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recSRAV*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recJR*/ REGUSE_JUMPR | REGUSE_RS_R,
/*recJALR*/ REGUSE_JUMPR | REGUSE_RS_R | REGUSE_RD_W,
REGUSE_NONE, REGUSE_NONE,
/*rSYSCALL*/ REGUSE_SYS | REGUSE_PC | REGUSE_COP0_STATUS | REGUSE_EXCEPTION,
/*recBREAK*/ REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE,
/*recMFHI*/ REGUSE_LOGIC | REGUSE_RD_W | REGUSE_HI_R,
/*recMTHI*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_HI_W,
/*recMFLO*/ REGUSE_LOGIC | REGUSE_RD_W | REGUSE_LO_R,
/*recMTLO*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_LO_W,
REGUSE_NONE, REGUSE_NONE , REGUSE_NONE, REGUSE_NONE,
/*recMULT*/ REGUSE_MULT | REGUSE_RS_R | REGUSE_RT_R | REGUSE_LO_W | REGUSE_HI_W,
/*recMULTU*/ REGUSE_MULT | REGUSE_RS_R | REGUSE_RT_R | REGUSE_LO_W | REGUSE_HI_W,
/*recDIV*/ REGUSE_MULT | REGUSE_RS_R | REGUSE_RT_R | REGUSE_LO_W | REGUSE_HI_W,
/*recDIVU*/ REGUSE_MULT | REGUSE_RS_R | REGUSE_RT_R | REGUSE_LO_W | REGUSE_HI_W,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recADD*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recADDU*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recSUB*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recSUBU*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recAND*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recOR*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recXOR*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recNOR*/ REGUSE_LOGIC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
REGUSE_NONE, REGUSE_NONE,
/*recSLT*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
/*recSLTU*/ REGUSE_ACC | REGUSE_RS_R | REGUSE_RT_R | REGUSE_RD_W,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE
};
static const int useREGIMM[32] = {
/*recBLTZ*/ REGUSE_BRANCH | REGUSE_RS_R,
/*recBGEZ*/ REGUSE_BRANCH | REGUSE_RS_R,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE,
/*recBLTZAL*/REGUSE_BRANCH | REGUSE_RS_R | REGUSE_R31_W,
/*recBGEZAL*/REGUSE_BRANCH | REGUSE_RS_R | REGUSE_R31_W,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE
};
static const int useCP0[32] = {
/*recMFC0*/ REGUSE_LOGIC | REGUSE_RT_W | REGUSE_COP0_RD_R,
REGUSE_NONE,
/*recCFC0*/ REGUSE_LOGIC | REGUSE_RT_W | REGUSE_COP0_RD_R,
REGUSE_NONE,
/*recMTC0*/ REGUSE_LOGIC | REGUSE_RT_R | REGUSE_COP0_RD_W,
REGUSE_NONE,
/*recCTC0*/ REGUSE_LOGIC | REGUSE_RT_R | REGUSE_COP0_RD_W,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recRFE*/ REGUSE_LOGIC | REGUSE_COP0_STATUS,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE
};
// TODO: make more explicit
static const int useCP2[64] = {
/*recBASIC*/ REGUSE_SUB | REGUSE_BASIC,
/*recRTPS*/ REGUSE_GTE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recNCLIP*/ REGUSE_GTE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recOP*/ REGUSE_GTE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recDPCS*/ REGUSE_GTE,
/*recINTPL*/ REGUSE_GTE,
/*recMVMVA*/ REGUSE_GTE,
/*recNCDS*/ REGUSE_GTE,
/*recCDP*/ REGUSE_GTE,
REGUSE_NONE,
/*recNCDT*/ REGUSE_GTE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recNCCS*/ REGUSE_GTE,
/*recCC*/ REGUSE_GTE,
REGUSE_NONE,
/*recNCS*/ REGUSE_GTE,
REGUSE_NONE,
/*recNCT*/ REGUSE_GTE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE,
/*recSQR*/ REGUSE_GTE,
/*recDCPL*/ REGUSE_GTE,
/*recDPCT*/ REGUSE_GTE,
REGUSE_NONE, REGUSE_NONE,
/*recAVSZ3*/ REGUSE_GTE,
/*recAVSZ4*/ REGUSE_GTE,
REGUSE_NONE,
/*recRTPT*/ REGUSE_GTE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE, REGUSE_NONE,
/*recGPF*/ REGUSE_GTE,
/*recGPL*/ REGUSE_GTE,
/*recNCCT*/ REGUSE_GTE
};
static const int useCP2BSC[32] = {
/*recMFC2*/ REGUSE_LOGIC | REGUSE_RT_W | REGUSE_COP2_RD_R,
REGUSE_NONE,
/*recCFC2*/ REGUSE_LOGIC | REGUSE_RT_W | REGUSE_COP2_RD_R,
REGUSE_NONE,
/*recMTC2*/ REGUSE_LOGIC | REGUSE_RT_R | REGUSE_COP2_RD_W,
REGUSE_NONE,
/*recCTC2*/ REGUSE_LOGIC | REGUSE_RT_R | REGUSE_COP2_RD_W,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE,
REGUSE_NONE
};
static int getRegUse(u32 code) __attribute__ ((__pure__));
static int getRegUse(u32 code)
{
int use = useBSC[code>>26];
switch (use & REGUSE_SUBMASK) {
case REGUSE_NONE:
break;
case REGUSE_SPECIAL:
use = useSPC[_fFunct_(code)];
break;
case REGUSE_REGIMM:
use = useREGIMM[_fRt_(code)];
break;
case REGUSE_COP0:
use = useCP0[_fRs_(code)];
break;
case REGUSE_COP2:
use = useCP2[_fFunct_(code)];
if ((use & REGUSE_SUBMASK) == REGUSE_BASIC)
use = useCP2BSC[_fRs_(code)];
break;
default:
use = REGUSE_UNKNOWN;
break;
}
if ((use & REGUSE_COP0_RD_W)) {
if (_fRd_(code) == 12 || _fRd_(code) == 13) {
use = REGUSE_UNKNOWN;
}
}
return use;
}
/* returns how psxreg is used in the code instruction */
int useOfPsxReg(u32 code, int use, int psxreg)
{
int retval = REGUSE_NONE;
// get use if it wasn't supplied
if (-1 == use) use = getRegUse(code);
// if we don't know what the usage is, assume it's read from
if (REGUSE_UNKNOWN == use) return REGUSE_READ;
if (psxreg < 32) {
// check for 3 standard types
if ((use & REGUSE_RT) && _fRt_(code) == (u32)psxreg) {
retval |= ((use & REGUSE_RT_R) ? REGUSE_READ:0) | ((use & REGUSE_RT_W) ? REGUSE_WRITE:0);
}
if ((use & REGUSE_RS) && _fRs_(code) == (u32)psxreg) {
retval |= ((use & REGUSE_RS_R) ? REGUSE_READ:0) | ((use & REGUSE_RS_W) ? REGUSE_WRITE:0);
}
if ((use & REGUSE_RD) && _fRd_(code) == (u32)psxreg) {
retval |= ((use & REGUSE_RD_R) ? REGUSE_READ:0) | ((use & REGUSE_RD_W) ? REGUSE_WRITE:0);
}
// some instructions explicitly writes to r31
if ((use & REGUSE_R31_W) && 31 == psxreg) {
retval |= REGUSE_WRITE;
}
} else if (psxreg == 32) { // Special register LO
retval |= ((use & REGUSE_LO_R) ? REGUSE_READ:0) | ((use & REGUSE_LO_W) ? REGUSE_WRITE:0);
} else if (psxreg == 33) { // Special register HI
retval |= ((use & REGUSE_HI_R) ? REGUSE_READ:0) | ((use & REGUSE_HI_W) ? REGUSE_WRITE:0);
}
return retval;
}
//#define NOREGUSE_FOLLOW
static int _nextPsxRegUse(u32 pc, int psxreg, int numInstr) __attribute__ ((__pure__, __unused__));
static int _nextPsxRegUse(u32 pc, int psxreg, int numInstr)
{
u32 *ptr, code, bPC = 0;
int i, use, reguse = 0;
for (i=0; i<numInstr; ) {
// load current instruction
ptr = PSXM(pc);
if (ptr==NULL) {
// going nowhere... might as well assume a write, since we will hopefully never reach here
reguse = REGUSE_WRITE;
break;
}
code = SWAP32(*ptr);
// get usage patterns for instruction
use = getRegUse(code);
// find the use of psxreg in the instruction
reguse = useOfPsxReg(code, use, psxreg);
// return if we have found a use
if (reguse != REGUSE_NONE)
break;
// goto next instruction
pc += 4;
i++;
// check for code branches/jumps
if (i != numInstr) {
if ((use & REGUSE_TYPEM) == REGUSE_BRANCH) {
#ifndef NOREGUSE_FOLLOW
// check delay slot
reguse = _nextPsxRegUse(pc, psxreg, 1);
if (reguse != REGUSE_NONE) break;
bPC = _fImm_(code) * 4 + pc;
reguse = _nextPsxRegUse(pc+4, psxreg, (numInstr-i-1)/2);
if (reguse != REGUSE_NONE) {
int reguse2 = _nextPsxRegUse(bPC, psxreg, (numInstr-i-1)/2);
if (reguse2 != REGUSE_NONE)
reguse |= reguse2;
else
reguse = REGUSE_NONE;
}
#endif
break;
} else if ((use & REGUSE_TYPEM) == REGUSE_JUMP) {
#ifndef NOREGUSE_FOLLOW
// check delay slot
reguse = _nextPsxRegUse(pc, psxreg, 1);
if (reguse != REGUSE_NONE) break;
bPC = _fTarget_(code) * 4 + (pc & 0xf0000000);
reguse = _nextPsxRegUse(bPC, psxreg, numInstr-i-1);
#endif
break;
} else if ((use & REGUSE_TYPEM) == REGUSE_JUMPR) {
#ifndef NOREGUSE_FOLLOW
// jump to unknown location - bail after checking delay slot
reguse = _nextPsxRegUse(pc, psxreg, 1);
#endif
break;
} else if ((use & REGUSE_TYPEM) == REGUSE_SYS) {
break;
}
}
}
return reguse;
}
int nextPsxRegUse(u32 pc, int psxreg)
{
#if 1
if (psxreg == 0)
return REGUSE_WRITE; // pretend we are writing to it to fool compiler
#ifdef SAME_CYCLE_MODE
return REGUSE_READ;
#else
return _nextPsxRegUse(pc, psxreg, 80);
#endif
#else
u32 code, bPC = 0;
int use, reguse = 0, reguse1 = 0, b = 0, i, index = 0;
retry:
for (i=index; i<80; i++) {
code = PSXMu32(pc);
use = getRegUse(code);
reguse = useOfPsxReg(code, use, psxreg);
if (reguse != REGUSE_NONE) break;
pc += 4;
if ((use & REGUSE_TYPEM) == REGUSE_BRANCH) {
if (b == 0) {
bPC = _fImm_(code) * 4 + pc;
index = i+1;
}
b += 1; // TODO: follow branches
continue;
} else if ((use & REGUSE_TYPEM) == REGUSE_JUMP) {
if (b == 0) {
bPC = _fTarget_(code) * 4 + (pc & 0xf0000000);
}
b = 2;
continue;
} else if ((use & REGUSE_TYPEM) == REGUSE_JUMPR ||
(use & REGUSE_TYPEM) == REGUSE_SYS) {
b = 2;
continue;
}
if (b == 2 && bPC && index == 0) {
pc = bPC; bPC = 0;
b = 1;
}
if (b >= 2) break; // only follow 1 branch
}
if (reguse == REGUSE_NONE) return reguse;
if (bPC) {
reguse1 = reguse;
pc = bPC; bPC = 0;
b = 1;
goto retry;
}
return reguse1 | reguse;
#endif
}
int isPsxRegUsed(u32 pc, int psxreg)
{
int use = nextPsxRegUse(pc, psxreg);
if (use == REGUSE_NONE)
return 2; // unknown use - assume it is used
else if (use & REGUSE_READ)
return 1; // the next use is a read
else
return 0; // the next use is a write, i.e. current value is not important
}
#endif