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(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(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(std::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(std::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 = std::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 : public JITDylib::DefinitionGenerator { 247 public: 248 Expected<SymbolNameSet> tryToGenerate(JITDylib &, 249 const SymbolNameSet &) override { 250 return make_error<StringError>("BadGenerator", inconvertibleErrorCode()); 251 } 252 }; 253 254 JD.addGenerator(std::make_unique<BadGenerator>()); 255 256 EXPECT_THAT_ERROR(JD.lookupFlags({Foo}).takeError(), Failed<StringError>()) 257 << "Generator failure did not propagate through lookupFlags"; 258 259 EXPECT_THAT_ERROR( 260 ES.lookup(JITDylibSearchList({{&JD, false}}), SymbolNameSet({Foo})) 261 .takeError(), 262 Failed<StringError>()) 263 << "Generator failure did not propagate through lookup"; 264 } 265 266 TEST_F(CoreAPIsStandardTest, TestBasicAliases) { 267 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); 268 cantFail(JD.define(symbolAliases({{Baz, {Foo, JITSymbolFlags::Exported}}, 269 {Qux, {Bar, JITSymbolFlags::Weak}}}))); 270 cantFail(JD.define(absoluteSymbols({{Qux, QuxSym}}))); 271 272 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz, Qux}); 273 EXPECT_TRUE(!!Result) << "Unexpected lookup failure"; 274 EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\""; 275 EXPECT_EQ(Result->count(Qux), 1U) << "No result for \"qux\""; 276 EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress()) 277 << "\"Baz\"'s address should match \"Foo\"'s"; 278 EXPECT_EQ((*Result)[Qux].getAddress(), QuxSym.getAddress()) 279 << "The \"Qux\" alias should have been overriden"; 280 } 281 282 TEST_F(CoreAPIsStandardTest, TestChainedAliases) { 283 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 284 cantFail(JD.define(symbolAliases( 285 {{Baz, {Bar, BazSym.getFlags()}}, {Bar, {Foo, BarSym.getFlags()}}}))); 286 287 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar, Baz}); 288 EXPECT_TRUE(!!Result) << "Unexpected lookup failure"; 289 EXPECT_EQ(Result->count(Bar), 1U) << "No result for \"bar\""; 290 EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\""; 291 EXPECT_EQ((*Result)[Bar].getAddress(), FooSym.getAddress()) 292 << "\"Bar\"'s address should match \"Foo\"'s"; 293 EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress()) 294 << "\"Baz\"'s address should match \"Foo\"'s"; 295 } 296 297 TEST_F(CoreAPIsStandardTest, TestBasicReExports) { 298 // Test that the basic use case of re-exporting a single symbol from another 299 // JITDylib works. 300 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 301 302 auto &JD2 = ES.createJITDylib("JD2"); 303 304 cantFail(JD2.define(reexports(JD, {{Bar, {Foo, BarSym.getFlags()}}}))); 305 306 auto Result = cantFail(ES.lookup(JITDylibSearchList({{&JD2, false}}), Bar)); 307 EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) 308 << "Re-export Bar for symbol Foo should match FooSym's address"; 309 } 310 311 TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) { 312 // Test that re-exports do not materialize symbols that have not been queried 313 // for. 314 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 315 316 bool BarMaterialized = false; 317 auto BarMU = std::make_unique<SimpleMaterializationUnit>( 318 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 319 [&](MaterializationResponsibility R) { 320 BarMaterialized = true; 321 R.notifyResolved({{Bar, BarSym}}); 322 R.notifyEmitted(); 323 }); 324 325 cantFail(JD.define(BarMU)); 326 327 auto &JD2 = ES.createJITDylib("JD2"); 328 329 cantFail(JD2.define(reexports( 330 JD, {{Baz, {Foo, BazSym.getFlags()}}, {Qux, {Bar, QuxSym.getFlags()}}}))); 331 332 auto Result = cantFail(ES.lookup(JITDylibSearchList({{&JD2, false}}), Baz)); 333 EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) 334 << "Re-export Baz for symbol Foo should match FooSym's address"; 335 336 EXPECT_FALSE(BarMaterialized) << "Bar should not have been materialized"; 337 } 338 339 TEST_F(CoreAPIsStandardTest, TestReexportsGenerator) { 340 // Test that a re-exports generator can dynamically generate reexports. 341 342 auto &JD2 = ES.createJITDylib("JD2"); 343 cantFail(JD2.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); 344 345 auto Filter = [this](SymbolStringPtr Name) { return Name != Bar; }; 346 347 JD.addGenerator(std::make_unique<ReexportsGenerator>(JD2, false, Filter)); 348 349 auto Flags = cantFail(JD.lookupFlags({Foo, Bar, Baz})); 350 EXPECT_EQ(Flags.size(), 1U) << "Unexpected number of results"; 351 EXPECT_EQ(Flags[Foo], FooSym.getFlags()) << "Unexpected flags for Foo"; 352 353 auto Result = cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 354 355 EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) 356 << "Incorrect reexported symbol address"; 357 } 358 359 TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) { 360 Optional<MaterializationResponsibility> FooR; 361 auto FooMU = std::make_unique<SimpleMaterializationUnit>( 362 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 363 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); 364 365 cantFail(JD.define(FooMU)); 366 367 bool FooReady = false; 368 auto OnCompletion = [&](Expected<SymbolMap> Result) { 369 cantFail(std::move(Result)); 370 FooReady = true; 371 }; 372 373 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready, 374 OnCompletion, NoDependenciesToRegister); 375 376 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}}); 377 FooR->notifyResolved({{Foo, FooSym}}); 378 FooR->notifyEmitted(); 379 380 EXPECT_TRUE(FooReady) 381 << "Self-dependency prevented symbol from being marked ready"; 382 } 383 384 TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) { 385 // Test that a circular symbol dependency between three symbols in a JITDylib 386 // does not prevent any symbol from becoming 'ready' once all symbols are 387 // emitted. 388 389 // Create three MaterializationResponsibility objects: one for each of Foo, 390 // Bar and Baz. These are optional because MaterializationResponsibility 391 // does not have a default constructor). 392 Optional<MaterializationResponsibility> FooR; 393 Optional<MaterializationResponsibility> BarR; 394 Optional<MaterializationResponsibility> BazR; 395 396 // Create a MaterializationUnit for each symbol that moves the 397 // MaterializationResponsibility into one of the locals above. 398 auto FooMU = std::make_unique<SimpleMaterializationUnit>( 399 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 400 [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); 401 402 auto BarMU = std::make_unique<SimpleMaterializationUnit>( 403 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 404 [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); }); 405 406 auto BazMU = std::make_unique<SimpleMaterializationUnit>( 407 SymbolFlagsMap({{Baz, BazSym.getFlags()}}), 408 [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); }); 409 410 // Define the symbols. 411 cantFail(JD.define(FooMU)); 412 cantFail(JD.define(BarMU)); 413 cantFail(JD.define(BazMU)); 414 415 // Query each of the symbols to trigger materialization. 416 bool FooResolved = false; 417 bool FooReady = false; 418 419 auto OnFooResolution = [&](Expected<SymbolMap> Result) { 420 cantFail(std::move(Result)); 421 FooResolved = true; 422 }; 423 424 auto OnFooReady = [&](Expected<SymbolMap> Result) { 425 cantFail(std::move(Result)); 426 FooReady = true; 427 }; 428 429 // Issue lookups for Foo. Use NoDependenciesToRegister: We're going to add 430 // the dependencies manually below. 431 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Resolved, 432 std::move(OnFooResolution), NoDependenciesToRegister); 433 434 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready, 435 std::move(OnFooReady), NoDependenciesToRegister); 436 437 bool BarResolved = false; 438 bool BarReady = false; 439 auto OnBarResolution = [&](Expected<SymbolMap> Result) { 440 cantFail(std::move(Result)); 441 BarResolved = true; 442 }; 443 444 auto OnBarReady = [&](Expected<SymbolMap> Result) { 445 cantFail(std::move(Result)); 446 BarReady = true; 447 }; 448 449 ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Resolved, 450 std::move(OnBarResolution), NoDependenciesToRegister); 451 452 ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready, 453 std::move(OnBarReady), NoDependenciesToRegister); 454 455 bool BazResolved = false; 456 bool BazReady = false; 457 458 auto OnBazResolution = [&](Expected<SymbolMap> Result) { 459 cantFail(std::move(Result)); 460 BazResolved = true; 461 }; 462 463 auto OnBazReady = [&](Expected<SymbolMap> Result) { 464 cantFail(std::move(Result)); 465 BazReady = true; 466 }; 467 468 ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz}, SymbolState::Resolved, 469 std::move(OnBazResolution), NoDependenciesToRegister); 470 471 ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz}, SymbolState::Ready, 472 std::move(OnBazReady), NoDependenciesToRegister); 473 474 // Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo. 475 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}}); 476 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}}); 477 BazR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}}); 478 479 // Add self-dependencies for good measure. This tests that the implementation 480 // of addDependencies filters these out. 481 FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}}); 482 BarR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}}); 483 BazR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}}); 484 485 // Check that nothing has been resolved yet. 486 EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet"; 487 EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet"; 488 EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet"; 489 490 // Resolve the symbols (but do not emit them). 491 FooR->notifyResolved({{Foo, FooSym}}); 492 BarR->notifyResolved({{Bar, BarSym}}); 493 BazR->notifyResolved({{Baz, BazSym}}); 494 495 // Verify that the symbols have been resolved, but are not ready yet. 496 EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now"; 497 EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now"; 498 EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now"; 499 500 EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet"; 501 EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet"; 502 EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet"; 503 504 // Emit two of the symbols. 505 FooR->notifyEmitted(); 506 BarR->notifyEmitted(); 507 508 // Verify that nothing is ready until the circular dependence is resolved. 509 EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready"; 510 EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready"; 511 EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready"; 512 513 // Emit the last symbol. 514 BazR->notifyEmitted(); 515 516 // Verify that everything becomes ready once the circular dependence resolved. 517 EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now"; 518 EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now"; 519 EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now"; 520 } 521 522 TEST_F(CoreAPIsStandardTest, DropMaterializerWhenEmpty) { 523 bool DestructorRun = false; 524 525 JITSymbolFlags WeakExported(JITSymbolFlags::Exported); 526 WeakExported |= JITSymbolFlags::Weak; 527 528 auto MU = std::make_unique<SimpleMaterializationUnit>( 529 SymbolFlagsMap({{Foo, WeakExported}, {Bar, WeakExported}}), 530 [](MaterializationResponsibility R) { 531 llvm_unreachable("Unexpected call to materialize"); 532 }, 533 [&](const JITDylib &JD, SymbolStringPtr Name) { 534 EXPECT_TRUE(Name == Foo || Name == Bar) 535 << "Discard of unexpected symbol?"; 536 }, 537 [&]() { DestructorRun = true; }); 538 539 cantFail(JD.define(MU)); 540 541 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 542 543 EXPECT_FALSE(DestructorRun) 544 << "MaterializationUnit should not have been destroyed yet"; 545 546 cantFail(JD.define(absoluteSymbols({{Bar, BarSym}}))); 547 548 EXPECT_TRUE(DestructorRun) 549 << "MaterializationUnit should have been destroyed"; 550 } 551 552 TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) { 553 bool FooMaterialized = false; 554 bool BarDiscarded = false; 555 556 JITSymbolFlags WeakExported(JITSymbolFlags::Exported); 557 WeakExported |= JITSymbolFlags::Weak; 558 559 auto MU = std::make_unique<SimpleMaterializationUnit>( 560 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}), 561 [&](MaterializationResponsibility R) { 562 assert(BarDiscarded && "Bar should have been discarded by this point"); 563 R.notifyResolved(SymbolMap({{Foo, FooSym}})); 564 R.notifyEmitted(); 565 FooMaterialized = true; 566 }, 567 [&](const JITDylib &JD, SymbolStringPtr Name) { 568 EXPECT_EQ(Name, Bar) << "Expected Name to be Bar"; 569 BarDiscarded = true; 570 }); 571 572 cantFail(JD.define(MU)); 573 cantFail(JD.define(absoluteSymbols({{Bar, BarSym}}))); 574 575 SymbolNameSet Names({Foo}); 576 577 bool OnCompletionRun = false; 578 579 auto OnCompletion = [&](Expected<SymbolMap> Result) { 580 EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; 581 auto I = Result->find(Foo); 582 EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; 583 EXPECT_EQ(I->second.getAddress(), FooSym.getAddress()) 584 << "Resolution returned incorrect result"; 585 OnCompletionRun = true; 586 }; 587 588 ES.lookup(JITDylibSearchList({{&JD, false}}), Names, SymbolState::Ready, 589 std::move(OnCompletion), NoDependenciesToRegister); 590 591 EXPECT_TRUE(FooMaterialized) << "Foo was not materialized"; 592 EXPECT_TRUE(BarDiscarded) << "Bar was not discarded"; 593 EXPECT_TRUE(OnCompletionRun) << "OnResolutionCallback was not run"; 594 } 595 596 TEST_F(CoreAPIsStandardTest, TestBasicWeakSymbolMaterialization) { 597 // Test that weak symbols are materialized correctly when we look them up. 598 BarSym.setFlags(BarSym.getFlags() | JITSymbolFlags::Weak); 599 600 bool BarMaterialized = false; 601 auto MU1 = std::make_unique<SimpleMaterializationUnit>( 602 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 603 [&](MaterializationResponsibility R) { 604 R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})), R.notifyEmitted(); 605 BarMaterialized = true; 606 }); 607 608 bool DuplicateBarDiscarded = false; 609 auto MU2 = std::make_unique<SimpleMaterializationUnit>( 610 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 611 [&](MaterializationResponsibility R) { 612 ADD_FAILURE() << "Attempt to materialize Bar from the wrong unit"; 613 R.failMaterialization(); 614 }, 615 [&](const JITDylib &JD, SymbolStringPtr Name) { 616 EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded"; 617 DuplicateBarDiscarded = true; 618 }); 619 620 cantFail(JD.define(MU1)); 621 cantFail(JD.define(MU2)); 622 623 bool OnCompletionRun = false; 624 625 auto OnCompletion = [&](Expected<SymbolMap> Result) { 626 cantFail(std::move(Result)); 627 OnCompletionRun = true; 628 }; 629 630 ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready, 631 std::move(OnCompletion), NoDependenciesToRegister); 632 633 EXPECT_TRUE(OnCompletionRun) << "OnCompletion not run"; 634 EXPECT_TRUE(BarMaterialized) << "Bar was not materialized at all"; 635 EXPECT_TRUE(DuplicateBarDiscarded) 636 << "Duplicate bar definition not discarded"; 637 } 638 639 TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) { 640 bool ExpectNoMoreMaterialization = false; 641 ES.setDispatchMaterialization( 642 [&](JITDylib &JD, std::unique_ptr<MaterializationUnit> MU) { 643 if (ExpectNoMoreMaterialization) 644 ADD_FAILURE() << "Unexpected materialization"; 645 MU->doMaterialize(JD); 646 }); 647 648 auto MU = std::make_unique<SimpleMaterializationUnit>( 649 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 650 [&](MaterializationResponsibility R) { 651 cantFail( 652 R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}}))); 653 R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); 654 R.notifyEmitted(); 655 }); 656 657 cantFail(JD.define(MU)); 658 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 659 660 // Assert that materialization is complete by now. 661 ExpectNoMoreMaterialization = true; 662 663 // Look up bar to verify that no further materialization happens. 664 auto BarResult = cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Bar)); 665 EXPECT_EQ(BarResult.getAddress(), BarSym.getAddress()) 666 << "Expected Bar == BarSym"; 667 } 668 669 TEST_F(CoreAPIsStandardTest, GeneratorTest) { 670 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 671 672 class TestGenerator : public JITDylib::DefinitionGenerator { 673 public: 674 TestGenerator(SymbolMap Symbols) : Symbols(std::move(Symbols)) {} 675 Expected<SymbolNameSet> tryToGenerate(JITDylib &JD, 676 const SymbolNameSet &Names) { 677 SymbolMap NewDefs; 678 SymbolNameSet NewNames; 679 680 for (auto &Name : Names) { 681 if (Symbols.count(Name)) { 682 NewDefs[Name] = Symbols[Name]; 683 NewNames.insert(Name); 684 } 685 } 686 cantFail(JD.define(absoluteSymbols(std::move(NewDefs)))); 687 return NewNames; 688 }; 689 690 private: 691 SymbolMap Symbols; 692 }; 693 694 JD.addGenerator(std::make_unique<TestGenerator>(SymbolMap({{Bar, BarSym}}))); 695 696 auto Result = 697 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo, Bar})); 698 699 EXPECT_EQ(Result.count(Bar), 1U) << "Expected to find fallback def for 'bar'"; 700 EXPECT_EQ(Result[Bar].getAddress(), BarSym.getAddress()) 701 << "Expected fallback def for Bar to be equal to BarSym"; 702 } 703 704 TEST_F(CoreAPIsStandardTest, FailResolution) { 705 auto MU = std::make_unique<SimpleMaterializationUnit>( 706 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported | JITSymbolFlags::Weak}, 707 {Bar, JITSymbolFlags::Exported | JITSymbolFlags::Weak}}), 708 [&](MaterializationResponsibility R) { 709 R.failMaterialization(); 710 }); 711 712 cantFail(JD.define(MU)); 713 714 SymbolNameSet Names({Foo, Bar}); 715 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), Names); 716 717 EXPECT_FALSE(!!Result) << "Expected failure"; 718 if (!Result) { 719 handleAllErrors(Result.takeError(), 720 [&](FailedToMaterialize &F) { 721 EXPECT_EQ(F.getSymbols(), Names) 722 << "Expected to fail on symbols in Names"; 723 }, 724 [](ErrorInfoBase &EIB) { 725 std::string ErrMsg; 726 { 727 raw_string_ostream ErrOut(ErrMsg); 728 EIB.log(ErrOut); 729 } 730 ADD_FAILURE() 731 << "Expected a FailedToResolve error. Got:\n" 732 << ErrMsg; 733 }); 734 } 735 } 736 737 TEST_F(CoreAPIsStandardTest, FailEmissionEarly) { 738 739 cantFail(JD.define(absoluteSymbols({{Baz, BazSym}}))); 740 741 auto MU = std::make_unique<SimpleMaterializationUnit>( 742 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 743 [&](MaterializationResponsibility R) { 744 R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); 745 746 ES.lookup( 747 JITDylibSearchList({{&JD, false}}), SymbolNameSet({Baz}), 748 SymbolState::Resolved, 749 [&R](Expected<SymbolMap> Result) { 750 // Called when "baz" is resolved. We don't actually depend 751 // on or care about baz, but use it to trigger failure of 752 // this materialization before Baz has been finalized in 753 // order to test that error propagation is correct in this 754 // scenario. 755 cantFail(std::move(Result)); 756 R.failMaterialization(); 757 }, 758 [&](const SymbolDependenceMap &Deps) { 759 R.addDependenciesForAll(Deps); 760 }); 761 }); 762 763 cantFail(JD.define(MU)); 764 765 SymbolNameSet Names({Foo, Bar}); 766 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), Names); 767 768 EXPECT_THAT_EXPECTED(std::move(Result), Failed()) 769 << "Unexpected success while trying to test error propagation"; 770 } 771 772 TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) { 773 auto MU = std::make_unique<SimpleMaterializationUnit>( 774 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), 775 [&](MaterializationResponsibility R) { 776 R.notifyResolved({{Foo, FooSym}}); 777 R.notifyEmitted(); 778 }); 779 780 cantFail(JD.define(MU)); 781 782 auto FooLookupResult = 783 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 784 785 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) 786 << "lookup returned an incorrect address"; 787 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) 788 << "lookup returned incorrect flags"; 789 } 790 791 TEST_F(CoreAPIsStandardTest, TestLookupWithThreadedMaterialization) { 792 #if LLVM_ENABLE_THREADS 793 794 std::thread MaterializationThread; 795 ES.setDispatchMaterialization( 796 [&](JITDylib &JD, std::unique_ptr<MaterializationUnit> MU) { 797 auto SharedMU = std::shared_ptr<MaterializationUnit>(std::move(MU)); 798 MaterializationThread = 799 std::thread([SharedMU, &JD]() { SharedMU->doMaterialize(JD); }); 800 }); 801 802 cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); 803 804 auto FooLookupResult = 805 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 806 807 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) 808 << "lookup returned an incorrect address"; 809 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) 810 << "lookup returned incorrect flags"; 811 MaterializationThread.join(); 812 #endif 813 } 814 815 TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) { 816 // Test that GetRequestedSymbols returns the set of symbols that currently 817 // have pending queries, and test that MaterializationResponsibility's 818 // replace method can be used to return definitions to the JITDylib in a new 819 // MaterializationUnit. 820 SymbolNameSet Names({Foo, Bar}); 821 822 bool FooMaterialized = false; 823 bool BarMaterialized = false; 824 825 auto MU = std::make_unique<SimpleMaterializationUnit>( 826 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 827 [&](MaterializationResponsibility R) { 828 auto Requested = R.getRequestedSymbols(); 829 EXPECT_EQ(Requested.size(), 1U) << "Expected one symbol requested"; 830 EXPECT_EQ(*Requested.begin(), Foo) << "Expected \"Foo\" requested"; 831 832 auto NewMU = std::make_unique<SimpleMaterializationUnit>( 833 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 834 [&](MaterializationResponsibility R2) { 835 R2.notifyResolved(SymbolMap({{Bar, BarSym}})); 836 R2.notifyEmitted(); 837 BarMaterialized = true; 838 }); 839 840 R.replace(std::move(NewMU)); 841 842 R.notifyResolved(SymbolMap({{Foo, FooSym}})); 843 R.notifyEmitted(); 844 845 FooMaterialized = true; 846 }); 847 848 cantFail(JD.define(MU)); 849 850 EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet"; 851 EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet"; 852 853 auto FooSymResult = 854 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Foo)); 855 EXPECT_EQ(FooSymResult.getAddress(), FooSym.getAddress()) 856 << "Address mismatch for Foo"; 857 858 EXPECT_TRUE(FooMaterialized) << "Foo should be materialized now"; 859 EXPECT_FALSE(BarMaterialized) << "Bar still should not be materialized"; 860 861 auto BarSymResult = 862 cantFail(ES.lookup(JITDylibSearchList({{&JD, false}}), Bar)); 863 EXPECT_EQ(BarSymResult.getAddress(), BarSym.getAddress()) 864 << "Address mismatch for Bar"; 865 EXPECT_TRUE(BarMaterialized) << "Bar should be materialized now"; 866 } 867 868 TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) { 869 auto MU = std::make_unique<SimpleMaterializationUnit>( 870 SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), 871 [&](MaterializationResponsibility R) { 872 auto R2 = R.delegate({Bar}); 873 874 R.notifyResolved({{Foo, FooSym}}); 875 R.notifyEmitted(); 876 R2.notifyResolved({{Bar, BarSym}}); 877 R2.notifyEmitted(); 878 }); 879 880 cantFail(JD.define(MU)); 881 882 auto Result = ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo, Bar}); 883 884 EXPECT_TRUE(!!Result) << "Result should be a success value"; 885 EXPECT_EQ(Result->count(Foo), 1U) << "\"Foo\" entry missing"; 886 EXPECT_EQ(Result->count(Bar), 1U) << "\"Bar\" entry missing"; 887 EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress()) 888 << "Address mismatch for \"Foo\""; 889 EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress()) 890 << "Address mismatch for \"Bar\""; 891 } 892 893 TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) { 894 // Confirm that once a weak definition is selected for materialization it is 895 // treated as strong. 896 JITSymbolFlags WeakExported = JITSymbolFlags::Exported; 897 WeakExported &= JITSymbolFlags::Weak; 898 899 std::unique_ptr<MaterializationResponsibility> FooResponsibility; 900 auto MU = std::make_unique<SimpleMaterializationUnit>( 901 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 902 [&](MaterializationResponsibility R) { 903 FooResponsibility = 904 std::make_unique<MaterializationResponsibility>(std::move(R)); 905 }); 906 907 cantFail(JD.define(MU)); 908 auto OnCompletion = [](Expected<SymbolMap> Result) { 909 cantFail(std::move(Result)); 910 }; 911 912 ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready, 913 std::move(OnCompletion), NoDependenciesToRegister); 914 915 auto MU2 = std::make_unique<SimpleMaterializationUnit>( 916 SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), 917 [](MaterializationResponsibility R) { 918 llvm_unreachable("This unit should never be materialized"); 919 }); 920 921 auto Err = JD.define(MU2); 922 EXPECT_TRUE(!!Err) << "Expected failure value"; 923 EXPECT_TRUE(Err.isA<DuplicateDefinition>()) 924 << "Expected a duplicate definition error"; 925 consumeError(std::move(Err)); 926 927 FooResponsibility->notifyResolved(SymbolMap({{Foo, FooSym}})); 928 FooResponsibility->notifyEmitted(); 929 } 930 931 } // namespace 932