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 28 SimpleMaterializationUnit(GetSymbolsFunction GetSymbols, 29 MaterializeFunction Materialize, 30 DiscardFunction Discard) 31 : GetSymbols(std::move(GetSymbols)), Materialize(std::move(Materialize)), 32 Discard(std::move(Discard)) {} 33 34 SymbolFlagsMap getSymbols() override { return GetSymbols(); } 35 36 Error materialize(VSO &V) override { return Materialize(V); } 37 38 void discard(VSO &V, SymbolStringPtr Name) override { 39 Discard(V, std::move(Name)); 40 } 41 42 private: 43 GetSymbolsFunction GetSymbols; 44 MaterializeFunction Materialize; 45 DiscardFunction Discard; 46 }; 47 48 TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) { 49 SymbolStringPool SP; 50 auto Foo = SP.intern("foo"); 51 constexpr JITTargetAddress FakeAddr = 0xdeadbeef; 52 SymbolNameSet Names({Foo}); 53 54 bool OnResolutionRun = false; 55 bool OnReadyRun = false; 56 auto OnResolution = [&](Expected<SymbolMap> Result) { 57 EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; 58 auto I = Result->find(Foo); 59 EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; 60 EXPECT_EQ(I->second.getAddress(), FakeAddr) 61 << "Resolution returned incorrect result"; 62 OnResolutionRun = true; 63 }; 64 auto OnReady = [&](Error Err) { 65 cantFail(std::move(Err)); 66 OnReadyRun = true; 67 }; 68 69 AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); 70 71 Q.setDefinition(Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported)); 72 73 EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; 74 EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; 75 } 76 77 TEST(CoreAPIsTest, AsynchronousSymbolQueryResolutionErrorOnly) { 78 SymbolStringPool SP; 79 auto Foo = SP.intern("foo"); 80 SymbolNameSet Names({Foo}); 81 82 bool OnResolutionRun = false; 83 bool OnReadyRun = false; 84 85 auto OnResolution = [&](Expected<SymbolMap> Result) { 86 EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success"; 87 auto Msg = toString(Result.takeError()); 88 EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result"; 89 OnResolutionRun = true; 90 }; 91 auto OnReady = [&](Error Err) { 92 cantFail(std::move(Err)); 93 OnReadyRun = true; 94 }; 95 96 AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); 97 98 Q.setFailed(make_error<StringError>("xyz", inconvertibleErrorCode())); 99 100 EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; 101 EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; 102 } 103 104 TEST(CoreAPIsTest, SimpleAsynchronousSymbolQueryAgainstVSO) { 105 SymbolStringPool SP; 106 auto Foo = SP.intern("foo"); 107 constexpr JITTargetAddress FakeAddr = 0xdeadbeef; 108 SymbolNameSet Names({Foo}); 109 110 bool OnResolutionRun = false; 111 bool OnReadyRun = false; 112 113 auto OnResolution = [&](Expected<SymbolMap> Result) { 114 EXPECT_TRUE(!!Result) << "Query unexpectedly returned error"; 115 auto I = Result->find(Foo); 116 EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; 117 EXPECT_EQ(I->second.getAddress(), FakeAddr) 118 << "Resolution returned incorrect result"; 119 OnResolutionRun = true; 120 }; 121 122 auto OnReady = [&](Error Err) { 123 cantFail(std::move(Err)); 124 OnReadyRun = true; 125 }; 126 127 auto Q = 128 std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady); 129 VSO V; 130 131 SymbolMap Defs; 132 Defs[Foo] = JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported); 133 cantFail(V.define(std::move(Defs))); 134 V.lookup(Q, Names); 135 136 EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; 137 EXPECT_TRUE(OnReadyRun) << "OnReady was not run"; 138 } 139 140 TEST(CoreAPIsTest, LookupFlagsTest) { 141 142 // Test that lookupFlags works on a predefined symbol, and does not trigger 143 // materialization of a lazy symbol. 144 145 SymbolStringPool SP; 146 auto Foo = SP.intern("foo"); 147 auto Bar = SP.intern("bar"); 148 auto Baz = SP.intern("baz"); 149 150 JITSymbolFlags FooFlags = JITSymbolFlags::Exported; 151 JITSymbolFlags BarFlags = static_cast<JITSymbolFlags::FlagNames>( 152 JITSymbolFlags::Exported | JITSymbolFlags::Weak); 153 154 VSO V; 155 156 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 157 [=]() { 158 return SymbolFlagsMap({{Bar, BarFlags}}); 159 }, 160 [](VSO &V) -> Error { 161 llvm_unreachable("Symbol materialized on flags lookup"); 162 }, 163 [](VSO &V, SymbolStringPtr Name) -> Error { 164 llvm_unreachable("Symbol finalized on flags lookup"); 165 }); 166 167 SymbolMap InitialDefs; 168 InitialDefs[Foo] = JITEvaluatedSymbol(0xdeadbeef, FooFlags); 169 cantFail(V.define(std::move(InitialDefs))); 170 171 cantFail(V.defineLazy(std::move(MU))); 172 173 SymbolNameSet Names({Foo, Bar, Baz}); 174 175 SymbolFlagsMap SymbolFlags; 176 auto SymbolsNotFound = V.lookupFlags(SymbolFlags, Names); 177 178 EXPECT_EQ(SymbolsNotFound.size(), 1U) << "Expected one not-found symbol"; 179 EXPECT_EQ(SymbolsNotFound.count(Baz), 1U) << "Expected Baz to be not-found"; 180 EXPECT_EQ(SymbolFlags.size(), 2U) 181 << "Returned symbol flags contains unexpected results"; 182 EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo"; 183 EXPECT_EQ(SymbolFlags[Foo], FooFlags) << "Incorrect flags returned for Foo"; 184 EXPECT_EQ(SymbolFlags.count(Bar), 1U) 185 << "Missing lookupFlags result for Bar"; 186 EXPECT_EQ(SymbolFlags[Bar], BarFlags) << "Incorrect flags returned for Bar"; 187 } 188 189 TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) { 190 191 constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; 192 constexpr JITTargetAddress FakeBarAddr = 0xcafef00d; 193 194 SymbolStringPool SP; 195 auto Foo = SP.intern("foo"); 196 auto Bar = SP.intern("bar"); 197 198 bool FooMaterialized = false; 199 bool BarDiscarded = false; 200 201 VSO V; 202 203 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 204 [=]() { 205 return SymbolFlagsMap( 206 {{Foo, JITSymbolFlags::Exported}, 207 {Bar, static_cast<JITSymbolFlags::FlagNames>( 208 JITSymbolFlags::Exported | JITSymbolFlags::Weak)}}); 209 }, 210 [&](VSO &V) { 211 assert(BarDiscarded && "Bar should have been discarded by this point"); 212 SymbolMap SymbolsToResolve; 213 SymbolsToResolve[Foo] = 214 JITEvaluatedSymbol(FakeFooAddr, JITSymbolFlags::Exported); 215 V.resolve(std::move(SymbolsToResolve)); 216 SymbolNameSet SymbolsToFinalize; 217 SymbolsToFinalize.insert(Foo); 218 V.finalize(SymbolsToFinalize); 219 FooMaterialized = true; 220 return Error::success(); 221 }, 222 [&](VSO &V, SymbolStringPtr Name) { 223 EXPECT_EQ(Name, Bar) << "Expected Name to be Bar"; 224 BarDiscarded = true; 225 }); 226 227 cantFail(V.defineLazy(std::move(MU))); 228 229 SymbolMap BarOverride; 230 BarOverride[Bar] = JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported); 231 cantFail(V.define(std::move(BarOverride))); 232 233 SymbolNameSet Names({Foo}); 234 235 bool OnResolutionRun = false; 236 bool OnReadyRun = false; 237 238 auto OnResolution = [&](Expected<SymbolMap> Result) { 239 EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; 240 auto I = Result->find(Foo); 241 EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; 242 EXPECT_EQ(I->second.getAddress(), FakeFooAddr) 243 << "Resolution returned incorrect result"; 244 OnResolutionRun = true; 245 }; 246 247 auto OnReady = [&](Error Err) { 248 cantFail(std::move(Err)); 249 OnReadyRun = true; 250 }; 251 252 auto Q = 253 std::make_shared<AsynchronousSymbolQuery>(Names, OnResolution, OnReady); 254 255 auto LR = V.lookup(std::move(Q), Names); 256 257 for (auto &SWKV : LR.MaterializationUnits) 258 cantFail(SWKV->materialize(V)); 259 260 EXPECT_TRUE(LR.UnresolvedSymbols.empty()) << "Could not find Foo in dylib"; 261 EXPECT_TRUE(FooMaterialized) << "Foo was not materialized"; 262 EXPECT_TRUE(BarDiscarded) << "Bar was not discarded"; 263 EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; 264 EXPECT_TRUE(OnReadyRun) << "OnReady was not run"; 265 } 266 267 TEST(CoreAPIsTest, TestLambdaSymbolResolver) { 268 JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported); 269 JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported); 270 271 SymbolStringPool SP; 272 auto Foo = SP.intern("foo"); 273 auto Bar = SP.intern("bar"); 274 auto Baz = SP.intern("baz"); 275 276 VSO V; 277 cantFail(V.define({{Foo, FooSym}, {Bar, BarSym}})); 278 279 auto Resolver = createSymbolResolver( 280 [&](SymbolFlagsMap &SymbolFlags, const SymbolNameSet &Symbols) { 281 return V.lookupFlags(SymbolFlags, Symbols); 282 }, 283 [&](std::shared_ptr<AsynchronousSymbolQuery> Q, SymbolNameSet Symbols) { 284 auto LR = V.lookup(std::move(Q), Symbols); 285 assert(LR.MaterializationUnits.empty() && 286 "Test generated unexpected materialization work?"); 287 return std::move(LR.UnresolvedSymbols); 288 }); 289 290 SymbolNameSet Symbols({Foo, Bar, Baz}); 291 292 SymbolFlagsMap SymbolFlags; 293 SymbolNameSet SymbolsNotFound = Resolver->lookupFlags(SymbolFlags, Symbols); 294 295 EXPECT_EQ(SymbolFlags.size(), 2U) 296 << "lookupFlags returned the wrong number of results"; 297 EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for foo"; 298 EXPECT_EQ(SymbolFlags.count(Bar), 1U) << "Missing lookupFlags result for bar"; 299 EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags()) 300 << "Incorrect lookupFlags result for Foo"; 301 EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags()) 302 << "Incorrect lookupFlags result for Bar"; 303 EXPECT_EQ(SymbolsNotFound.size(), 1U) 304 << "Expected one symbol not found in lookupFlags"; 305 EXPECT_EQ(SymbolsNotFound.count(Baz), 1U) 306 << "Expected baz not to be found in lookupFlags"; 307 308 bool OnResolvedRun = false; 309 310 auto OnResolved = [&](Expected<SymbolMap> Result) { 311 OnResolvedRun = true; 312 EXPECT_TRUE(!!Result) << "Unexpected error"; 313 EXPECT_EQ(Result->size(), 2U) << "Unexpected number of resolved symbols"; 314 EXPECT_EQ(Result->count(Foo), 1U) << "Missing lookup result for foo"; 315 EXPECT_EQ(Result->count(Bar), 1U) << "Missing lookup result for bar"; 316 EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress()) 317 << "Incorrect address for foo"; 318 EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress()) 319 << "Incorrect address for bar"; 320 }; 321 auto OnReady = [&](Error Err) { 322 EXPECT_FALSE(!!Err) << "Finalization should never fail in this test"; 323 }; 324 325 auto Q = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Foo, Bar}), 326 OnResolved, OnReady); 327 auto Unresolved = Resolver->lookup(std::move(Q), Symbols); 328 329 EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol"; 330 EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to not be resolved"; 331 EXPECT_TRUE(OnResolvedRun) << "OnResolved was never run"; 332 } 333 334 TEST(CoreAPIsTest, TestLookupWithUnthreadedMaterialization) { 335 constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; 336 JITEvaluatedSymbol FooSym(FakeFooAddr, JITSymbolFlags::Exported); 337 338 SymbolStringPool SSP; 339 auto Foo = SSP.intern("foo"); 340 341 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 342 [=]() { 343 return SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}); 344 }, 345 [&](VSO &V) -> Error { 346 V.resolve({{Foo, FooSym}}); 347 V.finalize({Foo}); 348 return Error::success(); 349 }, 350 [](VSO &V, SymbolStringPtr Name) -> Error { 351 llvm_unreachable("Not expecting finalization"); 352 }); 353 354 VSO V; 355 356 cantFail(V.defineLazy(std::move(MU))); 357 358 ExecutionSession ES(SSP); 359 auto FooLookupResult = 360 cantFail(lookup({&V}, Foo, MaterializeOnCurrentThread(ES))); 361 362 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) 363 << "lookup returned an incorrect address"; 364 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) 365 << "lookup returned incorrect flags"; 366 } 367 368 TEST(CoreAPIsTest, TestLookupWithThreadedMaterialization) { 369 #if LLVM_ENABLE_THREADS 370 constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; 371 JITEvaluatedSymbol FooSym(FakeFooAddr, JITSymbolFlags::Exported); 372 373 SymbolStringPool SSP; 374 auto Foo = SSP.intern("foo"); 375 376 auto MU = llvm::make_unique<SimpleMaterializationUnit>( 377 [=]() { 378 return SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}); 379 }, 380 [&](VSO &V) -> Error { 381 V.resolve({{Foo, FooSym}}); 382 V.finalize({Foo}); 383 return Error::success(); 384 }, 385 [](VSO &V, SymbolStringPtr Name) -> Error { 386 llvm_unreachable("Not expecting finalization"); 387 }); 388 389 VSO V; 390 391 cantFail(V.defineLazy(std::move(MU))); 392 393 ExecutionSession ES(SSP); 394 395 std::thread MaterializationThread; 396 auto MaterializeOnNewThread = [&](VSO &V, 397 std::unique_ptr<MaterializationUnit> MU) { 398 // FIXME: Use move capture once we move to C++14. 399 std::shared_ptr<MaterializationUnit> SharedMU = std::move(MU); 400 MaterializationThread = std::thread([&ES, &V, SharedMU]() { 401 if (auto Err = SharedMU->materialize(V)) 402 ES.reportError(std::move(Err)); 403 }); 404 }; 405 406 auto FooLookupResult = 407 cantFail(lookup({&V}, Foo, MaterializeOnNewThread)); 408 409 EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) 410 << "lookup returned an incorrect address"; 411 EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) 412 << "lookup returned incorrect flags"; 413 MaterializationThread.join(); 414 #endif 415 } 416 417 } // namespace 418