diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 31a0da4d7d..38c2fe2bd5 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -406,12 +406,12 @@ IRNativeReg IRNativeRegCacheBase::FindFreeReg(MIPSLoc type, MIPSMap flags) const bool IRNativeRegCacheBase::IsGPRClobbered(IRReg gpr) const { _dbg_assert_(IsValidGPR(gpr)); - return IsRegClobbered(MIPSLoc::REG, MIPSMap::INIT, gpr); + return IsRegClobbered(MIPSLoc::REG, gpr); } bool IRNativeRegCacheBase::IsFPRClobbered(IRReg fpr) const { _dbg_assert_(IsValidFPR(fpr)); - return IsRegClobbered(MIPSLoc::FREG, MIPSMap::INIT, fpr + 32); + return IsRegClobbered(MIPSLoc::FREG, fpr + 32); } IRUsage IRNativeRegCacheBase::GetNextRegUsage(const IRSituation &info, MIPSLoc type, IRReg r) const { @@ -423,7 +423,7 @@ IRUsage IRNativeRegCacheBase::GetNextRegUsage(const IRSituation &info, MIPSLoc t return IRUsage::UNKNOWN; } -bool IRNativeRegCacheBase::IsRegClobbered(MIPSLoc type, MIPSMap flags, IRReg r) const { +bool IRNativeRegCacheBase::IsRegClobbered(MIPSLoc type, IRReg r) const { static const int UNUSED_LOOKAHEAD_OPS = 30; IRSituation info; @@ -450,6 +450,21 @@ bool IRNativeRegCacheBase::IsRegClobbered(MIPSLoc type, MIPSMap flags, IRReg r) return false; } +bool IRNativeRegCacheBase::IsRegRead(MIPSLoc type, IRReg first) const { + static const int UNUSED_LOOKAHEAD_OPS = 30; + + IRSituation info; + info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; + // We look starting one ahead, unlike spilling. + info.currentIndex = irIndex_ + 1; + info.instructions = irBlock_->GetInstructions(); + info.numInstructions = irBlock_->GetNumInstructions(); + + // Note: this intentionally doesn't look at the full reg, only the lane. + IRUsage usage = GetNextRegUsage(info, type, first); + return usage == IRUsage::READ; +} + IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, MIPSMap flags, bool unusedOnly, bool *clobbered) const { int allocCount = 0, base = 0; const int *allocOrder = GetAllocationOrder(type, flags, allocCount, base); diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 07067cbdcd..4301886b44 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -239,7 +239,8 @@ protected: void SetSpillLockIRIndex(IRReg reg, int index); int GetMipsRegOffset(IRReg r); - bool IsRegClobbered(MIPSLoc type, MIPSMap flags, IRReg r) const; + bool IsRegClobbered(MIPSLoc type, IRReg r) const; + bool IsRegRead(MIPSLoc type, IRReg r) const; IRUsage GetNextRegUsage(const IRSituation &info, MIPSLoc type, IRReg r) const; bool IsValidGPR(IRReg r) const; diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index bd991c5018..6a38303286 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -480,12 +480,52 @@ bool X64IRRegCache::TransferNativeReg(IRNativeReg nreg, IRNativeReg dest, MIPSLo } bool X64IRRegCache::TransferVecTo1(IRNativeReg nreg, IRNativeReg dest, IRReg first, int oldlanes) { - // Okay, start by storing if dirty. - // TODO: Consider moving the others to free regs if available? Likely will be wanted later. IRReg oldfirst = nr[nreg].mipsReg; - if (nr[nreg].isDirty) { + + // Is it worth preserving any of the old regs? + int numKept = 0; + for (int i = 0; i < oldlanes; ++i) { + // Skip whichever one this is extracting. + if (oldfirst + i == first) + continue; + // If 0 isn't being transfered, easy to keep in its original reg. + if (i == 0 && dest != nreg) { + numKept++; + continue; + } + + IRNativeReg freeReg = FindFreeReg(MIPSLoc::FREG, MIPSMap::INIT); + if (freeReg != -1 && IsRegRead(MIPSLoc::FREG, oldfirst + i)) { + // If there's one free, use it. Don't modify nreg, though. + u8 shuf = VFPU_SWIZZLE(i, i, i, i); + if (i == 0) { + emit_->MOVAPS(FromNativeReg(freeReg), ::R(FromNativeReg(nreg))); + } else if (cpu_info.bAVX) { + emit_->VPERMILPS(128, FromNativeReg(freeReg), ::R(FromNativeReg(nreg)), shuf); + } else if (i == 2) { + emit_->MOVHLPS(FromNativeReg(freeReg), FromNativeReg(nreg)); + } else { + emit_->MOVAPS(FromNativeReg(freeReg), ::R(FromNativeReg(nreg))); + emit_->SHUFPS(FromNativeReg(freeReg), ::R(FromNativeReg(freeReg)), shuf); + } + + // Update accounting. + nr[freeReg].isDirty = nr[nreg].isDirty; + nr[freeReg].mipsReg = oldfirst + i; + mr[oldfirst + i].lane = -1; + mr[oldfirst + i].nReg = freeReg; + numKept++; + } + } + + // Unless all other lanes were kept, store. + if (nr[nreg].isDirty && numKept < oldlanes - 1) { StoreNativeReg(nreg, oldfirst, oldlanes); - nr[nreg].isDirty = false; + // Set false even for regs that were split out, since they were flushed too. + for (int i = 0; i < oldlanes; ++i) { + if (mr[oldfirst + i].nReg != -1) + nr[mr[oldfirst + i].nReg].isDirty = false; + } } // Next, shuffle the desired element into first place. @@ -494,23 +534,24 @@ bool X64IRRegCache::TransferVecTo1(IRNativeReg nreg, IRNativeReg dest, IRReg fir emit_->VPERMILPS(128, FromNativeReg(dest), ::R(FromNativeReg(nreg)), shuf); } else if (mr[first].lane <= 0 && dest != nreg) { emit_->MOVAPS(FromNativeReg(dest), ::R(FromNativeReg(nreg))); + } else if (mr[first].lane == 2) { + emit_->MOVHLPS(FromNativeReg(dest), FromNativeReg(nreg)); } else if (mr[first].lane > 0) { - if (mr[first].lane == 2) { - emit_->MOVHLPS(FromNativeReg(dest), FromNativeReg(nreg)); - } else { - if (dest != nreg) - emit_->MOVAPS(FromNativeReg(dest), ::R(FromNativeReg(nreg))); - emit_->SHUFPS(FromNativeReg(dest), ::R(FromNativeReg(dest)), shuf); - } + if (dest != nreg) + emit_->MOVAPS(FromNativeReg(dest), ::R(FromNativeReg(nreg))); + emit_->SHUFPS(FromNativeReg(dest), ::R(FromNativeReg(dest)), shuf); } // Now update accounting. for (int i = 0; i < oldlanes; ++i) { auto &mreg = mr[oldfirst + i]; if (oldfirst + i == first) { - mreg.lane = 0; + mreg.lane = -1; mreg.nReg = dest; - } else { + } else if (mreg.nReg == nreg && i == 0 && nreg != dest) { + // Still in the same register, but no longer a vec. + mreg.lane = -1; + } else if (mreg.nReg == nreg) { // No longer in a register. mreg.nReg = -1; mreg.lane = -1; @@ -520,8 +561,10 @@ bool X64IRRegCache::TransferVecTo1(IRNativeReg nreg, IRNativeReg dest, IRReg fir if (dest != nreg) { nr[dest].isDirty = nr[nreg].isDirty; - nr[nreg].mipsReg = -1; - nr[nreg].isDirty = false; + if (oldfirst == first) { + nr[nreg].mipsReg = -1; + nr[nreg].isDirty = false; + } } nr[dest].mipsReg = first;