1 //===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "OrcTestCommon.h"
10 #include "llvm/Config/llvm-config.h"
11 #include "llvm/ExecutionEngine/Orc/Core.h"
12 #include "llvm/ExecutionEngine/Orc/OrcError.h"
13 #include "llvm/Testing/Support/Error.h"
14
15 #include <set>
16 #include <thread>
17
18 using namespace llvm;
19 using namespace llvm::orc;
20
21 class CoreAPIsStandardTest : public CoreAPIsBasedStandardTest {};
22
23 namespace {
24
TEST_F(CoreAPIsStandardTest,BasicSuccessfulLookup)25 TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {
26 bool OnCompletionRun = false;
27
28 auto OnCompletion = [&](Expected<SymbolMap> Result) {
29 EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
30 auto &Resolved = *Result;
31 auto I = Resolved.find(Foo);
32 EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition";
33 EXPECT_EQ(I->second.getAddress(), FooAddr)
34 << "Resolution returned incorrect result";
35 OnCompletionRun = true;
36 };
37
38 std::shared_ptr<MaterializationResponsibility> FooMR;
39
40 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(
41 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
42 [&](MaterializationResponsibility R) {
43 FooMR = std::make_shared<MaterializationResponsibility>(std::move(R));
44 })));
45
46 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
47 SymbolLookupSet(Foo), SymbolState::Ready, OnCompletion,
48 NoDependenciesToRegister);
49
50 EXPECT_FALSE(OnCompletionRun) << "Should not have been resolved yet";
51
52 cantFail(FooMR->notifyResolved({{Foo, FooSym}}));
53
54 EXPECT_FALSE(OnCompletionRun) << "Should not be ready yet";
55
56 cantFail(FooMR->notifyEmitted());
57
58 EXPECT_TRUE(OnCompletionRun) << "Should have been marked ready";
59 }
60
TEST_F(CoreAPIsStandardTest,ExecutionSessionFailQuery)61 TEST_F(CoreAPIsStandardTest, ExecutionSessionFailQuery) {
62 bool OnCompletionRun = false;
63
64 auto OnCompletion = [&](Expected<SymbolMap> Result) {
65 EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success";
66 auto Msg = toString(Result.takeError());
67 EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result";
68 OnCompletionRun = true;
69 };
70
71 AsynchronousSymbolQuery Q(SymbolLookupSet(Foo), SymbolState::Ready,
72 OnCompletion);
73
74 ES.legacyFailQuery(Q,
75 make_error<StringError>("xyz", inconvertibleErrorCode()));
76
77 EXPECT_TRUE(OnCompletionRun) << "OnCompletionCallback was not run";
78 }
79
TEST_F(CoreAPIsStandardTest,EmptyLookup)80 TEST_F(CoreAPIsStandardTest, EmptyLookup) {
81 bool OnCompletionRun = false;
82
83 auto OnCompletion = [&](Expected<SymbolMap> Result) {
84 cantFail(std::move(Result));
85 OnCompletionRun = true;
86 };
87
88 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(),
89 SymbolState::Ready, OnCompletion, NoDependenciesToRegister);
90
91 EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run for empty query";
92 }
93
TEST_F(CoreAPIsStandardTest,ResolveUnrequestedSymbol)94 TEST_F(CoreAPIsStandardTest, ResolveUnrequestedSymbol) {
95 // Test that all symbols in a MaterializationUnit materialize corretly when
96 // only a subset of symbols is looked up.
97 // The aim here is to ensure that we're not relying on the query to set up
98 // state needed to materialize the unrequested symbols.
99
100 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(
101 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
102 [this](MaterializationResponsibility R) {
103 cantFail(R.notifyResolved({{Foo, FooSym}, {Bar, BarSym}}));
104 cantFail(R.notifyEmitted());
105 })));
106
107 auto Result =
108 cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo})));
109 EXPECT_EQ(Result.size(), 1U) << "Unexpected number of results";
110 EXPECT_TRUE(Result.count(Foo)) << "Expected result for \"Foo\"";
111 }
112
TEST_F(CoreAPIsStandardTest,MaterializationSideEffctsOnlyTest)113 TEST_F(CoreAPIsStandardTest, MaterializationSideEffctsOnlyTest) {
114 // Test that basic materialization-side-effects-only symbols work as expected:
115 // that they can be emitted without being resolved, that queries for them
116 // don't return until they're emitted, and that they don't appear in query
117 // results.
118
119 Optional<MaterializationResponsibility> FooR;
120 Optional<SymbolMap> Result;
121
122 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(
123 SymbolFlagsMap(
124 {{Foo, JITSymbolFlags::Exported |
125 JITSymbolFlags::MaterializationSideEffectsOnly}}),
126 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); })));
127
128 ES.lookup(
129 LookupKind::Static, makeJITDylibSearchOrder(&JD),
130 SymbolLookupSet({Foo}, SymbolLookupFlags::WeaklyReferencedSymbol),
131 SymbolState::Ready,
132 [&](Expected<SymbolMap> LookupResult) {
133 if (LookupResult)
134 Result = std::move(*LookupResult);
135 else
136 ADD_FAILURE() << "Unexpected lookup error: "
137 << toString(LookupResult.takeError());
138 },
139 NoDependenciesToRegister);
140
141 EXPECT_FALSE(Result) << "Lookup returned unexpectedly";
142 EXPECT_TRUE(FooR) << "Lookup failed to trigger materialization";
143 EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
144 << "Emission of materialization-side-effects-only symbol failed";
145
146 EXPECT_TRUE(Result) << "Lookup failed to return";
147 EXPECT_TRUE(Result->empty()) << "Lookup result contained unexpected value";
148 }
149
TEST_F(CoreAPIsStandardTest,RemoveSymbolsTest)150 TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) {
151 // Test that:
152 // (1) Missing symbols generate a SymbolsNotFound error.
153 // (2) Materializing symbols generate a SymbolCouldNotBeRemoved error.
154 // (3) Removal of unmaterialized symbols triggers discard on the
155 // materialization unit.
156 // (4) Removal of symbols destroys empty materialization units.
157 // (5) Removal of materialized symbols works.
158
159 // Foo will be fully materialized.
160 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
161
162 // Bar will be unmaterialized.
163 bool BarDiscarded = false;
164 bool BarMaterializerDestructed = false;
165 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(
166 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
167 [this](MaterializationResponsibility R) {
168 ADD_FAILURE() << "Unexpected materialization of \"Bar\"";
169 cantFail(R.notifyResolved({{Bar, BarSym}}));
170 cantFail(R.notifyEmitted());
171 },
172 nullptr,
173 [&](const JITDylib &JD, const SymbolStringPtr &Name) {
174 EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded";
175 if (Name == Bar)
176 BarDiscarded = true;
177 },
178 [&]() { BarMaterializerDestructed = true; })));
179
180 // Baz will be in the materializing state initially, then
181 // materialized for the final removal attempt.
182 Optional<MaterializationResponsibility> BazR;
183 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(
184 SymbolFlagsMap({{Baz, BazSym.getFlags()}}),
185 [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); },
186 nullptr,
187 [](const JITDylib &JD, const SymbolStringPtr &Name) {
188 ADD_FAILURE() << "\"Baz\" discarded unexpectedly";
189 })));
190
191 bool OnCompletionRun = false;
192 ES.lookup(
193 LookupKind::Static, makeJITDylibSearchOrder(&JD),
194 SymbolLookupSet({Foo, Baz}), SymbolState::Ready,
195 [&](Expected<SymbolMap> Result) {
196 cantFail(Result.takeError());
197 OnCompletionRun = true;
198 },
199 NoDependenciesToRegister);
200
201 {
202 // Attempt 1: Search for a missing symbol, Qux.
203 auto Err = JD.remove({Foo, Bar, Baz, Qux});
204 EXPECT_TRUE(!!Err) << "Expected failure";
205 EXPECT_TRUE(Err.isA<SymbolsNotFound>())
206 << "Expected a SymbolsNotFound error";
207 consumeError(std::move(Err));
208 }
209
210 {
211 // Attempt 2: Search for a symbol that is still materializing, Baz.
212 auto Err = JD.remove({Foo, Bar, Baz});
213 EXPECT_TRUE(!!Err) << "Expected failure";
214 EXPECT_TRUE(Err.isA<SymbolsCouldNotBeRemoved>())
215 << "Expected a SymbolsNotFound error";
216 consumeError(std::move(Err));
217 }
218
219 cantFail(BazR->notifyResolved({{Baz, BazSym}}));
220 cantFail(BazR->notifyEmitted());
221 {
222 // Attempt 3: Search now that all symbols are fully materialized
223 // (Foo, Baz), or not yet materialized (Bar).
224 auto Err = JD.remove({Foo, Bar, Baz});
225 EXPECT_FALSE(!!Err) << "Expected success";
226 }
227
228 EXPECT_TRUE(BarDiscarded) << "\"Bar\" should have been discarded";
229 EXPECT_TRUE(BarMaterializerDestructed)
230 << "\"Bar\"'s materializer should have been destructed";
231 EXPECT_TRUE(OnCompletionRun) << "OnCompletion should have been run";
232 }
233
TEST_F(CoreAPIsStandardTest,ChainedJITDylibLookup)234 TEST_F(CoreAPIsStandardTest, ChainedJITDylibLookup) {
235 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
236
237 auto &JD2 = ES.createBareJITDylib("JD2");
238
239 bool OnCompletionRun = false;
240
241 auto Q = std::make_shared<AsynchronousSymbolQuery>(
242 SymbolLookupSet({Foo}), SymbolState::Ready,
243 [&](Expected<SymbolMap> Result) {
244 cantFail(std::move(Result));
245 OnCompletionRun = true;
246 });
247
248 cantFail(JD2.legacyLookup(Q, cantFail(JD.legacyLookup(Q, {Foo}))));
249
250 EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run for empty query";
251 }
252
TEST_F(CoreAPIsStandardTest,LookupWithHiddenSymbols)253 TEST_F(CoreAPIsStandardTest, LookupWithHiddenSymbols) {
254 auto BarHiddenFlags = BarSym.getFlags() & ~JITSymbolFlags::Exported;
255 auto BarHiddenSym = JITEvaluatedSymbol(BarSym.getAddress(), BarHiddenFlags);
256
257 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarHiddenSym}})));
258
259 auto &JD2 = ES.createBareJITDylib("JD2");
260 cantFail(JD2.define(absoluteSymbols({{Bar, QuxSym}})));
261
262 /// Try a blocking lookup.
263 auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder({&JD, &JD2}),
264 SymbolLookupSet({Foo, Bar})));
265
266 EXPECT_EQ(Result.size(), 2U) << "Unexpected number of results";
267 EXPECT_EQ(Result.count(Foo), 1U) << "Missing result for \"Foo\"";
268 EXPECT_EQ(Result.count(Bar), 1U) << "Missing result for \"Bar\"";
269 EXPECT_EQ(Result[Bar].getAddress(), QuxSym.getAddress())
270 << "Wrong result for \"Bar\"";
271 }
272
TEST_F(CoreAPIsStandardTest,LookupFlagsTest)273 TEST_F(CoreAPIsStandardTest, LookupFlagsTest) {
274 // Test that lookupFlags works on a predefined symbol, and does not trigger
275 // materialization of a lazy symbol. Make the lazy symbol weak to test that
276 // the weak flag is propagated correctly.
277
278 BarSym.setFlags(static_cast<JITSymbolFlags::FlagNames>(
279 JITSymbolFlags::Exported | JITSymbolFlags::Weak));
280 auto MU = std::make_unique<SimpleMaterializationUnit>(
281 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
282 [](MaterializationResponsibility R) {
283 llvm_unreachable("Symbol materialized on flags lookup");
284 });
285
286 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
287 cantFail(JD.define(std::move(MU)));
288
289 auto SymbolFlags = cantFail(JD.lookupFlags(
290 LookupKind::Static, JITDylibLookupFlags::MatchExportedSymbolsOnly,
291 SymbolLookupSet({Foo, Bar, Baz})));
292
293 EXPECT_EQ(SymbolFlags.size(), 2U)
294 << "Returned symbol flags contains unexpected results";
295 EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo";
296 EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags())
297 << "Incorrect flags returned for Foo";
298 EXPECT_EQ(SymbolFlags.count(Bar), 1U)
299 << "Missing lookupFlags result for Bar";
300 EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags())
301 << "Incorrect flags returned for Bar";
302 }
303
TEST_F(CoreAPIsStandardTest,LookupWithGeneratorFailure)304 TEST_F(CoreAPIsStandardTest, LookupWithGeneratorFailure) {
305
306 class BadGenerator : public JITDylib::DefinitionGenerator {
307 public:
308 Error tryToGenerate(LookupKind K, JITDylib &, JITDylibLookupFlags,
309 const SymbolLookupSet &) override {
310 return make_error<StringError>("BadGenerator", inconvertibleErrorCode());
311 }
312 };
313
314 JD.addGenerator(std::make_unique<BadGenerator>());
315
316 EXPECT_THAT_ERROR(
317 JD.lookupFlags(LookupKind::Static,
318 JITDylibLookupFlags::MatchExportedSymbolsOnly,
319 SymbolLookupSet(Foo))
320 .takeError(),
321 Failed<StringError>())
322 << "Generator failure did not propagate through lookupFlags";
323
324 EXPECT_THAT_ERROR(
325 ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo)).takeError(),
326 Failed<StringError>())
327 << "Generator failure did not propagate through lookup";
328 }
329
TEST_F(CoreAPIsStandardTest,TestBasicAliases)330 TEST_F(CoreAPIsStandardTest, TestBasicAliases) {
331 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));
332 cantFail(JD.define(symbolAliases({{Baz, {Foo, JITSymbolFlags::Exported}},
333 {Qux, {Bar, JITSymbolFlags::Weak}}})));
334 cantFail(JD.define(absoluteSymbols({{Qux, QuxSym}})));
335
336 auto Result =
337 ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Baz, Qux}));
338 EXPECT_TRUE(!!Result) << "Unexpected lookup failure";
339 EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\"";
340 EXPECT_EQ(Result->count(Qux), 1U) << "No result for \"qux\"";
341 EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress())
342 << "\"Baz\"'s address should match \"Foo\"'s";
343 EXPECT_EQ((*Result)[Qux].getAddress(), QuxSym.getAddress())
344 << "The \"Qux\" alias should have been overriden";
345 }
346
TEST_F(CoreAPIsStandardTest,TestChainedAliases)347 TEST_F(CoreAPIsStandardTest, TestChainedAliases) {
348 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
349 cantFail(JD.define(symbolAliases(
350 {{Baz, {Bar, BazSym.getFlags()}}, {Bar, {Foo, BarSym.getFlags()}}})));
351
352 auto Result =
353 ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Bar, Baz}));
354 EXPECT_TRUE(!!Result) << "Unexpected lookup failure";
355 EXPECT_EQ(Result->count(Bar), 1U) << "No result for \"bar\"";
356 EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\"";
357 EXPECT_EQ((*Result)[Bar].getAddress(), FooSym.getAddress())
358 << "\"Bar\"'s address should match \"Foo\"'s";
359 EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress())
360 << "\"Baz\"'s address should match \"Foo\"'s";
361 }
362
TEST_F(CoreAPIsStandardTest,TestBasicReExports)363 TEST_F(CoreAPIsStandardTest, TestBasicReExports) {
364 // Test that the basic use case of re-exporting a single symbol from another
365 // JITDylib works.
366 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
367
368 auto &JD2 = ES.createBareJITDylib("JD2");
369
370 cantFail(JD2.define(reexports(JD, {{Bar, {Foo, BarSym.getFlags()}}})));
371
372 auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD2), Bar));
373 EXPECT_EQ(Result.getAddress(), FooSym.getAddress())
374 << "Re-export Bar for symbol Foo should match FooSym's address";
375 }
376
TEST_F(CoreAPIsStandardTest,TestThatReExportsDontUnnecessarilyMaterialize)377 TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) {
378 // Test that re-exports do not materialize symbols that have not been queried
379 // for.
380 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
381
382 bool BarMaterialized = false;
383 auto BarMU = std::make_unique<SimpleMaterializationUnit>(
384 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
385 [&](MaterializationResponsibility R) {
386 BarMaterialized = true;
387 cantFail(R.notifyResolved({{Bar, BarSym}}));
388 cantFail(R.notifyEmitted());
389 });
390
391 cantFail(JD.define(BarMU));
392
393 auto &JD2 = ES.createBareJITDylib("JD2");
394
395 cantFail(JD2.define(reexports(
396 JD, {{Baz, {Foo, BazSym.getFlags()}}, {Qux, {Bar, QuxSym.getFlags()}}})));
397
398 auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD2), Baz));
399 EXPECT_EQ(Result.getAddress(), FooSym.getAddress())
400 << "Re-export Baz for symbol Foo should match FooSym's address";
401
402 EXPECT_FALSE(BarMaterialized) << "Bar should not have been materialized";
403 }
404
TEST_F(CoreAPIsStandardTest,TestReexportsGenerator)405 TEST_F(CoreAPIsStandardTest, TestReexportsGenerator) {
406 // Test that a re-exports generator can dynamically generate reexports.
407
408 auto &JD2 = ES.createBareJITDylib("JD2");
409 cantFail(JD2.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));
410
411 auto Filter = [this](SymbolStringPtr Name) { return Name != Bar; };
412
413 JD.addGenerator(std::make_unique<ReexportsGenerator>(
414 JD2, JITDylibLookupFlags::MatchExportedSymbolsOnly, Filter));
415
416 auto Flags = cantFail(JD.lookupFlags(
417 LookupKind::Static, JITDylibLookupFlags::MatchExportedSymbolsOnly,
418 SymbolLookupSet({Foo, Bar, Baz})));
419 EXPECT_EQ(Flags.size(), 1U) << "Unexpected number of results";
420 EXPECT_EQ(Flags[Foo], FooSym.getFlags()) << "Unexpected flags for Foo";
421
422 auto Result = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));
423
424 EXPECT_EQ(Result.getAddress(), FooSym.getAddress())
425 << "Incorrect reexported symbol address";
426 }
427
TEST_F(CoreAPIsStandardTest,TestTrivialCircularDependency)428 TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) {
429 Optional<MaterializationResponsibility> FooR;
430 auto FooMU = std::make_unique<SimpleMaterializationUnit>(
431 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
432 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
433
434 cantFail(JD.define(FooMU));
435
436 bool FooReady = false;
437 auto OnCompletion = [&](Expected<SymbolMap> Result) {
438 cantFail(std::move(Result));
439 FooReady = true;
440 };
441
442 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
443 SymbolLookupSet({Foo}), SymbolState::Ready, OnCompletion,
444 NoDependenciesToRegister);
445
446 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
447 EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
448 << "No symbols marked failed, but Foo failed to resolve";
449 EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
450 << "No symbols marked failed, but Foo failed to emit";
451
452 EXPECT_TRUE(FooReady)
453 << "Self-dependency prevented symbol from being marked ready";
454 }
455
TEST_F(CoreAPIsStandardTest,TestCircularDependenceInOneJITDylib)456 TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
457 // Test that a circular symbol dependency between three symbols in a JITDylib
458 // does not prevent any symbol from becoming 'ready' once all symbols are
459 // emitted.
460
461 // Create three MaterializationResponsibility objects: one for each of Foo,
462 // Bar and Baz. These are optional because MaterializationResponsibility
463 // does not have a default constructor).
464 Optional<MaterializationResponsibility> FooR;
465 Optional<MaterializationResponsibility> BarR;
466 Optional<MaterializationResponsibility> BazR;
467
468 // Create a MaterializationUnit for each symbol that moves the
469 // MaterializationResponsibility into one of the locals above.
470 auto FooMU = std::make_unique<SimpleMaterializationUnit>(
471 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
472 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
473
474 auto BarMU = std::make_unique<SimpleMaterializationUnit>(
475 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
476 [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
477
478 auto BazMU = std::make_unique<SimpleMaterializationUnit>(
479 SymbolFlagsMap({{Baz, BazSym.getFlags()}}),
480 [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); });
481
482 // Define the symbols.
483 cantFail(JD.define(FooMU));
484 cantFail(JD.define(BarMU));
485 cantFail(JD.define(BazMU));
486
487 // Query each of the symbols to trigger materialization.
488 bool FooResolved = false;
489 bool FooReady = false;
490
491 auto OnFooResolution = [&](Expected<SymbolMap> Result) {
492 cantFail(std::move(Result));
493 FooResolved = true;
494 };
495
496 auto OnFooReady = [&](Expected<SymbolMap> Result) {
497 cantFail(std::move(Result));
498 FooReady = true;
499 };
500
501 // Issue lookups for Foo. Use NoDependenciesToRegister: We're going to add
502 // the dependencies manually below.
503 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
504 SymbolLookupSet(Foo), SymbolState::Resolved,
505 std::move(OnFooResolution), NoDependenciesToRegister);
506
507 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
508 SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),
509 NoDependenciesToRegister);
510
511 bool BarResolved = false;
512 bool BarReady = false;
513 auto OnBarResolution = [&](Expected<SymbolMap> Result) {
514 cantFail(std::move(Result));
515 BarResolved = true;
516 };
517
518 auto OnBarReady = [&](Expected<SymbolMap> Result) {
519 cantFail(std::move(Result));
520 BarReady = true;
521 };
522
523 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
524 SymbolLookupSet(Bar), SymbolState::Resolved,
525 std::move(OnBarResolution), NoDependenciesToRegister);
526
527 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
528 SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
529 NoDependenciesToRegister);
530
531 bool BazResolved = false;
532 bool BazReady = false;
533
534 auto OnBazResolution = [&](Expected<SymbolMap> Result) {
535 cantFail(std::move(Result));
536 BazResolved = true;
537 };
538
539 auto OnBazReady = [&](Expected<SymbolMap> Result) {
540 cantFail(std::move(Result));
541 BazReady = true;
542 };
543
544 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
545 SymbolLookupSet(Baz), SymbolState::Resolved,
546 std::move(OnBazResolution), NoDependenciesToRegister);
547
548 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
549 SymbolLookupSet(Baz), SymbolState::Ready, std::move(OnBazReady),
550 NoDependenciesToRegister);
551
552 // Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo.
553 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
554 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}});
555 BazR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
556
557 // Add self-dependencies for good measure. This tests that the implementation
558 // of addDependencies filters these out.
559 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
560 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
561 BazR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}});
562
563 // Check that nothing has been resolved yet.
564 EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet";
565 EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet";
566 EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet";
567
568 // Resolve the symbols (but do not emit them).
569 EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
570 << "No symbols failed, but Foo failed to resolve";
571 EXPECT_THAT_ERROR(BarR->notifyResolved({{Bar, BarSym}}), Succeeded())
572 << "No symbols failed, but Bar failed to resolve";
573 EXPECT_THAT_ERROR(BazR->notifyResolved({{Baz, BazSym}}), Succeeded())
574 << "No symbols failed, but Baz failed to resolve";
575
576 // Verify that the symbols have been resolved, but are not ready yet.
577 EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now";
578 EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now";
579 EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now";
580
581 EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet";
582 EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet";
583 EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet";
584
585 // Emit two of the symbols.
586 EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
587 << "No symbols failed, but Foo failed to emit";
588 EXPECT_THAT_ERROR(BarR->notifyEmitted(), Succeeded())
589 << "No symbols failed, but Bar failed to emit";
590
591 // Verify that nothing is ready until the circular dependence is resolved.
592 EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready";
593 EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready";
594 EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready";
595
596 // Emit the last symbol.
597 EXPECT_THAT_ERROR(BazR->notifyEmitted(), Succeeded())
598 << "No symbols failed, but Baz failed to emit";
599
600 // Verify that everything becomes ready once the circular dependence resolved.
601 EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now";
602 EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now";
603 EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now";
604 }
605
TEST_F(CoreAPIsStandardTest,FailureInDependency)606 TEST_F(CoreAPIsStandardTest, FailureInDependency) {
607 Optional<MaterializationResponsibility> FooR;
608 Optional<MaterializationResponsibility> BarR;
609
610 // Create a MaterializationUnit for each symbol that moves the
611 // MaterializationResponsibility into one of the locals above.
612 auto FooMU = std::make_unique<SimpleMaterializationUnit>(
613 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
614 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
615
616 auto BarMU = std::make_unique<SimpleMaterializationUnit>(
617 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
618 [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
619
620 // Define the symbols.
621 cantFail(JD.define(FooMU));
622 cantFail(JD.define(BarMU));
623
624 bool OnFooReadyRun = false;
625 auto OnFooReady = [&](Expected<SymbolMap> Result) {
626 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
627 OnFooReadyRun = true;
628 };
629
630 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
631 SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),
632 NoDependenciesToRegister);
633
634 bool OnBarReadyRun = false;
635 auto OnBarReady = [&](Expected<SymbolMap> Result) {
636 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
637 OnBarReadyRun = true;
638 };
639
640 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
641 SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
642 NoDependenciesToRegister);
643
644 // Add a dependency by Foo on Bar.
645 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
646
647 // Fail bar.
648 BarR->failMaterialization();
649
650 // Verify that queries on Bar failed, but queries on Foo have not yet.
651 EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
652 EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";
653
654 // Check that we can still resolve Foo (even though it has been failed).
655 EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
656 << "Expected resolution for \"Foo\" to fail.";
657
658 FooR->failMaterialization();
659
660 // Verify that queries on Foo have now failed.
661 EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";
662
663 // Verify that subsequent lookups on Bar and Foo fail.
664 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
665 << "Lookup on failed symbol should fail";
666
667 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
668 << "Lookup on failed symbol should fail";
669 }
670
TEST_F(CoreAPIsStandardTest,FailureInCircularDependency)671 TEST_F(CoreAPIsStandardTest, FailureInCircularDependency) {
672 Optional<MaterializationResponsibility> FooR;
673 Optional<MaterializationResponsibility> BarR;
674
675 // Create a MaterializationUnit for each symbol that moves the
676 // MaterializationResponsibility into one of the locals above.
677 auto FooMU = std::make_unique<SimpleMaterializationUnit>(
678 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
679 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
680
681 auto BarMU = std::make_unique<SimpleMaterializationUnit>(
682 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
683 [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
684
685 // Define the symbols.
686 cantFail(JD.define(FooMU));
687 cantFail(JD.define(BarMU));
688
689 bool OnFooReadyRun = false;
690 auto OnFooReady = [&](Expected<SymbolMap> Result) {
691 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
692 OnFooReadyRun = true;
693 };
694
695 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
696 SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),
697 NoDependenciesToRegister);
698
699 bool OnBarReadyRun = false;
700 auto OnBarReady = [&](Expected<SymbolMap> Result) {
701 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
702 OnBarReadyRun = true;
703 };
704
705 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
706 SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
707 NoDependenciesToRegister);
708
709 // Add a dependency by Foo on Bar and vice-versa.
710 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
711 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
712
713 // Fail bar.
714 BarR->failMaterialization();
715
716 // Verify that queries on Bar failed, but queries on Foo have not yet.
717 EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
718 EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";
719
720 // Verify that trying to resolve Foo fails.
721 EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
722 << "Expected resolution for \"Foo\" to fail.";
723
724 FooR->failMaterialization();
725
726 // Verify that queries on Foo have now failed.
727 EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";
728
729 // Verify that subsequent lookups on Bar and Foo fail.
730 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
731 << "Lookup on failed symbol should fail";
732
733 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
734 << "Lookup on failed symbol should fail";
735 }
736
TEST_F(CoreAPIsStandardTest,AddDependencyOnFailedSymbol)737 TEST_F(CoreAPIsStandardTest, AddDependencyOnFailedSymbol) {
738 Optional<MaterializationResponsibility> FooR;
739 Optional<MaterializationResponsibility> BarR;
740
741 // Create a MaterializationUnit for each symbol that moves the
742 // MaterializationResponsibility into one of the locals above.
743 auto FooMU = std::make_unique<SimpleMaterializationUnit>(
744 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
745 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
746
747 auto BarMU = std::make_unique<SimpleMaterializationUnit>(
748 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
749 [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
750
751 // Define the symbols.
752 cantFail(JD.define(FooMU));
753 cantFail(JD.define(BarMU));
754
755 bool OnFooReadyRun = false;
756 auto OnFooReady = [&](Expected<SymbolMap> Result) {
757 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
758 OnFooReadyRun = true;
759 };
760
761 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
762 SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),
763 NoDependenciesToRegister);
764
765 bool OnBarReadyRun = false;
766 auto OnBarReady = [&](Expected<SymbolMap> Result) {
767 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
768 OnBarReadyRun = true;
769 };
770
771 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
772 SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
773 NoDependenciesToRegister);
774
775 // Fail bar.
776 BarR->failMaterialization();
777
778 // We expect Bar's query to fail immediately, but Foo's query not to have run
779 // yet.
780 EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
781 EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" should not have run yet";
782
783 // Add dependency of Foo on Bar.
784 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
785
786 // Check that we can still resolve Foo (even though it has been failed).
787 EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
788 << "Expected resolution for \"Foo\" to fail.";
789
790 FooR->failMaterialization();
791
792 // Foo's query should have failed before we return from addDependencies.
793 EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";
794
795 // Verify that subsequent lookups on Bar and Foo fail.
796 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
797 << "Lookup on failed symbol should fail";
798
799 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
800 << "Lookup on failed symbol should fail";
801 }
802
TEST_F(CoreAPIsStandardTest,FailAfterMaterialization)803 TEST_F(CoreAPIsStandardTest, FailAfterMaterialization) {
804 Optional<MaterializationResponsibility> FooR;
805 Optional<MaterializationResponsibility> BarR;
806
807 // Create a MaterializationUnit for each symbol that moves the
808 // MaterializationResponsibility into one of the locals above.
809 auto FooMU = std::make_unique<SimpleMaterializationUnit>(
810 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
811 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
812
813 auto BarMU = std::make_unique<SimpleMaterializationUnit>(
814 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
815 [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
816
817 // Define the symbols.
818 cantFail(JD.define(FooMU));
819 cantFail(JD.define(BarMU));
820
821 bool OnFooReadyRun = false;
822 auto OnFooReady = [&](Expected<SymbolMap> Result) {
823 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
824 OnFooReadyRun = true;
825 };
826
827 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
828 SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnFooReady),
829 NoDependenciesToRegister);
830
831 bool OnBarReadyRun = false;
832 auto OnBarReady = [&](Expected<SymbolMap> Result) {
833 EXPECT_THAT_EXPECTED(std::move(Result), Failed());
834 OnBarReadyRun = true;
835 };
836
837 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
838 SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnBarReady),
839 NoDependenciesToRegister);
840
841 // Add a dependency by Foo on Bar and vice-versa.
842 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
843 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
844
845 // Materialize Foo.
846 EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
847 << "Expected resolution for \"Foo\" to succeed.";
848 EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
849 << "Expected emission for \"Foo\" to succeed.";
850
851 // Fail bar.
852 BarR->failMaterialization();
853
854 // Verify that both queries failed.
855 EXPECT_TRUE(OnFooReadyRun) << "Query for Foo did not run";
856 EXPECT_TRUE(OnBarReadyRun) << "Query for Bar did not run";
857 }
858
TEST_F(CoreAPIsStandardTest,FailMaterializerWithUnqueriedSymbols)859 TEST_F(CoreAPIsStandardTest, FailMaterializerWithUnqueriedSymbols) {
860 // Make sure that symbols with no queries aganist them still
861 // fail correctly.
862
863 bool MaterializerRun = false;
864 auto MU = std::make_unique<SimpleMaterializationUnit>(
865 SymbolFlagsMap(
866 {{Foo, JITSymbolFlags::Exported}, {Bar, JITSymbolFlags::Exported}}),
867 [&](MaterializationResponsibility R) {
868 MaterializerRun = true;
869 R.failMaterialization();
870 });
871
872 cantFail(JD.define(std::move(MU)));
873
874 // Issue a query for Foo, but not bar.
875 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
876 << "Expected lookup to fail.";
877
878 // Check that the materializer (and therefore failMaterialization) ran.
879 EXPECT_TRUE(MaterializerRun) << "Expected materializer to have run by now";
880
881 // Check that subsequent queries against both symbols fail.
882 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
883 << "Expected lookup for Foo to fail.";
884 EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
885 << "Expected lookup for Bar to fail.";
886 }
887
TEST_F(CoreAPIsStandardTest,DropMaterializerWhenEmpty)888 TEST_F(CoreAPIsStandardTest, DropMaterializerWhenEmpty) {
889 bool DestructorRun = false;
890
891 JITSymbolFlags WeakExported(JITSymbolFlags::Exported);
892 WeakExported |= JITSymbolFlags::Weak;
893
894 auto MU = std::make_unique<SimpleMaterializationUnit>(
895 SymbolFlagsMap({{Foo, WeakExported}, {Bar, WeakExported}}),
896 [](MaterializationResponsibility R) {
897 llvm_unreachable("Unexpected call to materialize");
898 },
899 nullptr,
900 [&](const JITDylib &JD, SymbolStringPtr Name) {
901 EXPECT_TRUE(Name == Foo || Name == Bar)
902 << "Discard of unexpected symbol?";
903 },
904 [&]() { DestructorRun = true; });
905
906 cantFail(JD.define(MU));
907
908 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
909
910 EXPECT_FALSE(DestructorRun)
911 << "MaterializationUnit should not have been destroyed yet";
912
913 cantFail(JD.define(absoluteSymbols({{Bar, BarSym}})));
914
915 EXPECT_TRUE(DestructorRun)
916 << "MaterializationUnit should have been destroyed";
917 }
918
TEST_F(CoreAPIsStandardTest,AddAndMaterializeLazySymbol)919 TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) {
920 bool FooMaterialized = false;
921 bool BarDiscarded = false;
922
923 JITSymbolFlags WeakExported(JITSymbolFlags::Exported);
924 WeakExported |= JITSymbolFlags::Weak;
925
926 auto MU = std::make_unique<SimpleMaterializationUnit>(
927 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}),
928 [&](MaterializationResponsibility R) {
929 assert(BarDiscarded && "Bar should have been discarded by this point");
930 cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}})));
931 cantFail(R.notifyEmitted());
932 FooMaterialized = true;
933 },
934 nullptr,
935 [&](const JITDylib &JD, SymbolStringPtr Name) {
936 EXPECT_EQ(Name, Bar) << "Expected Name to be Bar";
937 BarDiscarded = true;
938 });
939
940 cantFail(JD.define(MU));
941 cantFail(JD.define(absoluteSymbols({{Bar, BarSym}})));
942
943 bool OnCompletionRun = false;
944
945 auto OnCompletion = [&](Expected<SymbolMap> Result) {
946 EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
947 auto I = Result->find(Foo);
948 EXPECT_NE(I, Result->end()) << "Could not find symbol definition";
949 EXPECT_EQ(I->second.getAddress(), FooSym.getAddress())
950 << "Resolution returned incorrect result";
951 OnCompletionRun = true;
952 };
953
954 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
955 SymbolLookupSet(Foo), SymbolState::Ready, std::move(OnCompletion),
956 NoDependenciesToRegister);
957
958 EXPECT_TRUE(FooMaterialized) << "Foo was not materialized";
959 EXPECT_TRUE(BarDiscarded) << "Bar was not discarded";
960 EXPECT_TRUE(OnCompletionRun) << "OnResolutionCallback was not run";
961 }
962
TEST_F(CoreAPIsStandardTest,TestBasicWeakSymbolMaterialization)963 TEST_F(CoreAPIsStandardTest, TestBasicWeakSymbolMaterialization) {
964 // Test that weak symbols are materialized correctly when we look them up.
965 BarSym.setFlags(BarSym.getFlags() | JITSymbolFlags::Weak);
966
967 bool BarMaterialized = false;
968 auto MU1 = std::make_unique<SimpleMaterializationUnit>(
969 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
970 [&](MaterializationResponsibility R) {
971 cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
972 cantFail(R.notifyEmitted());
973 BarMaterialized = true;
974 });
975
976 bool DuplicateBarDiscarded = false;
977 auto MU2 = std::make_unique<SimpleMaterializationUnit>(
978 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
979 [&](MaterializationResponsibility R) {
980 ADD_FAILURE() << "Attempt to materialize Bar from the wrong unit";
981 R.failMaterialization();
982 },
983 nullptr,
984 [&](const JITDylib &JD, SymbolStringPtr Name) {
985 EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded";
986 DuplicateBarDiscarded = true;
987 });
988
989 cantFail(JD.define(MU1));
990 cantFail(JD.define(MU2));
991
992 bool OnCompletionRun = false;
993
994 auto OnCompletion = [&](Expected<SymbolMap> Result) {
995 cantFail(std::move(Result));
996 OnCompletionRun = true;
997 };
998
999 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
1000 SymbolLookupSet(Bar), SymbolState::Ready, std::move(OnCompletion),
1001 NoDependenciesToRegister);
1002
1003 EXPECT_TRUE(OnCompletionRun) << "OnCompletion not run";
1004 EXPECT_TRUE(BarMaterialized) << "Bar was not materialized at all";
1005 EXPECT_TRUE(DuplicateBarDiscarded)
1006 << "Duplicate bar definition not discarded";
1007 }
1008
TEST_F(CoreAPIsStandardTest,DefineMaterializingSymbol)1009 TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) {
1010 bool ExpectNoMoreMaterialization = false;
1011 ES.setDispatchMaterialization([&](std::unique_ptr<MaterializationUnit> MU,
1012 MaterializationResponsibility MR) {
1013 if (ExpectNoMoreMaterialization)
1014 ADD_FAILURE() << "Unexpected materialization";
1015 MU->materialize(std::move(MR));
1016 });
1017
1018 auto MU = std::make_unique<SimpleMaterializationUnit>(
1019 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
1020 [&](MaterializationResponsibility R) {
1021 cantFail(
1022 R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}})));
1023 cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
1024 cantFail(R.notifyEmitted());
1025 });
1026
1027 cantFail(JD.define(MU));
1028 cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));
1029
1030 // Assert that materialization is complete by now.
1031 ExpectNoMoreMaterialization = true;
1032
1033 // Look up bar to verify that no further materialization happens.
1034 auto BarResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Bar));
1035 EXPECT_EQ(BarResult.getAddress(), BarSym.getAddress())
1036 << "Expected Bar == BarSym";
1037 }
1038
TEST_F(CoreAPIsStandardTest,GeneratorTest)1039 TEST_F(CoreAPIsStandardTest, GeneratorTest) {
1040 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
1041
1042 class TestGenerator : public JITDylib::DefinitionGenerator {
1043 public:
1044 TestGenerator(SymbolMap Symbols) : Symbols(std::move(Symbols)) {}
1045 Error tryToGenerate(LookupKind K, JITDylib &JD,
1046 JITDylibLookupFlags JDLookupFlags,
1047 const SymbolLookupSet &Names) {
1048 SymbolMap NewDefs;
1049
1050 for (const auto &KV : Names) {
1051 const auto &Name = KV.first;
1052 if (Symbols.count(Name))
1053 NewDefs[Name] = Symbols[Name];
1054 }
1055
1056 cantFail(JD.define(absoluteSymbols(std::move(NewDefs))));
1057 return Error::success();
1058 };
1059
1060 private:
1061 SymbolMap Symbols;
1062 };
1063
1064 JD.addGenerator(std::make_unique<TestGenerator>(SymbolMap({{Bar, BarSym}})));
1065
1066 auto Result = cantFail(
1067 ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo, Bar})));
1068
1069 EXPECT_EQ(Result.count(Bar), 1U) << "Expected to find fallback def for 'bar'";
1070 EXPECT_EQ(Result[Bar].getAddress(), BarSym.getAddress())
1071 << "Expected fallback def for Bar to be equal to BarSym";
1072 }
1073
TEST_F(CoreAPIsStandardTest,FailResolution)1074 TEST_F(CoreAPIsStandardTest, FailResolution) {
1075 auto MU = std::make_unique<SimpleMaterializationUnit>(
1076 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported | JITSymbolFlags::Weak},
1077 {Bar, JITSymbolFlags::Exported | JITSymbolFlags::Weak}}),
1078 [&](MaterializationResponsibility R) {
1079 R.failMaterialization();
1080 });
1081
1082 cantFail(JD.define(MU));
1083
1084 SymbolNameSet Names({Foo, Bar});
1085 auto Result = ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet(Names));
1086
1087 EXPECT_FALSE(!!Result) << "Expected failure";
1088 if (!Result) {
1089 handleAllErrors(
1090 Result.takeError(),
1091 [&](FailedToMaterialize &F) {
1092 EXPECT_TRUE(F.getSymbols().count(&JD))
1093 << "Expected to fail on JITDylib JD";
1094 EXPECT_EQ(F.getSymbols().find(&JD)->second, Names)
1095 << "Expected to fail on symbols in Names";
1096 },
1097 [](ErrorInfoBase &EIB) {
1098 std::string ErrMsg;
1099 {
1100 raw_string_ostream ErrOut(ErrMsg);
1101 EIB.log(ErrOut);
1102 }
1103 ADD_FAILURE() << "Expected a FailedToResolve error. Got:\n" << ErrMsg;
1104 });
1105 }
1106 }
1107
TEST_F(CoreAPIsStandardTest,FailEmissionAfterResolution)1108 TEST_F(CoreAPIsStandardTest, FailEmissionAfterResolution) {
1109
1110 cantFail(JD.define(absoluteSymbols({{Baz, BazSym}})));
1111
1112 auto MU = std::make_unique<SimpleMaterializationUnit>(
1113 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
1114 [&](MaterializationResponsibility R) {
1115 cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
1116
1117 ES.lookup(
1118 LookupKind::Static, makeJITDylibSearchOrder(&JD),
1119 SymbolLookupSet({Baz}), SymbolState::Resolved,
1120 [&R](Expected<SymbolMap> Result) {
1121 // Called when "baz" is resolved. We don't actually depend
1122 // on or care about baz, but use it to trigger failure of
1123 // this materialization before Baz has been finalized in
1124 // order to test that error propagation is correct in this
1125 // scenario.
1126 cantFail(std::move(Result));
1127 R.failMaterialization();
1128 },
1129 [&](const SymbolDependenceMap &Deps) {
1130 R.addDependenciesForAll(Deps);
1131 });
1132 });
1133
1134 cantFail(JD.define(MU));
1135
1136 auto Result =
1137 ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo, Bar}));
1138
1139 EXPECT_THAT_EXPECTED(std::move(Result), Failed())
1140 << "Unexpected success while trying to test error propagation";
1141 }
1142
TEST_F(CoreAPIsStandardTest,FailAfterPartialResolution)1143 TEST_F(CoreAPIsStandardTest, FailAfterPartialResolution) {
1144
1145 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
1146
1147 // Fail materialization of bar.
1148 auto BarMU = std::make_unique<SimpleMaterializationUnit>(
1149 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
1150 [&](MaterializationResponsibility R) { R.failMaterialization(); });
1151
1152 cantFail(JD.define(std::move(BarMU)));
1153
1154 bool QueryHandlerRun = false;
1155 ES.lookup(
1156 LookupKind::Static, makeJITDylibSearchOrder(&JD),
1157 SymbolLookupSet({Foo, Bar}), SymbolState::Resolved,
1158 [&](Expected<SymbolMap> Result) {
1159 EXPECT_THAT_EXPECTED(std::move(Result), Failed())
1160 << "Expected query to fail";
1161 QueryHandlerRun = true;
1162 },
1163 NoDependenciesToRegister);
1164 EXPECT_TRUE(QueryHandlerRun) << "Query handler never ran";
1165 }
1166
TEST_F(CoreAPIsStandardTest,TestLookupWithUnthreadedMaterialization)1167 TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) {
1168 auto MU = std::make_unique<SimpleMaterializationUnit>(
1169 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
1170 [&](MaterializationResponsibility R) {
1171 cantFail(R.notifyResolved({{Foo, FooSym}}));
1172 cantFail(R.notifyEmitted());
1173 });
1174
1175 cantFail(JD.define(MU));
1176
1177 auto FooLookupResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));
1178
1179 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
1180 << "lookup returned an incorrect address";
1181 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
1182 << "lookup returned incorrect flags";
1183 }
1184
TEST_F(CoreAPIsStandardTest,TestLookupWithThreadedMaterialization)1185 TEST_F(CoreAPIsStandardTest, TestLookupWithThreadedMaterialization) {
1186 #if LLVM_ENABLE_THREADS
1187
1188 std::thread MaterializationThread;
1189 ES.setDispatchMaterialization([&](std::unique_ptr<MaterializationUnit> MU,
1190 MaterializationResponsibility MR) {
1191 auto SharedMR =
1192 std::make_shared<MaterializationResponsibility>(std::move(MR));
1193 MaterializationThread =
1194 std::thread([MU = std::move(MU), MR = std::move(SharedMR)] {
1195 MU->materialize(std::move(*MR));
1196 });
1197 });
1198
1199 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
1200
1201 auto FooLookupResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));
1202
1203 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress())
1204 << "lookup returned an incorrect address";
1205 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags())
1206 << "lookup returned incorrect flags";
1207 MaterializationThread.join();
1208 #endif
1209 }
1210
TEST_F(CoreAPIsStandardTest,TestGetRequestedSymbolsAndReplace)1211 TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) {
1212 // Test that GetRequestedSymbols returns the set of symbols that currently
1213 // have pending queries, and test that MaterializationResponsibility's
1214 // replace method can be used to return definitions to the JITDylib in a new
1215 // MaterializationUnit.
1216 SymbolNameSet Names({Foo, Bar});
1217
1218 bool FooMaterialized = false;
1219 bool BarMaterialized = false;
1220
1221 auto MU = std::make_unique<SimpleMaterializationUnit>(
1222 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
1223 [&](MaterializationResponsibility R) {
1224 auto Requested = R.getRequestedSymbols();
1225 EXPECT_EQ(Requested.size(), 1U) << "Expected one symbol requested";
1226 EXPECT_EQ(*Requested.begin(), Foo) << "Expected \"Foo\" requested";
1227
1228 auto NewMU = std::make_unique<SimpleMaterializationUnit>(
1229 SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
1230 [&](MaterializationResponsibility R2) {
1231 cantFail(R2.notifyResolved(SymbolMap({{Bar, BarSym}})));
1232 cantFail(R2.notifyEmitted());
1233 BarMaterialized = true;
1234 });
1235
1236 R.replace(std::move(NewMU));
1237
1238 cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}})));
1239 cantFail(R.notifyEmitted());
1240
1241 FooMaterialized = true;
1242 });
1243
1244 cantFail(JD.define(MU));
1245
1246 EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet";
1247 EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet";
1248
1249 auto FooSymResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Foo));
1250 EXPECT_EQ(FooSymResult.getAddress(), FooSym.getAddress())
1251 << "Address mismatch for Foo";
1252
1253 EXPECT_TRUE(FooMaterialized) << "Foo should be materialized now";
1254 EXPECT_FALSE(BarMaterialized) << "Bar still should not be materialized";
1255
1256 auto BarSymResult = cantFail(ES.lookup(makeJITDylibSearchOrder(&JD), Bar));
1257 EXPECT_EQ(BarSymResult.getAddress(), BarSym.getAddress())
1258 << "Address mismatch for Bar";
1259 EXPECT_TRUE(BarMaterialized) << "Bar should be materialized now";
1260 }
1261
TEST_F(CoreAPIsStandardTest,TestMaterializationResponsibilityDelegation)1262 TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) {
1263 auto MU = std::make_unique<SimpleMaterializationUnit>(
1264 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
1265 [&](MaterializationResponsibility R) {
1266 auto R2 = R.delegate({Bar});
1267
1268 cantFail(R.notifyResolved({{Foo, FooSym}}));
1269 cantFail(R.notifyEmitted());
1270 cantFail(R2.notifyResolved({{Bar, BarSym}}));
1271 cantFail(R2.notifyEmitted());
1272 });
1273
1274 cantFail(JD.define(MU));
1275
1276 auto Result =
1277 ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo, Bar}));
1278
1279 EXPECT_TRUE(!!Result) << "Result should be a success value";
1280 EXPECT_EQ(Result->count(Foo), 1U) << "\"Foo\" entry missing";
1281 EXPECT_EQ(Result->count(Bar), 1U) << "\"Bar\" entry missing";
1282 EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress())
1283 << "Address mismatch for \"Foo\"";
1284 EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress())
1285 << "Address mismatch for \"Bar\"";
1286 }
1287
TEST_F(CoreAPIsStandardTest,TestMaterializeWeakSymbol)1288 TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) {
1289 // Confirm that once a weak definition is selected for materialization it is
1290 // treated as strong.
1291 JITSymbolFlags WeakExported = JITSymbolFlags::Exported;
1292 WeakExported &= JITSymbolFlags::Weak;
1293
1294 std::unique_ptr<MaterializationResponsibility> FooResponsibility;
1295 auto MU = std::make_unique<SimpleMaterializationUnit>(
1296 SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
1297 [&](MaterializationResponsibility R) {
1298 FooResponsibility =
1299 std::make_unique<MaterializationResponsibility>(std::move(R));
1300 });
1301
1302 cantFail(JD.define(MU));
1303 auto OnCompletion = [](Expected<SymbolMap> Result) {
1304 cantFail(std::move(Result));
1305 };
1306
1307 ES.lookup(LookupKind::Static, makeJITDylibSearchOrder(&JD),
1308 SymbolLookupSet({Foo}), SymbolState::Ready, std::move(OnCompletion),
1309 NoDependenciesToRegister);
1310
1311 auto MU2 = std::make_unique<SimpleMaterializationUnit>(
1312 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
1313 [](MaterializationResponsibility R) {
1314 llvm_unreachable("This unit should never be materialized");
1315 });
1316
1317 auto Err = JD.define(MU2);
1318 EXPECT_TRUE(!!Err) << "Expected failure value";
1319 EXPECT_TRUE(Err.isA<DuplicateDefinition>())
1320 << "Expected a duplicate definition error";
1321 consumeError(std::move(Err));
1322
1323 // No dependencies registered, can't fail:
1324 cantFail(FooResponsibility->notifyResolved(SymbolMap({{Foo, FooSym}})));
1325 cantFail(FooResponsibility->notifyEmitted());
1326 }
1327
1328 } // namespace
1329