1 | /** |
2 | * Copyright (c) Glow Contributors. See CONTRIBUTORS file. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include "glow/LLVMIRCodeGen/CommandLine.h" |
18 | |
19 | #include "glow/LLVMIRCodeGen/GlowJIT.h" |
20 | #include "glow/Support/Debug.h" |
21 | |
22 | #include "llvm/ExecutionEngine/JITEventListener.h" |
23 | #include "llvm/Object/SymbolSize.h" |
24 | |
25 | #define DEBUG_TYPE "jit-engine" |
26 | |
27 | namespace { |
28 | /// An option to enabling the dump of the symbol information for the JITted |
29 | /// functions. It dumps e.g. the names of the functions, their start addresses |
30 | /// and their end addresses. |
31 | static llvm::cl::opt<bool> dumpJITSymbolInfo( |
32 | "dump-jit-symbol-info" , |
33 | llvm::cl::desc("Dump the load addresses and sizes of JITted symbols" ), |
34 | llvm::cl::init(false), llvm::cl::cat(getLLVMBackendCat())); |
35 | |
36 | /// This is a callback that is invoked when an LLVM module is compiled and |
37 | /// loaded by the JIT for execution. |
38 | class NotifyLoadedFunctorBase { |
39 | protected: |
40 | /// The listener for debugger events. It is used to provide debuggers with the |
41 | /// information about JITted code. |
42 | llvm::JITEventListener *dbgRegistrationListener_; |
43 | /// Dump symbol information for symbols defined by the object file. |
44 | void dumpSymbolInfo(const llvm::object::ObjectFile &loadedObj, |
45 | const llvm::RuntimeDyld::LoadedObjectInfo &objInfo) { |
46 | if (!dumpJITSymbolInfo) |
47 | return; |
48 | // Dump information about symbols. |
49 | for (auto symSizePair : llvm::object::computeSymbolSizes(loadedObj)) { |
50 | auto sym = symSizePair.first; |
51 | auto size = symSizePair.second; |
52 | auto symName = sym.getName(); |
53 | // Skip any unnamed symbols. |
54 | if (!symName || symName->empty()) |
55 | continue; |
56 | // The relative address of the symbol inside its section. |
57 | auto symAddr = sym.getAddress(); |
58 | if (!symAddr) |
59 | continue; |
60 | // The address the functions was loaded at. |
61 | auto loadedSymAddress = *symAddr; |
62 | auto symbolSection = sym.getSection(); |
63 | if (symbolSection) { |
64 | // Compute the load address of the symbol by adding the section load |
65 | // address. |
66 | loadedSymAddress += objInfo.getSectionLoadAddress(*symbolSection.get()); |
67 | } |
68 | llvm::outs() << llvm::format("Address range: [%12p, %12p]" , |
69 | loadedSymAddress, loadedSymAddress + size) |
70 | << "\tSymbol: " << *symName << "\n" ; |
71 | } |
72 | } |
73 | |
74 | NotifyLoadedFunctorBase() |
75 | : dbgRegistrationListener_( |
76 | llvm::JITEventListener::createGDBRegistrationListener()) {} |
77 | }; |
78 | |
79 | } // namespace |
80 | |
81 | //############################################################################## |
82 | #if GLOW_JIT_ORC_VERSION == 1 |
83 | //############################################################################## |
84 | using GlowJIT = llvm::orc::GlowJIT; |
85 | |
86 | class NotifyLoadedFunctor : public NotifyLoadedFunctorBase { |
87 | public: |
88 | void operator()(llvm::orc::VModuleKey key, |
89 | const llvm::object::ObjectFile &obj, |
90 | const llvm::RuntimeDyld::LoadedObjectInfo &objInfo) { |
91 | auto &loadedObj = obj; |
92 | // Inform the debugger about the loaded object file. This should allow for |
93 | // more complete stack traces under debugger. And even it should even enable |
94 | // the stepping functionality on platforms supporting it. |
95 | #if LLVM_VERSION_MAJOR == 7 || LLVM_VERSION_MAJOR == 10 || \ |
96 | (LLVM_VERSION_MAJOR <= 8 && FACEBOOK_INTERNAL) |
97 | // This fails sometimes with the following assertion: |
98 | // lib/ExecutionEngine/GDBRegistrationListener.cpp:168: virtual void |
99 | // {anonymous}::GDBJITRegistrationListener::NotifyObjectEmitted(const |
100 | // llvm::object::ObjectFile&, const llvm::RuntimeDyld::LoadedObjectInfo&): |
101 | // Assertion `ObjectBufferMap.find(Key) == ObjectBufferMap.end() && "Second |
102 | // attempt to perform debug registration."' failed. |
103 | // dbgRegistrationListener_->NotifyObjectEmitted(loadedObj, objInfo); |
104 | #else |
105 | dbgRegistrationListener_->notifyObjectLoaded( |
106 | (llvm::JITEventListener::ObjectKey)&loadedObj, loadedObj, objInfo); |
107 | #endif |
108 | |
109 | // Dump symbol information for the JITed symbols. |
110 | dumpSymbolInfo(loadedObj, objInfo); |
111 | } |
112 | }; |
113 | |
114 | //============================================================================== |
115 | #if LLVM_VERSION_MAJOR < 8 && FACEBOOK_INTERNAL |
116 | //============================================================================== |
117 | llvm::JITSymbol GlowJIT::resolveSymbol(const std::string &name) { |
118 | // Search for symbols which may not be exported. On PE/COFF targets |
119 | // (i.e. Windows), not all symbols are implicitly exported. If the |
120 | // symbols is not marked as DLLExport, it is not considered |
121 | // exported, and the symbol lookup may fail. This may also occur on |
122 | // ELF/MachO targets if built with hidden visibility. The JIT |
123 | // however maintains a list of all symbols and can find unexported |
124 | // symbols as well. |
125 | if (auto Sym = compileLayer_.findSymbol(Name, /*ExportedSymbolsOnly=*/false)) |
126 | return Sym; |
127 | else if (auto Err = Sym.takeError()) |
128 | return std::move(Err); |
129 | if (auto SymAddr = RTDyldMemoryManager::getSymbolAddressInProcess(Name)) |
130 | return JITSymbol(SymAddr, JITSymbolFlags::Exported); |
131 | return nullptr; |
132 | } |
133 | |
134 | template <typename LegacyLookupFn> |
135 | static std::shared_ptr<llvm::orc::LegacyLookupFnResolver<LegacyLookupFn>> |
136 | createLookupResolver(llvm::orc::ExecutionSession &, LegacyLookupFn LegacyLookup, |
137 | std::function<void(llvm::Error)> ErrorReporter) { |
138 | return createLegacyLookupResolver(std::move(LegacyLookup), |
139 | std::move(ErrorReporter)); |
140 | } |
141 | |
142 | //============================================================================== |
143 | #elif LLVM_VERSION_MAJOR < 8 |
144 | //============================================================================== |
145 | llvm::JITSymbol GlowJIT::resolveSymbol(const std::string &name) { |
146 | if (auto localSym = compileLayer_.findSymbol(name, false)) { |
147 | return localSym; |
148 | } else if (auto Err = localSym.takeError()) { |
149 | return std::move(Err); |
150 | } |
151 | // Some symbols are overridden, in particular __dso_handle and |
152 | // __cxa_atexit . |
153 | if (auto overriddenSym = cxxSymbolOverride_.searchOverrides(name)) { |
154 | return overriddenSym; |
155 | } |
156 | // FIXME: looking for symbols external to libjit in the process is |
157 | // dangerous because it can be environment dependent. For example, |
158 | // we get cases where a symbol is found in the Linux environment, |
159 | // but not in the Windows environment. |
160 | if (auto processSymAddr = |
161 | RTDyldMemoryManager::getSymbolAddressInProcess(name)) { |
162 | return JITSymbol(processSymAddr, JITSymbolFlags::Exported); |
163 | } |
164 | // The symbol was not resolved. This will make the retreival of |
165 | // 'main' function symbol fail later without much information about |
166 | // the source of the problem. Then, we dump an error message now to |
167 | // ease debugging. |
168 | DEBUG_GLOW(llvm::dbgs() << "JIT: Error resolving symbol '" << name << "'\n" ); |
169 | // Return a 'symbol not found' JITSymbol object (nullptr). |
170 | return nullptr; |
171 | } |
172 | |
173 | template <typename LegacyLookupFn> |
174 | static std::shared_ptr<llvm::orc::LegacyLookupFnResolver<LegacyLookupFn>> |
175 | createLookupResolver(llvm::orc::ExecutionSession &ES, |
176 | LegacyLookupFn LegacyLookup, |
177 | std::function<void(llvm::Error)> ErrorReporter) { |
178 | return createLegacyLookupResolver(ES, std::move(LegacyLookup), |
179 | std::move(ErrorReporter)); |
180 | } |
181 | |
182 | //============================================================================== |
183 | #else // 8 <= LLVM_VERSION_MAJOR |
184 | //============================================================================== |
185 | static bool symbolFound(llvm::JITSymbol &s) { |
186 | const llvm::JITSymbolFlags flags = s.getFlags(); |
187 | if (flags.getRawFlagsValue() || flags.getTargetFlags()) { |
188 | return true; |
189 | } |
190 | |
191 | llvm::Expected<llvm::JITTargetAddress> expAddr = s.getAddress(); |
192 | if (!expAddr) { |
193 | return false; // should never get here since no flags are set |
194 | } |
195 | |
196 | return expAddr.get() != 0; |
197 | } |
198 | |
199 | llvm::JITSymbol GlowJIT::resolveSymbol(const std::string &name) { |
200 | |
201 | // Search accross all modules for a strong symbol. If no strong symbol is |
202 | // found, return the first matching weak symbol found if any. |
203 | bool weakFound = false; |
204 | JITSymbol firstWeak(nullptr); |
205 | for (auto k : vModKeys_) { |
206 | JITSymbol localSym = compileLayer_.findSymbolIn(k, name, false); |
207 | if (auto Err = localSym.takeError()) { |
208 | return std::move(Err); |
209 | } |
210 | |
211 | if (!symbolFound(localSym)) { |
212 | continue; |
213 | } |
214 | |
215 | JITSymbolFlags flags = localSym.getFlags(); |
216 | if (flags.isStrong()) { |
217 | return localSym; |
218 | } |
219 | |
220 | // This is a matching weak or common symbol. Remember the first one we find |
221 | // in case we don't find a subsequent strong one. |
222 | if (!weakFound) { |
223 | firstWeak = std::move(localSym); |
224 | weakFound = true; |
225 | } |
226 | } |
227 | |
228 | #if !FACEBOOK_INTERNAL |
229 | // Some symbols are overridden, in particular __dso_handle and |
230 | // __cxa_atexit . |
231 | if (auto overriddenSym = cxxSymbolOverride_.searchOverrides(name)) { |
232 | return overriddenSym; |
233 | } |
234 | #endif |
235 | // FIXME: looking for symbols external to libjit in the process is |
236 | // dangerous because it can be environment dependent. For example, |
237 | // we get cases where a symbol is found in the Linux environment, |
238 | // but not in the Windows environment. |
239 | if (auto processSymAddr = |
240 | RTDyldMemoryManager::getSymbolAddressInProcess(name)) { |
241 | return JITSymbol(processSymAddr, JITSymbolFlags::Exported); |
242 | } |
243 | |
244 | // No strong symbol found. Return a weak symbol if we found one. |
245 | if (weakFound) { |
246 | return firstWeak; |
247 | } |
248 | |
249 | // The symbol was not resolved. This will make the retreival of |
250 | // 'main' function symbol fail later without much information about |
251 | // the source of the problem. Then, we dump an error message now to |
252 | // ease debugging. |
253 | DEBUG_GLOW(llvm::dbgs() << "JIT: Error resolving symbol '" << name << "'\n" ); |
254 | // Return a 'symbol not found' JITSymbol object (nullptr). |
255 | return nullptr; |
256 | } |
257 | |
258 | namespace { |
259 | // In order to work around a bug in the llvm-provided |
260 | // 'getResponsibilitySetWithLegacyFn' involving the handling of weak symbols, we |
261 | // provide our own implementation, called indirectly through this implementation |
262 | // of the 'llvm::orc::SymbolResolver' interface. |
263 | template <typename LegacyLookupFn> |
264 | class LookupFnResolver final : public llvm::orc::SymbolResolver { |
265 | private: |
266 | using Error = llvm::Error; |
267 | using ErrorReporter = std::function<void(Error)>; |
268 | using SymbolNameSet = llvm::orc::SymbolNameSet; |
269 | using AsynchronousSymbolQuery = llvm::orc::AsynchronousSymbolQuery; |
270 | using ExecutionSession = llvm::orc::ExecutionSession; |
271 | using JITSymbol = llvm::JITSymbol; |
272 | using JITSymbolFlags = llvm::JITSymbolFlags; |
273 | using JITTargetAddress = llvm::JITTargetAddress; |
274 | |
275 | ExecutionSession &ES; |
276 | LegacyLookupFn LegacyLookup; |
277 | ErrorReporter ReportError; |
278 | |
279 | llvm::Expected<SymbolNameSet> |
280 | getResponsibilitySetWithLegacyFn(const SymbolNameSet &Symbols) { |
281 | SymbolNameSet Result; |
282 | |
283 | for (auto &S : Symbols) { |
284 | // Note that we don't use Sym's operator bool() here since that returns |
285 | // false for symbols with no address (which includes weak symbols). |
286 | JITSymbol Sym = LegacyLookup(std::string(*S)); |
287 | if (auto Err = Sym.takeError()) { |
288 | return std::move(Err); |
289 | } |
290 | if (!Sym.getFlags().isStrong()) { |
291 | Result.insert(S); |
292 | } |
293 | } |
294 | |
295 | return Result; |
296 | } |
297 | |
298 | public: |
299 | LookupFnResolver(ExecutionSession &ES, LegacyLookupFn LegacyLookup, |
300 | ErrorReporter ReportError) |
301 | : ES(ES), LegacyLookup(std::move(LegacyLookup)), |
302 | ReportError(std::move(ReportError)) {} |
303 | |
304 | SymbolNameSet lookup(std::shared_ptr<AsynchronousSymbolQuery> Query, |
305 | SymbolNameSet Symbols) final { |
306 | return llvm::orc::lookupWithLegacyFn(ES, *Query, Symbols, LegacyLookup); |
307 | } |
308 | |
309 | SymbolNameSet getResponsibilitySet(const SymbolNameSet &Symbols) final { |
310 | auto ResponsibilitySet = getResponsibilitySetWithLegacyFn(Symbols); |
311 | |
312 | if (ResponsibilitySet) { |
313 | return std::move(*ResponsibilitySet); |
314 | } |
315 | |
316 | ReportError(ResponsibilitySet.takeError()); |
317 | return SymbolNameSet(); |
318 | } |
319 | }; |
320 | } // namespace |
321 | |
322 | template <typename LegacyLookupFn> |
323 | static std::shared_ptr<LookupFnResolver<LegacyLookupFn>> |
324 | createLookupResolver(llvm::orc::ExecutionSession &ES, |
325 | LegacyLookupFn LegacyLookup, |
326 | std::function<void(llvm::Error)> ErrorReporter) { |
327 | return std::make_shared<LookupFnResolver<LegacyLookupFn>>( |
328 | ES, std::move(LegacyLookup), std::move(ErrorReporter)); |
329 | } |
330 | #endif |
331 | |
332 | GlowJIT::GlowJIT(std::unique_ptr<llvm::TargetMachine> TM) |
333 | : TM_(std::move(TM)), DL_(TM_->createDataLayout()), |
334 | #if FACEBOOK_INTERNAL && LLVM_VERSION_MAJOR < 8 |
335 | ES_(SSP_), |
336 | resolver_(createLookupResolver( |
337 | ES_, |
338 | [this](const std::string &Name) -> JITSymbol { |
339 | return this->resolveSymbol(Name); |
340 | }, |
341 | [](Error Err) { cantFail(std::move(Err), "lookupFlags failed" ); })), |
342 | objectLayer_(ES_, |
343 | [this](llvm::orc::VModuleKey) { |
344 | return RTDyldObjectLinkingLayer::Resources{ |
345 | std::make_shared<SectionMemoryManager>(), resolver_}; |
346 | }), |
347 | #else |
348 | SSP_(std::make_shared<SymbolStringPool>()), ES_(SSP_), |
349 | #if !FACEBOOK_INTERNAL |
350 | cxxSymbolOverride_( |
351 | [this](const std::string &name) { return mangle(name); }), |
352 | #endif |
353 | resolver_(createLookupResolver( |
354 | ES_, |
355 | [this](llvm::StringRef name) -> JITSymbol { |
356 | return this->resolveSymbol(std::string(name)); |
357 | }, |
358 | [](Error Err) { cantFail(std::move(Err), "lookupFlags failed" ); })), |
359 | #if LLVM_VERSION_MAJOR == 7 || (LLVM_VERSION_MAJOR <= 8 && FACEBOOK_INTERNAL) |
360 | objectLayer_( |
361 | ES_, |
362 | [this](llvm::orc::VModuleKey) { |
363 | return RTDyldObjectLinkingLayer::Resources{ |
364 | std::make_shared<SectionMemoryManager>(), resolver_}; |
365 | }, |
366 | NotifyLoadedFunctor()), |
367 | #else |
368 | objectLayer_( |
369 | ES_, |
370 | [this](llvm::orc::VModuleKey) { |
371 | return LegacyRTDyldObjectLinkingLayer::Resources{ |
372 | std::make_shared<SectionMemoryManager>(), resolver_}; |
373 | }, |
374 | NotifyLoadedFunctor()), |
375 | #endif |
376 | #endif |
377 | compileLayer_(objectLayer_, SimpleCompiler(*TM_)) { |
378 | // When passing a null pointer to LoadLibraryPermanently, we request to |
379 | // 'load' the host process itself, making its exported symbols available for |
380 | // execution. |
381 | llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); |
382 | } |
383 | |
384 | GlowJIT::~GlowJIT() { |
385 | #if !FACEBOOK_INTERNAL |
386 | // Run any destructor registered with __cxa_atexit. |
387 | cxxSymbolOverride_.runDestructors(); |
388 | #endif |
389 | // Run any destructor discovered in the LLVM IR of the JIT modules. |
390 | for (auto &dtorRunner : irStaticDestructorRunners_) { |
391 | cantFail(dtorRunner.runViaLayer(compileLayer_)); |
392 | } |
393 | } |
394 | |
395 | GlowJIT::ModuleHandle GlowJIT::addModule(std::unique_ptr<llvm::Module> M) { |
396 | // Add the set to the JIT with the resolver and a newly created |
397 | // SectionMemoryManager. |
398 | |
399 | auto K = ES_.allocateVModule(); |
400 | |
401 | // Record the static constructors and destructors. We have to do this before |
402 | // we hand over ownership of the module to the JIT. |
403 | // Note: This code is based on the LLI/OrcLazyJIT LLVM tool code that is based |
404 | // on the ORCv1 API (see |
405 | // https://github.com/llvm-mirror/llvm/blob/release_60/tools/lli/OrcLazyJIT.cpp) |
406 | // In recent LLVM versions (7+), LLJIT uses the newer ORCv2 API (see |
407 | // https://github.com/llvm-mirror/llvm/blob/release_70/lib/ExecutionEngine/Orc/LLJIT.cpp). |
408 | std::vector<std::string> ctorNames, dtorNames; |
409 | for (auto ctor : orc::getConstructors(*M)) |
410 | ctorNames.push_back(mangle(ctor.Func->getName().str())); |
411 | for (auto dtor : orc::getDestructors(*M)) |
412 | dtorNames.push_back(mangle(dtor.Func->getName().str())); |
413 | |
414 | cantFail(compileLayer_.addModule(K, std::move(M))); |
415 | vModKeys_.insert(K); |
416 | |
417 | #if LLVM_VERSION_MAJOR == 7 || (LLVM_VERSION_MAJOR <= 8 && FACEBOOK_INTERNAL) |
418 | CtorDtorRunner<decltype(compileLayer_)> ctorRunner(std::move(ctorNames), K); |
419 | #else |
420 | LegacyCtorDtorRunner<decltype(compileLayer_)> ctorRunner(std::move(ctorNames), |
421 | K); |
422 | #endif |
423 | |
424 | // Run the static constructors and register static destructors. |
425 | consumeError(ctorRunner.runViaLayer(compileLayer_)); |
426 | irStaticDestructorRunners_.emplace_back(std::move(dtorNames), K); |
427 | |
428 | return K; |
429 | } |
430 | |
431 | void GlowJIT::removeModule(GlowJIT::ModuleHandle H) { |
432 | vModKeys_.erase(H); |
433 | cantFail(compileLayer_.removeModule(H)); |
434 | } |
435 | |
436 | std::string GlowJIT::mangle(const std::string &name) { |
437 | std::string mangledName; |
438 | raw_string_ostream MangledNameStream(mangledName); |
439 | Mangler::getNameWithPrefix(MangledNameStream, name, DL_); |
440 | return MangledNameStream.str(); |
441 | } |
442 | |
443 | llvm::JITSymbol GlowJIT::findSymbol(const std::string &name) { |
444 | return compileLayer_.findSymbol(mangle(name), false); |
445 | } |
446 | |
447 | void GlowJIT::setContext(std::unique_ptr<llvm::LLVMContext> ctx) { |
448 | ctx_ = std::move(ctx); |
449 | } |
450 | |
451 | //############################################################################## |
452 | #elif GLOW_JIT_ORC_VERSION == 2 |
453 | //############################################################################## |
454 | |
455 | namespace glow { |
456 | |
457 | class NotifyLoadedFunctorOrcV2Base : public NotifyLoadedFunctorBase { |
458 | protected: |
459 | void notify(const llvm::object::ObjectFile &obj, |
460 | const llvm::RuntimeDyld::LoadedObjectInfo &objInfo) { |
461 | auto &loadedObj = obj; |
462 | // Inform the debugger about the loaded object file. This should allow for |
463 | // more complete stack traces under debugger. And even it should even enable |
464 | // the stepping functionality on platforms supporting it. |
465 | dbgRegistrationListener_->notifyObjectLoaded( |
466 | (llvm::JITEventListener::ObjectKey)&loadedObj, loadedObj, objInfo); |
467 | |
468 | // Dump symbol information for the JITed symbols. |
469 | dumpSymbolInfo(loadedObj, objInfo); |
470 | } |
471 | }; |
472 | |
473 | //****************************************************************************** |
474 | #if LLVM_VERSION_MAJOR >= 12 |
475 | //****************************************************************************** |
476 | class NotifyLoadedFunctor : public NotifyLoadedFunctorOrcV2Base { |
477 | public: |
478 | void operator()(llvm::orc::MaterializationResponsibility &R, |
479 | const llvm::object::ObjectFile &obj, |
480 | const llvm::RuntimeDyld::LoadedObjectInfo &objInfo) { |
481 | notify(obj, objInfo); |
482 | } |
483 | }; |
484 | |
485 | class GlowJITDefGenerator : public llvm::orc::DefinitionGenerator { |
486 | GlowJIT *gj_; |
487 | |
488 | public: |
489 | GlowJITDefGenerator(GlowJIT *gj) : gj_(gj) {} |
490 | virtual ~GlowJITDefGenerator() {} |
491 | |
492 | llvm::Error |
493 | tryToGenerate(llvm::orc::LookupState &ls, llvm::orc::LookupKind k, |
494 | llvm::orc::JITDylib &jd, |
495 | llvm::orc::JITDylibLookupFlags jdLookupFlags, |
496 | const llvm::orc::SymbolLookupSet &lookupSet) override { |
497 | return gj_->tryToGenerate(k, jd, jdLookupFlags, lookupSet); |
498 | } |
499 | }; |
500 | |
501 | void endSession(llvm::orc::ExecutionSession &es) { |
502 | if (auto err = es.endSession()) { |
503 | llvm::errs() << "Error ending session: " << err << "\n" ; |
504 | } |
505 | } |
506 | |
507 | //============================================================================== |
508 | #else // LLVM_VERSION_MAJOR: 10, 11 |
509 | //============================================================================== |
510 | class NotifyLoadedFunctor : public NotifyLoadedFunctorOrcV2Base { |
511 | public: |
512 | void operator()(llvm::orc::VModuleKey key, |
513 | const llvm::object::ObjectFile &obj, |
514 | const llvm::RuntimeDyld::LoadedObjectInfo &objInfo) { |
515 | notify(obj, objInfo); |
516 | } |
517 | }; |
518 | |
519 | class GlowJITDefGenerator : public llvm::orc::JITDylib::DefinitionGenerator { |
520 | GlowJIT *gj_; |
521 | |
522 | public: |
523 | GlowJITDefGenerator(GlowJIT *gj) : gj_(gj) {} |
524 | virtual ~GlowJITDefGenerator() {} |
525 | |
526 | llvm::Error |
527 | tryToGenerate(llvm::orc::LookupKind k, llvm::orc::JITDylib &jd, |
528 | llvm::orc::JITDylibLookupFlags jdLookupFlags, |
529 | const llvm::orc::SymbolLookupSet &lookupSet) override { |
530 | return gj_->tryToGenerate(k, jd, jdLookupFlags, lookupSet); |
531 | } |
532 | }; |
533 | |
534 | void endSession(llvm::orc::ExecutionSession &es) {} |
535 | #endif |
536 | |
537 | //****************************************************************************** |
538 | #if LLVM_VERSION_MAJOR >= 11 |
539 | //****************************************************************************** |
540 | llvm::orc::JITDylib &createJITDylib(llvm::orc::ExecutionSession &es) { |
541 | return cantFail(es.createJITDylib(std::string("libGlowJIT.dylib" ))); |
542 | } |
543 | #else // LLVM_VERSION_MAJOR: 10 |
544 | llvm::orc::JITDylib &createJITDylib(llvm::orc::ExecutionSession &es) { |
545 | return es.createJITDylib(std::string("libGlowJIT.dylib" )); |
546 | } |
547 | #endif |
548 | |
549 | //****************************************************************************** |
550 | // GlowJITOrcV2 |
551 | //****************************************************************************** |
552 | GlowJITOrcV2::GlowJITOrcV2(std::unique_ptr<llvm::TargetMachine> tm) |
553 | : tm_(std::move(tm)), dl_(tm_->createDataLayout()), |
554 | ssp_(std::make_shared<llvm::orc::SymbolStringPool>()), es_(ssp_), |
555 | jd_(createJITDylib(es_)), |
556 | objectLayer_( |
557 | es_, []() { return std::make_unique<llvm::SectionMemoryManager>(); }), |
558 | compileLayer_(es_, objectLayer_, |
559 | std::make_unique<llvm::orc::SimpleCompiler>(*tm_)), |
560 | mangler_(es_, dl_) { |
561 | |
562 | cantFail(cxxSymbolOverride_.enable(jd_, mangler_)); |
563 | objectLayer_.setNotifyLoaded(NotifyLoadedFunctor()); |
564 | if (tm_->getTargetTriple().isOSBinFormatCOFF()) { |
565 | objectLayer_.setOverrideObjectFlagsWithResponsibilityFlags(true); |
566 | objectLayer_.setAutoClaimResponsibilityForObjectSymbols(true); |
567 | } |
568 | jd_.addGenerator(std::make_unique<GlowJITDefGenerator>(this)); |
569 | |
570 | // When passing a null pointer to LoadLibraryPermanently, we request to |
571 | // 'load' the host process itself, making its exported symbols available for |
572 | // execution. |
573 | llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); |
574 | } |
575 | |
576 | GlowJITOrcV2::~GlowJITOrcV2() { |
577 | // Run any destructor discovered in the LLVM IR of the JIT modules. |
578 | for (auto i = irStaticDestructorRunners_.rbegin(); |
579 | i != irStaticDestructorRunners_.rend(); ++i) { |
580 | cantFail(i->run()); |
581 | } |
582 | |
583 | // Run any destructor registered with __cxa_atexit. |
584 | cxxSymbolOverride_.runDestructors(); |
585 | |
586 | endSession(es_); |
587 | } |
588 | |
589 | llvm::Error |
590 | GlowJITOrcV2::tryToGenerate(llvm::orc::LookupKind K, llvm::orc::JITDylib &JD, |
591 | llvm::orc::JITDylibLookupFlags JDLookupFlags, |
592 | const llvm::orc::SymbolLookupSet &LookupSet) { |
593 | llvm::orc::SymbolMap newSymbols; |
594 | |
595 | for (const auto &i : LookupSet) { |
596 | const llvm::orc::SymbolStringPtr &ssp = i.first; |
597 | llvm::StringRef name = *ssp; |
598 | |
599 | // FIXME: looking for symbols external to libjit in the process is |
600 | // dangerous because it can be environment dependent. For example, |
601 | // we get cases where a symbol is found in the Linux environment, |
602 | // but not in the Windows environment. |
603 | if (auto processSymAddr = |
604 | llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name.str())) { |
605 | newSymbols[ssp] = llvm::JITEvaluatedSymbol( |
606 | processSymAddr, llvm::JITSymbolFlags::Exported); |
607 | continue; |
608 | } |
609 | |
610 | // The symbol was not resolved. This will make the retreival of |
611 | // 'main' function symbol fail later without much information about |
612 | // the source of the problem. Then, we dump an error message now to |
613 | // ease debugging. |
614 | DEBUG_GLOW(llvm::dbgs() |
615 | << "JIT: Error resolving symbol '" << name << "'\n" ); |
616 | // Return a 'symbol not found' JITSymbol object (nullptr). |
617 | } |
618 | |
619 | if (newSymbols.empty()) |
620 | return llvm::Error::success(); |
621 | |
622 | return JD.define(absoluteSymbols(std::move(newSymbols))); |
623 | } |
624 | |
625 | llvm::JITSymbol GlowJITOrcV2::findSymbol(const std::string &name) { |
626 | auto s = es_.lookup({&jd_}, name); |
627 | return s ? llvm::JITSymbol(s.get()) : llvm::JITSymbol(s.takeError()); |
628 | } |
629 | |
630 | void GlowJITOrcV2::setContext(std::unique_ptr<llvm::LLVMContext> ctx) { |
631 | ctx_ = llvm::orc::ThreadSafeContext(std::move(ctx)); |
632 | } |
633 | |
634 | void GlowJITOrcV2::addModule(std::unique_ptr<llvm::Module> m) { |
635 | auto ctors = llvm::orc::getConstructors(*m.get()); |
636 | llvm::orc::CtorDtorRunner ctorRunner(jd_); |
637 | ctorRunner.add(ctors); |
638 | |
639 | auto dtors = llvm::orc::getDestructors(*m.get()); |
640 | irStaticDestructorRunners_.emplace_back(jd_); |
641 | irStaticDestructorRunners_.back().add(dtors); |
642 | |
643 | cantFail( |
644 | compileLayer_.add(jd_, llvm::orc::ThreadSafeModule(std::move(m), ctx_))); |
645 | |
646 | // Run the static constructors |
647 | if (auto err = ctorRunner.run()) { |
648 | LOG(WARNING) << "Error while running static constructors for " |
649 | << m->getName().str() << ": " |
650 | << llvm::toString(std::move(err)); |
651 | } |
652 | } |
653 | |
654 | } // namespace glow |
655 | #else |
656 | #error Unsupported GLOW_JIT_ORC_VERSION |
657 | #endif |
658 | |