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:
@@ -543,9 +543,15 @@ public:
|
||||
// Add module (path to obj cache dir)
|
||||
void add(std::unique_ptr<llvm::Module> _module, const std::string& path);
|
||||
|
||||
// Returns false after LLVM fatal recovery. The compiler must be discarded.
|
||||
bool try_add(std::unique_ptr<llvm::Module> _module, const std::string& path, std::string& error);
|
||||
|
||||
// Add module (not cached)
|
||||
void add(std::unique_ptr<llvm::Module> _module);
|
||||
|
||||
// Returns false after LLVM fatal recovery. The compiler must be discarded.
|
||||
bool try_add(std::unique_ptr<llvm::Module> _module, std::string& error);
|
||||
|
||||
// Add object (path to obj file)
|
||||
bool add(const std::string& path);
|
||||
|
||||
@@ -558,6 +564,9 @@ public:
|
||||
// Finalize
|
||||
void fin();
|
||||
|
||||
// Returns false after LLVM fatal recovery. The compiler must be discarded.
|
||||
bool try_fin(std::string& error);
|
||||
|
||||
// Get compiled function address
|
||||
u64 get(const std::string& name);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -3044,6 +3044,32 @@ void thread_ctrl::set_name(std::string name)
|
||||
report_fatal_error(reason);
|
||||
}
|
||||
|
||||
void thread_ctrl::silent_exit() noexcept
|
||||
{
|
||||
if (const auto _this = g_tls_this_thread)
|
||||
{
|
||||
g_tls_error_callback();
|
||||
|
||||
u64 _self = _this->finalize(thread_state::errored);
|
||||
|
||||
if (_self == umax)
|
||||
{
|
||||
// Unused, detached thread support remnant
|
||||
delete _this;
|
||||
}
|
||||
|
||||
thread_base::finalize(umax);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
_endthreadex(0);
|
||||
#else
|
||||
pthread_exit(nullptr);
|
||||
#endif
|
||||
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void thread_ctrl::detect_cpu_layout()
|
||||
{
|
||||
if (!g_native_core_layout.compare_and_swap_test(native_core_arrangement::undefined, native_core_arrangement::generic))
|
||||
|
||||
@@ -315,6 +315,9 @@ public:
|
||||
// Exit.
|
||||
[[noreturn]] static void emergency_exit(std::string_view reason);
|
||||
|
||||
// Exit the current named thread as errored without reporting a fatal error.
|
||||
[[noreturn]] static void silent_exit() noexcept;
|
||||
|
||||
// Get current thread (may be nullptr)
|
||||
static thread_base* get_current()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user