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