From 745581d4bd9cfb711a9bcc9dc78d80eaba1dc14c Mon Sep 17 00:00:00 2001 From: Will Martin Date: Mon, 19 Jan 2026 01:34:56 +0900 Subject: [PATCH] [A64] Handle large stack local offsets in local loads/stores - Emit register-based SP addressing when immediate offsets exceed the scaled 12-bit range. --- src/xenia/cpu/backend/a64/a64_seq_memory.cc | 61 ++++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/src/xenia/cpu/backend/a64/a64_seq_memory.cc b/src/xenia/cpu/backend/a64/a64_seq_memory.cc index d7d66a14d..3fb205a6a 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_memory.cc +++ b/src/xenia/cpu/backend/a64/a64_seq_memory.cc @@ -286,52 +286,76 @@ EMITTER_OPCODE_TABLE(OPCODE_ATOMIC_COMPARE_EXCHANGE, // OPCODE_LOAD_LOCAL // ============================================================================ // Note: all types are always aligned on the stack. +template +void EmitLocalAccess(A64Emitter& e, uint32_t offset, uint32_t scale, + const EmitFn& emit_fn) { + const bool imm_valid = (offset % scale) == 0 && (offset / scale) <= 0xFFF; + if (imm_valid) { + emit_fn(SP, offset); + return; + } + + auto addr = GetTempReg(e); + e.MOV(addr, offset); + e.ADD(addr, SP, addr); + emit_fn(addr, 0); +} + struct LOAD_LOCAL_I8 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - e.LDRB(i.dest, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 1, [&](auto base, uint32_t imm) { + e.LDRB(i.dest, base, imm); + }); // e.TraceLoadI8(DATA_LOCAL, i.src1.constant, i.dest); } }; struct LOAD_LOCAL_I16 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - e.LDRH(i.dest, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 2, [&](auto base, uint32_t imm) { + e.LDRH(i.dest, base, imm); + }); // e.TraceLoadI16(DATA_LOCAL, i.src1.constant, i.dest); } }; struct LOAD_LOCAL_I32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - e.LDR(i.dest, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 4, + [&](auto base, uint32_t imm) { e.LDR(i.dest, base, imm); }); // e.TraceLoadI32(DATA_LOCAL, i.src1.constant, i.dest); } }; struct LOAD_LOCAL_I64 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - e.LDR(i.dest, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 8, + [&](auto base, uint32_t imm) { e.LDR(i.dest, base, imm); }); // e.TraceLoadI64(DATA_LOCAL, i.src1.constant, i.dest); } }; struct LOAD_LOCAL_F32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - e.LDR(i.dest, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 4, + [&](auto base, uint32_t imm) { e.LDR(i.dest, base, imm); }); // e.TraceLoadF32(DATA_LOCAL, i.src1.constant, i.dest); } }; struct LOAD_LOCAL_F64 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - e.LDR(i.dest, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 8, + [&](auto base, uint32_t imm) { e.LDR(i.dest, base, imm); }); // e.TraceLoadF64(DATA_LOCAL, i.src1.constant, i.dest); } }; struct LOAD_LOCAL_V128 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - e.LDR(i.dest, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 16, + [&](auto base, uint32_t imm) { e.LDR(i.dest, base, imm); }); // e.TraceLoadV128(DATA_LOCAL, i.src1.constant, i.dest); } }; @@ -347,49 +371,58 @@ struct STORE_LOCAL_I8 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { // e.TraceStoreI8(DATA_LOCAL, i.src1.constant, i.src2); - e.STRB(i.src2, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 1, [&](auto base, uint32_t imm) { + e.STRB(i.src2, base, imm); + }); } }; struct STORE_LOCAL_I16 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { // e.TraceStoreI16(DATA_LOCAL, i.src1.constant, i.src2); - e.STRH(i.src2, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 2, [&](auto base, uint32_t imm) { + e.STRH(i.src2, base, imm); + }); } }; struct STORE_LOCAL_I32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { // e.TraceStoreI32(DATA_LOCAL, i.src1.constant, i.src2); - e.STR(i.src2, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 4, + [&](auto base, uint32_t imm) { e.STR(i.src2, base, imm); }); } }; struct STORE_LOCAL_I64 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { // e.TraceStoreI64(DATA_LOCAL, i.src1.constant, i.src2); - e.STR(i.src2, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 8, + [&](auto base, uint32_t imm) { e.STR(i.src2, base, imm); }); } }; struct STORE_LOCAL_F32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { // e.TraceStoreF32(DATA_LOCAL, i.src1.constant, i.src2); - e.STR(i.src2, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 4, + [&](auto base, uint32_t imm) { e.STR(i.src2, base, imm); }); } }; struct STORE_LOCAL_F64 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { // e.TraceStoreF64(DATA_LOCAL, i.src1.constant, i.src2); - e.STR(i.src2, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 8, + [&](auto base, uint32_t imm) { e.STR(i.src2, base, imm); }); } }; struct STORE_LOCAL_V128 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { // e.TraceStoreV128(DATA_LOCAL, i.src1.constant, i.src2); - e.STR(i.src2, SP, i.src1.constant()); + EmitLocalAccess(e, i.src1.constant(), 16, + [&](auto base, uint32_t imm) { e.STR(i.src2, base, imm); }); } }; EMITTER_OPCODE_TABLE(OPCODE_STORE_LOCAL, STORE_LOCAL_I8, STORE_LOCAL_I16,