More regalloc fixing and tweaks. Still not working the way I want it.

This commit is contained in:
Henrik Rydgard 2013-01-09 13:38:44 +01:00
parent 17210c5364
commit dafe2c389c
8 changed files with 77 additions and 67 deletions

View File

@ -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;

View File

@ -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); }

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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) {

View File

@ -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
}
}

View File

@ -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,