Skip to content

Commit aea0947

Browse files
committed
PPU LLVM: paradigm shift
For now, compile only one block at time Use tail calls to move between blocks Fully write PPU context (except CIA) This fixes many compatibility problems
1 parent a29d7d3 commit aea0947

13 files changed

+1052
-832
lines changed

Utilities/JIT.cpp

+106-67
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <unordered_set>
66
#include <set>
77
#include <array>
8+
#include <deque>
89

910
#include "types.h"
1011
#include "StrFmt.h"
@@ -20,6 +21,7 @@
2021
#include "llvm/ExecutionEngine/ExecutionEngine.h"
2122
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
2223
#include "llvm/ExecutionEngine/JITEventListener.h"
24+
#include "llvm/ExecutionEngine/ObjectCache.h"
2325
#ifdef _MSC_VER
2426
#pragma warning(pop)
2527
#endif
@@ -50,11 +52,13 @@ static void* const s_memory = []() -> void*
5052
return utils::memory_reserve(s_memory_size);
5153
}();
5254

55+
static void* s_next;
56+
5357
// Code section
5458
static u8* s_code_addr;
55-
static u64 s_code_size;
5659

5760
#ifdef _WIN32
61+
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
5862
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
5963
#endif
6064

@@ -67,17 +71,17 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
6771

6872
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
6973
: m_link(table)
70-
, m_next(s_memory)
7174
, m_tramps(nullptr)
7275
{
76+
s_next = s_memory;
7377
}
7478

7579
[[noreturn]] static void null()
7680
{
7781
fmt::throw_exception("Null function" HERE);
7882
}
7983

80-
virtual u64 getSymbolAddress(const std::string& name) override
84+
llvm::JITSymbol findSymbol(const std::string& name) override
8185
{
8286
auto& addr = m_link[name];
8387

@@ -92,7 +96,6 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
9296
}
9397
else
9498
{
95-
// It's fine if some function is never called, for example.
9699
LOG_ERROR(GENERAL, "LLVM: Linkage failed: %s", name);
97100
addr = (u64)null;
98101
}
@@ -104,9 +107,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
104107
// Allocate memory for trampolines
105108
if (!m_tramps)
106109
{
107-
m_tramps = reinterpret_cast<decltype(m_tramps)>(m_next);
108-
utils::memory_commit(m_next, 4096, utils::protection::wx);
109-
m_next = (u8*)((u64)m_next + 4096);
110+
m_tramps = reinterpret_cast<decltype(m_tramps)>(s_next);
111+
utils::memory_commit(s_next, 4096, utils::protection::wx);
112+
s_next = (u8*)((u64)s_next + 4096);
110113
}
111114

112115
// Create a trampoline
@@ -129,32 +132,31 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
129132
}
130133
}
131134

132-
return addr;
135+
return {addr, llvm::JITSymbolFlags::Exported};
133136
}
134137

135138
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
136139
{
137140
// Simple allocation
138-
const u64 next = ::align((u64)m_next + size, 4096);
141+
const u64 next = ::align((u64)s_next + size, 4096);
139142

140143
if (next > (u64)s_memory + s_memory_size)
141144
{
142145
LOG_FATAL(GENERAL, "LLVM: Out of memory (size=0x%llx, aligned 0x%x)", size, align);
143146
return nullptr;
144147
}
145148

146-
utils::memory_commit(m_next, size, utils::protection::wx);
147-
s_code_addr = (u8*)m_next;
148-
s_code_size = size;
149+
utils::memory_commit(s_next, size, utils::protection::wx);
150+
s_code_addr = (u8*)s_next;
149151

150-
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);
151-
return (u8*)std::exchange(m_next, (void*)next);
152+
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);
153+
return (u8*)std::exchange(s_next, (void*)next);
152154
}
153155

154156
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
155157
{
156158
// Simple allocation
157-
const u64 next = ::align((u64)m_next + size, 4096);
159+
const u64 next = ::align((u64)s_next + size, 4096);
158160

159161
if (next > (u64)s_memory + s_memory_size)
160162
{
@@ -167,10 +169,10 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
167169
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
168170
}
169171

170-
utils::memory_commit(m_next, size);
172+
utils::memory_commit(s_next, size);
171173

172-
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");
173-
return (u8*)std::exchange(m_next, (void*)next);
174+
LOG_NOTICE(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), s_next, size, align, is_ro ? "ro" : "rw");
175+
return (u8*)std::exchange(s_next, (void*)next);
174176
}
175177

