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 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(llvm::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(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready, 47 OnCompletion, NoDependenciesToRegister); 48 49 EXPECT_FALSE(OnCompletionRun) << "Should not have been resolved yet"; 50 51 FooMR->notifyResolved({{Foo, FooSym}}); 52 53 EXPECT_FALSE(OnCompletionRun) << "Should not be ready yet"; 54 55 FooMR->notifyEmitted(); 56 57 EXPECT_TRUE(OnCompletionRun) << "Should have been marked ready"; 58 } 59 60 TEST_F(CoreAPIsStandardTest, ExecutionSessionFailQuery) { 61 bool OnCompletionRun = false; 62 63 auto OnCompletion = [&](Expected<SymbolMap> Result) { 64 EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success"; 65 auto Msg = toString(Result.takeError()); 66 EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result"; 67 OnCompletionRun = true; 68 }; 69 70 AsynchronousSymbolQuery Q(SymbolNameSet({Foo}), SymbolState::Ready, 71 OnCompletion); 72 73 ES.legacyFailQuery(Q, 74 make_error<StringError>("xyz", inconvertibleErrorCode())); 75 76 EXPECT_TRUE(OnCompletionRun) << "OnCompletionCallback was not run"; 77 } 78 79 TEST_F(CoreAPIsStandardTest, EmptyLookup) { 80 bool OnCompletionRun = false; 81 82 auto OnCompletion = [&](Expected<SymbolMap> Result) { 83 cantFail(std::move(Result)); 84 OnCompletionRun = true; 85 }; 86 87 ES.lookup(JITDylibSearchList({{&JD, false}}), {}, SymbolState::Ready, 88 OnCompletion, NoDependenciesToRegister); 89 90 EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run for empty query"; 91 } 92 93 TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) { 94 // Test that: 95 // (1) Missing symbols generate a SymbolsNotFound error. 96 // (2) Materializing symbols generate a SymbolCouldNotBeRemoved error. 97 // (3) Removal of unmaterialized symbols triggers discard on the 98 // materialization unit. 99 // (4) Removal of symbols destroys empty materialization units. 100 // (5) Removal of materialized symbols works. 101 102 // Foo will be fully materialized. 103 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 104 105 // Bar will be unmaterialized. 106 bool BarDiscarded = false; 107 bool BarMaterializerDestructed = false; 108 cantFail(JD.define(llvm::make_unique<SimpleMaterializationUnit>( 109 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 110 [this](MaterializationResponsibility R) { 111 ADD_FAILURE() << "Unexpected materialization of \"Bar\""; 112 R.notifyResolved({{Bar, BarSym}}); 113 R.notifyEmitted(); 114 }, 115 [&](const JITDylib &JD, const SymbolStringPtr &Name) { 116 EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded"; 117 if (Name == Bar) 118 BarDiscarded = true; 119 }, 120 [&]() { BarMaterializerDestructed = true; }))); 121 122 // Baz will be in the materializing state initially, then 123 // materialized for the final removal attempt. 124 Optional<MaterializationResponsibility> BazR; 125 cantFail(JD.define(llvm::make_unique<SimpleMaterializationUnit>( 126 SymbolFlagsMap({{Baz, BazSym.getFlags()}}), 127 [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); }, 128 [](const JITDylib &JD, const SymbolStringPtr &Name) { 129 ADD_FAILURE() << "\"Baz\" discarded unexpectedly"; 130 }))); 131 132 bool OnCompletionRun = false; 133 ES.lookup( 134 JITDylibSearchList({{&JD, false}}), {Foo, Baz}, SymbolState::Ready, 135 [&](Expected<SymbolMap> Result) { 136 cantFail(Result.takeError()); 137 OnCompletionRun = true; 138 }, 139 NoDependenciesToRegister); 140 141 { 142 // Attempt 1: Search for a missing symbol, Qux. 143 auto Err = JD.remove({Foo, Bar, Baz, Qux}); 144 EXPECT_TRUE(!!Err) << "Expected failure"; 145 EXPECT_TRUE(Err.isA<SymbolsNotFound>()) 146 << "Expected a SymbolsNotFound error"; 147 consumeError(std::move(Err)); 148 } 149 150 { 151 // Attempt 2: Search for a symbol that is still materializing, Baz. 152 auto Err = JD.remove({Foo, Bar, Baz}); 153 EXPECT_TRUE(!!Err) << "Expected failure"; 154 EXPECT_TRUE(Err.isA<SymbolsCouldNotBeRemoved>()) 155 << "Expected a SymbolsNotFound error"; 156 consumeError(std::move(Err)); 157 } 158 159 BazR->notifyResolved({{Baz, BazSym}}); 160 BazR->notifyEmitted(); 161 { 162 // Attempt 3: Search now that all symbols are fully materialized 163 // (Foo, Baz), or not yet materialized (Bar). 164 auto Err = JD.remove({Foo, Bar, Baz}); 165 EXPECT_FALSE(!!Err) << "Expected failure"; 166 } 167 168 EXPECT_TRUE(BarDiscarded) << "\"Bar\" should have been discarded"; 169 EXPECT_TRUE(BarMaterializerDestructed) 170 << "\"Bar\"'s materializer should have been destructed"; 171 EXPECT_TRUE(OnCompletionRun) << "OnCompletion should have been run"; 172 } 173 174 TEST_F(CoreAPIsStandardTest, ChainedJITDylibLookup) { 175 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 176 177 auto &JD2 = ES.createJITDylib("JD2"); 178 179 bool OnCompletionRun = false; 180 181 auto Q = std::make_shared<AsynchronousSymbolQuery>( 182 SymbolNameSet({Foo}), SymbolState::Ready, 183 [&](Expected<SymbolMap> Result) { 184 cantFail(std::move(Result)); 185 OnCompletionRun = true; 186 }); 187 188 cantFail(JD2.legacyLookup(Q, cantFail(JD.legacyLookup(Q, {Foo})))); 189 190 EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run for empty query"; 191 } 192 193 TEST_F(CoreAPIsStandardTest, LookupWithHiddenSymbols) { 194 auto BarHiddenFlags = BarSym.getFlags() & ~JITSymbolFlags::Exported; 195 auto BarHiddenSym = JITEvaluatedSymbol(BarSym.getAddress(), BarHiddenFlags); 196 197 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarHiddenSym}}))); 198 199 auto &JD2 = ES.createJITDylib("JD2"); 200 cantFail(JD2.define(absoluteSymbols({{Bar, QuxSym}}))); 201 202 /// Try a blocking lookup. 203 auto Result = cantFail( 204 ES.lookup(JITDylibSearchList({{&JD, false}, {&JD2, false}}), {Foo, Bar})); 205 206 EXPECT_EQ(Result.size(), 2U) << "Unexpected number of results"; 207 EXPECT_EQ(Result.count(Foo), 1U) << "Missing result for \"Foo\""; 208 EXPECT_EQ(Result.count(Bar), 1U) << "Missing result for \"Bar\""; 209 EXPECT_EQ(Result[Bar].getAddress(), QuxSym.getAddress()) 210 << "Wrong result for \"Bar\""; 211 } 212 213 TEST_F(CoreAPIsStandardTest, LookupFlagsTest) { 214 // Test that lookupFlags works on a predefined symbol, and does not trigger 215 // materialization of a lazy symbol. Make the lazy symbol weak to test that 216 // the weak flag is propagated correctly. 217 218 BarSym.setFlags(static_cast<JITSymbolFlags::FlagNames>( 219 JITSymbolFlags::Exported | JITSymbolFlags::Weak)); 220 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 221 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 222 [](MaterializationResponsibility R) { 223 llvm_unreachable("Symbol materialized on flags lookup"); 224 }); 225 226 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 227 cantFail(JD.define(std::move(MU))); 228 229 SymbolNameSet Names({Foo, Bar, Baz}); 230 231 auto SymbolFlags = cantFail(JD.lookupFlags(Names)); 232 233 EXPECT_EQ(SymbolFlags.size(), 2U) 234 << "Returned symbol flags contains unexpected results"; 235 EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo"; 236 EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags()) 237 << "Incorrect flags returned for Foo"; 238 EXPECT_EQ(SymbolFlags.count(Bar), 1U) 239 << "Missing lookupFlags result for Bar"; 240 EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags()) 241 << "Incorrect flags returned for Bar"; 242 } 243 244 TEST_F(CoreAPIsStandardTest, LookupWithGeneratorFailure) { 245 246 class BadGenerator { 247 public: 248 Expected<SymbolNameSet> operator()(JITDylib &, const SymbolNameSet &) { 249 return make_error<StringError>("BadGenerator", inconvertibleErrorCode()); 250 } 251 }; 252 253 JD.setGenerator(BadGenerator()); 254 255 EXPECT_THAT_ERROR(JD.lookupFlags({Foo}).takeError(), Failed<StringError>()) 256 << "Generator failure did not propagate through lookupFlags"; 257 258 EXPECT_THAT_ERROR( 259 ES.lookup(JITDylibSearchList({{&JD, false}}), SymbolNameSet({Foo})) 260 .takeError(), 261 Failed<StringError>()) 262 << "Generator failure did not propagate through lookup"; 263 } 264 265 TEST_F(CoreAPIsStandardTest, TestBasicAliases) { 266 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); 267 cantFail(JD.define(symbolAliases({{Baz, {Foo, JITSymbolFlags::Exported}}, 268 {Qux, {Bar, JITSymbolFlags::Weak}}}))); 269 cantFail(JD.define(absoluteSymbols({{Qux, QuxSym}}))); 270 271 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz, Qux}); 272 EXPECT_TRUE(!!Result) << "Unexpected lookup failure"; 273 EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\""; 274 EXPECT_EQ(Result->count(Qux), 1U) << "No result for \"qux\""; 275 EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress()) 276 << "\"Baz\"'s address should match \"Foo\"'s"; 277 EXPECT_EQ((*Result)[Qux].getAddress(), QuxSym.getAddress()) 278 << "The \"Qux\" alias should have been overriden"; 279 } 280 281 TEST_F(CoreAPIsStandardTest, TestChainedAliases) { 282 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 283 cantFail(JD.define(symbolAliases( 284 {{Baz, {Bar, BazSym.getFlags()}}, {Bar, {Foo, BarSym.getFlags()}}}))); 285 286 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar, Baz}); 287 EXPECT_TRUE(!!Result) << "Unexpected lookup failure"; 288 EXPECT_EQ(Result->count(Bar), 1U) << "No result for \"bar\""; 289 EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\""; 290 EXPECT_EQ((*Result)[Bar].getAddress(), FooSym.getAddress()) 291 << "\"Bar\"'s address should match \"Foo\"'s"; 292 EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress()) 293 << "\"Baz\"'s address should match \"Foo\"'s"; 294 } 295 296 TEST_F(CoreAPIsStandardTest, TestBasicReExports) { 297 // Test that the basic use case of re-exporting a single symbol from another 298 // JITDylib works. 299 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 300 301 auto &JD2 = ES.createJITDylib("JD2"); 302 303 cantFail(JD2.define(reexports(JD, {{Bar, {Foo, BarSym.getFlags()}}}))); 304 305 auto Result = cantFail(ES.lookup(JITDylibSearchList({{&JD2, false}}), Bar)); 306 EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) 307 << "Re-export Bar for symbol Foo should match FooSym's address"; 308 } 309 310 TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) { 311 // Test that re-exports do not materialize symbols that have not been queried 312 // for. 313 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 314 315 bool BarMaterialized = false; 316 auto BarMU = llvm::make_unique<SimpleMaterializationUnit>( 317 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 318 [&](MaterializationResponsibility R) { 319 BarMaterialized = true; 320 R.notifyResolved({{Bar, BarSym}}); 321 R.notifyEmitted(); 322 }); 323 324 cantFail(JD.define(BarMU)); 325 326 auto &JD2 = ES.createJITDylib("JD2"); 327 328 cantFail(JD2.define(reexports( 329 JD, {{Baz, {Foo, BazSym.getFlags()}}, {Qux, {Bar, QuxSym.getFlags()}}}))); 330 331 auto Result = cantFail(ES.lookup(JITDylibSearchList({{&JD2, false}}), Baz)); 332 EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) 333 << "Re-export Baz for symbol Foo should match FooSym's address"; 334 335 EXPECT_FALSE(BarMaterialized) << "Bar should not have been materialized"; 336 } 337 338 TEST_F(CoreAPIsStandardTest, TestReexportsGenerator) { 339 // Test that a re-exports generator can dynamically generate reexports. 340 341 auto &JD2 = ES.createJITDylib("JD2"); 342 cantFail(JD2.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); 343 344 auto Filter = [this](SymbolStringPtr Name) { return Name != Bar; }; 345 346 JD.setGenerator(ReexportsGenerator(JD2, false, Filter)); 347 348 auto Flags = cantFail(JD.lookupFlags({Foo, Bar, Baz})); 349 EXPECT_EQ(Flags.size(), 1U) << "Unexpected number of results"; 350 EXPECT_EQ(Flags[Foo], FooSym.getFlags()) << "Unexpected flags for Foo"; 351 352 auto Result = cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 353 354 EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) 355 << "Incorrect reexported symbol address"; 356 } 357 358 TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) { 359 Optional<MaterializationResponsibility> FooR; 360 auto FooMU = llvm::make_unique<SimpleMaterializationUnit>( 361 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 362 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); 363 364 cantFail(JD.define(FooMU)); 365 366 bool FooReady = false; 367 auto OnCompletion = [&](Expected<SymbolMap> Result) { 368 cantFail(std::move(Result)); 369 FooReady = true; 370 }; 371 372 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready, 373 OnCompletion, NoDependenciesToRegister); 374 375 FooR->notifyResolved({{Foo, FooSym}}); 376 FooR->notifyEmitted(); 377 378 EXPECT_TRUE(FooReady) 379 << "Self-dependency prevented symbol from being marked ready"; 380 } 381 382 TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) { 383 // Test that a circular symbol dependency between three symbols in a JITDylib 384 // does not prevent any symbol from becoming 'ready' once all symbols are 385 // emitted. 386 387 // Create three MaterializationResponsibility objects: one for each of Foo, 388 // Bar and Baz. These are optional because MaterializationResponsibility 389 // does not have a default constructor). 390 Optional<MaterializationResponsibility> FooR; 391 Optional<MaterializationResponsibility> BarR; 392 Optional<MaterializationResponsibility> BazR; 393 394 // Create a MaterializationUnit for each symbol that moves the 395 // MaterializationResponsibility into one of the locals above. 396 auto FooMU = llvm::make_unique<SimpleMaterializationUnit>( 397 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 398 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); 399 400 auto BarMU = llvm::make_unique<SimpleMaterializationUnit>( 401 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 402 [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); }); 403 404 auto BazMU = llvm::make_unique<SimpleMaterializationUnit>( 405 SymbolFlagsMap({{Baz, BazSym.getFlags()}}), 406 [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); }); 407 408 // Define the symbols. 409 cantFail(JD.define(FooMU)); 410 cantFail(JD.define(BarMU)); 411 cantFail(JD.define(BazMU)); 412 413 // Query each of the symbols to trigger materialization. 414 bool FooResolved = false; 415 bool FooReady = false; 416 417 auto OnFooResolution = [&](Expected<SymbolMap> Result) { 418 cantFail(std::move(Result)); 419 FooResolved = true; 420 }; 421 422 auto OnFooReady = [&](Expected<SymbolMap> Result) { 423 cantFail(std::move(Result)); 424 FooReady = true; 425 }; 426 427 // Issue lookups for Foo. Use NoDependenciesToRegister: We're going to add 428 // the dependencies manually below. 429 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Resolved, 430 std::move(OnFooResolution), NoDependenciesToRegister); 431 432 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready, 433 std::move(OnFooReady), NoDependenciesToRegister); 434 435 bool BarResolved = false; 436 bool BarReady = false; 437 auto OnBarResolution = [&](Expected<SymbolMap> Result) { 438 cantFail(std::move(Result)); 439 BarResolved = true; 440 }; 441 442 auto OnBarReady = [&](Expected<SymbolMap> Result) { 443 cantFail(std::move(Result)); 444 BarReady = true; 445 }; 446 447 ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Resolved, 448 std::move(OnBarResolution), NoDependenciesToRegister); 449 450 ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready, 451 std::move(OnBarReady), NoDependenciesToRegister); 452 453 bool BazResolved = false; 454 bool BazReady = false; 455 456 auto OnBazResolution = [&](Expected<SymbolMap> Result) { 457 cantFail(std::move(Result)); 458 BazResolved = true; 459 }; 460 461 auto OnBazReady = [&](Expected<SymbolMap> Result) { 462 cantFail(std::move(Result)); 463 BazReady = true; 464 }; 465 466 ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz}, SymbolState::Resolved, 467 std::move(OnBazResolution), NoDependenciesToRegister); 468 469 ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz}, SymbolState::Ready, 470 std::move(OnBazReady), NoDependenciesToRegister); 471 472 // Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo. 473 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}}); 474 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}}); 475 BazR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}}); 476 477 // Add self-dependencies for good measure. This tests that the implementation 478 // of addDependencies filters these out. 479 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}}); 480 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}}); 481 BazR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}}); 482 483 // Check that nothing has been resolved yet. 484 EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet"; 485 EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet"; 486 EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet"; 487 488 // Resolve the symbols (but do not emit them). 489 FooR->notifyResolved({{Foo, FooSym}}); 490 BarR->notifyResolved({{Bar, BarSym}}); 491 BazR->notifyResolved({{Baz, BazSym}}); 492 493 // Verify that the symbols have been resolved, but are not ready yet. 494 EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now"; 495 EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now"; 496 EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now"; 497 498 EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet"; 499 EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet"; 500 EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet"; 501 502 // Emit two of the symbols. 503 FooR->notifyEmitted(); 504 BarR->notifyEmitted(); 505 506 // Verify that nothing is ready until the circular dependence is resolved. 507 EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready"; 508 EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready"; 509 EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready"; 510 511 // Emit the last symbol. 512 BazR->notifyEmitted(); 513 514 // Verify that everything becomes ready once the circular dependence resolved. 515 EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now"; 516 EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now"; 517 EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now"; 518 } 519 520 TEST_F(CoreAPIsStandardTest, DropMaterializerWhenEmpty) { 521 bool DestructorRun = false; 522 523 JITSymbolFlags WeakExported(JITSymbolFlags::Exported); 524 WeakExported |= JITSymbolFlags::Weak; 525 526 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 527 SymbolFlagsMap({{Foo, WeakExported}, {Bar, WeakExported}}), 528 [](MaterializationResponsibility R) { 529 llvm_unreachable("Unexpected call to materialize"); 530 }, 531 [&](const JITDylib &JD, SymbolStringPtr Name) { 532 EXPECT_TRUE(Name == Foo || Name == Bar) 533 << "Discard of unexpected symbol?"; 534 }, 535 [&]() { DestructorRun = true; }); 536 537 cantFail(JD.define(MU)); 538 539 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 540 541 EXPECT_FALSE(DestructorRun) 542 << "MaterializationUnit should not have been destroyed yet"; 543 544 cantFail(JD.define(absoluteSymbols({{Bar, BarSym}}))); 545 546 EXPECT_TRUE(DestructorRun) 547 << "MaterializationUnit should have been destroyed"; 548 } 549 550 TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) { 551 bool FooMaterialized = false; 552 bool BarDiscarded = false; 553 554 JITSymbolFlags WeakExported(JITSymbolFlags::Exported); 555 WeakExported |= JITSymbolFlags::Weak; 556 557 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 558 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}), 559 [&](MaterializationResponsibility R) { 560 assert(BarDiscarded && "Bar should have been discarded by this point"); 561 R.notifyResolved(SymbolMap({{Foo, FooSym}})); 562 R.notifyEmitted(); 563 FooMaterialized = true; 564 }, 565 [&](const JITDylib &JD, SymbolStringPtr Name) { 566 EXPECT_EQ(Name, Bar) << "Expected Name to be Bar"; 567 BarDiscarded = true; 568 }); 569 570 cantFail(JD.define(MU)); 571 cantFail(JD.define(absoluteSymbols({{Bar, BarSym}}))); 572 573 SymbolNameSet Names({Foo}); 574 575 bool OnCompletionRun = false; 576 577 auto OnCompletion = [&](Expected<SymbolMap> Result) { 578 EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; 579 auto I = Result->find(Foo); 580 EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; 581 EXPECT_EQ(I->second.getAddress(), FooSym.getAddress()) 582 << "Resolution returned incorrect result"; 583 OnCompletionRun = true; 584 }; 585 586 ES.lookup(JITDylibSearchList({{&JD, false}}), Names, SymbolState::Ready, 587 std::move(OnCompletion), NoDependenciesToRegister); 588 589 EXPECT_TRUE(FooMaterialized) << "Foo was not materialized"; 590 EXPECT_TRUE(BarDiscarded) << "Bar was not discarded"; 591 EXPECT_TRUE(OnCompletionRun) << "OnResolutionCallback was not run"; 592 } 593 594 TEST_F(CoreAPIsStandardTest, TestBasicWeakSymbolMaterialization) { 595 // Test that weak symbols are materialized correctly when we look them up. 596 BarSym.setFlags(BarSym.getFlags() | JITSymbolFlags::Weak); 597 598 bool BarMaterialized = false; 599 auto MU1 = llvm::make_unique<SimpleMaterializationUnit>( 600 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 601 [&](MaterializationResponsibility R) { 602 R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})), R.notifyEmitted(); 603 BarMaterialized = true; 604 }); 605 606 bool DuplicateBarDiscarded = false; 607 auto MU2 = llvm::make_unique<SimpleMaterializationUnit>( 608 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 609 [&](MaterializationResponsibility R) { 610 ADD_FAILURE() << "Attempt to materialize Bar from the wrong unit"; 611 R.failMaterialization(); 612 }, 613 [&](const JITDylib &JD, SymbolStringPtr Name) { 614 EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded"; 615 DuplicateBarDiscarded = true; 616 }); 617 618 cantFail(JD.define(MU1)); 619 cantFail(JD.define(MU2)); 620 621 bool OnCompletionRun = false; 622 623 auto OnCompletion = [&](Expected<SymbolMap> Result) { 624 cantFail(std::move(Result)); 625 OnCompletionRun = true; 626 }; 627 628 ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready, 629 std::move(OnCompletion), NoDependenciesToRegister); 630 631 EXPECT_TRUE(OnCompletionRun) << "OnCompletion not run"; 632 EXPECT_TRUE(BarMaterialized) << "Bar was not materialized at all"; 633 EXPECT_TRUE(DuplicateBarDiscarded) 634 << "Duplicate bar definition not discarded"; 635 } 636 637 TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) { 638 bool ExpectNoMoreMaterialization = false; 639 ES.setDispatchMaterialization( 640 [&](JITDylib &JD, std::unique_ptr<MaterializationUnit> MU) { 641 if (ExpectNoMoreMaterialization) 642 ADD_FAILURE() << "Unexpected materialization"; 643 MU->doMaterialize(JD); 644 }); 645 646 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 647 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 648 [&](MaterializationResponsibility R) { 649 cantFail( 650 R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}}))); 651 R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); 652 R.notifyEmitted(); 653 }); 654 655 cantFail(JD.define(MU)); 656 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 657 658 // Assert that materialization is complete by now. 659 ExpectNoMoreMaterialization = true; 660 661 // Look up bar to verify that no further materialization happens. 662 auto BarResult = cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Bar)); 663 EXPECT_EQ(BarResult.getAddress(), BarSym.getAddress()) 664 << "Expected Bar == BarSym"; 665 } 666 667 TEST_F(CoreAPIsStandardTest, GeneratorTest) { 668 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 669 670 JD.setGenerator([&](JITDylib &JD2, const SymbolNameSet &Names) { 671 cantFail(JD2.define(absoluteSymbols({{Bar, BarSym}}))); 672 return SymbolNameSet({Bar}); 673 }); 674 675 auto Result = 676 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo, Bar})); 677 678 EXPECT_EQ(Result.count(Bar), 1U) << "Expected to find fallback def for 'bar'"; 679 EXPECT_EQ(Result[Bar].getAddress(), BarSym.getAddress()) 680 << "Expected fallback def for Bar to be equal to BarSym"; 681 } 682 683 TEST_F(CoreAPIsStandardTest, FailResolution) { 684 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 685 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported | JITSymbolFlags::Weak}, 686 {Bar, JITSymbolFlags::Exported | JITSymbolFlags::Weak}}), 687 [&](MaterializationResponsibility R) { 688 dbgs() << "Before failMat:\n"; 689 ES.dump(dbgs()); 690 R.failMaterialization(); 691 }); 692 693 cantFail(JD.define(MU)); 694 695 SymbolNameSet Names({Foo, Bar}); 696 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), Names); 697 698 EXPECT_FALSE(!!Result) << "Expected failure"; 699 if (!Result) { 700 handleAllErrors(Result.takeError(), 701 [&](FailedToMaterialize &F) { 702 EXPECT_EQ(F.getSymbols(), Names) 703 << "Expected to fail on symbols in Names"; 704 }, 705 [](ErrorInfoBase &EIB) { 706 std::string ErrMsg; 707 { 708 raw_string_ostream ErrOut(ErrMsg); 709 EIB.log(ErrOut); 710 } 711 ADD_FAILURE() 712 << "Expected a FailedToResolve error. Got:\n" 713 << ErrMsg; 714 }); 715 } 716 } 717 718 TEST_F(CoreAPIsStandardTest, FailEmissionEarly) { 719 720 cantFail(JD.define(absoluteSymbols({{Baz, BazSym}}))); 721 722 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 723 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 724 [&](MaterializationResponsibility R) { 725 R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); 726 727 ES.lookup( 728 JITDylibSearchList({{&JD, false}}), SymbolNameSet({Baz}), 729 SymbolState::Resolved, 730 [&R](Expected<SymbolMap> Result) { 731 // Called when "baz" is resolved. We don't actually depend 732 // on or care about baz, but use it to trigger failure of 733 // this materialization before Baz has been finalized in 734 // order to test that error propagation is correct in this 735 // scenario. 736 cantFail(std::move(Result)); 737 R.failMaterialization(); 738 }, 739 [&](const SymbolDependenceMap &Deps) { 740 R.addDependenciesForAll(Deps); 741 }); 742 }); 743 744 cantFail(JD.define(MU)); 745 746 SymbolNameSet Names({Foo, Bar}); 747 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), Names); 748 749 EXPECT_THAT_EXPECTED(std::move(Result), Failed()) 750 << "Unexpected success while trying to test error propagation"; 751 } 752 753 TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) { 754 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 755 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), 756 [&](MaterializationResponsibility R) { 757 R.notifyResolved({{Foo, FooSym}}); 758 R.notifyEmitted(); 759 }); 760 761 cantFail(JD.define(MU)); 762 763 auto FooLookupResult = 764 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 765 766 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) 767 << "lookup returned an incorrect address"; 768 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) 769 << "lookup returned incorrect flags"; 770 } 771 772 TEST_F(CoreAPIsStandardTest, TestLookupWithThreadedMaterialization) { 773 #if LLVM_ENABLE_THREADS 774 775 std::thread MaterializationThread; 776 ES.setDispatchMaterialization( 777 [&](JITDylib &JD, std::unique_ptr<MaterializationUnit> MU) { 778 auto SharedMU = std::shared_ptr<MaterializationUnit>(std::move(MU)); 779 MaterializationThread = 780 std::thread([SharedMU, &JD]() { SharedMU->doMaterialize(JD); }); 781 }); 782 783 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 784 785 auto FooLookupResult = 786 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 787 788 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) 789 << "lookup returned an incorrect address"; 790 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) 791 << "lookup returned incorrect flags"; 792 MaterializationThread.join(); 793 #endif 794 } 795 796 TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) { 797 // Test that GetRequestedSymbols returns the set of symbols that currently 798 // have pending queries, and test that MaterializationResponsibility's 799 // replace method can be used to return definitions to the JITDylib in a new 800 // MaterializationUnit. 801 SymbolNameSet Names({Foo, Bar}); 802 803 bool FooMaterialized = false; 804 bool BarMaterialized = false; 805 806 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 807 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 808 [&](MaterializationResponsibility R) { 809 auto Requested = R.getRequestedSymbols(); 810 EXPECT_EQ(Requested.size(), 1U) << "Expected one symbol requested"; 811 EXPECT_EQ(*Requested.begin(), Foo) << "Expected \"Foo\" requested"; 812 813 auto NewMU = llvm::make_unique<SimpleMaterializationUnit>( 814 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 815 [&](MaterializationResponsibility R2) { 816 R2.notifyResolved(SymbolMap({{Bar, BarSym}})); 817 R2.notifyEmitted(); 818 BarMaterialized = true; 819 }); 820 821 R.replace(std::move(NewMU)); 822 823 R.notifyResolved(SymbolMap({{Foo, FooSym}})); 824 R.notifyEmitted(); 825 826 FooMaterialized = true; 827 }); 828 829 cantFail(JD.define(MU)); 830 831 EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet"; 832 EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet"; 833 834 auto FooSymResult = 835 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 836 EXPECT_EQ(FooSymResult.getAddress(), FooSym.getAddress()) 837 << "Address mismatch for Foo"; 838 839 EXPECT_TRUE(FooMaterialized) << "Foo should be materialized now"; 840 EXPECT_FALSE(BarMaterialized) << "Bar still should not be materialized"; 841 842 auto BarSymResult = 843 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Bar)); 844 EXPECT_EQ(BarSymResult.getAddress(), BarSym.getAddress()) 845 << "Address mismatch for Bar"; 846 EXPECT_TRUE(BarMaterialized) << "Bar should be materialized now"; 847 } 848 849 TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) { 850 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 851 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 852 [&](MaterializationResponsibility R) { 853 auto R2 = R.delegate({Bar}); 854 855 R.notifyResolved({{Foo, FooSym}}); 856 R.notifyEmitted(); 857 R2.notifyResolved({{Bar, BarSym}}); 858 R2.notifyEmitted(); 859 }); 860 861 cantFail(JD.define(MU)); 862 863 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo, Bar}); 864 865 EXPECT_TRUE(!!Result) << "Result should be a success value"; 866 EXPECT_EQ(Result->count(Foo), 1U) << "\"Foo\" entry missing"; 867 EXPECT_EQ(Result->count(Bar), 1U) << "\"Bar\" entry missing"; 868 EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress()) 869 << "Address mismatch for \"Foo\""; 870 EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress()) 871 << "Address mismatch for \"Bar\""; 872 } 873 874 TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) { 875 // Confirm that once a weak definition is selected for materialization it is 876 // treated as strong. 877 JITSymbolFlags WeakExported = JITSymbolFlags::Exported; 878 WeakExported &= JITSymbolFlags::Weak; 879 880 std::unique_ptr<MaterializationResponsibility> FooResponsibility; 881 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 882 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 883 [&](MaterializationResponsibility R) { 884 FooResponsibility = 885 llvm::make_unique<MaterializationResponsibility>(std::move(R)); 886 }); 887 888 cantFail(JD.define(MU)); 889 auto OnCompletion = [](Expected<SymbolMap> Result) { 890 cantFail(std::move(Result)); 891 }; 892 893 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready, 894 std::move(OnCompletion), NoDependenciesToRegister); 895 896 auto MU2 = llvm::make_unique<SimpleMaterializationUnit>( 897 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), 898 [](MaterializationResponsibility R) { 899 llvm_unreachable("This unit should never be materialized"); 900 }); 901 902 auto Err = JD.define(MU2); 903 EXPECT_TRUE(!!Err) << "Expected failure value"; 904 EXPECT_TRUE(Err.isA<DuplicateDefinition>()) 905 << "Expected a duplicate definition error"; 906 consumeError(std::move(Err)); 907 908 FooResponsibility->notifyResolved(SymbolMap({{Foo, FooSym}})); 909 FooResponsibility->notifyEmitted(); 910 } 911 912 } // namespace 913