1 //===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===//
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 "OrcTestCommon.h"
11 #include "llvm/Config/llvm-config.h"
12 #include "llvm/ExecutionEngine/Orc/Core.h"
13 #include "gtest/gtest.h"
14 
15 #include <set>
16 #include <thread>
17 
18 using namespace llvm;
19 using namespace llvm::orc;
20 
21 namespace {
22 
23 class SimpleMaterializationUnit : public MaterializationUnit {
24 public:
25   using MaterializeFunction =
26       std::function<void(MaterializationResponsibility)>;
27   using DiscardFunction = std::function<void(const VSO &, SymbolStringPtr)>;
28   using DestructorFunction = std::function<void()>;
29 
30   SimpleMaterializationUnit(
31       SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize,
32       DiscardFunction Discard = DiscardFunction(),
33       DestructorFunction Destructor = DestructorFunction())
34       : MaterializationUnit(std::move(SymbolFlags)),
35         Materialize(std::move(Materialize)), Discard(std::move(Discard)),
36         Destructor(std::move(Destructor)) {}
37 
38   ~SimpleMaterializationUnit() override {
39     if (Destructor)
40       Destructor();
41   }
42 
43   void materialize(MaterializationResponsibility R) override {
44     Materialize(std::move(R));
45   }
46 
47   void discard(const VSO &V, SymbolStringPtr Name) override {
48     if (Discard)
49       Discard(V, std::move(Name));
50     else
51       llvm_unreachable("Discard not supported");
52   }
53 
54 private:
55   MaterializeFunction Materialize;
56   DiscardFunction Discard;
57   DestructorFunction Destructor;
58 };
59 
60 TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) {
61   SymbolStringPool SP;
62   auto Foo = SP.intern("foo");
63   constexpr JITTargetAddress FakeAddr = 0xdeadbeef;
64   SymbolNameSet Names({Foo});
65 
66   bool OnResolutionRun = false;
67   bool OnReadyRun = false;
68   auto OnResolution =
69       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
70         EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
71         auto &Resolved = Result->Symbols;
72         auto I = Resolved.find(Foo);
73         EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition";
74         EXPECT_EQ(I->second.getAddress(), FakeAddr)
75             << "Resolution returned incorrect result";
76         OnResolutionRun = true;
77       };
78   auto OnReady = [&](Error Err) {
79     cantFail(std::move(Err));
80     OnReadyRun = true;
81   };
82 
83   AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
84 
85   Q.resolve(Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported));
86 
87   EXPECT_TRUE(Q.isFullyResolved()) << "Expected query to be fully resolved";
88 
89   if (!Q.isFullyResolved())
90     return;
91 
92   Q.handleFullyResolved();
93 
94   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
95   EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
96 }
97 
98 TEST(CoreAPIsTest, ExecutionSessionFailQuery) {
99   ExecutionSession ES;
100   auto Foo = ES.getSymbolStringPool().intern("foo");
101   SymbolNameSet Names({Foo});
102 
103   bool OnResolutionRun = false;
104   bool OnReadyRun = false;
105 
106   auto OnResolution =
107       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
108         EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success";
109         auto Msg = toString(Result.takeError());
110         EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result";
111         OnResolutionRun = true;
112       };
113   auto OnReady = [&](Error Err) {
114     cantFail(std::move(Err));
115     OnReadyRun = true;
116   };
117 
118   AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
119 
120   ES.failQuery(Q, make_error<StringError>("xyz", inconvertibleErrorCode()));
121 
122   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
123   EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
124 }
125 
126 TEST(CoreAPIsTest, SimpleAsynchronousSymbolQueryAgainstVSO) {
127   ExecutionSession ES;
128   auto Foo = ES.getSymbolStringPool().intern("foo");
129   constexpr JITTargetAddress FakeAddr = 0xdeadbeef;
130   SymbolNameSet Names({Foo});
131 
132   bool OnResolutionRun = false;
133   bool OnReadyRun = false;
134 
135   auto OnResolution =
136       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
137         EXPECT_TRUE(!!Result) << "Query unexpectedly returned error";
138         auto &Resolved = Result->Symbols;
139         auto I = Resolved.find(Foo);
140         EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition";
141         EXPECT_EQ(I->second.getAddress(), FakeAddr)
142             << "Resolution returned incorrect result";
143         OnResolutionRun = true;
144       };
145 
146   auto OnReady = [&](Error Err) {
147     cantFail(std::move(Err));
148     OnReadyRun = true;
149   };
150 
151   auto Q =
152       std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
153   auto &V = ES.createVSO("V");
154 
155   auto Defs = absoluteSymbols(
156       {{Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported)}});
157   cantFail(V.define(Defs));
158   assert(Defs == nullptr && "Defs should have been accepted");
159   V.lookup(Q, Names);
160 
161   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
162   EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
163 }
164 
165 TEST(CoreAPIsTest, LookupFlagsTest) {
166 
167   // Test that lookupFlags works on a predefined symbol, and does not trigger
168   // materialization of a lazy symbol.
169 
170   ExecutionSession ES;
171   auto Foo = ES.getSymbolStringPool().intern("foo");
172   auto Bar = ES.getSymbolStringPool().intern("bar");
173   auto Baz = ES.getSymbolStringPool().intern("baz");
174 
175   JITSymbolFlags FooFlags = JITSymbolFlags::Exported;
176   JITSymbolFlags BarFlags = static_cast<JITSymbolFlags::FlagNames>(
177       JITSymbolFlags::Exported | JITSymbolFlags::Weak);
178 
179   VSO &V = ES.createVSO("V");
180 
181   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
182       SymbolFlagsMap({{Bar, BarFlags}}),
183       [](MaterializationResponsibility R) {
184         llvm_unreachable("Symbol materialized on flags lookup");
185       });
186 
187   cantFail(V.define(
188       absoluteSymbols({{Foo, JITEvaluatedSymbol(0xdeadbeef, FooFlags)}})));
189   cantFail(V.define(std::move(MU)));
190 
191   SymbolNameSet Names({Foo, Bar, Baz});
192 
193   SymbolFlagsMap SymbolFlags;
194   auto SymbolsNotFound = V.lookupFlags(SymbolFlags, Names);
195 
196   EXPECT_EQ(SymbolsNotFound.size(), 1U) << "Expected one not-found symbol";
197   EXPECT_EQ(SymbolsNotFound.count(Baz), 1U) << "Expected Baz to be not-found";
198   EXPECT_EQ(SymbolFlags.size(), 2U)
199       << "Returned symbol flags contains unexpected results";
200   EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo";
201   EXPECT_EQ(SymbolFlags[Foo], FooFlags) << "Incorrect flags returned for Foo";
202   EXPECT_EQ(SymbolFlags.count(Bar), 1U)
203       << "Missing  lookupFlags result for Bar";
204   EXPECT_EQ(SymbolFlags[Bar], BarFlags) << "Incorrect flags returned for Bar";
205 }
206 
207 TEST(CoreAPIsTest, TestCircularDependenceInOneVSO) {
208 
209   ExecutionSession ES;
210 
211   auto &V = ES.createVSO("V");
212 
213   // Create three symbols: Foo, Bar and Baz.
214   auto Foo = ES.getSymbolStringPool().intern("foo");
215   auto FooFlags = JITSymbolFlags::Exported;
216   auto FooSym = JITEvaluatedSymbol(1U, FooFlags);
217 
218   auto Bar = ES.getSymbolStringPool().intern("bar");
219   auto BarFlags = JITSymbolFlags::Exported;
220   auto BarSym = JITEvaluatedSymbol(2U, BarFlags);
221 
222   auto Baz = ES.getSymbolStringPool().intern("baz");
223   auto BazFlags = JITSymbolFlags::Exported;
224   auto BazSym = JITEvaluatedSymbol(3U, BazFlags);
225 
226   // Create three MaterializationResponsibility objects: one for each symbol
227   // (these are optional because MaterializationResponsibility does not have
228   // a default constructor).
229   Optional<MaterializationResponsibility> FooR;
230   Optional<MaterializationResponsibility> BarR;
231   Optional<MaterializationResponsibility> BazR;
232 
233   // Create a MaterializationUnit for each symbol that moves the
234   // MaterializationResponsibility into one of the locals above.
235   auto FooMU = llvm::make_unique<SimpleMaterializationUnit>(
236       SymbolFlagsMap({{Foo, FooFlags}}),
237       [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
238 
239   auto BarMU = llvm::make_unique<SimpleMaterializationUnit>(
240       SymbolFlagsMap({{Bar, BarFlags}}),
241       [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
242 
243   auto BazMU = llvm::make_unique<SimpleMaterializationUnit>(
244       SymbolFlagsMap({{Baz, BazFlags}}),
245       [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); });
246 
247   // Define the symbols.
248   cantFail(V.define(FooMU));
249   cantFail(V.define(BarMU));
250   cantFail(V.define(BazMU));
251 
252   // Query each of the symbols to trigger materialization.
253   bool FooResolved = false;
254   bool FooReady = false;
255   auto FooQ = std::make_shared<AsynchronousSymbolQuery>(
256       SymbolNameSet({Foo}),
257       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> RR) {
258         cantFail(std::move(RR));
259         FooResolved = true;
260       },
261       [&](Error Err) {
262         cantFail(std::move(Err));
263         FooReady = true;
264       });
265   {
266     auto Unresolved = V.lookup(FooQ, {Foo});
267     EXPECT_TRUE(Unresolved.empty()) << "Failed to resolve \"Foo\"";
268   }
269 
270   bool BarResolved = false;
271   bool BarReady = false;
272   auto BarQ = std::make_shared<AsynchronousSymbolQuery>(
273       SymbolNameSet({Bar}),
274       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> RR) {
275         cantFail(std::move(RR));
276         BarResolved = true;
277       },
278       [&](Error Err) {
279         cantFail(std::move(Err));
280         BarReady = true;
281       });
282   {
283     auto Unresolved = V.lookup(BarQ, {Bar});
284     EXPECT_TRUE(Unresolved.empty()) << "Failed to resolve \"Bar\"";
285   }
286 
287   bool BazResolved = false;
288   bool BazReady = false;
289   auto BazQ = std::make_shared<AsynchronousSymbolQuery>(
290       SymbolNameSet({Baz}),
291       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> RR) {
292         cantFail(std::move(RR));
293         BazResolved = true;
294       },
295       [&](Error Err) {
296         cantFail(std::move(Err));
297         BazReady = true;
298       });
299   {
300     auto Unresolved = V.lookup(BazQ, {Baz});
301     EXPECT_TRUE(Unresolved.empty()) << "Failed to resolve \"Baz\"";
302   }
303 
304   FooR->addDependencies({{&V, SymbolNameSet({Bar})}});
305   BarR->addDependencies({{&V, SymbolNameSet({Baz})}});
306   BazR->addDependencies({{&V, SymbolNameSet({Foo})}});
307 
308   EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet";
309   EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet";
310   EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet";
311 
312   FooR->resolve({{Foo, FooSym}});
313   BarR->resolve({{Bar, BarSym}});
314   BazR->resolve({{Baz, BazSym}});
315 
316   EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now";
317   EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now";
318   EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now";
319 
320   EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet";
321   EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet";
322   EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet";
323 
324   FooR->finalize();
325   BarR->finalize();
326 
327   // Verify that nothing is ready until the circular dependence is resolved.
328 
329   EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready";
330   EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready";
331   EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready";
332 
333   BazR->finalize();
334 
335   // Verify that everything becomes ready once the circular dependence resolved.
336   EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now";
337   EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now";
338   EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now";
339 }
340 
341 TEST(CoreAPIsTest, DropMaterializerWhenEmpty) {
342   ExecutionSession ES;
343   auto Foo = ES.getSymbolStringPool().intern("foo");
344   auto Bar = ES.getSymbolStringPool().intern("bar");
345 
346   bool DestructorRun = false;
347 
348   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
349       SymbolFlagsMap(
350           {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}),
351       [](MaterializationResponsibility R) {
352         llvm_unreachable("Unexpected call to materialize");
353       },
354       [&](const VSO &V, SymbolStringPtr Name) {
355         EXPECT_TRUE(Name == Foo || Name == Bar)
356             << "Discard of unexpected symbol?";
357       },
358       [&]() { DestructorRun = true; });
359 
360   auto &V = ES.createVSO("V");
361 
362   cantFail(V.define(MU));
363 
364   auto FooSym = JITEvaluatedSymbol(1, JITSymbolFlags::Exported);
365   auto BarSym = JITEvaluatedSymbol(2, JITSymbolFlags::Exported);
366   cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
367 
368   EXPECT_FALSE(DestructorRun)
369       << "MaterializationUnit should not have been destroyed yet";
370 
371   cantFail(V.define(absoluteSymbols({{Bar, BarSym}})));
372 
373   EXPECT_TRUE(DestructorRun)
374       << "MaterializationUnit should have been destroyed";
375 }
376 
377 TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) {
378 
379   constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
380   constexpr JITTargetAddress FakeBarAddr = 0xcafef00d;
381 
382   ExecutionSession ES;
383   auto Foo = ES.getSymbolStringPool().intern("foo");
384   auto Bar = ES.getSymbolStringPool().intern("bar");
385 
386   bool FooMaterialized = false;
387   bool BarDiscarded = false;
388 
389   auto &V = ES.createVSO("V");
390 
391   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
392       SymbolFlagsMap(
393           {{Foo, JITSymbolFlags::Exported},
394            {Bar, static_cast<JITSymbolFlags::FlagNames>(
395                      JITSymbolFlags::Exported | JITSymbolFlags::Weak)}}),
396       [&](MaterializationResponsibility R) {
397         assert(BarDiscarded && "Bar should have been discarded by this point");
398         SymbolMap SymbolsToResolve;
399         SymbolsToResolve[Foo] =
400             JITEvaluatedSymbol(FakeFooAddr, JITSymbolFlags::Exported);
401         R.resolve(std::move(SymbolsToResolve));
402         R.finalize();
403         FooMaterialized = true;
404       },
405       [&](const VSO &V, SymbolStringPtr Name) {
406         EXPECT_EQ(Name, Bar) << "Expected Name to be Bar";
407         BarDiscarded = true;
408       });
409 
410   cantFail(V.define(MU));
411 
412   ;
413   cantFail(V.define(absoluteSymbols(
414       {{Bar, JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported)}})));
415 
416   SymbolNameSet Names({Foo});
417 
418   bool OnResolutionRun = false;
419   bool OnReadyRun = false;
420 
421   auto OnResolution =
422       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
423         EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
424         auto I = Result->Symbols.find(Foo);
425         EXPECT_NE(I, Result->Symbols.end())
426             << "Could not find symbol definition";
427         EXPECT_EQ(I->second.getAddress(), FakeFooAddr)
428             << "Resolution returned incorrect result";
429         OnResolutionRun = true;
430       };
431 
432   auto OnReady = [&](Error Err) {
433     cantFail(std::move(Err));
434     OnReadyRun = true;
435   };
436 
437   auto Q =
438       std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
439 
440   auto Unresolved = V.lookup(std::move(Q), Names);
441 
442   EXPECT_TRUE(Unresolved.empty()) << "Could not find Foo in dylib";
443   EXPECT_TRUE(FooMaterialized) << "Foo was not materialized";
444   EXPECT_TRUE(BarDiscarded) << "Bar was not discarded";
445   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
446   EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
447 }
448 
449 TEST(CoreAPIsTest, DefineMaterializingSymbol) {
450   ExecutionSession ES;
451   auto Foo = ES.getSymbolStringPool().intern("foo");
452   auto Bar = ES.getSymbolStringPool().intern("bar");
453 
454   auto FooSym = JITEvaluatedSymbol(1, JITSymbolFlags::Exported);
455   auto BarSym = JITEvaluatedSymbol(2, JITSymbolFlags::Exported);
456 
457   bool ExpectNoMoreMaterialization = false;
458   ES.setDispatchMaterialization(
459       [&](VSO &V, std::unique_ptr<MaterializationUnit> MU) {
460         if (ExpectNoMoreMaterialization)
461           ADD_FAILURE() << "Unexpected materialization";
462         MU->doMaterialize(V);
463       });
464 
465   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
466       SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
467       [&](MaterializationResponsibility R) {
468         cantFail(
469             R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}})));
470         R.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}}));
471         R.finalize();
472       });
473 
474   auto &V = ES.createVSO("V");
475   cantFail(V.define(MU));
476 
477   auto OnResolution1 =
478       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
479         cantFail(std::move(Result));
480       };
481 
482   auto OnReady1 = [](Error Err) { cantFail(std::move(Err)); };
483 
484   auto Q1 = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Foo}),
485                                                       OnResolution1, OnReady1);
486 
487   V.lookup(std::move(Q1), {Foo});
488 
489   bool BarResolved = false;
490   auto OnResolution2 =
491       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
492         auto R = cantFail(std::move(Result));
493         EXPECT_EQ(R.Symbols.size(), 1U) << "Expected to resolve one symbol";
494         EXPECT_EQ(R.Symbols.count(Bar), 1U) << "Expected to resolve 'Bar'";
495         EXPECT_EQ(R.Symbols[Bar].getAddress(), BarSym.getAddress())
496             << "Expected Bar == BarSym";
497         BarResolved = true;
498       };
499 
500   auto OnReady2 = [](Error Err) { cantFail(std::move(Err)); };
501 
502   auto Q2 = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Bar}),
503                                                       OnResolution2, OnReady2);
504 
505   ExpectNoMoreMaterialization = true;
506   V.lookup(std::move(Q2), {Bar});
507 
508   EXPECT_TRUE(BarResolved) << "Bar should have been resolved";
509 }
510 
511 TEST(CoreAPIsTest, FailResolution) {
512   ExecutionSession ES;
513   auto Foo = ES.getSymbolStringPool().intern("foo");
514   auto Bar = ES.getSymbolStringPool().intern("bar");
515 
516   SymbolNameSet Names({Foo, Bar});
517 
518   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
519       SymbolFlagsMap(
520           {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}),
521       [&](MaterializationResponsibility R) { R.failMaterialization(); });
522 
523   auto &V = ES.createVSO("V");
524 
525   cantFail(V.define(MU));
526 
527   auto OnResolution =
528       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
529         handleAllErrors(Result.takeError(),
530                         [&](FailedToMaterialize &F) {
531                           EXPECT_EQ(F.getSymbols(), Names)
532                               << "Expected to fail on symbols in Names";
533                         },
534                         [](ErrorInfoBase &EIB) {
535                           std::string ErrMsg;
536                           {
537                             raw_string_ostream ErrOut(ErrMsg);
538                             EIB.log(ErrOut);
539                           }
540                           ADD_FAILURE()
541                               << "Expected a FailedToResolve error. Got:\n"
542                               << ErrMsg;
543                         });
544       };
545 
546   auto OnReady = [](Error Err) {
547     cantFail(std::move(Err));
548     ADD_FAILURE() << "OnReady should never be called";
549   };
550 
551   auto Q =
552       std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
553 
554   V.lookup(std::move(Q), Names);
555 }
556 
557 TEST(CoreAPIsTest, TestLambdaSymbolResolver) {
558   JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported);
559   JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported);
560 
561   ExecutionSession ES;
562 
563   auto Foo = ES.getSymbolStringPool().intern("foo");
564   auto Bar = ES.getSymbolStringPool().intern("bar");
565   auto Baz = ES.getSymbolStringPool().intern("baz");
566 
567   auto &V = ES.createVSO("V");
568   cantFail(V.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));
569 
570   auto Resolver = createSymbolResolver(
571       [&](SymbolFlagsMap &SymbolFlags, const SymbolNameSet &Symbols) {
572         return V.lookupFlags(SymbolFlags, Symbols);
573       },
574       [&](std::shared_ptr<AsynchronousSymbolQuery> Q, SymbolNameSet Symbols) {
575         return V.lookup(std::move(Q), Symbols);
576       });
577 
578   SymbolNameSet Symbols({Foo, Bar, Baz});
579 
580   SymbolFlagsMap SymbolFlags;
581   SymbolNameSet SymbolsNotFound = Resolver->lookupFlags(SymbolFlags, Symbols);
582 
583   EXPECT_EQ(SymbolFlags.size(), 2U)
584       << "lookupFlags returned the wrong number of results";
585   EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for foo";
586   EXPECT_EQ(SymbolFlags.count(Bar), 1U) << "Missing lookupFlags result for bar";
587   EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags())
588       << "Incorrect lookupFlags result for Foo";
589   EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags())
590       << "Incorrect lookupFlags result for Bar";
591   EXPECT_EQ(SymbolsNotFound.size(), 1U)
592       << "Expected one symbol not found in lookupFlags";
593   EXPECT_EQ(SymbolsNotFound.count(Baz), 1U)
594       << "Expected baz not to be found in lookupFlags";
595 
596   bool OnResolvedRun = false;
597 
598   auto OnResolved =
599       [&](Expected<AsynchronousSymbolQuery::ResolutionResult> Result) {
600         OnResolvedRun = true;
601         EXPECT_TRUE(!!Result) << "Unexpected error";
602         EXPECT_EQ(Result->Symbols.size(), 2U)
603             << "Unexpected number of resolved symbols";
604         EXPECT_EQ(Result->Symbols.count(Foo), 1U)
605             << "Missing lookup result for foo";
606         EXPECT_EQ(Result->Symbols.count(Bar), 1U)
607             << "Missing lookup result for bar";
608         EXPECT_EQ(Result->Symbols[Foo].getAddress(), FooSym.getAddress())
609             << "Incorrect address for foo";
610         EXPECT_EQ(Result->Symbols[Bar].getAddress(), BarSym.getAddress())
611             << "Incorrect address for bar";
612       };
613   auto OnReady = [&](Error Err) {
614     EXPECT_FALSE(!!Err) << "Finalization should never fail in this test";
615   };
616 
617   auto Q = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Foo, Bar}),
618                                                      OnResolved, OnReady);
619   auto Unresolved = Resolver->lookup(std::move(Q), Symbols);
620 
621   EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol";
622   EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to not be resolved";
623   EXPECT_TRUE(OnResolvedRun) << "OnResolved was never run";
624 }
625 
626 TEST(CoreAPIsTest, TestLookupWithUnthreadedMaterialization) {
627   constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
628   JITEvaluatedSymbol FooSym(FakeFooAddr, JITSymbolFlags::Exported);
629 
630   ExecutionSession ES(std::make_shared<SymbolStringPool>());
631   auto Foo = ES.getSymbolStringPool().intern("foo");
632 
633   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
634       SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
635       [&](MaterializationResponsibility R) {
636         R.resolve({{Foo, FooSym}});
637         R.finalize();
638       });
639 
640   auto &V = ES.createVSO("V");
641 
642   cantFail(V.define(MU));
643 
644   auto FooLookupResult = cantFail(lookup({&V}, Foo));
645 
646   EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
647       << "lookup returned an incorrect address";
648   EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
649       << "lookup returned incorrect flags";
650 }
651 
652 TEST(CoreAPIsTest, TestLookupWithThreadedMaterialization) {
653 #if LLVM_ENABLE_THREADS
654   constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
655   JITEvaluatedSymbol FooSym(FakeFooAddr, JITSymbolFlags::Exported);
656 
657   ExecutionSession ES(std::make_shared<SymbolStringPool>());
658 
659   std::thread MaterializationThread;
660   ES.setDispatchMaterialization(
661       [&](VSO &V, std::unique_ptr<MaterializationUnit> MU) {
662         auto SharedMU = std::shared_ptr<MaterializationUnit>(std::move(MU));
663         MaterializationThread =
664             std::thread([SharedMU, &V]() { SharedMU->doMaterialize(V); });
665       });
666   auto Foo = ES.getSymbolStringPool().intern("foo");
667 
668   auto &V = ES.createVSO("V");
669   cantFail(V.define(absoluteSymbols({{Foo, FooSym}})));
670 
671   auto FooLookupResult = cantFail(lookup({&V}, Foo));
672 
673   EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
674       << "lookup returned an incorrect address";
675   EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
676       << "lookup returned incorrect flags";
677   MaterializationThread.join();
678 #endif
679 }
680 
681 TEST(CoreAPIsTest, TestGetRequestedSymbolsAndDelegate) {
682   ExecutionSession ES;
683   auto Foo = ES.getSymbolStringPool().intern("foo");
684   auto Bar = ES.getSymbolStringPool().intern("bar");
685 
686   JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported);
687   JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported);
688 
689   SymbolNameSet Names({Foo, Bar});
690 
691   bool FooMaterialized = false;
692   bool BarMaterialized = false;
693 
694   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
695       SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
696       [&](MaterializationResponsibility R) {
697         auto Requested = R.getRequestedSymbols();
698         EXPECT_EQ(Requested.size(), 1U) << "Expected one symbol requested";
699         EXPECT_EQ(*Requested.begin(), Foo) << "Expected \"Foo\" requested";
700 
701         auto NewMU = llvm::make_unique<SimpleMaterializationUnit>(
702             SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
703             [&](MaterializationResponsibility R2) {
704               R2.resolve(SymbolMap({{Bar, BarSym}}));
705               R2.finalize();
706               BarMaterialized = true;
707             });
708 
709         R.delegate(std::move(NewMU));
710 
711         R.resolve(SymbolMap({{Foo, FooSym}}));
712         R.finalize();
713 
714         FooMaterialized = true;
715       });
716 
717   auto &V = ES.createVSO("V");
718 
719   cantFail(V.define(MU));
720 
721   EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet";
722   EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet";
723 
724   auto FooSymResult = cantFail(lookup({&V}, Foo));
725   EXPECT_EQ(FooSymResult.getAddress(), FooSym.getAddress())
726       << "Address mismatch for Foo";
727 
728   EXPECT_TRUE(FooMaterialized) << "Foo should be materialized now";
729   EXPECT_FALSE(BarMaterialized) << "Bar still should not be materialized";
730 
731   auto BarSymResult = cantFail(lookup({&V}, Bar));
732   EXPECT_EQ(BarSymResult.getAddress(), BarSym.getAddress())
733       << "Address mismatch for Bar";
734   EXPECT_TRUE(BarMaterialized) << "Bar should be materialized now";
735 }
736 
737 } // namespace
738