1 //===--------- Core.cpp - Core ORC APIs (SymbolSource, 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 SymbolResolver::anchor() {}
21 void SymbolSource::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     JITSymbolFlags Flags, std::shared_ptr<SymbolSource> Source)
73     : Flags(std::move(Flags)), Source(std::move(Source)) {}
74 
75 JITSymbolFlags VSO::MaterializationInfo::getFlags() const { return Flags; }
76 
77 JITTargetAddress VSO::MaterializationInfo::getAddress() const {
78   return Address;
79 }
80 
81 void VSO::MaterializationInfo::replaceWithSource(
82     VSO &V, SymbolStringPtr Name, JITSymbolFlags NewFlags,
83     std::shared_ptr<SymbolSource> NewSource) {
84   assert(Address == 0 && PendingResolution.empty() &&
85          PendingFinalization.empty() &&
86          "Cannot replace source during or after materialization");
87   Source->discard(V, Name);
88   Flags = std::move(NewFlags);
89   Source = std::move(NewSource);
90 }
91 
92 std::shared_ptr<SymbolSource> VSO::MaterializationInfo::query(
93     SymbolStringPtr Name, std::shared_ptr<AsynchronousSymbolQuery> Query) {
94   if (Address == 0) {
95     PendingResolution.push_back(std::move(Query));
96     auto S = std::move(Source);
97     Source = nullptr;
98     return S;
99   }
100 
101   Query->setDefinition(Name, JITEvaluatedSymbol(Address, Flags));
102   PendingFinalization.push_back(std::move(Query));
103   return nullptr;
104 }
105 
106 void VSO::MaterializationInfo::resolve(VSO &V, SymbolStringPtr Name,
107                                        JITEvaluatedSymbol Sym) {
108   if (Source) {
109     Source->discard(V, Name);
110     Source = nullptr;
111   }
112 
113   // FIXME: Sanity check flags?
114   Flags = Sym.getFlags();
115   Address = Sym.getAddress();
116   for (auto &Query : PendingResolution) {
117     Query->setDefinition(Name, std::move(Sym));
118     PendingFinalization.push_back(std::move(Query));
119   }
120   PendingResolution = {};
121 }
122 
123 void VSO::MaterializationInfo::finalize() {
124   for (auto &Query : PendingFinalization)
125     Query->notifySymbolFinalized();
126   PendingFinalization = {};
127 }
128 
129 VSO::SymbolTableEntry::SymbolTableEntry(JITSymbolFlags Flags,
130                                         std::shared_ptr<SymbolSource> Source)
131     : Flags(JITSymbolFlags::FlagNames(Flags | JITSymbolFlags::NotMaterialized)),
132       MatInfo(
133           llvm::make_unique<MaterializationInfo>(Flags, std::move(Source))) {
134   // FIXME: Assert flag sanity.
135 }
136 
137 VSO::SymbolTableEntry::SymbolTableEntry(JITEvaluatedSymbol Sym)
138     : Flags(Sym.getFlags()), Address(Sym.getAddress()) {
139   // FIXME: Assert flag sanity.
140 }
141 
142 VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other)
143     : Flags(Other.Flags), Address(0) {
144   if (Flags.isMaterialized())
145     Address = Other.Address;
146   else
147     MatInfo = std::move(Other.MatInfo);
148 }
149 
150 VSO::SymbolTableEntry::~SymbolTableEntry() {
151   if (!Flags.isMaterialized())
152     MatInfo.std::unique_ptr<MaterializationInfo>::~unique_ptr();
153 }
154 
155 JITSymbolFlags VSO::SymbolTableEntry::getFlags() const { return Flags; }
156 
157 void VSO::SymbolTableEntry::replaceWithSource(
158     VSO &V, SymbolStringPtr Name, JITSymbolFlags NewFlags,
159     std::shared_ptr<SymbolSource> NewSource) {
160   bool ReplaceExisting = !Flags.isMaterialized();
161   Flags = NewFlags;
162   if (ReplaceExisting)
163     MatInfo->replaceWithSource(V, Name, Flags, std::move(NewSource));
164   else
165     new (&MatInfo) std::unique_ptr<MaterializationInfo>(
166         llvm::make_unique<MaterializationInfo>(Flags, std::move(NewSource)));
167 }
168 
169 std::shared_ptr<SymbolSource>
170 VSO::SymbolTableEntry::query(SymbolStringPtr Name,
171                              std::shared_ptr<AsynchronousSymbolQuery> Query) {
172   if (Flags.isMaterialized()) {
173     Query->setDefinition(std::move(Name), JITEvaluatedSymbol(Address, Flags));
174     Query->notifySymbolFinalized();
175     return nullptr;
176   } else
177     return MatInfo->query(std::move(Name), std::move(Query));
178 }
179 
180 void VSO::SymbolTableEntry::resolve(VSO &V, SymbolStringPtr Name,
181                                     JITEvaluatedSymbol Sym) {
182   if (Flags.isMaterialized()) {
183     // FIXME: Should we assert flag state here (flags must match except for
184     //        materialization state, overrides must be legal) or in the caller
185     //        in VSO?
186     Flags = Sym.getFlags();
187     Address = Sym.getAddress();
188   } else
189     MatInfo->resolve(V, std::move(Name), std::move(Sym));
190 }
191 
192 void VSO::SymbolTableEntry::finalize() {
193   if (!Flags.isMaterialized()) {
194     auto TmpMatInfo = std::move(MatInfo);
195     MatInfo.std::unique_ptr<MaterializationInfo>::~unique_ptr();
196     // FIXME: Assert flag sanity?
197     Flags = TmpMatInfo->getFlags();
198     Address = TmpMatInfo->getAddress();
199     TmpMatInfo->finalize();
200   }
201   assert(Flags.isMaterialized() && "Trying to finalize not-emitted symbol");
202 }
203 
204 VSO::RelativeLinkageStrength VSO::compareLinkage(Optional<JITSymbolFlags> Old,
205                                                  JITSymbolFlags New) {
206   if (Old == None)
207     return llvm::orc::VSO::NewDefinitionIsStronger;
208 
209   if (Old->isStrong()) {
210     if (New.isStrong())
211       return llvm::orc::VSO::DuplicateDefinition;
212     else
213       return llvm::orc::VSO::ExistingDefinitionIsStronger;
214   } else {
215     if (New.isStrong())
216       return llvm::orc::VSO::NewDefinitionIsStronger;
217     else
218       return llvm::orc::VSO::ExistingDefinitionIsStronger;
219   }
220 }
221 
222 VSO::RelativeLinkageStrength
223 VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const {
224   auto I = Symbols.find(Name);
225   return compareLinkage(I == Symbols.end()
226                             ? None
227                             : Optional<JITSymbolFlags>(I->second.getFlags()),
228                         NewFlags);
229 }
230 
231 Error VSO::define(SymbolMap NewSymbols) {
232   Error Err = Error::success();
233   for (auto &KV : NewSymbols) {
234     auto I = Symbols.find(KV.first);
235     auto LinkageResult = compareLinkage(
236         I == Symbols.end() ? None
237                            : Optional<JITSymbolFlags>(I->second.getFlags()),
238         KV.second.getFlags());
239 
240     // Silently discard weaker definitions.
241     if (LinkageResult == ExistingDefinitionIsStronger)
242       continue;
243 
244     // Report duplicate definition errors.
245     if (LinkageResult == DuplicateDefinition) {
246       Err = joinErrors(std::move(Err),
247                        make_error<orc::DuplicateDefinition>(*KV.first));
248       continue;
249     }
250 
251     if (I != Symbols.end()) {
252       I->second.resolve(*this, KV.first, std::move(KV.second));
253       I->second.finalize();
254     } else
255       Symbols.insert(std::make_pair(KV.first, std::move(KV.second)));
256   }
257   return Err;
258 }
259 
260 Error VSO::defineLazy(const SymbolFlagsMap &NewSymbols,
261                       std::shared_ptr<SymbolSource> Source) {
262   Error Err = Error::success();
263   for (auto &KV : NewSymbols) {
264     auto I = Symbols.find(KV.first);
265 
266     auto LinkageResult = compareLinkage(
267         I == Symbols.end() ? None
268                            : Optional<JITSymbolFlags>(I->second.getFlags()),
269         KV.second);
270 
271     // Discard weaker definitions.
272     if (LinkageResult == ExistingDefinitionIsStronger)
273       Source->discard(*this, KV.first);
274 
275     // Report duplicate definition errors.
276     if (LinkageResult == DuplicateDefinition) {
277       Err = joinErrors(std::move(Err),
278                        make_error<orc::DuplicateDefinition>(*KV.first));
279       continue;
280     }
281 
282     if (I != Symbols.end())
283       I->second.replaceWithSource(*this, KV.first, KV.second, Source);
284     else
285       Symbols.emplace(
286           std::make_pair(KV.first, SymbolTableEntry(KV.second, Source)));
287   }
288   return Err;
289 }
290 
291 void VSO::resolve(SymbolMap SymbolValues) {
292   for (auto &KV : SymbolValues) {
293     auto I = Symbols.find(KV.first);
294     assert(I != Symbols.end() && "Resolving symbol not present in this dylib");
295     I->second.resolve(*this, KV.first, std::move(KV.second));
296   }
297 }
298 
299 void VSO::finalize(SymbolNameSet SymbolsToFinalize) {
300   for (auto &S : SymbolsToFinalize) {
301     auto I = Symbols.find(S);
302     assert(I != Symbols.end() && "Finalizing symbol not present in this dylib");
303     I->second.finalize();
304   }
305 }
306 
307 SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) {
308 
309   for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
310     auto Tmp = I++;
311     auto SymI = Symbols.find(*Tmp);
312 
313     // If the symbol isn't in this dylib then just continue.
314     if (SymI == Symbols.end())
315       continue;
316 
317     Names.erase(Tmp);
318 
319     Flags[SymI->first] =
320         JITSymbolFlags::stripTransientFlags(SymI->second.getFlags());
321   }
322 
323   return Names;
324 }
325 
326 VSO::LookupResult VSO::lookup(std::shared_ptr<AsynchronousSymbolQuery> Query,
327                               SymbolNameSet Names) {
328   SourceWorkMap MaterializationWork;
329 
330   for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
331     auto Tmp = I++;
332     auto SymI = Symbols.find(*Tmp);
333 
334     // If the symbol isn't in this dylib then just continue.
335     if (SymI == Symbols.end())
336       continue;
337 
338     // The symbol is in the dylib. Erase it from Names and proceed.
339     Names.erase(Tmp);
340 
341     // Forward the query to the given SymbolTableEntry, and if it return a
342     // layer to perform materialization with, add that to the
343     // MaterializationWork map.
344     if (auto Source = SymI->second.query(SymI->first, Query))
345       MaterializationWork[Source].insert(SymI->first);
346   }
347 
348   return {std::move(MaterializationWork), std::move(Names)};
349 }
350 
351 Expected<SymbolMap> lookup(const std::vector<VSO *> &VSOs, SymbolNameSet Names,
352                            MaterializationDispatcher DispatchMaterialization) {
353 #if LLVM_ENABLE_THREADS
354   // In the threaded case we use promises to return the results.
355   std::promise<SymbolMap> PromisedResult;
356   std::mutex ErrMutex;
357   Error ResolutionError = Error::success();
358   std::promise<void> PromisedReady;
359   Error ReadyError = Error::success();
360   auto OnResolve = [&](Expected<SymbolMap> Result) {
361     if (Result)
362       PromisedResult.set_value(std::move(*Result));
363     else {
364       {
365         ErrorAsOutParameter _(&ResolutionError);
366         std::lock_guard<std::mutex> Lock(ErrMutex);
367         ResolutionError = Result.takeError();
368       }
369       PromisedResult.set_value(SymbolMap());
370     }
371   };
372   auto OnReady = [&](Error Err) {
373     if (Err) {
374       ErrorAsOutParameter _(&ReadyError);
375       std::lock_guard<std::mutex> Lock(ErrMutex);
376       ReadyError = std::move(Err);
377     }
378     PromisedReady.set_value();
379   };
380 #else
381   SymbolMap Result;
382   Error ResolutionError = Error::success();
383   Error ReadyError = Error::success();
384 
385   auto OnResolve = [&](Expected<SymbolMap> R) {
386     ErrorAsOutParameter _(&ResolutionError);
387     if (R)
388       Result = std::move(*R);
389     else
390       ResolutionError = R.takeError();
391   };
392   auto OnReady = [&](Error Err) {
393     ErrorAsOutParameter _(&ReadyError);
394     if (Err)
395       ReadyError = std::move(Err);
396   };
397 #endif
398 
399   auto Query = std::make_shared<AsynchronousSymbolQuery>(
400       Names, std::move(OnResolve), std::move(OnReady));
401   SymbolNameSet UnresolvedSymbols(std::move(Names));
402 
403   for (auto *VSO : VSOs) {
404 
405     if (UnresolvedSymbols.empty())
406       break;
407 
408     assert(VSO && "VSO pointers in VSOs list should be non-null");
409     auto LR = VSO->lookup(Query, UnresolvedSymbols);
410     UnresolvedSymbols = std::move(LR.UnresolvedSymbols);
411 
412     for (auto I = LR.MaterializationWork.begin(),
413               E = LR.MaterializationWork.end();
414          I != E;) {
415       auto Tmp = I++;
416       std::shared_ptr<SymbolSource> Source = Tmp->first;
417       SymbolNameSet Names = std::move(Tmp->second);
418       LR.MaterializationWork.erase(Tmp);
419       DispatchMaterialization(*VSO, std::move(Source), std::move(Names));
420     }
421   }
422 
423 #if LLVM_ENABLE_THREADS
424   auto ResultFuture = PromisedResult.get_future();
425   auto Result = ResultFuture.get();
426 
427   {
428     std::lock_guard<std::mutex> Lock(ErrMutex);
429     if (ResolutionError) {
430       // ReadyError will never be assigned. Consume the success value.
431       cantFail(std::move(ReadyError));
432       return std::move(ResolutionError);
433     }
434   }
435 
436   auto ReadyFuture = PromisedReady.get_future();
437   ReadyFuture.get();
438 
439   {
440     std::lock_guard<std::mutex> Lock(ErrMutex);
441     if (ReadyError)
442       return std::move(ReadyError);
443   }
444 
445   return std::move(Result);
446 
447 #else
448   if (ResolutionError) {
449     // ReadyError will never be assigned. Consume the success value.
450     cantFail(std::move(ReadyError));
451     return std::move(ResolutionError);
452   }
453 
454   if (ReadyError)
455     return std::move(ReadyError);
456 
457   return Result;
458 #endif
459 }
460 
461 /// @brief Look up a symbol by searching a list of VSOs.
462 Expected<JITEvaluatedSymbol>
463 lookup(const std::vector<VSO *> VSOs, SymbolStringPtr Name,
464        MaterializationDispatcher DispatchMaterialization) {
465   SymbolNameSet Names({Name});
466   if (auto ResultMap =
467           lookup(VSOs, std::move(Names), std::move(DispatchMaterialization))) {
468     assert(ResultMap->size() == 1 && "Unexpected number of results");
469     assert(ResultMap->count(Name) && "Missing result for symbol");
470     return ResultMap->begin()->second;
471   } else
472     return ResultMap.takeError();
473 }
474 
475 ExecutionSession::ExecutionSession(SymbolStringPool &SSP) : SSP(SSP) {}
476 
477 VModuleKey ExecutionSession::allocateVModule() { return ++LastKey; }
478 
479 void ExecutionSession::releaseVModule(VModuleKey VMod) {
480   // FIXME: Recycle keys.
481 }
482 
483 void ExecutionSession::logErrorsToStdErr(Error Err) {
484   logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: ");
485 }
486 
487 } // End namespace orc.
488 } // End namespace llvm.
489