diff --git a/Core/MIPS/ARM64/Arm64IRRegCache.cpp b/Core/MIPS/ARM64/Arm64IRRegCache.cpp index c0432106d4..b6cfb94c60 100644 --- a/Core/MIPS/ARM64/Arm64IRRegCache.cpp +++ b/Core/MIPS/ARM64/Arm64IRRegCache.cpp @@ -471,6 +471,8 @@ bool Arm64IRRegCache::TransferNativeReg(IRNativeReg nreg, IRNativeReg dest, MIPS return true; if (oldlanes == 1 && Transfer1ToVec(nreg, dest, first, lanes)) return true; + if (oldlanes == 2 && lanes == 4 && Transfer2ToVec4(nreg, dest, first)) + return true; } return IRNativeRegCacheBase::TransferNativeReg(nreg, dest, type, first, lanes, flags); @@ -739,6 +741,69 @@ bool Arm64IRRegCache::Transfer1ToVec(IRNativeReg nreg, IRNativeReg dest, IRReg f return true; } +// Two pairs into a quad, like after two vs2i.p into the halves of one. Either half may be in memory. +bool Arm64IRRegCache::Transfer2ToVec4(IRNativeReg nreg, IRNativeReg dest, IRReg first) { + if ((first & 3) != 0) + return false; + + // Each half: the native reg holding just that pair, or -1 for in memory. + IRNativeReg half[2]; + for (int h = 0; h < 2; ++h) { + const IRReg m = first + h * 2; + const IRNativeReg r = mr[m].nReg; + if (r == -1) { + if (mr[m].loc != MIPSLoc::MEM || mr[m + 1].loc != MIPSLoc::MEM) + return false; + } else if (nr[r].mipsReg != m || mr[m].lane != 0 || mr[m + 1].nReg != r || mr[m + 1].lane != 1 || (h == 0 && mr[m + 2].nReg == r)) { + return false; + } + // The other half, if this instruction maps it separately. + if (r != nreg && mr[m].spillLockIRIndex >= irIndex_) + return false; + half[h] = r; + } + if (half[0] != nreg && half[1] != nreg) + return false; + + const ARM64Reg destReg = EncodeRegToQuad(FromNativeReg(dest)); + auto Q = [&](IRNativeReg r) { return EncodeRegToQuad(FromNativeReg(r)); }; + if (half[0] != -1 && half[1] != -1) { + fp_->ZIP1(64, destReg, Q(half[0]), Q(half[1])); + } else { + const int inReg = half[0] != -1 ? 0 : 1; + if (dest != half[inReg]) { + fp_->LDR(128, INDEX_UNSIGNED, destReg, CTXREG, GetMipsRegOffset(first)); + fp_->INS(64, destReg, inReg, Q(half[inReg]), 0); + } else { + IRNativeReg freeReg = FindFreeReg(MIPSLoc::FREG, MIPSMap::INIT); + if (freeReg == -1) + return false; + fp_->LDR(64, INDEX_UNSIGNED, EncodeRegToDouble(FromNativeReg(freeReg)), CTXREG, GetMipsRegOffset(first + (1 - inReg) * 2)); + if (inReg == 0) + fp_->INS(64, destReg, 1, Q(freeReg), 0); + else + fp_->ZIP1(64, destReg, Q(freeReg), destReg); + } + } + + bool dirty = false; + for (int h = 0; h < 2; ++h) { + if (half[h] == -1) + continue; + dirty = dirty || nr[half[h]].isDirty; + if (half[h] != dest) + DiscardNativeReg(half[h]); + } + for (int i = 0; i < 4; ++i) { + mr[first + i].lane = i; + mr[first + i].loc = MIPSLoc::FREG; + mr[first + i].nReg = dest; + } + nr[dest].mipsReg = first; + nr[dest].isDirty = dirty; + return true; +} + void Arm64IRRegCache::FlushAll(bool gprs, bool fprs) { // Note: make sure not to change the registers when flushing: // Branching code may expect the armreg to retain its value. diff --git a/Core/MIPS/ARM64/Arm64IRRegCache.h b/Core/MIPS/ARM64/Arm64IRRegCache.h index 5d542264cb..a2dd8d2713 100644 --- a/Core/MIPS/ARM64/Arm64IRRegCache.h +++ b/Core/MIPS/ARM64/Arm64IRRegCache.h @@ -103,6 +103,7 @@ protected: private: bool TransferVecTo1(IRNativeReg nreg, IRNativeReg dest, IRReg first, int oldlanes); bool Transfer1ToVec(IRNativeReg nreg, IRNativeReg dest, IRReg first, int lanes); + bool Transfer2ToVec4(IRNativeReg nreg, IRNativeReg dest, IRReg first); IRNativeReg GPRToNativeReg(Arm64Gen::ARM64Reg r); IRNativeReg VFPToNativeReg(Arm64Gen::ARM64Reg r);