From 0749477eb941c29ed8e31e4457f9c3124613b67e Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Tue, 2 Sep 2025 18:24:34 +0200 Subject: [PATCH] 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 {