From ff7bbaea7db5a9a9528c1ef087d3a49a18931b52 Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Sat, 30 May 2026 01:42:23 +0200 Subject: [PATCH] [dynarmic] bootstrap loongarch64 host build (#4015) Minimal additions to make Eden compile. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4015 Reviewed-by: Lizzie Reviewed-by: crueter --- externals/CMakeLists.txt | 5 + externals/cpmfile.json | 6 + src/CMakeLists.txt | 2 +- src/core/CMakeLists.txt | 2 +- src/dynarmic/CMakeLists.txt | 4 + src/dynarmic/src/dynarmic/CMakeLists.txt | 14 + .../src/dynarmic/backend/exception_handler.h | 10 + .../backend/exception_handler_generic.cpp | 6 +- .../backend/exception_handler_posix.cpp | 33 ++- .../backend/loongarch64/a32_interface.cpp | 170 +++++++++++ .../backend/loongarch64/a64_interface.cpp | 268 ++++++++++++++++++ .../dynarmic/backend/loongarch64/code_block.h | 23 ++ .../backend/loongarch64/exclusive_monitor.cpp | 54 ++++ .../dynarmic/common/spin_lock_loongarch64.cpp | 14 + .../src/dynarmic/frontend/decoder/matcher.h | 5 + 15 files changed, 607 insertions(+), 9 deletions(-) create mode 100644 src/dynarmic/src/dynarmic/backend/loongarch64/a32_interface.cpp create mode 100644 src/dynarmic/src/dynarmic/backend/loongarch64/a64_interface.cpp create mode 100644 src/dynarmic/src/dynarmic/backend/loongarch64/code_block.h create mode 100644 src/dynarmic/src/dynarmic/backend/loongarch64/exclusive_monitor.cpp create mode 100644 src/dynarmic/src/dynarmic/common/spin_lock_loongarch64.cpp diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 13de8f567d..1aaa4c57ca 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -82,6 +82,11 @@ if (ARCHITECTURE_riscv64) AddJsonPackage(biscuit) endif() +# Lagoon +if (ARCHITECTURE_loongarch64) + AddJsonPackage(lagoon) +endif() + # Vulkan stuff AddDependentPackages(vulkan-headers vulkan-utility-libraries) diff --git a/externals/cpmfile.json b/externals/cpmfile.json index 4e9356cb83..72adb8fece 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -67,6 +67,12 @@ "tag": "v%VERSION%", "hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856" }, + "lagoon": { + "repo": "loongson-community/lagoon", + "tag": "%VERSION%", + "version": "1.0.0", + "hash": "b9380f99c6effaeccc6d8f81d4942e852c11ad28613df637e155451556ae5826f93765bee57a5c87a9740d2bd1db463ad0f55a947772fe9d57eeabae3efa373e" + }, "libadrenotools": { "repo": "eden-emulator/libadrenotools", "sha": "8ba23b42d7", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 86aa23c627..a6d526c5eb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories(.) # Dynarmic -if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64) AND NOT YUZU_STATIC_ROOM) +if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) AND NOT YUZU_STATIC_ROOM) add_subdirectory(dynarmic) add_library(dynarmic::dynarmic ALIAS dynarmic) endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 39aebd5f48..2a42090ddc 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1246,7 +1246,7 @@ if (HAS_NCE) target_link_libraries(core PRIVATE merry::oaknut) endif() -if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) target_sources(core PRIVATE arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic_64.cpp diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 4c4bf86d3e..46b0b618e8 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -130,6 +130,10 @@ if ("riscv64" IN_LIST ARCHITECTURE) find_package(biscuit 0.9.1 REQUIRED) endif() +if ("loongarch64" IN_LIST ARCHITECTURE) + find_package(lagoon REQUIRED) +endif() + if ("x86_64" IN_LIST ARCHITECTURE) find_package(xbyak 7 CONFIG) endif() diff --git a/src/dynarmic/src/dynarmic/CMakeLists.txt b/src/dynarmic/src/dynarmic/CMakeLists.txt index c51a660c03..2274943613 100644 --- a/src/dynarmic/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/src/dynarmic/CMakeLists.txt @@ -295,6 +295,20 @@ if ("riscv64" IN_LIST ARCHITECTURE) message(WARNING "TODO: Incomplete frontend for this host architecture") endif() +if ("loongarch64" IN_LIST ARCHITECTURE) + target_link_libraries(dynarmic PRIVATE lagoon::lagoon) + + target_sources(dynarmic PRIVATE + backend/loongarch64/exclusive_monitor.cpp + backend/loongarch64/a32_interface.cpp + backend/loongarch64/a64_interface.cpp + backend/loongarch64/code_block.h + + common/spin_lock_loongarch64.cpp + ) + message(WARNING "TODO: Incomplete frontend for this host architecture") +endif() + if (WIN32) target_sources(dynarmic PRIVATE backend/exception_handler_windows.cpp) elseif (APPLE) diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler.h b/src/dynarmic/src/dynarmic/backend/exception_handler.h index b62bd7ab71..ce6abef5ce 100644 --- a/src/dynarmic/src/dynarmic/backend/exception_handler.h +++ b/src/dynarmic/src/dynarmic/backend/exception_handler.h @@ -26,6 +26,10 @@ class CodeBlock; namespace Dynarmic::Backend::RV64 { class CodeBlock; } // namespace Dynarmic::Backend::RV64 +#elif defined(ARCHITECTURE_loongarch64) +namespace Dynarmic::Backend::LoongArch64 { +class CodeBlock; +} // namespace Dynarmic::Backend::LoongArch64 #else # error "Invalid architecture" #endif @@ -45,6 +49,10 @@ struct FakeCall { struct FakeCall { u64 call_sepc; }; +#elif defined(ARCHITECTURE_loongarch64) +struct FakeCall { + u64 call_pc; +}; #else # error "Invalid architecture" #endif @@ -60,6 +68,8 @@ public: void Register(oaknut::CodeBlock& mem, std::size_t mem_size); #elif defined(ARCHITECTURE_riscv64) void Register(RV64::CodeBlock& mem, std::size_t mem_size); +#elif defined(ARCHITECTURE_loongarch64) + void Register(LoongArch64::CodeBlock& mem, std::size_t mem_size); #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp b/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp index 23dc294511..f19fe98a6d 100644 --- a/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp +++ b/src/dynarmic/src/dynarmic/backend/exception_handler_generic.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later /* This file is part of the dynarmic project. @@ -28,6 +28,10 @@ void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) { void ExceptionHandler::Register(RV64::CodeBlock&, std::size_t) { // Do nothing } +#elif defined(ARCHITECTURE_loongarch64) +void ExceptionHandler::Register(LoongArch64::CodeBlock&, std::size_t) { + // Do nothing +} #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp index 224559133a..c586cf2fb1 100644 --- a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp +++ b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp @@ -6,20 +6,25 @@ * SPDX-License-Identifier: 0BSD */ +#include + +#include +#include #include #include -#include #include #include -#include #include -#include -#include +#include + #include -#include "dynarmic/backend/exception_handler.h" +#include +#include + #include "common/assert.h" -#include "dynarmic/common/context.h" #include "common/common_types.h" +#include "dynarmic/backend/exception_handler.h" +#include "dynarmic/common/context.h" #if defined(ARCHITECTURE_x86_64) # include "dynarmic/backend/x64/block_of_code.h" #elif defined(ARCHITECTURE_arm64) @@ -27,6 +32,8 @@ # include "dynarmic/backend/arm64/abi.h" #elif defined(ARCHITECTURE_riscv64) # include "dynarmic/backend/riscv64/code_block.h" +#elif defined(ARCHITECTURE_loongarch64) +# include "dynarmic/backend/loongarch64/code_block.h" #else # error "Invalid architecture" #endif @@ -150,6 +157,16 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { } } fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_SEPC); +#elif defined(ARCHITECTURE_loongarch64) + { + std::shared_lock guard(sig_handler->code_block_infos_mutex); + if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) { + FakeCall fc = iter->second.cb(CTX_PC); + CTX_PC = fc.call_pc; + return; + } + } + fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); #else # error "Invalid architecture" #endif @@ -209,6 +226,10 @@ void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) { void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) { impl = std::make_unique(std::bit_cast(mem.ptr()), size); } +#elif defined(ARCHITECTURE_loongarch64) +void ExceptionHandler::Register(LoongArch64::CodeBlock& mem, std::size_t size) { + impl = std::make_unique(std::bit_cast(mem.ptr()), size); +} #else # error "Invalid architecture" #endif diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a32_interface.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_interface.cpp new file mode 100644 index 0000000000..d1166d79b6 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a32_interface.cpp @@ -0,0 +1,170 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "dynarmic/interface/A32/a32.h" + +namespace Dynarmic::A32 { + +struct Jit::Impl final { + explicit Impl(UserConfig conf_) : conf(std::move(conf_)) {} + + HaltReason Run() { + UNIMPLEMENTED(); + return halt_reason; + } + + HaltReason Step() { + UNIMPLEMENTED(); + return halt_reason | HaltReason::Step; + } + + void ClearCache() { + HaltExecution(HaltReason::CacheInvalidation); + } + + void InvalidateCacheRange(u32, std::size_t) { + HaltExecution(HaltReason::CacheInvalidation); + } + + void Reset() { + regs = {}; + ext_regs = {}; + cpsr = 0; + fpscr = 0; + halt_reason = {}; + } + + void HaltExecution(HaltReason hr) { + halt_reason |= hr; + } + + void ClearHalt(HaltReason hr) { + halt_reason &= ~hr; + } + + std::array& Regs() { + return regs; + } + + const std::array& Regs() const { + return regs; + } + + std::array& ExtRegs() { + return ext_regs; + } + + const std::array& ExtRegs() const { + return ext_regs; + } + + u32 Cpsr() const { + return cpsr; + } + + void SetCpsr(u32 value) { + cpsr = value; + } + + u32 Fpscr() const { + return fpscr; + } + + void SetFpscr(u32 value) { + fpscr = value; + } + + void ClearExclusiveState() {} + + std::string Disassemble() const { + return {}; + } + + UserConfig conf; + std::array regs{}; + std::array ext_regs{}; + u32 cpsr = 0; + u32 fpscr = 0; + HaltReason halt_reason{}; +}; + +Jit::Jit(UserConfig conf) : impl(std::make_unique(std::move(conf))) {} + +Jit::~Jit() = default; + +HaltReason Jit::Run() { + return impl->Run(); +} + +HaltReason Jit::Step() { + return impl->Step(); +} + +void Jit::ClearCache() { + impl->ClearCache(); +} + +void Jit::InvalidateCacheRange(u32 start_address, std::size_t length) { + impl->InvalidateCacheRange(start_address, length); +} + +void Jit::Reset() { + impl->Reset(); +} + +void Jit::HaltExecution(HaltReason hr) { + impl->HaltExecution(hr); +} + +void Jit::ClearHalt(HaltReason hr) { + impl->ClearHalt(hr); +} + +std::array& Jit::Regs() { + return impl->Regs(); +} + +const std::array& Jit::Regs() const { + return impl->Regs(); +} + +std::array& Jit::ExtRegs() { + return impl->ExtRegs(); +} + +const std::array& Jit::ExtRegs() const { + return impl->ExtRegs(); +} + +u32 Jit::Cpsr() const { + return impl->Cpsr(); +} + +void Jit::SetCpsr(u32 value) { + impl->SetCpsr(value); +} + +u32 Jit::Fpscr() const { + return impl->Fpscr(); +} + +void Jit::SetFpscr(u32 value) { + impl->SetFpscr(value); +} + +void Jit::ClearExclusiveState() { + impl->ClearExclusiveState(); +} + +std::string Jit::Disassemble() const { + return impl->Disassemble(); +} + +} // namespace Dynarmic::A32 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/a64_interface.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/a64_interface.cpp new file mode 100644 index 0000000000..0af55797f9 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/a64_interface.cpp @@ -0,0 +1,268 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "dynarmic/interface/A64/a64.h" + +namespace Dynarmic::A64 { + +struct Jit::Impl final { + explicit Impl(UserConfig conf_) : conf(std::move(conf_)) {} + + HaltReason Run() { + UNIMPLEMENTED(); + return halt_reason; + } + + HaltReason Step() { + UNIMPLEMENTED(); + return halt_reason | HaltReason::Step; + } + + void ClearCache() { + HaltExecution(HaltReason::CacheInvalidation); + } + + void InvalidateCacheRange(u64, std::size_t) { + HaltExecution(HaltReason::CacheInvalidation); + } + + void Reset() { + regs = {}; + vectors = {}; + sp = 0; + pc = 0; + fpcr = 0; + fpsr = 0; + pstate = 0; + halt_reason = {}; + } + + void HaltExecution(HaltReason hr) { + halt_reason |= hr; + } + + void ClearHalt(HaltReason hr) { + halt_reason &= ~hr; + } + + u64 GetSP() const { + return sp; + } + + void SetSP(u64 value) { + sp = value; + } + + u64 GetPC() const { + return pc; + } + + void SetPC(u64 value) { + pc = value; + } + + u64 GetRegister(std::size_t index) const { + return index == 31 ? sp : regs.at(index); + } + + void SetRegister(std::size_t index, u64 value) { + if (index == 31) { + sp = value; + return; + } + regs.at(index) = value; + } + + std::array GetRegisters() const { + return regs; + } + + void SetRegisters(const std::array& value) { + regs = value; + } + + Vector GetVector(std::size_t index) const { + return vectors.at(index); + } + + void SetVector(std::size_t index, Vector value) { + vectors.at(index) = value; + } + + std::array GetVectors() const { + return vectors; + } + + void SetVectors(const std::array& value) { + vectors = value; + } + + u32 GetFpcr() const { + return fpcr; + } + + void SetFpcr(u32 value) { + fpcr = value; + } + + u32 GetFpsr() const { + return fpsr; + } + + void SetFpsr(u32 value) { + fpsr = value; + } + + u32 GetPstate() const { + return pstate; + } + + void SetPstate(u32 value) { + pstate = value; + } + + void ClearExclusiveState() {} + + bool IsExecuting() const { + return false; + } + + std::string Disassemble() const { + return {}; + } + + UserConfig conf; + std::array regs{}; + std::array vectors{}; + u64 sp = 0; + u64 pc = 0; + u32 fpcr = 0; + u32 fpsr = 0; + u32 pstate = 0; + HaltReason halt_reason{}; +}; + +Jit::Jit(UserConfig conf) : impl(std::make_unique(std::move(conf))) {} + +Jit::~Jit() = default; + +HaltReason Jit::Run() { + return impl->Run(); +} + +HaltReason Jit::Step() { + return impl->Step(); +} + +void Jit::ClearCache() { + impl->ClearCache(); +} + +void Jit::InvalidateCacheRange(u64 start_address, std::size_t length) { + impl->InvalidateCacheRange(start_address, length); +} + +void Jit::Reset() { + impl->Reset(); +} + +void Jit::HaltExecution(HaltReason hr) { + impl->HaltExecution(hr); +} + +void Jit::ClearHalt(HaltReason hr) { + impl->ClearHalt(hr); +} + +u64 Jit::GetSP() const { + return impl->GetSP(); +} + +void Jit::SetSP(u64 value) { + impl->SetSP(value); +} + +u64 Jit::GetPC() const { + return impl->GetPC(); +} + +void Jit::SetPC(u64 value) { + impl->SetPC(value); +} + +u64 Jit::GetRegister(std::size_t index) const { + return impl->GetRegister(index); +} + +void Jit::SetRegister(std::size_t index, u64 value) { + impl->SetRegister(index, value); +} + +std::array Jit::GetRegisters() const { + return impl->GetRegisters(); +} + +void Jit::SetRegisters(const std::array& value) { + impl->SetRegisters(value); +} + +Vector Jit::GetVector(std::size_t index) const { + return impl->GetVector(index); +} + +void Jit::SetVector(std::size_t index, Vector value) { + impl->SetVector(index, value); +} + +std::array Jit::GetVectors() const { + return impl->GetVectors(); +} + +void Jit::SetVectors(const std::array& value) { + impl->SetVectors(value); +} + +u32 Jit::GetFpcr() const { + return impl->GetFpcr(); +} + +void Jit::SetFpcr(u32 value) { + impl->SetFpcr(value); +} + +u32 Jit::GetFpsr() const { + return impl->GetFpsr(); +} + +void Jit::SetFpsr(u32 value) { + impl->SetFpsr(value); +} + +u32 Jit::GetPstate() const { + return impl->GetPstate(); +} + +void Jit::SetPstate(u32 value) { + impl->SetPstate(value); +} + +void Jit::ClearExclusiveState() { + impl->ClearExclusiveState(); +} + +bool Jit::IsExecuting() const { + return impl->IsExecuting(); +} + +std::string Jit::Disassemble() const { + return impl->Disassemble(); +} + +} // namespace Dynarmic::A64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/code_block.h b/src/dynarmic/src/dynarmic/backend/loongarch64/code_block.h new file mode 100644 index 0000000000..a4011719d1 --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/code_block.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Dynarmic::Backend::LoongArch64 { + +class CodeBlock { +public: + template + T ptr() const noexcept { + return reinterpret_cast(mem); + } + +private: + u8* mem = nullptr; +}; + +} // namespace Dynarmic::Backend::LoongArch64 diff --git a/src/dynarmic/src/dynarmic/backend/loongarch64/exclusive_monitor.cpp b/src/dynarmic/src/dynarmic/backend/loongarch64/exclusive_monitor.cpp new file mode 100644 index 0000000000..24aa4853bb --- /dev/null +++ b/src/dynarmic/src/dynarmic/backend/loongarch64/exclusive_monitor.cpp @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/interface/exclusive_monitor.h" + +#include + +namespace Dynarmic { + +ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count) + : exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {} + +size_t ExclusiveMonitor::GetProcessorCount() const { + return exclusive_addresses.size(); +} + +void ExclusiveMonitor::Lock() { + lock.Lock(); +} + +void ExclusiveMonitor::Unlock() { + lock.Unlock(); +} + +bool ExclusiveMonitor::CheckAndClear(std::size_t processor_id, VAddr address) { + const VAddr masked_address = address & RESERVATION_GRANULE_MASK; + + Lock(); + if (exclusive_addresses[processor_id] != masked_address) { + Unlock(); + return false; + } + + for (VAddr& other_address : exclusive_addresses) { + if (other_address == masked_address) { + other_address = INVALID_EXCLUSIVE_ADDRESS; + } + } + return true; +} + +void ExclusiveMonitor::Clear() { + Lock(); + std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS); + Unlock(); +} + +void ExclusiveMonitor::ClearProcessor(std::size_t processor_id) { + Lock(); + exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS; + Unlock(); +} + +} // namespace Dynarmic diff --git a/src/dynarmic/src/dynarmic/common/spin_lock_loongarch64.cpp b/src/dynarmic/src/dynarmic/common/spin_lock_loongarch64.cpp new file mode 100644 index 0000000000..6efd05bc26 --- /dev/null +++ b/src/dynarmic/src/dynarmic/common/spin_lock_loongarch64.cpp @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "dynarmic/common/spin_lock.h" + +namespace Dynarmic { + +void SpinLock::Lock() noexcept { +} + +void SpinLock::Unlock() noexcept { +} + +} // namespace Dynarmic diff --git a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h index 49095f67e3..77fb7d941f 100644 --- a/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h +++ b/src/dynarmic/src/dynarmic/frontend/decoder/matcher.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include "common/assert.h" @@ -31,6 +32,10 @@ public: , expected{expected} {} + constexpr Matcher(std::tuple t) noexcept + : Matcher(std::get<0>(t), std::get<1>(t)) + {} + /// @brief Gets the mask for this instruction. constexpr inline T GetMask() const noexcept { return mask;