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 GetSymbolsFunction = std::function<SymbolFlagsMap()>;
26   using MaterializeFunction =
27       std::function<void(MaterializationResponsibility)>;
28   using DiscardFunction = std::function<void(const VSO &, SymbolStringPtr)>;
29   using DestructorFunction = std::function<void()>;
30 
31   SimpleMaterializationUnit(
32       GetSymbolsFunction GetSymbols, MaterializeFunction Materialize,
33       DiscardFunction Discard,
34       DestructorFunction Destructor = DestructorFunction())
35       : GetSymbols(std::move(GetSymbols)), Materialize(std::move(Materialize)),
36         Discard(std::move(Discard)), Destructor(std::move(Destructor)) {}
37 
38   ~SimpleMaterializationUnit() override {
39     if (Destructor)
40       Destructor();
41   }
42 
43   SymbolFlagsMap getSymbols() override { return GetSymbols(); }
44 
45   void materialize(MaterializationResponsibility R) override {
46     Materialize(std::move(R));
47   }
48 
49   void discard(const VSO &V, SymbolStringPtr Name) override {
50     Discard(V, std::move(Name));
51   }
52 
53 private:
54   GetSymbolsFunction GetSymbols;
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 = [&](Expected<SymbolMap> Result) {
69     EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
70     auto I = Result->find(Foo);
71     EXPECT_NE(I, Result->end()) << "Could not find symbol definition";
72     EXPECT_EQ(I->second.getAddress(), FakeAddr)
73         << "Resolution returned incorrect result";
74     OnResolutionRun = true;
75   };
76   auto OnReady = [&](Error Err) {
77     cantFail(std::move(Err));
78     OnReadyRun = true;
79   };
80 
81   AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
82 
83   Q.resolve(Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported));
84 
85   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
86   EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
87 }
88 
89 TEST(CoreAPIsTest, AsynchronousSymbolQueryResolutionErrorOnly) {
90   SymbolStringPool SP;
91   auto Foo = SP.intern("foo");
92   SymbolNameSet Names({Foo});
93 
94   bool OnResolutionRun = false;
95   bool OnReadyRun = false;
96 
97   auto OnResolution = [&](Expected<SymbolMap> Result) {
98     EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success";
99     auto Msg = toString(Result.takeError());
100     EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result";
101     OnResolutionRun = true;
102   };
103   auto OnReady = [&](Error Err) {
104     cantFail(std::move(Err));
105     OnReadyRun = true;
106   };
107 
108   AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
109 
110   Q.notifyMaterializationFailed(
111       make_error<StringError>("xyz", inconvertibleErrorCode()));
112 
113   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
114   EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
115 }
116 
117 TEST(CoreAPIsTest, SimpleAsynchronousSymbolQueryAgainstVSO) {
118   SymbolStringPool SP;
119   auto Foo = SP.intern("foo");
120   constexpr JITTargetAddress FakeAddr = 0xdeadbeef;
121   SymbolNameSet Names({Foo});
122 
123   bool OnResolutionRun = false;
124   bool OnReadyRun = false;
125 
126   auto OnResolution = [&](Expected<SymbolMap> Result) {
127     EXPECT_TRUE(!!Result) << "Query unexpectedly returned error";
128     auto I = Result->find(Foo);
129     EXPECT_NE(I, Result->end()) << "Could not find symbol definition";
130     EXPECT_EQ(I->second.getAddress(), FakeAddr)
131         << "Resolution returned incorrect result";
132     OnResolutionRun = true;
133   };
134 
135   auto OnReady = [&](Error Err) {
136     cantFail(std::move(Err));
137     OnReadyRun = true;
138   };
139 
140   auto Q =
141       std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
142   VSO V;
143 
144   SymbolMap Defs;
145   Defs[Foo] = JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported);
146   cantFail(V.define(std::move(Defs)));
147   V.lookup(Q, Names);
148 
149   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
150   EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
151 }
152 
153 TEST(CoreAPIsTest, LookupFlagsTest) {
154 
155   // Test that lookupFlags works on a predefined symbol, and does not trigger
156   // materialization of a lazy symbol.
157 
158   SymbolStringPool SP;
159   auto Foo = SP.intern("foo");
160   auto Bar = SP.intern("bar");
161   auto Baz = SP.intern("baz");
162 
163   JITSymbolFlags FooFlags = JITSymbolFlags::Exported;
164   JITSymbolFlags BarFlags = static_cast<JITSymbolFlags::FlagNames>(
165       JITSymbolFlags::Exported | JITSymbolFlags::Weak);
166 
167   VSO V;
168 
169   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
170       [=]() {
171         return SymbolFlagsMap({{Bar, BarFlags}});
172       },
173       [](MaterializationResponsibility R) {
174         llvm_unreachable("Symbol materialized on flags lookup");
175       },
176       [](const VSO &V, SymbolStringPtr Name) {
177         llvm_unreachable("Symbol finalized on flags lookup");
178       });
179 
180   SymbolMap InitialDefs;
181   InitialDefs[Foo] = JITEvaluatedSymbol(0xdeadbeef, FooFlags);
182   cantFail(V.define(std::move(InitialDefs)));
183 
184   cantFail(V.defineLazy(std::move(MU)));
185 
186   SymbolNameSet Names({Foo, Bar, Baz});
187 
188   SymbolFlagsMap SymbolFlags;
189   auto SymbolsNotFound = V.lookupFlags(SymbolFlags, Names);
190 
191   EXPECT_EQ(SymbolsNotFound.size(), 1U) << "Expected one not-found symbol";
192   EXPECT_EQ(SymbolsNotFound.count(Baz), 1U) << "Expected Baz to be not-found";
193   EXPECT_EQ(SymbolFlags.size(), 2U)
194       << "Returned symbol flags contains unexpected results";
195   EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo";
196   EXPECT_EQ(SymbolFlags[Foo], FooFlags) << "Incorrect flags returned for Foo";
197   EXPECT_EQ(SymbolFlags.count(Bar), 1U)
198       << "Missing  lookupFlags result for Bar";
199   EXPECT_EQ(SymbolFlags[Bar], BarFlags) << "Incorrect flags returned for Bar";
200 }
201 
202 TEST(CoreAPIsTest, DropMaterializerWhenEmpty) {
203   SymbolStringPool SP;
204   auto Foo = SP.intern("foo");
205   auto Bar = SP.intern("bar");
206 
207   bool DestructorRun = false;
208 
209   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
210       [=]() {
211         return SymbolFlagsMap(
212             {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}});
213       },
214       [](MaterializationResponsibility R) {
215         llvm_unreachable("Unexpected call to materialize");
216       },
217       [&](const VSO &V, SymbolStringPtr Name) {
218         EXPECT_TRUE(Name == Foo || Name == Bar)
219             << "Discard of unexpected symbol?";
220       },
221       [&]() { DestructorRun = true; });
222 
223   VSO V;
224 
225   cantFail(V.defineLazy(std::move(MU)));
226 
227   auto FooSym = JITEvaluatedSymbol(1, JITSymbolFlags::Exported);
228   auto BarSym = JITEvaluatedSymbol(2, JITSymbolFlags::Exported);
229   cantFail(V.define(SymbolMap({{Foo, FooSym}})));
230 
231   EXPECT_FALSE(DestructorRun)
232       << "MaterializationUnit should not have been destroyed yet";
233 
234   cantFail(V.define(SymbolMap({{Bar, BarSym}})));
235 
236   EXPECT_TRUE(DestructorRun)
237       << "MaterializationUnit should have been destroyed";
238 }
239 
240 TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) {
241 
242   constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
243   constexpr JITTargetAddress FakeBarAddr = 0xcafef00d;
244 
245   SymbolStringPool SP;
246   auto Foo = SP.intern("foo");
247   auto Bar = SP.intern("bar");
248 
249   bool FooMaterialized = false;
250   bool BarDiscarded = false;
251 
252   VSO V;
253 
254   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
255       [=]() {
256         return SymbolFlagsMap(
257             {{Foo, JITSymbolFlags::Exported},
258              {Bar, static_cast<JITSymbolFlags::FlagNames>(
259                        JITSymbolFlags::Exported | JITSymbolFlags::Weak)}});
260       },
261       [&](MaterializationResponsibility R) {
262         assert(BarDiscarded && "Bar should have been discarded by this point");
263         SymbolMap SymbolsToResolve;
264         SymbolsToResolve[Foo] =
265             JITEvaluatedSymbol(FakeFooAddr, JITSymbolFlags::Exported);
266         R.resolve(std::move(SymbolsToResolve));
267         R.finalize();
268         FooMaterialized = true;
269       },
270       [&](const VSO &V, SymbolStringPtr Name) {
271         EXPECT_EQ(Name, Bar) << "Expected Name to be Bar";
272         BarDiscarded = true;
273       });
274 
275   cantFail(V.defineLazy(std::move(MU)));
276 
277   SymbolMap BarOverride;
278   BarOverride[Bar] = JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported);
279   cantFail(V.define(std::move(BarOverride)));
280 
281   SymbolNameSet Names({Foo});
282 
283   bool OnResolutionRun = false;
284   bool OnReadyRun = false;
285 
286   auto OnResolution = [&](Expected<SymbolMap> Result) {
287     EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
288     auto I = Result->find(Foo);
289     EXPECT_NE(I, Result->end()) << "Could not find symbol definition";
290     EXPECT_EQ(I->second.getAddress(), FakeFooAddr)
291         << "Resolution returned incorrect result";
292     OnResolutionRun = true;
293   };
294 
295   auto OnReady = [&](Error Err) {
296     cantFail(std::move(Err));
297     OnReadyRun = true;
298   };
299 
300   auto Q =
301       std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
302 
303   auto LR = V.lookup(std::move(Q), Names);
304 
305   for (auto &M : LR.Materializers)
306     M();
307 
308   EXPECT_TRUE(LR.UnresolvedSymbols.empty()) << "Could not find Foo in dylib";
309   EXPECT_TRUE(FooMaterialized) << "Foo was not materialized";
310   EXPECT_TRUE(BarDiscarded) << "Bar was not discarded";
311   EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
312   EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
313 }
314 
315 TEST(CoreAPIsTest, FailResolution) {
316   SymbolStringPool SP;
317   auto Foo = SP.intern("foo");
318   auto Bar = SP.intern("bar");
319 
320   SymbolNameSet Names({Foo, Bar});
321 
322   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
323       [=]() {
324         return SymbolFlagsMap(
325             {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}});
326       },
327       [&](MaterializationResponsibility R) { R.notifyMaterializationFailed(); },
328       [&](const VSO &V, SymbolStringPtr Name) {
329         llvm_unreachable("Unexpected call to discard");
330       });
331 
332   VSO V;
333 
334   cantFail(V.defineLazy(std::move(MU)));
335 
336   auto OnResolution = [&](Expected<SymbolMap> Result) {
337     handleAllErrors(Result.takeError(),
338                     [&](FailedToResolve &F) {
339                       EXPECT_EQ(F.getSymbols(), Names)
340                           << "Expected to fail on symbols in Names";
341                     },
342                     [](ErrorInfoBase &EIB) {
343                       std::string ErrMsg;
344                       {
345                         raw_string_ostream ErrOut(ErrMsg);
346                         EIB.log(ErrOut);
347                       }
348                       ADD_FAILURE()
349                           << "Expected a FailedToResolve error. Got:\n"
350                           << ErrMsg;
351                     });
352   };
353 
354   auto OnReady = [](Error Err) {
355     cantFail(std::move(Err));
356     ADD_FAILURE() << "OnReady should never be called";
357   };
358 
359   auto Q =
360       std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
361 
362   auto LR = V.lookup(std::move(Q), Names);
363   for (auto &M : LR.Materializers)
364     M();
365 }
366 
367 TEST(CoreAPIsTest, FailFinalization) {
368   SymbolStringPool SP;
369   auto Foo = SP.intern("foo");
370   auto Bar = SP.intern("bar");
371 
372   SymbolNameSet Names({Foo, Bar});
373 
374   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
375       [=]() {
376         return SymbolFlagsMap(
377             {{Foo, JITSymbolFlags::Exported}, {Bar, JITSymbolFlags::Exported}});
378       },
379       [&](MaterializationResponsibility R) {
380         constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
381         constexpr JITTargetAddress FakeBarAddr = 0xcafef00d;
382 
383         auto FooSym = JITEvaluatedSymbol(FakeFooAddr, JITSymbolFlags::Exported);
384         auto BarSym = JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported);
385         R.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}}));
386         R.notifyMaterializationFailed();
387       },
388       [&](const VSO &V, SymbolStringPtr Name) {
389         llvm_unreachable("Unexpected call to discard");
390       });
391 
392   VSO V;
393 
394   cantFail(V.defineLazy(std::move(MU)));
395 
396   auto OnResolution = [](Expected<SymbolMap> Result) {
397     cantFail(std::move(Result));
398   };
399 
400   auto OnReady = [&](Error Err) {
401     handleAllErrors(std::move(Err),
402                     [&](FailedToFinalize &F) {
403                       EXPECT_EQ(F.getSymbols(), Names)
404                           << "Expected to fail on symbols in Names";
405                     },
406                     [](ErrorInfoBase &EIB) {
407                       std::string ErrMsg;
408                       {
409                         raw_string_ostream ErrOut(ErrMsg);
410                         EIB.log(ErrOut);
411                       }
412                       ADD_FAILURE()
413                           << "Expected a FailedToFinalize error. Got:\n"
414                           << ErrMsg;
415                     });
416   };
417 
418   auto Q =
419       std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
420 
421   auto LR = V.lookup(std::move(Q), Names);
422   for (auto &M : LR.Materializers)
423     M();
424 }
425 
426 TEST(CoreAPIsTest, TestLambdaSymbolResolver) {
427   JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported);
428   JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported);
429 
430   SymbolStringPool SP;
431   auto Foo = SP.intern("foo");
432   auto Bar = SP.intern("bar");
433   auto Baz = SP.intern("baz");
434 
435   VSO V;
436   cantFail(V.define({{Foo, FooSym}, {Bar, BarSym}}));
437 
438   auto Resolver = createSymbolResolver(
439       [&](SymbolFlagsMap &SymbolFlags, const SymbolNameSet &Symbols) {
440         return V.lookupFlags(SymbolFlags, Symbols);
441       },
442       [&](std::shared_ptr<AsynchronousSymbolQuery> Q, SymbolNameSet Symbols) {
443         auto LR = V.lookup(std::move(Q), Symbols);
444         assert(LR.Materializers.empty() &&
445                "Test generated unexpected materialization work?");
446         return std::move(LR.UnresolvedSymbols);
447       });
448 
449   SymbolNameSet Symbols({Foo, Bar, Baz});
450 
451   SymbolFlagsMap SymbolFlags;
452   SymbolNameSet SymbolsNotFound = Resolver->lookupFlags(SymbolFlags, Symbols);
453 
454   EXPECT_EQ(SymbolFlags.size(), 2U)
455       << "lookupFlags returned the wrong number of results";
456   EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for foo";
457   EXPECT_EQ(SymbolFlags.count(Bar), 1U) << "Missing lookupFlags result for bar";
458   EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags())
459       << "Incorrect lookupFlags result for Foo";
460   EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags())
461       << "Incorrect lookupFlags result for Bar";
462   EXPECT_EQ(SymbolsNotFound.size(), 1U)
463       << "Expected one symbol not found in lookupFlags";
464   EXPECT_EQ(SymbolsNotFound.count(Baz), 1U)
465       << "Expected baz not to be found in lookupFlags";
466 
467   bool OnResolvedRun = false;
468 
469   auto OnResolved = [&](Expected<SymbolMap> Result) {
470     OnResolvedRun = true;
471     EXPECT_TRUE(!!Result) << "Unexpected error";
472     EXPECT_EQ(Result->size(), 2U) << "Unexpected number of resolved symbols";
473     EXPECT_EQ(Result->count(Foo), 1U) << "Missing lookup result for foo";
474     EXPECT_EQ(Result->count(Bar), 1U) << "Missing lookup result for bar";
475     EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress())
476         << "Incorrect address for foo";
477     EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress())
478         << "Incorrect address for bar";
479   };
480   auto OnReady = [&](Error Err) {
481     EXPECT_FALSE(!!Err) << "Finalization should never fail in this test";
482   };
483 
484   auto Q = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Foo, Bar}),
485                                                      OnResolved, OnReady);
486   auto Unresolved = Resolver->lookup(std::move(Q), Symbols);
487 
488   EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol";
489   EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to not be resolved";
490   EXPECT_TRUE(OnResolvedRun) << "OnResolved was never run";
491 }
492 
493 TEST(CoreAPIsTest, TestLookupWithUnthreadedMaterialization) {
494   constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
495   JITEvaluatedSymbol FooSym(FakeFooAddr, JITSymbolFlags::Exported);
496 
497   ExecutionSession ES(std::make_shared<SymbolStringPool>());
498   auto Foo = ES.getSymbolStringPool().intern("foo");
499 
500   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
501       [=]() {
502         return SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}});
503       },
504       [&](MaterializationResponsibility R) {
505         R.resolve({{Foo, FooSym}});
506         R.finalize();
507       },
508       [](const VSO &V, SymbolStringPtr Name) {
509         llvm_unreachable("Not expecting finalization");
510       });
511 
512   VSO V;
513 
514   cantFail(V.defineLazy(std::move(MU)));
515 
516   auto FooLookupResult =
517       cantFail(lookup({&V}, Foo, MaterializeOnCurrentThread()));
518 
519   EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
520       << "lookup returned an incorrect address";
521   EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
522       << "lookup returned incorrect flags";
523 }
524 
525 TEST(CoreAPIsTest, TestLookupWithThreadedMaterialization) {
526 #if LLVM_ENABLE_THREADS
527   constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
528   JITEvaluatedSymbol FooSym(FakeFooAddr, JITSymbolFlags::Exported);
529 
530   ExecutionSession ES(std::make_shared<SymbolStringPool>());
531   auto Foo = ES.getSymbolStringPool().intern("foo");
532 
533   auto MU = llvm::make_unique<SimpleMaterializationUnit>(
534       [=]() {
535         return SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}});
536       },
537       [&](MaterializationResponsibility R) {
538         R.resolve({{Foo, FooSym}});
539         R.finalize();
540       },
541       [](const VSO &V, SymbolStringPtr Name) {
542         llvm_unreachable("Not expecting finalization");
543       });
544 
545   VSO V;
546 
547   cantFail(V.defineLazy(std::move(MU)));
548 
549   std::thread MaterializationThread;
550   auto MaterializeOnNewThread = [&](VSO::Materializer M) {
551     // FIXME: Use move capture once we move to C++14.
552     auto SharedM = std::make_shared<VSO::Materializer>(std::move(M));
553     MaterializationThread = std::thread([SharedM]() { (*SharedM)(); });
554   };
555 
556   auto FooLookupResult =
557     cantFail(lookup({&V}, Foo, MaterializeOnNewThread));
558 
559   EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
560       << "lookup returned an incorrect address";
561   EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
562       << "lookup returned incorrect flags";
563   MaterializationThread.join();
564 #endif
565 }
566 
567 } // namespace
568