From 04259f031d83430a348a5d8692425833f56eb6c9 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Wed, 6 Aug 2025 21:57:48 +0100 Subject: [PATCH 1/4] FEXLoader: Enable downstream kernel-side unaligned atomic handling --- .../Tools/FEXInterpreter/FEXInterpreter.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp index 7e28d1bcd..055f4b35a 100644 --- a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp +++ b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp @@ -222,6 +222,25 @@ void CheckForGCS() { } } // namespace FEX::GCS +namespace FEX::UnalignedAtomic { +void SetupKernelUnalignedAtomics() { +#ifndef PR_ARM64_SET_UNALIGN_ATOMIC +#define PR_ARM64_SET_UNALIGN_ATOMIC 0x46455849 +#define PR_ARM64_UNALIGN_ATOMIC_EMULATE (1UL << 0) +#define PR_ARM64_UNALIGN_ATOMIC_BACKPATCH (1UL << 1) +#define PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1UL << 2) +#endif + + // Interfaces with downstream FEX kernel patches to control unaligned atomic handling + FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS); + + uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | PR_ARM64_UNALIGN_ATOMIC_BACKPATCH | + PR_ARM64_UNALIGN_ATOMIC_EMULATE; + + prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0); +} +} // namespace FEX::UnalignedAtomic + /** * @brief Get an FD from an environment variable and then unset the environment variable. * @@ -458,6 +477,7 @@ int main(int argc, char** argv, char** const envp) { // Setup TSO hardware emulation immediately after initializing the context. FEX::TSO::SetupTSOEmulation(CTX.get()); + FEX::UnalignedAtomic::SetupKernelUnalignedAtomics(); if (!Loader.Is64BitMode()) { // Tell the kernel we want to use the compat input syscalls even though we're From 24d61e112580ec92e6c82b0f2df92f2641b9798b Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Wed, 6 Aug 2025 22:01:56 +0100 Subject: [PATCH 2/4] Windows: Enable downstream kernel-side unaligned atomic handling --- Source/Windows/ARM64EC/Module.cpp | 3 +-- Source/Windows/Common/TSOHandlerConfig.h | 18 +++++++++++++++++- Source/Windows/WOW64/Module.cpp | 3 +-- Source/Windows/include/winternl.h | 6 ++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index 723465800..e60ae29a4 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -640,7 +640,6 @@ NTSTATUS ProcessInit() { SignalDelegator = fextl::make_unique(); SyscallHandler = fextl::make_unique(); - Exception::HandlerConfig.emplace(); const auto NtDll = GetModuleHandle("ntdll.dll"); const bool IsWine = !!GetProcAddress(NtDll, "wine_get_version"); @@ -654,7 +653,7 @@ NTSTATUS ProcessInit() { CTX->SetSignalDelegator(SignalDelegator.get()); CTX->SetSyscallHandler(SyscallHandler.get()); CTX->InitCore(); - + Exception::HandlerConfig.emplace(*CTX); InvalidationTracker.emplace(*CTX, Threads); HandleImageMap(NtDllBase); diff --git a/Source/Windows/Common/TSOHandlerConfig.h b/Source/Windows/Common/TSOHandlerConfig.h index fbd84a571..cec36ec41 100644 --- a/Source/Windows/Common/TSOHandlerConfig.h +++ b/Source/Windows/Common/TSOHandlerConfig.h @@ -1,18 +1,32 @@ // SPDX-License-Identifier: MIT #pragma once +#include #include #include namespace FEX::Windows { class TSOHandlerConfig final { public: - TSOHandlerConfig() { + TSOHandlerConfig(FEXCore::Context::Context& CTX) { if (HalfBarrierTSOEnabled()) { UnalignedHandlerType = FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::HalfBarrier; } else { UnalignedHandlerType = FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::NonAtomic; } + + if (TSOEnabled()) { + BOOL Enable = TRUE; + NTSTATUS Status = NtSetInformationProcess(NtCurrentProcess(), ProcessFexHardwareTso, &Enable, sizeof(Enable)); + if (Status == STATUS_SUCCESS) { + CTX.SetHardwareTSOSupport(true); + } + } + + uint64_t Flags = + (StrictInProcessSplitLocks() ? FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | FEX_UNALIGN_ATOMIC_BACKPATCH | FEX_UNALIGN_ATOMIC_EMULATE; + + NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)); } FEXCore::ArchHelpers::Arm64::UnalignedHandlerType GetUnalignedHandlerType() const { @@ -20,7 +34,9 @@ public: } private: + FEX_CONFIG_OPT(TSOEnabled, TSOENABLED); FEX_CONFIG_OPT(HalfBarrierTSOEnabled, HALFBARRIERTSOENABLED); + FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS); FEXCore::ArchHelpers::Arm64::UnalignedHandlerType UnalignedHandlerType {FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::HalfBarrier}; }; diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index 4d539ca7e..c51f4f902 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -523,7 +523,6 @@ void BTCpuProcessInit() { SignalDelegator = fextl::make_unique(); SyscallHandler = fextl::make_unique(); - Context::HandlerConfig.emplace(); const auto NtDll = GetModuleHandle("ntdll.dll"); const bool IsWine = !!GetProcAddress(NtDll, "wine_get_version"); OvercommitTracker.emplace(IsWine); @@ -538,7 +537,7 @@ void BTCpuProcessInit() { CTX->SetSignalDelegator(SignalDelegator.get()); CTX->SetSyscallHandler(SyscallHandler.get()); CTX->InitCore(); - + Context::HandlerConfig.emplace(*CTX); InvalidationTracker.emplace(*CTX, Threads); auto NtDllX86 = reinterpret_cast(GetProcAddress(NtDll, "LdrSystemDllInitBlock"))->ntdll_handle; diff --git a/Source/Windows/include/winternl.h b/Source/Windows/include/winternl.h index 9607087f4..efd211c5a 100644 --- a/Source/Windows/include/winternl.h +++ b/Source/Windows/include/winternl.h @@ -455,6 +455,12 @@ typedef enum _MEMORY_INFORMATION_CLASS { #define SystemEmulationBasicInformation (SYSTEM_INFORMATION_CLASS)62 #define ProcessFexHardwareTso (PROCESSINFOCLASS)2000 +#define ProcessFexUnalignAtomic (PROCESSINFOCLASS)2001 + +// These match the prctl flag values +#define FEX_UNALIGN_ATOMIC_EMULATE (1ULL << 0) +#define FEX_UNALIGN_ATOMIC_BACKPATCH (1ULL << 1) +#define FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1ULL << 2) typedef enum _KEY_VALUE_INFORMATION_CLASS { KeyValueBasicInformation, From 387201815b79a05ad3daaf1c04df9e0dbdf3b280 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 10 Nov 2025 13:41:17 -0800 Subject: [PATCH 3/4] Config: Add option to enable or disable kernel backpatchin on unaligned atomic Default to enabled because this is the config we expect by default. In the future will get some benchmarking in various games like Assassin's Creed, and Call of Duty. --- FEXCore/Source/Interface/Config/Config.json.in | 7 +++++++ Source/Tools/FEXInterpreter/FEXInterpreter.cpp | 5 +++-- Source/Windows/Common/TSOHandlerConfig.h | 5 +++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index 103229418..c05b369c4 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -436,6 +436,13 @@ "This is required to ensure a split-lock doesn't tear inside the process" ] }, + "KernelUnalignedAtomicBackpatching": { + "Type": "bool", + "Default": "true", + "Desc": [ + "When the kernel unaligned atomic handler is enabled, use backpatching to reduce kernel context switches." + ] + }, "VolatileMetadata": { "Type": "bool", "Default": "true", diff --git a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp index 055f4b35a..fdb57abad 100644 --- a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp +++ b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp @@ -233,9 +233,10 @@ void SetupKernelUnalignedAtomics() { // Interfaces with downstream FEX kernel patches to control unaligned atomic handling FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS); + FEX_CONFIG_OPT(KernelUnalignedAtomicBackpatching, KERNELUNALIGNEDATOMICBACKPATCHING); - uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | PR_ARM64_UNALIGN_ATOMIC_BACKPATCH | - PR_ARM64_UNALIGN_ATOMIC_EMULATE; + uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | + (KernelUnalignedAtomicBackpatching() ? PR_ARM64_UNALIGN_ATOMIC_BACKPATCH : 0) | PR_ARM64_UNALIGN_ATOMIC_EMULATE; prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0); } diff --git a/Source/Windows/Common/TSOHandlerConfig.h b/Source/Windows/Common/TSOHandlerConfig.h index cec36ec41..482fe516f 100644 --- a/Source/Windows/Common/TSOHandlerConfig.h +++ b/Source/Windows/Common/TSOHandlerConfig.h @@ -23,8 +23,8 @@ public: } } - uint64_t Flags = - (StrictInProcessSplitLocks() ? FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | FEX_UNALIGN_ATOMIC_BACKPATCH | FEX_UNALIGN_ATOMIC_EMULATE; + uint64_t Flags = (StrictInProcessSplitLocks() ? FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | + (KernelUnalignedAtomicBackpatching() ? FEX_UNALIGN_ATOMIC_BACKPATCH : 0) | FEX_UNALIGN_ATOMIC_EMULATE; NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)); } @@ -37,6 +37,7 @@ private: FEX_CONFIG_OPT(TSOEnabled, TSOENABLED); FEX_CONFIG_OPT(HalfBarrierTSOEnabled, HALFBARRIERTSOENABLED); FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS); + FEX_CONFIG_OPT(KernelUnalignedAtomicBackpatching, KERNELUNALIGNEDATOMICBACKPATCHING); FEXCore::ArchHelpers::Arm64::UnalignedHandlerType UnalignedHandlerType {FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::HalfBarrier}; }; From 6772581c53acae866733214a0c76a1ad87d15118 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 16 Oct 2025 18:13:51 -0700 Subject: [PATCH 4/4] Arm64ec: Print a log when kernel unaligned atomics are used Not having this in FEXInterpreter as it is too spammy in the general case. --- Source/Windows/Common/TSOHandlerConfig.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Windows/Common/TSOHandlerConfig.h b/Source/Windows/Common/TSOHandlerConfig.h index 482fe516f..1a0a888ab 100644 --- a/Source/Windows/Common/TSOHandlerConfig.h +++ b/Source/Windows/Common/TSOHandlerConfig.h @@ -26,7 +26,9 @@ public: uint64_t Flags = (StrictInProcessSplitLocks() ? FEX_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | (KernelUnalignedAtomicBackpatching() ? FEX_UNALIGN_ATOMIC_BACKPATCH : 0) | FEX_UNALIGN_ATOMIC_EMULATE; - NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)); + if (NtSetInformationProcess(NtCurrentProcess(), ProcessFexUnalignAtomic, &Flags, sizeof(Flags)) == STATUS_SUCCESS) { + LogMan::Msg::IFmt("FEX: Kernel unaligned atomics enabled!"); + } } FEXCore::ArchHelpers::Arm64::UnalignedHandlerType GetUnalignedHandlerType() const {