From db3817a2607aea426d263ea530367830d42f55ae Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 22 Aug 2026 18:31:22 -0700 Subject: [PATCH] FEXCore: Fixes ever shrinking JIT code buffer I accidentally replaced a couple usages of `AllocatedSize` with `GetAllocatedSize()`. This resulted in a JIT buffer that ran out of space would actually allocate a slightly smaller buffer each time, and then it cascades downwards resulting in catastrophic performance. Fix the use in `SharedCodeBufferManager.cpp` and `Core.cpp` which were incorrect and renames the function to be more explicit. --- FEXCore/Source/Interface/Core/CodeCache.cpp | 10 +++++----- FEXCore/Source/Interface/Core/Core.cpp | 2 +- .../Source/Interface/Core/SharedCodeBufferManager.cpp | 2 +- .../Source/Interface/Core/SharedCodeBufferManager.h | 7 ++++++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/FEXCore/Source/Interface/Core/CodeCache.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp index f43b9b82c..c19872c6e 100644 --- a/FEXCore/Source/Interface/Core/CodeCache.cpp +++ b/FEXCore/Source/Interface/Core/CodeCache.cpp @@ -314,7 +314,7 @@ bool CodeCache::SaveData(Core::InternalThreadState& Thread, int fd, const Execut std::ranges::copy(GIT_HASH, header.FEXVersion); header.NumBlocks = LookupCache.BlockList.size(); header.NumCodePages = LookupCache.CodePages.size(); - header.CodeBufferSize = FEXCore::AlignUp(CodeBuffer->GetAllocatedSize(), Utils::FEX_PAGE_SIZE); + header.CodeBufferSize = FEXCore::AlignUp(CodeBuffer->AllocatedSpaceUsed(), Utils::FEX_PAGE_SIZE); header.NumRelocations = Relocations.size(); header.SerializedBaseAddress = SerializedBaseAddress; ::write(fd, &header, sizeof(header)); @@ -362,7 +362,7 @@ bool CodeCache::SaveData(Core::InternalThreadState& Thread, int fd, const Execut // Dump the host code (relocated for position-independent serialization) std::span CodeBufferData(reinterpret_cast(CodeBuffer->GetBufferBase()), - reinterpret_cast(CodeBuffer->GetBufferBase()) + CodeBuffer->GetAllocatedSize()); + reinterpret_cast(CodeBuffer->GetBufferBase()) + CodeBuffer->AllocatedSpaceUsed()); if (!ApplyCodeRelocations(SerializedBaseAddress, CodeBufferData, Relocations, 0, true)) { LOGMAN_THROW_A_FMT(false, "Failed to apply code relocations"); return false; @@ -427,7 +427,7 @@ void CodeCache::Validate(const ExecutableFileSectionInfo& Section, fextl::set NewCodeBuffer->UsableSize()) { ValidationCTX->ClearCodeCache(ValidationThread.get()); NewCodeBuffer = ValidationCTX->GetLatest(); - LogMan::Msg::IFmt("Increased cache validation code buffer size to {} MiB", NewCodeBuffer->GetAllocatedSize() / 1024 / 1024); + LogMan::Msg::IFmt("Increased cache validation code buffer size to {} MiB", NewCodeBuffer->TotalAllocationSize() / 1024 / 1024); } std::span CodeBufferRangeRef = @@ -448,10 +448,10 @@ void CodeCache::Validate(const ExecutableFileSectionInfo& Section, fextl::setGetAllocatedSize() <= CodeBufferRangeRef.size()) { + if (NewCodeBuffer->AllocatedSpaceUsed() <= CodeBufferRangeRef.size()) { // Reference compilation produced fewer bytes than our cache, so validation is going to fail. // Make sure we don't output any garbage bytes though. - CodeBufferRangeRef = CodeBufferRangeRef.subspan(0, NewCodeBuffer->GetAllocatedSize()); + CodeBufferRangeRef = CodeBufferRangeRef.subspan(0, NewCodeBuffer->AllocatedSpaceUsed()); } auto [Mismatch, _] = std::mismatch(CodeBufferRangeRef.begin(), CodeBufferRangeRef.end(), CachedCode.begin()); diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 5e258ff0f..f62aa5771 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -483,7 +483,7 @@ void ContextImpl::LockBeforeFork(FEXCore::Core::InternalThreadState* Thread) { void ContextImpl::OnCodeBufferAllocated(const fextl::shared_ptr& Buffer) { if (Config.GlobalJITNaming()) { - Symbols.RegisterJITSpace(Buffer->GetBufferBase(), Buffer->GetAllocatedSize()); + Symbols.RegisterJITSpace(Buffer->GetBufferBase(), Buffer->TotalAllocationSize()); } { diff --git a/FEXCore/Source/Interface/Core/SharedCodeBufferManager.cpp b/FEXCore/Source/Interface/Core/SharedCodeBufferManager.cpp index 5f8552c90..d63ec3ef2 100644 --- a/FEXCore/Source/Interface/Core/SharedCodeBufferManager.cpp +++ b/FEXCore/Source/Interface/Core/SharedCodeBufferManager.cpp @@ -88,7 +88,7 @@ fextl::shared_ptr SharedCodeBufferManager::StartLargerCodeBuffer() { return GetLatest(); } - auto NewCodeBufferSize = GetLatest()->GetAllocatedSize(); + auto NewCodeBufferSize = GetLatest()->TotalAllocationSize(); NewCodeBufferSize = std::min(NewCodeBufferSize * 2, MAX_CODE_SIZE); return AllocateNew(NewCodeBufferSize); } diff --git a/FEXCore/Source/Interface/Core/SharedCodeBufferManager.h b/FEXCore/Source/Interface/Core/SharedCodeBufferManager.h index ebfaa1a73..c047eb847 100644 --- a/FEXCore/Source/Interface/Core/SharedCodeBufferManager.h +++ b/FEXCore/Source/Interface/Core/SharedCodeBufferManager.h @@ -71,8 +71,13 @@ struct CodeBuffer { return AllocatedSize - FEXCore::Utils::FEX_PAGE_SIZE; } + // Returns the full size of the buffer, including the guard page. + size_t TotalAllocationSize() const { + return AllocatedSize; + } + // Returns the num of bytes currently allocated from the allocator. - size_t GetAllocatedSize() const { + size_t AllocatedSpaceUsed() const { return CodeBufferOffset - Ptr; }