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