176178
virtual bool finalizeMemory(std::string* = nullptr) override
@@ -191,24 +193,26 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
191193
{
192194
#ifdef _WIN32
193195
// Use s_memory as a BASE, compute the difference
194-
const u64 code_diff = (u64)s_code_addr - (u64)s_memory;
195196
const u64 unwind_diff = (u64)addr - (u64)s_memory;
196197

197198
// Fix RUNTIME_FUNCTION records (.pdata section)
198-
auto& pdata = s_unwind.back();
199+
auto pdata = std::move(s_unwater.front());
200+
s_unwater.pop_front();
199201

200202
for (auto& rf : pdata)
201203
{
202-
rf.BeginAddress += static_cast<DWORD>(code_diff);
203-
rf.EndAddress += static_cast<DWORD>(code_diff);
204-
rf.UnwindData += static_cast<DWORD>(unwind_diff);
204+
rf.UnwindData += static_cast<DWORD>(unwind_diff);
205205
}
206206

207207
// Register .xdata UNWIND_INFO structs
208208
if (!RtlAddFunctionTable(pdata.data(), (DWORD)pdata.size(), (u64)s_memory))
209209
{
210210
LOG_ERROR(GENERAL, "RtlAddFunctionTable() failed! Error %u", GetLastError());
211211
}
212+
else
213+
{
214+
s_unwind.emplace_back(std::move(pdata));
215+
}
212216
#endif
213217

214218
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
@@ -239,24 +243,13 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
239243

240244
utils::memory_decommit(s_memory, s_memory_size);
241245
}
242-
243-
private:
244-
void* m_next;
245246
};
246247

247248
// Helper class
248249
struct EventListener final : llvm::JITEventListener
249250
{
250-
std::string path;
251-
252251
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
253252
{
254-
if (!path.empty())
255-
{
256-
const llvm::StringRef elf = obj.getData();
257-
fs::file(path, fs::rewrite).write(elf.data(), elf.size());
258-
}
259-
260253
#ifdef _WIN32
261254
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
262255
{
@@ -282,7 +275,17 @@ struct EventListener final : llvm::JITEventListener
282275
}
283276
}
284277

285-
s_unwind.emplace_back(std::move(rfs));
278+
// Use s_memory as a BASE, compute the difference
279+
const u64 code_diff = (u64)s_code_addr - (u64)s_memory;
280+
281+
// Fix RUNTIME_FUNCTION records (.pdata section)
282+
for (auto& rf : rfs)
283+
{
284+
rf.BeginAddress += static_cast<DWORD>(code_diff);
285+
rf.EndAddress += static_cast<DWORD>(code_diff);
286+
}
287+
288+
s_unwater.emplace_back(std::move(rfs));
286289
}
287290
}
288291
#endif
@@ -291,6 +294,46 @@ struct EventListener final : llvm::JITEventListener
291294

292295
static EventListener s_listener;
293296

297+
// Helper class
298+
class ObjectCache final : public llvm::ObjectCache
299+
{
300+
const std::string& m_path;
301+
302+
public:
303+
ObjectCache(const std::string& path)
304+
: m_path(path)
305+
{
306+
}
307+
308+
~ObjectCache() override = default;
309+
310+
void notifyObjectCompiled(const llvm::Module* module, llvm::MemoryBufferRef obj) override
311+
{
312+
std::string name = m_path;
313+
name.append(module->getName());
314+
fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
315+
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
316+
}
317+
318+
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
319+
{
320+
std::string name = m_path;
321+
name.append(module->getName());
322+
323+
if (fs::file cached{name, fs::read})
324+
{
325+
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
326+
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
327+
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
328+
return buf;
329+
}
330+
else
331+
{
332+
return nullptr;
333+
}
334+
}
335+
};
336+
294337
jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_linkage_info, std::string _cpu)
295338
: m_link(std::move(init_linkage_info))
296339
, m_cpu(std::move(_cpu))
@@ -321,56 +364,52 @@ jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_
321364
}
322365

323366
m_engine->RegisterJITEventListener(&s_listener);
367+
368+
LOG_SUCCESS(GENERAL, "LLVM: JIT initialized (%s)", m_cpu);
324369
}
325370

