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