mirror of
https://github.com/libretro/ppsspp.git
synced 2025-02-04 07:56:15 +00:00
More regalloc fixing and tweaks. Still not working the way I want it.
This commit is contained in:
parent
17210c5364
commit
dafe2c389c
@ -54,8 +54,6 @@ bool TryMakeOperand2(u32 imm, Operand2 &op2) {
|
||||
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse)
|
||||
{
|
||||
if (!TryMakeOperand2(imm, op2)) {
|
||||
return false;
|
||||
// TODO: Test properly.
|
||||
*inverse = true;
|
||||
return TryMakeOperand2(~imm, op2);
|
||||
} else {
|
||||
@ -64,6 +62,17 @@ bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse)
|
||||
}
|
||||
}
|
||||
|
||||
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated)
|
||||
{
|
||||
if (!TryMakeOperand2(imm, op2)) {
|
||||
*negated = true;
|
||||
return TryMakeOperand2(-imm, op2);
|
||||
} else {
|
||||
*negated = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void ARMXEmitter::SetCodePtr(u8 *ptr)
|
||||
{
|
||||
code = ptr;
|
||||
|
@ -298,9 +298,13 @@ public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Use these when you don't know if an imm can be represented as an operand2.
|
||||
// This lets you generate both an optimal and a fallback solution by checking
|
||||
// the return value, which will be false if these fail to find a Operand2 that
|
||||
// represents your 32-bit imm value.
|
||||
bool TryMakeOperand2(u32 imm, Operand2 &op2);
|
||||
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse);
|
||||
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated);
|
||||
|
||||
|
||||
inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); }
|
||||
|
@ -80,10 +80,8 @@ void DisassembleArm(const u8 *data, int size);
|
||||
// At this offset - 4, there is an int specifying the block number.
|
||||
|
||||
void ArmAsmRoutineManager::QuickCallFunction(ARMReg reg, void *func) {
|
||||
PUSH(1, _LR);
|
||||
ARMABI_MOVI2R(reg, (u32)(func));
|
||||
BL(reg);
|
||||
POP(1, _LR);
|
||||
}
|
||||
|
||||
void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit)
|
||||
|
@ -34,10 +34,9 @@ using namespace MIPSAnalyst;
|
||||
|
||||
namespace MIPSComp
|
||||
{
|
||||
u32 EvalOR(u32 a, u32 b) { return a | b; }
|
||||
u32 EvalXOR(u32 a, u32 b) { return a ^ b; }
|
||||
u32 EvalAND(u32 a, u32 b) { return a & b; }
|
||||
|
||||
static u32 EvalOR(u32 a, u32 b) { return a | b; }
|
||||
static u32 EvalXOR(u32 a, u32 b) { return a ^ b; }
|
||||
static u32 EvalAND(u32 a, u32 b) { return a & b; }
|
||||
|
||||
void Jit::CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b))
|
||||
{
|
||||
@ -45,8 +44,8 @@ namespace MIPSComp
|
||||
gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm));
|
||||
} else {
|
||||
gpr.SpillLock(rs, rt);
|
||||
gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY);
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
gpr.MapReg(rt, MAP_DIRTY);
|
||||
gpr.MapReg(rs);
|
||||
gpr.ReleaseSpillLocks();
|
||||
// TODO: Special case when uimm can be represented as an Operand2
|
||||
ARMABI_MOVI2R(R0, (u32)uimm);
|
||||
@ -56,7 +55,7 @@ namespace MIPSComp
|
||||
|
||||
void Jit::Comp_IType(u32 op)
|
||||
{
|
||||
s32 simm = (s16)(op & 0xFFFF);
|
||||
s32 simm = (s16)(op & 0xFFFF); // sign extension
|
||||
u32 uimm = (u16)(op & 0xFFFF);
|
||||
|
||||
int rt = _RT;
|
||||
@ -64,18 +63,18 @@ namespace MIPSComp
|
||||
|
||||
switch (op >> 26)
|
||||
{
|
||||
/*
|
||||
case 8: // same as addiu?
|
||||
case 9: //R(rt) = R(rs) + simm; break; //addiu
|
||||
{
|
||||
if (gpr.IsImm(rs)) {
|
||||
gpr.SetImm(rt, gpr.GetImm(rs) + simm);
|
||||
} else if (rs == 0) {
|
||||
gpr.SetImm(rt, simm);
|
||||
} else if (rs == 0) { // add to zero register = immediate
|
||||
gpr.SetImm(rt, (u32)simm);
|
||||
} else {
|
||||
gpr.SpillLock(rs, rt);
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY);
|
||||
gpr.MapReg(rs);
|
||||
gpr.MapReg(rt, MAP_DIRTY);
|
||||
gpr.ReleaseSpillLocks();
|
||||
Operand2 op2;
|
||||
if (false && TryMakeOperand2(simm, op2)) {
|
||||
ADD(gpr.R(rt), gpr.R(rs), op2);
|
||||
@ -83,36 +82,15 @@ namespace MIPSComp
|
||||
ARMABI_MOVI2R(R0, (u32)simm);
|
||||
ADD(gpr.R(rt), gpr.R(rs), R0);
|
||||
}
|
||||
gpr.ReleaseSpillLocks();
|
||||
}
|
||||
else {
|
||||
} /* else {
|
||||
Comp_Generic(op);
|
||||
}
|
||||
break;
|
||||
} */
|
||||
/*
|
||||
case 13: // OR
|
||||
{
|
||||
if (gpr.IsImm(rs))
|
||||
{
|
||||
gpr.SetImm(rt, gpr.GetImm(rs) | uimm);
|
||||
break;
|
||||
} else if (rs == 0) {
|
||||
gpr.SetImm(rt, uimm);
|
||||
} else {
|
||||
gpr.SpillLock(rs, rt);
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY);
|
||||
ARMABI_MOVI2R(R0, (u32)uimm);
|
||||
ORR(gpr.R(rt), gpr.R(rs), R0);
|
||||
gpr.ReleaseSpillLocks();
|
||||
}
|
||||
} */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
*/
|
||||
//case 12: CompImmLogic(op, &XEmitter::AND); break;
|
||||
//case 14: CompImmLogic(op, &XEmitter::XOR); break;
|
||||
|
||||
//case 12: CompImmLogic(op, &XEmitter::AND, EvalAnd); break;
|
||||
//case 13: CompImmLogic(op, &XEmitter::OR, EvalOr); break;
|
||||
//case 14: CompImmLogic(op, &XEmitter::XOR, EvalXor); break;
|
||||
|
||||
/*
|
||||
case 10: // R(rt) = (s32)R(rs) < simm; break; //slti
|
||||
@ -137,7 +115,7 @@ namespace MIPSComp
|
||||
|
||||
*/
|
||||
|
||||
case 15: //R(rt) = uimm << 16; break; //lui
|
||||
case 15: // R(rt) = uimm << 16; //lui
|
||||
gpr.SetImm(rt, uimm << 16);
|
||||
break;
|
||||
|
||||
@ -174,9 +152,9 @@ namespace MIPSComp
|
||||
int rd = _RD;
|
||||
|
||||
gpr.SpillLock(rt, rs, rd);
|
||||
gpr.MapReg(rt, MAP_INITVAL);
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
gpr.MapReg(rd, MAP_INITVAL | MAP_DIRTY); // can get rid of INITVAL in some cases
|
||||
gpr.MapReg(rt);
|
||||
gpr.MapReg(rs);
|
||||
gpr.MapReg(rd, MAP_DIRTY); // can get rid of INITVAL in some cases
|
||||
gpr.ReleaseSpillLocks();
|
||||
|
||||
switch (op & 63)
|
||||
|
@ -65,19 +65,19 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely)
|
||||
|
||||
if (rt == 0)
|
||||
{
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
gpr.MapReg(rs);
|
||||
CMP(gpr.R(rs), Operand2(0, TYPE_IMM));
|
||||
}
|
||||
else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable'
|
||||
{
|
||||
gpr.MapReg(rt, MAP_INITVAL);
|
||||
CMP(gpr.R(rt), Operand2(0));
|
||||
gpr.MapReg(rt);
|
||||
CMP(gpr.R(rt), Operand2(0, TYPE_IMM));
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.SpillLock(rs, rt);
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
gpr.MapReg(rt, MAP_INITVAL);
|
||||
gpr.MapReg(rs);
|
||||
gpr.MapReg(rt);
|
||||
gpr.ReleaseSpillLocks();
|
||||
CMP(gpr.R(rs), gpr.R(rt));
|
||||
}
|
||||
@ -133,7 +133,7 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely)
|
||||
{
|
||||
// ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC);
|
||||
}
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
gpr.MapReg(rs);
|
||||
CMP(gpr.R(rs), Operand2(0, TYPE_IMM));
|
||||
FlushAll();
|
||||
|
||||
@ -383,15 +383,15 @@ void Jit::Comp_JumpReg(u32 op)
|
||||
// Do what with that information?
|
||||
delaySlotIsNice = false;
|
||||
|
||||
gpr.MapReg(rs, MAP_INITVAL);
|
||||
|
||||
if (delaySlotIsNice) {
|
||||
CompileAt(js.compilerPC + 4);
|
||||
gpr.MapReg(rs);
|
||||
MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented
|
||||
FlushAll();
|
||||
MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking.
|
||||
} else {
|
||||
// Delay slot
|
||||
gpr.MapReg(rs);
|
||||
MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented
|
||||
MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking.
|
||||
CompileAt(js.compilerPC + 4);
|
||||
|
@ -137,6 +137,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b)
|
||||
#endif
|
||||
while (js.compiling)
|
||||
{
|
||||
gpr.SetCompilerPC(js.compilerPC); // Let it know for log messages
|
||||
u32 inst = Memory::Read_Instruction(js.compilerPC);
|
||||
#ifdef LOGASM
|
||||
if (logBlocks > 0) {
|
||||
|
@ -58,11 +58,11 @@ static const ARMReg *GetMIPSAllocationOrder(int &count) {
|
||||
|
||||
ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) {
|
||||
// Let's see if it's already mapped. If so we just need to update the dirty flag.
|
||||
// We don't need to check for ML_INITVAL because we assume that anyone who maps
|
||||
// We don't need to check for ML_NOINIT because we assume that anyone who maps
|
||||
// with that flag immediately writes a "known" value to the register.
|
||||
if (mr[mipsReg].loc == ML_ARMREG) {
|
||||
if (ar[mr[mipsReg].reg].mipsReg != mipsReg) {
|
||||
ERROR_LOG(HLE, "Mapping out of sync! %i", mipsReg);
|
||||
ERROR_LOG(HLE, "Register mapping out of sync! %i", mipsReg);
|
||||
}
|
||||
if (mapFlags & MAP_DIRTY) {
|
||||
ar[mr[mipsReg].reg].isDirty = true;
|
||||
@ -83,7 +83,7 @@ allocate:
|
||||
// That means it's free. Grab it, and load the value into it (if requested).
|
||||
ar[reg].mipsReg = mipsReg;
|
||||
ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false;
|
||||
if (mapFlags & MAP_INITVAL) {
|
||||
if (!(mapFlags & MAP_NOINIT)) {
|
||||
if (mr[mipsReg].loc == ML_MEM)
|
||||
emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg);
|
||||
else if (mr[mipsReg].loc == ML_IMM)
|
||||
@ -95,17 +95,25 @@ allocate:
|
||||
}
|
||||
}
|
||||
|
||||
// Still nothing. Let's spill a reg and goto 10
|
||||
|
||||
// Still nothing. Let's spill a reg and goto 10.
|
||||
// TODO: Use age or something to choose which register to spill?
|
||||
int bestToSpill = -1;
|
||||
for (int i = 0; i < allocCount; i++) {
|
||||
if (ar[i].spillLock || ar[i].allocLock)
|
||||
int reg = allocOrder[i];
|
||||
if (ar[reg].spillLock || ar[reg].allocLock)
|
||||
continue;
|
||||
FlushArmReg((ARMReg)i);
|
||||
bestToSpill = reg;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bestToSpill != -1) {
|
||||
WARN_LOG(JIT, "Out of registers at PC %08x - spills register %i.", mips_->pc, bestToSpill);
|
||||
FlushArmReg((ARMReg)bestToSpill);
|
||||
goto allocate;
|
||||
}
|
||||
|
||||
// Uh oh, we have all them alloclocked and spilllocked....
|
||||
_assert_msg_(JIT, false, "All available registers are locked dumb dumb");
|
||||
ERROR_LOG(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
@ -146,12 +154,20 @@ void ArmRegCache::FlushMipsReg(MIPSReg r) {
|
||||
break;
|
||||
}
|
||||
mr[r].loc = ML_MEM;
|
||||
mr[r].reg = INVALID_REG;
|
||||
mr[r].imm = 0;
|
||||
}
|
||||
|
||||
void ArmRegCache::FlushAll() {
|
||||
for (int i = 0; i < NUM_MIPSREG; i++) {
|
||||
FlushMipsReg(i);
|
||||
}
|
||||
// Sanity check
|
||||
for (int i = 0; i < NUM_ARMREG; i++) {
|
||||
if (ar[i].mipsReg != -1) {
|
||||
ERROR_LOG(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArmRegCache::SetImm(MIPSReg r, u32 immVal) {
|
||||
@ -204,8 +220,7 @@ ARMReg ArmRegCache::R(int mipsReg) {
|
||||
return mr[mipsReg].reg;
|
||||
} else {
|
||||
_dbg_assert_msg_(JIT, false, "R: not mapped");
|
||||
ERROR_LOG(JIT, "Reg %i not in arm reg", mipsReg);
|
||||
ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,9 +60,10 @@ struct RegMIPS {
|
||||
// If loc == ML_MEM, it's back in its location in the CPU context struct.
|
||||
};
|
||||
|
||||
// Initing is the default so the flag is reversed.
|
||||
enum {
|
||||
MAP_DIRTY = 1,
|
||||
MAP_INITVAL = 2,
|
||||
MAP_NOINIT = 2,
|
||||
};
|
||||
|
||||
class ArmRegCache
|
||||
@ -97,10 +98,14 @@ public:
|
||||
|
||||
void SetEmitter(ARMXEmitter *emitter) { emit = emitter; }
|
||||
|
||||
// For better log output only.
|
||||
void SetCompilerPC(u32 compilerPC) { compilerPC_ = compilerPC; }
|
||||
|
||||
private:
|
||||
int GetMipsRegOffset(MIPSReg r);
|
||||
MIPSState *mips_;
|
||||
ARMXEmitter *emit;
|
||||
u32 compilerPC_;
|
||||
|
||||
enum {
|
||||
NUM_ARMREG = 16,
|
||||
|
Loading…
x
Reference in New Issue
Block a user