PPU LLVM: reuse loaded executables

This commit is contained in:
Nekotekina 2017-07-10 22:22:54 +03:00
parent a51f82c949
commit 73a2a937c4

View file

@ -963,9 +963,43 @@ extern void ppu_initialize(const ppu_module& info)
return link_table; return link_table;
}(); }();
// Get cache path for this executable
std::string cache_path;
if (info.name.empty())
{
cache_path = Emu.GetCachePath();
}
else
{
cache_path = vfs::get("/dev_flash/");
if (info.path.compare(0, cache_path.size(), cache_path) == 0)
{
// Remove prefix for dev_flash files
cache_path.clear();
}
else
{
cache_path = Emu.GetTitleID();
}
cache_path = fs::get_data_dir(cache_path, info.path);
}
#ifdef LLVM_AVAILABLE #ifdef LLVM_AVAILABLE
// Initialize compiler // Compiled PPU module info
jit_compiler jit(s_link_table, g_cfg.core.llvm_cpu); struct jit_module
{
std::vector<u64*> vars;
std::vector<ppu_function_t> funcs;
};
// Permanently loaded compiled PPU modules (name -> data)
jit_module& jit_mod = fxm::get_always<std::unordered_map<std::string, jit_module>>()->emplace(cache_path + info.name, jit_module{}).first->second;
// Compiler instance (deferred initialization)
std::unique_ptr<jit_compiler> jit;
// Compiler mutex // Compiler mutex
semaphore<> jmutex; semaphore<> jmutex;
@ -991,8 +1025,11 @@ extern void ppu_initialize(const ppu_module& info)
// Difference between function name and current location // Difference between function name and current location
const u32 reloc = info.name.empty() ? 0 : info.segs.at(0).addr; const u32 reloc = info.name.empty() ? 0 : info.segs.at(0).addr;
while (fpos < info.funcs.size()) while (jit_mod.vars.empty() && fpos < info.funcs.size())
{ {
// Initialize compiler instance
if (!jit) jit = std::make_unique<jit_compiler>(s_link_table, g_cfg.core.llvm_cpu);
// First function in current module part // First function in current module part
const auto fstart = fpos; const auto fstart = fpos;
@ -1084,7 +1121,7 @@ extern void ppu_initialize(const ppu_module& info)
} }
sha1_finish(&ctx, output); sha1_finish(&ctx, output);
fmt::append(obj_name, "-%016X-%s.obj", reinterpret_cast<be_t<u64>&>(output), jit.cpu()); fmt::append(obj_name, "-%016X-%s.obj", reinterpret_cast<be_t<u64>&>(output), jit->cpu());
} }
if (Emu.IsStopped()) if (Emu.IsStopped())
@ -1101,40 +1138,16 @@ extern void ppu_initialize(const ppu_module& info)
globals.emplace_back(fmt::format("__seg%u_%x", i, suffix), info.segs[i].addr); globals.emplace_back(fmt::format("__seg%u_%x", i, suffix), info.segs[i].addr);
} }
// Get cache path for this executable
std::string cache_path;
if (info.name.empty())
{
cache_path = Emu.GetCachePath();
}
else
{
cache_path = vfs::get("/dev_flash/");
if (info.path.compare(0, cache_path.size(), cache_path) == 0)
{
// Remove prefix for dev_flash files
cache_path.clear();
}
else
{
cache_path = Emu.GetTitleID();
}
cache_path = fs::get_data_dir(cache_path, info.path);
}
// Check object file // Check object file
if (fs::is_file(cache_path + obj_name)) if (fs::is_file(cache_path + obj_name))
{ {
semaphore_lock lock(jmutex); semaphore_lock lock(jmutex);
ppu_initialize2(jit, part, cache_path, obj_name); ppu_initialize2(*jit, part, cache_path, obj_name);
continue; continue;
} }
// Create worker thread for compilation // Create worker thread for compilation
jthreads.emplace_back([&jit, &jmutex, &jcores, obj_name = obj_name, part = std::move(part), cache_path = std::move(cache_path)]() jthreads.emplace_back([&jit, &jmutex, &jcores, obj_name = obj_name, part = std::move(part), &cache_path]()
{ {
// Set low priority // Set low priority
thread_ctrl::set_native_priority(-1); thread_ctrl::set_native_priority(-1);
@ -1160,7 +1173,7 @@ extern void ppu_initialize(const ppu_module& info)
// Proceed with original JIT instance // Proceed with original JIT instance
semaphore_lock lock(jmutex); semaphore_lock lock(jmutex);
ppu_initialize2(jit, part, cache_path, obj_name); ppu_initialize2(*jit, part, cache_path, obj_name);
}); });
} }
@ -1175,7 +1188,9 @@ extern void ppu_initialize(const ppu_module& info)
return; return;
} }
jit.fin(); if (jit_mod.vars.empty())
{
jit->fin();
// Get and install function addresses // Get and install function addresses
for (const auto& func : info.funcs) for (const auto& func : info.funcs)
@ -1186,7 +1201,9 @@ 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 - reloc))); const u64 addr = jit->get(fmt::format("__0x%x", block.first - reloc));
jit_mod.funcs.emplace_back(reinterpret_cast<ppu_function_t>(addr));
ppu_ref(block.first) = ::narrow<u32>(addr);
} }
} }
} }
@ -1194,11 +1211,48 @@ extern void ppu_initialize(const ppu_module& info)
// Initialize global variables // Initialize global variables
for (auto& var : globals) for (auto& var : globals)
{ {
if (u64 addr = jit.get(var.first)) const u64 addr = jit->get(var.first);
jit_mod.vars.emplace_back(reinterpret_cast<u64*>(addr));
if (addr)
{ {
*reinterpret_cast<u64*>(addr) = var.second; *reinterpret_cast<u64*>(addr) = var.second;
} }
} }
}
else
{
std::size_t index = 0;
// Locate existing functions
for (const auto& func : info.funcs)
{
if (!func.size) continue;
for (const auto& block : func.blocks)
{
if (block.second)
{
ppu_ref(block.first) = ::narrow<u32>(reinterpret_cast<uptr>(jit_mod.funcs[index++]));
}
}
}
index = 0;
// Rewrite global variables
while (index < jit_mod.vars.size())
{
*jit_mod.vars[index++] = (u64)vm::g_base_addr;
*jit_mod.vars[index++] = (u64)vm::g_exec_addr;
for (const auto& seg : info.segs)
{
*jit_mod.vars[index++] = seg.addr;
}
}
}
#endif #endif
} }