ARM64 regcache: Add support to "pointerify" registers. Use in load/store to cut down instructions.

This commit is contained in:
Henrik Rydgard 2015-03-22 11:46:14 +01:00
parent 0849e270ee
commit ad648baa9c
5 changed files with 69 additions and 2 deletions

View File

@ -256,6 +256,32 @@ namespace MIPSComp
case 40: //sb
case 41: //sh
case 43: //sw
if (jo.cachePointers && g_Config.bFastMemory) {
// ARM has smaller load/store immediate displacements than MIPS, 12 bits - and some memory ops only have 8 bits.
int offsetRange = 0x3ff;
if (o == 41 || o == 33 || o == 37 || o == 32)
offsetRange = 0xff; // 8 bit offset only
if (!gpr.IsImm(rs) && rs != rt && (offset <= offsetRange) && offset >= 0) {
gpr.SpillLock(rs, rt);
gpr.MapRegAsPointer(rs);
gpr.MapReg(rt, load ? MAP_NOINIT : 0);
switch (o) {
case 35: LDR(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
case 37: LDRH(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
case 33: LDRSH(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
case 36: LDRB(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
case 32: LDRSB(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
// Store
case 43: STR(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
case 41: STRH(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
case 40: STRB(INDEX_UNSIGNED, gpr.R(rt), gpr.RPtr(rs), offset); break;
}
gpr.ReleaseSpillLocks();
break;
}
}
if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) {
// TODO: Avoid mapping a register for the "zero" register, use R0 instead.

View File

@ -1439,7 +1439,7 @@ namespace MIPSComp
}
gpr.MapReg(MIPS_REG_VFPUCC, MAP_DIRTY);
ANDI2R(gpr.R(MIPS_REG_VFPUCC), gpr.R(MIPS_REG_VFPUCC), ~affected_bits);
ANDI2R(gpr.R(MIPS_REG_VFPUCC), gpr.R(MIPS_REG_VFPUCC), ~affected_bits, SCRATCH2);
ORR(gpr.R(MIPS_REG_VFPUCC), gpr.R(MIPS_REG_VFPUCC), SCRATCH1);
fpr.ReleaseSpillLocksAndDiscardTemps();

View File

@ -308,7 +308,6 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b)
}
b->codeSize = GetCodePtr() - b->normalEntry;
if (logBlocks > 0 && dontLogBlocks == 0) {
ILOG("=============== ARM (%d instructions -> %d bytes) ===============", js.numInstructions, b->codeSize);
DisassembleArm64Print(b->normalEntry, GetCodePtr() - b->normalEntry);

View File

@ -40,6 +40,7 @@ void Arm64RegCache::Start(MIPSAnalyst::AnalysisResults &stats) {
for (int i = 0; i < NUM_ARMREG; i++) {
ar[i].mipsReg = MIPS_REG_INVALID;
ar[i].isDirty = false;
ar[i].pointerified = false;
}
for (int i = 0; i < NUM_MIPSREG; i++) {
mr[i].loc = ML_MEM;
@ -120,6 +121,7 @@ void Arm64RegCache::MapRegTo(ARM64Reg reg, MIPSGPReg mipsReg, int mapFlags) {
}
}
ar[reg].mipsReg = mipsReg;
ar[reg].pointerified = false;
mr[mipsReg].reg = reg;
}
@ -167,6 +169,8 @@ ARM64Reg Arm64RegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {
// Mapping dirty means the old imm value is invalid.
mr[mipsReg].loc = ML_ARMREG;
ar[armReg].isDirty = true;
// If reg is written to, pointerification is lost.
ar[armReg].pointerified = false;
}
return (ARM64Reg)mr[mipsReg].reg;
}
@ -212,6 +216,24 @@ allocate:
return INVALID_REG;
}
Arm64Gen::ARM64Reg Arm64RegCache::MapRegAsPointer(MIPSGPReg reg) {
ARM64Reg retval = INVALID_REG;
if (mr[reg].loc != ML_ARMREG) {
retval = MapReg(reg);
}
if (mr[reg].loc == ML_ARMREG) {
int a = DecodeReg(mr[reg].reg);
if (!ar[a].pointerified) {
emit_->MOVK(ARM64Reg(X0 + a), ((uint64_t)Memory::base) >> 32, SHIFT_32);
ar[a].pointerified = true;
}
} else {
ERROR_LOG(JIT, "MapRegAsPointer : MapReg failed to allocate a register?");
}
return retval;
}
void Arm64RegCache::MapIn(MIPSGPReg rs) {
MapReg(rs);
}
@ -285,6 +307,7 @@ void Arm64RegCache::FlushArmReg(ARM64Reg r) {
}
ar[r].isDirty = false;
ar[r].mipsReg = MIPS_REG_INVALID;
ar[r].pointerified = false;
}
void Arm64RegCache::DiscardR(MIPSGPReg mipsReg) {
@ -293,6 +316,7 @@ void Arm64RegCache::DiscardR(MIPSGPReg mipsReg) {
ARM64Reg armReg = mr[mipsReg].reg;
ar[armReg].isDirty = false;
ar[armReg].mipsReg = MIPS_REG_INVALID;
ar[armReg].pointerified = false;
mr[mipsReg].reg = INVALID_REG;
mr[mipsReg].loc = ML_MEM;
mr[mipsReg].imm = 0;
@ -425,3 +449,18 @@ ARM64Reg Arm64RegCache::R(MIPSGPReg mipsReg) {
return INVALID_REG; // BAAAD
}
}
ARM64Reg Arm64RegCache::RPtr(MIPSGPReg mipsReg) {
if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {
int a = mr[mipsReg].reg;
if (ar[a].pointerified) {
return (ARM64Reg)mr[mipsReg].reg;
} else {
ERROR_LOG(JIT, "Tried to use a non-pointer register as a pointer");
return INVALID_REG;
}
} else {
ERROR_LOG_REPORT(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);
return INVALID_REG; // BAAAD
}
}

View File

@ -71,6 +71,7 @@ typedef int MIPSReg;
struct RegARM {
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!
};
struct RegMIPS {
@ -110,6 +111,7 @@ public:
// Returns an ARM register containing the requested MIPS register.
Arm64Gen::ARM64Reg MapReg(MIPSGPReg reg, int mapFlags = 0);
Arm64Gen::ARM64Reg MapRegAsPointer(MIPSGPReg reg);
bool IsMapped(MIPSGPReg reg);
bool IsMappedAsPointer(MIPSGPReg reg);
@ -127,6 +129,7 @@ public:
void DiscardR(MIPSGPReg r);
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
void SetEmitter(Arm64Gen::ARM64XEmitter *emitter) { emit_ = emitter; }