ARM64 IR JIT: Join two mapped pairs into a quad with a ZIP1

Mapping a quad whose halves were in registers as pairs (two vs2i.p
feeding a vi2f.q) flushed both and loaded the quad back, a store
forwarding stall.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-15 11:02:17 +02:00
1 parent 108e66f2d1
commit 3d20d51c70
2 files changed
+66

No files matched your search

+65
View File
@@ -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.
+1
View File
@@ -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);