From 7ba6cfe65181bbf85784e9eae576f894cf18dfe2 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Tue, 16 Dec 2025 11:32:03 +0100 Subject: [PATCH] FEXServer: Implement automatic code cache generation --- Source/Tools/FEXServer/ProcessPipe.cpp | 71 ++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/Source/Tools/FEXServer/ProcessPipe.cpp b/Source/Tools/FEXServer/ProcessPipe.cpp index 3d7dfbeaf..26f189d6d 100644 --- a/Source/Tools/FEXServer/ProcessPipe.cpp +++ b/Source/Tools/FEXServer/ProcessPipe.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -371,7 +372,7 @@ static void WriteNewCodeMap(const FEXCore::ExecutableFileInfo& File, const std:: // List the main executable and all used libraries OutputCodeMap.AppendSetMainExecutable(File); - for (auto& Dependency : Dependencies) { + for (auto& [Dependency, _] : Dependencies) { OutputCodeMap.AppendLibraryLoad(Dependency); } } else { @@ -384,27 +385,37 @@ static void WriteNewCodeMap(const FEXCore::ExecutableFileInfo& File, const std:: } } +enum class NeedsCacheRefresh { + No, + Yes, +}; + /** * Checks and processes new code maps generated by FEX for the given application. * * Processed code maps are merged into the reference code map and deleted afterwards. + * + * The returned map is a list of all dependencies of the main executables discovered, + * associated with a flag to indicate need for cache regeneration. */ -static void AggregateCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMultiblock) { - std::set Result; +static std::map AggregateCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMultiblock) { + std::map Result; // Read all dependencies discovered in previous runs { auto MainFileCodeMapPath = fmt::format("{}/ready/{}", CodeMapDirectory, FEXCore::CodeMap::GetBaseFilename(MainFileId, !HasMultiblock)); std::ifstream MainFileCodeMap(MainFileCodeMapPath, std::ios_base::binary); for (auto& [FileId, Contents] : FEXCore::CodeMap::ParseCodeMap(MainFileCodeMap)) { - Result.emplace(nullptr, FileId, Contents.Filename); + Result.emplace(std::piecewise_construct, std::forward_as_tuple(nullptr, FileId, Contents.Filename), + std::forward_as_tuple(NeedsCacheRefresh::No)); } } // Accumulate information from new code maps auto IncomingCodeMap = ImportPendingCodeMaps(MainFileId, HasMultiblock); for (auto& [File, _] : IncomingCodeMap) { - Result.emplace(nullptr, File.FileId, File.Filename); + Result.emplace(std::piecewise_construct, std::forward_as_tuple(nullptr, File.FileId, File.Filename), + std::forward_as_tuple(NeedsCacheRefresh::No)); } // For each referenced library, add referenced offsets to that library's reference code map @@ -425,12 +436,40 @@ static void AggregateCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, boo } } - // Update code map - std::set Empty; + // Update code map and queue for cache generation + std::map Empty; WriteNewCodeMap(File, OutputName, Blocks, true, File.FileId == MainFileId.FileId ? Result : Empty); + Result.at(File) = NeedsCacheRefresh::Yes; } + + return Result; } +int32_t EmbedSubprocess(const char* path, char* const* args) { + pid_t pid = fork(); + if (pid == 0) { + execvp(path, args); + _exit(-1); + } else { + int32_t Status {}; + while (waitpid(pid, &Status, 0) == -1 && errno == EINTR) + ; + if (WIFEXITED(Status)) { + return (int8_t)WEXITSTATUS(Status); + } + } + + return -1; +} + +/** + * Spawn a FEXOfflineCompiler instance to generate a code cache from the given code map + */ +static int RunOfflineCompiler(const char* CodeMap) { + const char* ExecveArgs[] = {"FEXOfflineCompiler", "generate", CodeMap, nullptr}; + return EmbedSubprocess("FEXOfflineCompiler", const_cast(&ExecveArgs[0])); +}; + void HandleSocketData(fasio::tcp_socket& Socket) { std::vector Data(1500); @@ -554,10 +593,22 @@ void HandleSocketData(fasio::tcp_socket& Socket) { FEXCore::ExecutableFileInfo MainFileId = {nullptr, filename_hash, fextl::string(Tmp, TmpLen)}; fmt::print("Requested {}cache generation for {}\n", HasMultiblock ? "" : "nomb-", MainFileId.Filename); - // Update code maps - AggregateCodeMaps(MainFileId, HasMultiblock); + // Update code maps; any update necessitates an update of the corresponding cache + auto Binaries = AggregateCodeMaps(MainFileId, HasMultiblock); - // TODO: Implement + // Trigger offline-compile for each binary that needs it + for (const auto& [File, NeedsRefresh] : Binaries) { + if (NeedsRefresh != NeedsCacheRefresh::Yes) { + continue; + } + + const auto BinaryName = (std::string)FEXCore::CodeMap::GetBaseFilename(File, !HasMultiblock); + fmt::println("Generating cache for {}", BinaryName); + int Status = RunOfflineCompiler(fmt::format("{}/ready/{}", CodeMapDirectory, BinaryName).c_str()); + if (Status != 0) { + fmt::println("ERROR: Cache generation failed with status {}", Status); + } + } FEXServerClient::FEXServerResultPacket Res { .Header {