mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-10 12:39:59 +00:00
PPU LLVM: multithread compilation
This commit is contained in:
parent
ef21e2253f
commit
6561ddae41
@ -11,6 +11,7 @@
|
|||||||
#include "StrFmt.h"
|
#include "StrFmt.h"
|
||||||
#include "File.h"
|
#include "File.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "mutex.h"
|
||||||
#include "VirtualMemory.h"
|
#include "VirtualMemory.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@ -32,8 +33,8 @@
|
|||||||
|
|
||||||
#include "JIT.h"
|
#include "JIT.h"
|
||||||
|
|
||||||
// Global LLVM context (thread-unsafe)
|
// Memory manager mutex
|
||||||
llvm::LLVMContext g_llvm_ctx;
|
shared_mutex s_mutex;
|
||||||
|
|
||||||
// Size of virtual memory area reserved: 512 MB
|
// Size of virtual memory area reserved: 512 MB
|
||||||
static const u64 s_memory_size = 0x20000000;
|
static const u64 s_memory_size = 0x20000000;
|
||||||
@ -41,6 +42,10 @@ static const u64 s_memory_size = 0x20000000;
|
|||||||
// Try to reserve a portion of virtual memory in the first 2 GB address space beforehand, if possible.
|
// Try to reserve a portion of virtual memory in the first 2 GB address space beforehand, if possible.
|
||||||
static void* const s_memory = []() -> void*
|
static void* const s_memory = []() -> void*
|
||||||
{
|
{
|
||||||
|
llvm::InitializeNativeTarget();
|
||||||
|
llvm::InitializeNativeTargetAsmPrinter();
|
||||||
|
LLVMLinkInMCJIT();
|
||||||
|
|
||||||
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
||||||
{
|
{
|
||||||
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
||||||
@ -52,28 +57,47 @@ static void* const s_memory = []() -> void*
|
|||||||
return utils::memory_reserve(s_memory_size);
|
return utils::memory_reserve(s_memory_size);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
static void* s_next;
|
static void* s_next = s_memory;
|
||||||
|
|
||||||
// Code section
|
|
||||||
static u8* s_code_addr;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
|
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
|
||||||
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Reset memory manager
|
||||||
|
extern void jit_finalize()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
for (auto&& unwind : s_unwind)
|
||||||
|
{
|
||||||
|
if (!RtlDeleteFunctionTable(unwind.data()))
|
||||||
|
{
|
||||||
|
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_unwind.clear();
|
||||||
|
#else
|
||||||
|
// TODO: unregister EH frames if necessary
|
||||||
|
#endif
|
||||||
|
|
||||||
|
utils::memory_decommit(s_memory, s_memory_size);
|
||||||
|
|
||||||
|
s_next = s_memory;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper class
|
// Helper class
|
||||||
struct MemoryManager final : llvm::RTDyldMemoryManager
|
struct MemoryManager : llvm::RTDyldMemoryManager
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, std::uintptr_t>& m_link;
|
std::unordered_map<std::string, std::uintptr_t>& m_link;
|
||||||
|
|
||||||
std::array<u8, 16>* m_tramps;
|
std::array<u8, 16>* m_tramps{};
|
||||||
|
|
||||||
|
u8* m_code_addr{}; // TODO
|
||||||
|
|
||||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
|
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
|
||||||
: m_link(table)
|
: m_link(table)
|
||||||
, m_tramps(nullptr)
|
|
||||||
{
|
{
|
||||||
s_next = s_memory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] static void null()
|
[[noreturn]] static void null()
|
||||||
@ -104,6 +128,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
// Verify address for small code model
|
// Verify address for small code model
|
||||||
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
||||||
{
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// Allocate memory for trampolines
|
// Allocate memory for trampolines
|
||||||
if (!m_tramps)
|
if (!m_tramps)
|
||||||
{
|
{
|
||||||
@ -137,6 +164,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
|
|
||||||
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||||
{
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// Simple allocation
|
// Simple allocation
|
||||||
const u64 next = ::align((u64)s_next + size, 4096);
|
const u64 next = ::align((u64)s_next + size, 4096);
|
||||||
|
|
||||||
@ -147,7 +177,7 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
utils::memory_commit(s_next, size, utils::protection::wx);
|
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||||
s_code_addr = (u8*)s_next;
|
m_code_addr = (u8*)s_next;
|
||||||
|
|
||||||
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), s_next, size, align);
|
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), s_next, size, align);
|
||||||
return (u8*)std::exchange(s_next, (void*)next);
|
return (u8*)std::exchange(s_next, (void*)next);
|
||||||
@ -155,6 +185,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
|
|
||||||
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||||
{
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// Simple allocation
|
// Simple allocation
|
||||||
const u64 next = ::align((u64)s_next + size, 4096);
|
const u64 next = ::align((u64)s_next + size, 4096);
|
||||||
|
|
||||||
@ -177,6 +210,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
|
|
||||||
virtual bool finalizeMemory(std::string* = nullptr) override
|
virtual bool finalizeMemory(std::string* = nullptr) override
|
||||||
{
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// TODO: make only read-only sections read-only
|
// TODO: make only read-only sections read-only
|
||||||
//#ifdef _WIN32
|
//#ifdef _WIN32
|
||||||
// DWORD op;
|
// DWORD op;
|
||||||
@ -192,6 +228,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// Use s_memory as a BASE, compute the difference
|
// Use s_memory as a BASE, compute the difference
|
||||||
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
||||||
|
|
||||||
@ -224,30 +263,18 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
|
|
||||||
return RTDyldMemoryManager::deregisterEHFrames(addr, load_addr, size);
|
return RTDyldMemoryManager::deregisterEHFrames(addr, load_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
~MemoryManager()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
for (auto&& unwind : s_unwind)
|
|
||||||
{
|
|
||||||
if (!RtlDeleteFunctionTable(unwind.data()))
|
|
||||||
{
|
|
||||||
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s_unwind.clear();
|
|
||||||
#else
|
|
||||||
// TODO: unregister EH frames if necessary
|
|
||||||
#endif
|
|
||||||
|
|
||||||
utils::memory_decommit(s_memory, s_memory_size);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper class
|
// Helper class
|
||||||
struct EventListener final : llvm::JITEventListener
|
struct EventListener : llvm::JITEventListener
|
||||||
{
|
{
|
||||||
|
MemoryManager& m_mem;
|
||||||
|
|
||||||
|
EventListener(MemoryManager& mem)
|
||||||
|
: m_mem(mem)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -275,8 +302,11 @@ struct EventListener final : llvm::JITEventListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// Use s_memory as a BASE, compute the difference
|
// Use s_memory as a BASE, compute the difference
|
||||||
const u64 code_diff = (u64)s_code_addr - (u64)s_memory;
|
const u64 code_diff = (u64)m_mem.m_code_addr - (u64)s_memory;
|
||||||
|
|
||||||
// Fix RUNTIME_FUNCTION records (.pdata section)
|
// Fix RUNTIME_FUNCTION records (.pdata section)
|
||||||
for (auto& rf : rfs)
|
for (auto& rf : rfs)
|
||||||
@ -292,8 +322,6 @@ struct EventListener final : llvm::JITEventListener
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static EventListener s_listener;
|
|
||||||
|
|
||||||
// Helper class
|
// Helper class
|
||||||
class ObjectCache final : public llvm::ObjectCache
|
class ObjectCache final : public llvm::ObjectCache
|
||||||
{
|
{
|
||||||
@ -334,15 +362,10 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_linkage_info, std::string _cpu)
|
jit_compiler::jit_compiler(const std::unordered_map<std::string, std::uintptr_t>& _link, std::string _cpu)
|
||||||
: m_link(std::move(init_linkage_info))
|
: m_link(std::move(_link))
|
||||||
, m_cpu(std::move(_cpu))
|
, m_cpu(std::move(_cpu))
|
||||||
{
|
{
|
||||||
// Initialization
|
|
||||||
llvm::InitializeNativeTarget();
|
|
||||||
llvm::InitializeNativeTargetAsmPrinter();
|
|
||||||
LLVMLinkInMCJIT();
|
|
||||||
|
|
||||||
if (m_cpu.empty())
|
if (m_cpu.empty())
|
||||||
{
|
{
|
||||||
m_cpu = llvm::sys::getHostCPUName();
|
m_cpu = llvm::sys::getHostCPUName();
|
||||||
@ -350,22 +373,42 @@ jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_
|
|||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("", g_llvm_ctx))
|
if (m_link.empty())
|
||||||
.setErrorStr(&result)
|
{
|
||||||
.setMCJITMemoryManager(std::make_unique<MemoryManager>(m_link))
|
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null_", m_context))
|
||||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
.setErrorStr(&result)
|
||||||
.setCodeModel(llvm::CodeModel::Small)
|
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||||
.setMCPU(m_cpu)
|
.setCodeModel(llvm::CodeModel::Small)
|
||||||
.create());
|
.setMCPU(m_cpu)
|
||||||
|
.create());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto mem = std::make_unique<MemoryManager>(m_link);
|
||||||
|
m_jit_el = std::make_unique<EventListener>(*mem);
|
||||||
|
|
||||||
|
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null", m_context))
|
||||||
|
.setErrorStr(&result)
|
||||||
|
.setMCJITMemoryManager(std::move(mem))
|
||||||
|
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||||
|
.setCodeModel(llvm::CodeModel::Small)
|
||||||
|
.setMCPU(m_cpu)
|
||||||
|
.create());
|
||||||
|
|
||||||
|
if (m_engine)
|
||||||
|
{
|
||||||
|
m_engine->RegisterJITEventListener(m_jit_el.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_engine)
|
if (!m_engine)
|
||||||
{
|
{
|
||||||
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_engine->RegisterJITEventListener(&s_listener);
|
jit_compiler::~jit_compiler()
|
||||||
|
{
|
||||||
LOG_SUCCESS(GENERAL, "LLVM: JIT initialized (%s)", m_cpu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string& path)
|
void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string& path)
|
||||||
@ -385,13 +428,23 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void jit_compiler::fin(const std::string& path)
|
void jit_compiler::fin()
|
||||||
{
|
{
|
||||||
m_engine->finalizeObject();
|
m_engine->finalizeObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
void jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
u64 jit_compiler::get(const std::string & name)
|
||||||
{
|
{
|
||||||
|
return m_engine->getFunctionAddress(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, u64> jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
||||||
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, u64> result;
|
||||||
|
|
||||||
std::size_t size = 0;
|
std::size_t size = 0;
|
||||||
|
|
||||||
for (auto&& pair : data)
|
for (auto&& pair : data)
|
||||||
@ -405,15 +458,13 @@ void jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
|||||||
for (auto&& pair : data)
|
for (auto&& pair : data)
|
||||||
{
|
{
|
||||||
std::memcpy(s_next, pair.second.data(), pair.second.size());
|
std::memcpy(s_next, pair.second.data(), pair.second.size());
|
||||||
m_link.emplace(pair.first, (u64)s_next);
|
result.emplace(pair.first, (u64)s_next);
|
||||||
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
|
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
s_next = (void*)::align((u64)s_next, 4096);
|
s_next = (void*)::align((u64)s_next, 4096);
|
||||||
}
|
|
||||||
|
|
||||||
jit_compiler::~jit_compiler()
|
return result;
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
#include "restore_new.h"
|
#include "restore_new.h"
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@ -20,48 +21,45 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "define_new_memleakdetect.h"
|
#include "define_new_memleakdetect.h"
|
||||||
|
|
||||||
extern llvm::LLVMContext g_llvm_ctx;
|
|
||||||
|
|
||||||
// Temporary compiler interface
|
// Temporary compiler interface
|
||||||
class jit_compiler final
|
class jit_compiler final
|
||||||
{
|
{
|
||||||
|
// Local LLVM context
|
||||||
|
llvm::LLVMContext m_context;
|
||||||
|
|
||||||
|
// JIT Event Listener
|
||||||
|
std::unique_ptr<struct EventListener> m_jit_el;
|
||||||
|
|
||||||
// Execution instance
|
// Execution instance
|
||||||
std::unique_ptr<llvm::ExecutionEngine> m_engine;
|
std::unique_ptr<llvm::ExecutionEngine> m_engine;
|
||||||
|
|
||||||
// Linkage cache
|
// Link table
|
||||||
std::unordered_map<std::string, u64> m_link;
|
std::unordered_map<std::string, u64> m_link;
|
||||||
|
|
||||||
// Compiled functions
|
|
||||||
std::unordered_map<std::string, u64> m_map;
|
|
||||||
|
|
||||||
// Arch
|
// Arch
|
||||||
std::string m_cpu;
|
std::string m_cpu;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
jit_compiler(std::unordered_map<std::string, u64>, std::string _cpu);
|
jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu);
|
||||||
~jit_compiler();
|
~jit_compiler();
|
||||||
|
|
||||||
|
// Get LLVM context
|
||||||
|
auto& get_context()
|
||||||
|
{
|
||||||
|
return m_context;
|
||||||
|
}
|
||||||
|
|
||||||
// Add module
|
// Add module
|
||||||
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
||||||
|
|
||||||
// Finalize
|
// Finalize
|
||||||
void fin(const std::string& path);
|
void fin();
|
||||||
|
|
||||||
// Add functions directly (name -> code)
|
|
||||||
void add(std::unordered_map<std::string, std::string>);
|
|
||||||
|
|
||||||
// Get compiled function address
|
// Get compiled function address
|
||||||
u64 get(const std::string& name) const
|
u64 get(const std::string& name);
|
||||||
{
|
|
||||||
const auto found = m_map.find(name);
|
|
||||||
|
|
||||||
if (found != m_map.end())
|
|
||||||
{
|
|
||||||
return found->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_engine->getFunctionAddress(name);
|
// Add functions directly to the memory manager (name -> code)
|
||||||
}
|
static std::unordered_map<std::string, u64> add(std::unordered_map<std::string, std::string>);
|
||||||
|
|
||||||
// Get CPU info
|
// Get CPU info
|
||||||
const std::string& cpu() const
|
const std::string& cpu() const
|
||||||
|
@ -1819,6 +1819,37 @@ void thread_ctrl::test()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thread_ctrl::set_native_priority(int priority)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE _this_thread = GetCurrentThread();
|
||||||
|
INT native_priority = THREAD_PRIORITY_NORMAL;
|
||||||
|
|
||||||
|
switch (priority)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
native_priority = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
native_priority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetThreadPriority(_this_thread, native_priority);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_ctrl::set_ideal_processor_core(int core)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE _this_thread = GetCurrentThread();
|
||||||
|
SetThreadIdealProcessor(_this_thread, core);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
named_thread::named_thread()
|
named_thread::named_thread()
|
||||||
{
|
{
|
||||||
|
@ -230,6 +230,9 @@ public:
|
|||||||
|
|
||||||
thread_ctrl::start(out, std::forward<F>(func));
|
thread_ctrl::start(out, std::forward<F>(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_native_priority(int priority);
|
||||||
|
static void set_ideal_processor_core(int core);
|
||||||
};
|
};
|
||||||
|
|
||||||
class named_thread
|
class named_thread
|
||||||
|
@ -182,34 +182,3 @@ std::string cpu_thread::dump() const
|
|||||||
{
|
{
|
||||||
return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load());
|
return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_thread::set_native_priority(int priority)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
HANDLE _this_thread = GetCurrentThread();
|
|
||||||
INT native_priority = THREAD_PRIORITY_NORMAL;
|
|
||||||
|
|
||||||
switch (priority)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
native_priority = THREAD_PRIORITY_ABOVE_NORMAL;
|
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
native_priority = THREAD_PRIORITY_BELOW_NORMAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetThreadPriority(_this_thread, native_priority);
|
|
||||||
#endif // _WIN32
|
|
||||||
}
|
|
||||||
|
|
||||||
void cpu_thread::set_ideal_processor_core(int core)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
HANDLE _this_thread = GetCurrentThread();
|
|
||||||
SetThreadIdealProcessor(_this_thread, core);
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -65,10 +65,6 @@ public:
|
|||||||
|
|
||||||
// Callback for cpu_flag::suspend
|
// Callback for cpu_flag::suspend
|
||||||
virtual void cpu_sleep() {}
|
virtual void cpu_sleep() {}
|
||||||
|
|
||||||
//native scheduler tweaks
|
|
||||||
void set_native_priority(int priority);
|
|
||||||
void set_ideal_processor_core(int core);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline cpu_thread* get_current_cpu_thread() noexcept
|
inline cpu_thread* get_current_cpu_thread() noexcept
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include "Modules/cellMsgDialog.h"
|
#include "Modules/cellMsgDialog.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
#include <cfenv>
|
#include <cfenv>
|
||||||
#include "Utilities/GSL.h"
|
#include "Utilities/GSL.h"
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ const ppu_decoder<ppu_interpreter_fast> s_ppu_interpreter_fast;
|
|||||||
|
|
||||||
extern void ppu_initialize();
|
extern void ppu_initialize();
|
||||||
extern void ppu_initialize(const ppu_module& info);
|
extern void ppu_initialize(const ppu_module& info);
|
||||||
static void ppu_initialize2(const ppu_module& info);
|
static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name);
|
||||||
extern void ppu_execute_syscall(ppu_thread& ppu, u64 code);
|
extern void ppu_execute_syscall(ppu_thread& ppu, u64 code);
|
||||||
|
|
||||||
// Get pointer to executable cache
|
// Get pointer to executable cache
|
||||||
@ -1059,11 +1060,8 @@ extern void ppu_initialize(const ppu_module& info)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LLVM_AVAILABLE
|
// Link table
|
||||||
using namespace llvm;
|
static const std::unordered_map<std::string, u64> s_link_table = []()
|
||||||
|
|
||||||
// Initialize JIT compiler
|
|
||||||
if (!fxm::check<jit_compiler>())
|
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, u64> link_table
|
std::unordered_map<std::string, u64> link_table
|
||||||
{
|
{
|
||||||
@ -1098,15 +1096,32 @@ extern void ppu_initialize(const ppu_module& info)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fxm::make<jit_compiler>(std::move(link_table), g_cfg.core.llvm_cpu);
|
return link_table;
|
||||||
|
}();
|
||||||
|
|
||||||
|
#ifdef LLVM_AVAILABLE
|
||||||
|
// Initialize compiler
|
||||||
|
jit_compiler jit(s_link_table, g_cfg.core.llvm_cpu);
|
||||||
|
|
||||||
|
// Compiler mutex
|
||||||
|
semaphore<> jmutex;
|
||||||
|
|
||||||
|
// Initialize semaphore with the max number of threads
|
||||||
|
semaphore<INT32_MAX> jcores(std::thread::hardware_concurrency());
|
||||||
|
|
||||||
|
if (!jcores.get())
|
||||||
|
{
|
||||||
|
// Min value 1
|
||||||
|
jcores.post();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
// Worker threads
|
||||||
|
std::vector<std::thread> jthreads;
|
||||||
|
|
||||||
// Split module into fragments <= 1 MiB
|
// Split module into fragments <= 1 MiB
|
||||||
std::size_t fpos = 0;
|
std::size_t fpos = 0;
|
||||||
|
|
||||||
ppu_module part;
|
ppu_module part;
|
||||||
part.funcs.reserve(65536);
|
|
||||||
|
|
||||||
while (fpos < info.funcs.size())
|
while (fpos < info.funcs.size())
|
||||||
{
|
{
|
||||||
@ -1115,12 +1130,13 @@ extern void ppu_initialize(const ppu_module& info)
|
|||||||
std::size_t bsize = 0;
|
std::size_t bsize = 0;
|
||||||
|
|
||||||
part.funcs.clear();
|
part.funcs.clear();
|
||||||
|
part.funcs.reserve(16000);
|
||||||
|
|
||||||
while (fpos < info.funcs.size())
|
while (fpos < info.funcs.size())
|
||||||
{
|
{
|
||||||
auto& func = info.funcs[fpos];
|
auto& func = info.funcs[fpos];
|
||||||
|
|
||||||
if (bsize + func.size > 1024 * 1024 && bsize)
|
if (bsize + func.size > 256 * 1024 && bsize)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1158,13 +1174,90 @@ extern void ppu_initialize(const ppu_module& info)
|
|||||||
part.name.append("+0");
|
part.name.append("+0");
|
||||||
}
|
}
|
||||||
|
|
||||||
ppu_initialize2(part);
|
// Compute module hash
|
||||||
|
std::string obj_name;
|
||||||
|
{
|
||||||
|
sha1_context ctx;
|
||||||
|
u8 output[20];
|
||||||
|
sha1_starts(&ctx);
|
||||||
|
|
||||||
|
for (const auto& func : part.funcs)
|
||||||
|
{
|
||||||
|
if (func.size == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const be_t<u32> addr = func.addr;
|
||||||
|
const be_t<u32> size = func.size;
|
||||||
|
sha1_update(&ctx, reinterpret_cast<const u8*>(&addr), sizeof(addr));
|
||||||
|
sha1_update(&ctx, reinterpret_cast<const u8*>(&size), sizeof(size));
|
||||||
|
|
||||||
|
for (const auto& block : func.blocks)
|
||||||
|
{
|
||||||
|
if (block.second == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha1_update(&ctx, vm::ps3::_ptr<const u8>(block.first), block.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
sha1_update(&ctx, vm::ps3::_ptr<const u8>(func.addr), func.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
sha1_finish(&ctx, output);
|
||||||
|
|
||||||
|
// Version, module name and hash: vX-liblv2.sprx-0123456789ABCDEF.obj
|
||||||
|
fmt::append(obj_name, "v1%s-%016X-%s.obj", part.name, reinterpret_cast<be_t<u64>&>(output), jit.cpu());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Emu.IsStopped())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check object file
|
||||||
|
if (fs::is_file(Emu.GetCachePath() + obj_name))
|
||||||
|
{
|
||||||
|
semaphore_lock lock(jmutex);
|
||||||
|
ppu_initialize2(jit, part, Emu.GetCachePath(), obj_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create worker thread for compilation
|
||||||
|
jthreads.emplace_back([&jit, &jmutex, &jcores, obj_name = obj_name, part = std::move(part)]()
|
||||||
|
{
|
||||||
|
// Set low priority
|
||||||
|
thread_ctrl::set_native_priority(-1);
|
||||||
|
|
||||||
|
// Allocate "core"
|
||||||
|
{
|
||||||
|
semaphore_lock jlock(jcores);
|
||||||
|
|
||||||
|
if (Emu.IsStopped())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use another JIT instance
|
||||||
|
jit_compiler jit2({}, g_cfg.core.llvm_cpu);
|
||||||
|
ppu_initialize2(jit2, part, Emu.GetCachePath(), obj_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with original JIT instance
|
||||||
|
semaphore_lock lock(jmutex);
|
||||||
|
ppu_initialize2(jit, part, Emu.GetCachePath(), obj_name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LLVM_AVAILABLE
|
// Join worker threads
|
||||||
const auto jit = fxm::check_unlocked<jit_compiler>();
|
for (auto& thread : jthreads)
|
||||||
|
{
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
jit->fin(Emu.GetCachePath());
|
jit.fin();
|
||||||
|
|
||||||
// Get and install function addresses
|
// Get and install function addresses
|
||||||
for (const auto& func : info.funcs)
|
for (const auto& func : info.funcs)
|
||||||
@ -1175,74 +1268,27 @@ extern void ppu_initialize(const ppu_module& info)
|
|||||||
{
|
{
|
||||||
if (block.second)
|
if (block.second)
|
||||||
{
|
{
|
||||||
ppu_ref(block.first) = ::narrow<u32>(jit->get(fmt::format("__0x%x", block.first)));
|
ppu_ref(block.first) = ::narrow<u32>(jit.get(fmt::format("__0x%x", block.first)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ppu_initialize2(const ppu_module& module_part)
|
static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name)
|
||||||
{
|
{
|
||||||
if (Emu.IsStopped())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute module hash
|
|
||||||
std::string obj_name;
|
|
||||||
{
|
|
||||||
sha1_context ctx;
|
|
||||||
u8 output[20];
|
|
||||||
sha1_starts(&ctx);
|
|
||||||
|
|
||||||
for (const auto& func : module_part.funcs)
|
|
||||||
{
|
|
||||||
if (func.size == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const be_t<u32> addr = func.addr;
|
|
||||||
const be_t<u32> size = func.size;
|
|
||||||
sha1_update(&ctx, reinterpret_cast<const u8*>(&addr), sizeof(addr));
|
|
||||||
sha1_update(&ctx, reinterpret_cast<const u8*>(&size), sizeof(size));
|
|
||||||
|
|
||||||
for (const auto& block : func.blocks)
|
|
||||||
{
|
|
||||||
if (block.second == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1_update(&ctx, vm::ps3::_ptr<const u8>(block.first), block.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1_update(&ctx, vm::ps3::_ptr<const u8>(func.addr), func.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1_finish(&ctx, output);
|
|
||||||
|
|
||||||
// Version, module name and hash: vX-liblv2.sprx-0123456789ABCDEF.obj
|
|
||||||
fmt::append(obj_name, "b1%s-%016X.obj", module_part.name, reinterpret_cast<be_t<u64>&>(output));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef LLVM_AVAILABLE
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
const auto jit = fxm::get<jit_compiler>();
|
|
||||||
|
|
||||||
// Create LLVM module
|
// Create LLVM module
|
||||||
std::unique_ptr<Module> module = std::make_unique<Module>(obj_name, g_llvm_ctx);
|
std::unique_ptr<Module> module = std::make_unique<Module>(obj_name, jit.get_context());
|
||||||
|
|
||||||
// Initialize target
|
// Initialize target
|
||||||
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
|
||||||
|
|
||||||
// Initialize translator
|
// Initialize translator
|
||||||
std::unique_ptr<PPUTranslator> translator = std::make_unique<PPUTranslator>(g_llvm_ctx, module.get(), 0);
|
std::unique_ptr<PPUTranslator> translator = std::make_unique<PPUTranslator>(jit.get_context(), module.get(), 0);
|
||||||
|
|
||||||
// Define some types
|
// Define some types
|
||||||
const auto _void = Type::getVoidTy(g_llvm_ctx);
|
const auto _void = Type::getVoidTy(jit.get_context());
|
||||||
const auto _func = FunctionType::get(_void, {translator->GetContextType()->getPointerTo()}, false);
|
const auto _func = FunctionType::get(_void, {translator->GetContextType()->getPointerTo()}, false);
|
||||||
|
|
||||||
// Initialize function list
|
// Initialize function list
|
||||||
@ -1258,7 +1304,7 @@ static void ppu_initialize2(const ppu_module& module_part)
|
|||||||
std::shared_ptr<MsgDialogBase> dlg;
|
std::shared_ptr<MsgDialogBase> dlg;
|
||||||
|
|
||||||
// Check cached file
|
// Check cached file
|
||||||
if (!fs::is_file(Emu.GetCachePath() + obj_name))
|
if (!fs::is_file(cache_path + obj_name))
|
||||||
{
|
{
|
||||||
legacy::FunctionPassManager pm(module.get());
|
legacy::FunctionPassManager pm(module.get());
|
||||||
|
|
||||||
@ -1404,7 +1450,7 @@ static void ppu_initialize2(const ppu_module& module_part)
|
|||||||
if (g_cfg.core.llvm_logs)
|
if (g_cfg.core.llvm_logs)
|
||||||
{
|
{
|
||||||
out << *module; // print IR
|
out << *module; // print IR
|
||||||
fs::file(Emu.GetCachePath() + obj_name + ".log", fs::rewrite).write(out.str());
|
fs::file(cache_path + obj_name + ".log", fs::rewrite).write(out.str());
|
||||||
result.clear();
|
result.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1418,11 +1464,7 @@ static void ppu_initialize2(const ppu_module& module_part)
|
|||||||
LOG_NOTICE(PPU, "LLVM: %zu functions generated", module->getFunctionList().size());
|
LOG_NOTICE(PPU, "LLVM: %zu functions generated", module->getFunctionList().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access JIT compiler
|
// Load or compile module
|
||||||
if (const auto jit = fxm::check_unlocked<jit_compiler>())
|
jit.add(std::move(module), cache_path);
|
||||||
{
|
#endif // LLVM_AVAILABLE
|
||||||
// Load or compile module
|
|
||||||
jit->add(std::move(module), Emu.GetCachePath());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
@ -147,13 +147,13 @@ void SPUThread::on_spawn()
|
|||||||
auto half_count = core_count / 2;
|
auto half_count = core_count / 2;
|
||||||
auto assigned_secondary_core = ((g_num_spu_threads % half_count) * 2) + 1;
|
auto assigned_secondary_core = ((g_num_spu_threads % half_count) * 2) + 1;
|
||||||
|
|
||||||
set_ideal_processor_core(assigned_secondary_core);
|
thread_ctrl::set_ideal_processor_core(assigned_secondary_core);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_cfg.core.lower_spu_priority)
|
if (g_cfg.core.lower_spu_priority)
|
||||||
{
|
{
|
||||||
set_native_priority(-1);
|
thread_ctrl::set_native_priority(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_num_spu_threads++;
|
g_num_spu_threads++;
|
||||||
|
@ -681,6 +681,11 @@ void Emulator::Stop()
|
|||||||
{
|
{
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LLVM_AVAILABLE
|
||||||
|
extern void jit_finalize();
|
||||||
|
jit_finalize();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 error_code::error_report(const fmt_type_info* sup, u64 arg)
|
s32 error_code::error_report(const fmt_type_info* sup, u64 arg)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user