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