From 707e8afb2917c7652dbe006961ba8ae735cd79d8 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 9 May 2026 05:26:17 +0200 Subject: [PATCH 1/5] [common] remove logging thread, simply write in place like a maniac (#3928) theoretically, it's better because distributes load of logging across various threads this should work because 99% of I/O solutions are blocking by default EXCEPT, maybe android differs? please check logcat didn't get affected (again) by me underestimating android ~~stupidity~~ brillaince Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3928 Reviewed-by: CamilleLaVey --- src/common/logging.cpp | 58 ++++++++++++------------------------------ 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/src/common/logging.cpp b/src/common/logging.cpp index 0b82d3e727..68e033b129 100644 --- a/src/common/logging.cpp +++ b/src/common/logging.cpp @@ -349,35 +349,8 @@ struct Impl { // Well, I mean it's the default constructor! explicit Impl() noexcept : filter(Level::Trace) {} - void StartBackendThread() noexcept { - backend_thread = std::jthread([this](std::stop_token stop_token) { - Common::SetCurrentThreadName("Logger"); - Entry entry; - const auto write_logs = [this, &entry]() { - ForEachBackend([&entry](Backend& backend) { - backend.Write(entry); - }); - }; - do { - message_queue.PopWait(entry, stop_token); - write_logs(); - } while (!stop_token.stop_requested()); - // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a - // case where a system is repeatedly spamming logs even on close. - int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; - while (max_logs_to_write-- && message_queue.TryPop(entry)) - write_logs(); - }); - } - - void StopBackendThread() noexcept { - backend_thread.request_stop(); - if (backend_thread.joinable()) - backend_thread.join(); - ForEachBackend([](Backend& backend) { backend.Flush(); }); - } - - void ForEachBackend(auto lambda) noexcept { + template + void ForEachBackend(F&& lambda) noexcept { lambda(static_cast(color_console_backend)); #ifndef __OPENORBIS__ if (file_backend) @@ -402,9 +375,7 @@ struct Impl { #ifdef ANDROID LogcatBackend lc_backend{}; #endif - MPSCQueue message_queue{}; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; - std::jthread backend_thread; }; } // namespace @@ -428,13 +399,11 @@ void Initialize() { } void Start() { - if (logging_instance) - logging_instance->StartBackendThread(); } void Stop() { if (logging_instance) - logging_instance->StopBackendThread(); + logging_instance->ForEachBackend([](Backend& backend) { backend.Flush(); }); } void SetGlobalFilter(const Filter& filter) { @@ -449,14 +418,19 @@ void SetColorConsoleBackendEnabled(bool enabled) { void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) { if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) { - logging_instance->message_queue.EmplaceWait(Entry{ - .message = fmt::vformat(format, args), - .timestamp = std::chrono::duration_cast(std::chrono::steady_clock::now() - logging_instance->time_origin), - .log_class = log_class, - .log_level = log_level, - .filename = TrimSourcePath(filename), - .function = function, - .line_num = line_num, + auto const flush = ::Settings::values.log_flush_line.GetValue(); + logging_instance->ForEachBackend([=](Backend& backend) { + backend.Write(Entry{ + .message = fmt::vformat(format, args), + .timestamp = std::chrono::duration_cast(std::chrono::steady_clock::now() - logging_instance->time_origin), + .log_class = log_class, + .log_level = log_level, + .filename = TrimSourcePath(filename), + .function = function, + .line_num = line_num, + }); + if (flush) + backend.Flush(); }); } } From f87b1dafc83d50b7e363409444a9a2a8fdff76cb Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 9 May 2026 05:27:33 +0200 Subject: [PATCH 2/5] [file_sys/sytem_archive] add missing identifiers for +8.0 (#3867) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3867 Reviewed-by: crueter Reviewed-by: MaranBr --- .../system_archive/system_archive.cpp | 138 ++++++++---------- 1 file changed, 64 insertions(+), 74 deletions(-) diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index cb68d5b55e..bbdf27039b 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -15,86 +15,76 @@ namespace FileSys::SystemArchive { -constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800; -constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28; - -using SystemArchiveSupplier = VirtualDir (*)(); - struct SystemArchiveDescriptor { - u64 title_id; const char* name; - SystemArchiveSupplier supplier; + VirtualDir (*supplier)(); }; -constexpr std::array SYSTEM_ARCHIVES{{ - {0x0100000000000800, "CertStore", nullptr}, - {0x0100000000000801, "ErrorMessage", nullptr}, - {0x0100000000000802, "MiiModel", &MiiModel}, - {0x0100000000000803, "BrowserDll", nullptr}, - {0x0100000000000804, "Help", nullptr}, - {0x0100000000000805, "SharedFont", nullptr}, - {0x0100000000000806, "NgWord", &NgWord1}, - {0x0100000000000807, "SsidList", nullptr}, - {0x0100000000000808, "Dictionary", nullptr}, - {0x0100000000000809, "SystemVersion", &SystemVersion}, - {0x010000000000080A, "AvatarImage", nullptr}, - {0x010000000000080B, "LocalNews", nullptr}, - {0x010000000000080C, "Eula", nullptr}, - {0x010000000000080D, "UrlBlackList", nullptr}, - {0x010000000000080E, "TimeZoneBinary", &TimeZoneBinary}, - {0x010000000000080F, "CertStoreCruiser", nullptr}, - {0x0100000000000810, "FontNintendoExtension", &FontNintendoExtension}, - {0x0100000000000811, "FontStandard", &FontStandard}, - {0x0100000000000812, "FontKorean", &FontKorean}, - {0x0100000000000813, "FontChineseTraditional", &FontChineseTraditional}, - {0x0100000000000814, "FontChineseSimple", &FontChineseSimple}, - {0x0100000000000815, "FontBfcpx", nullptr}, - {0x0100000000000816, "SystemUpdate", nullptr}, - {0x0100000000000817, "0100000000000817", nullptr}, - {0x0100000000000818, "FirmwareDebugSettings", nullptr}, - {0x0100000000000819, "BootImagePackage", nullptr}, - {0x010000000000081A, "BootImagePackageSafe", nullptr}, - {0x010000000000081B, "BootImagePackageExFat", nullptr}, - {0x010000000000081C, "BootImagePackageExFatSafe", nullptr}, - {0x010000000000081D, "FatalMessage", nullptr}, - {0x010000000000081E, "ControllerIcon", nullptr}, - {0x010000000000081F, "PlatformConfigIcosa", nullptr}, - {0x0100000000000820, "PlatformConfigCopper", nullptr}, - {0x0100000000000821, "PlatformConfigHoag", nullptr}, - {0x0100000000000822, "ControllerFirmware", nullptr}, - {0x0100000000000823, "NgWord2", &NgWord2}, - {0x0100000000000824, "PlatformConfigIcosaMariko", nullptr}, - {0x0100000000000825, "ApplicationBlackList", nullptr}, - {0x0100000000000826, "RebootlessSystemUpdateVersion", nullptr}, - {0x0100000000000827, "ContentActionTable", nullptr}, -}}; +constexpr inline SystemArchiveDescriptor GetSystemArchive(u64 title_id) { + switch (title_id) { + case 0x0100000000000800: return {"CertStore", nullptr}; + case 0x0100000000000801: return {"ErrorMessage", nullptr}; + case 0x0100000000000802: return {"MiiModel", &MiiModel}; + case 0x0100000000000803: return {"BrowserDll", nullptr}; + case 0x0100000000000804: return {"Help", nullptr}; + case 0x0100000000000805: return {"SharedFont", nullptr}; + case 0x0100000000000806: return {"NgWord", &NgWord1}; + case 0x0100000000000807: return {"SsidList", nullptr}; + case 0x0100000000000808: return {"Dictionary", nullptr}; + case 0x0100000000000809: return {"SystemVersion", &SystemVersion}; + case 0x010000000000080A: return {"AvatarImage", nullptr}; + case 0x010000000000080B: return {"LocalNews", nullptr}; + case 0x010000000000080C: return {"Eula", nullptr}; + case 0x010000000000080D: return {"UrlBlackList", nullptr}; + case 0x010000000000080E: return {"TimeZoneBinary", &TimeZoneBinary}; + case 0x010000000000080F: return {"CertStoreCruiser", nullptr}; + case 0x0100000000000810: return {"FontNintendoExtension", &FontNintendoExtension}; + case 0x0100000000000811: return {"FontStandard", &FontStandard}; + case 0x0100000000000812: return {"FontKorean", &FontKorean}; + case 0x0100000000000813: return {"FontChineseTraditional", &FontChineseTraditional}; + case 0x0100000000000814: return {"FontChineseSimple", &FontChineseSimple}; + case 0x0100000000000815: return {"FontBfcpx", nullptr}; + case 0x0100000000000816: return {"SystemUpdate", nullptr}; + case 0x0100000000000817: return {"0100000000000817", nullptr}; + case 0x0100000000000818: return {"FirmwareDebugSettings", nullptr}; + case 0x0100000000000819: return {"BootImagePackage", nullptr}; + case 0x010000000000081A: return {"BootImagePackageSafe", nullptr}; + case 0x010000000000081B: return {"BootImagePackageExFat", nullptr}; + case 0x010000000000081C: return {"BootImagePackageExFatSafe", nullptr}; + case 0x010000000000081D: return {"FatalMessage", nullptr}; + case 0x010000000000081E: return {"ControllerIcon", nullptr}; + case 0x010000000000081F: return {"PlatformConfigIcosa", nullptr}; + case 0x0100000000000820: return {"PlatformConfigCopper", nullptr}; + case 0x0100000000000821: return {"PlatformConfigHoag", nullptr}; + case 0x0100000000000822: return {"ControllerFirmware", nullptr}; + case 0x0100000000000823: return {"NgWord2", &NgWord2}; + case 0x0100000000000824: return {"PlatformConfigIcosaMariko", nullptr}; + case 0x0100000000000825: return {"ApplicationBlackList", nullptr}; + case 0x0100000000000826: return {"RebootlessSystemUpdateVersion", nullptr}; + case 0x0100000000000827: return {"ContentActionTable", nullptr}; + case 0x0100000000000828: return {"FunctionBlackList", nullptr}; + case 0x0100000000000829: return {"PlatformConfigCalcio", nullptr}; + case 0x0100000000000830: return {"NgWordT", nullptr}; + case 0x0100000000000831: return {"PlatformConfigAula", nullptr}; + case 0x0100000000000832: return {"CradleFirmware", nullptr}; + case 0x0100000000000835: return {"ErrorMessageUtf8", nullptr}; + case 0x0100000000000859: return {"ClientCertData", nullptr}; + case 0x010000000000085C: return {"GameCardConfigurationData", nullptr}; + default: return {nullptr, nullptr}; + } +} VirtualFile SynthesizeSystemArchive(const u64 title_id) { - if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) { - return nullptr; + auto const desc = GetSystemArchive(title_id); + LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, title_id); + if (desc.supplier != nullptr) { + if (auto const dir = desc.supplier(); dir != nullptr) { + if (auto const romfs = CreateRomFS(dir); romfs != nullptr) { + LOG_INFO(Service_FS, " - System archive generation successful!"); + return romfs; + } + } } - - const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; - - LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); - - if (desc.supplier == nullptr) { - return nullptr; - } - - const auto dir = desc.supplier(); - - if (dir == nullptr) { - return nullptr; - } - - const auto romfs = CreateRomFS(dir); - - if (romfs == nullptr) { - return nullptr; - } - - LOG_INFO(Service_FS, " - System archive generation successful!"); - return romfs; + return nullptr; } } // namespace FileSys::SystemArchive From a6423a88cc068f8213db8707900a1d1b38b9566d Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 9 May 2026 05:28:22 +0200 Subject: [PATCH 3/5] [file_sys] resize SD card size in 4GiB chunks (#3921) some homebrew theoretically would freak out when 1TB is reached... so let's just magically resize the SD card :) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3921 Reviewed-by: CamilleLaVey --- src/core/file_sys/sdmc_factory.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index f3e2e21f4c..a2903dbacf 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -1,7 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/alignment.h" +#include "common/literals.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/sdmc_factory.h" #include "core/file_sys/vfs/vfs.h" @@ -9,8 +14,6 @@ namespace FileSys { -constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB - SDMCFactory::SDMCFactory(VirtualDir sd_dir_, VirtualDir sd_mod_dir_) : sd_dir(std::move(sd_dir_)), sd_mod_dir(std::move(sd_mod_dir_)), contents(std::make_unique( @@ -56,7 +59,11 @@ u64 SDMCFactory::GetSDMCFreeSpace() const { } u64 SDMCFactory::GetSDMCTotalSpace() const { - return SDMC_TOTAL_SIZE; + // Resize the SD space automatically, always leaving around 4GiB last from next chunk block + using namespace Common::Literals; + auto const bytes_per_sector = 512; + auto const size_block = (sd_dir->GetSize() + 4_GiB) / 4_GiB; + return Common::AlignUp(size_block * 4_GiB, bytes_per_sector); } } // namespace FileSys From eec460ec2ef13dee7376f2854ba00da9a98b0cd5 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 9 May 2026 05:41:41 +0200 Subject: [PATCH 4/5] [dynarmic] remove decode matcher function handlers using std::function<>, use raw function pointers (#3920) issues: - std::function<> is used, which is famously bad - storage of tehse in tables makes big fucking tables for no good reason - lets just store a normal pointer and stuff! :) this pr attempts to address that Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3920 Reviewed-by: CamilleLaVey --- src/core/arm/nce/interpreter_visitor.cpp | 7 +- .../src/dynarmic/frontend/A32/decoder/arm.h | 65 ++++++------- .../src/dynarmic/frontend/A32/decoder/asimd.h | 17 ++-- .../dynarmic/frontend/A32/decoder/thumb16.h | 17 ++-- .../dynarmic/frontend/A32/decoder/thumb32.h | 17 ++-- .../src/dynarmic/frontend/A32/decoder/vfp.h | 34 ++----- .../frontend/A32/translate/translate_arm.cpp | 24 ++--- .../A32/translate/translate_thumb.cpp | 40 ++++---- .../src/dynarmic/frontend/A64/decoder/a64.h | 64 ++++++------- .../frontend/A64/translate/a64_translate.cpp | 15 +-- .../frontend/decoder/decoder_detail.h | 93 ++++++------------- .../src/dynarmic/frontend/decoder/matcher.h | 51 +++++----- src/dynarmic/src/dynarmic/frontend/imm.h | 5 +- src/dynarmic/tests/decoder_tests.cpp | 2 +- src/dynarmic/tests/print_info.cpp | 12 +-- 15 files changed, 187 insertions(+), 276 deletions(-) diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp index 077a696cc8..ec2b155fe4 100644 --- a/src/core/arm/nce/interpreter_visitor.cpp +++ b/src/core/arm/nce/interpreter_visitor.cpp @@ -761,8 +761,7 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3 return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt); } -std::optional MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, - fpsimd_context* fpsimd_context) { +std::optional MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, fpsimd_context* fpsimd_context) { std::span regs(reinterpret_cast(context->regs), 31); std::span vregs(reinterpret_cast(fpsimd_context->vregs), 32); u64& sp = *reinterpret_cast(&context->sp); @@ -772,9 +771,9 @@ std::optional MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, m u32 instruction = memory.Read32(pc); bool was_executed = false; - auto decoder = Dynarmic::A64::Decode(instruction); + auto decoder = Dynarmic::A64::Decode(visitor, instruction); if (decoder) { - was_executed = decoder->get().call(visitor, instruction); + was_executed = *decoder; } else { was_executed = false; } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h index d1cd9a923d..a9309ef5a8 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/arm.h @@ -26,48 +26,41 @@ namespace Dynarmic::A32 { template using ArmMatcher = Decoder::Matcher; -template -using ArmDecodeTable = std::array>, 0x1000>; - -namespace detail { -inline size_t ToFastLookupIndexArm(u32 instruction) noexcept { - return ((instruction >> 4) & 0x00F) | ((instruction >> 16) & 0xFF0); -} -} // namespace detail - -template -static ArmDecodeTable GetArmDecodeTable() noexcept { - ArmDecodeTable table{}; - for (size_t i = 0; i < table.size(); ++i) { - // PLEASE HEAP ELLIDE - for (auto const& e : std::vector>{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template +static std::optional DecodeArm(V& visitor, u32 instruction) noexcept { + auto const make_fast_index = [](u32 a) { + return ((a >> 4) & 0x00F) | ((a >> 16) & 0xFF0); + }; + struct Handler { + bool (*fn)(V&, u32); + u32 mask; + u32 expect; + }; + alignas(64) static const std::array, 0x1000> table = [&] { + std::array, 0x1000> t{}; + for (size_t i = 0; i < t.size(); ++i) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((i & make_fast_index(mask)) == make_fast_index(expect)) { \ + t[i].emplace_back([](V& visitor, u32 instruction) -> bool { \ + return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + }, mask, expect); \ + } \ + } while (0); #include "./arm.inc" #undef INST - }) { - auto const expect = detail::ToFastLookupIndexArm(e.GetExpected()); - auto const mask = detail::ToFastLookupIndexArm(e.GetMask()); - if ((i & mask) == expect) { - table[i].push_back(e); - } } - } - return table; + return t; + }(); + for (auto const& e : table[make_fast_index(instruction)]) + if ((instruction & e.mask) == e.expect) + return e.fn(visitor, instruction); + return std::nullopt; } template -static std::optional>> DecodeArm(u32 instruction) noexcept { - alignas(64) static const auto table = GetArmDecodeTable(); - const auto matches_instruction = [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }; - const auto& subtable = table[detail::ToFastLookupIndexArm(instruction)]; - auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction); - return iter != subtable.end() ? std::optional>>(*iter) : std::nullopt; -} - -template -static std::optional GetNameARM(u32 inst) noexcept { +static std::optional GetNameArm(u32 inst) noexcept { std::vector>> list = { #define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) }, #include "./arm.inc" diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h index 5b0336e59f..4feae6f478 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/asimd.h @@ -26,17 +26,16 @@ namespace Dynarmic::A32 { template using ASIMDMatcher = Decoder::Matcher; -template -static std::optional>> DecodeASIMD(u32 instruction) noexcept { - alignas(64) static const auto table = std::array{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template +static std::optional DecodeASIMD(V& visitor, u32 instruction) noexcept { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + } while (0); #include "./asimd.inc" #undef INST - }; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional>>(*iter) : std::nullopt; + return std::nullopt; } template diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h index 590878a6de..465159a00d 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb16.h @@ -23,17 +23,16 @@ namespace Dynarmic::A32 { template using Thumb16Matcher = Decoder::Matcher; -template -static std::optional>> DecodeThumb16(u16 instruction) { - alignas(64) static const auto table = std::array{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)), +template +static std::optional DecodeThumb16(V& visitor, u16 instruction) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)); \ + if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)); \ + } while (0); #include "./thumb16.inc" #undef INST - }; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional>>(*iter) : std::nullopt; + return std::nullopt; } template diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h index 6a09c66afb..b3588c7a1d 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/thumb32.h @@ -22,17 +22,16 @@ namespace Dynarmic::A32 { template using Thumb32Matcher = Decoder::Matcher; -template -static std::optional>> DecodeThumb32(u32 instruction) { - alignas(64) static const auto table = std::array{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template +static std::optional DecodeThumb32(V& visitor, u32 instruction) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + } while (0); #include "./thumb32.inc" #undef INST - }; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional>>(*iter) : std::nullopt; + return std::nullopt; } template diff --git a/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h b/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h index e02cc03a6b..8703aa24b4 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h +++ b/src/dynarmic/src/dynarmic/frontend/A32/decoder/vfp.h @@ -23,32 +23,18 @@ namespace Dynarmic::A32 { template using VFPMatcher = Decoder::Matcher; -template -static std::optional>> DecodeVFP(u32 instruction) { - using Table = std::vector>; - alignas(64) static const struct Tables { - Table unconditional; - Table conditional; - } tables = []() { - Table list = { -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template +static std::optional DecodeVFP(V& visitor, u32 instruction) { + bool const i_uncond = (instruction & 0xF0000000) == 0xF0000000; +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + bool const m_uncond = (mask & 0xF0000000) == 0xF0000000; \ + if ((instruction & mask) == expect && m_uncond == i_uncond) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + } while (0); #include "./vfp.inc" #undef INST - }; - auto const it = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) { - return (matcher.GetMask() & 0xF0000000) == 0xF0000000; - }); - return Tables{ - Table{list.begin(), it}, - Table{it, list.end()}, - }; - }(); - const bool is_unconditional = (instruction & 0xF0000000) == 0xF0000000; - const Table& table = is_unconditional ? tables.unconditional : tables.conditional; - auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != table.end() ? std::optional>>(*iter) : std::nullopt; + return std::nullopt; } template diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp index 3fd475d074..6c89a4af36 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_arm.cpp @@ -41,12 +41,12 @@ void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCall tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir); ticks_for_instruction = tcb->GetTicksForCode(false, arm_pc, *arm_instruction); - if (const auto vfp_decoder = DecodeVFP(*arm_instruction)) { - should_continue = vfp_decoder->get().call(visitor, *arm_instruction); - } else if (const auto asimd_decoder = DecodeASIMD(*arm_instruction)) { - should_continue = asimd_decoder->get().call(visitor, *arm_instruction); - } else if (const auto decoder = DecodeArm(*arm_instruction)) { - should_continue = decoder->get().call(visitor, *arm_instruction); + if (const auto vfp_decoder = DecodeVFP(visitor, *arm_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD(visitor, *arm_instruction)) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeArm(visitor, *arm_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.arm_UDF(); } @@ -88,12 +88,12 @@ bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descript const u64 ticks_for_instruction = 1; - if (const auto vfp_decoder = DecodeVFP(arm_instruction)) { - should_continue = vfp_decoder->get().call(visitor, arm_instruction); - } else if (const auto asimd_decoder = DecodeASIMD(arm_instruction)) { - should_continue = asimd_decoder->get().call(visitor, arm_instruction); - } else if (const auto decoder = DecodeArm(arm_instruction)) { - should_continue = decoder->get().call(visitor, arm_instruction); + if (const auto vfp_decoder = DecodeVFP(visitor, arm_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD(visitor, arm_instruction)) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeArm(visitor, arm_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.arm_UDF(); } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp index 332860a95c..2a2fbea63f 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/translate_thumb.cpp @@ -126,24 +126,24 @@ void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCa if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) { if (is_thumb_16) { - if (const auto decoder = DecodeThumb16(static_cast(thumb_instruction))) { - should_continue = decoder->get().call(visitor, static_cast(thumb_instruction)); + if (const auto decoder = DecodeThumb16(visitor, u16(thumb_instruction))) { + should_continue = *decoder; } else { should_continue = visitor.thumb16_UDF(); } } else { if (MaybeVFPOrASIMDInstruction(thumb_instruction)) { - if (const auto vfp_decoder = DecodeVFP(thumb_instruction)) { - should_continue = vfp_decoder->get().call(visitor, thumb_instruction); - } else if (const auto asimd_decoder = DecodeASIMD(ConvertASIMDInstruction(thumb_instruction))) { - should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction)); - } else if (const auto decoder = DecodeThumb32(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + if (const auto vfp_decoder = DecodeVFP(visitor, thumb_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD(visitor, ConvertASIMDInstruction(thumb_instruction))) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeThumb32(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } - } else if (const auto decoder = DecodeThumb32(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + } else if (const auto decoder = DecodeThumb32(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } @@ -187,25 +187,25 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri const u64 ticks_for_instruction = 1; if (is_thumb_16) { - if (const auto decoder = DecodeThumb16(static_cast(thumb_instruction))) { - should_continue = decoder->get().call(visitor, static_cast(thumb_instruction)); + if (const auto decoder = DecodeThumb16(visitor, u16(thumb_instruction))) { + should_continue = *decoder; } else { should_continue = visitor.thumb16_UDF(); } } else { thumb_instruction = mcl::bit::swap_halves_32(thumb_instruction); if (MaybeVFPOrASIMDInstruction(thumb_instruction)) { - if (const auto vfp_decoder = DecodeVFP(thumb_instruction)) { - should_continue = vfp_decoder->get().call(visitor, thumb_instruction); - } else if (const auto asimd_decoder = DecodeASIMD(ConvertASIMDInstruction(thumb_instruction))) { - should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction)); - } else if (const auto decoder = DecodeThumb32(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + if (const auto vfp_decoder = DecodeVFP(visitor, thumb_instruction)) { + should_continue = *vfp_decoder; + } else if (const auto asimd_decoder = DecodeASIMD(visitor, ConvertASIMDInstruction(thumb_instruction))) { + should_continue = *asimd_decoder; + } else if (const auto decoder = DecodeThumb32(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } - } else if (const auto decoder = DecodeThumb32(thumb_instruction)) { - should_continue = decoder->get().call(visitor, thumb_instruction); + } else if (const auto decoder = DecodeThumb32(visitor, thumb_instruction)) { + should_continue = *decoder; } else { should_continue = visitor.thumb32_UDF(); } diff --git a/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h b/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h index ccf890cbcd..f0b7baba7b 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h +++ b/src/dynarmic/src/dynarmic/frontend/A64/decoder/a64.h @@ -26,45 +26,37 @@ namespace Dynarmic::A64 { template using Matcher = Decoder::Matcher; -template -using DecodeTable = std::array>, 0x1000>; - -namespace detail { -inline size_t ToFastLookupIndex(u32 instruction) { - return ((instruction >> 10) & 0x00F) | ((instruction >> 18) & 0xFF0); -} -} // namespace detail - -template -inline DecodeTable GetDecodeTable() { - DecodeTable table{}; - for (size_t i = 0; i < table.size(); ++i) { - // PLEASE HEAP ELLIDE - for (auto const& e : std::vector>{ -#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)), +template +static std::optional Decode(V& visitor, u32 instruction) noexcept { + auto const make_fast_index = [](u32 a) { + return ((a >> 10) & 0x00F) | ((a >> 18) & 0xFF0); + }; + struct Handler { + bool (*fn)(V&, u32); + u32 mask; + u32 expect; + }; + alignas(64) static const std::array, 0x1000> table = [&] { + std::array, 0x1000> t{}; + for (size_t i = 0; i < t.size(); ++i) { +#define INST(fn, name, bitstring) \ + do { \ + auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + if ((i & make_fast_index(mask)) == make_fast_index(expect)) { \ + t[i].emplace_back([](V& visitor, u32 instruction) -> bool { \ + return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \ + }, mask, expect); \ + } \ + } while (0); #include "./a64.inc" #undef INST - }) { - const auto expect = detail::ToFastLookupIndex(e.GetExpected()); - const auto mask = detail::ToFastLookupIndex(e.GetMask()); - if ((i & mask) == expect) - table[i].push_back(e); } - } - return table; -} - -/// In practice it must always suceed, otherwise something else unrelated would have gone awry -template -inline std::optional>> Decode(u32 instruction) { - alignas(64) static const auto table = GetDecodeTable(); - const auto& subtable = table[detail::ToFastLookupIndex(instruction)]; - auto iter = std::find_if(subtable.begin(), subtable.end(), [instruction](const auto& matcher) { - return matcher.Matches(instruction); - }); - return iter != subtable.end() - ? std::optional{ std::reference_wrapper>(*iter) } - : std::nullopt; + return t; + }(); + for (auto const& e : table[make_fast_index(instruction)]) + if ((instruction & e.mask) == e.expect) + return e.fn(visitor, instruction); + return std::nullopt; } template diff --git a/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp b/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp index 4afce6bd29..7840c63d79 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A64/translate/a64_translate.cpp @@ -24,9 +24,8 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu do { const u64 pc = visitor.ir.current_location->PC(); if (const auto instruction = memory_read_code(pc)) { - auto decoder = Decode(*instruction); - if (decoder) { - should_continue = decoder->get().call(visitor, *instruction); + if (auto decoder = Decode(visitor, *instruction)) { + should_continue = *decoder; } else { should_continue = visitor.RaiseException(Exception::UnallocatedEncoding); } @@ -47,13 +46,9 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction) { TranslatorVisitor visitor{block, descriptor, {}}; - bool should_continue = true; - auto const decoder = Decode(instruction); - if (decoder) { - should_continue = decoder->get().call(visitor, instruction); - } else { - should_continue = false; - } + bool should_continue = false; + if (auto const decoder = Decode(visitor, instruction)) + should_continue = *decoder; visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4); block.CycleCount()++; diff --git a/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h b/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h index c22e585c26..e383fb0262 100644 --- a/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h +++ b/src/dynarmic/src/dynarmic/frontend/decoder/decoder_detail.h @@ -8,31 +8,25 @@ #pragma once -#include #include #include - +#include #include "common/assert.h" #include "dynarmic/mcl/bit.hpp" -#include "dynarmic/mcl/function_info.hpp" namespace Dynarmic::Decoder { namespace detail { template -inline consteval std::array StringToArray(const char (&str)[N + 1]) { - std::array result{}; - for (size_t i = 0; i < N; i++) { - result[i] = str[i]; - } - return result; +inline consteval std::array StringToArray(const char (&s)[N + 1]) { + std::array r{}; + for (size_t i = 0; i < N; i++) + r[i] = s[i]; + return r; } -/** - * Helper functions for the decoders. - * - * @tparam MatcherT The type of the Matcher to use. - */ +/// @brief Helper functions for the decoders. +/// @tparam MatcherT The type of the Matcher to use. template struct detail { using opcode_type = typename MatcherT::opcode_type; @@ -40,17 +34,15 @@ struct detail { static constexpr size_t opcode_bitsize = mcl::bitsizeof; - /** - * Generates the mask and the expected value after masking from a given bitstring. - * A '0' in a bitstring indicates that a zero must be present at that bit position. - * A '1' in a bitstring indicates that a one must be present at that bit position. - */ + /// @brief Generates the mask and the expected value after masking from a given bitstring. + /// A '0' in a bitstring indicates that a zero must be present at that bit position. + /// A '1' in a bitstring indicates that a one must be present at that bit position. #ifdef __clang__ static constexpr auto GetMaskAndExpect(std::array bitstring) { #else static consteval auto GetMaskAndExpect(std::array bitstring) { #endif - const auto one = static_cast(1); + const auto one = opcode_type(1); opcode_type mask = 0, expect = 0; for (size_t i = 0; i < opcode_bitsize; i++) { const size_t bit_position = opcode_bitsize - i - 1; @@ -101,7 +93,7 @@ struct detail { } #if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__) // Avoids a MSVC ICE, and avoids Android NDK issue. - ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; })); + DEBUG_ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; })); #endif return std::make_tuple(masks, shifts); } @@ -109,65 +101,32 @@ struct detail { /// @brief This struct's Make member function generates a lambda which decodes an instruction /// based on the provided arg_masks and arg_shifts. The Visitor member function to call is /// provided as a template argument. - template - struct VisitorCaller; - #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4800) // forcing value to bool 'true' or 'false' (performance warning) #endif - template - struct VisitorCaller { - template - static constexpr auto Make(std::integer_sequence, - ReturnType (V::*const fn)(Args...), - const std::array arg_masks, - const std::array arg_shifts) { - static_assert(std::is_same_v, "Member function is not from Matcher's Visitor"); - return [fn, arg_masks, arg_shifts](V& v, opcode_type instruction) { - (void)instruction; - (void)arg_masks; - (void)arg_shifts; - return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...); - }; - } - }; - - template - struct VisitorCaller { - template - static constexpr auto Make(std::integer_sequence, - ReturnType (V::*const fn)(Args...) const, - const std::array arg_masks, - const std::array arg_shifts) { - static_assert(std::is_same_v, "Member function is not from Matcher's Visitor"); - return [fn, arg_masks, arg_shifts](const V& v, opcode_type instruction) { - (void)instruction; - (void)arg_masks; - (void)arg_shifts; - return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...); - }; + template + struct VisitorCaller { + template + static inline constexpr auto Invoke(std::index_sequence, V& visitor, [[maybe_unused]] opcode_type instruction, ReturnType (V::*const fn)(Args...), [[maybe_unused]] const std::array arg_masks, [[maybe_unused]] const std::array arg_shifts) { + return (visitor.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...); } }; #ifdef _MSC_VER # pragma warning(pop) #endif - /// @brief Creates a matcher that can match and parse instructions based on bitstring. - /// See also: GetMaskAndExpect and GetArgInfo for format of bitstring. - template - static constexpr auto GetMatcher(F fn) { - constexpr size_t args_count = mcl::parameter_count_v; - constexpr auto mask = std::get<0>(GetMaskAndExpect(bitstring)); - constexpr auto expect = std::get<1>(GetMaskAndExpect(bitstring)); - constexpr auto arg_masks = std::get<0>(GetArgInfo(bitstring)); - constexpr auto arg_shifts = std::get<1>(GetArgInfo(bitstring)); - const auto proxy_fn = VisitorCaller::Make(std::make_index_sequence(), fn, arg_masks, arg_shifts); - return MatcherT(mask, expect, proxy_fn); + template + static inline constexpr auto GetMatcherFunction(V& visitor, [[maybe_unused]] opcode_type instruction, ReturnType (V::*const fn)(Args...)) { + constexpr auto arg_masks = std::get<0>(GetArgInfo(bitstring)); + constexpr auto arg_shifts = std::get<1>(GetArgInfo(bitstring)); + return VisitorCaller::Invoke(std::index_sequence_for(), visitor, instruction, fn, arg_masks, arg_shifts); } }; -#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail>::template GetMatcher(&V::fn) +#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail>::GetMaskAndExpect(bitstring) + +#define DYNARMIC_DECODER_GET_MATCHER_FUNCTION(MatcherT, fn, name, bitstring) Decoder::detail::detail>::template GetMatcherFunction(visitor, instruction, &V::fn) } // namespace detail } // namespace Dynarmic::Decoder diff --git a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h index 194f646ed2..49095f67e3 100644 --- a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h +++ b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h @@ -15,50 +15,41 @@ namespace Dynarmic::Decoder { /// Generic instruction handling construct. -/// @tparam Visitor An arbitrary visitor type that will be passed through -/// to the function being handled. This type must be the -/// type of the first parameter in a handler function. -/// @tparam OpcodeType Type representing an opcode. This must be the -/// type of the second parameter in a handler function. -template +/// @tparam V An arbitrary visitor type that will be passed through +/// to the function being handled. This type must be the +/// type of the first parameter in a handler function. +/// @tparam T Type representing an opcode. This must be the +/// type of the second parameter in a handler function. +template class Matcher { public: - using opcode_type = OpcodeType; - using visitor_type = Visitor; - using handler_return_type = typename Visitor::instruction_return_type; - using handler_function = std::function; - Matcher(opcode_type mask, opcode_type expected, handler_function func) - : mask{mask}, expected{expected}, fn{std::move(func)} {} + using opcode_type = T; + using visitor_type = V; - /// Gets the mask for this instruction. - inline opcode_type GetMask() const noexcept { + constexpr Matcher(T mask, T expected) noexcept + : mask{mask} + , expected{expected} + {} + + /// @brief Gets the mask for this instruction. + constexpr inline T GetMask() const noexcept { return mask; } - /// Gets the expected value after masking for this instruction. - inline opcode_type GetExpected() const noexcept { + /// @brief Gets the expected value after masking for this instruction. + constexpr inline T GetExpected() const noexcept { return expected; } - /// Tests to see if the given instruction is the instruction this matcher represents. + /// @brief Tests to see if the given instruction is the instruction this matcher represents. /// @param instruction The instruction to test /// @returns true if the given instruction matches. - inline bool Matches(opcode_type instruction) const noexcept { + constexpr inline bool Matches(T instruction) const noexcept { return (instruction & mask) == expected; } - /// Calls the corresponding instruction handler on visitor for this type of instruction. - /// @param v The visitor to use - /// @param instruction The instruction to decode. - inline handler_return_type call(Visitor& v, opcode_type instruction) const noexcept { - ASSERT(Matches(instruction)); - return fn(v, instruction); - } - -private: - opcode_type mask; - opcode_type expected; - handler_function fn; + T mask; + T expected; }; } // namespace Dynarmic::Decoder diff --git a/src/dynarmic/src/dynarmic/frontend/imm.h b/src/dynarmic/src/dynarmic/frontend/imm.h index 56fbcbda0e..f305b5bfc8 100644 --- a/src/dynarmic/src/dynarmic/frontend/imm.h +++ b/src/dynarmic/src/dynarmic/frontend/imm.h @@ -27,9 +27,8 @@ class Imm { public: static constexpr size_t bit_size = bit_size_; - explicit Imm(u32 value) - : value(value) { - ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected"); + explicit Imm(u32 value) : value(value) { + DEBUG_ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected"); } template diff --git a/src/dynarmic/tests/decoder_tests.cpp b/src/dynarmic/tests/decoder_tests.cpp index 2a6c0f6a4f..17ed2c0ce5 100644 --- a/src/dynarmic/tests/decoder_tests.cpp +++ b/src/dynarmic/tests/decoder_tests.cpp @@ -51,7 +51,7 @@ TEST_CASE("ASIMD Decoder: Ensure table order correctness", "[decode][a32][.]") { const bool iserr = is_decode_error(*iter, instruction); const auto alternative = std::find_if(table.cbegin(), iter, [instruction](const auto& m) { - return m.Matches(instruction); + return (instruction & mask) == expect; }); const bool altiserr = is_decode_error(*alternative, instruction); diff --git a/src/dynarmic/tests/print_info.cpp b/src/dynarmic/tests/print_info.cpp index 6d6109d849..988a8d294e 100644 --- a/src/dynarmic/tests/print_info.cpp +++ b/src/dynarmic/tests/print_info.cpp @@ -43,12 +43,12 @@ using namespace Dynarmic; std::string_view GetNameOfA32Instruction(u32 instruction) { - if (auto const vfp_decoder = A32::DecodeVFP(instruction)) - return *A32::GetNameVFP(instruction); - else if (auto const asimd_decoder = A32::DecodeASIMD(instruction)) - return *A32::GetNameASIMD(instruction); - else if (auto const decoder = A32::DecodeArm(instruction)) - return *A32::GetNameARM(instruction); + if (auto const vfp_decoder = A32::GetNameVFP(instruction)) + return *vfp_decoder; + else if (auto const asimd_decoder = A32::GetNameASIMD(instruction)) + return *asimd_decoder; + else if (auto const decoder = A32::GetNameArm(instruction)) + return *decoder; return ""; } From 672c21829b1126aeb1c1bfb867ac03e57e2e3c3a Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 9 May 2026 05:48:20 +0200 Subject: [PATCH 5/5] [core/hle/kernel] Remove redundant TLS load/stores, reuse computed segment+address instead (#3932) While originally for MSVC, this also should help clang/gcc not die trying to make codegen for the load/store of fields for the tls_data should help to reuse computed values instead of recomputing shit for no reason Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3932 Reviewed-by: MaranBr Reviewed-by: CamilleLaVey --- src/core/hle/kernel/kernel.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 129bda194a..5f88fa498f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -372,17 +372,17 @@ struct KernelCore::Impl { } // Gets the dummy KThread for the caller, allocating a new one if this is the first time - KThread* GetHostDummyThread(KThread* existing_thread) { - if (tls_data.thread == nullptr) { + KThread* GetHostDummyThread(ThreadLocalData& t, KThread* existing_thread) { + if (t.thread == nullptr) { auto const initialize{[](KThread* thread) { ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); return thread; }}; - tls_data.raw_thread.emplace(system.Kernel()); - tls_data.thread = existing_thread ? existing_thread : initialize(&*tls_data.raw_thread); - ASSERT(tls_data.thread != nullptr); + t.raw_thread.emplace(system.Kernel()); + t.thread = existing_thread ? existing_thread : initialize(&*t.raw_thread); + ASSERT(t.thread != nullptr); } - return tls_data.thread; + return t.thread; } /// Registers a CPU core thread by allocating a host thread ID for it @@ -395,7 +395,7 @@ struct KernelCore::Impl { /// Registers a new host thread by allocating a host thread ID for it void RegisterHostThread(KThread* existing_thread) { - (void)GetHostDummyThread(existing_thread); + (void)GetHostDummyThread(tls_data, existing_thread); } [[nodiscard]] u32 GetCurrentHostThreadID() { @@ -419,9 +419,8 @@ struct KernelCore::Impl { } KThread* GetCurrentEmuThread() { - if (!tls_data.current_thread) - tls_data.current_thread = GetHostDummyThread(nullptr); - return tls_data.current_thread; + auto& t = tls_data; + return t.current_thread ? t.current_thread : (t.current_thread = GetHostDummyThread(t, nullptr)); } void SetCurrentEmuThread(KThread* thread) {