From 5cf5d3e3a263672a2a10522d75d49e5bca3149bb Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 3 Sep 2025 11:08:15 +0200 Subject: [PATCH 01/10] docs: Drop reference to removed CreateIRCopy function --- FEXCore/docs/IR.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/FEXCore/docs/IR.md b/FEXCore/docs/IR.md index 11d60c77e..986fd6949 100644 --- a/FEXCore/docs/IR.md +++ b/FEXCore/docs/IR.md @@ -78,14 +78,9 @@ When generating IR inside of the `OpDispatchBuilder` it is straight forward, jus This is an intrusive allocator that is used by the `OpDispatchBuilder` for storing IR data. It is a simple linear arena allocator without resizing capabilities. ### OpDispatchBuilder -OpDispatchBuilder provides two routines for handling the IR outside of the class -* `IRListView ViewIR();` - * Returns a wrapper container class the allows you to view the IR. This doesn't take ownership of the IR data. - * If the OpDispatcherBuilder changes its IR then changes are also visible to this class -* `IRListView *CreateIRCopy()` - * As the name says, it creates a new copy of the IR that is in the OpDispatchBuilder - * Copying the IR only copies the memory used and doesn't have any free space for optimizations after this copy operation - * Useful for tiered recompilers, AOT, and offline analysis +OpDispatchBuilder provides `IRListView ViewIR()` for handling the IR outside of the class: +* Returns a wrapper container class the allows you to view the IR. This doesn't take ownership of the IR data. +* If the OpDispatcherBuilder changes its IR then changes are also visible to this class This class uses two IntrusiveAllocator objects for tracking IR data. `ListData` and `Data` are the object names. * `ListData` is for tracking the doubly linked list of nodes From 7d4bf84304fd93eb9dcab133a9fdc26ca1b24d23 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 3 Sep 2025 10:57:28 +0200 Subject: [PATCH 02/10] FHU: Add GetFilename overload for std::string_view on WoA --- FEXHeaderUtils/FEXHeaderUtils/Filesystem.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/FEXHeaderUtils/FEXHeaderUtils/Filesystem.h b/FEXHeaderUtils/FEXHeaderUtils/Filesystem.h index f92215726..44c784d24 100644 --- a/FEXHeaderUtils/FEXHeaderUtils/Filesystem.h +++ b/FEXHeaderUtils/FEXHeaderUtils/Filesystem.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #ifndef _WIN32 #include #include @@ -354,6 +354,11 @@ inline fextl::string GetFilename(const fextl::string& Path) { return PathToString(std::filesystem::path(Path).filename()); } +inline std::string_view GetFilename(std::string_view Path) { + auto Filename = PathToString(std::filesystem::path(Path).filename()); + return Path.substr(Path.size() - Filename.size()); +} + inline fextl::string ParentPath(const fextl::string& Path) { return PathToString(std::filesystem::path(Path).parent_path()); } From 48787ab46030a9db7dc100dc6bc44e3b4bdafa69 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 3 Sep 2025 11:12:19 +0200 Subject: [PATCH 03/10] Context: Drop unneeded header includes --- FEXCore/include/FEXCore/Core/Context.h | 4 ---- Source/Tools/LinuxEmulation/VDSO_Emulation.h | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index 007b14e1b..3fbdb5916 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -13,10 +13,6 @@ #include #include -#include -#include -#include - namespace FEXCore { class CodeLoader; struct HostFeatures; diff --git a/Source/Tools/LinuxEmulation/VDSO_Emulation.h b/Source/Tools/LinuxEmulation/VDSO_Emulation.h index e8f3f0347..3bcd810ec 100644 --- a/Source/Tools/LinuxEmulation/VDSO_Emulation.h +++ b/Source/Tools/LinuxEmulation/VDSO_Emulation.h @@ -11,6 +11,8 @@ namespace FEX::HLE { class SyscallHandler; } +#include + namespace FEX::VDSO { struct VDSOMapping { void* VDSOBase {}; From bf1b76dcd2382a6b4d2d640b4ee8ecc205f01889 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Tue, 2 Sep 2025 18:01:22 +0200 Subject: [PATCH 04/10] LinuxSyscalls: Clean up MappedResource insertion None of the call sites needed this to be a template, and the previous function produced unreadable error messages. --- .../LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp | 6 +++--- .../LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index a3bec6e71..18c4b54da 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -375,7 +375,7 @@ void SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint6 if (PathLength != -1) { Tmp[PathLength] = '\0'; - auto [Iter, Inserted] = VMATracking.EmplaceMappedResource(mrid, VMATracking::MappedResource {nullptr, nullptr, 0}); + auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); Resource = &Iter->second; if (Inserted) { @@ -386,7 +386,7 @@ void SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint6 } else if (flags & MAP_SHARED) { VMATracking::MRID mrid {VMATracking::SpecialDev::Anon, AnonSharedId++}; - auto [Iter, Inserted] = VMATracking.EmplaceMappedResource(mrid, VMATracking::MappedResource {nullptr, nullptr, 0}); + auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); LOGMAN_THROW_A_FMT(Inserted == true, "VMA tracking error"); Resource = &Iter->second; Resource->Iterator = Iter; @@ -446,7 +446,7 @@ void SyscallHandler::TrackMremap(FEXCore::Core::InternalThreadState* Thread, uin void SyscallHandler::TrackShmat(FEXCore::Core::InternalThreadState* Thread, int shmid, uint64_t shmaddr, int shmflg, uint64_t Length) { VMATracking::MRID mrid {VMATracking::SpecialDev::SHM, static_cast(shmid)}; - auto [Iter, Inserted] = VMATracking.EmplaceMappedResource(mrid, VMATracking::MappedResource {nullptr, nullptr, Length}); + auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, Length}); auto Resource = &Iter->second; if (Inserted) { Resource->Iterator = Iter; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h index 5c0f0e693..39b3558d5 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h @@ -114,11 +114,9 @@ struct VMATracking { // Returns the Size of the Shm or 0 if not found uintptr_t DeleteSHMRegion(FEXCore::Context::Context* Ctx, uintptr_t Base); - // Emplaces a new `MappedResource` to track. - // Used for `mmap` and `shmat` resources; Anonymous, FD, and SHM depending on flags. - template - inline auto EmplaceMappedResource(Args&&... args) { - return MappedResources.emplace(args...); + // Adds a new `MappedResource` to track. + inline auto InsertMappedResource(const MRID& mrid, MappedResource Resource) { + return MappedResources.emplace(mrid, std::move(Resource)); } private: bool ListRemove(VMAEntry* Mapping); From af1c2cccac9b5b51b57421445e426a32e526fd64 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Tue, 2 Sep 2025 18:20:05 +0200 Subject: [PATCH 05/10] Rename AOTIR.cpp to CodeCache.cpp --- FEXCore/Source/Interface/{IR/AOTIR.cpp => Core/CodeCache.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename FEXCore/Source/Interface/{IR/AOTIR.cpp => Core/CodeCache.cpp} (100%) diff --git a/FEXCore/Source/Interface/IR/AOTIR.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp similarity index 100% rename from FEXCore/Source/Interface/IR/AOTIR.cpp rename to FEXCore/Source/Interface/Core/CodeCache.cpp From db601d333bcfafd78e23daf0f78303e409b15386 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Tue, 2 Sep 2025 18:23:34 +0200 Subject: [PATCH 06/10] Core: Rename and move AOTIR.cpp and AOTIR.h The new names better reflect the contents after recent/upcoming API changes. --- FEXCore/Source/CMakeLists.txt | 2 +- FEXCore/Source/Interface/Context/Context.h | 2 +- FEXCore/Source/Interface/Core/CodeCache.cpp | 2 +- .../Source/Interface/{IR/AOTIR.h => Core/JIT/DebugData.h} | 0 FEXCore/Source/Interface/GDBJIT/GDBJIT.h | 5 ++--- 5 files changed, 5 insertions(+), 6 deletions(-) rename FEXCore/Source/Interface/{IR/AOTIR.h => Core/JIT/DebugData.h} (100%) diff --git a/FEXCore/Source/CMakeLists.txt b/FEXCore/Source/CMakeLists.txt index ae94d50de..3ff663ca1 100644 --- a/FEXCore/Source/CMakeLists.txt +++ b/FEXCore/Source/CMakeLists.txt @@ -18,6 +18,7 @@ set (SRCS Common/JitSymbols.cpp Interface/Context/Context.cpp Interface/Core/LookupCache.cpp + Interface/Core/CodeCache.cpp Interface/Core/Core.cpp Interface/Core/CPUBackend.cpp Interface/Core/Addressing.cpp @@ -57,7 +58,6 @@ set (SRCS Interface/Core/X86Tables/VEXTables.cpp Interface/Core/X86Tables/X87Tables.cpp Interface/GDBJIT/GDBJIT.cpp - Interface/IR/AOTIR.cpp Interface/IR/IRDumper.cpp Interface/IR/IREmitter.cpp Interface/IR/PassManager.cpp diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index 88c1e4089..e4e5f3689 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -6,7 +6,7 @@ #include "Interface/Core/CPUID.h" #include "Interface/Core/X86HelperGen.h" #include "Interface/Core/Dispatcher/Dispatcher.h" -#include "Interface/IR/AOTIR.h" +#include #include #include #include diff --git a/FEXCore/Source/Interface/Core/CodeCache.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp index 1426d186c..867ce6e15 100644 --- a/FEXCore/Source/Interface/Core/CodeCache.cpp +++ b/FEXCore/Source/Interface/Core/CodeCache.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT #include "FEXHeaderUtils/Filesystem.h" #include "Interface/Context/Context.h" -#include "Interface/IR/AOTIR.h" +#include "Interface/Core/JIT/DebugData.h" #include #include diff --git a/FEXCore/Source/Interface/IR/AOTIR.h b/FEXCore/Source/Interface/Core/JIT/DebugData.h similarity index 100% rename from FEXCore/Source/Interface/IR/AOTIR.h rename to FEXCore/Source/Interface/Core/JIT/DebugData.h diff --git a/FEXCore/Source/Interface/GDBJIT/GDBJIT.h b/FEXCore/Source/Interface/GDBJIT/GDBJIT.h index 101224577..11fdc3952 100644 --- a/FEXCore/Source/Interface/GDBJIT/GDBJIT.h +++ b/FEXCore/Source/Interface/GDBJIT/GDBJIT.h @@ -1,9 +1,8 @@ // SPDX-License-Identifier: MIT - -#include +#include namespace FEXCore { void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry* Entry, uintptr_t VAFileStart, uint64_t GuestRIP, uintptr_t HostEntry, FEXCore::Core::DebugData* DebugData); -} \ No newline at end of file +} From 0749477eb941c29ed8e31e4457f9c3124613b67e Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Tue, 2 Sep 2025 18:24:34 +0200 Subject: [PATCH 07/10] CodeCache: Introduce revamped interfaces --- FEXCore/Source/Interface/Context/Context.h | 32 +++++++-- FEXCore/Source/Interface/Core/CPUBackend.h | 17 ++--- FEXCore/Source/Interface/Core/CodeCache.cpp | 69 ++++++++++--------- FEXCore/Source/Interface/Core/Core.cpp | 38 ++++------ .../Interface/Core/JIT/Arm64Relocations.cpp | 42 ++++++----- FEXCore/Source/Interface/Core/JIT/DebugData.h | 37 ---------- FEXCore/Source/Interface/Core/JIT/JITClass.h | 4 +- FEXCore/Source/Interface/GDBJIT/GDBJIT.cpp | 14 ++-- FEXCore/Source/Interface/GDBJIT/GDBJIT.h | 4 +- FEXCore/include/FEXCore/Core/CodeCache.h | 66 ++++++++++++++++++ FEXCore/include/FEXCore/Core/Context.h | 13 +--- .../include/FEXCore/HLE/SourcecodeResolver.h | 4 -- FEXCore/include/FEXCore/HLE/SyscallHandler.h | 23 ++----- Source/Tools/CodeSizeValidation/Main.cpp | 5 +- Source/Tools/CommonTools/DummyHandlers.h | 4 +- .../LinuxEmulation/LinuxSyscalls/Syscalls.h | 4 +- .../LinuxSyscalls/SyscallsSMCTracking.cpp | 17 ++--- .../LinuxSyscalls/SyscallsVMATracking.cpp | 6 -- .../LinuxSyscalls/SyscallsVMATracking.h | 9 +-- Source/Windows/ARM64EC/Module.cpp | 4 +- Source/Windows/WOW64/Module.cpp | 4 +- 21 files changed, 211 insertions(+), 205 deletions(-) create mode 100644 FEXCore/include/FEXCore/Core/CodeCache.h diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index e4e5f3689..c9e9a1625 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -71,7 +71,27 @@ struct CustomIRResult { using BlockDelinkerFunc = void (*)(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record); constexpr uint32_t TSC_SCALE_MAXIMUM = 1'000'000'000; ///< 1Ghz -class ContextImpl final : public FEXCore::Context::Context, CPU::CodeBufferManager { +class CodeCache : public AbstractCodeCache { +public: + CodeCache(ContextImpl&); + ~CodeCache(); + + ContextImpl& CTX; + bool IsGeneratingCache = false; + + fextl::string ComputeCodeMapId(std::string_view Filename) override; + + void LoadData(Core::InternalThreadState&, std::byte* MappedCacheFile, const ExecutableFileSectionInfo&) override; + bool SaveData(Core::InternalThreadState&, int TargetFD, const ExecutableFileSectionInfo&, uint64_t SerializedBaseAddress) override; + + void InitiateCacheGeneration() override { + IsGeneratingCache = true; + } + + void PostCompileCode(FEXCore::Core::InternalThreadState& Thread, void* CodePtr, uint64_t GuestRIP, Core::DebugData&); +}; + +class ContextImpl final : public FEXCore::Context::Context, public CPU::CodeBufferManager { public: // Context base class implementation. bool InitCore() override; @@ -141,10 +161,9 @@ public: FEXCore::CPUID::XCRResults RunXCRFunction(uint32_t Function) override; FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) override; - FEXCore::IR::AOTIRCacheEntry* LoadAOTIRCacheEntry(const fextl::string& Name) override; - void UnloadAOTIRCacheEntry(FEXCore::IR::AOTIRCacheEntry* Entry) override; - - void FinalizeAOTIRCache() override {} + CodeCache& GetCodeCache() override { + return CodeCache; + } void OnCodeBufferAllocated(CPU::CodeBuffer&) override; void ClearCodeCache(FEXCore::Core::InternalThreadState* Thread, bool NewCodeBuffer = true) override; @@ -224,6 +243,7 @@ public: FEXCore::HLE::SourcecodeResolver* SourcecodeResolver {}; FEXCore::ThunkHandler* ThunkHandler {}; fextl::unique_ptr Dispatcher; + CodeCache CodeCache; SignalDelegator* SignalDelegation {}; X86GeneratedCode X86CodeGen; @@ -330,8 +350,6 @@ private: */ void InitializeCompiler(FEXCore::Core::InternalThreadState* Thread); - IR::AOTIRCaptureCache IRCaptureCache; - bool SupportsHardwareTSO = false; bool AtomicTSOEmulationEnabled = true; bool VectorAtomicTSOEmulationEnabled = false; diff --git a/FEXCore/Source/Interface/Core/CPUBackend.h b/FEXCore/Source/Interface/Core/CPUBackend.h index ed5fc29d8..ab9f13b54 100644 --- a/FEXCore/Source/Interface/Core/CPUBackend.h +++ b/FEXCore/Source/Interface/Core/CPUBackend.h @@ -17,6 +17,10 @@ $end_info$ #include +namespace FEXCore::CPU { +union Relocation; +} + namespace FEXCore { namespace IR { @@ -157,18 +161,7 @@ namespace CPU { virtual CompiledCode CompileCode(uint64_t Entry, uint64_t Size, bool SingleInst, const FEXCore::IR::IRListView* IR, FEXCore::Core::DebugData* DebugData, bool CheckTF) = 0; - /** - * @brief Relocates a block of code from the JIT code object cache - * - * @param Entry - RIP of the entry - * @param SerializationData - Serialization data referring to the object cache for `Entry` - * - * @return An executable function pointer relocated from the cache object - */ - [[nodiscard]] - virtual void* RelocateJITObjectCode(uint64_t /* Entry */, const CodeSerialize::CodeObjectFileSection* /* SerializationData */) { - return nullptr; - } + virtual fextl::vector TakeRelocations() = 0; virtual void ClearCache() {} diff --git a/FEXCore/Source/Interface/Core/CodeCache.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp index 867ce6e15..98441d2b3 100644 --- a/FEXCore/Source/Interface/Core/CodeCache.cpp +++ b/FEXCore/Source/Interface/Core/CodeCache.cpp @@ -1,60 +1,61 @@ // SPDX-License-Identifier: MIT -#include "FEXHeaderUtils/Filesystem.h" -#include "Interface/Context/Context.h" -#include "Interface/Core/JIT/DebugData.h" +#include +#include -#include #include -#include +#include -#include #include #include -namespace FEXCore::IR { +namespace FEXCore { -bool AOTIRCaptureCache::PostCompileCode(FEXCore::Core::InternalThreadState* Thread, void* CodePtr, uint64_t GuestRIP, uint64_t StartAddr, - uint64_t Length, FEXCore::Core::DebugData* DebugData) { +ExecutableFileInfo::~ExecutableFileInfo() = default; - // Both generated ir and LibraryJITName need a named region lookup - if (CTX->Config.LibraryJITNaming() || CTX->Config.GDBSymbols()) { +} // namespace FEXCore - auto AOTIRCacheEntry = CTX->SyscallHandler->LookupAOTIRCacheEntry(Thread, GuestRIP); +namespace FEXCore::Context { - if (AOTIRCacheEntry.Entry) { - if (DebugData && CTX->Config.LibraryJITNaming()) { - CTX->Symbols.RegisterNamedRegion(Thread->SymbolBuffer.get(), CodePtr, DebugData->HostCodeSize, AOTIRCacheEntry.Entry->Filename); +CodeCache::CodeCache(ContextImpl& CTX_) + : CTX(CTX_) {} +CodeCache::~CodeCache() = default; + +void CodeCache::LoadData(Core::InternalThreadState& Thread, std::byte* MappedCacheFile, const ExecutableFileSectionInfo& GuestRIPLookup) { + // TODO +} + +bool CodeCache::SaveData(Core::InternalThreadState& Thread, int fd, const ExecutableFileSectionInfo& SourceBinary, uint64_t SerializedBaseAddress) { + // TODO + return true; +} + +void CodeCache::PostCompileCode(FEXCore::Core::InternalThreadState& Thread, void* CodePtr, uint64_t GuestRIP, FEXCore::Core::DebugData& DebugData) { + if (CTX.Config.LibraryJITNaming() || CTX.Config.GDBSymbols()) { + auto MappedSection = CTX.SyscallHandler->LookupExecutableFileSection(Thread, GuestRIP); + if (MappedSection) { + if (CTX.Config.LibraryJITNaming()) { + CTX.Symbols.RegisterNamedRegion(Thread.SymbolBuffer.get(), CodePtr, DebugData.HostCodeSize, MappedSection->FileInfo.Filename); } - if (CTX->Config.GDBSymbols()) { - GDBJITRegister(AOTIRCacheEntry.Entry, AOTIRCacheEntry.VAFileStart, GuestRIP, (uintptr_t)CodePtr, DebugData); + if (CTX.Config.GDBSymbols()) { + GDBJITRegister(MappedSection->FileInfo, MappedSection->FileStartVA, GuestRIP, (uintptr_t)CodePtr, DebugData); } } } - - return false; } -AOTIRCacheEntry* AOTIRCaptureCache::LoadAOTIRCacheEntry(const fextl::string& filename) { - fextl::string base_filename = FHU::Filesystem::GetFilename(filename); - +fextl::string CodeCache::ComputeCodeMapId(std::string_view Filename) { + auto base_filename = FHU::Filesystem::GetFilename(Filename); if (!base_filename.empty()) { - auto filename_hash = XXH3_64bits(filename.c_str(), filename.size()); + auto filename_hash = XXH3_64bits(Filename.data(), Filename.size()); - auto fileid = fextl::fmt::format("{}-{}-{}{}{}", base_filename, filename_hash, - (CTX->Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) ? 'S' : 's', - CTX->Config.TSOEnabled ? 'T' : 't', CTX->Config.ABILocalFlags ? 'L' : 'l'); - - std::unique_lock lk(AOTIRCacheLock); - - auto Inserted = AOTIRCache.insert({fileid, AOTIRCacheEntry {.FileId = fileid, .Filename = filename}}); - auto Entry = &(Inserted.first->second); - - return Entry; + return fextl::fmt::format("{}-{:016x}-{}{}{}", base_filename, filename_hash, + (CTX.Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) ? 'S' : 's', CTX.Config.TSOEnabled ? 'T' : 't', + CTX.Config.ABILocalFlags ? 'L' : 'l'); } return nullptr; } -} // namespace FEXCore::IR +} // namespace FEXCore::Context diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 67cebe5ab..97f2ea851 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -77,7 +77,7 @@ namespace FEXCore::Context { ContextImpl::ContextImpl(const FEXCore::HostFeatures& Features) : HostFeatures {Features} , CPUID {this} - , IRCaptureCache {this} { + , CodeCache {*this} { if (!Config.Is64BitMode()) { // When operating in 32-bit mode, the virtual memory we care about is only the lower 32-bits. Config.VirtualMemSize = 1ULL << 32; @@ -708,9 +708,9 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP, uint64_t MaxInst) { if (SourcecodeResolver && Config.GDBSymbols()) { - auto AOTIRCacheEntry = SyscallHandler->LookupAOTIRCacheEntry(Thread, GuestRIP); - if (AOTIRCacheEntry.Entry) { - AOTIRCacheEntry.Entry->SourcecodeMap = SourcecodeResolver->GenerateMap(AOTIRCacheEntry.Entry->Filename, AOTIRCacheEntry.Entry->FileId); + auto MappedSection = SyscallHandler->LookupExecutableFileSection(*Thread, GuestRIP); + if (MappedSection) { + MappedSection->FileInfo.SourcecodeMap = SourcecodeResolver->GenerateMap(MappedSection->FileInfo.Filename, MappedSection->FileInfo.FileId); } } @@ -786,22 +786,22 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_ auto FragmentBasePtr = CompiledCode.BlockBegin; if (DebugData) { - auto GuestRIPLookup = SyscallHandler->LookupAOTIRCacheEntry(Thread, GuestRIP); + auto GuestRIPLookup = SyscallHandler->LookupExecutableFileSection(*Thread, GuestRIP); if (DebugData->Subblocks.size()) { for (auto& Subblock : DebugData->Subblocks) { auto BlockBasePtr = FragmentBasePtr + Subblock.HostCodeOffset; - if (GuestRIPLookup.Entry) { - Symbols.Register(Thread->SymbolBuffer.get(), BlockBasePtr, CompiledCode.Size, GuestRIPLookup.Entry->Filename, - GuestRIP - GuestRIPLookup.VAFileStart); + if (GuestRIPLookup) { + Symbols.Register(Thread->SymbolBuffer.get(), BlockBasePtr, CompiledCode.Size, GuestRIPLookup->FileInfo.Filename, + GuestRIP - GuestRIPLookup->FileStartVA); } else { Symbols.Register(Thread->SymbolBuffer.get(), BlockBasePtr, GuestRIP, Subblock.HostCodeSize); } } } else { - if (GuestRIPLookup.Entry) { - Symbols.Register(Thread->SymbolBuffer.get(), FragmentBasePtr, CompiledCode.Size, GuestRIPLookup.Entry->Filename, - GuestRIP - GuestRIPLookup.VAFileStart); + if (GuestRIPLookup) { + Symbols.Register(Thread->SymbolBuffer.get(), FragmentBasePtr, CompiledCode.Size, GuestRIPLookup->FileInfo.Filename, + GuestRIP - GuestRIPLookup->FileStartVA); } else { Symbols.Register(Thread->SymbolBuffer.get(), FragmentBasePtr, GuestRIP, CompiledCode.Size); } @@ -810,13 +810,12 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_ } // Clear any relocations that might have been generated - Thread->CPUBackend->ClearRelocations(); - - if (IRCaptureCache.PostCompileCode(Thread, CompiledCode.BlockBegin, GuestRIP, StartAddr, Length, DebugData.get())) { - // Early exit - return (uintptr_t)CodePtr; + if (!CodeCache.IsGeneratingCache) { + Thread->CPUBackend->ClearRelocations(); } + CodeCache.PostCompileCode(*Thread, CompiledCode.BlockBegin, GuestRIP, *DebugData.get()); + if (NeedsAddGuestCodeRanges) { // Track in the guest to host map all entrypoints for all pages the compiled block touches, if any page didn't previously // contain code, inform the frontend so it can setup SMC detection. @@ -1009,13 +1008,6 @@ void ContextImpl::MonoBackpatcherWrite(FEXCore::Core::CpuStateFrame* Frame, uint CTX->SyscallHandler->InvalidateGuestCodeRange(Thread, Address, Size); } -IR::AOTIRCacheEntry* ContextImpl::LoadAOTIRCacheEntry(const fextl::string& filename) { - auto rv = IRCaptureCache.LoadAOTIRCacheEntry(filename); - return rv; -} - -void ContextImpl::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry* Entry) {} - void ContextImpl::ConfigureAOTGen(FEXCore::Core::InternalThreadState* Thread, fextl::set* ExternalBranches, uint64_t SectionMaxAddress) { Thread->FrontendDecoder->SetExternalBranches(ExternalBranches); Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress); diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64Relocations.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64Relocations.cpp index 68d3c585d..64c590014 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64Relocations.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64Relocations.cpp @@ -81,35 +81,32 @@ void Arm64JITCore::InsertGuestRIPMove(ARMEmitter::Register Reg, uint64_t Constan Relocations.emplace_back(MoveABI); } -bool Arm64JITCore::ApplyRelocations(uint64_t GuestEntry, uint64_t CodeEntry, uint64_t CursorEntry, size_t NumRelocations, - const char* EntryRelocations) { - size_t DataIndex {}; - for (size_t j = 0; j < NumRelocations; ++j) { - const FEXCore::CPU::Relocation* Reloc = reinterpret_cast(&EntryRelocations[DataIndex]); - LOGMAN_THROW_A_FMT((DataIndex % alignof(Relocation)) == 0, "Alignment of relocation wasn't adhered to"); +bool Arm64JITCore::ApplyRelocations(uint64_t GuestEntry, std::span Code, std::span Relocations) { + const auto OrigBase = GetBufferBase(); + const auto OrigSize = GetBufferSize(); + const auto OrigOffset = GetCursorOffset(); - switch (Reloc->Header.Type) { + SetBuffer(reinterpret_cast(Code.data()), Code.size_bytes()); + for (auto& Reloc : Relocations) { + switch (Reloc.Header.Type) { case FEXCore::CPU::RelocationTypes::RELOC_NAMED_SYMBOL_LITERAL: { - uint64_t Pointer = GetNamedSymbolLiteral(Reloc->NamedSymbolLiteral.Symbol); + uint64_t Pointer = GetNamedSymbolLiteral(Reloc.NamedSymbolLiteral.Symbol); // Relocation occurs at the cursorEntry + offset relative to that cursor - SetCursorOffset(CursorEntry + Reloc->NamedSymbolLiteral.Offset); + SetCursorOffset(Reloc.NamedSymbolLiteral.Offset); // Generate a literal so we can place it dc64(Pointer); - - DataIndex += sizeof(Reloc->NamedSymbolLiteral); break; } case FEXCore::CPU::RelocationTypes::RELOC_NAMED_THUNK_MOVE: { - uint64_t Pointer = reinterpret_cast(EmitterCTX->ThunkHandler->LookupThunk(Reloc->NamedThunkMove.Symbol)); + uint64_t Pointer = reinterpret_cast(EmitterCTX->ThunkHandler->LookupThunk(Reloc.NamedThunkMove.Symbol)); if (Pointer == ~0ULL) { return false; } // Relocation occurs at the cursorEntry + offset relative to that cursor. - SetCursorOffset(CursorEntry + Reloc->NamedThunkMove.Offset); - LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Register(Reloc->NamedThunkMove.RegisterIndex), Pointer, true); - DataIndex += sizeof(Reloc->NamedThunkMove); + SetCursorOffset(Reloc.NamedThunkMove.Offset); + LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Register(Reloc.NamedThunkMove.RegisterIndex), Pointer, true); break; } case FEXCore::CPU::RelocationTypes::RELOC_GUEST_RIP_MOVE: { @@ -117,18 +114,27 @@ bool Arm64JITCore::ApplyRelocations(uint64_t GuestEntry, uint64_t CodeEntry, uin // XXX: Should spin the relocation list, create a list of guest RIP moves, and ask for them all once, reduces lock contention. uint64_t Pointer = ~0ULL; // EmitterCTX->JITObjectCache->FindRelocatedRIP(Reloc->GuestRIPMove.GuestRIP); if (Pointer == ~0ULL) { + SetBuffer(OrigBase, OrigSize); + SetCursorOffset(OrigOffset); return false; } // Relocation occurs at the cursorEntry + offset relative to that cursor. - SetCursorOffset(CursorEntry + Reloc->GuestRIPMove.Offset); - LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Register(Reloc->GuestRIPMove.RegisterIndex), Pointer, true); - DataIndex += sizeof(Reloc->GuestRIPMove); + SetCursorOffset(Reloc.GuestRIPMove.Offset); + LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Register(Reloc.GuestRIPMove.RegisterIndex), Pointer, true); break; } } } + SetBuffer(OrigBase, OrigSize); + SetCursorOffset(OrigOffset); + return true; } + +fextl::vector Arm64JITCore::TakeRelocations() { + return std::move(Relocations); +} + } // namespace FEXCore::CPU diff --git a/FEXCore/Source/Interface/Core/JIT/DebugData.h b/FEXCore/Source/Interface/Core/JIT/DebugData.h index 86f2aa51b..ffbd9f765 100644 --- a/FEXCore/Source/Interface/Core/JIT/DebugData.h +++ b/FEXCore/Source/Interface/Core/JIT/DebugData.h @@ -2,20 +2,15 @@ #pragma once #include -#include -#include -#include #include #include -#include namespace FEXCore::CPU { union Relocation; } // namespace FEXCore::CPU namespace FEXCore::Core { -struct InternalThreadState; struct DebugDataSubblock { uint32_t HostCodeOffset; uint32_t HostCodeSize; @@ -38,35 +33,3 @@ struct DebugData : public FEXCore::Allocator::FEXAllocOperators { fextl::vector* Relocations; }; } // namespace FEXCore::Core - -namespace FEXCore::Context { -class ContextImpl; -} - -namespace FEXCore::IR { - -struct AOTIRCacheEntry { - fextl::unique_ptr SourcecodeMap; - fextl::string FileId; - fextl::string Filename; -}; - -class AOTIRCaptureCache final { -public: - AOTIRCaptureCache(FEXCore::Context::ContextImpl* ctx) - : CTX {ctx} {} - - bool PostCompileCode(FEXCore::Core::InternalThreadState* Thread, void* CodePtr, uint64_t GuestRIP, uint64_t StartAddr, uint64_t Length, - FEXCore::Core::DebugData* DebugData); - - AOTIRCacheEntry* LoadAOTIRCacheEntry(const fextl::string& filename); - -private: - FEXCore::Context::ContextImpl* CTX; - - std::shared_mutex AOTIRCacheLock; - - fextl::unordered_map AOTIRCache; -}; - -} // namespace FEXCore::IR diff --git a/FEXCore/Source/Interface/Core/JIT/JITClass.h b/FEXCore/Source/Interface/Core/JIT/JITClass.h index 1753a9df9..28119a03e 100644 --- a/FEXCore/Source/Interface/Core/JIT/JITClass.h +++ b/FEXCore/Source/Interface/Core/JIT/JITClass.h @@ -381,7 +381,9 @@ private: fextl::vector Relocations; ///< Relocation code loading - bool ApplyRelocations(uint64_t GuestEntry, uint64_t CodeEntry, uint64_t CursorEntry, size_t NumRelocations, const char* EntryRelocations); + bool ApplyRelocations(uint64_t GuestEntry, std::span Code, std::span); + + fextl::vector TakeRelocations() override; /** @} */ diff --git a/FEXCore/Source/Interface/GDBJIT/GDBJIT.cpp b/FEXCore/Source/Interface/GDBJIT/GDBJIT.cpp index ede808ce1..b19deb686 100644 --- a/FEXCore/Source/Interface/GDBJIT/GDBJIT.cpp +++ b/FEXCore/Source/Interface/GDBJIT/GDBJIT.cpp @@ -42,19 +42,19 @@ void __attribute__((noinline)) __jit_debug_register_code() { namespace FEXCore { -void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry* Entry, uintptr_t VAFileStart, uint64_t GuestRIP, uintptr_t HostEntry, - FEXCore::Core::DebugData* DebugData) { - auto map = Entry->SourcecodeMap.get(); +void GDBJITRegister(FEXCore::ExecutableFileInfo& Entry, uintptr_t VAFileStart, uint64_t GuestRIP, uintptr_t HostEntry, + FEXCore::Core::DebugData& DebugData) { + auto map = Entry.SourcecodeMap.get(); if (map) { auto FileOffset = GuestRIP - VAFileStart; auto Sym = map->FindSymbolMapping(FileOffset); - auto SymName = HLE::SourcecodeSymbolMapping::SymName(Sym, Entry->Filename, HostEntry, FileOffset); + auto SymName = HLE::SourcecodeSymbolMapping::SymName(Sym, Entry.Filename, HostEntry, FileOffset); fextl::vector Lines; - for (const auto& GuestOpcode : DebugData->GuestOpcodes) { + for (const auto& GuestOpcode : DebugData.GuestOpcodes) { auto Line = map->FindLineMapping(GuestRIP + GuestOpcode.GuestEntryOffset - VAFileStart); if (Line) { Lines.push_back({Line->LineNumber, HostEntry + GuestOpcode.HostEntryOffset}); @@ -80,7 +80,7 @@ void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry* Entry, uintptr_t VAFileStart, for (int i = 0; i < info->nblocks; i++) { strncpy(blocks[i].name, SymName.c_str(), 511); blocks[i].start = HostEntry; - blocks[i].end = HostEntry + DebugData->HostCodeSize; + blocks[i].end = HostEntry + DebugData.HostCodeSize; } info->nlines = Lines.size(); @@ -113,7 +113,7 @@ void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry* Entry, uintptr_t VAFileStart, } // namespace FEXCore #else namespace FEXCore { -void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry*, uintptr_t, uint64_t, uintptr_t, FEXCore::Core::DebugData*) { +void GDBJITRegister(FEXCore::ExecutableFileInfo&, uintptr_t, uint64_t, uintptr_t, FEXCore::Core::DebugData&) { ERROR_AND_DIE_FMT("GDBSymbols support not compiled in"); } } // namespace FEXCore diff --git a/FEXCore/Source/Interface/GDBJIT/GDBJIT.h b/FEXCore/Source/Interface/GDBJIT/GDBJIT.h index 11fdc3952..b169260b2 100644 --- a/FEXCore/Source/Interface/GDBJIT/GDBJIT.h +++ b/FEXCore/Source/Interface/GDBJIT/GDBJIT.h @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT +#include #include namespace FEXCore { -void GDBJITRegister(FEXCore::IR::AOTIRCacheEntry* Entry, uintptr_t VAFileStart, uint64_t GuestRIP, uintptr_t HostEntry, - FEXCore::Core::DebugData* DebugData); +void GDBJITRegister(FEXCore::ExecutableFileInfo&, uintptr_t VAFileStart, uint64_t GuestRIP, uintptr_t HostEntry, FEXCore::Core::DebugData&); } diff --git a/FEXCore/include/FEXCore/Core/CodeCache.h b/FEXCore/include/FEXCore/Core/CodeCache.h new file mode 100644 index 000000000..2f2ca50ea --- /dev/null +++ b/FEXCore/include/FEXCore/Core/CodeCache.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +#pragma once + +#include +#include + +#include + +namespace FEXCore { + +namespace Core { + struct InternalThreadState; +} // namespace Core + +namespace HLE { + struct SourcecodeMap; +} // namespace HLE + +// Generic information associated with an executable file. +struct ExecutableFileInfo { + ~ExecutableFileInfo(); + + fextl::unique_ptr SourcecodeMap; + fextl::string FileId; + fextl::string Filename; +}; + +// Information associated with a specific section of an executable file +struct ExecutableFileSectionInfo { + ExecutableFileInfo& FileInfo; + + // Start address that the file is mapped to. + uintptr_t FileStartVA; +}; + +class AbstractCodeCache { +public: + virtual ~AbstractCodeCache() = default; + + /** + * Computes a unique identifier for the referenced binary file to be used for + * generating the code map. + * This identifier is independent of FEX build/runtime configuration and + * stable across FEX updates. + */ + virtual fextl::string ComputeCodeMapId(std::string_view Filename) = 0; + + /** + * Loads a code cache from mapped memory and appends it to the current Core state. + * TODO: Optionally recompiles all contained code blocks at runtime for validation. + */ + virtual void LoadData(Core::InternalThreadState&, std::byte* MappedCacheFile, const ExecutableFileSectionInfo&) = 0; + + /** + * Bundles the current Core state (CodeBuffer, GuestToHostMapping, ...) to a code cache and writes it to the given file descriptor. + * Returns true on success. + */ + virtual bool SaveData(Core::InternalThreadState&, int TargetFD, const ExecutableFileSectionInfo&, uint64_t SerializedBaseAddress) = 0; + + /** + * Function to be called before compiling any code for caching purposes + */ + virtual void InitiateCacheGeneration() = 0; +}; + +} // namespace FEXCore diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index 3fbdb5916..3f12d6017 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -14,7 +15,6 @@ #include namespace FEXCore { -class CodeLoader; struct HostFeatures; class ForkableSharedMutex; class ThunkHandler; @@ -25,17 +25,11 @@ struct CPUState; struct InternalThreadState; } // namespace FEXCore::Core -namespace FEXCore::CPU { -class CPUBackend; -} - namespace FEXCore::HLE { -struct SyscallArguments; class SyscallHandler; } // namespace FEXCore::HLE namespace FEXCore::IR { -struct AOTIRCacheEntry; class IREmitter; } // namespace FEXCore::IR @@ -144,10 +138,7 @@ public: FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::XCRResults RunXCRFunction(uint32_t Function) = 0; FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) = 0; - FEX_DEFAULT_VISIBILITY virtual FEXCore::IR::AOTIRCacheEntry* LoadAOTIRCacheEntry(const fextl::string& Name) = 0; - FEX_DEFAULT_VISIBILITY virtual void UnloadAOTIRCacheEntry(FEXCore::IR::AOTIRCacheEntry* Entry) = 0; - - FEX_DEFAULT_VISIBILITY virtual void FinalizeAOTIRCache() = 0; + virtual AbstractCodeCache& GetCodeCache() = 0; FEX_DEFAULT_VISIBILITY virtual void ClearCodeCache(FEXCore::Core::InternalThreadState* Thread, bool NewCodeBuffer = true) = 0; FEX_DEFAULT_VISIBILITY virtual void InvalidateGuestCodeRange( diff --git a/FEXCore/include/FEXCore/HLE/SourcecodeResolver.h b/FEXCore/include/FEXCore/HLE/SourcecodeResolver.h index 86cc31410..a110924a1 100644 --- a/FEXCore/include/FEXCore/HLE/SourcecodeResolver.h +++ b/FEXCore/include/FEXCore/HLE/SourcecodeResolver.h @@ -9,10 +9,6 @@ #include #include -namespace FEXCore::IR { -struct AOTIRCacheEntry; -} - namespace FEXCore::HLE { struct SourcecodeLineMapping { diff --git a/FEXCore/include/FEXCore/HLE/SyscallHandler.h b/FEXCore/include/FEXCore/HLE/SyscallHandler.h index 9052e02e4..5677b5989 100644 --- a/FEXCore/include/FEXCore/HLE/SyscallHandler.h +++ b/FEXCore/include/FEXCore/HLE/SyscallHandler.h @@ -1,13 +1,11 @@ // SPDX-License-Identifier: MIT #pragma once #include -#include +#include +#include #include - -namespace FEXCore::IR { -struct AOTIRCacheEntry; -} +#include namespace FEXCore::Context { class Context; @@ -51,19 +49,6 @@ struct ExecutableRangeInfo { class SyscallHandler; class SourcecodeResolver; -struct AOTIRCacheEntryLookupResult { - AOTIRCacheEntryLookupResult(FEXCore::IR::AOTIRCacheEntry* Entry, uintptr_t VAFileStart) - : Entry(Entry) - , VAFileStart(VAFileStart) {} - - AOTIRCacheEntryLookupResult(AOTIRCacheEntryLookupResult&&) = default; - - FEXCore::IR::AOTIRCacheEntry* Entry; - uintptr_t VAFileStart; - - friend class SyscallHandler; -}; - class SyscallHandler { public: virtual ~SyscallHandler() = default; @@ -82,7 +67,7 @@ public: virtual void MarkOvercommitRange(uint64_t Start, uint64_t Length) {} virtual void UnmarkOvercommitRange(uint64_t Start, uint64_t Length) {} virtual ExecutableRangeInfo QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) = 0; - virtual AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) = 0; + virtual std::optional LookupExecutableFileSection(Core::InternalThreadState& Thread, uint64_t GuestAddr) = 0; virtual void PreCompile() {} diff --git a/Source/Tools/CodeSizeValidation/Main.cpp b/Source/Tools/CodeSizeValidation/Main.cpp index f751a590e..c80bcbe73 100644 --- a/Source/Tools/CodeSizeValidation/Main.cpp +++ b/Source/Tools/CodeSizeValidation/Main.cpp @@ -460,8 +460,9 @@ public: } // These are no-ops implementations of the SyscallHandler API - FEXCore::HLE::AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) override { - return {0, 0}; + std::optional + LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) override { + return std::nullopt; } FEXCore::HLE::ExecutableRangeInfo QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) override { diff --git a/Source/Tools/CommonTools/DummyHandlers.h b/Source/Tools/CommonTools/DummyHandlers.h index 6bd8ace62..fea414779 100644 --- a/Source/Tools/CommonTools/DummyHandlers.h +++ b/Source/Tools/CommonTools/DummyHandlers.h @@ -22,8 +22,8 @@ public: } // These are no-ops implementations of the SyscallHandler API - FEXCore::HLE::AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) override { - return {0, 0}; + std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState&, uint64_t) override { + return std::nullopt; } FEXCore::HLE::ExecutableRangeInfo QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) override { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index 7f56cb37c..2578ce2f1 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -290,8 +290,8 @@ public: static bool HandleSegfault(FEXCore::Core::InternalThreadState* Thread, int Signal, void* info, void* ucontext); void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override; void InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override; - // AOTIRCacheEntryLookupResult also includes a shared lock guard, so the pointed AOTIRCacheEntry return can be safely used - FEXCore::HLE::AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) final override; + std::optional + LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) final override; FEXCore::HLE::ExecutableRangeInfo QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) override; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 18c4b54da..6d4a09b4f 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -167,19 +167,19 @@ void SyscallHandler::InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, Start, Length); } -// Used for AOT -FEXCore::HLE::AOTIRCacheEntryLookupResult SyscallHandler::LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) { - auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, Thread); +std::optional +SyscallHandler::LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) { + auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, &Thread); // Get the first mapping after GuestAddr, or end // GuestAddr is inclusive // If the write spans two pages, they will be flushed one at a time (generating two faults) auto Entry = VMATracking.FindVMAEntry(GuestAddr); - if (Entry == VMATracking.VMAs.end()) { - return {nullptr, 0}; + if (Entry == VMATracking.VMAs.end() || !Entry->second.Resource) { + return std::nullopt; } - return {Entry->second.Resource ? Entry->second.Resource->AOTIRCacheEntry : nullptr, Entry->second.Base - Entry->second.Offset}; + return FEXCore::ExecutableFileSectionInfo {*Entry->second.Resource->MappedFile, Entry->second.Base - Entry->second.Offset}; } FEXCore::HLE::ExecutableRangeInfo SyscallHandler::QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) { @@ -374,12 +374,13 @@ void SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint6 auto PathLength = FEX::get_fdpath(fd, Tmp); if (PathLength != -1) { - Tmp[PathLength] = '\0'; auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); Resource = &Iter->second; if (Inserted) { - Resource->AOTIRCacheEntry = CTX->LoadAOTIRCacheEntry(fextl::string(Tmp, PathLength)); + Resource->MappedFile = fextl::make_unique(); + Resource->MappedFile->Filename = fextl::string(Tmp, PathLength); + Resource->MappedFile->FileId = CTX->GetCodeCache().ComputeCodeMapId(Resource->MappedFile->Filename); Resource->Iterator = Iter; } } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.cpp index 74c383300..f12736fa3 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.cpp @@ -235,9 +235,6 @@ void VMATracking::DeleteVMARange(FEXCore::Context::Context* CTX, uintptr_t Base, // If linked to a Mapped Resource, remove from linked list and possibly delete the Mapped Resource if (Current->Resource) { if (ListRemove(Current) && Current->Resource != PreservedMappedResource) { - if (Current->Resource->AOTIRCacheEntry) { - CTX->UnloadAOTIRCacheEntry(Current->Resource->AOTIRCacheEntry); - } MappedResources.erase(Current->Resource->Iterator); } } @@ -540,9 +537,6 @@ uintptr_t VMATracking::DeleteSHMRegion(FEXCore::Context::Context* CTX, uintptr_t do { if (Entry->second.Resource == Resource) { if (ListRemove(&Entry->second)) { - if (Entry->second.Resource->AOTIRCacheEntry) { - CTX->UnloadAOTIRCacheEntry(Entry->second.Resource->AOTIRCacheEntry); - } MappedResources.erase(Entry->second.Resource->Iterator); } Entry = VMAs.erase(Entry); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h index 39b3558d5..1aa9ba2da 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h @@ -5,12 +5,9 @@ #include #include +#include #include -namespace FEXCore::IR { -struct AOTIRCacheEntry; -} - namespace FEX::HLE::VMATracking { ///// VMA (Virtual Memory Area) tracking ///// @@ -37,7 +34,7 @@ struct VMAEntry; struct MappedResource { using ContainerType = fextl::map; - FEXCore::IR::AOTIRCacheEntry* AOTIRCacheEntry; + fextl::unique_ptr MappedFile; // Pointer to lowest memory range this file is mapped to VMAEntry* FirstVMA; uint64_t Length; // 0 if not fixed size @@ -87,7 +84,7 @@ struct VMATracking { using VMACIterator = decltype(VMAs)::const_iterator; // Find a VMA entry associated with the memory address. - // Used by `mremap`, and SIGSEGV handler to find previously mapped ranges, and `AOTIR` cache to find cache entries. + // Used by `mremap` and SIGSEGV handler to find previously mapped ranges, and CodeCache to find cache entries. // - Mutex must be at least shared_locked before calling VMACIterator FindVMAEntry(uint64_t GuestAddr) const; diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index 73930de69..cd0ef311e 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -586,8 +586,8 @@ public: return {.NumArgs = 0, .HasReturn = false, .HostSyscallNumber = -1}; } - FEXCore::HLE::AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) override { - return {0, 0}; + std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState&, uint64_t) override { + return std::nullopt; } void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override { diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index 278330eec..bdb2c55b8 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -478,8 +478,8 @@ public: return {.NumArgs = 0, .HasReturn = false, .HostSyscallNumber = -1}; } - FEXCore::HLE::AOTIRCacheEntryLookupResult LookupAOTIRCacheEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) override { - return {0, 0}; + std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState&, uint64_t) override { + return std::nullopt; } void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override { From 70a1d92d9fbd75da7f1108fccdda1c0997d594ff Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Mon, 8 Sep 2025 16:27:59 +0200 Subject: [PATCH 08/10] CodeCache: Drop ComputeCodeMapId member function --- FEXCore/Source/Interface/Context/Context.h | 2 -- FEXCore/Source/Interface/Core/CodeCache.cpp | 16 ---------------- FEXCore/include/FEXCore/Core/CodeCache.h | 8 -------- .../LinuxSyscalls/SyscallsSMCTracking.cpp | 1 - 4 files changed, 27 deletions(-) diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index c9e9a1625..9592d77a0 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -79,8 +79,6 @@ public: ContextImpl& CTX; bool IsGeneratingCache = false; - fextl::string ComputeCodeMapId(std::string_view Filename) override; - void LoadData(Core::InternalThreadState&, std::byte* MappedCacheFile, const ExecutableFileSectionInfo&) override; bool SaveData(Core::InternalThreadState&, int TargetFD, const ExecutableFileSectionInfo&, uint64_t SerializedBaseAddress) override; diff --git a/FEXCore/Source/Interface/Core/CodeCache.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp index 98441d2b3..c6d86d5e0 100644 --- a/FEXCore/Source/Interface/Core/CodeCache.cpp +++ b/FEXCore/Source/Interface/Core/CodeCache.cpp @@ -1,5 +1,4 @@ // SPDX-License-Identifier: MIT -#include #include #include @@ -7,8 +6,6 @@ #include -#include - namespace FEXCore { ExecutableFileInfo::~ExecutableFileInfo() = default; @@ -45,17 +42,4 @@ void CodeCache::PostCompileCode(FEXCore::Core::InternalThreadState& Thread, void } } -fextl::string CodeCache::ComputeCodeMapId(std::string_view Filename) { - auto base_filename = FHU::Filesystem::GetFilename(Filename); - if (!base_filename.empty()) { - auto filename_hash = XXH3_64bits(Filename.data(), Filename.size()); - - return fextl::fmt::format("{}-{:016x}-{}{}{}", base_filename, filename_hash, - (CTX.Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) ? 'S' : 's', CTX.Config.TSOEnabled ? 'T' : 't', - CTX.Config.ABILocalFlags ? 'L' : 'l'); - } - - return nullptr; -} - } // namespace FEXCore::Context diff --git a/FEXCore/include/FEXCore/Core/CodeCache.h b/FEXCore/include/FEXCore/Core/CodeCache.h index 2f2ca50ea..3a16fbeaa 100644 --- a/FEXCore/include/FEXCore/Core/CodeCache.h +++ b/FEXCore/include/FEXCore/Core/CodeCache.h @@ -37,14 +37,6 @@ class AbstractCodeCache { public: virtual ~AbstractCodeCache() = default; - /** - * Computes a unique identifier for the referenced binary file to be used for - * generating the code map. - * This identifier is independent of FEX build/runtime configuration and - * stable across FEX updates. - */ - virtual fextl::string ComputeCodeMapId(std::string_view Filename) = 0; - /** * Loads a code cache from mapped memory and appends it to the current Core state. * TODO: Optionally recompiles all contained code blocks at runtime for validation. diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 6d4a09b4f..1bebc4df8 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -380,7 +380,6 @@ void SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint6 if (Inserted) { Resource->MappedFile = fextl::make_unique(); Resource->MappedFile->Filename = fextl::string(Tmp, PathLength); - Resource->MappedFile->FileId = CTX->GetCodeCache().ComputeCodeMapId(Resource->MappedFile->Filename); Resource->Iterator = Iter; } } From 6c86ffdeb7ca9e308155ef03de9e89d5a5bcbf28 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Mon, 8 Sep 2025 16:32:27 +0200 Subject: [PATCH 09/10] Core: Remove redundant nullptr check --- FEXCore/Source/Interface/Core/Core.cpp | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 97f2ea851..74357316c 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -785,27 +785,25 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_ if (Config.BlockJITNaming()) { auto FragmentBasePtr = CompiledCode.BlockBegin; - if (DebugData) { - auto GuestRIPLookup = SyscallHandler->LookupExecutableFileSection(*Thread, GuestRIP); + auto GuestRIPLookup = SyscallHandler->LookupExecutableFileSection(*Thread, GuestRIP); - if (DebugData->Subblocks.size()) { - for (auto& Subblock : DebugData->Subblocks) { - auto BlockBasePtr = FragmentBasePtr + Subblock.HostCodeOffset; - if (GuestRIPLookup) { - Symbols.Register(Thread->SymbolBuffer.get(), BlockBasePtr, CompiledCode.Size, GuestRIPLookup->FileInfo.Filename, - GuestRIP - GuestRIPLookup->FileStartVA); - } else { - Symbols.Register(Thread->SymbolBuffer.get(), BlockBasePtr, GuestRIP, Subblock.HostCodeSize); - } - } - } else { + if (DebugData->Subblocks.size()) { + for (auto& Subblock : DebugData->Subblocks) { + auto BlockBasePtr = FragmentBasePtr + Subblock.HostCodeOffset; if (GuestRIPLookup) { - Symbols.Register(Thread->SymbolBuffer.get(), FragmentBasePtr, CompiledCode.Size, GuestRIPLookup->FileInfo.Filename, + Symbols.Register(Thread->SymbolBuffer.get(), BlockBasePtr, CompiledCode.Size, GuestRIPLookup->FileInfo.Filename, GuestRIP - GuestRIPLookup->FileStartVA); } else { - Symbols.Register(Thread->SymbolBuffer.get(), FragmentBasePtr, GuestRIP, CompiledCode.Size); + Symbols.Register(Thread->SymbolBuffer.get(), BlockBasePtr, GuestRIP, Subblock.HostCodeSize); } } + } else { + if (GuestRIPLookup) { + Symbols.Register(Thread->SymbolBuffer.get(), FragmentBasePtr, CompiledCode.Size, GuestRIPLookup->FileInfo.Filename, + GuestRIP - GuestRIPLookup->FileStartVA); + } else { + Symbols.Register(Thread->SymbolBuffer.get(), FragmentBasePtr, GuestRIP, CompiledCode.Size); + } } } From b5b8ff01d574e88edf94decef35514b1a5e6e5a6 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Mon, 8 Sep 2025 16:36:56 +0200 Subject: [PATCH 10/10] CodeCache: Move LibraryJITNaming and GDBSymbols checks to Core --- FEXCore/Source/Interface/Context/Context.h | 2 -- FEXCore/Source/Interface/Core/CodeCache.cpp | 18 ------------------ FEXCore/Source/Interface/Core/Core.cpp | 16 ++++++++++++++-- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index 9592d77a0..8f0ab1cde 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -85,8 +85,6 @@ public: void InitiateCacheGeneration() override { IsGeneratingCache = true; } - - void PostCompileCode(FEXCore::Core::InternalThreadState& Thread, void* CodePtr, uint64_t GuestRIP, Core::DebugData&); }; class ContextImpl final : public FEXCore::Context::Context, public CPU::CodeBufferManager { diff --git a/FEXCore/Source/Interface/Core/CodeCache.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp index c6d86d5e0..adac418a3 100644 --- a/FEXCore/Source/Interface/Core/CodeCache.cpp +++ b/FEXCore/Source/Interface/Core/CodeCache.cpp @@ -1,11 +1,8 @@ // SPDX-License-Identifier: MIT #include -#include #include -#include - namespace FEXCore { ExecutableFileInfo::~ExecutableFileInfo() = default; @@ -27,19 +24,4 @@ bool CodeCache::SaveData(Core::InternalThreadState& Thread, int fd, const Execut return true; } -void CodeCache::PostCompileCode(FEXCore::Core::InternalThreadState& Thread, void* CodePtr, uint64_t GuestRIP, FEXCore::Core::DebugData& DebugData) { - if (CTX.Config.LibraryJITNaming() || CTX.Config.GDBSymbols()) { - auto MappedSection = CTX.SyscallHandler->LookupExecutableFileSection(Thread, GuestRIP); - if (MappedSection) { - if (CTX.Config.LibraryJITNaming()) { - CTX.Symbols.RegisterNamedRegion(Thread.SymbolBuffer.get(), CodePtr, DebugData.HostCodeSize, MappedSection->FileInfo.Filename); - } - - if (CTX.Config.GDBSymbols()) { - GDBJITRegister(MappedSection->FileInfo, MappedSection->FileStartVA, GuestRIP, (uintptr_t)CodePtr, DebugData); - } - } - } -} - } // namespace FEXCore::Context diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 74357316c..7608fcec4 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -18,6 +18,7 @@ $end_info$ #include "Interface/Core/JIT/JITClass.h" #include "Interface/Core/Dispatcher/Dispatcher.h" #include "Interface/Core/X86Tables/X86Tables.h" +#include #include "Interface/IR/IR.h" #include "Interface/IR/IREmitter.h" #include "Interface/IR/Passes/RegisterAllocationPass.h" @@ -807,13 +808,24 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_ } } + if (Config.LibraryJITNaming() || Config.GDBSymbols()) { + auto MappedSection = SyscallHandler->LookupExecutableFileSection(*Thread, GuestRIP); + if (MappedSection) { + if (Config.LibraryJITNaming()) { + Symbols.RegisterNamedRegion(Thread->SymbolBuffer.get(), CodePtr, DebugData->HostCodeSize, MappedSection->FileInfo.Filename); + } + + if (Config.GDBSymbols()) { + GDBJITRegister(MappedSection->FileInfo, MappedSection->FileStartVA, GuestRIP, (uintptr_t)CodePtr, *DebugData); + } + } + } + // Clear any relocations that might have been generated if (!CodeCache.IsGeneratingCache) { Thread->CPUBackend->ClearRelocations(); } - CodeCache.PostCompileCode(*Thread, CompiledCode.BlockBegin, GuestRIP, *DebugData.get()); - if (NeedsAddGuestCodeRanges) { // Track in the guest to host map all entrypoints for all pages the compiled block touches, if any page didn't previously // contain code, inform the frontend so it can setup SMC detection.