From e62d67e1a021b114c4b7e8e59bc2de2a22d86a9a Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 2 Jul 2026 20:19:10 +0000 Subject: [PATCH] [dynarmic] Coalesce Write/Read fallback functions Signed-off-by: lizzie --- docs/dynarmic/README.md | 31 ++-- src/core/arm/dynarmic/arm_dynarmic_32.cpp | 52 +++---- src/core/arm/dynarmic/arm_dynarmic_32.h | 10 +- src/core/arm/dynarmic/arm_dynarmic_64.cpp | 52 +++---- src/core/arm/dynarmic/arm_dynarmic_64.h | 11 +- src/core/hle/service/jit/jit_context.cpp | 62 ++++---- src/core/tools/freezer.cpp | 16 +- .../backend/arm64/a32_address_space.cpp | 41 ++--- .../backend/arm64/a64_address_space.cpp | 41 ++--- .../backend/x64/a32_emit_x64_memory.cpp | 60 ++++---- .../backend/x64/a64_emit_x64_memory.cpp | 62 ++++---- .../backend/x64/emit_x64_memory.cpp.inc | 36 +++-- .../src/dynarmic/interface/A32/config.h | 14 +- .../src/dynarmic/interface/A64/config.h | 14 +- src/dynarmic/src/dynarmic/ir/opt_passes.cpp | 8 +- src/dynarmic/tests/A32/fuzz_thumb.cpp | 2 +- src/dynarmic/tests/A32/testenv.h | 127 ++++++++-------- src/dynarmic/tests/A64/a64.cpp | 4 +- src/dynarmic/tests/A64/fibonacci.cpp | 81 +++++----- src/dynarmic/tests/A64/testenv.h | 142 ++++++++++-------- src/dynarmic/tests/print_info.cpp | 72 +++++---- .../tests/unicorn_emu/a32_unicorn.cpp | 21 +-- .../tests/unicorn_emu/a64_unicorn.cpp | 21 +-- 23 files changed, 443 insertions(+), 537 deletions(-) diff --git a/docs/dynarmic/README.md b/docs/dynarmic/README.md index bfba7c9cb0..39e2af65b9 100644 --- a/docs/dynarmic/README.md +++ b/docs/dynarmic/README.md @@ -76,23 +76,22 @@ public: u64 ticks_left = 0; std::array memory{}; - u8 MemoryRead8(u32 vaddr) override { - if (vaddr >= memory.size()) { - return 0; + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): + return vaddr >= memory.size() ? 0 : memory[vaddr]; + default: + std::abort(); } - return memory[vaddr]; - } - - u16 MemoryRead16(u32 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - - u32 MemoryRead32(u32 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - - u64 MemoryRead64(u32 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; } void MemoryWrite8(u32 vaddr, u8 value) override { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index e434a31d5d..80a5a60550 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} -u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) { - CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); - return m_memory.Read8(vaddr); -} -u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) { - CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); - return m_memory.Read16(vaddr); -} -u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) { - CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); - return m_memory.Read32(vaddr); -} -u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) { - CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); - return m_memory.Read64(vaddr); +u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) { + CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read); + switch (size) { + case sizeof(u64): return m_memory.Read64(vaddr); + case sizeof(u32): return m_memory.Read32(vaddr); + case sizeof(u16): return m_memory.Read16(vaddr); + case sizeof(u8): return m_memory.Read8(vaddr); + default: UNREACHABLE(); + } } std::optional DynarmicCallbacks32::MemoryReadCode(u32 vaddr) { @@ -50,27 +44,17 @@ std::optional DynarmicCallbacks32::MemoryReadCode(u32 vaddr) { return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; } -void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) { - if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { - m_memory.Write8(vaddr, value); +void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) { + if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) { + switch (size) { + case sizeof(u64): return m_memory.Write64(vaddr, value); + case sizeof(u32): return m_memory.Write32(vaddr, u32(value)); + case sizeof(u16): return m_memory.Write16(vaddr, u16(value)); + case sizeof(u8): return m_memory.Write8(vaddr, u8(value)); + default: UNREACHABLE(); + } } } -void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) { - if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { - m_memory.Write16(vaddr, value); - } -} -void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) { - if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { - m_memory.Write32(vaddr, value); - } -} -void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) { - if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { - m_memory.Write64(vaddr, value); - } -} - bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) { return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && m_memory.WriteExclusive8(vaddr, value, expected); diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 623597152f..22655dff47 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -30,18 +30,12 @@ class System; class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process); - u8 MemoryRead8(u32 vaddr) override; - u16 MemoryRead16(u32 vaddr) override; - u32 MemoryRead32(u32 vaddr) override; - u64 MemoryRead64(u32 vaddr) override; + u64 MemoryRead(u32 vaddr, size_t size) override; std::optional MemoryReadCode(u32 vaddr) override; void InstructionSynchronizationBarrierRaised() override { last_code_addr = u64(-1); //reset back, force refetch } - void MemoryWrite8(u32 vaddr, u8 value) override; - void MemoryWrite16(u32 vaddr, u16 value) override; - void MemoryWrite32(u32 vaddr, u32 value) override; - void MemoryWrite64(u32 vaddr, u64 value) override; + void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override; bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override; bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override; bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index e6c7e61265..7a843a3322 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -10,6 +10,7 @@ #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/core_timing.h" #include "core/hle/kernel/k_process.h" +#include "dynarmic/interface/A64/config.h" namespace Core { @@ -21,21 +22,15 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {} -u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) { - CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read); - return m_memory.Read8(vaddr); -} -u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) { - CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read); - return m_memory.Read16(vaddr); -} -u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) { - CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read); - return m_memory.Read32(vaddr); -} -u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) { - CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read); - return m_memory.Read64(vaddr); +u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) { + CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read); + switch (size) { + case sizeof(u64): return m_memory.Read64(vaddr); + case sizeof(u32): return m_memory.Read32(vaddr); + case sizeof(u16): return m_memory.Read16(vaddr); + case sizeof(u8): return m_memory.Read8(vaddr); + default: UNREACHABLE(); + } } Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) { CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); @@ -53,24 +48,15 @@ std::optional DynarmicCallbacks64::MemoryReadCode(u64 vaddr) { return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; } -void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) { - if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) { - m_memory.Write8(vaddr, value); - } -} -void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) { - if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) { - m_memory.Write16(vaddr, value); - } -} -void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) { - if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) { - m_memory.Write32(vaddr, value); - } -} -void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) { - if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) { - m_memory.Write64(vaddr, value); +void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) { + if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) { + switch (size) { + case sizeof(u64): return m_memory.Write64(vaddr, u64(value)); + case sizeof(u32): return m_memory.Write32(vaddr, u32(value)); + case sizeof(u16): return m_memory.Write16(vaddr, u16(value)); + case sizeof(u8): return m_memory.Write8(vaddr, u8(value)); + default: UNREACHABLE(); + } } } void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 6d0760986f..9fd2bc954b 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -16,6 +16,7 @@ #include "common/hash.h" #include "core/arm/arm_interface.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" +#include "dynarmic/interface/A64/config.h" namespace Core::Memory { class Memory; @@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process); - u8 MemoryRead8(u64 vaddr) override; - u16 MemoryRead16(u64 vaddr) override; - u32 MemoryRead32(u64 vaddr) override; - u64 MemoryRead64(u64 vaddr) override; + u64 MemoryRead(u64 vaddr, size_t size) override; Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override; std::optional MemoryReadCode(u64 vaddr) override; void InstructionSynchronizationBarrierRaised() override { last_code_addr = u64(-1); //reset back, force refetch } - void MemoryWrite8(u64 vaddr, u8 value) override; - void MemoryWrite16(u64 vaddr, u16 value) override; - void MemoryWrite32(u64 vaddr, u32 value) override; - void MemoryWrite64(u64 vaddr, u64 value) override; + void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override; void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override; bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override; bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override; diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 36bf5d6649..e1e8eeadbe 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp @@ -71,17 +71,14 @@ public: void InstructionSynchronizationBarrierRaised() override { last_code_addr = u64(-1); //reset back, force refetch } - u8 MemoryRead8(u64 vaddr) override { - return ReadMemory(vaddr); - } - u16 MemoryRead16(u64 vaddr) override { - return ReadMemory(vaddr); - } - u32 MemoryRead32(u64 vaddr) override { - return ReadMemory(vaddr); - } - u64 MemoryRead64(u64 vaddr) override { - return ReadMemory(vaddr); + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): return ReadMemory(vaddr); + case sizeof(u32): return ReadMemory(vaddr); + case sizeof(u16): return ReadMemory(vaddr); + case sizeof(u8): return ReadMemory(vaddr); + default: UNREACHABLE(); + } } u128 MemoryRead128(u64 vaddr) override { return ReadMemory(vaddr); @@ -89,22 +86,19 @@ public: std::string MemoryReadCString(u64 vaddr) { std::string result{}; u8 next; - while ((next = MemoryRead8(vaddr++)) != 0) + while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0) result += char(next); return result; } - void MemoryWrite8(u64 vaddr, u8 value) override { - WriteMemory(vaddr, value); - } - void MemoryWrite16(u64 vaddr, u16 value) override { - WriteMemory(vaddr, value); - } - void MemoryWrite32(u64 vaddr, u32 value) override { - WriteMemory(vaddr, value); - } - void MemoryWrite64(u64 vaddr, u64 value) override { - WriteMemory(vaddr, value); + void MemoryWrite(u64 vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): WriteMemory(vaddr, u64(value)); break; + case sizeof(u32): WriteMemory(vaddr, u32(value)); break; + case sizeof(u16): WriteMemory(vaddr, u16(value)); break; + case sizeof(u8): WriteMemory(vaddr, u8(value)); break; + default: UNREACHABLE(); + } } void MemoryWrite128(u64 vaddr, u128 value) override { WriteMemory(vaddr, value); @@ -193,14 +187,14 @@ public: // The loaded NRO file has ELF relocations that must be processed before it can run. // Normally this would be processed by RTLD, but in HLE context, we don't have // the linker available, so we have to do it ourselves. - const VAddr mod_offset{callbacks->MemoryRead32(4)}; - if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) + const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))}; + if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0')) return false; // For more info about dynamic entries, see the ELF ABI specification: // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html - VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; + VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))}; VAddr rela_dyn = 0, relr_dyn = 0; size_t num_rela = 0, num_relr = 0; while (true) { @@ -222,8 +216,8 @@ public: for (size_t i = 0; i < num_rela; i++) { const auto rela{callbacks->ReadMemory(rela_dyn + i * sizeof(Elf64_Rela))}; if (Elf64RelType(rela.r_info) == ElfAArch64Relative) { - const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; - callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); + const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))}; + callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64)); } } @@ -231,7 +225,7 @@ public: for (size_t i = 0; i < num_relr; i++) { const auto relr = callbacks->ReadMemory(relr_dyn + i * sizeof(Elf64_Relr)); const auto incr = [&](VAddr where) { - callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase); + callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64)); }; if ((relr & 1) == 0) { // where pointer @@ -294,7 +288,7 @@ public: if (argument_stack.size() > 8) { const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); for (size_t i = 8; i < argument_stack.size(); i++) - callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]); + callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64)); jit->SetSP(new_sp); } // Reset the call state for the next invocation @@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) { if (dest < src) { for (size_t i = 0; i < n; i++) - MemoryWrite8(dest + i, MemoryRead8(src + i)); + MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8)); } else { for (size_t i = n; i > 0; i--) - MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1)); + MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8)); } } else if (pc == parent.helpers[size_t(HelperFn::Memset)]) { const VAddr dest{parent.jit->GetRegister(0)}; const u64 c{parent.jit->GetRegister(1)}; const size_t n{parent.jit->GetRegister(2)}; for (size_t i = 0; i < n; i++) - MemoryWrite8(dest + i, u8(c)); + MemoryWrite(dest + i, u8(c), sizeof(u8)); } else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) { // X0 contains a char* for a symbol to resolve const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; @@ -422,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) { } void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { - auto const inst = MemoryRead32(pc); + auto const inst = MemoryRead(pc, sizeof(u32)); LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst); parent.jit->HaltExecution(); } diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 73a86b3c8c..606ac2da42 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -33,18 +33,10 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) { switch (width) { - case 1: - memory.Write8(addr, static_cast(value)); - break; - case 2: - memory.Write16(addr, static_cast(value)); - break; - case 4: - memory.Write32(addr, static_cast(value)); - break; - case 8: - memory.Write64(addr, value); - break; + case sizeof(u64): return memory.Write64(addr, value); + case sizeof(u32): return memory.Write32(addr, u32(value)); + case sizeof(u16): return memory.Write16(addr, u16(value)); + case sizeof(u8): return memory.Write8(addr, u8(value)); default: UNREACHABLE(); } diff --git a/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp index da51220e9c..16f15b076c 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -59,6 +59,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_ ABI_PushRegisters(code, save_regs, 0); code.LDR(X0, l_this); code.MOV(X1, Xscratch0); + code.LDR(X2, sizeof(T)); code.LDR(Xscratch0, l_addr); code.BLR(Xscratch0); code.MOV(Xscratch0, X0); @@ -82,7 +83,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T { return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { - return (conf.callbacks->*callback)(vaddr); + return (conf.callbacks->*callback)(vaddr, sizeof(T)); }); }; @@ -115,6 +116,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this code.LDR(X0, l_this); code.MOV(X1, Xscratch0); code.MOV(X2, Xscratch1); + code.MOV(X3, sizeof(T)); code.LDR(Xscratch0, l_addr); code.BLR(Xscratch0); ABI_PopRegisters(code, save_regs, 0); @@ -136,12 +138,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const oaknut::Label l_addr, l_this; auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 { - return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, - [&](T expected) -> bool { - return (conf.callbacks->*callback)(vaddr, value, expected); - }) - ? 0 - : 1; + return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, [&](T expected) -> bool { + return (conf.callbacks->*callback)(vaddr, value, expected); + }) ? 0 : 1; }; void* target = code.xptr(); @@ -179,26 +178,14 @@ void A32AddressSpace::EmitPrelude() { UnprotectCodeMemory(); - prelude_info.read_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks); - prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks); - prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf); - prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf); - prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf); - prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf); - prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks); - prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks); + prelude_info.read_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks); + prelude_info.wrapped_read_memory = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks); + prelude_info.exclusive_read_memory = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf); + prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf); + prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf); + prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf); + prelude_info.write_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks); + prelude_info.wrapped_write_memory = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf); diff --git a/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp b/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp index 2c71ffe282..c3921f7ee3 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/a64_address_space.cpp @@ -81,7 +81,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T { return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { - return (conf.callbacks->*callback)(vaddr); + return (conf.callbacks->*callback)(vaddr, sizeof(T)); }); }; @@ -114,6 +114,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this code.LDR(X0, l_this); code.MOV(X1, Xscratch0); code.MOV(X2, Xscratch1); + code.MOV(X3, sizeof(T)); code.LDR(Xscratch0, l_addr); code.BLR(Xscratch0); ABI_PopRegisters(code, save_regs, 0); @@ -133,14 +134,10 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const using namespace oaknut::util; oaknut::Label l_addr, l_this; - auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 { - return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, - [&](T expected) -> bool { - return (conf.callbacks->*callback)(vaddr, value, expected); - }) - ? 0 - : 1; + return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, [&](T expected) -> bool { + return (conf.callbacks->*callback)(vaddr, value, expected); + }) ? 0 : 1; }; void* target = code.xptr(); @@ -346,30 +343,18 @@ void A64AddressSpace::EmitPrelude() { UnprotectCodeMemory(); - prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks); + prelude_info.read_memory = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks); prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks); - prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks); - prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks); - prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks); - prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks); + prelude_info.wrapped_read_memory = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks); prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks); - prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead8, u8>(code, conf); - prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf); - prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf); - prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf); + prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf); + prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf); + prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf); + prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf); prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf); - prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks); + prelude_info.write_memory = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks); prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks); - prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks); - prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks); - prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks); - prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks); + prelude_info.wrapped_write_memory = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks); prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks); prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf); prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf); diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp index f138b5f137..e866b2acd6 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64_memory.cpp @@ -31,16 +31,16 @@ using namespace Xbyak::util; void A32EmitX64::GenFastmemFallbacks() { const std::initializer_list idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; const std::array, 4> read_callbacks{{ - {8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)}, - {16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)}, - {32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)}, - {64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)}, + {8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, + {16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, + {32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, + {64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)}, }}; const std::array, 4> write_callbacks{{ - {8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)}, - {16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)}, - {32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)}, - {64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)}, + {8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)}, }}; const std::array, 4> exclusive_write_callbacks{{ {8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)}, @@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() { code.align(); read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx)); + // params = { this, vaddr, size } if (vaddr_idx != code.ABI_PARAM2.getIdx()) { code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx}); } - if (ordered) { - code.mfence(); - } + code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT); + if (ordered) code.mfence(); callback.EmitCall(code); if (value_idx != code.ABI_RETURN.getIdx()) { code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN); @@ -76,6 +76,7 @@ void A32EmitX64::GenFastmemFallbacks() { code.align(); write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(code); + // params = { this, vaddr, value, size } if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) { code.xchg(code.ABI_PARAM2, code.ABI_PARAM3); } else if (vaddr_idx == code.ABI_PARAM3.getIdx()) { @@ -92,10 +93,9 @@ void A32EmitX64::GenFastmemFallbacks() { } } code.ZeroExtendFrom(bitsize, code.ABI_PARAM3); + code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT); callback.EmitCall(code); - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); ABI_PopCallerSaveRegistersAndAdjustStack(code); code.ret(); PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize)); @@ -138,35 +138,35 @@ void A32EmitX64::GenFastmemFallbacks() { #undef Axx void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); + EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); + EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); + EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); + EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst); + EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst); + EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst); + EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst); + EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite>(ctx, inst); } void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) { @@ -175,33 +175,33 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) { void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead>(ctx, inst); } } diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp index 4b7054b4b2..8bb2fcfa59 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64_memory.cpp @@ -115,16 +115,16 @@ void A64EmitX64::GenMemory128Accessors() { void A64EmitX64::GenFastmemFallbacks() { const std::initializer_list idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; const std::array, 4> read_callbacks{{ - {8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)}, - {16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)}, - {32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)}, - {64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)}, + {8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, + {16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, + {32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, + {64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)}, }}; const std::array, 4> write_callbacks{{ - {8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)}, - {16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)}, - {32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)}, - {64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)}, + {8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, + {64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)}, }}; const std::array, 4> exclusive_write_callbacks{{ {8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)}, @@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() { code.align(); read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx)); + // params = { this, vaddr, size } if (vaddr_idx != code.ABI_PARAM2.getIdx()) { code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx}); } - if (ordered) { - code.mfence(); - } + code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT); + if (ordered) code.mfence(); callback.EmitCall(code); if (value_idx != code.ABI_RETURN.getIdx()) { code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN); @@ -224,6 +224,7 @@ void A64EmitX64::GenFastmemFallbacks() { code.align(); write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(code); + // params = { this, vaddr, value, size } if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) { code.xchg(code.ABI_PARAM2, code.ABI_PARAM3); } else if (vaddr_idx == code.ABI_PARAM3.getIdx()) { @@ -240,10 +241,9 @@ void A64EmitX64::GenFastmemFallbacks() { } } code.ZeroExtendFrom(bitsize, code.ABI_PARAM3); + code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT); callback.EmitCall(code); - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); ABI_PopCallerSaveRegistersAndAdjustStack(code); code.ret(); PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize)); @@ -286,19 +286,19 @@ void A64EmitX64::GenFastmemFallbacks() { #undef Axx void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); + EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); + EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); + EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); + EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead>(ctx, inst); } void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) { @@ -306,23 +306,23 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) { } void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite8>(ctx, inst); + EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst); + EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite32>(ctx, inst); + EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst); + EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) { - EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite64>(ctx, inst); + EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite>(ctx, inst); } void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) { @@ -331,33 +331,33 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) { void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst); + EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst); + EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst); + EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) { if (conf.fastmem_exclusive_access) { - EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst); } else { - EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst); + EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead>(ctx, inst); } } diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc index 4fa14d504b..8bb8be3d5b 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2022 MerryMage * SPDX-License-Identifier: 0BSD @@ -56,16 +59,13 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) { // Neither fastmem nor page table: Use callbacks if constexpr (bitsize == 128) { ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]); - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); code.CallFunction(memory_read_128); ctx.reg_alloc.DefineValue(code, inst, xmm1); } else { ctx.reg_alloc.HostCall(code, inst, {}, args[1]); - if (ordered) { - code.mfence(); - } + code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT); + if (ordered) code.mfence(); Devirtualize(conf.callbacks).EmitCall(code); code.ZeroExtendFrom(bitsize, code.ABI_RETURN); } @@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.HostCall(code, nullptr); code.CallFunction(memory_write_128); } else { + // { this, vaddr, value, size } ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]); + code.mov(code.ABI_PARAM4.cvt32(), bitsize / CHAR_BIT); Devirtualize(conf.callbacks).EmitCall(code); } - if (ordered) { - code.mfence(); - } + if (ordered) code.mfence(); EmitCheckMemoryAbort(ctx, inst); return; } @@ -230,12 +230,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) { if (ordered) { code.mfence(); } - code.CallLambda( - [](AxxUserConfig& conf, Axx::VAddr vaddr) -> T { - return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { - return (conf.callbacks->*callback)(vaddr); - }); + code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T { + return conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> T { + return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT); }); + }); code.ZeroExtendFrom(bitsize, code.ABI_RETURN); } else { const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code); @@ -250,12 +249,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) { if (ordered) { code.mfence(); } - code.CallLambda( - [](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) { - ret = conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> Vector { - return (conf.callbacks->*callback)(vaddr); - }); + code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) { + ret = conf.global_monitor->ReadAndMark(conf.processor_id, vaddr, [&]() -> Vector { + return (conf.callbacks->*callback)(vaddr); }); + }); code.movups(result, xword[rsp + ABI_SHADOW_SPACE]); ctx.reg_alloc.ReleaseStackSpace(code, 16 + ABI_SHADOW_SPACE); diff --git a/src/dynarmic/src/dynarmic/interface/A32/config.h b/src/dynarmic/src/dynarmic/interface/A32/config.h index 5a97fb69f3..dae0c8f692 100644 --- a/src/dynarmic/src/dynarmic/interface/A32/config.h +++ b/src/dynarmic/src/dynarmic/interface/A32/config.h @@ -66,7 +66,9 @@ struct UserCallbacks : public TranslateCallbacks { // All reads through this callback are 4-byte aligned. // Memory must be interpreted as little endian. - std::optional MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); } + std::optional MemoryReadCode(VAddr vaddr) override { + return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t))); + } // This function is called before the instruction at pc is read. // IR code can be emitted by the callee prior to instruction handling. @@ -80,16 +82,10 @@ struct UserCallbacks : public TranslateCallbacks { // Reads through these callbacks may not be aligned. // Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT. - virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0; - virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0; - virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0; - virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0; + virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0; // Writes through these callbacks may not be aligned. - virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0; - virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0; - virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0; - virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0; + virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0; // Writes through these callbacks may not be aligned. virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; } diff --git a/src/dynarmic/src/dynarmic/interface/A64/config.h b/src/dynarmic/src/dynarmic/interface/A64/config.h index 83c1593fd8..1f38e73859 100644 --- a/src/dynarmic/src/dynarmic/interface/A64/config.h +++ b/src/dynarmic/src/dynarmic/interface/A64/config.h @@ -89,20 +89,16 @@ struct UserCallbacks { // All reads through this callback are 4-byte aligned. // Memory must be interpreted as little endian. - virtual std::optional MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); } + virtual std::optional MemoryReadCode(VAddr vaddr) { + return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t))); + } // Reads through these callbacks may not be aligned. - virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0; - virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0; - virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0; - virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0; + virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0; virtual Vector MemoryRead128(VAddr vaddr) = 0; // Writes through these callbacks may not be aligned. - virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0; - virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0; - virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0; - virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0; + virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0; virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0; // Writes through these callbacks may not be aligned. diff --git a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp index 2b972cbdf4..719081cd29 100644 --- a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp +++ b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp @@ -40,7 +40,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u8 value_from_memory = cb->MemoryRead8(vaddr); + const u8 value_from_memory = u8(cb->MemoryRead(vaddr, sizeof(u8))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } @@ -51,7 +51,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u16 value_from_memory = cb->MemoryRead16(vaddr); + const u16 value_from_memory = u16(cb->MemoryRead(vaddr, sizeof(u16))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } @@ -62,7 +62,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u32 value_from_memory = cb->MemoryRead32(vaddr); + const u32 value_from_memory = u32(cb->MemoryRead(vaddr, sizeof(u32))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } @@ -73,7 +73,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { if (inst.AreAllArgsImmediates()) { const u32 vaddr = inst.GetArg(1).GetU32(); if (cb->IsReadOnlyMemory(vaddr)) { - const u64 value_from_memory = cb->MemoryRead64(vaddr); + const u64 value_from_memory = u64(cb->MemoryRead(vaddr, sizeof(u64))); inst.ReplaceUsesWith(IR::Value{value_from_memory}); } } diff --git a/src/dynarmic/tests/A32/fuzz_thumb.cpp b/src/dynarmic/tests/A32/fuzz_thumb.cpp index 67a01daf9c..a29b877f3e 100644 --- a/src/dynarmic/tests/A32/fuzz_thumb.cpp +++ b/src/dynarmic/tests/A32/fuzz_thumb.cpp @@ -503,7 +503,7 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") { } } -TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") { +TEST_CASE("Verify fix for off by one error in MemoryRead<32> worked", "[Thumb][Thumb16]") { ThumbTestEnv test_env; // Prepare test subjects diff --git a/src/dynarmic/tests/A32/testenv.h b/src/dynarmic/tests/A32/testenv.h index bebc2566da..a22cc7131e 100644 --- a/src/dynarmic/tests/A32/testenv.h +++ b/src/dynarmic/tests/A32/testenv.h @@ -16,6 +16,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "dynarmic/frontend/A32/translate/translate_callbacks.h" #include "dynarmic/interface/A32/a32.h" template @@ -59,42 +60,51 @@ public: return infinite_loop_u32; // B . } - std::uint8_t MemoryRead8(u32 vaddr) override { - if (IsInCodeMem(vaddr)) { - return reinterpret_cast(code_mem.data())[vaddr]; + u64 MemoryRead(u32 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): { + if (IsInCodeMem(vaddr)) + return reinterpret_cast(code_mem.data())[vaddr]; + if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) + return iter->second; + return u8(vaddr); } - if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) { - return iter->second; + default: + std::abort(); } - return static_cast(vaddr); - } - std::uint16_t MemoryRead16(u32 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - std::uint32_t MemoryRead32(u32 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - std::uint64_t MemoryRead64(u32 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; } - void MemoryWrite8(u32 vaddr, std::uint8_t value) override { - if (vaddr < code_mem.size() * sizeof(u32)) { - code_mem_modified_by_guest = true; + void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + if (vaddr < code_mem.size() * sizeof(u32)) + code_mem_modified_by_guest = true; + modified_memory[vaddr] = value; + break; + default: + std::abort(); } - modified_memory[vaddr] = value; - } - void MemoryWrite16(u32 vaddr, std::uint16_t value) override { - MemoryWrite8(vaddr, static_cast(value)); - MemoryWrite8(vaddr + 1, static_cast(value >> 8)); - } - void MemoryWrite32(u32 vaddr, std::uint32_t value) override { - MemoryWrite16(vaddr, static_cast(value)); - MemoryWrite16(vaddr + 2, static_cast(value >> 16)); - } - void MemoryWrite64(u32 vaddr, std::uint64_t value) override { - MemoryWrite32(vaddr, static_cast(value)); - MemoryWrite32(vaddr + 4, static_cast(value >> 32)); } void CallSVC(std::uint32_t swi) override { @@ -143,46 +153,41 @@ public: return read(vaddr); } - std::uint8_t MemoryRead8(std::uint32_t vaddr) override { - return read(vaddr); - } - std::uint16_t MemoryRead16(std::uint32_t vaddr) override { - return read(vaddr); - } - std::uint32_t MemoryRead32(std::uint32_t vaddr) override { - return read(vaddr); - } - std::uint64_t MemoryRead64(std::uint32_t vaddr) override { - return read(vaddr); + u64 MemoryRead(u32 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): return read(vaddr); + case sizeof(u32): return read(vaddr); + case sizeof(u16): return read(vaddr); + case sizeof(u8): return read(vaddr); + default: + std::abort(); + } } - void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override { - write(vaddr, value); - } - void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override { - write(vaddr, value); - } - void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override { - write(vaddr, value); - } - void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override { - write(vaddr, value); + void MemoryWrite(Dynarmic::A32::VAddr vaddr, std::uint64_t value, size_t size) override { + switch (size) { + case sizeof(u64): return write(vaddr, u64(value)); + case sizeof(u32): return write(vaddr, u32(value)); + case sizeof(u16): return write(vaddr, u16(value)); + case sizeof(u8): return write(vaddr, u8(value)); + default: std::abort(); + } } - bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { - MemoryWrite8(vaddr, value); + bool MemoryWriteExclusive8(Dynarmic::A32::VAddr vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { + MemoryWrite(vaddr, value, sizeof(u8)); return true; } - bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { - MemoryWrite16(vaddr, value); + bool MemoryWriteExclusive16(Dynarmic::A32::VAddr vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { + MemoryWrite(vaddr, value, sizeof(u16)); return true; } - bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { - MemoryWrite32(vaddr, value); + bool MemoryWriteExclusive32(Dynarmic::A32::VAddr vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { + MemoryWrite(vaddr, value, sizeof(u32)); return true; } - bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { - MemoryWrite64(vaddr, value); + bool MemoryWriteExclusive64(Dynarmic::A32::VAddr vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { + MemoryWrite(vaddr, value, sizeof(u64)); return true; } diff --git a/src/dynarmic/tests/A64/a64.cpp b/src/dynarmic/tests/A64/a64.cpp index 99cc9d3d4d..27ef60a69b 100644 --- a/src/dynarmic/tests/A64/a64.cpp +++ b/src/dynarmic/tests/A64/a64.cpp @@ -1006,8 +1006,8 @@ TEST_CASE("A64: 128-bit exclusive read/write", "[a64]") { REQUIRE(jit.GetRegister(1) == 0x7f7e7d7c7b7a7978); REQUIRE(jit.GetRegister(2) == 0x8786858483828180); REQUIRE(jit.GetRegister(4) == 0); - REQUIRE(env.MemoryRead64(0x1234567812345678) == 0xaf00d1e5badcafe0); - REQUIRE(env.MemoryRead64(0x1234567812345680) == 0xd0d0cacad0d0caca); + REQUIRE(env.MemoryRead(0x1234567812345678, sizeof(u64)) == 0xaf00d1e5badcafe0); + REQUIRE(env.MemoryRead(0x1234567812345680, sizeof(u64)) == 0xd0d0cacad0d0caca); } TEST_CASE("A64: CNTPCT_EL0", "[a64]") { diff --git a/src/dynarmic/tests/A64/fibonacci.cpp b/src/dynarmic/tests/A64/fibonacci.cpp index 12c4170f66..d3c6b86024 100644 --- a/src/dynarmic/tests/A64/fibonacci.cpp +++ b/src/dynarmic/tests/A64/fibonacci.cpp @@ -25,48 +25,55 @@ public: u64 ticks_left = 0; ankerl::unordered_dense::map memory{}; - u8 MemoryRead8(u64 vaddr) override { - return memory[vaddr]; - } - - u16 MemoryRead16(u64 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - - u32 MemoryRead32(u64 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - - u64 MemoryRead64(u64 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): + return memory[vaddr]; + default: + std::abort(); + } } std::array MemoryRead128(u64 vaddr) override { - return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)}; + return { + MemoryRead(vaddr, sizeof(u64)), + MemoryRead(vaddr + sizeof(u64), sizeof(u64)) + }; } - void MemoryWrite8(u64 vaddr, u8 value) override { - memory[vaddr] = value; + void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + memory[vaddr] = value; + break; + default: + std::abort(); + } } - - void MemoryWrite16(u64 vaddr, u16 value) override { - MemoryWrite8(vaddr, u8(value)); - MemoryWrite8(vaddr + 1, u8(value >> 8)); - } - - void MemoryWrite32(u64 vaddr, u32 value) override { - MemoryWrite16(vaddr, u16(value)); - MemoryWrite16(vaddr + 2, u16(value >> 16)); - } - - void MemoryWrite64(u64 vaddr, u64 value) override { - MemoryWrite32(vaddr, u32(value)); - MemoryWrite32(vaddr + 4, u32(value >> 32)); - } - void MemoryWrite128(u64 vaddr, std::array value) override { - MemoryWrite64(vaddr, value[0]); - MemoryWrite64(vaddr + 8, value[1]); + MemoryWrite(vaddr, value[0], sizeof(u64)); + MemoryWrite(vaddr + 8, value[1], sizeof(u64)); } void CallSVC(u32) override { @@ -135,9 +142,9 @@ TEST_CASE("A64: fibonacci", "[a64]") { code.RET(); for (size_t i = 0; i < 1024; i++) { - env.MemoryWrite32(i * 4, instructions[i]); + env.MemoryWrite(i * 4, instructions[i], sizeof(u32)); } - env.MemoryWrite32(8888, 0xd4200000); + env.MemoryWrite(8888, 0xd4200000, sizeof(u32)); cpu.SetRegister(30, 8888); cpu.SetRegister(0, 10); diff --git a/src/dynarmic/tests/A64/testenv.h b/src/dynarmic/tests/A64/testenv.h index 1d260f5460..78cd195845 100644 --- a/src/dynarmic/tests/A64/testenv.h +++ b/src/dynarmic/tests/A64/testenv.h @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "dynarmic/interface/A64/a64.h" +#include "dynarmic/interface/A64/config.h" using Vector = Dynarmic::A64::Vector; @@ -34,64 +35,79 @@ public: return code_mem[index]; } - std::uint8_t MemoryRead8(u64 vaddr) override { - if (IsInCodeMem(vaddr)) { - return reinterpret_cast(code_mem.data())[vaddr - code_mem_start_address]; + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): { + if (IsInCodeMem(vaddr)) + return reinterpret_cast(code_mem.data())[vaddr - code_mem_start_address]; + if (auto const it = modified_memory.find(vaddr); it != modified_memory.end()) + return it->second; + return u8(vaddr); + } + default: + std::abort(); } - if (auto const it = modified_memory.find(vaddr); it != modified_memory.end()) - return it->second; - return u8(vaddr); - } - std::uint16_t MemoryRead16(u64 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - std::uint32_t MemoryRead32(u64 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - std::uint64_t MemoryRead64(u64 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; - } - Vector MemoryRead128(u64 vaddr) override { - return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)}; } - void MemoryWrite8(u64 vaddr, std::uint8_t value) override { - if (IsInCodeMem(vaddr)) { - code_mem_modified_by_guest = true; + Vector MemoryRead128(u64 vaddr) override { + return { + MemoryRead(vaddr, sizeof(u64)), + MemoryRead(vaddr + 8, sizeof(u64)) + }; + } + + void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + if (IsInCodeMem(vaddr)) { + code_mem_modified_by_guest = true; + } + modified_memory[vaddr] = value; + break; + default: + std::abort(); } - modified_memory[vaddr] = value; - } - void MemoryWrite16(u64 vaddr, std::uint16_t value) override { - MemoryWrite8(vaddr, u8(value)); - MemoryWrite8(vaddr + 1, u8(value >> 8)); - } - void MemoryWrite32(u64 vaddr, std::uint32_t value) override { - MemoryWrite16(vaddr, u16(value)); - MemoryWrite16(vaddr + 2, u16(value >> 16)); - } - void MemoryWrite64(u64 vaddr, std::uint64_t value) override { - MemoryWrite32(vaddr, u32(value)); - MemoryWrite32(vaddr + 4, u32(value >> 32)); } void MemoryWrite128(u64 vaddr, Vector value) override { - MemoryWrite64(vaddr, value[0]); - MemoryWrite64(vaddr + 8, value[1]); + MemoryWrite(vaddr, value[0], sizeof(u64)); + MemoryWrite(vaddr + 8, value[1], sizeof(u64)); } bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { - MemoryWrite8(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u8)); return true; } bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { - MemoryWrite16(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u16)); return true; } bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { - MemoryWrite32(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u32)); return true; } bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { - MemoryWrite64(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u64)); return true; } bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override { @@ -145,52 +161,46 @@ public: return read(vaddr); } - std::uint8_t MemoryRead8(u64 vaddr) override { - return read(vaddr); - } - std::uint16_t MemoryRead16(u64 vaddr) override { - return read(vaddr); - } - std::uint32_t MemoryRead32(u64 vaddr) override { - return read(vaddr); - } - std::uint64_t MemoryRead64(u64 vaddr) override { - return read(vaddr); + u64 MemoryRead(u64 vaddr, size_t size) override { + switch (size) { + case sizeof(u64): return read(vaddr); + case sizeof(u32): return read(vaddr); + case sizeof(u16): return read(vaddr); + case sizeof(u8): return read(vaddr); + default: std::abort(); + } } Vector MemoryRead128(u64 vaddr) override { return read(vaddr); } - void MemoryWrite8(u64 vaddr, std::uint8_t value) override { - write(vaddr, value); - } - void MemoryWrite16(u64 vaddr, std::uint16_t value) override { - write(vaddr, value); - } - void MemoryWrite32(u64 vaddr, std::uint32_t value) override { - write(vaddr, value); - } - void MemoryWrite64(u64 vaddr, std::uint64_t value) override { - write(vaddr, value); + void MemoryWrite(u64 vaddr, std::uint64_t value, size_t size) override { + switch (size) { + case sizeof(u64): return write(vaddr, u64(value)); + case sizeof(u32): return write(vaddr, u32(value)); + case sizeof(u16): return write(vaddr, u16(value)); + case sizeof(u8): return write(vaddr, u8(value)); + default: std::abort(); + } } void MemoryWrite128(u64 vaddr, Vector value) override { write(vaddr, value); } bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override { - MemoryWrite8(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u8)); return true; } bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override { - MemoryWrite16(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u16)); return true; } bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override { - MemoryWrite32(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u32)); return true; } bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override { - MemoryWrite64(vaddr, value); + MemoryWrite(vaddr, value, sizeof(u64)); return true; } bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override { diff --git a/src/dynarmic/tests/print_info.cpp b/src/dynarmic/tests/print_info.cpp index 8901ea167d..09a5766490 100644 --- a/src/dynarmic/tests/print_info.cpp +++ b/src/dynarmic/tests/print_info.cpp @@ -18,6 +18,7 @@ #include #include #include +#include "dynarmic/frontend/A32/translate/translate_callbacks.h" #include "dynarmic/mcl/bit.hpp" #include "common/common_types.h" @@ -118,36 +119,47 @@ public: u64 ticks_left = 0; std::map memory; - std::uint8_t MemoryRead8(u32 vaddr) override { - if (auto iter = memory.find(vaddr); iter != memory.end()) { - return iter->second; + u64 MemoryRead(Dynarmic::A32::VAddr vaddr, size_t size) override { + switch (size) { + case sizeof(u64): + return MemoryRead(vaddr, sizeof(u32)) + | MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32; + case sizeof(u32): + return MemoryRead(vaddr, sizeof(u16)) + | MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16; + case sizeof(u16): + return MemoryRead(vaddr, sizeof(u8)) + | MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8; + case sizeof(u8): { + if (auto const it = memory.find(vaddr); it != memory.end()) + return it->second; + return 0; + } + default: + std::abort(); } - return 0; - } - std::uint16_t MemoryRead16(u32 vaddr) override { - return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8; - } - std::uint32_t MemoryRead32(u32 vaddr) override { - return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16; - } - std::uint64_t MemoryRead64(u32 vaddr) override { - return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32; } - void MemoryWrite8(u32 vaddr, std::uint8_t value) override { - memory[vaddr] = value; - } - void MemoryWrite16(u32 vaddr, std::uint16_t value) override { - MemoryWrite8(vaddr, static_cast(value)); - MemoryWrite8(vaddr + 1, static_cast(value >> 8)); - } - void MemoryWrite32(u32 vaddr, std::uint32_t value) override { - MemoryWrite16(vaddr, static_cast(value)); - MemoryWrite16(vaddr + 2, static_cast(value >> 16)); - } - void MemoryWrite64(u32 vaddr, std::uint64_t value) override { - MemoryWrite32(vaddr, static_cast(value)); - MemoryWrite32(vaddr + 4, static_cast(value >> 32)); + void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override { + switch (size) { + case sizeof(u64): + MemoryWrite(vaddr, u32(value), sizeof(u32)); + MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32)); + break; + case sizeof(u32): + MemoryWrite(vaddr, u16(value), sizeof(u16)); + MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16)); + break; + case sizeof(u16): + MemoryWrite(vaddr, u8(value), sizeof(u8)); + MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8)); + break; + case sizeof(u8): + memory[vaddr] = value; + break; + default: + std::abort(); + } } void CallSVC(std::uint32_t swi) override { @@ -231,7 +243,7 @@ void ExecuteA32Instruction(u32 instruction) { if (const auto address = get_value()) { fmt::print("value: "); if (const auto value = get_value()) { - env.MemoryWrite32(*address, *value); + env.MemoryWrite(*address, *value, sizeof(u32)); fmt::print("> mem[{:#08x}] = {:#08x}\n", *address, *value); } } @@ -247,8 +259,8 @@ void ExecuteA32Instruction(u32 instruction) { cpu.SetFpscr(fpscr); const u32 initial_pc = regs[15]; - env.MemoryWrite32(initial_pc + 0, instruction); - env.MemoryWrite32(initial_pc + 4, 0xEAFFFFFE); // B +0 + env.MemoryWrite(initial_pc + 0, instruction, sizeof(u32)); + env.MemoryWrite(initial_pc + 4, 0xEAFFFFFE, sizeof(u32)); // B +0 cpu.Run(); fmt::print("{}", fmt::join(cpu.Disassemble(), "\n")); diff --git a/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp b/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp index c0e1e13d9b..1fe09cdef0 100644 --- a/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp +++ b/src/dynarmic/tests/unicorn_emu/a32_unicorn.cpp @@ -290,7 +290,7 @@ bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type auto page = std::make_unique(); page->address = base_address; for (size_t i = 0; i < page->data.size(); ++i) - page->data[i] = this_->testenv.MemoryRead8(static_cast(base_address + i)); + page->data[i] = u8(this_->testenv.MemoryRead(u32(base_address + i), sizeof(u8))); uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); if (err == UC_ERR_MAP) @@ -321,24 +321,7 @@ bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type template bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) { auto* this_ = static_cast(user_data); - - switch (size) { - case 1: - this_->testenv.MemoryWrite8(start_address, static_cast(value)); - break; - case 2: - this_->testenv.MemoryWrite16(start_address, static_cast(value)); - break; - case 4: - this_->testenv.MemoryWrite32(start_address, static_cast(value)); - break; - case 8: - this_->testenv.MemoryWrite64(start_address, value); - break; - default: - UNREACHABLE(); - } - + this_->testenv.MemoryWrite(start_address, value, size); return true; } diff --git a/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp b/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp index c9a194d50a..4afb6a6859 100644 --- a/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp +++ b/src/dynarmic/tests/unicorn_emu/a64_unicorn.cpp @@ -197,7 +197,7 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta auto page = std::make_unique(); page->address = base_address; for (size_t i = 0; i < page->data.size(); ++i) - page->data[i] = this_->testenv.MemoryRead8(base_address + i); + page->data[i] = u8(this_->testenv.MemoryRead(base_address + i, sizeof(u8))); uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data()); if (err == UC_ERR_MAP) @@ -227,23 +227,6 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) { auto* this_ = static_cast(user_data); - - switch (size) { - case 1: - this_->testenv.MemoryWrite8(start_address, static_cast(value)); - break; - case 2: - this_->testenv.MemoryWrite16(start_address, static_cast(value)); - break; - case 4: - this_->testenv.MemoryWrite32(start_address, static_cast(value)); - break; - case 8: - this_->testenv.MemoryWrite64(start_address, value); - break; - default: - UNREACHABLE(); - } - + this_->testenv.MemoryWrite(start_address, value, size); return true; }