LLVM: splitting and caching

This commit is contained in:
Nekotekina 2017-02-26 18:56:31 +03:00
parent 4aab8db420
commit 0eb6bf6a67
11 changed files with 362 additions and 148 deletions

View File

@ -68,16 +68,16 @@ static u8* s_unwind_info;
static u64 s_unwind_size;
#ifdef _WIN32
static std::vector<RUNTIME_FUNCTION> s_unwind; // Custom .pdata section replacement
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
#endif
// Helper class
struct MemoryManager final : llvm::RTDyldMemoryManager
{
std::unordered_map<std::string, std::uintptr_t> table;
std::unordered_map<std::string, std::uintptr_t>& m_link;
MemoryManager(std::unordered_map<std::string, std::uintptr_t>&& table)
: table(std::move(table))
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
: m_link(table)
{
}
@ -95,9 +95,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
return addr;
}
const auto found = table.find(name);
const auto found = m_link.find(name);
if (found != table.end())
if (found != m_link.end())
{
return found->second;
}
@ -131,7 +131,7 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
s_code_addr = (u8*)m_next;
s_code_size = size;
LOG_SUCCESS(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
return (u8*)std::exchange(m_next, (void*)next);
}
@ -161,21 +161,21 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
return nullptr;
}
LOG_SUCCESS(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
LOG_NOTICE(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
return (u8*)std::exchange(m_next, (void*)next);
}
virtual bool finalizeMemory(std::string* = nullptr) override
{
// TODO: make only read-only sections read-only
#ifdef _WIN32
DWORD op;
VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
#else
::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
#endif
//#ifdef _WIN32
// DWORD op;
// VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
// VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
//#else
// ::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
// ::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
//#endif
return false;
}
@ -197,11 +197,16 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
~MemoryManager()
{
#ifdef _WIN32
if (!RtlDeleteFunctionTable(s_unwind.data()))
for (auto&& unwind : s_unwind)
{
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
if (!RtlDeleteFunctionTable(unwind.data()))
{
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
}
}
s_unwind.clear();
if (!VirtualFree(s_memory, 0, MEM_DECOMMIT))
{
LOG_FATAL(GENERAL, "VirtualFree(%p) failed! Error %u", s_memory, GetLastError());
@ -223,36 +228,44 @@ private:
// Helper class
struct EventListener final : llvm::JITEventListener
{
std::string path;
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
{
const llvm::StringRef elf = obj.getData();
fs::file(fs::get_config_dir() + "LLVM.obj", fs::rewrite)
.write(elf.data(), elf.size());
if (!path.empty())
{
const llvm::StringRef elf = obj.getData();
fs::file(path, fs::rewrite).write(elf.data(), elf.size());
}
}
};
static EventListener s_listener;
jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unordered_map<std::string, std::uintptr_t>&& table)
jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_linkage_info)
: m_link(std::move(init_linkage_info))
{
verify(HERE), s_memory;
std::string result;
const auto module_ptr = _module.get();
// Initialization
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
LLVMLinkInMCJIT();
const auto _cpu = llvm::sys::getHostCPUName();
m_cpu = llvm::sys::getHostCPUName();
m_engine.reset(llvm::EngineBuilder(std::move(_module))
if (m_cpu == "skylake")
{
m_cpu = "haswell";
}
std::string result;
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("", g_llvm_ctx))
.setErrorStr(&result)
.setMCJITMemoryManager(std::make_unique<MemoryManager>(std::move(table)))
.setMCJITMemoryManager(std::make_unique<MemoryManager>(m_link))
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setCodeModel((u64)s_memory <= 0x60000000 ? llvm::CodeModel::Small : llvm::CodeModel::Large) // TODO
.setMCPU(_cpu == "skylake" ? "haswell" : _cpu)
.setMCPU(m_cpu)
.create());
if (!m_engine)
@ -262,8 +275,45 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
m_engine->setProcessAllSections(true); // ???
m_engine->RegisterJITEventListener(&s_listener);
}
void jit_compiler::load(std::unique_ptr<llvm::Module> module, std::unique_ptr<llvm::object::ObjectFile> object)
{
s_listener.path.clear();
auto* module_ptr = module.get();
m_engine->addModule(std::move(module));
m_engine->addObjectFile(std::move(object));
m_engine->finalizeObject();
m_map.clear();
for (auto& func : module_ptr->functions())
{
const std::string& name = func.getName();
if (!m_link.count(name))
{
// Register compiled function
m_map[name] = m_engine->getFunctionAddress(name);
}
}
init();
}
void jit_compiler::make(std::unique_ptr<llvm::Module> module, std::string path)
{
s_listener.path = std::move(path);
auto* module_ptr = module.get();
m_engine->addModule(std::move(module));
m_engine->finalizeObject();
m_map.clear();
for (auto& func : module_ptr->functions())
{
if (!func.empty())
@ -278,6 +328,11 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
func.deleteBody();
}
init();
}
void jit_compiler::init()
{
#ifdef _WIN32
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
std::set<u64> func_set;
@ -290,8 +345,8 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
const u64 base = (u64)s_memory;
const u8* bits = s_unwind_info;
s_unwind.clear();
s_unwind.reserve(m_map.size());
std::vector<RUNTIME_FUNCTION> unwind;
unwind.reserve(m_map.size());
for (const u64 addr : func_set)
{
@ -304,7 +359,7 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
uw.BeginAddress = static_cast<u32>(addr - base);
uw.EndAddress = static_cast<u32>(next - base);
uw.UnwindData = static_cast<u32>((u64)bits - base);
s_unwind.emplace_back(uw);
unwind.emplace_back(uw);
// Parse .xdata UNWIND_INFO record
const u8 flags = *bits++; // Version and flags
@ -327,14 +382,16 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
{
LOG_ERROR(GENERAL, "LLVM: .xdata analysis failed! (%p != %p)", s_unwind_info + s_unwind_size, bits);
}
else if (!RtlAddFunctionTable(s_unwind.data(), (DWORD)s_unwind.size(), base))
else if (!RtlAddFunctionTable(unwind.data(), (DWORD)unwind.size(), base))
{
LOG_ERROR(GENERAL, "RtlAddFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
}
else
{
LOG_SUCCESS(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
LOG_NOTICE(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
}
s_unwind.emplace_back(std::move(unwind));
#endif
}

View File

@ -31,10 +31,25 @@ class jit_compiler final
// Compiled functions
std::unordered_map<std::string, std::uintptr_t> m_map;
// Linkage cache
std::unordered_map<std::string, std::uintptr_t> m_link;
// Arch
std::string m_cpu;
// Internal
void init();
public:
jit_compiler(std::unique_ptr<llvm::Module>&&, std::unordered_map<std::string, std::uintptr_t>&&);
jit_compiler(std::unordered_map<std::string, std::uintptr_t>);
~jit_compiler();
// Compile module
void make(std::unique_ptr<llvm::Module>, std::string);
// Load object
void load(std::unique_ptr<llvm::Module>, std::unique_ptr<llvm::object::ObjectFile>);
// Get compiled function address
std::uintptr_t get(const std::string& name) const
{
@ -47,6 +62,12 @@ public:
return 0;
}
// Get CPU info
const std::string& cpu() const
{
return m_cpu;
}
};
#endif

View File

@ -1136,9 +1136,11 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
// Convert map to vector (destructive)
std::vector<ppu_function> result;
for (auto&& func : funcs)
for (auto&& pair : funcs)
{
result.emplace_back(std::move(func.second));
auto& func = pair.second;
LOG_TRACE(PPU, "Function __0x%x (size=0x%x, toc=0x%x, attr %#x)", func.addr, func.size, func.toc, func.attr);
result.emplace_back(std::move(func));
}
LOG_NOTICE(PPU, "Function analysis: %zu functions (%zu enqueued)", result.size(), func_queue.size());

View File

@ -35,6 +35,13 @@ struct ppu_function
std::set<u32> called_from; // Set of called functions
};
// PPU Module Information
struct ppu_module
{
std::string name;
std::vector<ppu_function> funcs;
};
// Aux
struct ppu_pattern
{

View File

@ -117,6 +117,8 @@ cfg::set_entry g_cfg_load_libs(cfg::root.core, "Load libraries");
extern std::string ppu_get_function_name(const std::string& module, u32 fnid);
extern std::string ppu_get_variable_name(const std::string& module, u32 vnid);
extern void ppu_register_range(u32 addr, u32 size);
extern void ppu_initialize(const ppu_module& info);
extern void ppu_initialize();
extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
@ -735,7 +737,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
}
}
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf)
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& name)
{
std::vector<std::pair<u32, u32>> segments;
std::vector<std::pair<u32, u32>> sections;
@ -931,7 +933,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf)
prx->start.set(prx->specials[0xbc9a0086]);
prx->stop.set(prx->specials[0xab779874]);
prx->exit.set(prx->specials[0x3ab9a95e]);
prx->name = name;
return prx;
}
@ -953,9 +955,6 @@ void ppu_load_exec(const ppu_exec_object& elf)
// Section info (optional)
std::vector<std::pair<u32, u32>> sections;
// Functions
std::vector<ppu_function> exec_set;
// TLS information
u32 tls_vaddr = 0;
u32 tls_fsize = 0;
@ -1114,7 +1113,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
// Initialize process
std::vector<u32> start_funcs;
std::vector<std::shared_ptr<lv2_prx>> loaded_modules;
// Load modules
const std::string& lle_dir = vfs::get("/dev_flash/sys/external");
@ -1125,7 +1124,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
if (obj == elf_error::ok)
{
start_funcs.push_back(ppu_load_prx(obj)->start.addr());
loaded_modules.push_back(ppu_load_prx(obj, "liblv2.sprx"));
}
else
{
@ -1142,16 +1141,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
{
LOG_WARNING(LOADER, "Loading library: %s", name);
const auto prx = ppu_load_prx(obj);
// Register start function
if (prx->start)
{
start_funcs.push_back(prx->start.addr());
}
// Add functions
exec_set.insert(exec_set.end(), prx->funcs.begin(), prx->funcs.end());
auto prx = ppu_load_prx(obj, name);
if (prx->funcs.empty())
{
@ -1162,6 +1152,8 @@ void ppu_load_exec(const ppu_exec_object& elf)
// TODO: fix arguments
ppu_validate(lle_dir + '/' + name, prx->funcs, prx->funcs[0].addr);
}
loaded_modules.emplace_back(std::move(prx));
}
else
{
@ -1292,18 +1284,15 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
}
// Analyse executable
std::vector<ppu_function> main_funcs = ppu_analyse(segments, sections, 0);
{
// Analyse executable
std::vector<ppu_function> main_funcs = ppu_analyse(segments, sections, 0);
ppu_validate(vfs::get(Emu.GetPath()), main_funcs, 0);
ppu_validate(vfs::get(Emu.GetPath()), main_funcs, 0);
// Append
exec_set.insert(exec_set.cend(),
std::make_move_iterator(main_funcs.begin()),
std::make_move_iterator(main_funcs.end()));
// Share function list
fxm::make<std::vector<ppu_function>>(std::move(exec_set));
// Share function list
fxm::make<std::vector<ppu_function>>(std::move(main_funcs));
}
// Set SDK version
g_ps3_sdk_version = sdk_version;
@ -1344,13 +1333,18 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
// Run start functions
for (u32 func : start_funcs)
for (const auto& prx : loaded_modules)
{
if (!prx->start)
{
continue;
}
// Reset arguments, run module entry point function
ppu->cmd_list
({
{ ppu_cmd::set_args, 2 }, u64{0}, u64{0},
{ ppu_cmd::lle_call, func },
{ ppu_cmd::lle_call, prx->start.addr() },
});
}

View File

@ -4,6 +4,8 @@
#include "PPUCallback.h"
#include "ErrorCodes.h"
#include <map>
// Generate FNID or VNID for given name
extern u32 ppu_generate_id(const char* name);
@ -41,8 +43,8 @@ public:
task_stack on_load;
task_stack on_unload;
std::unordered_map<u32, ppu_static_function> functions;
std::unordered_map<u32, ppu_static_variable> variables;
std::map<u32, ppu_static_function> functions;
std::map<u32, ppu_static_variable> variables;
public:
ppu_static_module(const char* name);

View File

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "Utilities/Config.h"
#include "Utilities/VirtualMemory.h"
#include "Crypto/sha1.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
@ -9,6 +10,7 @@
#include "PPUAnalyser.h"
#include "PPUModule.h"
#include "lv2/sys_sync.h"
#include "lv2/sys_prx.h"
#ifdef LLVM_AVAILABLE
#include "restore_new.h"
@ -93,7 +95,8 @@ cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder"
const ppu_decoder<ppu_interpreter_precise> s_ppu_interpreter_precise;
const ppu_decoder<ppu_interpreter_fast> s_ppu_interpreter_fast;
static void ppu_initialize();
extern void ppu_initialize();
extern void ppu_initialize(const ppu_module& info);
extern void ppu_execute_syscall(ppu_thread& ppu, u64 code);
extern void ppu_execute_function(ppu_thread& ppu, u32 index);
@ -679,27 +682,27 @@ static void ppu_trace(u64 addr)
LOG_NOTICE(PPU, "Trace: 0x%llx", addr);
}
static u32 ppu_lwarx(u32 addr)
extern u32 ppu_lwarx(ppu_thread& ppu, u32 addr)
{
be_t<u32> reg_value;
vm::reservation_acquire(&reg_value, addr, sizeof(reg_value));
return reg_value;
}
static u64 ppu_ldarx(u32 addr)
extern u64 ppu_ldarx(ppu_thread& ppu, u32 addr)
{
be_t<u64> reg_value;
vm::reservation_acquire(&reg_value, addr, sizeof(reg_value));
return reg_value;
}
static bool ppu_stwcx(u32 addr, u32 reg_value)
extern bool ppu_stwcx(ppu_thread& ppu, u32 addr, u32 reg_value)
{
const be_t<u32> data = reg_value;
return vm::reservation_update(addr, &data, sizeof(data));
}
static bool ppu_stdcx(u32 addr, u64 reg_value)
extern bool ppu_stdcx(ppu_thread& ppu, u32 addr, u64 reg_value)
{
const be_t<u64> data = reg_value;
return vm::reservation_update(addr, &data, sizeof(data));
@ -716,9 +719,14 @@ static bool adde_carry(u64 a, u64 b, bool c)
#endif
}
static void ppu_initialize()
extern void ppu_initialize()
{
const auto _funcs = fxm::get_always<std::vector<ppu_function>>();
const auto _funcs = fxm::withdraw<std::vector<ppu_function>>();
if (!_funcs)
{
return;
}
if (g_cfg_ppu_decoder.get() != ppu_decoder_type::llvm || _funcs->empty())
{
@ -739,38 +747,145 @@ static void ppu_initialize()
return;
}
std::unordered_map<std::string, std::uintptr_t> link_table
std::size_t fpos = 0;
while (fpos < _funcs->size())
{
{ "__mptr", (u64)&vm::g_base_addr },
{ "__cptr", (u64)&s_ppu_compiled },
{ "__trap", (u64)&ppu_trap },
{ "__end", (u64)&ppu_unreachable },
{ "__check", (u64)&ppu_check },
{ "__trace", (u64)&ppu_trace },
{ "__hlecall", (u64)&ppu_execute_function },
{ "__syscall", (u64)&ppu_execute_syscall },
{ "__get_tb", (u64)&get_timebased_time },
{ "__lwarx", (u64)&ppu_lwarx },
{ "__ldarx", (u64)&ppu_ldarx },
{ "__stwcx", (u64)&ppu_stwcx },
{ "__stdcx", (u64)&ppu_stdcx },
{ "__adde_get_ca", (u64)&adde_carry },
{ "__vexptefp", (u64)&sse_exp2_ps },
{ "__vlogefp", (u64)&sse_log2_ps },
{ "__vperm", (u64)&sse_altivec_vperm },
{ "__lvsl", (u64)&sse_altivec_lvsl },
{ "__lvsr", (u64)&sse_altivec_lvsr },
{ "__lvlx", (u64)&sse_cellbe_lvlx },
{ "__lvrx", (u64)&sse_cellbe_lvrx },
{ "__stvlx", (u64)&sse_cellbe_stvlx },
{ "__stvrx", (u64)&sse_cellbe_stvrx },
};
// Split module (TODO)
ppu_module info;
info.name = fmt::format("%05X", _funcs->at(fpos).addr);
info.funcs.reserve(2000);
while (fpos < _funcs->size() && info.funcs.size() < 2000)
{
info.funcs.emplace_back(std::move(_funcs->at(fpos++)));
}
if (!Emu.IsStopped())
{
ppu_initialize(info);
}
}
idm::select<lv2_obj, lv2_prx>([](u32, lv2_prx& prx)
{
if (!Emu.IsStopped())
{
ppu_initialize(prx);
}
});
}
extern void ppu_initialize(const ppu_module& info)
{
if (g_cfg_ppu_decoder.get() != ppu_decoder_type::llvm)
{
for (const auto& func : info.funcs)
{
ppu_register_function_at(func.addr, func.size, nullptr);
}
return;
}
// Compute module hash
std::string obj_name;
{
sha1_context ctx;
u8 output[20];
sha1_starts(&ctx);
for (const auto& func : info.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_finish(&ctx, output);
// Version, module name and hash: vX-liblv2.sprx-0123456789ABCDEF.obj
fmt::append(obj_name, "v0-%s-%016X.obj", info.name, reinterpret_cast<be_t<u64>&>(output));
}
#ifdef LLVM_AVAILABLE
using namespace llvm;
if (!fxm::check<jit_compiler>())
{
std::unordered_map<std::string, std::uintptr_t> link_table
{
{ "__mptr", (u64)&vm::g_base_addr },
{ "__cptr", (u64)&s_ppu_compiled },
{ "__trap", (u64)&ppu_trap },
{ "__end", (u64)&ppu_unreachable },
{ "__check", (u64)&ppu_check },
{ "__trace", (u64)&ppu_trace },
{ "__hlecall", (u64)&ppu_execute_function },
{ "__syscall", (u64)&ppu_execute_syscall },
{ "__get_tb", (u64)&get_timebased_time },
{ "__lwarx", (u64)&ppu_lwarx },
{ "__ldarx", (u64)&ppu_ldarx },
{ "__stwcx", (u64)&ppu_stwcx },
{ "__stdcx", (u64)&ppu_stdcx },
{ "__adde_get_ca", (u64)&adde_carry },
{ "__vexptefp", (u64)&sse_exp2_ps },
{ "__vlogefp", (u64)&sse_log2_ps },
{ "__vperm", (u64)&sse_altivec_vperm },
{ "__lvsl", (u64)&sse_altivec_lvsl },
{ "__lvsr", (u64)&sse_altivec_lvsr },
{ "__lvlx", (u64)&sse_cellbe_lvlx },
{ "__lvrx", (u64)&sse_cellbe_lvrx },
{ "__stvlx", (u64)&sse_cellbe_stvlx },
{ "__stvrx", (u64)&sse_cellbe_stvrx },
};
for (u64 index = 0; index < 1024; index++)
{
if (auto sc = ppu_get_syscall(index))
{
link_table.emplace(ppu_get_syscall_name(index), (u64)sc);
}
}
for (u64 index = 1; ; index++)
{
if (auto func = ppu_get_function(index))
{
link_table.emplace(ppu_get_module_function_name(index), (u64)func);
}
else
{
break;
}
}
const auto jit = fxm::make<jit_compiler>(std::move(link_table));
LOG_SUCCESS(PPU, "LLVM: JIT initialized (%s)", jit->cpu());
}
// Initialize compiler
const auto jit = fxm::get<jit_compiler>();
// Create LLVM module
std::unique_ptr<Module> module = std::make_unique<Module>("", g_llvm_ctx);
std::unique_ptr<Module> module = std::make_unique<Module>(obj_name, g_llvm_ctx);
// Initialize target
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
@ -783,16 +898,44 @@ static void ppu_initialize()
const auto _func = FunctionType::get(_void, { translator->GetContextType()->getPointerTo() }, false);
// Initialize function list
for (const auto& info : *_funcs)
for (const auto& func : info.funcs)
{
if (info.size)
if (func.size)
{
const auto f = cast<Function>(module->getOrInsertFunction(fmt::format("__0x%x", info.addr), _func));
const auto f = cast<Function>(module->getOrInsertFunction(fmt::format("__0x%x", func.addr), _func));
f->addAttribute(1, Attribute::NoAlias);
translator->AddFunction(info.addr, f);
translator->AddFunction(func.addr, f);
}
}
if (fs::file cached{Emu.GetCachePath() + obj_name})
{
std::string buf;
buf.reserve(cached.size());
cached.read(buf, cached.size());
auto buffer = llvm::MemoryBuffer::getMemBuffer(buf, obj_name);
auto result = llvm::object::ObjectFile::createObjectFile(*buffer);
if (result)
{
jit->load(std::move(module), std::move(result.get()));
for (const auto& func : info.funcs)
{
if (func.size)
{
const std::uintptr_t link = jit->get(fmt::format("__0x%x", func.addr));
s_ppu_compiled[func.addr / 4] = ::narrow<u32>(link);
}
}
LOG_SUCCESS(PPU, "LLVM: Loaded executable: %s", obj_name);
return;
}
LOG_ERROR(PPU, "LLVM: Failed to load executable: %s", obj_name);
}
legacy::FunctionPassManager pm(module.get());
// Basic optimizations
@ -831,11 +974,11 @@ static void ppu_initialize()
Emu.CallAfter([=]()
{
dlg->Create("Recompiling PPU executable.\nPlease wait...");
dlg->Create("Compiling PPU executable: " + info.name + "\nPlease wait...");
});
// Translate functions
for (size_t fi = 0; fi < _funcs->size(); fi++)
for (size_t fi = 0, fmax = info.funcs.size(); fi < fmax; fi++)
{
if (Emu.IsStopped())
{
@ -843,21 +986,19 @@ static void ppu_initialize()
return;
}
auto& info = _funcs->at(fi);
if (info.size)
if (info.funcs[fi].size)
{
// Update dialog
Emu.CallAfter([=, max = _funcs->size()]()
// Update dialog
Emu.CallAfter([=, max = info.funcs.size()]()
{
dlg->ProgressBarSetMsg(0, fmt::format("Compiling %u of %u", fi + 1, max));
dlg->ProgressBarSetMsg(0, fmt::format("Compiling %u of %u", fi + 1, fmax));
if (fi * 100 / max != (fi + 1) * 100 / max)
if (fi * 100 / fmax != (fi + 1) * 100 / fmax)
dlg->ProgressBarInc(0, 1);
});
// Translate
const auto func = translator->TranslateToIR(info, vm::_ptr<u32>(info.addr));
const auto func = translator->TranslateToIR(info.funcs[fi], vm::_ptr<u32>(info.funcs[fi].addr));
// Run optimization passes
pm.run(*func);
@ -883,7 +1024,6 @@ static void ppu_initialize()
{
const auto n = ppu_get_syscall_name(index);
const auto f = cast<Function>(module->getOrInsertFunction(n, _func));
link_table.emplace(n, reinterpret_cast<std::uintptr_t>(ptr));
// Call the syscall directly
ReplaceInstWithInst(ci, CallInst::Create(f, {ci->getArgOperand(0)}));
@ -898,7 +1038,6 @@ static void ppu_initialize()
{
const auto n = ppu_get_module_function_name(index);
const auto f = cast<Function>(module->getOrInsertFunction(n, _func));
link_table.emplace(n, reinterpret_cast<std::uintptr_t>(ptr));
// Call the function directly
ReplaceInstWithInst(ci, CallInst::Create(f, {ci->getArgOperand(0)}));
@ -966,30 +1105,20 @@ static void ppu_initialize()
return;
}
LOG_SUCCESS(PPU, "LLVM: %zu functions generated", module->getFunctionList().size());
LOG_NOTICE(PPU, "LLVM: %zu functions generated", module->getFunctionList().size());
Module* module_ptr = module.get();
const auto jit = fxm::make<jit_compiler>(std::move(module), std::move(link_table));
if (!jit)
{
LOG_FATAL(PPU, "LLVM: Multiple modules are not yet supported");
return;
}
jit->make(std::move(module), Emu.GetCachePath() + obj_name);
// Get and install function addresses
for (const auto& info : *_funcs)
for (const auto& func : info.funcs)
{
if (info.size)
if (func.size)
{
const std::uintptr_t link = jit->get(fmt::format("__0x%x", info.addr));
s_ppu_compiled[info.addr / 4] = ::narrow<u32>(link);
LOG_TRACE(PPU, "** Function __0x%x -> 0x%llx (size=0x%x, toc=0x%x, attr %#x)", info.addr, link, info.size, info.toc, info.attr);
const std::uintptr_t link = jit->get(fmt::format("__0x%x", func.addr));
s_ppu_compiled[func.addr / 4] = ::narrow<u32>(link);
}
}
LOG_SUCCESS(PPU, "LLVM: Compilation finished (%s)", sys::getHostCPUName().data());
LOG_SUCCESS(PPU, "LLVM: Created executable: %s", obj_name);
#endif
}

View File

@ -245,12 +245,12 @@ Function* PPUTranslator::TranslateToIR(const ppu_function& info, be_t<u32>* bin,
// Bloat the beginning of each block: check state
const auto vstate = m_ir->CreateLoad(m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1));
const auto vblock = BasicBlock::Create(m_context, fmt::format("l0c_%llx", m_current_addr), m_function);
const auto vcheck = BasicBlock::Create(m_context, fmt::format("lcc_%llx", m_current_addr), m_function);
const auto vblock = BasicBlock::Create(m_context, fmt::format("l0c_%llx", block.first), m_function);
const auto vcheck = BasicBlock::Create(m_context, fmt::format("lcc_%llx", block.first), m_function);
m_ir->CreateCondBr(m_ir->CreateIsNull(vstate), vblock, vcheck, m_md_unlikely);
m_ir->SetInsertPoint(vcheck);
Call(GetType<void>(), "__check", m_thread, m_ir->getInt64(m_current_addr));
Call(GetType<void>(), "__check", m_thread, m_ir->getInt64(block.first));
m_ir->CreateBr(vblock);
m_ir->SetInsertPoint(vblock);
@ -2355,7 +2355,7 @@ void PPUTranslator::MFOCRF(ppu_opcode_t op)
void PPUTranslator::LWARX(ppu_opcode_t op)
{
SetGpr(op.rd, Call(GetType<u32>(), "__lwarx", op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb)));
SetGpr(op.rd, Call(GetType<u32>(), "__lwarx", m_thread, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb)));
}
void PPUTranslator::LDX(ppu_opcode_t op)
@ -2491,7 +2491,7 @@ void PPUTranslator::MULHW(ppu_opcode_t op)
void PPUTranslator::LDARX(ppu_opcode_t op)
{
SetGpr(op.rd, Call(GetType<u64>(), "__ldarx", op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb)));
SetGpr(op.rd, Call(GetType<u64>(), "__ldarx", m_thread, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb)));
}
void PPUTranslator::DCBF(ppu_opcode_t op)
@ -2601,7 +2601,7 @@ void PPUTranslator::STDX(ppu_opcode_t op)
void PPUTranslator::STWCX(ppu_opcode_t op)
{
const auto bit = Call(GetType<bool>(), "__stwcx", op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb), GetGpr(op.rs, 32));
const auto bit = Call(GetType<bool>(), "__stwcx", m_thread, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb), GetGpr(op.rs, 32));
SetCrField(0, m_ir->getFalse(), m_ir->getFalse(), bit);
}
@ -2662,7 +2662,7 @@ void PPUTranslator::SUBFZE(ppu_opcode_t op)
void PPUTranslator::STDCX(ppu_opcode_t op)
{
const auto bit = Call(GetType<bool>(), "__stdcx", op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb), GetGpr(op.rs));
const auto bit = Call(GetType<bool>(), "__stdcx", m_thread, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb), GetGpr(op.rs));
SetCrField(0, m_ir->getFalse(), m_ir->getFalse(), bit);
}

