diff --git a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp index 64a7092087..284d5b0024 100644 --- a/Core/MIPS/ARM64/Arm64CompLoadStore.cpp +++ b/Core/MIPS/ARM64/Arm64CompLoadStore.cpp @@ -350,17 +350,25 @@ namespace MIPSComp { (dataSize == 1 || (offset & (dataSize - 1)) == 0)) { // Check that the offset is aligned to the access size as that's required for INDEX_UNSIGNED encodings. we can get here through fallback from lwl/lwr gpr.SpillLock(rs, rt); gpr.MapRegAsPointer(rs); - gpr.MapReg(rt, load ? MAP_NOINIT : 0); + + Arm64Gen::ARM64Reg ar; + if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0) { + // Can just store from the zero register directly. + ar = WZR; + } else { + gpr.MapReg(rt, load ? MAP_NOINIT : 0); + ar = gpr.R(rt); + } 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; + case 35: LDR(INDEX_UNSIGNED, ar, gpr.RPtr(rs), offset); break; + case 37: LDRH(INDEX_UNSIGNED, ar, gpr.RPtr(rs), offset); break; + case 33: LDRSH(INDEX_UNSIGNED, ar, gpr.RPtr(rs), offset); break; + case 36: LDRB(INDEX_UNSIGNED, ar, gpr.RPtr(rs), offset); break; + case 32: LDRSB(INDEX_UNSIGNED, ar, 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; + case 43: STR(INDEX_UNSIGNED, ar, gpr.RPtr(rs), offset); break; + case 41: STRH(INDEX_UNSIGNED, ar, gpr.RPtr(rs), offset); break; + case 40: STRB(INDEX_UNSIGNED, ar, gpr.RPtr(rs), offset); break; } gpr.ReleaseSpillLocks(); break; diff --git a/Core/MIPS/ARM64/Arm64CompVFPU.cpp b/Core/MIPS/ARM64/Arm64CompVFPU.cpp index 93ad88b910..4cf488f10d 100644 --- a/Core/MIPS/ARM64/Arm64CompVFPU.cpp +++ b/Core/MIPS/ARM64/Arm64CompVFPU.cpp @@ -929,9 +929,14 @@ namespace MIPSComp { // rt = 0, imm = 255 appears to be used as a CPU interlock by some games. if (rt != 0) { if (imm < 128) { //R(rt) = VI(imm); - fpr.MapRegV(imm, 0); - gpr.MapReg(rt, MAP_NOINIT | MAP_DIRTY); - fp.FMOV(gpr.R(rt), fpr.V(imm)); + if (!fpr.IsInRAMV(imm)) { + fpr.MapRegV(imm, 0); + gpr.MapReg(rt, MAP_NOINIT | MAP_DIRTY); + fp.FMOV(gpr.R(rt), fpr.V(imm)); + } else { + gpr.MapReg(rt, MAP_NOINIT | MAP_DIRTY); + LDR(INDEX_UNSIGNED, gpr.R(rt), CTXREG, fpr.GetMipsRegOffsetV(imm)); + } } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc if (imm - 128 == VFPU_CTRL_CC) { if (gpr.IsImm(MIPS_REG_VFPUCC)) { @@ -955,9 +960,17 @@ namespace MIPSComp { case 7: // mtv if (imm < 128) { - gpr.MapReg(rt); - fpr.MapRegV(imm, MAP_DIRTY | MAP_NOINIT); - fp.FMOV(fpr.V(imm), gpr.R(rt)); + if (rt == MIPS_REG_ZERO) { + fpr.MapRegV(imm, MAP_DIRTY | MAP_NOINIT); + fp.MOVI2F(fpr.V(imm), 0.0f, SCRATCH1); + } else if (!gpr.IsInRAM(rt)) { + gpr.MapReg(rt); + fpr.MapRegV(imm, MAP_DIRTY | MAP_NOINIT); + fp.FMOV(fpr.V(imm), gpr.R(rt)); + } else { + fpr.MapRegV(imm, MAP_DIRTY | MAP_NOINIT); + fp.LDR(32, INDEX_UNSIGNED, fpr.V(imm), CTXREG, gpr.GetMipsRegOffset(rt)); + } } else if (imm < 128 + VFPU_CTRL_MAX) { //mtvc //currentMIPS->vfpuCtrl[imm - 128] = R(rt); if (imm - 128 == VFPU_CTRL_CC) { if (gpr.IsImm(rt)) { diff --git a/Core/MIPS/ARM64/Arm64RegCache.cpp b/Core/MIPS/ARM64/Arm64RegCache.cpp index fd888ac5f8..f20cdf8918 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.cpp +++ b/Core/MIPS/ARM64/Arm64RegCache.cpp @@ -134,6 +134,10 @@ void Arm64RegCache::FlushBeforeCall() { FlushArmReg(W30); } +bool Arm64RegCache::IsInRAM(MIPSGPReg reg) { + return mr[reg].loc == ML_MEM; +} + bool Arm64RegCache::IsMapped(MIPSGPReg mipsReg) { return mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM; } diff --git a/Core/MIPS/ARM64/Arm64RegCache.h b/Core/MIPS/ARM64/Arm64RegCache.h index ff3efe20de..5225c8bfb0 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.h +++ b/Core/MIPS/ARM64/Arm64RegCache.h @@ -109,6 +109,7 @@ public: bool IsMapped(MIPSGPReg reg); bool IsMappedAsPointer(MIPSGPReg reg); + bool IsInRAM(MIPSGPReg reg); void MarkDirty(Arm64Gen::ARM64Reg reg); void MapIn(MIPSGPReg rs); diff --git a/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp b/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp index acc10229e6..24be128546 100644 --- a/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp +++ b/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp @@ -62,41 +62,33 @@ void Arm64RegCacheFPU::SetupInitialRegs() { } const ARM64Reg *Arm64RegCacheFPU::GetMIPSAllocationOrder(int &count) { - // VFP mapping - // VFPU registers and regular FP registers are mapped interchangably on top of the standard - // 16 FPU registers. + // On ARM64, all 32 registers are fully 128-bit and fully interchangable so we don't + // have to care about upper or lower registers. However, only S8-S15 are callee-save, and + // only the bottom 64 bits of those. So we should allocate into these when we call + // C functions, although we don't currently do so... - // NEON mapping - // We map FPU and VFPU registers entirely separately. FPU is mapped to 12 of the bottom 16 S registers. - // VFPU is mapped to the upper 48 regs, 32 of which can only be reached through NEON - // (or D16-D31 as doubles, but not relevant). - // Might consider shifting the split in the future, giving more regs to NEON allowing it to map more quads. - - // We should attempt to map scalars to low Q registers and wider things to high registers, - // as the NEON instructions are all 2-vector or 4-vector, they don't do scalar, we want to be - // able to use regular VFP instructions too. static const ARM64Reg allocationOrder[] = { - // Reserve four temp registers. Useful when building quads until we really figure out - // how to do that best. Note that we avoid the callee-save registers for now. - S4, S5, S6, S7, // Q1 - S16, S17, S18, S19, // Q4 - S20, S21, S22, S23, // Q5 - S24, S25, S26, S27, // Q6 - S28, S29, S30, S31, // Q7 - // Q8-Q15 free for NEON tricks + // Reserve four full 128-bit temp registers, should be plenty. + S4, S5, S6, S7, + S8, S9, S10, S11, // Partially callee-save (bottom 64 bits) + S12, S13, S14, S15, // Partially callee-save (bottom 64 bits) + S16, S17, S18, S19, + S20, S21, S22, S23, + S24, S25, S26, S27, + S28, S29, S30, S31, }; static const ARM64Reg allocationOrderNEONVFPU[] = { - // Reserve four temp registers. Useful when building quads until we really figure out - // how to do that best. - S4, S5, S6, S7, // Q1 - S8, S9, S10, S11, // Q2 - S12, S13, S14, S15, // Q3 - // Q4-Q15 free for VFPU + // Reserve four full 128-bit temp registers, should be plenty. + + // Then let's use 12 register as singles + S4, S5, S6, S7, + S8, S9, S10, S11, + S12, S13, S14, S15, + + // And do quads in the rest? Or use a strategy more similar to what we do on x86? }; - // NOTE: It's important that S2/S3 are not allocated with bNEON, even if !useNEONVFPU. - // They are used by a few instructions, like vh2f. if (jo_->useASIMDVFPU) { count = sizeof(allocationOrderNEONVFPU) / sizeof(const ARM64Reg); return allocationOrderNEONVFPU; @@ -110,6 +102,10 @@ bool Arm64RegCacheFPU::IsMapped(MIPSReg r) { return mr[r].loc == ML_ARMREG; } +bool Arm64RegCacheFPU::IsInRAM(MIPSReg r) { + return mr[r].loc == ML_MEM; +} + ARM64Reg Arm64RegCacheFPU::MapReg(MIPSReg mipsReg, int mapFlags) { // INFO_LOG(JIT, "FPR MapReg: %i flags=%i", mipsReg, mapFlags); if (jo_->useASIMDVFPU && mipsReg >= 32) { diff --git a/Core/MIPS/ARM64/Arm64RegCacheFPU.h b/Core/MIPS/ARM64/Arm64RegCacheFPU.h index 4822b32363..aa162a1c55 100644 --- a/Core/MIPS/ARM64/Arm64RegCacheFPU.h +++ b/Core/MIPS/ARM64/Arm64RegCacheFPU.h @@ -114,6 +114,9 @@ public: void MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad = true); void MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad = true); bool IsMapped(MIPSReg r); + bool IsMappedV(MIPSReg r) { return IsMapped((MIPSReg)(r + 32)); } + bool IsInRAM(MIPSReg r); + bool IsInRAMV(MIPSReg r) { return IsInRAM((MIPSReg)(r + 32)); } void FlushArmReg(Arm64Gen::ARM64Reg r); void FlushR(MIPSReg r); void DiscardR(MIPSReg r); @@ -145,14 +148,14 @@ public: void SetEmitter(Arm64Gen::ARM64XEmitter *emitter, Arm64Gen::ARM64FloatEmitter *fp) { emit_ = emitter; fp_ = fp; } int GetMipsRegOffset(MIPSReg r); + int GetMipsRegOffsetV(MIPSReg r) { + return GetMipsRegOffset(r + 32); + } private: Arm64Gen::ARM64Reg ARM64RegForFlush(int r); MIPSReg GetTempR(); const Arm64Gen::ARM64Reg *GetMIPSAllocationOrder(int &count); - int GetMipsRegOffsetV(MIPSReg r) { - return GetMipsRegOffset(r + 32); - } void SetupInitialRegs();