1 //===----- Core.cpp - Core ORC APIs (MaterializationUnit, VSO, etc.) ------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "llvm/ExecutionEngine/Orc/Core.h"
11 #include "llvm/ExecutionEngine/Orc/OrcError.h"
12 
13 #if LLVM_ENABLE_THREADS
14 #include <future>
15 #endif
16 
17 namespace llvm {
18 namespace orc {
19 
20 void MaterializationUnit::anchor() {}
21 void SymbolResolver::anchor() {}
22 
23 AsynchronousSymbolQuery::AsynchronousSymbolQuery(
24     const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved,
25     SymbolsReadyCallback NotifySymbolsReady)
26     : NotifySymbolsResolved(std::move(NotifySymbolsResolved)),
27       NotifySymbolsReady(std::move(NotifySymbolsReady)) {
28   assert(this->NotifySymbolsResolved &&
29          "Symbols resolved callback must be set");
30   assert(this->NotifySymbolsReady && "Symbols ready callback must be set");
31   OutstandingResolutions = OutstandingFinalizations = Symbols.size();
32 }
33 
34 void AsynchronousSymbolQuery::setFailed(Error Err) {
35   OutstandingResolutions = OutstandingFinalizations = 0;
36   if (NotifySymbolsResolved)
37     NotifySymbolsResolved(std::move(Err));
38   else
39     NotifySymbolsReady(std::move(Err));
40 }
41 
42 void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name,
43                                             JITEvaluatedSymbol Sym) {
44   // If OutstandingResolutions is zero we must have errored out already. Just
45   // ignore this.
46   if (OutstandingResolutions == 0)
47     return;
48 
49   assert(!Symbols.count(Name) && "Symbol has already been assigned an address");
50   Symbols.insert(std::make_pair(std::move(Name), std::move(Sym)));
51   --OutstandingResolutions;
52   if (OutstandingResolutions == 0) {
53     NotifySymbolsResolved(std::move(Symbols));
54     // Null out NotifySymbolsResolved to indicate that we've already called it.
55     NotifySymbolsResolved = {};
56   }
57 }
58 
59 void AsynchronousSymbolQuery::notifySymbolFinalized() {
60   // If OutstandingFinalizations is zero we must have errored out already. Just
61   // ignore this.
62   if (OutstandingFinalizations == 0)
63     return;
64 
65   assert(OutstandingFinalizations > 0 && "All symbols already finalized");
66   --OutstandingFinalizations;
67   if (OutstandingFinalizations == 0)
68     NotifySymbolsReady(Error::success());
69 }
70 
71 VSO::MaterializationInfo::MaterializationInfo(
72     size_t SymbolsRemaining, std::unique_ptr<MaterializationUnit> MU)
73     : SymbolsRemaining(SymbolsRemaining), MU(std::move(MU)) {}
74 
75 VSO::SymbolTableEntry::SymbolTableEntry(
76     JITSymbolFlags Flags, MaterializationInfoIterator MaterializationInfoItr)
77     : Flags(JITSymbolFlags::FlagNames(Flags | JITSymbolFlags::NotMaterialized)),
78       MaterializationInfoItr(std::move(MaterializationInfoItr)) {
79   // FIXME: Assert flag sanity.
80 }
81 
82 VSO::SymbolTableEntry::SymbolTableEntry(JITEvaluatedSymbol Sym)
83     : Flags(Sym.getFlags()), Address(Sym.getAddress()) {
84   // FIXME: Assert flag sanity.
85 }
86 
87 VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other)
88     : Flags(Other.Flags), Address(0) {
89   if (Flags.isMaterialized())
90     Address = Other.Address;
91   else
92     MaterializationInfoItr = std::move(Other.MaterializationInfoItr);
93 }
94 
95 VSO::SymbolTableEntry::~SymbolTableEntry() { destroy(); }
96 
97 VSO::SymbolTableEntry &VSO::SymbolTableEntry::
98 operator=(JITEvaluatedSymbol Sym) {
99   destroy();
100   Flags = Sym.getFlags();
101   Address = Sym.getAddress();
102   return *this;
103 }
104 
105 void VSO::SymbolTableEntry::destroy() {
106   if (!Flags.isMaterialized())
107     MaterializationInfoItr.~MaterializationInfoIterator();
108 }
109 
110 JITSymbolFlags VSO::SymbolTableEntry::getFlags() const { return Flags; }
111 
112 void VSO::SymbolTableEntry::replaceWith(
113     VSO &V, SymbolStringPtr Name, JITSymbolFlags NewFlags,
114     MaterializationInfoIterator NewMaterializationInfoItr) {
115   bool ReplaceExistingLazyDefinition = !Flags.isMaterialized();
116   Flags = NewFlags;
117   if (ReplaceExistingLazyDefinition) {
118     // If we are replacing an existing lazy definition with a stronger one,
119     // we need to notify the old lazy definition to discard its definition.
120     assert((*MaterializationInfoItr)->MU != nullptr &&
121            (*MaterializationInfoItr)->Symbols.count(Name) == 0 &&
122            (*MaterializationInfoItr)->PendingResolution.count(Name) == 0 &&
123            (*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 &&
124            "Attempt to replace materializer during materialization");
125 
126     if (--(*MaterializationInfoItr)->SymbolsRemaining == 0)
127       V.MaterializationInfos.erase(MaterializationInfoItr);
128   }
129   MaterializationInfoItr = std::move(NewMaterializationInfoItr);
130 }
131 
132 std::unique_ptr<MaterializationUnit>
133 VSO::SymbolTableEntry::query(SymbolStringPtr Name,
134                              std::shared_ptr<AsynchronousSymbolQuery> Query) {
135   if (Flags.isMaterialized()) {
136     Query->setDefinition(std::move(Name), JITEvaluatedSymbol(Address, Flags));
137     Query->notifySymbolFinalized();
138     return nullptr;
139   } else {
140     if ((*MaterializationInfoItr)->MU) {
141       assert((*MaterializationInfoItr)->PendingResolution.count(Name) == 0 &&
142              (*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 &&
143              "Materializer should have been activated on first query");
144       (*MaterializationInfoItr)
145           ->PendingResolution[Name]
146           .push_back(std::move(Query));
147       return std::move((*MaterializationInfoItr)->MU);
148     } else {
149       assert((*MaterializationInfoItr)->MU == nullptr &&
150              "Materializer should have been activated on first query");
151       auto SymValueItr = (*MaterializationInfoItr)->Symbols.find(Name);
152       if (SymValueItr == (*MaterializationInfoItr)->Symbols.end()) {
153         // Symbol has not been resolved yet.
154         (*MaterializationInfoItr)
155             ->PendingResolution[Name]
156             .push_back(std::move(Query));
157         return nullptr;
158       } else {
159         // Symbol has already resolved, is just waiting on finalization.
160         Query->setDefinition(Name, SymValueItr->second);
161         (*MaterializationInfoItr)
162             ->PendingFinalization[Name]
163             .push_back(std::move(Query));
164         return nullptr;
165       }
166     }
167   }
168 }
169 
170 void VSO::SymbolTableEntry::resolve(VSO &V, SymbolStringPtr Name,
171                                     JITEvaluatedSymbol Sym) {
172   if (Flags.isMaterialized()) {
173     // FIXME: Should we assert flag state here (flags must match except for
174     //        materialization state, overrides must be legal) or in the caller
175     //        in VSO?
176     Flags = Sym.getFlags();
177     Address = Sym.getAddress();
178   } else {
179     assert((*MaterializationInfoItr)->MU == nullptr &&
180            "Can not resolve a symbol that has not been materialized");
181     assert((*MaterializationInfoItr)->Symbols.count(Name) == 0 &&
182            "Symbol resolved more than once");
183 
184     // Add the symbol to the MaterializationInfo Symbols table.
185     (*MaterializationInfoItr)->Symbols[Name] = Sym;
186 
187     // If there are any queries waiting on this symbol then notify them that it
188     // has been resolved, then move them to the PendingFinalization list.
189     auto I = (*MaterializationInfoItr)->PendingResolution.find(Name);
190     if (I != (*MaterializationInfoItr)->PendingResolution.end()) {
191       assert((*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 &&
192              "Queries already pending finalization on newly resolved symbol");
193       auto &PendingFinalization =
194           (*MaterializationInfoItr)->PendingFinalization[Name];
195 
196       for (auto &Query : I->second) {
197         Query->setDefinition(Name, Sym);
198         PendingFinalization.push_back(Query);
199       }
200 
201       // Clear the PendingResolution list for this symbol.
202       (*MaterializationInfoItr)->PendingResolution.erase(I);
203     }
204   }
205 }
206 
207 void VSO::SymbolTableEntry::finalize(VSO &V, SymbolStringPtr Name) {
208   if (!Flags.isMaterialized()) {
209     auto SymI = (*MaterializationInfoItr)->Symbols.find(Name);
210     assert(SymI != (*MaterializationInfoItr)->Symbols.end() &&
211            "Finalizing an unresolved symbol");
212     auto Sym = SymI->second;
213     (*MaterializationInfoItr)->Symbols.erase(SymI);
214     auto I = (*MaterializationInfoItr)->PendingFinalization.find(Name);
215     if (I != (*MaterializationInfoItr)->PendingFinalization.end()) {
216       for (auto &Query : I->second)
217         Query->notifySymbolFinalized();
218       (*MaterializationInfoItr)->PendingFinalization.erase(I);
219     }
220 
221     if (--(*MaterializationInfoItr)->SymbolsRemaining == 0)
222       V.MaterializationInfos.erase(MaterializationInfoItr);
223 
224     // Destruct the iterator and re-define this entry using the final symbol
225     // value.
226     destroy();
227     Flags = Sym.getFlags();
228     Address = Sym.getAddress();
229   }
230   assert(Flags.isMaterialized() && "Trying to finalize not-emitted symbol");
231 }
232 
233 void VSO::SymbolTableEntry::discard(VSO &V, SymbolStringPtr Name) {
234   assert((*MaterializationInfoItr)->MU != nullptr &&
235          "Can not override a symbol after it has been materialized");
236   (*MaterializationInfoItr)->MU->discard(V, Name);
237   --(*MaterializationInfoItr)->SymbolsRemaining;
238 }
239 
240 VSO::RelativeLinkageStrength VSO::compareLinkage(Optional<JITSymbolFlags> Old,
241                                                  JITSymbolFlags New) {
242   if (Old == None)
243     return llvm::orc::VSO::NewDefinitionIsStronger;
244 
245   if (Old->isStrong()) {
246     if (New.isStrong())
247       return llvm::orc::VSO::DuplicateDefinition;
248     else
249       return llvm::orc::VSO::ExistingDefinitionIsStronger;
250   } else {
251     if (New.isStrong())
252       return llvm::orc::VSO::NewDefinitionIsStronger;
253     else
254       return llvm::orc::VSO::ExistingDefinitionIsStronger;
255   }
256 }
257 
258 VSO::RelativeLinkageStrength
259 VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const {
260   auto I = Symbols.find(Name);
261   return compareLinkage(I == Symbols.end()
262                             ? None
263                             : Optional<JITSymbolFlags>(I->second.getFlags()),
264                         NewFlags);
265 }
266 
267 Error VSO::define(SymbolMap NewSymbols) {
268   Error Err = Error::success();
269   for (auto &KV : NewSymbols) {
270     auto I = Symbols.find(KV.first);
271     auto LinkageResult = compareLinkage(
272         I == Symbols.end() ? None
273                            : Optional<JITSymbolFlags>(I->second.getFlags()),
274         KV.second.getFlags());
275 
276     // Silently discard weaker definitions.
277     if (LinkageResult == ExistingDefinitionIsStronger)
278       continue;
279 
280     // Report duplicate definition errors.
281     if (LinkageResult == DuplicateDefinition) {
282       Err = joinErrors(std::move(Err),
283                        make_error<orc::DuplicateDefinition>(*KV.first));
284       continue;
285     }
286 
287     if (I != Symbols.end()) {
288       // This is an override -- discard the overridden definition and overwrite.
289       I->second.discard(*this, KV.first);
290       I->second = std::move(KV.second);
291     } else
292       Symbols.insert(std::make_pair(KV.first, std::move(KV.second)));
293   }
294   return Err;
295 }
296 
297 Error VSO::defineLazy(std::unique_ptr<MaterializationUnit> MU) {
298 
299   auto NewSymbols = MU->getSymbols();
300 
301   auto MaterializationInfoItr =
302       MaterializationInfos
303           .insert(llvm::make_unique<MaterializationInfo>(NewSymbols.size(),
304                                                          std::move(MU)))
305           .first;
306 
307   Error Err = Error::success();
308   for (auto &KV : NewSymbols) {
309     auto I = Symbols.find(KV.first);
310 
311     auto LinkageResult = compareLinkage(
312         I == Symbols.end() ? None
313                            : Optional<JITSymbolFlags>(I->second.getFlags()),
314         KV.second);
315 
316     // Discard weaker definitions.
317     if (LinkageResult == ExistingDefinitionIsStronger) {
318       (*MaterializationInfoItr)->MU->discard(*this, KV.first);
319       assert((*MaterializationInfoItr)->SymbolsRemaining > 0 &&
320              "Discarding non-existant symbols?");
321       --(*MaterializationInfoItr)->SymbolsRemaining;
322       continue;
323     }
324 
325     // Report duplicate definition errors.
326     if (LinkageResult == DuplicateDefinition) {
327       Err = joinErrors(std::move(Err),
328                        make_error<orc::DuplicateDefinition>(*KV.first));
329       // Duplicate definitions are discarded, so remove the duplicates from
330       // materializer.
331       assert((*MaterializationInfoItr)->SymbolsRemaining > 0 &&
332              "Discarding non-existant symbols?");
333       --(*MaterializationInfoItr)->SymbolsRemaining;
334       continue;
335     }
336 
337     if (I != Symbols.end())
338       I->second.replaceWith(*this, KV.first, KV.second, MaterializationInfoItr);
339     else
340       Symbols.emplace(std::make_pair(
341           KV.first, SymbolTableEntry(KV.second, MaterializationInfoItr)));
342   }
343 
344   // If we ended up overriding all definitions in this materializer then delete
345   // it.
346   if ((*MaterializationInfoItr)->SymbolsRemaining == 0)
347     MaterializationInfos.erase(MaterializationInfoItr);
348 
349   return Err;
350 }
351 
352 void VSO::resolve(SymbolMap SymbolValues) {
353   for (auto &KV : SymbolValues) {
354     auto I = Symbols.find(KV.first);
355     assert(I != Symbols.end() && "Resolving symbol not present in this dylib");
356     I->second.resolve(*this, KV.first, std::move(KV.second));
357   }
358 }
359 
360 void VSO::finalize(SymbolNameSet SymbolsToFinalize) {
361   for (auto &S : SymbolsToFinalize) {
362     auto I = Symbols.find(S);
363     assert(I != Symbols.end() && "Finalizing symbol not present in this dylib");
364     I->second.finalize(*this, S);
365   }
366 }
367 
368 SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) {
369 
370   for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
371     auto Tmp = I++;
372     auto SymI = Symbols.find(*Tmp);
373 
374     // If the symbol isn't in this dylib then just continue.
375     if (SymI == Symbols.end())
376       continue;
377 
378     Names.erase(Tmp);
379 
380     Flags[SymI->first] =
381         JITSymbolFlags::stripTransientFlags(SymI->second.getFlags());
382   }
383 
384   return Names;
385 }
386 
387 VSO::LookupResult VSO::lookup(std::shared_ptr<AsynchronousSymbolQuery> Query,
388                               SymbolNameSet Names) {
389   MaterializationUnitList MaterializationUnits;
390 
391   for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
392     auto Tmp = I++;
393     auto SymI = Symbols.find(*Tmp);
394 
395     // If the symbol isn't in this dylib then just continue.
396     if (SymI == Symbols.end())
397       continue;
398 
399     // The symbol is in the dylib. Erase it from Names and proceed.
400     Names.erase(Tmp);
401 
402     // Forward the query to the given SymbolTableEntry, and if it return a
403     // layer to perform materialization with, add that to the
404     // MaterializationWork map.
405     if (auto MU = SymI->second.query(SymI->first, Query))
406       MaterializationUnits.push_back(std::move(MU));
407   }
408 
409   return {std::move(MaterializationUnits), std::move(Names)};
410 }
411 
412 Expected<SymbolMap> lookup(const std::vector<VSO *> &VSOs, SymbolNameSet Names,
413                            MaterializationDispatcher DispatchMaterialization) {
414 #if LLVM_ENABLE_THREADS
415   // In the threaded case we use promises to return the results.
416   std::promise<SymbolMap> PromisedResult;
417   std::mutex ErrMutex;
418   Error ResolutionError = Error::success();
419   std::promise<void> PromisedReady;
420   Error ReadyError = Error::success();
421   auto OnResolve = [&](Expected<SymbolMap> Result) {
422     if (Result)
423       PromisedResult.set_value(std::move(*Result));
424     else {
425       {
426         ErrorAsOutParameter _(&ResolutionError);
427         std::lock_guard<std::mutex> Lock(ErrMutex);
428         ResolutionError = Result.takeError();
429       }
430       PromisedResult.set_value(SymbolMap());
431     }
432   };
433   auto OnReady = [&](Error Err) {
434     if (Err) {
435       ErrorAsOutParameter _(&ReadyError);
436       std::lock_guard<std::mutex> Lock(ErrMutex);
437       ReadyError = std::move(Err);
438     }
439     PromisedReady.set_value();
440   };
441 #else
442   SymbolMap Result;
443   Error ResolutionError = Error::success();
444   Error ReadyError = Error::success();
445 
446   auto OnResolve = [&](Expected<SymbolMap> R) {
447     ErrorAsOutParameter _(&ResolutionError);
448     if (R)
449       Result = std::move(*R);
450     else
451       ResolutionError = R.takeError();
452   };
453   auto OnReady = [&](Error Err) {
454     ErrorAsOutParameter _(&ReadyError);
455     if (Err)
456       ReadyError = std::move(Err);
457   };
458 #endif
459 
460   auto Query = std::make_shared<AsynchronousSymbolQuery>(
461       Names, std::move(OnResolve), std::move(OnReady));
462   SymbolNameSet UnresolvedSymbols(std::move(Names));
463 
464   for (auto *V : VSOs) {
465 
466     if (UnresolvedSymbols.empty())
467       break;
468 
469     assert(V && "VSO pointers in VSOs list should be non-null");
470     auto LR = V->lookup(Query, UnresolvedSymbols);
471     UnresolvedSymbols = std::move(LR.UnresolvedSymbols);
472 
473     for (auto &MU : LR.MaterializationUnits)
474       DispatchMaterialization(*V, std::move(MU));
475   }
476 
477 #if LLVM_ENABLE_THREADS
478   auto ResultFuture = PromisedResult.get_future();
479   auto Result = ResultFuture.get();
480 
481   {
482     std::lock_guard<std::mutex> Lock(ErrMutex);
483     if (ResolutionError) {
484       // ReadyError will never be assigned. Consume the success value.
485       cantFail(std::move(ReadyError));
486       return std::move(ResolutionError);
487     }
488   }
489 
490   auto ReadyFuture = PromisedReady.get_future();
491   ReadyFuture.get();
492 
493   {
494     std::lock_guard<std::mutex> Lock(ErrMutex);
495     if (ReadyError)
496       return std::move(ReadyError);
497   }
498 
499   return std::move(Result);
500 
501 #else
502   if (ResolutionError) {
503     // ReadyError will never be assigned. Consume the success value.
504     cantFail(std::move(ReadyError));
505     return std::move(ResolutionError);
506   }
507 
508   if (ReadyError)
509     return std::move(ReadyError);
510 
511   return Result;
512 #endif
513 }
514 
515 /// @brief Look up a symbol by searching a list of VSOs.
516 Expected<JITEvaluatedSymbol>
517 lookup(const std::vector<VSO *> VSOs, SymbolStringPtr Name,
518        MaterializationDispatcher DispatchMaterialization) {
519   SymbolNameSet Names({Name});
520   if (auto ResultMap =
521           lookup(VSOs, std::move(Names), std::move(DispatchMaterialization))) {
522     assert(ResultMap->size() == 1 && "Unexpected number of results");
523     assert(ResultMap->count(Name) && "Missing result for symbol");
524     return ResultMap->begin()->second;
525   } else
526     return ResultMap.takeError();
527 }
528 
529 void ExecutionSession::logErrorsToStdErr(Error Err) {
530   logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: ");
531 }
532 
533 } // End namespace orc.
534 } // End namespace llvm.
535