diff --git a/src/xenia/cpu/backend/x64/x64_seq_memory.cc b/src/xenia/cpu/backend/x64/x64_seq_memory.cc index 6931104c7..8b5afbc50 100644 --- a/src/xenia/cpu/backend/x64/x64_seq_memory.cc +++ b/src/xenia/cpu/backend/x64/x64_seq_memory.cc @@ -31,6 +31,7 @@ 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_inline_mmio_checks); namespace xe { namespace cpu { @@ -1292,6 +1293,35 @@ struct LOAD_OFFSET_I32 e.CallNativeSafe(addrptr); e.mov(i.dest, e.eax); } else { + Xbyak::Label normal_access, done; + bool inline_mmio = cvars::emit_inline_mmio_checks && !IsTracingData(); + if (inline_mmio) { + // Compute guest address (src1 + src2) for range check. + if (i.src1.is_constant) { + e.mov(e.eax, (uint32_t)i.src1.constant()); + } else { + e.mov(e.eax, i.src1.reg().cvt32()); + } + if (i.src2.is_constant) { + e.add(e.eax, (uint32_t)i.src2.constant()); + } else { + e.add(e.eax, i.src2.reg().cvt32()); + } + e.cmp(e.eax, 0x7FC00000); + e.jb(normal_access, e.T_NEAR); + e.cmp(e.eax, 0x7FFFFFFF); + e.ja(normal_access, e.T_NEAR); + // MMIO path + void* mmio_fn = (void*)&MMIOAwareLoad; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareLoad; + } + e.mov(e.GetNativeParam(0).cvt32(), e.eax); + e.CallNativeSafe(mmio_fn); + e.mov(i.dest, e.eax); + e.jmp(done, e.T_NEAR); + e.L(normal_access); + } auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (e.IsFeatureEnabled(kX64EmitMovbe)) { @@ -1303,6 +1333,9 @@ struct LOAD_OFFSET_I32 } else { e.mov(i.dest, e.dword[addr]); } + if (inline_mmio) { + e.L(done); + } } } }; @@ -1401,6 +1434,39 @@ struct STORE_OFFSET_I32 e.CallNativeSafe(addrptr); } else { + Xbyak::Label normal_access, done; + bool inline_mmio = cvars::emit_inline_mmio_checks && !IsTracingData(); + if (inline_mmio) { + // Compute guest address (src1 + src2) for range check. + if (i.src1.is_constant) { + e.mov(e.eax, (uint32_t)i.src1.constant()); + } else { + e.mov(e.eax, i.src1.reg().cvt32()); + } + if (i.src2.is_constant) { + e.add(e.eax, (uint32_t)i.src2.constant()); + } else { + e.add(e.eax, i.src2.reg().cvt32()); + } + e.cmp(e.eax, 0x7FC00000); + e.jb(normal_access, e.T_NEAR); + e.cmp(e.eax, 0x7FFFFFFF); + e.ja(normal_access, e.T_NEAR); + // MMIO path + void* mmio_fn = (void*)&MMIOAwareStore; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareStore; + } + e.mov(e.GetNativeParam(0).cvt32(), e.eax); + if (i.src3.is_constant) { + e.mov(e.GetNativeParam(1).cvt32(), i.src3.constant()); + } else { + e.mov(e.GetNativeParam(1).cvt32(), i.src3); + } + e.CallNativeSafe(mmio_fn); + e.jmp(done, e.T_NEAR); + e.L(normal_access); + } auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (i.src3.is_constant) { @@ -1424,6 +1490,9 @@ struct STORE_OFFSET_I32 e.mov(e.dword[addr], i.src3); } } + if (inline_mmio) { + e.L(done); + } } } }; @@ -1507,6 +1576,30 @@ struct LOAD_I32 : Sequence> { e.CallNativeSafe(addrptr); e.mov(i.dest, e.eax); } else { + Xbyak::Label normal_access, done; + bool inline_mmio = cvars::emit_inline_mmio_checks && !IsTracingData(); + if (inline_mmio) { + // Compute guest address for range check. + if (i.src1.is_constant) { + e.mov(e.eax, (uint32_t)i.src1.constant()); + } else { + e.mov(e.eax, i.src1.reg().cvt32()); + } + e.cmp(e.eax, 0x7FC00000); + e.jb(normal_access, e.T_NEAR); + e.cmp(e.eax, 0x7FFFFFFF); + e.ja(normal_access, e.T_NEAR); + // MMIO path + void* mmio_fn = (void*)&MMIOAwareLoad; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareLoad; + } + e.mov(e.GetNativeParam(0).cvt32(), e.eax); + e.CallNativeSafe(mmio_fn); + e.mov(i.dest, e.eax); + e.jmp(done, e.T_NEAR); + e.L(normal_access); + } auto addr = ComputeMemoryAddress(e, i.src1); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (e.IsFeatureEnabled(kX64EmitMovbe)) { @@ -1523,6 +1616,9 @@ struct LOAD_I32 : Sequence> { e.lea(e.GetNativeParam(0), e.ptr[addr]); e.CallNative(reinterpret_cast(TraceMemoryLoadI32)); } + if (inline_mmio) { + e.L(done); + } } } }; @@ -1671,6 +1767,34 @@ struct STORE_I32 : Sequence> { e.CallNativeSafe(addrptr); } else { + Xbyak::Label normal_access, done; + bool inline_mmio = cvars::emit_inline_mmio_checks && !IsTracingData(); + if (inline_mmio) { + // Compute guest address for range check. + if (i.src1.is_constant) { + e.mov(e.eax, (uint32_t)i.src1.constant()); + } else { + e.mov(e.eax, i.src1.reg().cvt32()); + } + e.cmp(e.eax, 0x7FC00000); + e.jb(normal_access, e.T_NEAR); + e.cmp(e.eax, 0x7FFFFFFF); + e.ja(normal_access, e.T_NEAR); + // MMIO path + void* mmio_fn = (void*)&MMIOAwareStore; + if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { + mmio_fn = (void*)&MMIOAwareStore; + } + e.mov(e.GetNativeParam(0).cvt32(), e.eax); + if (i.src2.is_constant) { + e.mov(e.GetNativeParam(1).cvt32(), i.src2.constant()); + } else { + e.mov(e.GetNativeParam(1).cvt32(), i.src2); + } + e.CallNativeSafe(mmio_fn); + e.jmp(done, e.T_NEAR); + e.L(normal_access); + } auto addr = ComputeMemoryAddress(e, i.src1); if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) { if (i.src2.is_constant) { @@ -1695,6 +1819,9 @@ struct STORE_I32 : Sequence> { e.CallNative(reinterpret_cast(TraceMemoryStoreI32)); } } + if (inline_mmio) { + e.L(done); + } } } }; diff --git a/src/xenia/memory.cc b/src/xenia/memory.cc index c45b6f623..a81691d60 100644 --- a/src/xenia/memory.cc +++ b/src/xenia/memory.cc @@ -28,6 +28,10 @@ DEFINE_bool(protect_zero, true, "Protect the zero page from reads and writes.", "Memory"); +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(protect_on_release, false, "Protect released memory to prevent accesses.", "Memory"); DEFINE_bool(scribble_heap, false, @@ -550,11 +554,13 @@ bool Memory::AddVirtualMappedRange(uint32_t virtual_address, uint32_t mask, uint32_t size, void* context, cpu::MMIOReadCallback read_callback, cpu::MMIOWriteCallback write_callback) { - if (!xe::memory::AllocFixed(TranslateVirtual(virtual_address), size, - xe::memory::AllocationType::kCommit, - xe::memory::PageAccess::kNoAccess)) { - XELOGE("Unable to map range; commit/protect failed"); - return false; + if (!cvars::emit_inline_mmio_checks) { + if (!xe::memory::AllocFixed(TranslateVirtual(virtual_address), size, + xe::memory::AllocationType::kCommit, + xe::memory::PageAccess::kNoAccess)) { + XELOGE("Unable to map range; commit/protect failed"); + return false; + } } return mmio_handler_->RegisterRange(virtual_address, mask, size, context, read_callback, write_callback);