//===----------- CoreAPIsTest.cpp - Unit tests for Core ORC APIs ----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "OrcTestCommon.h" #include "llvm/Config/llvm-config.h" #include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/OrcError.h" #include #include using namespace llvm; using namespace llvm::orc; class CoreAPIsStandardTest : public CoreAPIsBasedStandardTest {}; namespace { class SimpleMaterializationUnit : public MaterializationUnit { public: using MaterializeFunction = std::function; using DiscardFunction = std::function; using DestructorFunction = std::function; SimpleMaterializationUnit( SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize, DiscardFunction Discard = DiscardFunction(), DestructorFunction Destructor = DestructorFunction()) : MaterializationUnit(std::move(SymbolFlags)), Materialize(std::move(Materialize)), Discard(std::move(Discard)), Destructor(std::move(Destructor)) {} ~SimpleMaterializationUnit() override { if (Destructor) Destructor(); } void materialize(MaterializationResponsibility R) override { Materialize(std::move(R)); } void discard(const JITDylib &JD, SymbolStringPtr Name) override { if (Discard) Discard(JD, std::move(Name)); else llvm_unreachable("Discard not supported"); } private: MaterializeFunction Materialize; DiscardFunction Discard; DestructorFunction Destructor; }; TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) { bool OnResolutionRun = false; bool OnReadyRun = false; auto OnResolution = [&](Expected Result) { EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; auto &Resolved = *Result; auto I = Resolved.find(Foo); EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition"; EXPECT_EQ(I->second.getAddress(), FooAddr) << "Resolution returned incorrect result"; OnResolutionRun = true; }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); OnReadyRun = true; }; std::shared_ptr FooMR; cantFail(JD.define(llvm::make_unique( SymbolFlagsMap({{Foo, FooSym.getFlags()}}), [&](MaterializationResponsibility R) { FooMR = std::make_shared(std::move(R)); }))); ES.lookup({&JD}, {Foo}, OnResolution, OnReady, NoDependenciesToRegister); EXPECT_FALSE(OnResolutionRun) << "Should not have been resolved yet"; EXPECT_FALSE(OnReadyRun) << "Should not have been marked ready yet"; FooMR->resolve({{Foo, FooSym}}); EXPECT_TRUE(OnResolutionRun) << "Should have been resolved"; EXPECT_FALSE(OnReadyRun) << "Should not have been marked ready yet"; FooMR->emit(); EXPECT_TRUE(OnReadyRun) << "Should have been marked ready"; } TEST_F(CoreAPIsStandardTest, ExecutionSessionFailQuery) { bool OnResolutionRun = false; bool OnReadyRun = false; auto OnResolution = [&](Expected Result) { EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success"; auto Msg = toString(Result.takeError()); EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result"; OnResolutionRun = true; }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); OnReadyRun = true; }; AsynchronousSymbolQuery Q(SymbolNameSet({Foo}), OnResolution, OnReady); ES.legacyFailQuery(Q, make_error("xyz", inconvertibleErrorCode())); EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; } TEST_F(CoreAPIsStandardTest, EmptyLookup) { bool OnResolvedRun = false; bool OnReadyRun = false; auto OnResolution = [&](Expected Result) { cantFail(std::move(Result)); OnResolvedRun = true; }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); OnReadyRun = true; }; ES.lookup({&JD}, {}, OnResolution, OnReady, NoDependenciesToRegister); EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run for empty query"; EXPECT_TRUE(OnReadyRun) << "OnReady was not run for empty query"; } TEST_F(CoreAPIsStandardTest, ChainedJITDylibLookup) { cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); auto &JD2 = ES.createJITDylib("JD2"); bool OnResolvedRun = false; bool OnReadyRun = false; auto Q = std::make_shared( SymbolNameSet({Foo}), [&](Expected Result) { cantFail(std::move(Result)); OnResolvedRun = true; }, [&](Error Err) { cantFail(std::move(Err)); OnReadyRun = true; }); JD2.legacyLookup(Q, JD.legacyLookup(Q, {Foo})); EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run for empty query"; EXPECT_TRUE(OnReadyRun) << "OnReady was not run for empty query"; } TEST_F(CoreAPIsStandardTest, LookupFlagsTest) { // Test that lookupFlags works on a predefined symbol, and does not trigger // materialization of a lazy symbol. Make the lazy symbol weak to test that // the weak flag is propagated correctly. BarSym.setFlags(static_cast( JITSymbolFlags::Exported | JITSymbolFlags::Weak)); auto MU = llvm::make_unique( SymbolFlagsMap({{Bar, BarSym.getFlags()}}), [](MaterializationResponsibility R) { llvm_unreachable("Symbol materialized on flags lookup"); }); cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); cantFail(JD.define(std::move(MU))); SymbolNameSet Names({Foo, Bar, Baz}); auto SymbolFlags = JD.lookupFlags(Names); EXPECT_EQ(SymbolFlags.size(), 2U) << "Returned symbol flags contains unexpected results"; EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for Foo"; EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags()) << "Incorrect flags returned for Foo"; EXPECT_EQ(SymbolFlags.count(Bar), 1U) << "Missing lookupFlags result for Bar"; EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags()) << "Incorrect flags returned for Bar"; } TEST_F(CoreAPIsStandardTest, TestBasicAliases) { cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); cantFail(JD.define(symbolAliases({{Baz, {Foo, JITSymbolFlags::Exported}}, {Qux, {Bar, JITSymbolFlags::Weak}}}))); cantFail(JD.define(absoluteSymbols({{Qux, QuxSym}}))); auto Result = lookup({&JD}, {Baz, Qux}); EXPECT_TRUE(!!Result) << "Unexpected lookup failure"; EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\""; EXPECT_EQ(Result->count(Qux), 1U) << "No result for \"qux\""; EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress()) << "\"Baz\"'s address should match \"Foo\"'s"; EXPECT_EQ((*Result)[Qux].getAddress(), QuxSym.getAddress()) << "The \"Qux\" alias should have been overriden"; } TEST_F(CoreAPIsStandardTest, TestChainedAliases) { cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); cantFail(JD.define(symbolAliases( {{Baz, {Bar, BazSym.getFlags()}}, {Bar, {Foo, BarSym.getFlags()}}}))); auto Result = lookup({&JD}, {Bar, Baz}); EXPECT_TRUE(!!Result) << "Unexpected lookup failure"; EXPECT_EQ(Result->count(Bar), 1U) << "No result for \"bar\""; EXPECT_EQ(Result->count(Baz), 1U) << "No result for \"baz\""; EXPECT_EQ((*Result)[Bar].getAddress(), FooSym.getAddress()) << "\"Bar\"'s address should match \"Foo\"'s"; EXPECT_EQ((*Result)[Baz].getAddress(), FooSym.getAddress()) << "\"Baz\"'s address should match \"Foo\"'s"; } TEST_F(CoreAPIsStandardTest, TestBasicReExports) { // Test that the basic use case of re-exporting a single symbol from another // JITDylib works. cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); auto &JD2 = ES.createJITDylib("JD2"); cantFail(JD2.define(reexports(JD, {{Bar, {Foo, BarSym.getFlags()}}}))); auto Result = cantFail(lookup({&JD2}, Bar)); EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) << "Re-export Bar for symbol Foo should match FooSym's address"; } TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) { // Test that re-exports do not materialize symbols that have not been queried // for. cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); bool BarMaterialized = false; auto BarMU = llvm::make_unique( SymbolFlagsMap({{Bar, BarSym.getFlags()}}), [&](MaterializationResponsibility R) { BarMaterialized = true; R.resolve({{Bar, BarSym}}); R.emit(); }); cantFail(JD.define(BarMU)); auto &JD2 = ES.createJITDylib("JD2"); cantFail(JD2.define(reexports( JD, {{Baz, {Foo, BazSym.getFlags()}}, {Qux, {Bar, QuxSym.getFlags()}}}))); auto Result = cantFail(lookup({&JD2}, Baz)); EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) << "Re-export Baz for symbol Foo should match FooSym's address"; EXPECT_FALSE(BarMaterialized) << "Bar should not have been materialized"; } TEST_F(CoreAPIsStandardTest, TestReexportsFallbackGenerator) { // Test that a re-exports fallback generator can dynamically generate // reexports. auto &JD2 = ES.createJITDylib("JD2"); cantFail(JD2.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); auto Filter = [this](SymbolStringPtr Name) { return Name != Bar; }; JD.setFallbackDefinitionGenerator( ReexportsFallbackDefinitionGenerator(JD2, Filter)); auto Flags = JD.lookupFlags({Foo, Bar, Baz}); EXPECT_EQ(Flags.size(), 1U) << "Unexpected number of results"; EXPECT_EQ(Flags[Foo], FooSym.getFlags()) << "Unexpected flags for Foo"; auto Result = cantFail(lookup({&JD}, Foo)); EXPECT_EQ(Result.getAddress(), FooSym.getAddress()) << "Incorrect reexported symbol address"; } TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) { Optional FooR; auto FooMU = llvm::make_unique( SymbolFlagsMap({{Foo, FooSym.getFlags()}}), [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); cantFail(JD.define(FooMU)); bool FooReady = false; auto OnResolution = [](Expected R) { cantFail(std::move(R)); }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); FooReady = true; }; ES.lookup({&JD}, {Foo}, std::move(OnResolution), std::move(OnReady), NoDependenciesToRegister); FooR->resolve({{Foo, FooSym}}); FooR->emit(); EXPECT_TRUE(FooReady) << "Self-dependency prevented symbol from being marked ready"; } TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) { // Test that a circular symbol dependency between three symbols in a JITDylib // does not prevent any symbol from becoming 'ready' once all symbols are // emitted. // Create three MaterializationResponsibility objects: one for each of Foo, // Bar and Baz. These are optional because MaterializationResponsibility // does not have a default constructor). Optional FooR; Optional BarR; Optional BazR; // Create a MaterializationUnit for each symbol that moves the // MaterializationResponsibility into one of the locals above. auto FooMU = llvm::make_unique( SymbolFlagsMap({{Foo, FooSym.getFlags()}}), [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); auto BarMU = llvm::make_unique( SymbolFlagsMap({{Bar, BarSym.getFlags()}}), [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); }); auto BazMU = llvm::make_unique( SymbolFlagsMap({{Baz, BazSym.getFlags()}}), [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); }); // Define the symbols. cantFail(JD.define(FooMU)); cantFail(JD.define(BarMU)); cantFail(JD.define(BazMU)); // Query each of the symbols to trigger materialization. bool FooResolved = false; bool FooReady = false; auto OnFooResolution = [&](Expected Result) { cantFail(std::move(Result)); FooResolved = true; }; auto OnFooReady = [&](Error Err) { cantFail(std::move(Err)); FooReady = true; }; // Issue a lookup for Foo. Use NoDependenciesToRegister: We're going to add // the dependencies manually below. ES.lookup({&JD}, {Foo}, std::move(OnFooResolution), std::move(OnFooReady), NoDependenciesToRegister); bool BarResolved = false; bool BarReady = false; auto OnBarResolution = [&](Expected Result) { cantFail(std::move(Result)); BarResolved = true; }; auto OnBarReady = [&](Error Err) { cantFail(std::move(Err)); BarReady = true; }; ES.lookup({&JD}, {Bar}, std::move(OnBarResolution), std::move(OnBarReady), NoDependenciesToRegister); bool BazResolved = false; bool BazReady = false; auto OnBazResolution = [&](Expected Result) { cantFail(std::move(Result)); BazResolved = true; }; auto OnBazReady = [&](Error Err) { cantFail(std::move(Err)); BazReady = true; }; ES.lookup({&JD}, {Baz}, std::move(OnBazResolution), std::move(OnBazReady), NoDependenciesToRegister); // Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo. FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}}); BarR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}}); BazR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}}); // Add self-dependencies for good measure. This tests that the implementation // of addDependencies filters these out. FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}}); BarR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}}); BazR->addDependenciesForAll({{&JD, SymbolNameSet({Baz})}}); // Check that nothing has been resolved yet. EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet"; EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet"; EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet"; // Resolve the symbols (but do not emit them). FooR->resolve({{Foo, FooSym}}); BarR->resolve({{Bar, BarSym}}); BazR->resolve({{Baz, BazSym}}); // Verify that the symbols have been resolved, but are not ready yet. EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now"; EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now"; EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now"; EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet"; EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet"; EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet"; // Emit two of the symbols. FooR->emit(); BarR->emit(); // Verify that nothing is ready until the circular dependence is resolved. EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready"; EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready"; EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready"; // Emit the last symbol. BazR->emit(); // Verify that everything becomes ready once the circular dependence resolved. EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now"; EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now"; EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now"; } TEST_F(CoreAPIsStandardTest, DropMaterializerWhenEmpty) { bool DestructorRun = false; JITSymbolFlags WeakExported(JITSymbolFlags::Exported); WeakExported |= JITSymbolFlags::Weak; auto MU = llvm::make_unique( SymbolFlagsMap({{Foo, WeakExported}, {Bar, WeakExported}}), [](MaterializationResponsibility R) { llvm_unreachable("Unexpected call to materialize"); }, [&](const JITDylib &JD, SymbolStringPtr Name) { EXPECT_TRUE(Name == Foo || Name == Bar) << "Discard of unexpected symbol?"; }, [&]() { DestructorRun = true; }); cantFail(JD.define(MU)); cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); EXPECT_FALSE(DestructorRun) << "MaterializationUnit should not have been destroyed yet"; cantFail(JD.define(absoluteSymbols({{Bar, BarSym}}))); EXPECT_TRUE(DestructorRun) << "MaterializationUnit should have been destroyed"; } TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) { bool FooMaterialized = false; bool BarDiscarded = false; JITSymbolFlags WeakExported(JITSymbolFlags::Exported); WeakExported |= JITSymbolFlags::Weak; auto MU = llvm::make_unique( SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}), [&](MaterializationResponsibility R) { assert(BarDiscarded && "Bar should have been discarded by this point"); R.resolve(SymbolMap({{Foo, FooSym}})); R.emit(); FooMaterialized = true; }, [&](const JITDylib &JD, SymbolStringPtr Name) { EXPECT_EQ(Name, Bar) << "Expected Name to be Bar"; BarDiscarded = true; }); cantFail(JD.define(MU)); cantFail(JD.define(absoluteSymbols({{Bar, BarSym}}))); SymbolNameSet Names({Foo}); bool OnResolutionRun = false; bool OnReadyRun = false; auto OnResolution = [&](Expected Result) { EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; auto I = Result->find(Foo); EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; EXPECT_EQ(I->second.getAddress(), FooSym.getAddress()) << "Resolution returned incorrect result"; OnResolutionRun = true; }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); OnReadyRun = true; }; ES.lookup({&JD}, Names, std::move(OnResolution), std::move(OnReady), NoDependenciesToRegister); EXPECT_TRUE(FooMaterialized) << "Foo was not materialized"; EXPECT_TRUE(BarDiscarded) << "Bar was not discarded"; EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; EXPECT_TRUE(OnReadyRun) << "OnReady was not run"; } TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) { bool ExpectNoMoreMaterialization = false; ES.setDispatchMaterialization( [&](JITDylib &JD, std::unique_ptr MU) { if (ExpectNoMoreMaterialization) ADD_FAILURE() << "Unexpected materialization"; MU->doMaterialize(JD); }); auto MU = llvm::make_unique( SymbolFlagsMap({{Foo, FooSym.getFlags()}}), [&](MaterializationResponsibility R) { cantFail( R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}}))); R.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); R.emit(); }); cantFail(JD.define(MU)); cantFail(lookup({&JD}, Foo)); // Assert that materialization is complete by now. ExpectNoMoreMaterialization = true; // Look up bar to verify that no further materialization happens. auto BarResult = cantFail(lookup({&JD}, Bar)); EXPECT_EQ(BarResult.getAddress(), BarSym.getAddress()) << "Expected Bar == BarSym"; } TEST_F(CoreAPIsStandardTest, FallbackDefinitionGeneratorTest) { cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); JD.setFallbackDefinitionGenerator( [&](JITDylib &JD2, const SymbolNameSet &Names) { cantFail(JD2.define(absoluteSymbols({{Bar, BarSym}}))); return SymbolNameSet({Bar}); }); auto Result = cantFail(lookup({&JD}, {Foo, Bar})); EXPECT_EQ(Result.count(Bar), 1U) << "Expected to find fallback def for 'bar'"; EXPECT_EQ(Result[Bar].getAddress(), BarSym.getAddress()) << "Expected fallback def for Bar to be equal to BarSym"; } TEST_F(CoreAPIsStandardTest, FailResolution) { auto MU = llvm::make_unique( SymbolFlagsMap( {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}), [&](MaterializationResponsibility R) { R.failMaterialization(); }); cantFail(JD.define(MU)); SymbolNameSet Names({Foo, Bar}); auto Result = lookup({&JD}, Names); EXPECT_FALSE(!!Result) << "Expected failure"; if (!Result) { handleAllErrors(Result.takeError(), [&](FailedToMaterialize &F) { EXPECT_EQ(F.getSymbols(), Names) << "Expected to fail on symbols in Names"; }, [](ErrorInfoBase &EIB) { std::string ErrMsg; { raw_string_ostream ErrOut(ErrMsg); EIB.log(ErrOut); } ADD_FAILURE() << "Expected a FailedToResolve error. Got:\n" << ErrMsg; }); } } TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) { auto MU = llvm::make_unique( SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), [&](MaterializationResponsibility R) { R.resolve({{Foo, FooSym}}); R.emit(); }); cantFail(JD.define(MU)); auto FooLookupResult = cantFail(lookup({&JD}, Foo)); EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) << "lookup returned an incorrect address"; EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) << "lookup returned incorrect flags"; } TEST_F(CoreAPIsStandardTest, TestLookupWithThreadedMaterialization) { #if LLVM_ENABLE_THREADS std::thread MaterializationThread; ES.setDispatchMaterialization( [&](JITDylib &JD, std::unique_ptr MU) { auto SharedMU = std::shared_ptr(std::move(MU)); MaterializationThread = std::thread([SharedMU, &JD]() { SharedMU->doMaterialize(JD); }); }); cantFail(JD.define(absoluteSymbols({{Foo, FooSym}}))); auto FooLookupResult = cantFail(lookup({&JD}, Foo)); EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) << "lookup returned an incorrect address"; EXPECT_EQ(FooLookupResult.getFlags(), FooSym.getFlags()) << "lookup returned incorrect flags"; MaterializationThread.join(); #endif } TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) { // Test that GetRequestedSymbols returns the set of symbols that currently // have pending queries, and test that MaterializationResponsibility's // replace method can be used to return definitions to the JITDylib in a new // MaterializationUnit. SymbolNameSet Names({Foo, Bar}); bool FooMaterialized = false; bool BarMaterialized = false; auto MU = llvm::make_unique( SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), [&](MaterializationResponsibility R) { auto Requested = R.getRequestedSymbols(); EXPECT_EQ(Requested.size(), 1U) << "Expected one symbol requested"; EXPECT_EQ(*Requested.begin(), Foo) << "Expected \"Foo\" requested"; auto NewMU = llvm::make_unique( SymbolFlagsMap({{Bar, BarSym.getFlags()}}), [&](MaterializationResponsibility R2) { R2.resolve(SymbolMap({{Bar, BarSym}})); R2.emit(); BarMaterialized = true; }); R.replace(std::move(NewMU)); R.resolve(SymbolMap({{Foo, FooSym}})); R.emit(); FooMaterialized = true; }); cantFail(JD.define(MU)); EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet"; EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet"; auto FooSymResult = cantFail(lookup({&JD}, Foo)); EXPECT_EQ(FooSymResult.getAddress(), FooSym.getAddress()) << "Address mismatch for Foo"; EXPECT_TRUE(FooMaterialized) << "Foo should be materialized now"; EXPECT_FALSE(BarMaterialized) << "Bar still should not be materialized"; auto BarSymResult = cantFail(lookup({&JD}, Bar)); EXPECT_EQ(BarSymResult.getAddress(), BarSym.getAddress()) << "Address mismatch for Bar"; EXPECT_TRUE(BarMaterialized) << "Bar should be materialized now"; } TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) { auto MU = llvm::make_unique( SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), [&](MaterializationResponsibility R) { auto R2 = R.delegate({Bar}); R.resolve({{Foo, FooSym}}); R.emit(); R2.resolve({{Bar, BarSym}}); R2.emit(); }); cantFail(JD.define(MU)); auto Result = lookup({&JD}, {Foo, Bar}); EXPECT_TRUE(!!Result) << "Result should be a success value"; EXPECT_EQ(Result->count(Foo), 1U) << "\"Foo\" entry missing"; EXPECT_EQ(Result->count(Bar), 1U) << "\"Bar\" entry missing"; EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress()) << "Address mismatch for \"Foo\""; EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress()) << "Address mismatch for \"Bar\""; } TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) { // Confirm that once a weak definition is selected for materialization it is // treated as strong. JITSymbolFlags WeakExported = JITSymbolFlags::Exported; WeakExported &= JITSymbolFlags::Weak; std::unique_ptr FooResponsibility; auto MU = llvm::make_unique( SymbolFlagsMap({{Foo, FooSym.getFlags()}}), [&](MaterializationResponsibility R) { FooResponsibility = llvm::make_unique(std::move(R)); }); cantFail(JD.define(MU)); auto OnResolution = [](Expected Result) { cantFail(std::move(Result)); }; auto OnReady = [](Error Err) { cantFail(std::move(Err)); }; ES.lookup({&JD}, {Foo}, std::move(OnResolution), std::move(OnReady), NoDependenciesToRegister); auto MU2 = llvm::make_unique( SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), [](MaterializationResponsibility R) { llvm_unreachable("This unit should never be materialized"); }); auto Err = JD.define(MU2); EXPECT_TRUE(!!Err) << "Expected failure value"; EXPECT_TRUE(Err.isA()) << "Expected a duplicate definition error"; consumeError(std::move(Err)); FooResponsibility->resolve(SymbolMap({{Foo, FooSym}})); FooResponsibility->emit(); } } // namespace