diff --git a/src/xenia/cpu/backend/a64/a64_backend.cc b/src/xenia/cpu/backend/a64/a64_backend.cc index 84c449e63..fffa3edb5 100644 --- a/src/xenia/cpu/backend/a64/a64_backend.cc +++ b/src/xenia/cpu/backend/a64/a64_backend.cc @@ -123,14 +123,15 @@ HostToGuestThunk A64HelperEmitter::EmitHostToGuestThunk() { // Set up guest execution state. // x20 = context (PPCContext*) mov(x20, x1); + // x19 = backend context (immediately before PPCContext in memory) + sub(x19, x20, static_cast(sizeof(A64BackendContext))); // x21 = virtual_membase (loaded from context) ldr(x21, ptr(x20, static_cast( offsetof(ppc::PPCContext, virtual_membase)))); // Restore the guest scalar FPCR on every host->guest entry so host-side // work done before the call can't leak a stale rounding / non-IEEE mode. - sub(x10, x20, static_cast(sizeof(A64BackendContext))); ldr(w11, - ptr(x10, static_cast(offsetof(A64BackendContext, fpcr_fpu)))); + ptr(x19, static_cast(offsetof(A64BackendContext, fpcr_fpu)))); msr(3, 3, 4, 4, 0, x11); // x0 still holds target, x2 holds return address. // The guest function's prolog stores x0 to GUEST_RET_ADDR on its stack @@ -247,9 +248,9 @@ GuestToHostThunk A64HelperEmitter::EmitGuestToHostThunk() { // Host callbacks may change FPCR. Restore the guest scalar FPCR before // resuming the JIT so later guest ops observe the cached PPC mode. - sub(x10, x20, static_cast(sizeof(A64BackendContext))); + // x19 (backend context) is callee-saved, so it survives the host call. ldr(w11, - ptr(x10, static_cast(offsetof(A64BackendContext, fpcr_fpu)))); + ptr(x19, static_cast(offsetof(A64BackendContext, fpcr_fpu)))); msr(3, 3, 4, 4, 0, x11); code_offsets.epilog = getSize(); @@ -365,8 +366,8 @@ ResolveFunctionThunk A64HelperEmitter::EmitResolveFunctionThunk() { // On entry (set by the tail-emitted sync check in the guest function): // x8 = return address (where to jump after fixup) // x9 = caller's stack size (to subtract from restored SP) +// x19 = A64BackendContext* // x20 = PPCContext* -// The backend context is at (x20 - sizeof(A64BackendContext)). void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() { using namespace Xbyak_aarch64; struct { @@ -381,15 +382,13 @@ void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() { code_offsets.prolog_stack_alloc = getSize(); code_offsets.body = getSize(); - // Load backend context pointer: x17 = x20 - sizeof(A64BackendContext) - mov(x17, static_cast(sizeof(A64BackendContext))); - sub(x17, x20, x17); + // x19 = backend context pointer (already set up by HostToGuestThunk) // x10 = stackpoints array pointer - ldr(x10, ptr(x17, static_cast( + ldr(x10, ptr(x19, static_cast( offsetof(A64BackendContext, stackpoints)))); // w11 = current_stackpoint_depth - ldr(w11, ptr(x17, static_cast(offsetof(A64BackendContext, + ldr(w11, ptr(x19, static_cast(offsetof(A64BackendContext, current_stackpoint_depth)))); // w12 = current guest r1 @@ -438,7 +437,7 @@ void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() { // Update current_stackpoint_depth = index + 1 // (the entry we restored to has been consumed) add(w13, w13, 1); - str(w13, ptr(x17, static_cast(offsetof(A64BackendContext, + str(w13, ptr(x19, static_cast(offsetof(A64BackendContext, current_stackpoint_depth)))); // Jump back to the caller. @@ -587,7 +586,7 @@ bool A64Backend::Initialize(Processor* processor) { // Set up machine info for the register allocator. machine_info_.supports_extended_load_store = true; - // GPR set: x19, x22-x28 (8 registers, excluding x20=context, x21=membase) + // GPR set: x22-x28 (7 registers; x19=backend ctx, x20=context, x21=membase) auto& gpr_set = machine_info_.register_sets[0]; gpr_set.id = 0; std::strcpy(gpr_set.name, "gpr"); diff --git a/src/xenia/cpu/backend/a64/a64_emitter.cc b/src/xenia/cpu/backend/a64/a64_emitter.cc index cdd40b872..99f8e647e 100644 --- a/src/xenia/cpu/backend/a64/a64_emitter.cc +++ b/src/xenia/cpu/backend/a64/a64_emitter.cc @@ -57,10 +57,10 @@ static uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) { static constexpr size_t kMaxCodeSize = 1_MiB; // Register maps: -// GPR allocatable registers: x19, x22, x23, x24, x25, x26, x27, x28 -// (x20=context, x21=membase are reserved) +// GPR allocatable registers: x22, x23, x24, x25, x26, x27, x28 +// (x19=backend context, x20=context, x21=membase are reserved) const uint32_t A64Emitter::gpr_reg_map_[GPR_COUNT] = { - 19, 22, 23, 24, 25, 26, 27, 28, + 22, 23, 24, 25, 26, 27, 28, }; // VEC allocatable registers: v4-v7, v16-v31 @@ -170,9 +170,7 @@ bool A64Emitter::Emit(hir::HIRBuilder* builder, EmitFunctionInfo& func_info) { // for post-call detection (if depth changes, a longjmp skipped frames). PushStackpoint(); if (cvars::a64_enable_host_guest_stack_synchronization) { - mov(x17, static_cast(sizeof(A64BackendContext))); - sub(x17, x20, x17); - ldr(w16, ptr(x17, static_cast(offsetof( + ldr(w16, ptr(x19, static_cast(offsetof( A64BackendContext, current_stackpoint_depth)))); str(w16, ptr(sp, static_cast( StackLayout::GUEST_SAVED_STACKPOINT_DEPTH))); @@ -551,14 +549,10 @@ void A64Emitter::PushStackpoint() { if (!cvars::a64_enable_host_guest_stack_synchronization) { return; } - // Load backend context pointer: x17 = x20 - sizeof(A64BackendContext) - mov(x17, static_cast(sizeof(A64BackendContext))); - sub(x17, x20, x17); - // x8 = stackpoints array, w9 = current depth - ldr(x8, ptr(x17, + ldr(x8, ptr(x19, static_cast(offsetof(A64BackendContext, stackpoints)))); - ldr(w9, ptr(x17, static_cast( + ldr(w9, ptr(x19, static_cast( offsetof(A64BackendContext, current_stackpoint_depth)))); // Compute offset into array: x10 = w9 * sizeof(A64BackendStackpoint) @@ -581,7 +575,7 @@ void A64Emitter::PushStackpoint() { // Increment depth. add(w9, w9, 1); - str(w9, ptr(x17, static_cast( + str(w9, ptr(x19, static_cast( offsetof(A64BackendContext, current_stackpoint_depth)))); // Check for overflow. @@ -599,12 +593,10 @@ void A64Emitter::PopStackpoint() { return; } // Decrement current_stackpoint_depth. - mov(x17, static_cast(sizeof(A64BackendContext))); - sub(x17, x20, x17); - ldr(w8, ptr(x17, static_cast( + ldr(w8, ptr(x19, static_cast( offsetof(A64BackendContext, current_stackpoint_depth)))); sub(w8, w8, 1); - str(w8, ptr(x17, static_cast( + str(w8, ptr(x19, static_cast( offsetof(A64BackendContext, current_stackpoint_depth)))); } @@ -617,9 +609,7 @@ void A64Emitter::EnsureSynchronizedGuestAndHostStack() { // some frames' PopStackpoint never ran. auto& return_from_sync = NewCachedLabel(); - mov(x17, static_cast(sizeof(A64BackendContext))); - sub(x17, x20, x17); - ldr(w17, ptr(x17, static_cast(offsetof(A64BackendContext, + ldr(w17, ptr(x19, static_cast(offsetof(A64BackendContext, current_stackpoint_depth)))); ldr(w16, ptr(sp, static_cast( StackLayout::GUEST_SAVED_STACKPOINT_DEPTH))); diff --git a/src/xenia/cpu/backend/a64/a64_emitter.h b/src/xenia/cpu/backend/a64/a64_emitter.h index 65a24bf15..ef341610d 100644 --- a/src/xenia/cpu/backend/a64/a64_emitter.h +++ b/src/xenia/cpu/backend/a64/a64_emitter.h @@ -71,10 +71,10 @@ class A64Emitter : public Xbyak_aarch64::CodeGenerator { std::vector* out_source_map); public: - // Reserved: sp, x20 (context), x21 (membase) + // Reserved: sp, x19 (backend context), x20 (context), x21 (membase) // Scratch: x0-x18 (caller-saved), v0-v3 - // Available GPRs for register allocator: x19, x22-x28 - static constexpr int GPR_COUNT = 8; + // Available GPRs for register allocator: x22-x28 + static constexpr int GPR_COUNT = 7; // Available VEC regs: v4-v7, v16-v31 static constexpr int VEC_COUNT = 20; static constexpr size_t kStashOffset = 32; @@ -122,6 +122,9 @@ class A64Emitter : public Xbyak_aarch64::CodeGenerator { void CallNativeSafe(void* fn); void SetReturnAddress(uint64_t value); + // Backend context register = x19. + // Points to A64BackendContext (immediately before PPCContext in memory). + const Xbyak_aarch64::XReg& GetBackendCtxReg() const { return x19; } // Context register = x20. const Xbyak_aarch64::XReg& GetContextReg() const { return x20; } // Memory base register = x21. diff --git a/src/xenia/cpu/backend/a64/a64_seq_memory.cc b/src/xenia/cpu/backend/a64/a64_seq_memory.cc index bcb758324..00c945da4 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_memory.cc +++ b/src/xenia/cpu/backend/a64/a64_seq_memory.cc @@ -1087,10 +1087,11 @@ EMITTER_OPCODE_TABLE(OPCODE_STORE_MMIO, STORE_MMIO_I32); // ============================================================================ // OPCODE_RESERVED_LOAD / OPCODE_RESERVED_STORE // ============================================================================ -// Helper: get pointer to A64BackendContext in x17. -static void LoadBackendCtxPtr(A64Emitter& e) { - e.sub(e.x17, e.GetContextReg(), - static_cast(sizeof(A64BackendContext))); +// Helper: get pointer to A64BackendContext. +// x19 is the dedicated backend context register, so this is a no-op +// accessor for readability. The returned register is x19. +static const Xbyak_aarch64::XReg& LoadBackendCtxPtr(A64Emitter& e) { + return e.GetBackendCtxReg(); } struct RESERVED_LOAD_I32 @@ -1102,20 +1103,20 @@ struct RESERVED_LOAD_I32 // Load the value (may clobber addr if dest == addr). e.ldr(i.dest, ptr(e.GetMembaseReg(), addr)); // Save reservation: address and value in backend context. - LoadBackendCtxPtr(e); + auto bctx = LoadBackendCtxPtr(e); // Store the guest address (already saved in x0). - e.str(e.x0, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_offset)))); + e.str(e.x0, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_offset)))); // Store the loaded value (zero-extended to 64-bit). e.mov(e.w1, i.dest); - e.str(e.x1, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_value_)))); + e.str(e.x1, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_value_)))); // Set the "has reserve" flag (bit 1). - e.ldr(e.w1, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); + e.ldr(e.w1, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); e.orr(e.w1, e.w1, static_cast(1u << kA64BackendHasReserveBit)); - e.str(e.w1, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); + e.str(e.w1, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); } }; struct RESERVED_LOAD_I64 @@ -1127,16 +1128,16 @@ struct RESERVED_LOAD_I64 // Load the value (may clobber addr if dest == addr). e.ldr(i.dest, ptr(e.GetMembaseReg(), addr)); // Save reservation in backend context. - LoadBackendCtxPtr(e); - e.str(e.x0, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_offset)))); - e.str(i.dest, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_value_)))); - e.ldr(e.w1, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); + auto bctx = LoadBackendCtxPtr(e); + e.str(e.x0, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_offset)))); + e.str(i.dest, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_value_)))); + e.ldr(e.w1, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); e.orr(e.w1, e.w1, static_cast(1u << kA64BackendHasReserveBit)); - e.str(e.w1, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); + e.str(e.w1, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); } }; EMITTER_OPCODE_TABLE(OPCODE_RESERVED_LOAD, RESERVED_LOAD_I32, @@ -1150,25 +1151,25 @@ struct RESERVED_STORE_I32 auto& no_reserve = e.NewCachedLabel(); auto& done = e.NewCachedLabel(); // Check if we have a reservation. - LoadBackendCtxPtr(e); - e.ldr(e.w4, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); + auto bctx = LoadBackendCtxPtr(e); + e.ldr(e.w4, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); e.tbz(e.w4, kA64BackendHasReserveBit, no_reserve); // Clear the reserve flag. e.and_(e.w4, e.w4, static_cast(~(1u << kA64BackendHasReserveBit))); - e.str(e.w4, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); + e.str(e.w4, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); // Check if address matches. - e.ldr(e.x4, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_offset)))); + e.ldr(e.x4, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_offset)))); e.mov(e.w5, WReg(addr.getIdx())); e.cmp(e.x4, e.x5); e.b(Xbyak_aarch64::NE, no_reserve); // Address matches. Do atomic compare-exchange. // Expected value from cached_reserve_value_. - e.ldr(e.w5, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_value_)))); + e.ldr(e.w5, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_value_)))); // Desired value. if (i.src2.is_constant) { e.mov(e.w6, @@ -1204,22 +1205,22 @@ struct RESERVED_STORE_I64 auto addr = ComputeMemoryAddress(e, i.src1); auto& no_reserve = e.NewCachedLabel(); auto& done = e.NewCachedLabel(); - LoadBackendCtxPtr(e); - e.ldr(e.w4, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); + auto bctx = LoadBackendCtxPtr(e); + e.ldr(e.w4, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); e.tbz(e.w4, kA64BackendHasReserveBit, no_reserve); e.and_(e.w4, e.w4, static_cast(~(1u << kA64BackendHasReserveBit))); - e.str(e.w4, ptr(e.x17, - static_cast(offsetof(A64BackendContext, flags)))); - e.ldr(e.x4, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_offset)))); + e.str(e.w4, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); + e.ldr(e.x4, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_offset)))); e.mov(e.w5, WReg(addr.getIdx())); e.cmp(e.x4, e.x5); e.b(Xbyak_aarch64::NE, no_reserve); // 64-bit compare-exchange. - e.ldr(e.x5, ptr(e.x17, static_cast(offsetof( - A64BackendContext, cached_reserve_value_)))); + e.ldr(e.x5, ptr(bctx, static_cast(offsetof( + A64BackendContext, cached_reserve_value_)))); if (i.src2.is_constant) { e.mov(e.x6, static_cast(i.src2.constant())); } else { diff --git a/src/xenia/cpu/backend/a64/a64_seq_util.h b/src/xenia/cpu/backend/a64/a64_seq_util.h index 46be7b811..e5e65c906 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_util.h +++ b/src/xenia/cpu/backend/a64/a64_seq_util.h @@ -35,10 +35,9 @@ inline void EmitWithVmxFpcr(A64Emitter& e, Fn&& emit_op) { // and restore around each VMX op so vector code doesn't leak FPCR changes // into later scalar instructions. e.mrs(e.x13, 3, 3, 4, 4, 0); - e.sub(e.x14, e.GetContextReg(), - static_cast(sizeof(A64BackendContext))); - e.ldr(e.w15, Xbyak_aarch64::ptr(e.x14, static_cast(offsetof( - A64BackendContext, fpcr_vmx)))); + e.ldr(e.w15, Xbyak_aarch64::ptr(e.GetBackendCtxReg(), + static_cast( + offsetof(A64BackendContext, fpcr_vmx)))); e.msr(3, 3, 4, 4, 0, e.x15); emit_op(); e.msr(3, 3, 4, 4, 0, e.x13); diff --git a/src/xenia/cpu/backend/a64/a64_sequences.cc b/src/xenia/cpu/backend/a64/a64_sequences.cc index 82e6d1fb5..8bcdc3768 100644 --- a/src/xenia/cpu/backend/a64/a64_sequences.cc +++ b/src/xenia/cpu/backend/a64/a64_sequences.cc @@ -4434,20 +4434,19 @@ struct SET_ROUNDING_MODE static void Emit(A64Emitter& e, const EmitArgType& i) { // Input is PPC FPSCR bits (already masked to 0-7 by the frontend). // We set FPCR RMode + FZ bits and cache the value in the backend context. - // x17 = backend context base pointer. - e.sub(e.x17, e.GetContextReg(), - static_cast(sizeof(A64BackendContext))); + auto bctx = e.GetBackendCtxReg(); if (i.src1.is_constant) { uint32_t fpcr_val = fpcr_table[i.src1.constant() & 7]; e.mov(e.x0, static_cast(fpcr_val)); e.msr(3, 3, 4, 4, 0, e.x0); // msr FPCR, x0 // Cache in backend context. - e.str(e.w0, ptr(e.x17, static_cast( - offsetof(A64BackendContext, fpcr_fpu)))); + e.str(e.w0, ptr(bctx, static_cast( + offsetof(A64BackendContext, fpcr_fpu)))); // Update NonIEEE flag. - e.ldr(e.w0, ptr(e.x17, static_cast( - offsetof(A64BackendContext, flags)))); + e.ldr( + e.w0, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); if (i.src1.constant() & 4) { e.orr(e.w0, e.w0, 1u << kA64BackendNonIEEEMode); } else { @@ -4455,8 +4454,9 @@ struct SET_ROUNDING_MODE e.mov(e.w1, 1u << kA64BackendNonIEEEMode); e.bic(e.w0, e.w0, e.w1); } - e.str(e.w0, ptr(e.x17, static_cast( - offsetof(A64BackendContext, flags)))); + e.str( + e.w0, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); } else { // Dynamic: look up FPCR value from table. e.mov(e.x0, reinterpret_cast(fpcr_table)); @@ -4465,11 +4465,12 @@ struct SET_ROUNDING_MODE // Write FPCR. e.msr(3, 3, 4, 4, 0, e.x0); // Cache in backend context. - e.str(e.w0, ptr(e.x17, static_cast( - offsetof(A64BackendContext, fpcr_fpu)))); + e.str(e.w0, ptr(bctx, static_cast( + offsetof(A64BackendContext, fpcr_fpu)))); // Update NonIEEE flag based on bit 2 of input. - e.ldr(e.w0, ptr(e.x17, static_cast( - offsetof(A64BackendContext, flags)))); + e.ldr( + e.w0, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); // Clear bit kA64BackendNonIEEEMode using BIC (avoids bitmask encoding). e.mov(e.w1, 1u << kA64BackendNonIEEEMode); e.bic(e.w0, e.w0, e.w1); @@ -4477,8 +4478,9 @@ struct SET_ROUNDING_MODE e.tst(i.src1, 4); e.csel(e.w1, e.w1, e.wzr, Xbyak_aarch64::Cond::NE); e.orr(e.w0, e.w0, e.w1); - e.str(e.w0, ptr(e.x17, static_cast( - offsetof(A64BackendContext, flags)))); + e.str( + e.w0, + ptr(bctx, static_cast(offsetof(A64BackendContext, flags)))); } e.ChangeFpcrMode(FPCRMode::Fpu, /*already_set=*/true); }