aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorlizzie <lizzie@eden-emu.dev>2026-09-19 01:39:46 +0200
committercrueter <crueter@eden-emu.dev>2026-09-19 01:39:46 +0200
commit9a30172e45fcb7833cc49ba4d6450211449b14df (patch)
tree4ab0ffbb276464086615fa247d69a9cdf9d5ac55 /src/core
parent07f40d5cac635ff102aa6ce9c32572cc20d69959 (diff)
[dynarmic] Coalesce non-exclusive Write/Read fallback functions (#4158)
the general idea is to have a common procedure form whom to call this way theres less "jumping around" for values of different sizes additionally this **should** allow for better codegen since most of the u8,u16,u32,u64 can be held within a u64 theoretically this means that you could deifne callbacks in suck a way that it's essentially as costly as a `mov r64, m64` but that's not doable due to the fact we have to do translations... Is this a good change? Primarily aimed for x86 and ARM to benefit, but I suppose LooooooongArch64 can benefit too. Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4158 Reviewed-by: crueter <crueter@eden-emu.dev> Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Diffstat (limited to 'src/core')
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp52
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp52
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h11
-rw-r--r--src/core/hle/service/jit/jit_context.cpp62
-rw-r--r--src/core/tools/freezer.cpp16
6 files changed, 74 insertions, 129 deletions
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 8be0abc4fa..a59aa3cac3 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<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
@@ -50,27 +44,17 @@ std::optional<u32> 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::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);
+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();
+ }
}
}
-
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<u32> 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 9db40b9356..894857a86d 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<u32> 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 4c8570913f..5ea5dde8a6 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<u32> 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<u8>(vaddr);
- }
- u16 MemoryRead16(u64 vaddr) override {
- return ReadMemory<u16>(vaddr);
- }
- u32 MemoryRead32(u64 vaddr) override {
- return ReadMemory<u32>(vaddr);
- }
- u64 MemoryRead64(u64 vaddr) override {
- return ReadMemory<u64>(vaddr);
+ u64 MemoryRead(u64 vaddr, size_t size) override {
+ switch (size) {
+ case sizeof(u64): return ReadMemory<u64>(vaddr);
+ case sizeof(u32): return ReadMemory<u32>(vaddr);
+ case sizeof(u16): return ReadMemory<u16>(vaddr);
+ case sizeof(u8): return ReadMemory<u8>(vaddr);
+ default: UNREACHABLE();
+ }
}
u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(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<u8>(vaddr, value);
- }
- void MemoryWrite16(u64 vaddr, u16 value) override {
- WriteMemory<u16>(vaddr, value);
- }
- void MemoryWrite32(u64 vaddr, u32 value) override {
- WriteMemory<u32>(vaddr, value);
- }
- void MemoryWrite64(u64 vaddr, u64 value) override {
- WriteMemory<u64>(vaddr, value);
+ void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
+ switch (size) {
+ case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
+ case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
+ case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
+ case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
+ default: UNREACHABLE();
+ }
}
void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(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<Elf64_Rela>(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<Elf64_Relr>(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<u8>(value));
- break;
- case 2:
- memory.Write16(addr, static_cast<u16>(value));
- break;
- case 4:
- memory.Write32(addr, static_cast<u32>(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();
}