View File

@ -10,7 +10,8 @@
namespace vm { using namespace ps3; }
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&);
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&, const std::string&);
extern void ppu_initialize(const ppu_module&);
logs::channel sys_prx("sys_prx", logs::level::notice);
@ -25,13 +26,15 @@ s32 prx_load_module(std::string path, u64 flags, vm::ptr<sys_prx_load_module_opt
return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
}
const auto prx = ppu_load_prx(obj);
const auto prx = ppu_load_prx(obj, path.substr(path.find_last_of('/') + 1));
if (!prx)
{
return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
}
ppu_initialize(*prx);
return idm::last_id();
}

View File

@ -73,14 +73,13 @@ struct sys_prx_get_module_list_t
vm::ps3::bptr<s32> idlist;
};
struct lv2_prx final : lv2_obj
struct lv2_prx final : lv2_obj, ppu_module
{
static const u32 id_base = 0x23000000;
bool is_started = false;
std::unordered_map<u32, u32> specials;
std::vector<ppu_function> funcs;
vm::ps3::ptr<s32(int argc, vm::ps3::ptr<void> argv)> start = vm::null;
vm::ps3::ptr<s32(int argc, vm::ps3::ptr<void> argv)> stop = vm::null;

View File

@ -49,7 +49,7 @@ extern u64 get_system_time();
extern void ppu_load_exec(const ppu_exec_object&);
extern void spu_load_exec(const spu_exec_object&);
extern void arm_load_exec(const arm_exec_object&);
extern std::shared_ptr<struct lv2_prx> ppu_load_prx(const ppu_prx_object&);
extern std::shared_ptr<struct lv2_prx> ppu_load_prx(const ppu_prx_object&, const std::string&);
extern void ppu_finalize();
fs::file g_tty;
@ -321,7 +321,7 @@ void Emulator::Load()
g_system = system_type::ps3;
m_status = Ready;
vm::ps3::init();
ppu_load_prx(ppu_prx);
ppu_load_prx(ppu_prx, "");
}
else if (spu_exec.open(elf_file) == elf_error::ok)
{