326-
void jit_compiler::load(std::unique_ptr<llvm::Module> module, std::unique_ptr<llvm::object::ObjectFile> object)
371+
void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string& path)
327372
{
328-
s_listener.path.clear();
329-
330-
auto* module_ptr = module.get();
373+
ObjectCache cache{path};
374+
m_engine->setObjectCache(&cache);
331375

376+
const auto ptr = module.get();
332377
m_engine->addModule(std::move(module));
333-
m_engine->addObjectFile(std::move(object));
334-
m_engine->finalizeObject();
335-
336-
m_map.clear();
378+
m_engine->generateCodeForModule(ptr);
379+
m_engine->setObjectCache(nullptr);
337380

338-
for (auto& func : module_ptr->functions())
381+
for (auto& func : ptr->functions())
339382
{
340-
const std::string& name = func.getName();
341-
342-
if (!m_link.count(name))
343-
{
344-
// Register compiled function
345-
m_map[name] = m_engine->getFunctionAddress(name);
346-
}
383+
// Delete IR to lower memory consumption
384+
func.deleteBody();
347385
}
348386
}
349387

350-
void jit_compiler::make(std::unique_ptr<llvm::Module> module, std::string path)
388+
void jit_compiler::fin(const std::string& path)
351389
{
352-
s_listener.path = std::move(path);
353-
354-
auto* module_ptr = module.get();
355-
356-
m_engine->addModule(std::move(module));
357390
m_engine->finalizeObject();
391+
}
358392

359-
m_map.clear();
393+
void jit_compiler::add(std::unordered_map<std::string, std::string> data)
394+
{
395+
std::size_t size = 0;
360396

361-
for (auto& func : module_ptr->functions())
397+
for (auto&& pair : data)
362398
{
363-
if (!func.empty())
364-
{
365-
const std::string& name = func.getName();
399+
size += ::align(pair.second.size(), 16);
400+
}
366401

367-
// Register compiled function
368-
m_map[name] = m_engine->getFunctionAddress(name);
369-
}
402+
utils::memory_commit(s_next, size, utils::protection::wx);
403+
std::memset(s_next, 0xc3, ::align(size, 4096));
370404

371-
// Delete IR to lower memory consumption
372-
func.deleteBody();
405+
for (auto&& pair : data)
406+
{
407+
std::memcpy(s_next, pair.second.data(), pair.second.size());
408+
m_link.emplace(pair.first, (u64)s_next);
409+
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
373410
}
411+
412+
s_next = (void*)::align((u64)s_next, 4096);
374413
}
375414

376415
jit_compiler::~jit_compiler()

Utilities/JIT.h

+14-11
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,30 @@ class jit_compiler final
2828
// Execution instance
2929
std::unique_ptr<llvm::ExecutionEngine> m_engine;
3030

31-
// Compiled functions
32-
std::unordered_map<std::string, std::uintptr_t> m_map;
33-
3431
// Linkage cache
35-
std::unordered_map<std::string, std::uintptr_t> m_link;
32+
std::unordered_map<std::string, u64> m_link;
33+
34+
// Compiled functions
35+
std::unordered_map<std::string, u64> m_map;
3636

3737
// Arch
3838
std::string m_cpu;
3939

4040
public:
41-
jit_compiler(std::unordered_map<std::string, std::uintptr_t>, std::string _cpu);
41+
jit_compiler(std::unordered_map<std::string, u64>, std::string _cpu);
4242
~jit_compiler();
4343

44-
// Compile module
45-
void make(std::unique_ptr<llvm::Module>, std::string);
44+
// Add module
45+
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
46+
47+
// Finalize
48+
void fin(const std::string& path);
4649

47-
// Load object
48-
void load(std::unique_ptr<llvm::Module>, std::unique_ptr<llvm::object::ObjectFile>);
50+
// Add functions directly (name -> code)
51+
void add(std::unordered_map<std::string, std::string>);
4952

5053
// Get compiled function address
51-
std::uintptr_t get(const std::string& name) const
54+
u64 get(const std::string& name) const
5255
{
5356
const auto found = m_map.find(name);
5457

@@ -57,7 +60,7 @@ class jit_compiler final
5760
return found->second;
5861
}
5962

60-
return 0;
63+
return m_engine->getFunctionAddress(name);
6164
}
6265

6366
// Get CPU info

0 commit comments

Comments
 (0)