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