Merge pull request #4516 from unknownbrackets/armjit-minor

Improve spilling regs on ARM
This commit is contained in:
Henrik Rydgård committed 2013-11-12 01:01:19 -08:00
commit e111034b53
4 files changed
+171 -294

No files matched your search

+65 -28
View File
@@ -15,9 +15,11 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ArmRegCache.h"
#include "ArmEmitter.h"
#include "ArmJit.h"
#include "Core/MIPS/ARM/ArmRegCache.h"
#include "Core/MIPS/ARM/ArmJit.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/Reporting.h"
#include "Common/ArmEmitter.h"
#if defined(MAEMO)
#include "stddef.h"
@@ -185,6 +187,38 @@ void ArmRegCache::MapRegTo(ARMReg reg, MIPSGPReg mipsReg, int mapFlags) {
mr[mipsReg].reg = reg;
}
ARMReg ArmRegCache::FindBestToSpill(bool unusedOnly) {
int allocCount;
const ARMReg *allocOrder = GetMIPSAllocationOrder(allocCount);
static const int UNUSED_LOOKAHEAD_OPS = 3;
for (int i = 0; i < allocCount; i++) {
ARMReg reg = allocOrder[i];
if (ar[reg].mipsReg != MIPS_REG_INVALID && mr[ar[reg].mipsReg].spillLock)
continue;
if (unusedOnly) {
bool unused = true;
for (int ahead = 1; ahead <= UNUSED_LOOKAHEAD_OPS; ++ahead) {
MIPSOpcode laterOp = Memory::Read_Instruction(compilerPC_ + ahead * sizeof(u32));
// If read, it might need to be mapped again. If output, it might not need to be stored.
if (MIPSAnalyst::ReadsFromGPReg(laterOp, ar[reg].mipsReg) || MIPSAnalyst::GetOutGPReg(laterOp) == ar[reg].mipsReg) {
unused = false;
}
}
if (!unused) {
continue;
}
}
return reg;
}
return INVALID_REG;
}
// TODO: Somewhat smarter spilling - currently simply spills the first available, should do
// round robin or FIFO or something.
ARMReg ArmRegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {
@@ -194,7 +228,7 @@ ARMReg ArmRegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {
if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {
ARMReg armReg = mr[mipsReg].reg;
if (ar[armReg].mipsReg != mipsReg) {
ERROR_LOG(JIT, "Register mapping out of sync! %i", mipsReg);
ERROR_LOG_REPORT(JIT, "Register mapping out of sync! %i", mipsReg);
}
if (mapFlags & MAP_DIRTY) {
// Mapping dirty means the old imm value is invalid.
@@ -250,13 +284,9 @@ allocate:
// Still nothing. Let's spill a reg and goto 10.
// TODO: Use age or something to choose which register to spill?
// TODO: Spill dirty regs first? or opposite?
ARMReg bestToSpill = INVALID_REG;
for (int i = 0; i < allocCount; i++) {
ARMReg reg = allocOrder[i];
if (ar[reg].mipsReg != MIPS_REG_INVALID && mr[ar[reg].mipsReg].spillLock)
continue;
bestToSpill = reg;
break;
ARMReg bestToSpill = FindBestToSpill(true);
if (bestToSpill == INVALID_REG) {
bestToSpill = FindBestToSpill(false);
}
if (bestToSpill != INVALID_REG) {
@@ -266,7 +296,7 @@ allocate:
}
// Uh oh, we have all them spilllocked....
ERROR_LOG(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
ERROR_LOG_REPORT(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
return INVALID_REG;
}
@@ -318,17 +348,24 @@ void ArmRegCache::MapDirtyDirtyInIn(MIPSGPReg rd1, MIPSGPReg rd2, MIPSGPReg rs,
void ArmRegCache::FlushArmReg(ARMReg r) {
if (ar[r].mipsReg == MIPS_REG_INVALID) {
// Nothing to do, reg not mapped.
if (ar[r].isDirty) {
ERROR_LOG_REPORT(JIT, "Dirty but no mipsreg?");
}
return;
}
if (ar[r].mipsReg != MIPS_REG_INVALID) {
if (ar[r].isDirty && (mr[ar[r].mipsReg].loc == ML_ARMREG || mr[ar[r].mipsReg].loc == ML_ARMREG_IMM))
emit_->STR(r, CTXREG, GetMipsRegOffset(ar[r].mipsReg));
// IMMs won't be in an ARM reg.
mr[ar[r].mipsReg].loc = ML_MEM;
mr[ar[r].mipsReg].reg = INVALID_REG;
mr[ar[r].mipsReg].imm = 0;
} else {
ERROR_LOG(JIT, "Dirty but no mipsreg?");
auto &mreg = mr[ar[r].mipsReg];
if (mreg.loc == ML_ARMREG_IMM) {
// We know it's immedate value, no need to STR now.
mreg.loc = ML_IMM;
mreg.reg = INVALID_REG;
} else {
if (ar[r].isDirty && mreg.loc == ML_ARMREG)
emit_->STR(r, CTXREG, GetMipsRegOffset(ar[r].mipsReg));
mreg.loc = ML_MEM;
mreg.reg = INVALID_REG;
mreg.imm = 0;
}
}
ar[r].isDirty = false;
ar[r].mipsReg = MIPS_REG_INVALID;
@@ -359,7 +396,7 @@ void ArmRegCache::FlushR(MIPSGPReg r) {
case ML_ARMREG:
case ML_ARMREG_IMM:
if (mr[r].reg == INVALID_REG) {
ERROR_LOG(JIT, "FlushR: MipsReg %d had bad ArmReg", r);
ERROR_LOG_REPORT(JIT, "FlushR: MipsReg %d had bad ArmReg", r);
}
if (ar[mr[r].reg].isDirty) {
if (r != MIPS_REG_ZERO) {
@@ -373,7 +410,7 @@ void ArmRegCache::FlushR(MIPSGPReg r) {
case ML_ARMREG_AS_PTR:
// Never dirty.
if (ar[mr[r].reg].isDirty) {
ERROR_LOG(JIT, "ARMREG_AS_PTR cannot be dirty (yet)");
ERROR_LOG_REPORT(JIT, "ARMREG_AS_PTR cannot be dirty (yet)");
}
ar[mr[r].reg].mipsReg = MIPS_REG_INVALID;
break;
@@ -383,7 +420,7 @@ void ArmRegCache::FlushR(MIPSGPReg r) {
break;
default:
ERROR_LOG(JIT, "FlushR: MipsReg %d with invalid location %d", r, mr[r].loc);
ERROR_LOG_REPORT(JIT, "FlushR: MipsReg %d with invalid location %d", r, mr[r].loc);
break;
}
mr[r].loc = ML_MEM;
@@ -488,7 +525,7 @@ void ArmRegCache::FlushAll() {
// Sanity check
for (int i = 0; i < NUM_ARMREG; i++) {
if (ar[i].mipsReg != MIPS_REG_INVALID) {
ERROR_LOG(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg);
ERROR_LOG_REPORT(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg);
}
}
}
@@ -519,7 +556,7 @@ bool ArmRegCache::IsImm(MIPSGPReg r) const {
u32 ArmRegCache::GetImm(MIPSGPReg r) const {
if (r == MIPS_REG_ZERO) return 0;
if (mr[r].loc != ML_IMM && mr[r].loc != ML_ARMREG_IMM) {
ERROR_LOG(JIT, "Trying to get imm from non-imm register %i", r);
ERROR_LOG_REPORT(JIT, "Trying to get imm from non-imm register %i", r);
}
return mr[r].imm;
}
@@ -533,7 +570,7 @@ int ArmRegCache::GetMipsRegOffset(MIPSGPReg r) {
case MIPS_REG_LO:
return offsetof(MIPSState, lo);
default:
ERROR_LOG(JIT, "bad mips register %i", r);
ERROR_LOG_REPORT(JIT, "bad mips register %i", r);
return 0; // or what?
}
}
@@ -559,7 +596,7 @@ ARMReg ArmRegCache::R(MIPSGPReg mipsReg) {
if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {
return (ARMReg)mr[mipsReg].reg;
} else {
ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);
ERROR_LOG_REPORT(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);
return INVALID_REG; // BAAAD
}
}
@@ -568,7 +605,7 @@ ARMReg ArmRegCache::RPtr(MIPSGPReg mipsReg) {
if (mr[mipsReg].loc == ML_ARMREG_AS_PTR) {
return (ARMReg)mr[mipsReg].reg;
} else {
ERROR_LOG(JIT, "Reg %i not in arm reg as pointer. compilerPC = %08x", mipsReg, compilerPC_);
ERROR_LOG_REPORT(JIT, "Reg %i not in arm reg as pointer. compilerPC = %08x", mipsReg, compilerPC_);
return INVALID_REG; // BAAAD
}
}
+1
View File
@@ -124,6 +124,7 @@ private:
const ARMReg *GetMIPSAllocationOrder(int &count);
void MapRegTo(ARMReg reg, MIPSGPReg mipsReg, int mapFlags);
int FlushGetSequential(MIPSGPReg startMipsReg, bool allowFlushImm);
ARMReg FindBestToSpill(bool unusedOnly);
MIPSState *mips_;
MIPSComp::ArmJitOptions *options_;
+104 -266
View File
@@ -165,6 +165,106 @@ bool Jit::PredictTakeBranch(u32 targetAddr, bool likely) {
return targetAddr > js.compilerPC;
}
void Jit::CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink) {
// We may want to try to continue along this branch a little while, to reduce reg flushing.
if (CanContinueBranch())
{
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
if (predictTakeBranch)
cc = FlipCCFlag(cc);
Gen::FixupBranch ptr;
RegCacheState state;
if (!likely)
{
if (!delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_SAFE);
ptr = J_CC(cc, true);
GetStateAndFlushAll(state);
}
else
{
ptr = J_CC(cc, true);
if (predictTakeBranch)
GetStateAndFlushAll(state);
else
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (predictTakeBranch)
{
// We flipped the cc, the not taken case is first.
CONDITIONAL_LOG_EXIT(notTakenAddr);
WriteExit(notTakenAddr, js.nextExit++);
// Now our taken path. Bring the regs back, we didn't flush 'em after all.
SetJumpTarget(ptr);
RestoreState(state);
CONDITIONAL_LOG_EXIT(targetAddr);
if (andLink)
gpr.SetImm(MIPS_REG_RA, js.compilerPC + 8);
// Don't forget to run the delay slot if likely.
if (likely)
CompileDelaySlot(DELAYSLOT_NICE);
// Account for the increment in the loop.
js.compilerPC = targetAddr - 4;
// In case the delay slot was a break or something.
js.compiling = true;
}
else
{
// Take the branch
if (andLink)
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8));
CONDITIONAL_LOG_EXIT(targetAddr);
WriteExit(targetAddr, js.nextExit++);
// Not taken
SetJumpTarget(ptr);
RestoreState(state);
CONDITIONAL_LOG_EXIT(notTakenAddr);
// Account for the delay slot.
js.compilerPC += 4;
// In case the delay slot was a break or something.
js.compiling = true;
}
}
else
{
Gen::FixupBranch ptr;
if (!likely)
{
if (!delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);
else
FlushAll();
ptr = J_CC(cc, true);
}
else
{
FlushAll();
ptr = J_CC(cc, true);
CompileDelaySlot(DELAYSLOT_FLUSH);
}
// Take the branch
if (andLink)
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8));
CONDITIONAL_LOG_EXIT(targetAddr);
WriteExit(targetAddr, js.nextExit++);
// Not taken
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(notTakenAddr);
WriteExit(notTakenAddr, js.nextExit++);
js.compiling = false;
}
}
void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
{
CONDITIONAL_LOG;
@@ -225,71 +325,7 @@ void Jit::BranchRSRTComp(MIPSOpcode op, Gen::CCFlags cc, bool likely)
CMP(32, gpr.R(rs), gpr.R(rt));
}
// We may want to try to continue along this branch a little while, to reduce reg flushing.
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
bool continueBranch = false;
// Likely delay slots may change regs, can't take those branches inline safely.
if (CanContinueBranch() && (!likely || !predictTakeBranch))
{
continueBranch = true;
if (predictTakeBranch)
cc = FlipCCFlag(cc);
}
Gen::FixupBranch ptr;
RegCacheState state;
if (!likely)
{
if (!delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
else
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
}
else
{
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (continueBranch && predictTakeBranch)
{
// We flipped the cc, the not taken case is first.
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
WriteExit(js.compilerPC + 8, js.nextExit++);
// Now our taken path.
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(targetAddr);
// Account for the increment in the loop.
js.compilerPC = targetAddr - 4;
// In case the delay slot was a break or something.
js.compiling = true;
return;
}
// Take the branch
CONDITIONAL_LOG_EXIT(targetAddr);
WriteExit(targetAddr, js.nextExit++);
// Not taken
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
if (continueBranch && !predictTakeBranch)
{
// Account for the delay slot.
js.compilerPC += 4;
RestoreState(state);
// In case the delay slot was a break or something.
js.compiling = true;
}
else
{
WriteExit(js.compilerPC + 8, js.nextExit++);
js.compiling = false;
}
CompBranchExits(cc, targetAddr, js.compilerPC + 8, delaySlotIsNice, likely, false);
}
void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool likely)
@@ -347,75 +383,7 @@ void Jit::BranchRSZeroComp(MIPSOpcode op, Gen::CCFlags cc, bool andLink, bool li
gpr.MapReg(rs, true, false);
CMP(32, gpr.R(rs), Imm32(0));
// We may want to try to continue along this branch a little while, to reduce reg flushing.
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
bool continueBranch = false;
// Likely delay slots may change regs, can't take those branches inline safely.
if (CanContinueBranch() && (!likely || !predictTakeBranch))
{
continueBranch = true;
if (predictTakeBranch)
cc = FlipCCFlag(cc);
}
Gen::FixupBranch ptr;
RegCacheState state;
if (!likely)
{
if (!delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
else
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
}
else
{
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (continueBranch && predictTakeBranch)
{
// We flipped the cc, the not taken case is first.
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
WriteExit(js.compilerPC + 8, js.nextExit++);
// Now our taken path.
SetJumpTarget(ptr);
if (andLink)
gpr.SetImm(MIPS_REG_RA, js.compilerPC + 8);
CONDITIONAL_LOG_EXIT(targetAddr);
// Account for the increment in the loop.
js.compilerPC = targetAddr - 4;
// In case the delay slot was a break or something.
js.compiling = true;
return;
}
// Take the branch
if (andLink)
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8));
CONDITIONAL_LOG_EXIT(targetAddr);
WriteExit(targetAddr, js.nextExit++);
// Not taken
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
if (continueBranch && !predictTakeBranch)
{
// Account for the delay slot.
js.compilerPC += 4;
RestoreState(state);
// In case the delay slot was a break or something.
js.compiling = true;
}
else
{
WriteExit(js.compilerPC + 8, js.nextExit++);
js.compiling = false;
}
CompBranchExits(cc, targetAddr, js.compilerPC + 8, delaySlotIsNice, likely, andLink);
}
@@ -477,72 +445,9 @@ void Jit::BranchFPFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
if (!likely && delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_NICE);
// We may want to try to continue along this branch a little while, to reduce reg flushing.
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
bool continueBranch = false;
// Likely delay slots may change regs, can't take those branches inline safely.
if (CanContinueBranch() && (!likely || !predictTakeBranch))
{
continueBranch = true;
if (predictTakeBranch)
cc = FlipCCFlag(cc);
}
TEST(32, M((void *)&(mips_->fpcond)), Imm32(1));
Gen::FixupBranch ptr;
RegCacheState state;
if (!likely)
{
if (!delaySlotIsNice)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
else
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
}
else
{
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
CompileDelaySlot(DELAYSLOT_FLUSH);
}
if (continueBranch && predictTakeBranch)
{
// We flipped the cc, the not taken case is first.
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
WriteExit(js.compilerPC + 8, js.nextExit++);
// Now our taken path.
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(targetAddr);
// Account for the increment in the loop.
js.compilerPC = targetAddr - 4;
// In case the delay slot was a break or something.
js.compiling = true;
return;
}
// Take the branch
CONDITIONAL_LOG_EXIT(targetAddr);
WriteExit(targetAddr, js.nextExit++);
// Not taken
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(js.compilerPC + 8);
if (continueBranch && !predictTakeBranch)
{
// Account for the delay slot.
js.compilerPC += 4;
RestoreState(state);
// In case the delay slot was a break or something.
js.compiling = true;
}
else
{
WriteExit(js.compilerPC + 8, js.nextExit++);
js.compiling = false;
}
CompBranchExits(cc, targetAddr, js.compilerPC + 8, delaySlotIsNice, likely, false);
}
@@ -584,81 +489,14 @@ void Jit::BranchVFPUFlag(MIPSOpcode op, Gen::CCFlags cc, bool likely)
if (delaySlotIsBranch && (signed short)(delaySlotOp & 0xFFFF) != (signed short)(op & 0xFFFF) - 1)
ERROR_LOG_REPORT(JIT, "VFPU branch in VFPU delay slot at %08x with different target %d / %d", js.compilerPC, (signed short)(delaySlotOp & 0xFFFF), (signed short)(op & 0xFFFF) - 1);
// We may want to try to continue along this branch a little while, to reduce reg flushing.
bool predictTakeBranch = PredictTakeBranch(targetAddr, likely);
bool continueBranch = false;
// Likely delay slots may change regs, can't take those branches inline safely.
// TODO: Maybe delaySlotIsBranch could work, but let's play it safe.
if (CanContinueBranch() && (!likely || !predictTakeBranch) && !delaySlotIsBranch)
{
continueBranch = true;
if (predictTakeBranch)
cc = FlipCCFlag(cc);
}
// THE CONDITION
int imm3 = (op >> 18) & 7;
//int val = (mips_->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
TEST(32, M((void *)&(mips_->vfpuCtrl[VFPU_CTRL_CC])), Imm32(1 << imm3));
Gen::FixupBranch ptr;
RegCacheState state;
if (!likely)
{
if (!delaySlotIsNice && !delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_SAFE_FLUSH, state);
else
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
}
else
{
GetStateAndFlushAll(state);
ptr = J_CC(cc, true);
if (!delaySlotIsBranch)
CompileDelaySlot(DELAYSLOT_FLUSH);
}
u32 notTakenTarget = js.compilerPC + (delaySlotIsBranch ? 4 : 8);
if (continueBranch && predictTakeBranch)
{
// We flipped the cc, the not taken case is first.
CONDITIONAL_LOG_EXIT(notTakenTarget);
WriteExit(notTakenTarget, js.nextExit++);
// Now our taken path.
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(targetAddr);
// Account for the increment in the loop.
js.compilerPC = targetAddr - 4;
// In case the delay slot was a break or something.
js.compiling = true;
return;
}
// Take the branch
CONDITIONAL_LOG_EXIT(targetAddr);
WriteExit(targetAddr, js.nextExit++);
// Not taken
SetJumpTarget(ptr);
CONDITIONAL_LOG_EXIT(notTakenTarget);
if (continueBranch && !predictTakeBranch)
{
// Account for the delay slot.
if (!delaySlotIsBranch)
js.compilerPC += 4;
RestoreState(state);
// In case the delay slot was a break or something.
js.compiling = true;
}
else
{
WriteExit(notTakenTarget, js.nextExit++);
js.compiling = false;
}
CompBranchExits(cc, targetAddr, notTakenTarget, delaySlotIsNice, likely, false);
}
+1
View File
@@ -196,6 +196,7 @@ private:
void CompITypeMemWrite(MIPSOpcode op, u32 bits, void *safeFunc);
void CompITypeMemUnpairedLR(MIPSOpcode op, bool isStore);
void CompITypeMemUnpairedLRInner(MIPSOpcode op, X64Reg shiftReg);
void CompBranchExits(CCFlags cc, u32 targetAddr, u32 notTakenAddr, bool delaySlotIsNice, bool likely, bool andLink);
void CompFPTriArith(MIPSOpcode op, void (XEmitter::*arith)(X64Reg reg, OpArg), bool orderMatters);
void CompFPComp(int lhs, int rhs, u8 compare, bool allowNaN = false);