FEXServer: Implement automatic code cache generation

This commit is contained in:
Tony Wasserka committed 2025-12-22 21:20:22 +01:00
1 parent 50939df6b5
commit 7ba6cfe651
1 file changed
+61 -10
+61 -10
View File
@@ -24,6 +24,7 @@
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <vector>
#include <xxhash.h>
@@ -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<FEXCore::ExecutableFileInfo> Result;
static std::map<FEXCore::ExecutableFileInfo, NeedsCacheRefresh> AggregateCodeMaps(const FEXCore::ExecutableFileInfo& MainFileId, bool HasMultiblock) {
std::map<FEXCore::ExecutableFileInfo, NeedsCacheRefresh> 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<FEXCore::ExecutableFileInfo> Empty;
// Update code map and queue for cache generation
std::map<FEXCore::ExecutableFileInfo, NeedsCacheRefresh> 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<char* const*>(&ExecveArgs[0]));
};
void HandleSocketData(fasio::tcp_socket& Socket) {
std::vector<uint8_t> 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 {