SPU LLVM: Retry ARM64 TBL2 register scavenger failures

- Some SPU programs inexplicably fail to compile when TBL2/TBX2 are used.
- As an insane workaround, first try to compile with TBL2/TBX2, if LLVM crashes while compiling, try to compile the same program without TBL2/TBX2.
This commit is contained in:
Malcolm
2026-05-02 16:10:24 -04:00
committed by Elad
parent dff29a7864
commit a87d175295
8 changed files with 394 additions and 44 deletions
+106
View File
@@ -12,6 +12,10 @@
#include <charconv>
#if defined(__APPLE__)
#include <pthread.h>
#endif
LOG_CHANNEL(jit_log, "JIT");
#ifdef LLVM_AVAILABLE
@@ -50,6 +54,44 @@ LOG_CHANNEL(jit_log, "JIT");
#include "Emu/CPU/Backends/AArch64/AArch64Common.h"
#endif
namespace
{
thread_local std::string* g_llvm_fatal_message = nullptr;
template <typename F>
bool run_recoverable_llvm(F&& func, std::string& error)
{
error.clear();
// Run LLVM codegen in a disposable thread. If LLVM invokes the fatal
// handler, only this helper thread exits.
named_thread worker("LLVM JIT", [&]()
{
#if defined(__APPLE__)
pthread_jit_write_protect_np(false);
#endif
g_llvm_fatal_message = &error;
std::forward<F>(func)();
g_llvm_fatal_message = nullptr;
#if defined(__APPLE__)
pthread_jit_write_protect_np(true);
#endif
});
worker();
const bool result = static_cast<thread_state>(worker) == thread_state::finished;
if (!result && error.empty())
{
error = "LLVM crash recovery invoked";
}
return result;
}
}
const bool jit_initialize = []() -> bool
{
llvm::InitializeNativeTarget();
@@ -649,6 +691,13 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
llvm::install_fatal_error_handler([](void*, const char* msg, bool)
{
const std::string_view out = msg ? msg : "";
if (g_llvm_fatal_message)
{
*g_llvm_fatal_message = out;
thread_ctrl::silent_exit();
}
fmt::throw_exception("LLVM Emergency Exit Invoked: '%s'", out);
}, nullptr);
@@ -788,6 +837,33 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> _module, const std::string&
}
}
bool jit_compiler::try_add(std::unique_ptr<llvm::Module> _module, const std::string& path, std::string& error)
{
ObjectCache cache{path, this};
m_engine->setObjectCache(&cache);
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
if (!run_recoverable_llvm([&]()
{
m_engine->generateCodeForModule(ptr);
}, error))
{
return false;
}
m_engine->setObjectCache(nullptr);
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
return true;
}
void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
{
const auto ptr = _module.get();
@@ -801,6 +877,28 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
}
}
bool jit_compiler::try_add(std::unique_ptr<llvm::Module> _module, std::string& error)
{
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
if (!run_recoverable_llvm([&]()
{
m_engine->generateCodeForModule(ptr);
}, error))
{
return false;
}
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
return true;
}
bool jit_compiler::add(const std::string& path)
{
auto cache = ObjectCache::load(path);
@@ -852,6 +950,14 @@ void jit_compiler::fin()
m_engine->finalizeObject();
}
bool jit_compiler::try_fin(std::string& error)
{
return run_recoverable_llvm([&]()
{
m_engine->finalizeObject();
}, error);
}
u64 jit_compiler::get(const std::string& name)
{
return m_engine->getGlobalValueAddress(name);