diff --git a/src/xenia/cpu/backend/a64/a64_seq_memory.cc b/src/xenia/cpu/backend/a64/a64_seq_memory.cc index ad6ba9997..bcb758324 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_memory.cc +++ b/src/xenia/cpu/backend/a64/a64_seq_memory.cc @@ -11,7 +11,6 @@ #include "xenia/base/clock.h" #include "xenia/base/cvar.h" -#include "xenia/base/logging.h" #include "xenia/base/memory.h" #include "xenia/cpu/backend/a64/a64_backend.h" #include "xenia/cpu/backend/a64/a64_emitter.h" @@ -21,7 +20,9 @@ #include "xenia/cpu/hir/instr.h" #include "xenia/cpu/ppc/ppc_context.h" #include "xenia/cpu/processor.h" +#include "xenia/cpu/xex_module.h" +DECLARE_bool(emit_mmio_aware_stores_for_recorded_exception_addresses); DECLARE_bool(emit_inline_mmio_checks); namespace xe { @@ -31,6 +32,23 @@ namespace a64 { volatile int anchor_memory = 0; +static bool IsPossibleMMIOInstruction(A64Emitter& e, const hir::Instr* i) { + if (!cvars::emit_mmio_aware_stores_for_recorded_exception_addresses) { + return false; + } + uint32_t guest_address = i->GuestAddressFor(); + if (!guest_address) { + return false; + } + + auto* guest_module = e.GuestModule(); + if (!guest_module) { + return false; + } + auto* flags = guest_module->GetInstructionAddressFlags(guest_address); + return flags && flags->accessed_mmio; +} + // ============================================================================ // OPCODE_DELAY_EXECUTION // ============================================================================ @@ -151,6 +169,21 @@ struct LOAD_I16 : Sequence> { }; struct LOAD_I32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { + if (IsPossibleMMIOInstruction(e, i.instr)) { + void* mmio_fn = (void*)&MMIOAwareLoad; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareLoad; + } + if (i.src1.is_constant) { + e.mov(e.w1, + static_cast(static_cast(i.src1.constant()))); + } else { + e.mov(e.w1, WReg(i.src1.reg().getIdx())); + } + e.CallNativeSafe(mmio_fn); + e.mov(i.dest, e.w0); + return; + } if (cvars::emit_inline_mmio_checks) { if (i.src1.is_constant) { e.mov(e.w17, @@ -277,6 +310,26 @@ struct STORE_I16 : Sequence> { }; struct STORE_I32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { + if (IsPossibleMMIOInstruction(e, i.instr)) { + void* mmio_fn = (void*)&MMIOAwareStore; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareStore; + } + if (i.src1.is_constant) { + e.mov(e.w1, + static_cast(static_cast(i.src1.constant()))); + } else { + e.mov(e.w1, WReg(i.src1.reg().getIdx())); + } + if (i.src2.is_constant) { + e.mov(e.w2, + static_cast(static_cast(i.src2.constant()))); + } else { + e.mov(e.w2, i.src2); + } + e.CallNativeSafe(mmio_fn); + return; + } if (cvars::emit_inline_mmio_checks) { if (i.src1.is_constant) { e.mov(e.w17, @@ -473,26 +526,14 @@ EMITTER_OPCODE_TABLE(OPCODE_LOAD_CLOCK, LOAD_CLOCK); struct LOAD_OFFSET_I8 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); e.ldrb(i.dest, ptr(e.GetMembaseReg(), e.x0)); } }; struct LOAD_OFFSET_I16 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); e.ldrh(i.dest, ptr(e.GetMembaseReg(), e.x0)); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { e.rev16(i.dest, i.dest); @@ -502,6 +543,28 @@ struct LOAD_OFFSET_I16 struct LOAD_OFFSET_I32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { + if (IsPossibleMMIOInstruction(e, i.instr)) { + void* mmio_fn = (void*)&MMIOAwareLoad; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareLoad; + } + if (i.src1.is_constant) { + e.mov(e.w1, + static_cast(static_cast(i.src1.constant()))); + } else { + e.mov(e.w1, WReg(i.src1.reg().getIdx())); + } + if (i.src2.is_constant) { + e.mov(e.w17, + static_cast(static_cast(i.src2.constant()))); + } else { + e.mov(e.w17, WReg(i.src2.reg().getIdx())); + } + e.add(e.w1, e.w1, e.w17); + e.CallNativeSafe(mmio_fn); + e.mov(i.dest, e.w0); + return; + } if (cvars::emit_inline_mmio_checks) { // Compute raw guest address (src1 + src2) in w17 for range check. if (i.src1.is_constant) { @@ -538,13 +601,7 @@ struct LOAD_OFFSET_I32 e.b(done); e.L(normal_access); { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); e.ldr(i.dest, ptr(e.GetMembaseReg(), e.x0)); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { e.rev(i.dest, i.dest); @@ -552,13 +609,7 @@ struct LOAD_OFFSET_I32 } e.L(done); } else { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); e.ldr(i.dest, ptr(e.GetMembaseReg(), e.x0)); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { e.rev(i.dest, i.dest); @@ -569,13 +620,7 @@ struct LOAD_OFFSET_I32 struct LOAD_OFFSET_I64 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); e.ldr(i.dest, ptr(e.GetMembaseReg(), e.x0)); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { e.rev(i.dest, i.dest); @@ -589,13 +634,7 @@ struct STORE_OFFSET_I8 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); if (i.src3.is_constant) { e.mov(e.w17, static_cast(i.src3.constant() & 0xFF)); e.strb(e.w17, ptr(e.GetMembaseReg(), e.x0)); @@ -608,13 +647,7 @@ struct STORE_OFFSET_I16 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (i.src3.is_constant) { uint16_t val = xe::byte_swap(static_cast(i.src3.constant())); @@ -637,6 +670,33 @@ struct STORE_OFFSET_I32 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { + if (IsPossibleMMIOInstruction(e, i.instr)) { + void* mmio_fn = (void*)&MMIOAwareStore; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareStore; + } + if (i.src1.is_constant) { + e.mov(e.w1, + static_cast(static_cast(i.src1.constant()))); + } else { + e.mov(e.w1, WReg(i.src1.reg().getIdx())); + } + if (i.src2.is_constant) { + e.mov(e.w17, + static_cast(static_cast(i.src2.constant()))); + } else { + e.mov(e.w17, WReg(i.src2.reg().getIdx())); + } + e.add(e.w1, e.w1, e.w17); + if (i.src3.is_constant) { + e.mov(e.w2, + static_cast(static_cast(i.src3.constant()))); + } else { + e.mov(e.w2, i.src3); + } + e.CallNativeSafe(mmio_fn); + return; + } if (cvars::emit_inline_mmio_checks) { // Compute raw guest address (src1 + src2) in w17 for range check. if (i.src1.is_constant) { @@ -678,13 +738,7 @@ struct STORE_OFFSET_I32 e.b(done); e.L(normal_access); { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (i.src3.is_constant) { uint32_t val = @@ -706,13 +760,7 @@ struct STORE_OFFSET_I32 } e.L(done); } else { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (i.src3.is_constant) { uint32_t val = @@ -738,13 +786,7 @@ struct STORE_OFFSET_I64 : Sequence> { static void Emit(A64Emitter& e, const EmitArgType& i) { - auto addr_reg = ComputeMemoryAddress(e, i.src1); - if (i.src2.is_constant) { - e.mov(e.x17, static_cast(i.src2.constant())); - e.add(e.x0, addr_reg, e.x17); - } else { - e.add(e.x0, addr_reg, i.src2.reg()); - } + AddGuestMemoryOffset(e, ComputeMemoryAddress(e, i.src1), i.src2); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (i.src3.is_constant) { uint64_t val = xe::byte_swap(static_cast(i.src3.constant())); diff --git a/src/xenia/cpu/backend/a64/a64_seq_util.h b/src/xenia/cpu/backend/a64/a64_seq_util.h index ecb235e2e..6916853ed 100644 --- a/src/xenia/cpu/backend/a64/a64_seq_util.h +++ b/src/xenia/cpu/backend/a64/a64_seq_util.h @@ -85,9 +85,11 @@ inline XReg ComputeMemoryAddress(A64Emitter& e, const I64Op& guest) { e.mov(e.x0, static_cast(address)); return e.x0; } else { + auto src = guest.reg(); + // Guest addresses are always 32-bit. Clear any stale upper bits before + // applying the host membase so guest pointers can't escape above 4 GB. + e.mov(e.w0, WReg(src.getIdx())); if (xe::memory::allocation_granularity() > 0x1000) { - auto src = guest.reg(); - e.mov(e.w0, WReg(src.getIdx())); e.mov(e.w17, 0xE0000000u); e.cmp(e.w0, e.w17); auto& skip = e.NewCachedLabel(); @@ -96,12 +98,28 @@ inline XReg ComputeMemoryAddress(A64Emitter& e, const I64Op& guest) { e.mov(e.w17, 0x1000u); e.add(e.w0, e.w0, e.w17); e.L(skip); - return e.x0; } - return guest.reg(); + return e.x0; } } +template +inline XReg AddGuestMemoryOffset(A64Emitter& e, const XReg& base, + const OffsetOp& offset) { + // Guest address arithmetic wraps at 32 bits before the host membase is + // applied. Keep the add in W registers so stale high bits can't escape into + // the final host pointer. + e.mov(e.w0, WReg(base.getIdx())); + if (offset.is_constant) { + e.mov(e.w17, + static_cast(static_cast(offset.constant()))); + e.add(e.w0, e.w0, e.w17); + } else { + e.add(e.w0, e.w0, WReg(offset.reg().getIdx())); + } + return e.x0; +} + // Flush denormal float32 lanes to zero in a NEON register (in-place). // A float32 is denormal when 0 < abs(val) < 0x00800000. // vreg must not equal sa or sb. diff --git a/src/xenia/cpu/backend/x64/x64_backend.cc b/src/xenia/cpu/backend/x64/x64_backend.cc index 4b7d0e089..8bfd7a3e6 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.cc +++ b/src/xenia/cpu/backend/x64/x64_backend.cc @@ -26,11 +26,7 @@ #include "xenia/cpu/stack_walker.h" #include "xenia/cpu/xex_module.h" -DEFINE_bool(record_mmio_access_exceptions, true, - "For guest addresses records whether we caught any mmio accesses " - "for them. This info can then be used on a subsequent run to " - "instruct the recompiler to emit checks", - "x64"); +DECLARE_bool(record_mmio_access_exceptions); DEFINE_int64(max_stackpoints, 65536, "Max number of host->guest stack mappings we can record.", "x64"); diff --git a/src/xenia/cpu/backend/x64/x64_seq_memory.cc b/src/xenia/cpu/backend/x64/x64_seq_memory.cc index 8b5afbc50..b543a57b8 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_memory.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_memory.cc @@ -27,10 +27,7 @@ DEFINE_bool(enable_rmw_context_merging, false, "Permit merging read-modify-write HIR instr sequences together " "into x86 instructions that use a memory operand.", "x64"); -DEFINE_bool(emit_mmio_aware_stores_for_recorded_exception_addresses, true, - "Uses info gathered via record_mmio_access_exceptions to emit " - "special stores that are faster than trapping the exception", - "CPU"); +DECLARE_bool(emit_mmio_aware_stores_for_recorded_exception_addresses); DECLARE_bool(emit_inline_mmio_checks); namespace xe { diff --git a/src/xenia/cpu/testing/guest_address_truncation_test.cc b/src/xenia/cpu/testing/guest_address_truncation_test.cc new file mode 100644 index 000000000..f408eafe2 --- /dev/null +++ b/src/xenia/cpu/testing/guest_address_truncation_test.cc @@ -0,0 +1,187 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2026 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/cpu/testing/util.h" + +#include + +using namespace xe; +using namespace xe::cpu; +using namespace xe::cpu::hir; +using namespace xe::cpu::testing; +using xe::cpu::ppc::PPCContext; + +// ============================================================================= +// Guest addresses are 32-bit. If a GPR holding a guest address has stale +// upper 32 bits, the backend must mask them before adding the host membase. +// Otherwise the final host pointer escapes the guest address space. +// ============================================================================= + +TEST_CASE("LOAD_I32_STALE_UPPER_BITS", "[instr]") { + TestFunction test([](HIRBuilder& b) { + auto addr = LoadGPR(b, 4); + StoreGPR(b, 3, b.ZeroExtend(b.Load(addr, INT32_TYPE), INT64_TYPE)); + b.Return(); + }); + test.Run( + [&test](PPCContext* ctx) { + uint32_t addr = test.memory->SystemHeapAlloc(4, 4); + auto* host = test.memory->TranslateVirtual(addr); + uint32_t sentinel = 0xCAFEBABE; + std::memcpy(host, &sentinel, 4); + // Set the GPR to the valid address with garbage upper bits. + ctx->r[4] = 0xDEAD000000000000ULL | addr; + }, + [&test](PPCContext* ctx) { + REQUIRE(static_cast(ctx->r[3]) == 0xCAFEBABE); + test.memory->SystemHeapFree( + static_cast(ctx->r[4] & 0xFFFFFFFF)); + }); +} + +TEST_CASE("STORE_I32_STALE_UPPER_BITS", "[instr]") { + TestFunction test([](HIRBuilder& b) { + auto addr = LoadGPR(b, 4); + auto val = b.Truncate(LoadGPR(b, 5), INT32_TYPE); + b.Store(addr, val); + b.Return(); + }); + test.Run( + [&test](PPCContext* ctx) { + uint32_t addr = test.memory->SystemHeapAlloc(4, 4); + std::memset(test.memory->TranslateVirtual(addr), 0, 4); + ctx->r[4] = 0xDEAD000000000000ULL | addr; + ctx->r[5] = 0x12345678; + }, + [&test](PPCContext* ctx) { + uint32_t addr = static_cast(ctx->r[4] & 0xFFFFFFFF); + auto* host = test.memory->TranslateVirtual(addr); + uint32_t result; + std::memcpy(&result, host, 4); + REQUIRE(result == 0x12345678); + test.memory->SystemHeapFree(addr); + }); +} + +// ============================================================================= +// Guest address arithmetic must wrap at 32 bits. Test by computing +// (base + offset) in HIR where the 32-bit sum wraps, then loading/storing. +// ============================================================================= + +TEST_CASE("LOAD_I32_ADDRESS_WRAPS_AT_32_BITS", "[instr]") { + TestFunction test([](HIRBuilder& b) { + // Compute guest address as (r4 + r5) truncated to 32 bits, then load. + auto base = b.Truncate(LoadGPR(b, 4), INT32_TYPE); + auto offset = b.Truncate(LoadGPR(b, 5), INT32_TYPE); + auto addr = b.ZeroExtend(b.Add(base, offset), INT64_TYPE); + StoreGPR(b, 3, b.ZeroExtend(b.Load(addr, INT32_TYPE), INT64_TYPE)); + b.Return(); + }); + test.Run( + [&test](PPCContext* ctx) { + uint32_t target_addr = test.memory->SystemHeapAlloc(4, 4); + auto* host = test.memory->TranslateVirtual(target_addr); + uint32_t sentinel = 0xDEADF00D; + std::memcpy(host, &sentinel, 4); + + // base + offset overflows 32 bits and wraps to target_addr. + ctx->r[4] = 0xFFFF0000u; + ctx->r[5] = static_cast(target_addr) + 0x10000u; + }, + [&test](PPCContext* ctx) { + REQUIRE(static_cast(ctx->r[3]) == 0xDEADF00D); + uint32_t target_addr = static_cast( + 0xFFFF0000u + static_cast(ctx->r[5])); + test.memory->SystemHeapFree(target_addr); + }); +} + +TEST_CASE("STORE_I32_ADDRESS_WRAPS_AT_32_BITS", "[instr]") { + TestFunction test([](HIRBuilder& b) { + auto base = b.Truncate(LoadGPR(b, 4), INT32_TYPE); + auto offset = b.Truncate(LoadGPR(b, 5), INT32_TYPE); + auto addr = b.ZeroExtend(b.Add(base, offset), INT64_TYPE); + auto val = b.Truncate(LoadGPR(b, 6), INT32_TYPE); + b.Store(addr, val); + b.Return(); + }); + test.Run( + [&test](PPCContext* ctx) { + uint32_t target_addr = test.memory->SystemHeapAlloc(4, 4); + std::memset(test.memory->TranslateVirtual(target_addr), 0, 4); + + ctx->r[4] = 0xFFFF0000u; + ctx->r[5] = static_cast(target_addr) + 0x10000u; + ctx->r[6] = 0xBAADF00D; + }, + [&test](PPCContext* ctx) { + uint32_t target_addr = static_cast( + 0xFFFF0000u + static_cast(ctx->r[5])); + auto* host = test.memory->TranslateVirtual(target_addr); + uint32_t result; + std::memcpy(&result, host, 4); + REQUIRE(result == 0xBAADF00D); + test.memory->SystemHeapFree(target_addr); + }); +} + +// ============================================================================= +// LOAD_OFFSET with constant offset and stale upper bits in base. +// ============================================================================= + +TEST_CASE("LOAD_OFFSET_I32_STALE_UPPER_BITS", "[instr]") { + TestFunction test([](HIRBuilder& b) { + auto base = LoadGPR(b, 4); + auto offset = b.LoadConstantInt64(4); + StoreGPR(b, 3, + b.ZeroExtend(b.LoadOffset(base, offset, INT32_TYPE), INT64_TYPE)); + b.Return(); + }); + test.Run( + [&test](PPCContext* ctx) { + uint32_t addr = test.memory->SystemHeapAlloc(8, 4); + auto* host = test.memory->TranslateVirtual(addr + 4); + uint32_t sentinel = 0x87654321; + std::memcpy(host, &sentinel, 4); + + // Garbage upper bits in the base register. + ctx->r[4] = 0xBEEF000000000000ULL | addr; + }, + [&test](PPCContext* ctx) { + REQUIRE(static_cast(ctx->r[3]) == 0x87654321); + uint32_t addr = static_cast(ctx->r[4] & 0xFFFFFFFF); + test.memory->SystemHeapFree(addr); + }); +} + +TEST_CASE("STORE_OFFSET_I32_STALE_UPPER_BITS", "[instr]") { + TestFunction test([](HIRBuilder& b) { + auto base = LoadGPR(b, 4); + auto offset = b.LoadConstantInt64(4); + auto val = b.Truncate(LoadGPR(b, 5), INT32_TYPE); + b.StoreOffset(base, offset, val); + b.Return(); + }); + test.Run( + [&test](PPCContext* ctx) { + uint32_t addr = test.memory->SystemHeapAlloc(8, 4); + std::memset(test.memory->TranslateVirtual(addr), 0, 8); + + ctx->r[4] = 0xBEEF000000000000ULL | addr; + ctx->r[5] = 0xFEEDFACE; + }, + [&test](PPCContext* ctx) { + uint32_t addr = static_cast(ctx->r[4] & 0xFFFFFFFF); + auto* host = test.memory->TranslateVirtual(addr + 4); + uint32_t result; + std::memcpy(&result, host, 4); + REQUIRE(result == 0xFEEDFACE); + test.memory->SystemHeapFree(addr); + }); +} diff --git a/src/xenia/memory.cc b/src/xenia/memory.cc index 88a52efcf..e3fff3fd3 100644 --- a/src/xenia/memory.cc +++ b/src/xenia/memory.cc @@ -32,6 +32,15 @@ DEFINE_bool(emit_inline_mmio_checks, false, "Emit inline MMIO range checks for all I32 loads/stores instead " "of relying on exception-based MMIO detection.", "CPU"); +DEFINE_bool(emit_mmio_aware_stores_for_recorded_exception_addresses, true, + "Uses info gathered via record_mmio_access_exceptions to emit " + "special stores that are faster than trapping the exception", + "CPU"); +DEFINE_bool(record_mmio_access_exceptions, true, + "For guest addresses records whether we caught any mmio accesses " + "for them. This info can then be used on a subsequent run to " + "instruct the recompiler to emit checks", + "CPU"); DEFINE_bool(protect_on_release, false, "Protect released memory to prevent accesses.", "Memory"); DEFINE_bool(scribble_heap, false,