ARM64 IR JIT: Keep the address out of SCRATCH1 across flushes

A flush stores pending imms through SCRATCH1, so in a non-constant MemoryCheck, and in
ValidateAddress's bad-address path, the address computed into SCRATCH1 could be replaced by an imm
before IRRunMemCheck or ReportBadAddress saw it. It now waits in SCRATCH2, which flushes don't use.

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-14 17:10:35 +02:00
1 parent 985440a0c6
commit a56e5f2f6a
1 file changed
+7 -3
+7 -3
View File
@@ -153,11 +153,13 @@ void Arm64JitBackend::CompIR_Breakpoint(IRInst inst) {
if (memchecks.empty())
break;
// The flush may use SCRATCH1 for an imm, so the address waits in SCRATCH2.
ARM64Reg addrBase = regs_.MapGPR(inst.src1);
ADDI2R(SCRATCH1, addrBase, inst.constant, SCRATCH2);
ADDI2R(SCRATCH2, addrBase, inst.constant, SCRATCH1);
// We need to flush, or conditions and log expressions will see old register values.
FlushAll();
MOV(SCRATCH1, SCRATCH2);
std::vector<FixupBranch> hitChecks;
for (auto it : memchecks) {
@@ -434,13 +436,15 @@ void Arm64JitBackend::CompIR_ValidateAddress(IRInst inst) {
SetJumpTarget(tooHighScratch);
// If we got here, something unusual and bad happened, so we'll always go back to the dispatcher.
// Because of that, we can avoid flushing outside this case.
// Because of that, we can avoid flushing outside this case. The flush may use SCRATCH1 for an
// imm, so the address waits in SCRATCH2.
MOV(SCRATCH2, SCRATCH1);
auto regsCopy = regs_;
regsCopy.FlushAll();
// Ignores the return value, always returns to the dispatcher.
// Otherwise would need a thunk to restore regs.
MOV(W0, SCRATCH1);
MOV(W0, SCRATCH2);
MOVI2R(W1, alignment);
MOVI2R(W2, isWrite ? 1 : 0);
QuickCallFunction(SCRATCH2, &ReportBadAddress);