From 5906e6844fb1c77a3387892a9e2902792309ec69 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 1 Oct 2025 08:04:34 +0000 Subject: [PATCH 1/7] [core] use memcpy instead of hand rolling aligned cases Hand rolling memcpy like this is always frowned upon because the compiler has more insight on whats going on (plus the code resolves to a worse version of itself on assembly). This removes some branches that are just straight up redundant. May save stuff especially for systems without fastmem enabled. Signed-off-by: lizzie --- src/core/memory.cpp | 203 ++++++++------------------------------------ 1 file changed, 36 insertions(+), 167 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 2583aae867..f84507d125 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "common/assert.h" @@ -681,22 +682,17 @@ struct Memory::Impl { } } - [[nodiscard]] u8* GetPointerImpl(u64 vaddr, auto on_unmapped, auto on_rasterizer) const { + template + [[nodiscard]] u8* GetPointerImpl(u64 vaddr, F&& on_unmapped, G&& on_rasterizer) const { // AARCH64 masks the upper 16 bit of all memory accesses - vaddr = vaddr & 0xffffffffffffULL; - if (!AddressSpaceContains(*current_page_table, vaddr, 1)) [[unlikely]] { - on_unmapped(); - return nullptr; - } else { + vaddr &= 0xffffffffffffULL; + if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] { // Avoid adding any extra logic to this fast-path block const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); - if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { + if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] { return reinterpret_cast(pointer + vaddr); } else { switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { - case Common::PageType::Unmapped: - on_unmapped(); - return nullptr; case Common::PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); return nullptr; @@ -707,11 +703,18 @@ struct Memory::Impl { on_rasterizer(); return host_ptr; } + case Common::PageType::Unmapped: [[unlikely]] { + on_unmapped(); + return nullptr; + } default: UNREACHABLE(); } return nullptr; } + } else { + on_unmapped(); + return nullptr; } } @@ -729,172 +732,38 @@ struct Memory::Impl { GetInteger(vaddr), []() {}, []() {}); } - /** - * Reads a particular data type out of memory at the given virtual address. - * - * @param vaddr The virtual address to read the data type from. - * - * @tparam T The data type to read out of memory. This type *must* be - * trivially copyable, otherwise the behavior of this function - * is undefined. - * - * @returns The instance of T read from the specified virtual address. - */ + /// @brief Reads a particular data type out of memory at the given virtual address. + /// @param vaddr The virtual address to read the data type from. + /// @tparam T The data type to read out of memory. + /// @returns The instance of T read from the specified virtual address. template - T Read(Common::ProcessAddress vaddr) { - // Fast path for aligned reads of common sizes + inline T Read(Common::ProcessAddress vaddr) requires(std::is_trivially_copyable_v) noexcept { const u64 addr = GetInteger(vaddr); - if constexpr (std::is_same_v || std::is_same_v) { - // 8-bit reads are always aligned - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read8 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*ptr); - } - return 0; - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 16-bit reads - if ((addr & 1) == 0) { - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read16 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*reinterpret_cast(ptr)); - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 32-bit reads - if ((addr & 3) == 0) { - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read32 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*reinterpret_cast(ptr)); - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 64-bit reads - if ((addr & 7) == 0) { - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read64 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*reinterpret_cast(ptr)); - } - } - } - - // Fall back to the general case for other types or unaligned access - T result = 0; - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { + if (auto const ptr = GetPointerImpl(addr, [addr]() { + LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr); + }, [&]() { + HandleRasterizerDownload(addr, sizeof(T)); + }); ptr) [[likely]] { + // It may be tempting to rewrite this particular section to use "reinterpret_cast"; + // afterall, it's trivially copyable so surely it can be copied ov- Alignment. + // Remember, alignment. memcpy() will deal with all the alignment extremely fast. + T result{}; std::memcpy(&result, ptr, sizeof(T)); + return result; } - return result; + return T{}; } - /** - * Writes a particular data type to memory at the given virtual address. - * - * @param vaddr The virtual address to write the data type to. - * - * @tparam T The data type to write to memory. This type *must* be - * trivially copyable, otherwise the behavior of this function - * is undefined. - */ + /// @brief Writes a particular data type to memory at the given virtual address. + /// @param vaddr The virtual address to write the data type to. + /// @tparam T The data type to write to memory. template - void Write(Common::ProcessAddress vaddr, const T data) { - // Fast path for aligned writes of common sizes + inline void Write(Common::ProcessAddress vaddr, const T data) requires(std::is_trivially_copyable_v) noexcept { const u64 addr = GetInteger(vaddr); - if constexpr (std::is_same_v || std::is_same_v) { - // 8-bit writes are always aligned - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write8 @ 0x{:016X} = 0x{:02X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *ptr = static_cast(data); - } - return; - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 16-bit writes - if ((addr & 1) == 0) { - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write16 @ 0x{:016X} = 0x{:04X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *reinterpret_cast(ptr) = static_cast(data); - return; - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 32-bit writes - if ((addr & 3) == 0) { - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write32 @ 0x{:016X} = 0x{:08X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *reinterpret_cast(ptr) = static_cast(data); - return; - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 64-bit writes - if ((addr & 7) == 0) { - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write64 @ 0x{:016X} = 0x{:016X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *reinterpret_cast(ptr) = static_cast(data); - return; - } - } - } - - // Fall back to the general case for other types or unaligned access - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, - addr, static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { + if (auto const ptr = GetPointerImpl(addr, [addr, data]() { + LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, addr, u64(data)); + }, [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); ptr) [[likely]] std::memcpy(ptr, &data, sizeof(T)); - } } template From 90961d3695430ca311a68cba9dfc953e4c4b9229 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 2 Oct 2025 05:19:45 +0000 Subject: [PATCH 2/7] fix Signed-off-by: lizzie --- src/core/memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f84507d125..e1aa3473fc 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -737,7 +737,7 @@ struct Memory::Impl { /// @tparam T The data type to read out of memory. /// @returns The instance of T read from the specified virtual address. template - inline T Read(Common::ProcessAddress vaddr) requires(std::is_trivially_copyable_v) noexcept { + inline T Read(Common::ProcessAddress vaddr) noexcept requires(std::is_trivially_copyable_v) { const u64 addr = GetInteger(vaddr); if (auto const ptr = GetPointerImpl(addr, [addr]() { LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr); @@ -758,7 +758,7 @@ struct Memory::Impl { /// @param vaddr The virtual address to write the data type to. /// @tparam T The data type to write to memory. template - inline void Write(Common::ProcessAddress vaddr, const T data) requires(std::is_trivially_copyable_v) noexcept { + inline void Write(Common::ProcessAddress vaddr, const T data) noexcept requires(std::is_trivially_copyable_v) { const u64 addr = GetInteger(vaddr); if (auto const ptr = GetPointerImpl(addr, [addr, data]() { LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, addr, u64(data)); From de594c8792622caea4400c49df2cadac8f8b1143 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 2 Oct 2025 23:20:45 +0200 Subject: [PATCH 3/7] [dynarmic] add safe-opt to skip IR verification (#2613) Most programs are well behaved and don't cause internal IR issues. Hence, verification can be safely skipped. Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2613 Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/dynarmic/src/dynarmic/interface/optimization_flags.h | 5 +++++ src/dynarmic/src/dynarmic/ir/opt_passes.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dynarmic/src/dynarmic/interface/optimization_flags.h b/src/dynarmic/src/dynarmic/interface/optimization_flags.h index 743d902767..9e58197b47 100644 --- a/src/dynarmic/src/dynarmic/interface/optimization_flags.h +++ b/src/dynarmic/src/dynarmic/interface/optimization_flags.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2020 MerryMage * SPDX-License-Identifier: 0BSD @@ -34,6 +37,8 @@ enum class OptimizationFlag : std::uint32_t { MiscIROpt = 0x00000020, /// Optimize for code speed rather than for code size (this serves well for tight loops) CodeSpeed = 0x00000040, + /// Disable verification passes + DisableVerification = 0x00000080, /// This is an UNSAFE optimization that reduces accuracy of fused multiply-add operations. /// This unfuses fused instructions to improve performance on host CPUs without FMA support. diff --git a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp index 844e29023c..e9175f0e6b 100644 --- a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp +++ b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp @@ -1491,9 +1491,9 @@ void Optimize(IR::Block& block, const A32::UserConfig& conf, const Optimization: Optimization::DeadCodeElimination(block); } Optimization::IdentityRemovalPass(block); - //if (!conf.HasOptimization(OptimizationFlag::DisableVerification)) { + if (!conf.HasOptimization(OptimizationFlag::DisableVerification)) { Optimization::VerificationPass(block); - //} + } } void Optimize(IR::Block& block, const A64::UserConfig& conf, const Optimization::PolyfillOptions& polyfill_options) { @@ -1511,9 +1511,9 @@ void Optimize(IR::Block& block, const A64::UserConfig& conf, const Optimization: if (conf.HasOptimization(OptimizationFlag::MiscIROpt)) [[likely]] { Optimization::A64MergeInterpretBlocksPass(block, conf.callbacks); } - //if (!conf.HasOptimization(OptimizationFlag::DisableVerification)) { + if (!conf.HasOptimization(OptimizationFlag::DisableVerification)) { Optimization::VerificationPass(block); - //} + } } } // namespace Dynarmic::Optimization From 9173eec402f9da453dee4f56c883ac5df665d3f6 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 2 Oct 2025 23:39:59 +0200 Subject: [PATCH 4/7] [compat] fix logind DBus on non-linux unixes (#2648) Backported from FreeBSD ports patches Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2648 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- src/yuzu/main.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index fc7a953d77..4d5238643c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1770,16 +1770,25 @@ void GMainWindow::OnDisplayTitleBars(bool show) { void GMainWindow::SetupPrepareForSleep() { #ifdef __unix__ - auto bus = QDBusConnection::systemBus(); - if (bus.isConnected()) { + if (auto bus = QDBusConnection::systemBus(); bus.isConnected()) { + // See https://github.com/ConsoleKit2/ConsoleKit2/issues/150 +#ifdef __linux__ + const auto dbus_logind_service = QStringLiteral("org.freedesktop.login1"); + const auto dbus_logind_path = QStringLiteral("/org/freedesktop/login1"); + const auto dbus_logind_manager_if = QStringLiteral("org.freedesktop.login1.Manager"); + //const auto dbus_logind_session_if = QStringLiteral("org.freedesktop.login1.Session"); +#else + const auto dbus_logind_service = QStringLiteral("org.freedesktop.ConsoleKit"); + const auto dbus_logind_path = QStringLiteral("/org/freedesktop/ConsoleKit/Manager"); + const auto dbus_logind_manager_if = QStringLiteral("org.freedesktop.ConsoleKit.Manager"); + //const auto dbus_logind_session_if = QStringLiteral("org.freedesktop.ConsoleKit.Session"); +#endif const bool success = bus.connect( - QStringLiteral("org.freedesktop.login1"), QStringLiteral("/org/freedesktop/login1"), - QStringLiteral("org.freedesktop.login1.Manager"), QStringLiteral("PrepareForSleep"), + dbus_logind_service, dbus_logind_path, + dbus_logind_manager_if, QStringLiteral("PrepareForSleep"), QStringLiteral("b"), this, SLOT(OnPrepareForSleep(bool))); - - if (!success) { + if (!success) LOG_WARNING(Frontend, "Couldn't register PrepareForSleep signal"); - } } else { LOG_WARNING(Frontend, "QDBusConnection system bus is not connected"); } From f4f3425d86c7e1ed4b9094365cd39cc0bfa174a5 Mon Sep 17 00:00:00 2001 From: Calchan Date: Fri, 3 Oct 2025 04:46:27 +0200 Subject: [PATCH 5/7] [sockets] block more domains (#2632) * Bring in the domain-blocking code from the legacy branch * Make blockedDomains a `static constexpr const std::array` Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2632 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: Calchan Co-committed-by: Calchan --- src/core/hle/service/sockets/sfdnsres.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index b07bd3e58e..68d73f0a59 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -53,6 +53,19 @@ enum class NetDbError : s32 { NoData = 4, }; +static const constexpr std::array blockedDomains = {"srv.nintendo.net", + "battle.net", + "microsoft.com", + "mojang.com", + "xboxlive.com", + "minecraftservices.com"}; + +static bool IsBlockedHost(const std::string& host) { + return std::any_of( + blockedDomains.begin(), blockedDomains.end(), + [&host](const std::string& domain) { return host.find(domain) != std::string::npos; }); +} + static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) { // These combinations have been verified on console (but are not // exhaustive). @@ -154,7 +167,7 @@ static std::pair GetHostByNameRequestImpl(HLERequestConte // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. // Prevent resolution of Nintendo servers - if (host.find("srv.nintendo.net") != std::string::npos) { + if (IsBlockedHost(host)) { LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); return {0, GetAddrInfoError::AGAIN}; } @@ -271,7 +284,7 @@ static std::pair GetAddrInfoRequestImpl(HLERequestContext const std::string host = Common::StringFromBuffer(host_buffer); // Prevent resolution of Nintendo servers - if (host.find("srv.nintendo.net") != std::string::npos) { + if (IsBlockedHost(host)) { LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host); return {0, GetAddrInfoError::AGAIN}; } @@ -359,5 +372,4 @@ void SFDNSRES::ResolverSetOptionRequest(HLERequestContext& ctx) { rb.Push(ResultSuccess); rb.Push(0); // bsd errno } - } // namespace Service::Sockets From d6490c9cb870b590ce8c3e86fcfa6a1772aabf3b Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 1 Oct 2025 08:04:34 +0000 Subject: [PATCH 6/7] [core] use memcpy instead of hand rolling aligned cases Hand rolling memcpy like this is always frowned upon because the compiler has more insight on whats going on (plus the code resolves to a worse version of itself on assembly). This removes some branches that are just straight up redundant. May save stuff especially for systems without fastmem enabled. Signed-off-by: lizzie --- src/core/memory.cpp | 203 ++++++++------------------------------------ 1 file changed, 36 insertions(+), 167 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 2583aae867..f84507d125 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "common/assert.h" @@ -681,22 +682,17 @@ struct Memory::Impl { } } - [[nodiscard]] u8* GetPointerImpl(u64 vaddr, auto on_unmapped, auto on_rasterizer) const { + template + [[nodiscard]] u8* GetPointerImpl(u64 vaddr, F&& on_unmapped, G&& on_rasterizer) const { // AARCH64 masks the upper 16 bit of all memory accesses - vaddr = vaddr & 0xffffffffffffULL; - if (!AddressSpaceContains(*current_page_table, vaddr, 1)) [[unlikely]] { - on_unmapped(); - return nullptr; - } else { + vaddr &= 0xffffffffffffULL; + if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] { // Avoid adding any extra logic to this fast-path block const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); - if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { + if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] { return reinterpret_cast(pointer + vaddr); } else { switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { - case Common::PageType::Unmapped: - on_unmapped(); - return nullptr; case Common::PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr); return nullptr; @@ -707,11 +703,18 @@ struct Memory::Impl { on_rasterizer(); return host_ptr; } + case Common::PageType::Unmapped: [[unlikely]] { + on_unmapped(); + return nullptr; + } default: UNREACHABLE(); } return nullptr; } + } else { + on_unmapped(); + return nullptr; } } @@ -729,172 +732,38 @@ struct Memory::Impl { GetInteger(vaddr), []() {}, []() {}); } - /** - * Reads a particular data type out of memory at the given virtual address. - * - * @param vaddr The virtual address to read the data type from. - * - * @tparam T The data type to read out of memory. This type *must* be - * trivially copyable, otherwise the behavior of this function - * is undefined. - * - * @returns The instance of T read from the specified virtual address. - */ + /// @brief Reads a particular data type out of memory at the given virtual address. + /// @param vaddr The virtual address to read the data type from. + /// @tparam T The data type to read out of memory. + /// @returns The instance of T read from the specified virtual address. template - T Read(Common::ProcessAddress vaddr) { - // Fast path for aligned reads of common sizes + inline T Read(Common::ProcessAddress vaddr) requires(std::is_trivially_copyable_v) noexcept { const u64 addr = GetInteger(vaddr); - if constexpr (std::is_same_v || std::is_same_v) { - // 8-bit reads are always aligned - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read8 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*ptr); - } - return 0; - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 16-bit reads - if ((addr & 1) == 0) { - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read16 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*reinterpret_cast(ptr)); - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 32-bit reads - if ((addr & 3) == 0) { - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read32 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*reinterpret_cast(ptr)); - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 64-bit reads - if ((addr & 7) == 0) { - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read64 @ 0x{:016X}", addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { - return static_cast(*reinterpret_cast(ptr)); - } - } - } - - // Fall back to the general case for other types or unaligned access - T result = 0; - const u8* const ptr = GetPointerImpl( - addr, - [addr]() { - LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr); - }, - [&]() { HandleRasterizerDownload(addr, sizeof(T)); }); - if (ptr) { + if (auto const ptr = GetPointerImpl(addr, [addr]() { + LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr); + }, [&]() { + HandleRasterizerDownload(addr, sizeof(T)); + }); ptr) [[likely]] { + // It may be tempting to rewrite this particular section to use "reinterpret_cast"; + // afterall, it's trivially copyable so surely it can be copied ov- Alignment. + // Remember, alignment. memcpy() will deal with all the alignment extremely fast. + T result{}; std::memcpy(&result, ptr, sizeof(T)); + return result; } - return result; + return T{}; } - /** - * Writes a particular data type to memory at the given virtual address. - * - * @param vaddr The virtual address to write the data type to. - * - * @tparam T The data type to write to memory. This type *must* be - * trivially copyable, otherwise the behavior of this function - * is undefined. - */ + /// @brief Writes a particular data type to memory at the given virtual address. + /// @param vaddr The virtual address to write the data type to. + /// @tparam T The data type to write to memory. template - void Write(Common::ProcessAddress vaddr, const T data) { - // Fast path for aligned writes of common sizes + inline void Write(Common::ProcessAddress vaddr, const T data) requires(std::is_trivially_copyable_v) noexcept { const u64 addr = GetInteger(vaddr); - if constexpr (std::is_same_v || std::is_same_v) { - // 8-bit writes are always aligned - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write8 @ 0x{:016X} = 0x{:02X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *ptr = static_cast(data); - } - return; - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 16-bit writes - if ((addr & 1) == 0) { - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write16 @ 0x{:016X} = 0x{:04X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *reinterpret_cast(ptr) = static_cast(data); - return; - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 32-bit writes - if ((addr & 3) == 0) { - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write32 @ 0x{:016X} = 0x{:08X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *reinterpret_cast(ptr) = static_cast(data); - return; - } - } - } else if constexpr (std::is_same_v || std::is_same_v) { - // Check alignment for 64-bit writes - if ((addr & 7) == 0) { - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write64 @ 0x{:016X} = 0x{:016X}", addr, - static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { - *reinterpret_cast(ptr) = static_cast(data); - return; - } - } - } - - // Fall back to the general case for other types or unaligned access - u8* const ptr = GetPointerImpl( - addr, - [addr, data]() { - LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, - addr, static_cast(data)); - }, - [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); - if (ptr) { + if (auto const ptr = GetPointerImpl(addr, [addr, data]() { + LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, addr, u64(data)); + }, [&]() { HandleRasterizerWrite(addr, sizeof(T)); }); ptr) [[likely]] std::memcpy(ptr, &data, sizeof(T)); - } } template From ec56567886dc686d468ec931e528c689fb30973c Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 2 Oct 2025 05:19:45 +0000 Subject: [PATCH 7/7] fix Signed-off-by: lizzie --- src/core/memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f84507d125..e1aa3473fc 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -737,7 +737,7 @@ struct Memory::Impl { /// @tparam T The data type to read out of memory. /// @returns The instance of T read from the specified virtual address. template - inline T Read(Common::ProcessAddress vaddr) requires(std::is_trivially_copyable_v) noexcept { + inline T Read(Common::ProcessAddress vaddr) noexcept requires(std::is_trivially_copyable_v) { const u64 addr = GetInteger(vaddr); if (auto const ptr = GetPointerImpl(addr, [addr]() { LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, addr); @@ -758,7 +758,7 @@ struct Memory::Impl { /// @param vaddr The virtual address to write the data type to. /// @tparam T The data type to write to memory. template - inline void Write(Common::ProcessAddress vaddr, const T data) requires(std::is_trivially_copyable_v) noexcept { + inline void Write(Common::ProcessAddress vaddr, const T data) noexcept requires(std::is_trivially_copyable_v) { const u64 addr = GetInteger(vaddr); if (auto const ptr = GetPointerImpl(addr, [addr, data]() { LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8, addr, u64(data));