5
5
#include < unordered_set>
6
6
#include < set>
7
7
#include < array>
8
+ #include < deque>
8
9
9
10
#include " types.h"
10
11
#include " StrFmt.h"
20
21
#include " llvm/ExecutionEngine/ExecutionEngine.h"
21
22
#include " llvm/ExecutionEngine/RTDyldMemoryManager.h"
22
23
#include " llvm/ExecutionEngine/JITEventListener.h"
24
+ #include " llvm/ExecutionEngine/ObjectCache.h"
23
25
#ifdef _MSC_VER
24
26
#pragma warning(pop)
25
27
#endif
@@ -50,11 +52,13 @@ static void* const s_memory = []() -> void*
50
52
return utils::memory_reserve (s_memory_size);
51
53
}();
52
54
55
+ static void * s_next;
56
+
53
57
// Code section
54
58
static u8* s_code_addr;
55
- static u64 s_code_size;
56
59
57
60
#ifdef _WIN32
61
+ static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
58
62
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
59
63
#endif
60
64
@@ -67,17 +71,17 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
67
71
68
72
MemoryManager (std::unordered_map<std::string, std::uintptr_t >& table)
69
73
: m_link(table)
70
- , m_next(s_memory)
71
74
, m_tramps(nullptr )
72
75
{
76
+ s_next = s_memory;
73
77
}
74
78
75
79
[[noreturn]] static void null ()
76
80
{
77
81
fmt::throw_exception (" Null function" HERE);
78
82
}
79
83
80
- virtual u64 getSymbolAddress (const std::string& name) override
84
+ llvm::JITSymbol findSymbol (const std::string& name) override
81
85
{
82
86
auto & addr = m_link[name];
83
87
@@ -92,7 +96,6 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
92
96
}
93
97
else
94
98
{
95
- // It's fine if some function is never called, for example.
96
99
LOG_ERROR (GENERAL, " LLVM: Linkage failed: %s" , name);
97
100
addr = (u64)null;
98
101
}
@@ -104,9 +107,9 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
104
107
// Allocate memory for trampolines
105
108
if (!m_tramps)
106
109
{
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 );
110
113
}
111
114
112
115
// Create a trampoline
@@ -129,32 +132,31 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
129
132
}
130
133
}
131
134
132
- return addr;
135
+ return { addr, llvm::JITSymbolFlags::Exported} ;
133
136
}
134
137
135
138
virtual u8* allocateCodeSection (std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
136
139
{
137
140
// Simple allocation
138
- const u64 next = ::align ((u64)m_next + size, 4096 );
141
+ const u64 next = ::align ((u64)s_next + size, 4096 );
139
142
140
143
if (next > (u64)s_memory + s_memory_size)
141
144
{
142
145
LOG_FATAL (GENERAL, " LLVM: Out of memory (size=0x%llx, aligned 0x%x)" , size, align);
143
146
return nullptr ;
144
147
}
145
148
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;
149
151
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);
152
154
}
153
155
154
156
virtual u8* allocateDataSection (std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
155
157
{
156
158
// Simple allocation
157
- const u64 next = ::align ((u64)m_next + size, 4096 );
159
+ const u64 next = ::align ((u64)s_next + size, 4096 );
158
160
159
161
if (next > (u64)s_memory + s_memory_size)
160
162
{
@@ -167,10 +169,10 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
167
169
LOG_ERROR (GENERAL, " LLVM: Writeable data section not supported!" );
168
170
}
169
171
170
- utils::memory_commit (m_next , size);
172
+ utils::memory_commit (s_next , size);
171
173
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);
174
176
}
175
177
176
178
virtual bool finalizeMemory (std::string* = nullptr ) override
@@ -191,24 +193,26 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
191
193
{
192
194
#ifdef _WIN32
193
195
// Use s_memory as a BASE, compute the difference
194
- const u64 code_diff = (u64)s_code_addr - (u64)s_memory;
195
196
const u64 unwind_diff = (u64)addr - (u64)s_memory;
196
197
197
198
// 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 ();
199
201
200
202
for (auto & rf : pdata)
201
203
{
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);
205
205
}
206
206
207
207
// Register .xdata UNWIND_INFO structs
208
208
if (!RtlAddFunctionTable (pdata.data (), (DWORD)pdata.size (), (u64)s_memory))
209
209
{
210
210
LOG_ERROR (GENERAL, " RtlAddFunctionTable() failed! Error %u" , GetLastError ());
211
211
}
212
+ else
213
+ {
214
+ s_unwind.emplace_back (std::move (pdata));
215
+ }
212
216
#endif
213
217
214
218
return RTDyldMemoryManager::registerEHFrames (addr, load_addr, size);
@@ -239,24 +243,13 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
239
243
240
244
utils::memory_decommit (s_memory, s_memory_size);
241
245
}
242
-
243
- private:
244
- void * m_next;
245
246
};
246
247
247
248
// Helper class
248
249
struct EventListener final : llvm::JITEventListener
249
250
{
250
- std::string path;
251
-
252
251
virtual void NotifyObjectEmitted (const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
253
252
{
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
-
260
253
#ifdef _WIN32
261
254
for (auto it = obj.section_begin (), end = obj.section_end (); it != end; ++it)
262
255
{
@@ -282,7 +275,17 @@ struct EventListener final : llvm::JITEventListener
282
275
}
283
276
}
284
277
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));
286
289
}
287
290
}
288
291
#endif
@@ -291,6 +294,46 @@ struct EventListener final : llvm::JITEventListener
291
294
292
295
static EventListener s_listener;
293
296
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
+
294
337
jit_compiler::jit_compiler (std::unordered_map<std::string, std::uintptr_t > init_linkage_info, std::string _cpu)
295
338
: m_link(std::move(init_linkage_info))
296
339
, m_cpu(std::move(_cpu))
@@ -321,56 +364,52 @@ jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_
321
364
}
322
365
323
366
m_engine->RegisterJITEventListener (&s_listener);
367
+
368
+ LOG_SUCCESS (GENERAL, " LLVM: JIT initialized (%s)" , m_cpu);
324
369
}
325
370
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 )
327
372
{
328
- s_listener.path .clear ();
329
-
330
- auto * module_ptr = module.get ();
373
+ ObjectCache cache{path};
374
+ m_engine->setObjectCache (&cache);
331
375
376
+ const auto ptr = module.get ();
332
377
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 );
337
380
338
- for (auto & func : module_ptr ->functions ())
381
+ for (auto & func : ptr ->functions ())
339
382
{
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 ();
347
385
}
348
386
}
349
387
350
- void jit_compiler::make (std::unique_ptr<llvm::Module> module, std::string path)
388
+ void jit_compiler::fin ( const std::string& path)
351
389
{
352
- s_listener.path = std::move (path);
353
-
354
- auto * module_ptr = module.get ();
355
-
356
- m_engine->addModule (std::move (module));
357
390
m_engine->finalizeObject ();
391
+ }
358
392
359
- m_map.clear ();
393
+ void jit_compiler::add (std::unordered_map<std::string, std::string> data)
394
+ {
395
+ std::size_t size = 0 ;
360
396
361
- for (auto & func : module_ptr-> functions () )
397
+ for (auto && pair : data )
362
398
{
363
- if (!func.empty ())
364
- {
365
- const std::string& name = func.getName ();
399
+ size += ::align (pair.second .size (), 16 );
400
+ }
366
401
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 ));
370
404
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 );
373
410
}
411
+
412
+ s_next = (void *)::align ((u64)s_next, 4096 );
374
413
}
375
414
376
415
jit_compiler::~jit_compiler ()
0 commit comments