Permalink
Browse files

arm64jit: Jit lwl/lwr with proper temp regs.

It's possible rt might overlap with w9/w10, so we really need to allocate
these properly.  This locks and spills as necessary.
  • Loading branch information...
unknownbrackets committed Dec 28, 2017
1 parent 970326c commit 1b1e2c773beb76d01a71c7d1c6de54ff174a1d27
Showing with 70 additions and 57 deletions.
  1. +13 −22 Core/MIPS/ARM64/Arm64CompLoadStore.cpp
  2. +53 −35 Core/MIPS/ARM64/Arm64RegCache.cpp
  3. +4 −0 Core/MIPS/ARM64/Arm64RegCache.h
@@ -171,8 +171,6 @@ namespace MIPSComp {
return;
}
DISABLE;
_dbg_assert_msg_(JIT, !gpr.IsImm(rs), "Invalid immediate address? CPU bug?");
if (load) {
gpr.MapDirtyIn(rt, rs, false);
@@ -186,14 +184,10 @@ namespace MIPSComp {
SetScratch1ToEffectiveAddress(rs, offset);
}
// Need temp regs. TODO: Get from the regcache?
static const ARM64Reg LR_SCRATCH3 = W9;
static const ARM64Reg LR_SCRATCH4 = W10;
if (false && load) {
PUSH(EncodeRegTo64(LR_SCRATCH3));
} else {
PUSH2(EncodeRegTo64(LR_SCRATCH3), EncodeRegTo64(LR_SCRATCH4));
}
// We can lose rs now, since we have it in SCRATCH1.
gpr.SpillLock(rt);
ARM64Reg LR_SCRATCH3 = gpr.GetAndLockTempR();
ARM64Reg LR_SCRATCH4 = gpr.GetAndLockTempR();
// Here's our shift amount.
ANDI2R(SCRATCH2, SCRATCH1, 3);
@@ -205,7 +199,7 @@ namespace MIPSComp {
switch (o) {
case 34: // lwl
MOVI2R(LR_SCRATCH3, 0x00ffffff);
LDR(SCRATCH1, MEMBASEREG, SCRATCH1);
LDR(SCRATCH1, MEMBASEREG, ArithOption(SCRATCH1));
LSRV(LR_SCRATCH3, LR_SCRATCH3, SCRATCH2);
AND(gpr.R(rt), gpr.R(rt), LR_SCRATCH3);
NEG(SCRATCH2, SCRATCH2);
@@ -216,7 +210,7 @@ namespace MIPSComp {
case 38: // lwr
MOVI2R(LR_SCRATCH3, 0xffffff00);
LDR(SCRATCH1, MEMBASEREG, SCRATCH1);
LDR(SCRATCH1, MEMBASEREG, ArithOption(SCRATCH1));
LSRV(SCRATCH1, SCRATCH1, SCRATCH2);
NEG(SCRATCH2, SCRATCH2);
ADDI2R(SCRATCH2, SCRATCH2, 24);
@@ -227,19 +221,20 @@ namespace MIPSComp {
case 42: // swl
MOVI2R(LR_SCRATCH3, 0xffffff00);
LDR(LR_SCRATCH4, MEMBASEREG, SCRATCH1);
LDR(LR_SCRATCH4, MEMBASEREG, ArithOption(SCRATCH1));
LSLV(LR_SCRATCH3, LR_SCRATCH3, SCRATCH2);
AND(LR_SCRATCH4, LR_SCRATCH4, LR_SCRATCH3);
NEG(SCRATCH2, SCRATCH2);
ADDI2R(SCRATCH2, SCRATCH2, 24);
LSRV(LR_SCRATCH3, gpr.R(rt), SCRATCH2);
ORR(LR_SCRATCH4, LR_SCRATCH4, LR_SCRATCH3);
STR(LR_SCRATCH4, MEMBASEREG, SCRATCH1);
STR(LR_SCRATCH4, MEMBASEREG, ArithOption(SCRATCH1));
break;
case 46: // swr
MOVI2R(LR_SCRATCH3, 0x00ffffff);
LDR(LR_SCRATCH4, MEMBASEREG, SCRATCH1);
LDR(LR_SCRATCH4, MEMBASEREG, ArithOption(SCRATCH1));
NEG(SCRATCH2, SCRATCH2);
ADDI2R(SCRATCH2, SCRATCH2, 24);
LSRV(LR_SCRATCH3, LR_SCRATCH3, SCRATCH2);
@@ -248,19 +243,15 @@ namespace MIPSComp {
ADDI2R(SCRATCH2, SCRATCH2, 24);
LSLV(LR_SCRATCH3, gpr.R(rt), SCRATCH2);
ORR(LR_SCRATCH4, LR_SCRATCH4, LR_SCRATCH3);
STR(LR_SCRATCH4, MEMBASEREG, SCRATCH1);
STR(LR_SCRATCH4, MEMBASEREG, ArithOption(SCRATCH1));
break;
}
if (false && load) {
POP(EncodeRegTo64(LR_SCRATCH3));
} else {
POP2(EncodeRegTo64(LR_SCRATCH3), EncodeRegTo64(LR_SCRATCH4));
}
for (auto skip : skips) {
SetJumpTarget(skip);
}
gpr.ReleaseSpillLocks();
}
void Arm64Jit::Comp_ITypeMem(MIPSOpcode op) {
@@ -226,6 +226,43 @@ void Arm64RegCache::MapRegTo(ARM64Reg reg, MIPSGPReg mipsReg, int mapFlags) {
mr[mipsReg].reg = reg;
}
ARM64Reg Arm64RegCache::AllocateReg() {
int allocCount;
const ARM64Reg *allocOrder = GetMIPSAllocationOrder(allocCount);
allocate:
for (int i = 0; i < allocCount; i++) {
ARM64Reg reg = allocOrder[i];
if (ar[reg].mipsReg == MIPS_REG_INVALID && !ar[reg].tempLocked) {
return reg;
}
}
// 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?
bool clobbered;
ARM64Reg bestToSpill = FindBestToSpill(true, &clobbered);
if (bestToSpill == INVALID_REG) {
bestToSpill = FindBestToSpill(false, &clobbered);
}
if (bestToSpill != INVALID_REG) {
if (clobbered) {
DiscardR(ar[bestToSpill].mipsReg);
} else {
FlushArmReg(bestToSpill);
}
// Now one must be free.
goto allocate;
}
// Uh oh, we have all of them spilllocked....
ERROR_LOG_REPORT(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
return INVALID_REG;
}
ARM64Reg Arm64RegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) {
int allocCount;
const ARM64Reg *allocOrder = GetMIPSAllocationOrder(allocCount);
@@ -287,6 +324,14 @@ ARM64Reg Arm64RegCache::MapTempImm(MIPSGPReg r) {
return INVALID_REG;
}
ARM64Reg Arm64RegCache::GetAndLockTempR() {
ARM64Reg reg = AllocateReg();
if (reg != INVALID_REG) {
ar[reg].tempLocked = true;
}
return reg;
}
// TODO: Somewhat smarter spilling - currently simply spills the first available, should do
// round robin or FIFO or something.
ARM64Reg Arm64RegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {
@@ -362,43 +407,13 @@ ARM64Reg Arm64RegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {
}
// Okay, not mapped, so we need to allocate an ARM register.
int allocCount;
const ARM64Reg *allocOrder = GetMIPSAllocationOrder(allocCount);
allocate:
for (int i = 0; i < allocCount; i++) {
ARM64Reg reg = allocOrder[i];
if (ar[reg].mipsReg == MIPS_REG_INVALID) {
// That means it's free. Grab it, and load the value into it (if requested).
MapRegTo(reg, mipsReg, mapFlags);
return reg;
}
}
// 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?
bool clobbered;
ARM64Reg bestToSpill = FindBestToSpill(true, &clobbered);
if (bestToSpill == INVALID_REG) {
bestToSpill = FindBestToSpill(false, &clobbered);
}
if (bestToSpill != INVALID_REG) {
if (clobbered) {
DiscardR(ar[bestToSpill].mipsReg);
} else {
FlushArmReg(bestToSpill);
}
// Now one must be free.
goto allocate;
ARM64Reg reg = AllocateReg();
if (reg != INVALID_REG) {
// Grab it, and load the value into it (if requested).
MapRegTo(reg, mipsReg, mapFlags);
}
// Uh oh, we have all of them spilllocked....
ERROR_LOG_REPORT(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
return INVALID_REG;
return reg;
}
Arm64Gen::ARM64Reg Arm64RegCache::MapRegAsPointer(MIPSGPReg reg) {
@@ -864,6 +879,9 @@ void Arm64RegCache::ReleaseSpillLocks() {
if (!mr[i].isStatic)
mr[i].spillLock = false;
}
for (int i = 0; i < NUM_ARMREG; i++) {
ar[i].tempLocked = false;
}
}
void Arm64RegCache::ReleaseSpillLock(MIPSGPReg reg) {
@@ -62,6 +62,7 @@ struct RegARM64 {
MIPSGPReg mipsReg; // if -1, no mipsreg attached.
bool isDirty; // Should the register be written back?
bool pointerified; // Has used movk to move the memory base into the top part of the reg. Note - still usable as 32-bit reg!
bool tempLocked; // Reserved for a temp register.
};
struct RegMIPS {
@@ -124,6 +125,8 @@ class Arm64RegCache {
void FlushR(MIPSGPReg r);
void DiscardR(MIPSGPReg r);
Arm64Gen::ARM64Reg GetAndLockTempR();
Arm64Gen::ARM64Reg R(MIPSGPReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer
Arm64Gen::ARM64Reg RPtr(MIPSGPReg preg); // Returns a cached register, if it has been mapped as a pointer
@@ -147,6 +150,7 @@ class Arm64RegCache {
const StaticAllocation *GetStaticAllocations(int &count);
const Arm64Gen::ARM64Reg *GetMIPSAllocationOrder(int &count);
void MapRegTo(Arm64Gen::ARM64Reg reg, MIPSGPReg mipsReg, int mapFlags);
Arm64Gen::ARM64Reg AllocateReg();
Arm64Gen::ARM64Reg FindBestToSpill(bool unusedOnly, bool *clobbered);
Arm64Gen::ARM64Reg ARM64RegForFlush(MIPSGPReg r);

0 comments on commit 1b1e2c7

Please sign in to comment.