From 301c030234a5c970e521b78fd4e0e9b2c20b953a Mon Sep 17 00:00:00 2001 From: Exverge Date: Mon, 13 Jul 2026 22:47:25 -0400 Subject: [PATCH] [core] Windows NCE because why the fuck not This implementation is mostly a recommendation for now; MSVC hates inline assembly and disallows it completely it on arm64 --- CMakeLists.txt | 2 +- src/common/vector_math.h | 2 +- src/core/CMakeLists.txt | 8 + src/core/arm/nce/arm_nce.cpp | 85 +- src/core/arm/nce/arm_nce.h | 12 +- src/core/arm/nce/guest_context.h | 56 +- src/core/arm/nce/patcher.cpp | 79 +- src/core/arm/nce/patcher.h | 3 + src/core/arm/nce/win/exceptions.cpp | 29 + src/core/arm/nce/win/exceptions.h | 19 + src/core/arm/nce/win/platform_visitor.cpp | 17 + src/core/arm/nce/win/platform_visitor.h | 1361 +++++++++++++++++ src/core/hle/kernel/k_thread.h | 2 +- .../src/dynarmic/interface/code_page.h | 2 +- 14 files changed, 1629 insertions(+), 48 deletions(-) create mode 100644 src/core/arm/nce/win/exceptions.cpp create mode 100644 src/core/arm/nce/win/exceptions.h create mode 100644 src/core/arm/nce/win/platform_visitor.cpp create mode 100644 src/core/arm/nce/win/platform_visitor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0786dead03..753c08ae50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,7 +300,7 @@ if (NOT EXISTS ${PROJECT_BINARY_DIR}/${compat_json}) file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "") endif() -if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX)) +if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX OR WIN32)) set(HAS_NCE 1) add_compile_definitions(HAS_NCE=1) endif() diff --git a/src/common/vector_math.h b/src/common/vector_math.h index 829a79ce5a..6ab58756e5 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -654,7 +654,7 @@ template <> float32x4_t va = vld1q_f32(&a.x); float32x4_t vb = vld1q_f32(&b.x); float32x4_t result = vmulq_f32(va, vb); -#if defined(__aarch64__) // Use vaddvq_f32 in ARMv8 architectures +#if defined(ARCHITECTURE_arm64) // Use vaddvq_f32 in ARMv8 architectures return vaddvq_f32(result); #else // Use manual addition for older architectures float32x2_t sum2 = vadd_f32(vget_high_f32(result), vget_low_f32(result)); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f43b1c376b..a43d5bfdfb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1247,6 +1247,14 @@ if (HAS_NCE) arm/nce/patcher.h arm/nce/visitor_base.h) target_link_libraries(core PRIVATE merry::oaknut) + + if (WIN32) + target_sources(core PRIVATE + arm/nce/win/platform_visitor.h + arm/nce/win/platform_visitor.cpp + arm/nce/win/exceptions.h + arm/nce/win/exceptions.cpp) + endif () endif() if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITECTURE_loongarch64) diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp index 760ae9d7f3..01ea36a4fa 100644 --- a/src/core/arm/nce/arm_nce.cpp +++ b/src/core/arm/nce/arm_nce.cpp @@ -4,7 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#ifdef __aarch64__ +#ifdef ARCHITECTURE_arm64 // Certain functions have to be marked naked so that the compiler doesn't touch the stack // or implement a return (we "artificially" return later by setting PC to the LR value) @@ -14,15 +14,12 @@ _Pragma("GCC diagnostic ignored \"-Wreturn-type\"") \ __attribute__((naked)) #elif defined(_MSC_VER) -// todo: windows support?? it supports native context switching and signal handling -// https://learn.microsoft.com/en-us/windows/win32/debug/using-a-vectored-exception-handler -// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext #define YUZU_NAKED __declspec(naked) #else #error Unsupported compiler #endif -#if defined(__GNUC__) +#if defined(__clang__) || defined(__GNUC__) #define YUZU_NAKED_END _Pragma("GCC diagnostic pop") #else #define YUZU_NAKED_END @@ -40,18 +37,23 @@ #include "core/hle/kernel/k_process.h" +#ifndef __WIN32 #include #include #include +#else +#include "core/arm/nce/win/exceptions.h" +#endif namespace Core { namespace { +#ifndef __WIN32 struct sigaction g_orig_bus_action; struct sigaction g_orig_segv_action; +#endif -// Verify assembly offsets. using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters; using namespace Common::Literals; @@ -62,15 +64,22 @@ constexpr u32 StackSize = 128_KiB; YUZU_ALWAYS_INLINE void* ArmNce::GetGuestParameters() { void* nep; /* NativeExecutionParameters* */ -#ifdef __APPLE__ +#if defined(__APPLE__) // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/libsyscall/os/tsd.h#L156-L189 asm volatile( "mrs %[out], TPIDRRO_EL0\n" // load pthreads TLS storage "ldr %[out], [ %[out], #%[off] ]\n" // accessed like an array, so i * sizeof(u64) : [out] "=&r"(nep) - : [off] "i"(CONTEXT_KEY * 8) + : [off] "i"((ContextKey - 1) * 8) : "memory"); -#else +#elif defined(__WIN32) + asm volatile( + "mrs %[out], TPIDR_EL0\n" // load windows TLS storage + "ldr %[out], [ %[out], #%[off] ]\n" + : [out] "=&r"(nep) + : [off] "i"(TlsSlots + 8 * ContextKey) + : "memory"); +#elif defined(__linux__) asm volatile( "mrs %0, TPIDR_EL0\n" : "=r"(nep)); @@ -137,7 +146,7 @@ void ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void *i auto tpidr = static_cast(RestoreGuestContext(raw_context)); RestoreGuestContext(raw_context); -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__WIN32) // Save old value of TPIDR_EL0, load guest one u64 tpidr_el0; asm volatile("mrs %0, TPIDR_EL0\n" @@ -169,7 +178,7 @@ HaltReason ArmNce::ReturnToRunCodeByTrampoline(void *tpidr, u64 trampoline_addr) "ldr x2, [ x0, #%[ctx_off] ]\n" "add x5, x2, #%[host_ctx] \n" -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__WIN32) // Load guest tpidr_el0 "mrs x4, TPIDR_EL0\n" "msr TPIDR_EL0, x0\n" @@ -206,7 +215,7 @@ HaltReason ArmNce::ReturnToRunCodeByTrampoline(void *tpidr, u64 trampoline_addr) :: [ctx_off] "i"(offsetof(NativeExecutionParameters, native_context)), [sp_off] "i"(offsetof(GuestContext, sp)), [host_ctx] "i"(offsetof(GuestContext, host_ctx)) -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__WIN32) ,[is_running_off] "i"(offsetof(NativeExecutionParameters, is_actually_running)) #endif ); @@ -217,7 +226,7 @@ static_assert(offsetof(HostContext, host_sp) == 0xE0); // TODO: don't use magic void ArmNce::BreakFromRunCodeSignalHandler(int sig, void *info, void *raw_context) { NativeExecutionParameters* tpidr = reinterpret_cast(GetGuestParameters()); -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__WIN32) if (tpidr->is_actually_running) { tpidr->is_actually_running = false; #else @@ -234,11 +243,11 @@ void ArmNce::BreakFromRunCodeSignalHandler(int sig, void *info, void *raw_contex } void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_context) { - DEBUG_ASSERT(sig == SIGSEGV); + DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS); NativeExecutionParameters* nep = static_cast(GetGuestParameters()); -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__WIN32) if (nep->is_actually_running) { nep->is_actually_running = false; #else @@ -250,22 +259,26 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co :: [host_tpidr] "r"(host_tpidr)); #endif - auto* info = static_cast(raw_info); auto* guest_ctx = static_cast(nep->native_context); auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory(); if (sig == SIGSEGV) { // Try to handle an invalid access. // TODO: handle accesses which split a page? +#ifndef __WIN32 + const Common::ProcessAddress addr = + (reinterpret_cast(static_cast(raw_info)->si_addr) & ~Memory::YUZU_PAGEMASK); +#else const Common::ProcessAddress addr = - (reinterpret_cast(info->si_addr) & ~Memory::YUZU_PAGEMASK); + (reinterpret_cast(*static_cast(raw_info)) & ~Memory::YUZU_PAGEMASK); +#endif if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) { // We handled the access successfully and are returning to guest code. goto ret; } } else if (sig == SIGBUS) { // Match and execute an instruction. - auto ctx = KernelContext(&static_cast(raw_context)->uc_mcontext); + auto ctx = KernelContext(raw_context); auto next_pc = MatchAndExecuteOneInstruction(memory, &ctx); if (next_pc) { // We handled the access successfully and are returning to guest code. @@ -292,7 +305,9 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co #else nep->is_actually_running = true; #endif - } else { + } +#ifndef __WIN32 + else { // Host fault, call original handler if (sig == SIGSEGV) { g_orig_segv_action.sa_sigaction(sig, static_cast(raw_info), raw_context); @@ -302,11 +317,14 @@ void ArmNce::GuestMemoryFaultSignalHandler(int sig, void* raw_info, void* raw_co UNREACHABLE_MSG("unexpected signal {}", sig); } } +#elif + is_host_fault = true; +#endif } void* ArmNce::RestoreGuestContext(void* raw_context) { // Retrieve the host context. - auto host_ctx = KernelContext(&static_cast(raw_context)->uc_mcontext); + auto host_ctx = KernelContext(raw_context); // Thread-local parameters will be located in x9. auto* tpidr = reinterpret_cast(host_ctx.regs()[9]); @@ -336,7 +354,7 @@ void* ArmNce::RestoreGuestContext(void* raw_context) { void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) { // Retrieve the host context. - auto host_ctx = KernelContext(&static_cast(raw_context)->uc_mcontext); + auto host_ctx = KernelContext(raw_context); // Save all guest registers except tpidr_el0. std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs(), sizeof(guest_ctx->cpu_registers)); @@ -364,11 +382,10 @@ void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) { } bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) { - auto host_ctx = KernelContext(&static_cast(raw_context)->uc_mcontext); - auto* info = static_cast(raw_info); + auto host_ctx = KernelContext(raw_context); // We can't handle the access, so determine why we crashed. - const bool is_prefetch_abort = *host_ctx.pc() == reinterpret_cast(info->si_addr); + const bool is_prefetch_abort = *host_ctx.pc() == reinterpret_cast(static_cast(raw_info)->si_addr); // For data aborts, skip the instruction and return to guest code. // This will allow games to continue in many scenarios where they would otherwise crash. @@ -419,7 +436,9 @@ HaltReason ArmNce::RunThread(Kernel::KThread* thread) { auto* process = thread->GetOwnerProcess(); #ifdef __APPLE__ - ASSERT(pthread_setspecific(CONTEXT_KEY, &thread_params) == 0); + ASSERT(pthread_setspecific(ContextKey, &thread_params) == 0); +#elif + ASSERT(TlsSetValue(ContextKey, &thread_params) == 0); #endif // Move non-critical operations outside the locked section @@ -502,11 +521,16 @@ void ArmNce::Initialize() { m_thread_id = pthread_mach_thread_np(pthread_self()); } - ASSERT(pthread_key_init_np(CONTEXT_KEY, [](void*) -> void {}) == 0); + ASSERT(pthread_key_init_np(ContextKey, [](void*) -> void {}) == 0); #elif defined(__linux__) if (m_thread_id == -1) { m_thread_id = gettid(); } +#elif defined(__WIN32) + if (m_thread_id == nullptr) { + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), + &m_thread_id, 0, false, DUPLICATE_SAME_ACCESS); + } #endif // Configure signal stack. @@ -522,6 +546,7 @@ void ArmNce::Initialize() { // Set up signals. static std::once_flag flag; std::call_once(flag, [] { +#ifndef __WIN32 using HandlerType = decltype(sigaction::sa_sigaction); sigset_t signal_mask; @@ -559,6 +584,9 @@ void ArmNce::Initialize() { reinterpret_cast(&ArmNce::GuestMemoryFaultSignalHandler); access_fault_action.sa_mask = signal_mask; Common::SigAction(SIGSEGV, &access_fault_action, &g_orig_segv_action); +#else + AddVectoredExceptionHandler(1, VectoredExceptionHandler); +#endif }); } @@ -619,6 +647,9 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) { "svc #0x80\n" :: "r"(static_cast(m_thread_id)), "r"(static_cast(SIGURG)) : "x0", "x1", "x16", "memory", "cc"); +#elif + SuspendThread(m_thread_id); + UnlockThreadParameters(params); #endif } else { // If the thread is no longer running, we have nothing to do. @@ -639,4 +670,4 @@ void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) { } // namespace Core -#endif // #ifdef __aarch64__ +#endif // #ifdef ARCHITECTURE_arm64 diff --git a/src/core/arm/nce/arm_nce.h b/src/core/arm/nce/arm_nce.h index 04a7a96e1c..da3c696b95 100644 --- a/src/core/arm/nce/arm_nce.h +++ b/src/core/arm/nce/arm_nce.h @@ -27,7 +27,13 @@ class System; // This value is actually reserved for old versions of iOSSimulator, however we aren't iOSSimulator, // so we can manually initialize and use it. // https://github.com/apple-oss-distributions/libpthread/blob/42d026df5b07825070f60134b980a1ec2552dfee/private/pthread/tsd_private.h#L241-L245 -constexpr pthread_key_t CONTEXT_KEY = 210; +constexpr pthread_key_t ContextKey = 210; +#else +#include + +static const u32 ContextKey = TlsAlloc(); +static const u32 NCEStorage = TlsAlloc(); +static const u64 TlsSlots = offsetof(TEB, TlsSlots); #endif @@ -91,7 +97,11 @@ public: // Members set on initialization. std::size_t m_core_index{}; +#ifndef __WIN32 pid_t m_thread_id{-1}; +#else + void* m_thread_id{}; +#endif // Core context. GuestContext m_guest_ctx{}; diff --git a/src/core/arm/nce/guest_context.h b/src/core/arm/nce/guest_context.h index 79216cad46..59443d808c 100644 --- a/src/core/arm/nce/guest_context.h +++ b/src/core/arm/nce/guest_context.h @@ -49,7 +49,7 @@ struct GuestContext { class KernelContext { public: #if defined(__linux__) - KernelContext(void* ptr_) : ptr(static_cast(ptr_)), fpsimd{GetFloatingPointState(ptr)} {} + KernelContext(void* ptr_) : ptr(&static_cast(ptr_)->uc_mcontext), fpsimd{GetFloatingPointState(ptr)} {} u64* pc() { // u64 (unsigned long) does not equal unsigned long long @@ -84,40 +84,74 @@ public: } #elif defined(__APPLE__) - KernelContext(void* ptr) : ptr(*static_cast(ptr)) {} + KernelContext(void* ptr) : ptr(static_cast(ptr).uc_mcontext) {} u64* pc() { - return &(*ptr)->__ss.__pc; + return &ptr->__ss.__pc; } u64* sp() { - return &(*ptr)->__ss.__sp; + return &ptr->__ss.__sp; } u64* regs() { - return (*ptr)->__ss.__x; + return ptr->__ss.__x; } u128* vregs() { // .__v returns __uint128, u128 is an std::array - return reinterpret_cast((*ptr)->__ns.__v); + return reinterpret_cast(ptr->__ns.__v); } u32* fpcr() { - return &(*ptr)->__ns.__fpcr; + return &ptr->__ns.__fpcr; } u32* fpsr() { - return &(*ptr)->__ns.__fpsr; + return &ptr->__ns.__fpsr; } u32* pstate() { - return &(*ptr)->__ss.__cpsr; + return &ptr->__ss.__cpsr; + } +#elif defined(__WIN32) + KernelContext(void* ptr) : ptr(static_cast(ptr)) {} + + u64* pc() { + return ptr->Pc; + } + + u64* sp() { + return ptr->Sp; + } + + u64* regs() { + return ptr->X; + } + + u128* vregs() { + // V returns ARM64_NT_NEON128, u128 is an std::array + return reinterpret_cast(ptr->V); + } + + u32* fpcr() { + return &ptr->Fpcr; + } + + u32* fpsr() { + return &ptr->Fpsr; + } + + u32* pstate() { + return &ptr->Cpsr; } #endif + private: +#if defined(__APPLE__) + mcontext_t ptr; +#elif defined(__linux__) mcontext_t* ptr; -#ifdef __linux__ fpsimd_context* fpsimd; fpsimd_context* GetFloatingPointState(mcontext_t* host_ctx) { @@ -127,6 +161,8 @@ private: } return reinterpret_cast(header); } +#elif defined(__WIN32) + ARM64_NT_CONTEXT* ptr; #endif }; diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp index c50c94a4a1..28c0261a67 100644 --- a/src/core/arm/nce/patcher.cpp +++ b/src/core/arm/nce/patcher.cpp @@ -17,6 +17,7 @@ #include "core/hle/kernel/svc.h" #include "core/memory.h" #include "core/hle/kernel/k_thread.h" +#include "win/platform_visitor.h" namespace Core::NCE { @@ -165,6 +166,19 @@ bool Patcher::PatchText(std::span program_image, const Kernel::CodeSet if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { curr_patch->m_exclusives.push_back(i); } +#ifdef __WIN32 + // TODO: keep track of exclusives? + if (auto scratch = CheckForPlatformRegister(inst); scratch) { + bool pre_buffer = false; + auto ret = AddRelocations(pre_buffer); + + if (pre_buffer) { + WritePlatformRegHandler(ret, inst, scratch, c_pre); + } else { + WritePlatformRegHandler(ret, inst, scratch, c); + } + } +#endif } // Determine patching mode for the final relocation step @@ -365,7 +379,7 @@ size_t Patcher::GetPreSectionSize() const noexcept { return Common::AlignUp(m_patch_instructions_pre.size() * sizeof(u32), Common::HostPageSize); } -__attribute__((always_inline)) +YUZU_ALWAYS_INLINE void Patcher::LoadTLS(oaknut::VectorCodeGenerator& cg, oaknut::XReg out) { #ifdef __APPLE__ // The kernel zeros out TPIDR_EL0 too unpredictably, so we use pthreads TLS instead @@ -373,12 +387,40 @@ void Patcher::LoadTLS(oaknut::VectorCodeGenerator& cg, oaknut::XReg out) { // https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/libsyscall/os/tsd.h#L156-L189 cg.MRS(out, oaknut::SystemReg::TPIDRRO_EL0); - cg.LDR(out, out, CONTEXT_KEY * 8); + cg.LDR(out, out, ContextKey * 8); +#elif __WIN32 + // x18 always points to TEB in Windows, we can just use that for TLS storage + ASSERT(out != X18); + cg.LDR(out, X18, TlsSlots + 8 * ContextKey); #else cg.MRS(out, oaknut::SystemReg::TPIDR_EL0); #endif } +#ifdef __WIN32 +void Patcher::WritePlatformRegHandler(ModuleDestLabel module_dest, uint32 instruction, oaknut::XReg scratch, oaknut::VectorCodeGenerator& code) { + // Save X18 register and scratch register + cg.STP(X18, scratch, SP, PRE_INDEXED, -16); + + // Load x18 register + cg.MOV(scratch, X18); + cg.LDR(X18, scratch, TlsSlots + 8 * NCEStorage); + + // Perform operation + cg.append(instruction); + + // Store x18 register and restore scratch register + cg.STR(X18, scratch, TlsSlots + 8 * NCEStorage); + cg.LDP(X18, scratch, SP, POST_INDEXED, 16); + + // Jump back to the instruction after the "emulated" instruction. + if (&cg == &c_pre) + this->BranchToModulePre(module_dest); + else + this->BranchToModule(module_dest); +} +#endif + void Patcher::WriteLoadContext(oaknut::VectorCodeGenerator& cg) { // This function was called, which modifies X30, so use that as a scratch register. // SP contains the guest X30, so save our return X30 to SP + 8, since we have allocated 16 bytes @@ -495,7 +537,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut // Reload host TPIDR_EL0 and SP. cg.LDP(X2, X3, X1, offsetof(HostContext, host_sp)); cg.MOV(SP, X2); -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__WIN32) static_assert(offsetof(HostContext, host_sp) + 8 == offsetof(HostContext, host_tpidr_el0)); cg.MSR(oaknut::SystemReg::TPIDR_EL0, X3); #endif @@ -563,12 +605,28 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut // Retrieve emulated TLS register from GuestContext. void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& cg) { +#ifdef __WIN32 + if (dest_reg != X18) { +#endif LoadTLS(cg, dest_reg); + if (src_reg == oaknut::SystemReg::TPIDRRO_EL0) { cg.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidrro_el0)); } else { cg.LDR(dest_reg, dest_reg, offsetof(NativeExecutionParameters, tpidr_el0)); } +#ifdef __WIN32 + } else { + const auto scratch = dest_reg.index() == 0 ? X1 : X0; + cg.STR(scratch, SP, PRE_INDEXED, -16); + + LoadTLS(cg, scratch); + cg.LDR(scratch, scratch, offsetof(NativeExecutionParameters, native_context)); + cg.STR(scratch, X18, TlsSlots + 8 * NCEStorage); + + cg.LDR(scratch, SP, POST_INDEXED, 16); + } +#endif // Jump back to the instruction after the emulated MRS. if (&cg == &c_pre) @@ -579,14 +637,23 @@ void Patcher::WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& cg) { const auto scratch_reg = src_reg.index() == 0 ? X1 : X0; - cg.STR(scratch_reg, SP, PRE_INDEXED, -16); + const auto scratch_reg2 = src_reg.index() == 2 ? X3 : X2; + + cg.STP(scratch_reg, scratch_reg2, SP, PRE_INDEXED, -16); // Save guest value to NativeExecutionParameters::tpidr_el0. LoadTLS(cg, scratch_reg); +#ifdef __WIN32 + if (src_reg == X18) { + // Load real x18 value and use that + cg.LDR(scratch_reg2, X18, TlsSlots + 8 * NCEStorage); + src_reg = scratch_reg2; + } else +#endif cg.STR(src_reg, scratch_reg, offsetof(NativeExecutionParameters, tpidr_el0)); - // Restore scratch register. - cg.LDR(scratch_reg, SP, POST_INDEXED, 16); + // Restore scratch registers. + cg.LDP(scratch_reg, scratch_reg2, SP, POST_INDEXED, 16); // Jump back to the instruction after the emulated MSR. if (&cg == &c_pre) diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h index 01c32219cf..3bc5bc83c4 100644 --- a/src/core/arm/nce/patcher.h +++ b/src/core/arm/nce/patcher.h @@ -82,6 +82,9 @@ private: void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& code); void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& code); void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code); +#ifdef __WIN32 + void WritePlatformRegHandler(ModuleDestLabel module_dest, uint32 instruction, oaknut::XReg scratch, oaknut::VectorCodeGenerator& code); +#endif // Convenience wrappers using default code generator void WriteLoadContext() { WriteLoadContext(c); } diff --git a/src/core/arm/nce/win/exceptions.cpp b/src/core/arm/nce/win/exceptions.cpp new file mode 100644 index 0000000000..44a8170ee7 --- /dev/null +++ b/src/core/arm/nce/win/exceptions.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/arm/nce/win/exceptions.h" + +#include "core/arm/nce/arm_nce.h" + +namespace Core { + +static s32 WINAPI VectoredExecptionHandler(EXCEPTION_POINTERS* info) { + u32 code = info->ExceptionRecord->ExceptionCode; + + if (code == SIGSEGV || code == SIGBUS) { + GuestMemoryFaultSignalHandler(code, &info->ExceptionRecord->ExceptionAddress, info->ContextRecord); + if (is_host_fault) { + is_host_fault = false; + return EXCEPTION_CONTINUE_SEARCH; + } + return EXCEPTION_CONTINUE_EXECUTION; + } else if (code == SIGUSR2) { + ReturnToGuestByExceptionLevelChangeSignalHandler(code, &info->ExceptionRecord->ExceptionAddress, info->ContextRecord); + return EXCEPTION_CONTINUE_EXECUTION; + } else { + // other exception?? let it pass + return EXCEPTION_CONTINUE_SEARCH; + } +} + +} // namespace Core \ No newline at end of file diff --git a/src/core/arm/nce/win/exceptions.h b/src/core/arm/nce/win/exceptions.h new file mode 100644 index 0000000000..b6fcc77ac8 --- /dev/null +++ b/src/core/arm/nce/win/exceptions.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#define SIGBUS EXCEPTION_DATATYPE_MISALIGNMENT +#define SIGSEGV EXCEPTION_ACCESS_VIOLATION +#define SIGUSR2 0xE0000001 +#define SIGURG 0xE0000002 + +namespace Core { + +thread_local bool is_host_fault = false; + +static s32 WINAPI VectoredExecptionHandler(EXCEPTION_POINTERS* info); + +} // namespace Core \ No newline at end of file diff --git a/src/core/arm/nce/win/platform_visitor.cpp b/src/core/arm/nce/win/platform_visitor.cpp new file mode 100644 index 0000000000..5f929d9c9a --- /dev/null +++ b/src/core/arm/nce/win/platform_visitor.cpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/arm/nce/win/platform_visitor.h" + +#include + +namespace Core { + +std::optional CheckForPlatformRegister(u32 instruction) { + auto visitor = PlatformVisitor(); + auto decoder = Dynarmic::A64::Decode(visitor, instruction); + + return decoder ? std::optional(oaknut::XReg(static_cast(visitor.scratch))) : std::nullopt; +} + +} // namespace Core diff --git a/src/core/arm/nce/win/platform_visitor.h b/src/core/arm/nce/win/platform_visitor.h new file mode 100644 index 0000000000..8e718cbe5a --- /dev/null +++ b/src/core/arm/nce/win/platform_visitor.h @@ -0,0 +1,1361 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/arm/nce/visitor_base.h" + +#include +#include + +namespace oaknut { +struct XReg; +} + +namespace Core { + +std::optional CheckForPlatformRegister(u32 instruction); + +class PlatformVisitor final : public VisitorBase { +public: + PlatformVisitor(); + ~PlatformVisitor() = default; + + Reg scratch; + + template + void ChooseScratch(Regs... args) { + static_assert((std::is_same_v && ...)); + std::array regs{args...}; + + for (int i = 0; i < 31; ++i) { + if (std::find(regs.begin(), regs.end(), static_cast(i)) == regs.end()) { + scratch = static_cast(i); + return; + } + } + + UNREACHABLE(); + } + + bool UnallocatedEncoding() { + return false; + } + + bool ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool ADRP(Imm<2> immlo, Imm<19> immhi, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool ADDG(Imm<6> offset_imm, Imm<4> tag_offset, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SUBG(Imm<6> offset_imm, Imm<4> tag_offset, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool ADD_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool ADDS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SUB_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SUBS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool AND_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool ORR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool EOR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool ANDS_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool MOVN(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool MOVZ(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool MOVK(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool SBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool BFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool UBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool ASR_1(Imm<5> immr, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool ASR_2(Imm<6> immr, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SXTB_1(Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SXTB_2(Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SXTH_1(Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SXTH_2(Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SXTW(Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool EXTR(bool sf, bool N, Reg Rm, Imm<6> imms, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool XPAC_1(bool D, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool PACIA_1(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool PACIB_1(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool AUTIA_1(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool AUTIB_1(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool SYS(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool SYSL(Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool RMIF(Imm<6> lsb, Reg Rn, Imm<4> mask) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool SETF8(Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool SETF16(Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool DC_IVAC(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_ISW(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_CSW(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_CISW(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_ZVA(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_CVAC(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_CVAU(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_CVAP(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool DC_CIVAC(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool IC_IVAU(Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool BR(Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool BRA(bool Z, bool M, Reg Rn, Reg Rm) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool BLR(Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool BLRA(bool Z, bool M, Reg Rn, Reg Rm) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool RET(Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool CBZ(bool sf, Imm<19> imm19, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool CBNZ(bool sf, Imm<19> imm19, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool STx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LDx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LDx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool ST1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool ST1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool ST3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool ST3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool ST2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool ST2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool ST4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool ST4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD1R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD1R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD3R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD3R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD2R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD2R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LD4R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LD4R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool STXR(Imm<2> size, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool STLXR(Imm<2> size, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool STXP(Imm<1> size, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool STLXP(Imm<1> size, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDXR(Imm<2> size, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDAXR(Imm<2> size, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDXP(Imm<1> size, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDAXP(Imm<1> size, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STLLR(Imm<2> size, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STLR(Imm<2> size, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDAR(Imm<2> size, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool CASP(bool sz, bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool CASB(bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool CASH(bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool CAS(bool sz, bool L, Reg Rs, bool o0, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool LDRSW_lit(Imm<19> imm19, Reg Rt) { + ChooseScratch(Reg::X18, Rt); + return false || Rt == Reg::X18; + } + + bool STNP_LDNP_gen(Imm<1> upper_opc, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STNP_LDNP_fpsimd(Imm<2> opc, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STGP_1(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STGP_2(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STGP_3(Imm<7> offset_imm, Reg Rt2, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool PRFM_imm(Imm<12> imm12, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool PRFM_unscaled_imm(Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STTRB(Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDTRB(Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDTRSB(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STTRH(Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDTRH(Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDTRSH(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDTRSW(Imm<9> imm9, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDADDB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDCLRB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDEORB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSETB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSMAXB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSMINB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDUMAXB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDUMINB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool SWPB(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDAPRB(Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDADDH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDCLRH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDEORH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSETH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSMAXH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSMINH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDUMAXH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDUMINH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool SWPH(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDAPRH(Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDADD(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDCLR(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDEOR(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSET(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSMAX(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDSMIN(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDUMAX(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDUMIN(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool SWP(bool A, bool R, Reg Rs, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rs); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rs == Reg::X18; + } + + bool LDAPR(Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rm); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rm == Reg::X18; + } + + bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt, Rm); + return false || Rn == Reg::X18 || Rt == Reg::X18 || Rm == Reg::X18; + } + + bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool STG_1(Imm<9> imm9, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STG_2(Imm<9> imm9, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STG_3(Imm<9> imm9, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LDG(Imm<9> offset_imm, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STZG_1(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STZG_2(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STZG_3(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool ST2G_1(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool ST2G_2(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool ST2G_3(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STGV(Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool STZ2G_1(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STZ2G_2(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool STZ2G_3(Imm<9> offset_imm, Reg Rn) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool LDGV(Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool LDRA(bool M, bool S, Imm<9> imm9, bool W, Reg Rn, Reg Rt) { + ChooseScratch(Reg::X18, Rn, Rt); + return false || Rn == Reg::X18 || Rt == Reg::X18; + } + + bool UDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool LSLV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool LSRV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ASRV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool RORV(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool CRC32(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool CRC32C(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool PACGA(Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SUBP(Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool IRG(Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool GMI(Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SUBPS(Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool RBIT_int(bool sf, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool REV16_int(bool sf, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool REV(bool sf, bool opc_0, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool CLZ_int(bool sf, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool CLS_int(bool sf, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool REV32_int(Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool PACDA(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool PACDB(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool AUTDA(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool AUTDB(bool Z, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd); + return false || Rn == Reg::X18 || Rd == Reg::X18; + } + + bool AND_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool BIC_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ORR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ORN_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool EOR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool EON(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ANDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool BICS(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ADDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SUB_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SUBS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ADD_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ADDS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SUB_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SUBS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ADC(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool ADCS(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SBC(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SBCS(bool sf, Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool CCMN_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool CCMP_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool CCMN_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool CCMP_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool CSEL(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool CSINC(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool CSINV(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool CSNEG(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool MADD(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm, Ra); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18 || Ra == Reg::X18; + } + + bool MSUB(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm, Ra); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18 || Ra == Reg::X18; + } + + bool SMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm, Ra); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18 || Ra == Reg::X18; + } + + bool SMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm, Ra); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18 || Ra == Reg::X18; + } + + bool SMULH(Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool UMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm, Ra); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18 || Ra == Reg::X18; + } + + bool UMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm, Ra); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18 || Ra == Reg::X18; + } + + bool UMULH(Reg Rm, Reg Rn, Reg Rd) { + ChooseScratch(Reg::X18, Rn, Rd, Rm); + return false || Rn == Reg::X18 || Rd == Reg::X18 || Rm == Reg::X18; + } + + bool SQDMLAL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool SQDMLSL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool SQDMULL_vec_1(Imm<2> size, Reg Rm, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn, Rm); + return false || Rn == Reg::X18 || Rm == Reg::X18; + } + + bool DUP_gen(bool Q, Imm<5> imm5, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool SMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool UMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool INS_gen(Imm<5> imm5, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool SCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool UCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool FCVTZS_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTZU_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTNS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTNU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool SCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool UCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) { + ChooseScratch(Reg::X18, Rn); + return false || Rn == Reg::X18; + } + + bool FCVTAS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTAU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTPS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTPU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTMS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTMU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTZS_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FCVTZU_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } + + bool FJCVTZS(Vec Vn, Reg Rd) { + ChooseScratch(Reg::X18, Rd); + return false || Rd == Reg::X18; + } +}; + +} // namespace Core \ No newline at end of file diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 76c8bd7380..5e6e425220 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -668,7 +668,7 @@ public: public: // TODO: This shouldn't be defined in kernel namespace struct NativeExecutionParameters { -#if defined(__APPLE__) && HAS_NCE +#if (defined(__APPLE__) || defined(__WIN32)) && HAS_NCE // Are we in actual guest code? bool is_actually_running{}; #endif diff --git a/src/dynarmic/src/dynarmic/interface/code_page.h b/src/dynarmic/src/dynarmic/interface/code_page.h index 0491448823..e6e0fe8e28 100644 --- a/src/dynarmic/src/dynarmic/interface/code_page.h +++ b/src/dynarmic/src/dynarmic/interface/code_page.h @@ -11,7 +11,7 @@ namespace Dynarmic { /// @brief Smallest valid page /// // TODO: can we base this off the system page size without using the heap? -#if defined(__APPLE__) && defined(__aarch64__) +#if defined(__APPLE__) && defined(ARCHITECTURE_arm64) constexpr inline uint64_t CODE_PAGE_SIZE = 0x4000; #else constexpr inline uint64_t CODE_PAGE_SIZE = 0x1000;