forked from JuliaLang/julia
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jitlayers.cpp
399 lines (345 loc) · 14.9 KB
/
jitlayers.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
// This file is part of Julia.
// Parts of this file are copied from LLVM, under the UIUC license.
template <class T>
static void addOptimizationPasses(T *PM)
{
#ifdef __has_feature
# if __has_feature(address_sanitizer)
# if defined(LLVM37) && !defined(LLVM38)
// LLVM 3.7 BUG: ASAN pass doesn't properly initialize its dependencies
initializeTargetLibraryInfoWrapperPassPass(*PassRegistry::getPassRegistry());
# endif
FPM->add(createAddressSanitizerFunctionPass());
# endif
# if __has_feature(memory_sanitizer)
FPM->add(llvm::createMemorySanitizerPass(true));
# endif
#endif
#ifdef LLVM37
PM->add(createTargetTransformInfoWrapperPass(jl_TargetMachine->getTargetIRAnalysis()));
#else
jl_TargetMachine->addAnalysisPasses(*PM);
#endif
#ifdef LLVM38
PM->add(createTypeBasedAAWrapperPass());
#else
PM->add(createTypeBasedAliasAnalysisPass());
#endif
if (jl_options.opt_level>=1) {
#ifdef LLVM38
PM->add(createBasicAAWrapperPass());
#else
PM->add(createBasicAliasAnalysisPass());
#endif
}
// list of passes from vmkit
PM->add(createCFGSimplificationPass()); // Clean up disgusting code
PM->add(createPromoteMemoryToRegisterPass());// Kill useless allocas
#ifndef INSTCOMBINE_BUG
PM->add(createInstructionCombiningPass()); // Cleanup for scalarrepl.
#endif
PM->add(createSROAPass()); // Break up aggregate allocas
#ifndef INSTCOMBINE_BUG
PM->add(createInstructionCombiningPass()); // Cleanup for scalarrepl.
#endif
PM->add(createJumpThreadingPass()); // Thread jumps.
// NOTE: CFG simp passes after this point seem to hurt native codegen.
// See issue #6112. Should be re-evaluated when we switch to MCJIT.
//FPM->add(createCFGSimplificationPass()); // Merge & remove BBs
#ifndef INSTCOMBINE_BUG
PM->add(createInstructionCombiningPass()); // Combine silly seq's
#endif
//FPM->add(createCFGSimplificationPass()); // Merge & remove BBs
PM->add(createReassociatePass()); // Reassociate expressions
// this has the potential to make some things a bit slower
//FPM->add(createBBVectorizePass());
PM->add(createEarlyCSEPass()); //// ****
PM->add(createLoopIdiomPass()); //// ****
PM->add(createLoopRotatePass()); // Rotate loops.
// LoopRotate strips metadata from terminator, so run LowerSIMD afterwards
PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "simdloop" as LLVM parallel loop
PM->add(createLICMPass()); // Hoist loop invariants
PM->add(createLoopUnswitchPass()); // Unswitch loops.
// Subsequent passes not stripping metadata from terminator
#ifndef INSTCOMBINE_BUG
PM->add(createInstructionCombiningPass());
#endif
PM->add(createIndVarSimplifyPass()); // Canonicalize indvars
PM->add(createLoopDeletionPass()); // Delete dead loops
#if defined(LLVM35)
PM->add(createSimpleLoopUnrollPass()); // Unroll small loops
#else
PM->add(createLoopUnrollPass()); // Unroll small loops
#endif
#if !defined(LLVM35) && !defined(INSTCOMBINE_BUG)
PM->add(createLoopVectorizePass()); // Vectorize loops
#endif
//FPM->add(createLoopStrengthReducePass()); // (jwb added)
#ifndef INSTCOMBINE_BUG
PM->add(createInstructionCombiningPass()); // Clean up after the unroller
#endif
PM->add(createGVNPass()); // Remove redundancies
//FPM->add(createMemCpyOptPass()); // Remove memcpy / form memset
PM->add(createSCCPPass()); // Constant prop with SCCP
// Run instcombine after redundancy elimination to exploit opportunities
// opened up by them.
PM->add(createSinkingPass()); ////////////// ****
PM->add(createInstructionSimplifierPass());///////// ****
#ifndef INSTCOMBINE_BUG
PM->add(createInstructionCombiningPass());
#endif
PM->add(createJumpThreadingPass()); // Thread jumps
PM->add(createDeadStoreEliminationPass()); // Delete dead stores
#if !defined(INSTCOMBINE_BUG)
if (jl_options.opt_level>=1)
PM->add(createSLPVectorizerPass()); // Vectorize straight-line code
#endif
PM->add(createAggressiveDCEPass()); // Delete dead instructions
#if !defined(INSTCOMBINE_BUG)
if (jl_options.opt_level>=1)
PM->add(createInstructionCombiningPass()); // Clean up after SLP loop vectorizer
#endif
#if defined(LLVM35)
PM->add(createLoopVectorizePass()); // Vectorize loops
PM->add(createInstructionCombiningPass()); // Clean up after loop vectorizer
#endif
//FPM->add(createCFGSimplificationPass()); // Merge & remove BBs
}
#ifdef USE_ORCJIT
// ------------------------ TEMPORARILY COPIED FROM LLVM -----------------
// This must be kept in sync with gdb/gdb/jit.h .
extern "C" {
typedef enum {
JIT_NOACTION = 0,
JIT_REGISTER_FN,
JIT_UNREGISTER_FN
} jit_actions_t;
struct jit_code_entry {
struct jit_code_entry *next_entry;
struct jit_code_entry *prev_entry;
const char *symfile_addr;
uint64_t symfile_size;
};
struct jit_descriptor {
uint32_t version;
// This should be jit_actions_t, but we want to be specific about the
// bit-width.
uint32_t action_flag;
struct jit_code_entry *relevant_entry;
struct jit_code_entry *first_entry;
};
// We put information about the JITed function in this global, which the
// debugger reads. Make sure to specify the version statically, because the
// debugger checks the version before we can set it during runtime.
extern struct jit_descriptor __jit_debug_descriptor;
LLVM_ATTRIBUTE_NOINLINE extern void __jit_debug_register_code();
}
extern JL_DLLEXPORT void ORCNotifyObjectEmitted(JITEventListener *Listener,
const object::ObjectFile &obj,
const object::ObjectFile &debugObj,
const RuntimeDyld::LoadedObjectInfo &L);
namespace {
using namespace llvm;
using namespace llvm::object;
using namespace llvm::orc;
/// Do the registration.
void NotifyDebugger(jit_code_entry* JITCodeEntry) {
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
// Insert this entry at the head of the list.
JITCodeEntry->prev_entry = nullptr;
jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry;
JITCodeEntry->next_entry = NextEntry;
if (NextEntry) {
NextEntry->prev_entry = JITCodeEntry;
}
__jit_debug_descriptor.first_entry = JITCodeEntry;
__jit_debug_descriptor.relevant_entry = JITCodeEntry;
__jit_debug_register_code();
}
// --------------------------------------------------------------------------
class DebugObjectRegistrar {
private:
void NotifyGDB(OwningBinary<ObjectFile> &DebugObj) {
const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart();
size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize();
assert(Buffer && "Attempt to register a null object with a debugger.");
jit_code_entry* JITCodeEntry = new jit_code_entry();
if (!JITCodeEntry) {
jl_printf(JL_STDERR, "WARNING: Allocation failed when registering a JIT entry!\n");
} else {
JITCodeEntry->symfile_addr = Buffer;
JITCodeEntry->symfile_size = Size;
NotifyDebugger(JITCodeEntry);
}
}
std::vector<OwningBinary<ObjectFile>> SavedObjects;
std::unique_ptr<JITEventListener> JuliaListener;
public:
DebugObjectRegistrar() : JuliaListener(CreateJuliaJITEventListener()) {}
template <typename ObjSetT, typename LoadResult>
void operator()(ObjectLinkingLayerBase::ObjSetHandleT, const ObjSetT &Objects,
const LoadResult &LOS) {
auto oit = Objects.begin();
auto lit = LOS.begin();
while (oit != Objects.end()) {
auto &Object = *oit;
auto &LO = *lit;
OwningBinary<ObjectFile> SavedObject = LO->getObjectForDebug(*Object);
// If the debug object is unavailable, save (a copy of) the original object
// for our backtraces
if (!SavedObject.getBinary()) {
// This is unfortunate, but there doesn't seem to be a way to take
// ownership of the original buffer
auto NewBuffer = MemoryBuffer::getMemBufferCopy(Object->getData(), Object->getFileName());
auto NewObj = ObjectFile::createObjectFile(NewBuffer->getMemBufferRef());
SavedObject = OwningBinary<ObjectFile>(std::move(*NewObj),std::move(NewBuffer));
}
else {
NotifyGDB(SavedObject);
}
SavedObjects.push_back(std::move(SavedObject));
ORCNotifyObjectEmitted(JuliaListener.get(),*Object,*SavedObjects.back().getBinary(),*LO);
++oit;
++lit;
}
}
};
}
#if defined(_OS_DARWIN_) && defined(LLVM37) && defined(LLVM_SHLIB)
#define CUSTOM_MEMORY_MANAGER 1
extern RTDyldMemoryManager* createRTDyldMemoryManagerOSX();
#endif
class JuliaOJIT {
public:
typedef orc::ObjectLinkingLayer<DebugObjectRegistrar> ObjLayerT;
typedef orc::IRCompileLayer<ObjLayerT> CompileLayerT;
typedef CompileLayerT::ModuleSetHandleT ModuleHandleT;
typedef StringMap<void*> GlobalSymbolTableT;
typedef object::OwningBinary<object::ObjectFile> OwningObj;
JuliaOJIT(TargetMachine &TM)
: TM(TM),
DL(TM.createDataLayout()),
ObjStream(ObjBufferSV),
MemMgr(
#ifdef CUSTOM_MEMORY_MANAGER
createRTDyldMemoryManagerOSX()
#else
new SectionMemoryManager
#endif
) {
#ifdef JL_DEBUG_BUILD
PM.add(createVerifierPass());
#endif
// In imaging mode, we run the pass manager on creation
// to make sure it ends up optimized in the shadow module
if (!imaging_mode) {
addOptimizationPasses(&PM);
#ifdef JL_DEBUG_BUILD
PM.add(createVerifierPass());
#endif
}
if (TM.addPassesToEmitMC(PM, Ctx, ObjStream))
llvm_unreachable("Target does not support MC emission.");
CompileLayer = std::unique_ptr<CompileLayerT>{new CompileLayerT(ObjectLayer,
[&](Module &M) {
PM.run(M);
std::unique_ptr<MemoryBuffer> ObjBuffer(
new ObjectMemoryBuffer(std::move(ObjBufferSV)));
ErrorOr<std::unique_ptr<object::ObjectFile>> Obj =
object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef());
if (!Obj) {
M.dump();
llvm::report_fatal_error("FATAL: Unable to compile LLVM Module.\n"
"The module's content was printed above. Please file a bug report");
}
return OwningObj(std::move(*Obj), std::move(ObjBuffer));
}
)};
// Make sure SectionMemoryManager::getSymbolAddressInProcess can resolve
// symbols in the program as well. The nullptr argument to the function
// tells DynamicLibrary to load the program, not a library.
std::string *ErrorStr = nullptr;
if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr, ErrorStr))
report_fatal_error("FATAL: unable to dlopen self\n" + *ErrorStr);
}
std::string mangle(const std::string &Name) {
std::string MangledName;
{
raw_string_ostream MangledNameStream(MangledName);
Mangler::getNameWithPrefix(MangledNameStream, Name, DL);
}
return MangledName;
}
void addGlobalMapping(StringRef Name, void *Addr) {
GlobalSymbolTable[mangle(Name)] = Addr;
}
ModuleHandleT addModule(Module *M) {
// We need a memory manager to allocate memory and resolve symbols for this
// new module. Create one that resolves symbols by looking back into the
// JIT.
auto Resolver = orc::createLambdaResolver(
[&](const std::string &Name) {
// TODO: consider moving the FunctionMover resolver here
// Step 0: ObjectLinkingLayer has checked whether it is in the current module
// Step 1: Check against list of known external globals
GlobalSymbolTableT::const_iterator pos = GlobalSymbolTable.find(Name);
if (pos != GlobalSymbolTable.end())
return RuntimeDyld::SymbolInfo((intptr_t)pos->second, JITSymbolFlags::Exported);
// Step 2: Search all previously emitted symbols
if (auto Sym = findSymbol(Name))
return RuntimeDyld::SymbolInfo(Sym.getAddress(),
Sym.getFlags());
// Step 2: Search the program symbols
if (uint64_t addr = SectionMemoryManager::getSymbolAddressInProcess(Name))
return RuntimeDyld::SymbolInfo(addr, JITSymbolFlags::Exported);
// Return failure code
return RuntimeDyld::SymbolInfo(nullptr);
},
[](const std::string &S) { return nullptr; }
);
SmallVector<std::unique_ptr<Module>,1> Ms;
Ms.push_back(std::unique_ptr<Module>{M});
return CompileLayer->addModuleSet(std::move(Ms),
MemMgr,
std::move(Resolver));
}
void removeModule(ModuleHandleT H) { CompileLayer->removeModuleSet(H); }
orc::JITSymbol findSymbol(const std::string &Name) {
return CompileLayer->findSymbol(Name, true);
}
orc::JITSymbol findUnmangledSymbol(const std::string Name) {
return findSymbol(mangle(Name));
}
uint64_t getGlobalValueAddress(const std::string &Name) {
return CompileLayer->findSymbol(mangle(Name), false).getAddress();
}
uint64_t getFunctionAddress(const std::string &Name) {
return CompileLayer->findSymbol(mangle(Name), false).getAddress();
}
uint64_t FindFunctionNamed(const std::string &Name) {
return 0; // Functions are not kept around
}
void RegisterJITEventListener(JITEventListener *L) {
// TODO
}
const DataLayout& getDataLayout() const {
return DL;
}
const Triple& getTargetTriple() const {
return TM.getTargetTriple();
}
private:
TargetMachine &TM;
const DataLayout DL;
// Should be big enough that in the common case, The
// object fits in its entirety
SmallVector<char, 4096> ObjBufferSV;
raw_svector_ostream ObjStream;
legacy::PassManager PM;
MCContext *Ctx;
RTDyldMemoryManager *MemMgr;
ObjLayerT ObjectLayer;
std::unique_ptr<CompileLayerT> CompileLayer;
GlobalSymbolTableT GlobalSymbolTable;
};
#endif