diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
commit | eb11fae6d08f479c0799db45860a98af528fa6e7 (patch) | |
tree | 44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /unittests/ExecutionEngine/Orc | |
parent | b8a2042aa938069e862750553db0e4d82d25822c (diff) |
Notes
Diffstat (limited to 'unittests/ExecutionEngine/Orc')
-rw-r--r-- | unittests/ExecutionEngine/Orc/CMakeLists.txt | 2 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp | 25 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp | 744 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp | 10 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp | 183 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp | 126 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/OrcCAPITest.cpp | 60 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/OrcTestCommon.cpp | 5 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/OrcTestCommon.h | 57 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp | 127 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp | 34 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp | 6 |
12 files changed, 1194 insertions, 185 deletions
diff --git a/unittests/ExecutionEngine/Orc/CMakeLists.txt b/unittests/ExecutionEngine/Orc/CMakeLists.txt index dd0281d0b73d..6dbff7c592a4 100644 --- a/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -11,9 +11,11 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(OrcJITTests CompileOnDemandLayerTest.cpp + CoreAPIsTest.cpp IndirectionUtilsTest.cpp GlobalMappingLayerTest.cpp LazyEmittingLayerTest.cpp + LegacyAPIInteropTest.cpp ObjectTransformLayerTest.cpp OrcCAPITest.cpp OrcTestCommon.cpp diff --git a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp index 61ce310e6311..a1f864bae01f 100644 --- a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp @@ -18,7 +18,8 @@ namespace { class DummyCallbackManager : public orc::JITCompileCallbackManager { public: - DummyCallbackManager() : JITCompileCallbackManager(0) {} + DummyCallbackManager(ExecutionSession &ES) + : JITCompileCallbackManager(ES, 0) {} public: Error grow() override { llvm_unreachable("not implemented"); } @@ -35,11 +36,11 @@ public: llvm_unreachable("Not implemented"); } - JITSymbol findStub(StringRef Name, bool ExportedStubsOnly) override { + JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override { llvm_unreachable("Not implemented"); } - JITSymbol findPointer(StringRef Name) override { + JITEvaluatedSymbol findPointer(StringRef Name) override { llvm_unreachable("Not implemented"); } @@ -57,11 +58,23 @@ TEST(CompileOnDemandLayerTest, FindSymbol) { return JITSymbol(nullptr); }; - DummyCallbackManager CallbackMgr; + + ExecutionSession ES(std::make_shared<SymbolStringPool>()); + DummyCallbackManager CallbackMgr(ES); + + auto GetResolver = + [](orc::VModuleKey) -> std::shared_ptr<llvm::orc::SymbolResolver> { + llvm_unreachable("Should never be called"); + }; + + auto SetResolver = [](orc::VModuleKey, std::shared_ptr<orc::SymbolResolver>) { + llvm_unreachable("Should never be called"); + }; llvm::orc::CompileOnDemandLayer<decltype(TestBaseLayer)> COD( - TestBaseLayer, [](Function &F) { return std::set<Function *>{&F}; }, - CallbackMgr, [] { return llvm::make_unique<DummyStubsManager>(); }, true); + ES, TestBaseLayer, GetResolver, SetResolver, + [](Function &F) { return std::set<Function *>{&F}; }, CallbackMgr, + [] { return llvm::make_unique<DummyStubsManager>(); }, true); auto Sym = COD.findSymbol("foo", true); diff --git a/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp new file mode 100644 index 000000000000..baa1e3b5e8cb --- /dev/null +++ b/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp @@ -0,0 +1,744 @@ +//===----------- 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 <set> +#include <thread> + +using namespace llvm; +using namespace llvm::orc; + +class CoreAPIsStandardTest : public CoreAPIsBasedStandardTest {}; + +namespace { + +class SimpleMaterializationUnit : public MaterializationUnit { +public: + using MaterializeFunction = + std::function<void(MaterializationResponsibility)>; + using DiscardFunction = std::function<void(const VSO &, SymbolStringPtr)>; + using DestructorFunction = std::function<void()>; + + 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 VSO &V, SymbolStringPtr Name) override { + if (Discard) + Discard(V, 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<SymbolMap> 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<MaterializationResponsibility> FooMR; + + cantFail(V.define(llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, FooSym.getFlags()}}), + [&](MaterializationResponsibility R) { + FooMR = std::make_shared<MaterializationResponsibility>(std::move(R)); + }))); + + ES.lookup({&V}, {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->finalize(); + + EXPECT_TRUE(OnReadyRun) << "Should have been marked ready"; +} + +TEST_F(CoreAPIsStandardTest, ExecutionSessionFailQuery) { + bool OnResolutionRun = false; + bool OnReadyRun = false; + + auto OnResolution = [&](Expected<SymbolMap> 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<StringError>("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<SymbolMap> Result) { + cantFail(std::move(Result)); + OnResolvedRun = true; + }; + + auto OnReady = [&](Error Err) { + cantFail(std::move(Err)); + OnReadyRun = true; + }; + + ES.lookup({&V}, {}, 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, ChainedVSOLookup) { + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + + auto &V2 = ES.createVSO("V2"); + + bool OnResolvedRun = false; + bool OnReadyRun = false; + + auto Q = std::make_shared<AsynchronousSymbolQuery>( + SymbolNameSet({Foo}), + [&](Expected<SymbolMap> Result) { + cantFail(std::move(Result)); + OnResolvedRun = true; + }, + [&](Error Err) { + cantFail(std::move(Err)); + OnReadyRun = true; + }); + + V2.legacyLookup(Q, V.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::FlagNames>( + JITSymbolFlags::Exported | JITSymbolFlags::Weak)); + auto MU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Bar, BarSym.getFlags()}}), + [](MaterializationResponsibility R) { + llvm_unreachable("Symbol materialized on flags lookup"); + }); + + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + cantFail(V.define(std::move(MU))); + + SymbolNameSet Names({Foo, Bar, Baz}); + + auto SymbolFlags = V.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(V.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); + cantFail(V.define(symbolAliases({{Baz, {Foo, JITSymbolFlags::Exported}}, + {Qux, {Bar, JITSymbolFlags::Weak}}}))); + cantFail(V.define(absoluteSymbols({{Qux, QuxSym}}))); + + auto Result = lookup({&V}, {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(V.define(absoluteSymbols({{Foo, FooSym}}))); + cantFail(V.define(symbolAliases( + {{Baz, {Bar, BazSym.getFlags()}}, {Bar, {Foo, BarSym.getFlags()}}}))); + + auto Result = lookup({&V}, {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 + // VSO works. + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + + auto &V2 = ES.createVSO("V2"); + + cantFail(V2.define(reexports(V, {{Bar, {Foo, BarSym.getFlags()}}}))); + + auto Result = cantFail(lookup({&V2}, 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(V.define(absoluteSymbols({{Foo, FooSym}}))); + + bool BarMaterialized = false; + auto BarMU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Bar, BarSym.getFlags()}}), + [&](MaterializationResponsibility R) { + BarMaterialized = true; + R.resolve({{Bar, BarSym}}); + R.finalize(); + }); + + cantFail(V.define(BarMU)); + + auto &V2 = ES.createVSO("V2"); + + cantFail(V2.define(reexports( + V, {{Baz, {Foo, BazSym.getFlags()}}, {Qux, {Bar, QuxSym.getFlags()}}}))); + + auto Result = cantFail(lookup({&V2}, 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, TestTrivialCircularDependency) { + Optional<MaterializationResponsibility> FooR; + auto FooMU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, FooSym.getFlags()}}), + [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); + + cantFail(V.define(FooMU)); + + bool FooReady = false; + auto OnResolution = [](Expected<SymbolMap> R) { cantFail(std::move(R)); }; + auto OnReady = [&](Error Err) { + cantFail(std::move(Err)); + FooReady = true; + }; + + ES.lookup({&V}, {Foo}, std::move(OnResolution), std::move(OnReady), + NoDependenciesToRegister); + + FooR->resolve({{Foo, FooSym}}); + FooR->finalize(); + + EXPECT_TRUE(FooReady) + << "Self-dependency prevented symbol from being marked ready"; +} + +TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneVSO) { + // Test that a circular symbol dependency between three symbols in a VSO does + // not prevent any symbol from becoming 'ready' once all symbols are + // finalized. + + // Create three MaterializationResponsibility objects: one for each of Foo, + // Bar and Baz. These are optional because MaterializationResponsibility + // does not have a default constructor). + Optional<MaterializationResponsibility> FooR; + Optional<MaterializationResponsibility> BarR; + Optional<MaterializationResponsibility> BazR; + + // Create a MaterializationUnit for each symbol that moves the + // MaterializationResponsibility into one of the locals above. + auto FooMU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, FooSym.getFlags()}}), + [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); + + auto BarMU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Bar, BarSym.getFlags()}}), + [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); }); + + auto BazMU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Baz, BazSym.getFlags()}}), + [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); }); + + // Define the symbols. + cantFail(V.define(FooMU)); + cantFail(V.define(BarMU)); + cantFail(V.define(BazMU)); + + // Query each of the symbols to trigger materialization. + bool FooResolved = false; + bool FooReady = false; + + auto OnFooResolution = [&](Expected<SymbolMap> 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({&V}, {Foo}, std::move(OnFooResolution), std::move(OnFooReady), + NoDependenciesToRegister); + + bool BarResolved = false; + bool BarReady = false; + auto OnBarResolution = [&](Expected<SymbolMap> Result) { + cantFail(std::move(Result)); + BarResolved = true; + }; + + auto OnBarReady = [&](Error Err) { + cantFail(std::move(Err)); + BarReady = true; + }; + + ES.lookup({&V}, {Bar}, std::move(OnBarResolution), std::move(OnBarReady), + NoDependenciesToRegister); + + bool BazResolved = false; + bool BazReady = false; + + auto OnBazResolution = [&](Expected<SymbolMap> Result) { + cantFail(std::move(Result)); + BazResolved = true; + }; + + auto OnBazReady = [&](Error Err) { + cantFail(std::move(Err)); + BazReady = true; + }; + + ES.lookup({&V}, {Baz}, std::move(OnBazResolution), std::move(OnBazReady), + NoDependenciesToRegister); + + // Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo. + FooR->addDependenciesForAll({{&V, SymbolNameSet({Bar})}}); + BarR->addDependenciesForAll({{&V, SymbolNameSet({Baz})}}); + BazR->addDependenciesForAll({{&V, SymbolNameSet({Foo})}}); + + // Add self-dependencies for good measure. This tests that the implementation + // of addDependencies filters these out. + FooR->addDependenciesForAll({{&V, SymbolNameSet({Foo})}}); + BarR->addDependenciesForAll({{&V, SymbolNameSet({Bar})}}); + BazR->addDependenciesForAll({{&V, 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 finalized 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"; + + // Finalize two of the symbols. + FooR->finalize(); + BarR->finalize(); + + // 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"; + + // Finalize the last symbol. + BazR->finalize(); + + // 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<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, WeakExported}, {Bar, WeakExported}}), + [](MaterializationResponsibility R) { + llvm_unreachable("Unexpected call to materialize"); + }, + [&](const VSO &V, SymbolStringPtr Name) { + EXPECT_TRUE(Name == Foo || Name == Bar) + << "Discard of unexpected symbol?"; + }, + [&]() { DestructorRun = true; }); + + cantFail(V.define(MU)); + + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + + EXPECT_FALSE(DestructorRun) + << "MaterializationUnit should not have been destroyed yet"; + + cantFail(V.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<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}), + [&](MaterializationResponsibility R) { + assert(BarDiscarded && "Bar should have been discarded by this point"); + R.resolve(SymbolMap({{Foo, FooSym}})); + R.finalize(); + FooMaterialized = true; + }, + [&](const VSO &V, SymbolStringPtr Name) { + EXPECT_EQ(Name, Bar) << "Expected Name to be Bar"; + BarDiscarded = true; + }); + + cantFail(V.define(MU)); + cantFail(V.define(absoluteSymbols({{Bar, BarSym}}))); + + SymbolNameSet Names({Foo}); + + bool OnResolutionRun = false; + bool OnReadyRun = false; + + auto OnResolution = [&](Expected<SymbolMap> 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({&V}, 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( + [&](VSO &V, std::unique_ptr<MaterializationUnit> MU) { + if (ExpectNoMoreMaterialization) + ADD_FAILURE() << "Unexpected materialization"; + MU->doMaterialize(V); + }); + + auto MU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, FooSym.getFlags()}}), + [&](MaterializationResponsibility R) { + cantFail( + R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}}))); + R.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); + R.finalize(); + }); + + cantFail(V.define(MU)); + cantFail(lookup({&V}, Foo)); + + // Assert that materialization is complete by now. + ExpectNoMoreMaterialization = true; + + // Look up bar to verify that no further materialization happens. + auto BarResult = cantFail(lookup({&V}, Bar)); + EXPECT_EQ(BarResult.getAddress(), BarSym.getAddress()) + << "Expected Bar == BarSym"; +} + +TEST_F(CoreAPIsStandardTest, FallbackDefinitionGeneratorTest) { + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + + V.setFallbackDefinitionGenerator([&](VSO &W, const SymbolNameSet &Names) { + cantFail(W.define(absoluteSymbols({{Bar, BarSym}}))); + return SymbolNameSet({Bar}); + }); + + auto Result = cantFail(lookup({&V}, {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<SimpleMaterializationUnit>( + SymbolFlagsMap( + {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}), + [&](MaterializationResponsibility R) { R.failMaterialization(); }); + + cantFail(V.define(MU)); + + SymbolNameSet Names({Foo, Bar}); + auto Result = lookup({&V}, 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<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), + [&](MaterializationResponsibility R) { + R.resolve({{Foo, FooSym}}); + R.finalize(); + }); + + cantFail(V.define(MU)); + + auto FooLookupResult = cantFail(lookup({&V}, 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( + [&](VSO &V, std::unique_ptr<MaterializationUnit> MU) { + auto SharedMU = std::shared_ptr<MaterializationUnit>(std::move(MU)); + MaterializationThread = + std::thread([SharedMU, &V]() { SharedMU->doMaterialize(V); }); + }); + + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + + auto FooLookupResult = cantFail(lookup({&V}, 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 VSO in a new + // MaterializationUnit. + SymbolNameSet Names({Foo, Bar}); + + bool FooMaterialized = false; + bool BarMaterialized = false; + + auto MU = llvm::make_unique<SimpleMaterializationUnit>( + 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<SimpleMaterializationUnit>( + SymbolFlagsMap({{Bar, BarSym.getFlags()}}), + [&](MaterializationResponsibility R2) { + R2.resolve(SymbolMap({{Bar, BarSym}})); + R2.finalize(); + BarMaterialized = true; + }); + + R.replace(std::move(NewMU)); + + R.resolve(SymbolMap({{Foo, FooSym}})); + R.finalize(); + + FooMaterialized = true; + }); + + cantFail(V.define(MU)); + + EXPECT_FALSE(FooMaterialized) << "Foo should not be materialized yet"; + EXPECT_FALSE(BarMaterialized) << "Bar should not be materialized yet"; + + auto FooSymResult = cantFail(lookup({&V}, 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({&V}, 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<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}), + [&](MaterializationResponsibility R) { + auto R2 = R.delegate({Bar}); + + R.resolve({{Foo, FooSym}}); + R.finalize(); + R2.resolve({{Bar, BarSym}}); + R2.finalize(); + }); + + cantFail(V.define(MU)); + + auto Result = lookup({&V}, {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<MaterializationResponsibility> FooResponsibility; + auto MU = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, FooSym.getFlags()}}), + [&](MaterializationResponsibility R) { + FooResponsibility = + llvm::make_unique<MaterializationResponsibility>(std::move(R)); + }); + + cantFail(V.define(MU)); + auto OnResolution = [](Expected<SymbolMap> Result) { + cantFail(std::move(Result)); + }; + + auto OnReady = [](Error Err) { cantFail(std::move(Err)); }; + + ES.lookup({&V}, {Foo}, std::move(OnResolution), std::move(OnReady), + NoDependenciesToRegister); + + auto MU2 = llvm::make_unique<SimpleMaterializationUnit>( + SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), + [](MaterializationResponsibility R) { + llvm_unreachable("This unit should never be materialized"); + }); + + auto Err = V.define(MU2); + EXPECT_TRUE(!!Err) << "Expected failure value"; + EXPECT_TRUE(Err.isA<DuplicateDefinition>()) + << "Expected a duplicate definition error"; + consumeError(std::move(Err)); + + FooResponsibility->resolve(SymbolMap({{Foo, FooSym}})); + FooResponsibility->finalize(); +} + +} // namespace diff --git a/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp b/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp index 0dba66d47535..3801fa918db7 100644 --- a/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/LazyEmittingLayerTest.cpp @@ -15,11 +15,8 @@ namespace { struct MockBaseLayer { typedef int ModuleHandleT; - ModuleHandleT addModule( - std::shared_ptr<llvm::Module>, - std::unique_ptr<llvm::RuntimeDyld::MemoryManager> MemMgr, - std::unique_ptr<llvm::JITSymbolResolver> Resolver) { - EXPECT_FALSE(MemMgr); + ModuleHandleT addModule(llvm::orc::VModuleKey, + std::shared_ptr<llvm::Module>) { return 42; } }; @@ -27,7 +24,8 @@ struct MockBaseLayer { TEST(LazyEmittingLayerTest, Empty) { MockBaseLayer M; llvm::orc::LazyEmittingLayer<MockBaseLayer> L(M); - cantFail(L.addModule(std::unique_ptr<llvm::Module>(), nullptr)); + cantFail( + L.addModule(llvm::orc::VModuleKey(), std::unique_ptr<llvm::Module>())); } } diff --git a/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp b/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp new file mode 100644 index 000000000000..746ae1dca490 --- /dev/null +++ b/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp @@ -0,0 +1,183 @@ +//===----------- 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/ExecutionEngine/Orc/Legacy.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::orc; + +class LegacyAPIsStandardTest : public CoreAPIsBasedStandardTest {}; + +namespace { + +TEST_F(LegacyAPIsStandardTest, TestLambdaSymbolResolver) { + cantFail(V.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); + + auto Resolver = createSymbolResolver( + [&](const SymbolNameSet &Symbols) { return V.lookupFlags(Symbols); }, + [&](std::shared_ptr<AsynchronousSymbolQuery> Q, SymbolNameSet Symbols) { + return V.legacyLookup(std::move(Q), Symbols); + }); + + SymbolNameSet Symbols({Foo, Bar, Baz}); + + SymbolFlagsMap SymbolFlags = Resolver->lookupFlags(Symbols); + + EXPECT_EQ(SymbolFlags.size(), 2U) + << "lookupFlags returned the wrong number of results"; + EXPECT_EQ(SymbolFlags.count(Foo), 1U) << "Missing lookupFlags result for foo"; + EXPECT_EQ(SymbolFlags.count(Bar), 1U) << "Missing lookupFlags result for bar"; + EXPECT_EQ(SymbolFlags[Foo], FooSym.getFlags()) + << "Incorrect lookupFlags result for Foo"; + EXPECT_EQ(SymbolFlags[Bar], BarSym.getFlags()) + << "Incorrect lookupFlags result for Bar"; + + bool OnResolvedRun = false; + + auto OnResolved = [&](Expected<SymbolMap> Result) { + OnResolvedRun = true; + EXPECT_TRUE(!!Result) << "Unexpected error"; + EXPECT_EQ(Result->size(), 2U) << "Unexpected number of resolved symbols"; + EXPECT_EQ(Result->count(Foo), 1U) << "Missing lookup result for foo"; + EXPECT_EQ(Result->count(Bar), 1U) << "Missing lookup result for bar"; + EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress()) + << "Incorrect address for foo"; + EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress()) + << "Incorrect address for bar"; + }; + auto OnReady = [&](Error Err) { + EXPECT_FALSE(!!Err) << "Finalization should never fail in this test"; + }; + + auto Q = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Foo, Bar}), + OnResolved, OnReady); + auto Unresolved = Resolver->lookup(std::move(Q), Symbols); + + EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol"; + EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to not be resolved"; + EXPECT_TRUE(OnResolvedRun) << "OnResolved was never run"; +} + +TEST(LegacyAPIInteropTest, QueryAgainstVSO) { + + ExecutionSession ES(std::make_shared<SymbolStringPool>()); + auto Foo = ES.getSymbolStringPool().intern("foo"); + + auto &V = ES.createVSO("V"); + JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported); + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + + auto LookupFlags = [&](const SymbolNameSet &Names) { + return V.lookupFlags(Names); + }; + + auto Lookup = [&](std::shared_ptr<AsynchronousSymbolQuery> Query, + SymbolNameSet Symbols) { + return V.legacyLookup(std::move(Query), Symbols); + }; + + auto UnderlyingResolver = + createSymbolResolver(std::move(LookupFlags), std::move(Lookup)); + JITSymbolResolverAdapter Resolver(ES, *UnderlyingResolver, nullptr); + + JITSymbolResolver::LookupSet Names{StringRef("foo")}; + + auto LFR = Resolver.lookupFlags(Names); + EXPECT_TRUE(!!LFR) << "lookupFlags failed"; + EXPECT_EQ(LFR->size(), 1U) + << "lookupFlags returned the wrong number of results"; + EXPECT_EQ(LFR->count(*Foo), 1U) + << "lookupFlags did not contain a result for 'foo'"; + EXPECT_EQ((*LFR)[*Foo], FooSym.getFlags()) + << "lookupFlags contained the wrong result for 'foo'"; + + auto LR = Resolver.lookup(Names); + EXPECT_TRUE(!!LR) << "lookup failed"; + EXPECT_EQ(LR->size(), 1U) << "lookup returned the wrong number of results"; + EXPECT_EQ(LR->count(*Foo), 1U) << "lookup did not contain a result for 'foo'"; + EXPECT_EQ((*LR)[*Foo].getFlags(), FooSym.getFlags()) + << "lookup returned the wrong result for flags of 'foo'"; + EXPECT_EQ((*LR)[*Foo].getAddress(), FooSym.getAddress()) + << "lookup returned the wrong result for address of 'foo'"; +} + +TEST(LegacyAPIInteropTset, LegacyLookupHelpersFn) { + constexpr JITTargetAddress FooAddr = 0xdeadbeef; + JITSymbolFlags FooFlags = JITSymbolFlags::Exported; + + bool BarMaterialized = false; + constexpr JITTargetAddress BarAddr = 0xcafef00d; + JITSymbolFlags BarFlags = static_cast<JITSymbolFlags::FlagNames>( + JITSymbolFlags::Exported | JITSymbolFlags::Weak); + + auto LegacyLookup = [&](const std::string &Name) -> JITSymbol { + if (Name == "foo") + return {FooAddr, FooFlags}; + + if (Name == "bar") { + auto BarMaterializer = [&]() -> Expected<JITTargetAddress> { + BarMaterialized = true; + return BarAddr; + }; + + return {BarMaterializer, BarFlags}; + } + + return nullptr; + }; + + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); + auto Baz = ES.getSymbolStringPool().intern("baz"); + + SymbolNameSet Symbols({Foo, Bar, Baz}); + + auto SymbolFlags = lookupFlagsWithLegacyFn(Symbols, LegacyLookup); + + EXPECT_TRUE(!!SymbolFlags) << "Expected lookupFlagsWithLegacyFn to succeed"; + EXPECT_EQ(SymbolFlags->size(), 2U) << "Wrong number of flags returned"; + EXPECT_EQ(SymbolFlags->count(Foo), 1U) << "Flags for foo missing"; + EXPECT_EQ(SymbolFlags->count(Bar), 1U) << "Flags for foo missing"; + EXPECT_EQ((*SymbolFlags)[Foo], FooFlags) << "Wrong flags for foo"; + EXPECT_EQ((*SymbolFlags)[Bar], BarFlags) << "Wrong flags for foo"; + EXPECT_FALSE(BarMaterialized) + << "lookupFlags should not have materialized bar"; + + bool OnResolvedRun = false; + bool OnReadyRun = false; + auto OnResolved = [&](Expected<SymbolMap> Result) { + OnResolvedRun = true; + EXPECT_TRUE(!!Result) << "lookuWithLegacy failed to resolve"; + + EXPECT_EQ(Result->size(), 2U) << "Wrong number of symbols resolved"; + EXPECT_EQ(Result->count(Foo), 1U) << "Result for foo missing"; + EXPECT_EQ(Result->count(Bar), 1U) << "Result for bar missing"; + EXPECT_EQ((*Result)[Foo].getAddress(), FooAddr) << "Wrong address for foo"; + EXPECT_EQ((*Result)[Foo].getFlags(), FooFlags) << "Wrong flags for foo"; + EXPECT_EQ((*Result)[Bar].getAddress(), BarAddr) << "Wrong address for bar"; + EXPECT_EQ((*Result)[Bar].getFlags(), BarFlags) << "Wrong flags for bar"; + }; + auto OnReady = [&](Error Err) { + EXPECT_FALSE(!!Err) << "Finalization unexpectedly failed"; + OnReadyRun = true; + }; + + AsynchronousSymbolQuery Q({Foo, Bar}, OnResolved, OnReady); + auto Unresolved = lookupWithLegacyFn(ES, Q, Symbols, LegacyLookup); + + EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run"; + EXPECT_TRUE(OnReadyRun) << "OnReady was not run"; + EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol"; + EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to be unresolved"; +} + +} // namespace diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp index 7cd6443b5d4a..6ad3c19ada95 100644 --- a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp @@ -14,6 +14,7 @@ #include "llvm/ExecutionEngine/Orc/NullResolver.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/IR/Module.h" #include "llvm/Object/ObjectFile.h" #include "gtest/gtest.h" @@ -44,42 +45,32 @@ struct AllocatingTransform { // transform layer called the base layer and forwarded any return value. class MockBaseLayer { public: - typedef int ObjHandleT; - MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); } - template <typename ObjPtrT> - llvm::Expected<ObjHandleT> - addObject(ObjPtrT Obj, - std::shared_ptr<llvm::JITSymbolResolver> Resolver) { - EXPECT_EQ(MockResolver, Resolver) << "Resolver should pass through"; + template <typename ObjPtrT> llvm::Error addObject(VModuleKey K, ObjPtrT Obj) { + EXPECT_EQ(MockKey, K) << "Key should pass through"; EXPECT_EQ(MockObject + 1, *Obj) << "Transform should be applied"; LastCalled = "addObject"; - MockObjHandle = 111; - return MockObjHandle; + return llvm::Error::success(); } - template <typename ObjPtrT> - void expectAddObject(ObjPtrT Obj, - std::shared_ptr<llvm::JITSymbolResolver> Resolver) { - MockResolver = Resolver; + template <typename ObjPtrT> void expectAddObject(VModuleKey K, ObjPtrT Obj) { + MockKey = K; MockObject = *Obj; } - - void verifyAddObject(ObjHandleT Returned) { + void verifyAddObject() { EXPECT_EQ("addObject", LastCalled); - EXPECT_EQ(MockObjHandle, Returned) << "Return should pass through"; resetExpectations(); } - llvm::Error removeObject(ObjHandleT H) { - EXPECT_EQ(MockObjHandle, H); + llvm::Error removeObject(VModuleKey K) { + EXPECT_EQ(MockKey, K); LastCalled = "removeObject"; return llvm::Error::success(); } - void expectRemoveObject(ObjHandleT H) { MockObjHandle = H; } + void expectRemoveObject(VModuleKey K) { MockKey = K; } void verifyRemoveObject() { EXPECT_EQ("removeObject", LastCalled); resetExpectations(); @@ -105,18 +96,18 @@ public: resetExpectations(); } - llvm::JITSymbol findSymbolIn(ObjHandleT H, const std::string &Name, + llvm::JITSymbol findSymbolIn(VModuleKey K, const std::string &Name, bool ExportedSymbolsOnly) { - EXPECT_EQ(MockObjHandle, H) << "Handle should pass through"; + EXPECT_EQ(MockKey, K) << "VModuleKey should pass through"; EXPECT_EQ(MockName, Name) << "Name should pass through"; EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through"; LastCalled = "findSymbolIn"; MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None); return llvm::JITSymbol(122, llvm::JITSymbolFlags::None); } - void expectFindSymbolIn(ObjHandleT H, const std::string &Name, + void expectFindSymbolIn(VModuleKey K, const std::string &Name, bool ExportedSymbolsOnly) { - MockObjHandle = H; + MockKey = K; MockName = Name; MockBool = ExportedSymbolsOnly; } @@ -128,29 +119,29 @@ public: resetExpectations(); } - llvm::Error emitAndFinalize(ObjHandleT H) { - EXPECT_EQ(MockObjHandle, H) << "Handle should pass through"; + llvm::Error emitAndFinalize(VModuleKey K) { + EXPECT_EQ(MockKey, K) << "VModuleKey should pass through"; LastCalled = "emitAndFinalize"; return llvm::Error::success(); } - void expectEmitAndFinalize(ObjHandleT H) { MockObjHandle = H; } + void expectEmitAndFinalize(VModuleKey K) { MockKey = K; } void verifyEmitAndFinalize() { EXPECT_EQ("emitAndFinalize", LastCalled); resetExpectations(); } - void mapSectionAddress(ObjHandleT H, const void *LocalAddress, + void mapSectionAddress(VModuleKey K, const void *LocalAddress, llvm::JITTargetAddress TargetAddr) { - EXPECT_EQ(MockObjHandle, H); + EXPECT_EQ(MockKey, K); EXPECT_EQ(MockLocalAddress, LocalAddress); EXPECT_EQ(MockTargetAddress, TargetAddr); LastCalled = "mapSectionAddress"; } - void expectMapSectionAddress(ObjHandleT H, const void *LocalAddress, + void expectMapSectionAddress(VModuleKey K, const void *LocalAddress, llvm::JITTargetAddress TargetAddr) { - MockObjHandle = H; + MockKey = K; MockLocalAddress = LocalAddress; MockTargetAddress = TargetAddr; } @@ -162,9 +153,8 @@ public: private: // Backing fields for remembering parameter/return values std::string LastCalled; - std::shared_ptr<llvm::JITSymbolResolver> MockResolver; + VModuleKey MockKey; MockObjectFile MockObject; - ObjHandleT MockObjHandle; std::string MockName; bool MockBool; llvm::JITSymbol MockSymbol; @@ -175,9 +165,8 @@ private: // Clear remembered parameters between calls void resetExpectations() { LastCalled = "nothing"; - MockResolver = nullptr; + MockKey = 0; MockObject = 0; - MockObjHandle = 0; MockName = "bogus"; MockSymbol = llvm::JITSymbol(nullptr); MockLocalAddress = nullptr; @@ -190,6 +179,8 @@ private: TEST(ObjectTransformLayerTest, Main) { MockBaseLayer M; + ExecutionSession ES(std::make_shared<SymbolStringPool>()); + // Create one object transform layer using a transform (as a functor) // that allocates new objects, and deals in unique pointers. ObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(M); @@ -205,22 +196,23 @@ TEST(ObjectTransformLayerTest, Main) { }); // Test addObject with T1 (allocating) + auto K1 = ES.allocateVModule(); auto Obj1 = std::make_shared<MockObjectFile>(211); - auto SR = std::make_shared<NullResolver>(); - M.expectAddObject(Obj1, SR); - auto H = cantFail(T1.addObject(std::move(Obj1), SR)); - M.verifyAddObject(H); + M.expectAddObject(K1, Obj1); + cantFail(T1.addObject(K1, std::move(Obj1))); + M.verifyAddObject(); // Test addObjectSet with T2 (mutating) + auto K2 = ES.allocateVModule(); auto Obj2 = std::make_shared<MockObjectFile>(222); - M.expectAddObject(Obj2, SR); - H = cantFail(T2.addObject(Obj2, SR)); - M.verifyAddObject(H); + M.expectAddObject(K2, Obj2); + cantFail(T2.addObject(K2, Obj2)); + M.verifyAddObject(); EXPECT_EQ(223, *Obj2) << "Expected mutation"; // Test removeObjectSet - M.expectRemoveObject(H); - cantFail(T1.removeObject(H)); + M.expectRemoveObject(K2); + cantFail(T1.removeObject(K2)); M.verifyRemoveObject(); // Test findSymbol @@ -233,20 +225,20 @@ TEST(ObjectTransformLayerTest, Main) { // Test findSymbolIn Name = "bar"; ExportedOnly = false; - M.expectFindSymbolIn(H, Name, ExportedOnly); - llvm::JITSymbol Sym2 = T1.findSymbolIn(H, Name, ExportedOnly); + M.expectFindSymbolIn(K1, Name, ExportedOnly); + llvm::JITSymbol Sym2 = T1.findSymbolIn(K1, Name, ExportedOnly); M.verifyFindSymbolIn(std::move(Sym2)); // Test emitAndFinalize - M.expectEmitAndFinalize(H); - cantFail(T2.emitAndFinalize(H)); + M.expectEmitAndFinalize(K1); + cantFail(T2.emitAndFinalize(K1)); M.verifyEmitAndFinalize(); // Test mapSectionAddress char Buffer[24]; llvm::JITTargetAddress MockAddress = 255; - M.expectMapSectionAddress(H, Buffer, MockAddress); - T1.mapSectionAddress(H, Buffer, MockAddress); + M.expectMapSectionAddress(K1, Buffer, MockAddress); + T1.mapSectionAddress(K1, Buffer, MockAddress); M.verifyMapSectionAddress(); // Verify transform getter (non-const) @@ -290,37 +282,35 @@ TEST(ObjectTransformLayerTest, Main) { }; // Construct the jit layers. - RTDyldObjectLinkingLayer BaseLayer( - []() { - return std::make_shared<llvm::SectionMemoryManager>(); - }); - - auto IdentityTransform = - [](std::shared_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>> - Obj) { - return Obj; - }; + RTDyldObjectLinkingLayer BaseLayer(ES, [](VModuleKey) { + return RTDyldObjectLinkingLayer::Resources{ + std::make_shared<llvm::SectionMemoryManager>(), + std::make_shared<NullResolver>()}; + }); + + auto IdentityTransform = [](std::unique_ptr<llvm::MemoryBuffer> Obj) { + return Obj; + }; ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)> TransformLayer(BaseLayer, IdentityTransform); auto NullCompiler = [](llvm::Module &) { - return llvm::object::OwningBinary<llvm::object::ObjectFile>(nullptr, - nullptr); + return std::unique_ptr<llvm::MemoryBuffer>(nullptr); }; IRCompileLayer<decltype(TransformLayer), decltype(NullCompiler)> CompileLayer(TransformLayer, NullCompiler); // Make sure that the calls from IRCompileLayer to ObjectTransformLayer // compile. - auto Resolver = std::make_shared<NullResolver>(); - cantFail(CompileLayer.addModule(std::shared_ptr<llvm::Module>(), Resolver)); + cantFail(CompileLayer.addModule(ES.allocateVModule(), + std::unique_ptr<llvm::Module>())); // Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer // compile. - decltype(TransformLayer)::ObjHandleT H2; - cantFail(TransformLayer.emitAndFinalize(H2)); - TransformLayer.findSymbolIn(H2, Name, false); + VModuleKey DummyKey = ES.allocateVModule(); + cantFail(TransformLayer.emitAndFinalize(DummyKey)); + TransformLayer.findSymbolIn(DummyKey, Name, false); TransformLayer.findSymbol(Name, true); - TransformLayer.mapSectionAddress(H2, nullptr, 0); - cantFail(TransformLayer.removeObject(H2)); + TransformLayer.mapSectionAddress(DummyKey, nullptr, 0); + cantFail(TransformLayer.removeObject(DummyKey)); } } diff --git a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp index d9448d47667f..b288b6bab2c9 100644 --- a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp +++ b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp @@ -38,13 +38,11 @@ protected: return MB.takeModule(); } - std::shared_ptr<object::OwningBinary<object::ObjectFile>> - createTestObject() { + std::unique_ptr<MemoryBuffer> createTestObject() { orc::SimpleCompiler IRCompiler(*TM); auto M = createTestModule(TM->getTargetTriple()); M->setDataLayout(TM->createDataLayout()); - return std::make_shared<object::OwningBinary<object::ObjectFile>>( - IRCompiler(*M)); + return IRCompiler(*M); } typedef int (*MainFnTy)(); @@ -75,9 +73,8 @@ protected: CompileContext *CCtx = static_cast<CompileContext*>(Ctx); auto *ET = CCtx->APIExecTest; CCtx->M = ET->createTestModule(ET->TM->getTargetTriple()); - LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(CCtx->M.release())); - LLVMOrcAddEagerlyCompiledIR(JITStack, &CCtx->H, SM, myResolver, nullptr); - LLVMOrcDisposeSharedModuleRef(SM); + LLVMOrcAddEagerlyCompiledIR(JITStack, &CCtx->H, wrap(CCtx->M.release()), + myResolver, nullptr); CCtx->Compiled = true; LLVMOrcTargetAddress MainAddr; LLVMOrcGetSymbolAddress(JITStack, &MainAddr, "main"); @@ -89,7 +86,7 @@ protected: char *OrcCAPIExecutionTest::testFuncName = nullptr; TEST_F(OrcCAPIExecutionTest, TestEagerIRCompilation) { - if (!TM) + if (!SupportsJIT) return; LLVMOrcJITStackRef JIT = @@ -99,16 +96,28 @@ TEST_F(OrcCAPIExecutionTest, TestEagerIRCompilation) { LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc"); - LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(M.release())); LLVMOrcModuleHandle H; - LLVMOrcAddEagerlyCompiledIR(JIT, &H, SM, myResolver, nullptr); - LLVMOrcDisposeSharedModuleRef(SM); - LLVMOrcTargetAddress MainAddr; - LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main"); - MainFnTy MainFn = (MainFnTy)MainAddr; - int Result = MainFn(); - EXPECT_EQ(Result, 42) - << "Eagerly JIT'd code did not return expected result"; + LLVMOrcAddEagerlyCompiledIR(JIT, &H, wrap(M.release()), myResolver, nullptr); + + // get symbol address searching the entire stack + { + LLVMOrcTargetAddress MainAddr; + LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main"); + MainFnTy MainFn = (MainFnTy)MainAddr; + int Result = MainFn(); + EXPECT_EQ(Result, 42) + << "Eagerly JIT'd code did not return expected result"; + } + + // and then just searching a single handle + { + LLVMOrcTargetAddress MainAddr; + LLVMOrcGetSymbolAddressIn(JIT, &MainAddr, H, "main"); + MainFnTy MainFn = (MainFnTy)MainAddr; + int Result = MainFn(); + EXPECT_EQ(Result, 42) + << "Eagerly JIT'd code did not return expected result"; + } LLVMOrcRemoveModule(JIT, H); @@ -117,7 +126,7 @@ TEST_F(OrcCAPIExecutionTest, TestEagerIRCompilation) { } TEST_F(OrcCAPIExecutionTest, TestLazyIRCompilation) { - if (!TM) + if (!SupportsIndirection) return; LLVMOrcJITStackRef JIT = @@ -127,10 +136,8 @@ TEST_F(OrcCAPIExecutionTest, TestLazyIRCompilation) { LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc"); - LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(M.release())); LLVMOrcModuleHandle H; - LLVMOrcAddLazilyCompiledIR(JIT, &H, SM, myResolver, nullptr); - LLVMOrcDisposeSharedModuleRef(SM); + LLVMOrcAddLazilyCompiledIR(JIT, &H, wrap(M.release()), myResolver, nullptr); LLVMOrcTargetAddress MainAddr; LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main"); MainFnTy MainFn = (MainFnTy)MainAddr; @@ -145,15 +152,10 @@ TEST_F(OrcCAPIExecutionTest, TestLazyIRCompilation) { } TEST_F(OrcCAPIExecutionTest, TestAddObjectFile) { - if (!TM) + if (!SupportsJIT) return; - std::unique_ptr<MemoryBuffer> ObjBuffer; - { - auto OwningObj = createTestObject(); - auto ObjAndBuffer = OwningObj->takeBinary(); - ObjBuffer = std::move(ObjAndBuffer.second); - } + auto ObjBuffer = createTestObject(); LLVMOrcJITStackRef JIT = LLVMOrcCreateInstance(wrap(TM.get())); @@ -175,7 +177,7 @@ TEST_F(OrcCAPIExecutionTest, TestAddObjectFile) { } TEST_F(OrcCAPIExecutionTest, TestDirectCallbacksAPI) { - if (!TM) + if (!SupportsIndirection) return; LLVMOrcJITStackRef JIT = diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp b/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp index ccd2fc0fb189..a12b1876e29c 100644 --- a/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp +++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.cpp @@ -15,6 +15,11 @@ using namespace llvm; +const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::FooAddr; +const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::BarAddr; +const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::BazAddr; +const JITTargetAddress llvm::orc::CoreAPIsBasedStandardTest::QuxAddr; + bool OrcNativeTarget::NativeTargetInitialized = false; ModuleBuilder::ModuleBuilder(LLVMContext &Context, StringRef Triple, diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/unittests/ExecutionEngine/Orc/OrcTestCommon.h index 28a5f08ee439..c6caaf07db0e 100644 --- a/unittests/ExecutionEngine/Orc/OrcTestCommon.h +++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.h @@ -17,17 +17,59 @@ #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/TypeBuilder.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" + #include <memory> namespace llvm { +namespace orc { +// CoreAPIsStandardTest that saves a bunch of boilerplate by providing the +// following: +// +// (1) ES -- An ExecutionSession +// (2) Foo, Bar, Baz, Qux -- SymbolStringPtrs for strings "foo", "bar", "baz", +// and "qux" respectively. +// (3) FooAddr, BarAddr, BazAddr, QuxAddr -- Dummy addresses. Guaranteed +// distinct and non-null. +// (4) FooSym, BarSym, BazSym, QuxSym -- JITEvaluatedSymbols with FooAddr, +// BarAddr, BazAddr, and QuxAddr respectively. All with default strong, +// linkage and non-hidden visibility. +// (5) V -- A VSO associated with ES. +class CoreAPIsBasedStandardTest : public testing::Test { +public: +protected: + ExecutionSession ES; + VSO &V = ES.createVSO("V"); + SymbolStringPtr Foo = ES.getSymbolStringPool().intern("foo"); + SymbolStringPtr Bar = ES.getSymbolStringPool().intern("bar"); + SymbolStringPtr Baz = ES.getSymbolStringPool().intern("baz"); + SymbolStringPtr Qux = ES.getSymbolStringPool().intern("qux"); + static const JITTargetAddress FooAddr = 1U; + static const JITTargetAddress BarAddr = 2U; + static const JITTargetAddress BazAddr = 3U; + static const JITTargetAddress QuxAddr = 4U; + JITEvaluatedSymbol FooSym = + JITEvaluatedSymbol(FooAddr, JITSymbolFlags::Exported); + JITEvaluatedSymbol BarSym = + JITEvaluatedSymbol(BarAddr, JITSymbolFlags::Exported); + JITEvaluatedSymbol BazSym = + JITEvaluatedSymbol(BazAddr, JITSymbolFlags::Exported); + JITEvaluatedSymbol QuxSym = + JITEvaluatedSymbol(QuxAddr, JITSymbolFlags::Exported); +}; + +} // end namespace orc + class OrcNativeTarget { public: static void initialize() { @@ -59,15 +101,26 @@ public: // If we found a TargetMachine, check that it's one that Orc supports. const Triple& TT = TM->getTargetTriple(); + // Bail out for windows platforms. We do not support these yet. if ((TT.getArch() != Triple::x86_64 && TT.getArch() != Triple::x86) || - TT.isOSWindows()) - TM = nullptr; + TT.isOSWindows()) + return; + + // Target can JIT? + SupportsJIT = TM->getTarget().hasJIT(); + // Use ability to create callback manager to detect whether Orc + // has indirection support on this platform. This way the test + // and Orc code do not get out of sync. + SupportsIndirection = !!orc::createLocalCompileCallbackManager(TT, ES, 0); } }; protected: + orc::ExecutionSession ES; LLVMContext Context; std::unique_ptr<TargetMachine> TM; + bool SupportsJIT = false; + bool SupportsIndirection = false; }; class ModuleBuilder { diff --git a/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp b/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp index ed7b327124d7..420631c36ad2 100644 --- a/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp @@ -12,6 +12,7 @@ #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/LambdaResolver.h" +#include "llvm/ExecutionEngine/Orc/Legacy.h" #include "llvm/ExecutionEngine/Orc/NullResolver.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/Constants.h" @@ -66,7 +67,12 @@ TEST(RTDyldObjectLinkingLayerTest, TestSetProcessAllSections) { bool DebugSectionSeen = false; auto MM = std::make_shared<MemoryManagerWrapper>(DebugSectionSeen); - RTDyldObjectLinkingLayer ObjLayer([&MM]() { return MM; }); + ExecutionSession ES(std::make_shared<SymbolStringPool>()); + + RTDyldObjectLinkingLayer ObjLayer(ES, [&MM](VModuleKey) { + return RTDyldObjectLinkingLayer::Resources{ + MM, std::make_shared<NullResolver>()}; + }); LLVMContext Context; auto M = llvm::make_unique<Module>("", Context); @@ -88,46 +94,48 @@ TEST(RTDyldObjectLinkingLayerTest, TestSetProcessAllSections) { if (!TM) return; - auto Obj = - std::make_shared<object::OwningBinary<object::ObjectFile>>( - SimpleCompiler(*TM)(*M)); - - auto Resolver = - createLambdaResolver( - [](const std::string &Name) { - return JITSymbol(nullptr); - }, - [](const std::string &Name) { - return JITSymbol(nullptr); - }); + auto Obj = SimpleCompiler(*TM)(*M); { // Test with ProcessAllSections = false (the default). - auto H = cantFail(ObjLayer.addObject(Obj, Resolver)); - cantFail(ObjLayer.emitAndFinalize(H)); + auto K = ES.allocateVModule(); + cantFail(ObjLayer.addObject( + K, MemoryBuffer::getMemBufferCopy(Obj->getBuffer()))); + cantFail(ObjLayer.emitAndFinalize(K)); EXPECT_EQ(DebugSectionSeen, false) << "Unexpected debug info section"; - cantFail(ObjLayer.removeObject(H)); + cantFail(ObjLayer.removeObject(K)); } { // Test with ProcessAllSections = true. ObjLayer.setProcessAllSections(true); - auto H = cantFail(ObjLayer.addObject(Obj, Resolver)); - cantFail(ObjLayer.emitAndFinalize(H)); + auto K = ES.allocateVModule(); + cantFail(ObjLayer.addObject(K, std::move(Obj))); + cantFail(ObjLayer.emitAndFinalize(K)); EXPECT_EQ(DebugSectionSeen, true) << "Expected debug info section not seen"; - cantFail(ObjLayer.removeObject(H)); + cantFail(ObjLayer.removeObject(K)); } } TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { - if (!TM) + if (!SupportsJIT) return; + ExecutionSession ES(std::make_shared<SymbolStringPool>()); + auto MM = std::make_shared<SectionMemoryManagerWrapper>(); - RTDyldObjectLinkingLayer ObjLayer([&MM]() { return MM; }); + std::map<orc::VModuleKey, std::shared_ptr<orc::SymbolResolver>> Resolvers; + + RTDyldObjectLinkingLayer ObjLayer(ES, [&](VModuleKey K) { + auto I = Resolvers.find(K); + assert(I != Resolvers.end() && "Missing resolver"); + auto R = std::move(I->second); + Resolvers.erase(I); + return RTDyldObjectLinkingLayer::Resources{MM, std::move(R)}; + }); SimpleCompiler Compile(*TM); // Create a pair of modules that will trigger recursive finalization: @@ -153,9 +161,7 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { Builder.CreateRet(FourtyTwo); } - auto Obj1 = - std::make_shared<object::OwningBinary<object::ObjectFile>>( - Compile(*MB1.getModule())); + auto Obj1 = Compile(*MB1.getModule()); ModuleBuilder MB2(Context, "", "dummy"); { @@ -166,25 +172,29 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { IRBuilder<> Builder(FooEntry); Builder.CreateRet(Builder.CreateCall(BarDecl)); } - auto Obj2 = - std::make_shared<object::OwningBinary<object::ObjectFile>>( - Compile(*MB2.getModule())); - - auto Resolver = - createLambdaResolver( - [&](const std::string &Name) { - if (auto Sym = ObjLayer.findSymbol(Name, true)) - return Sym; - return JITSymbol(nullptr); + auto Obj2 = Compile(*MB2.getModule()); + + auto K1 = ES.allocateVModule(); + Resolvers[K1] = std::make_shared<NullResolver>(); + cantFail(ObjLayer.addObject(K1, std::move(Obj1))); + + auto K2 = ES.allocateVModule(); + auto LegacyLookup = [&](const std::string &Name) { + return ObjLayer.findSymbol(Name, true); + }; + + Resolvers[K2] = createSymbolResolver( + [&](const SymbolNameSet &Symbols) { + return cantFail(lookupFlagsWithLegacyFn(Symbols, LegacyLookup)); }, - [](const std::string &Name) { - return JITSymbol(nullptr); + [&](std::shared_ptr<AsynchronousSymbolQuery> Query, + const SymbolNameSet &Symbols) { + return lookupWithLegacyFn(ES, *Query, Symbols, LegacyLookup); }); - cantFail(ObjLayer.addObject(std::move(Obj1), Resolver)); - auto H = cantFail(ObjLayer.addObject(std::move(Obj2), Resolver)); - cantFail(ObjLayer.emitAndFinalize(H)); - cantFail(ObjLayer.removeObject(H)); + cantFail(ObjLayer.addObject(K2, std::move(Obj2))); + cantFail(ObjLayer.emitAndFinalize(K2)); + cantFail(ObjLayer.removeObject(K2)); // Finalization of module 2 should trigger finalization of module 1. // Verify that finalize on SMMW is only called once. @@ -193,12 +203,17 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { } TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) { - if (!TM) + if (!SupportsJIT) return; + ExecutionSession ES(std::make_shared<SymbolStringPool>()); + auto MM = std::make_shared<SectionMemoryManagerWrapper>(); - RTDyldObjectLinkingLayer ObjLayer([&MM]() { return MM; }); + RTDyldObjectLinkingLayer ObjLayer(ES, [&MM](VModuleKey K) { + return RTDyldObjectLinkingLayer::Resources{ + MM, std::make_shared<NullResolver>()}; + }); SimpleCompiler Compile(*TM); // Create a pair of unrelated modules: @@ -225,9 +240,7 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) { Builder.CreateRet(FourtyTwo); } - auto Obj1 = - std::make_shared<object::OwningBinary<object::ObjectFile>>( - Compile(*MB1.getModule())); + auto Obj1 = Compile(*MB1.getModule()); ModuleBuilder MB2(Context, "", "dummy"); { @@ -239,15 +252,13 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) { Value *Seven = ConstantInt::getSigned(Int32Ty, 7); Builder.CreateRet(Seven); } - auto Obj2 = - std::make_shared<object::OwningBinary<object::ObjectFile>>( - Compile(*MB2.getModule())); + auto Obj2 = Compile(*MB2.getModule()); - auto NR = std::make_shared<NullResolver>(); - auto H = cantFail(ObjLayer.addObject(std::move(Obj1), NR)); - cantFail(ObjLayer.addObject(std::move(Obj2), NR)); - cantFail(ObjLayer.emitAndFinalize(H)); - cantFail(ObjLayer.removeObject(H)); + auto K = ES.allocateVModule(); + cantFail(ObjLayer.addObject(K, std::move(Obj1))); + cantFail(ObjLayer.addObject(ES.allocateVModule(), std::move(Obj2))); + cantFail(ObjLayer.emitAndFinalize(K)); + cantFail(ObjLayer.removeObject(K)); // Only one call to needsToReserveAllocationSpace should have been made. EXPECT_EQ(MM->NeedsToReserveAllocationSpaceCount, 1) @@ -256,10 +267,14 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoPrematureAllocation) { } TEST_F(RTDyldObjectLinkingLayerExecutionTest, TestNotifyLoadedSignature) { + ExecutionSession ES(std::make_shared<SymbolStringPool>()); RTDyldObjectLinkingLayer ObjLayer( - []() { return nullptr; }, - [](RTDyldObjectLinkingLayer::ObjHandleT, - const RTDyldObjectLinkingLayer::ObjectPtr &obj, + ES, + [](VModuleKey) { + return RTDyldObjectLinkingLayer::Resources{ + nullptr, std::make_shared<NullResolver>()}; + }, + [](VModuleKey, const object::ObjectFile &obj, const RuntimeDyld::LoadedObjectInfo &info) {}); } diff --git a/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp b/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp index 819c5f8eb34b..ae408a06f84d 100644 --- a/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/RemoteObjectLayerTest.cpp @@ -24,8 +24,7 @@ public: using ObjHandleT = uint64_t; - using ObjectPtr = - std::shared_ptr<object::OwningBinary<object::ObjectFile>>; + using ObjectPtr = std::unique_ptr<MemoryBuffer>; using LookupFn = std::function<JITSymbol(StringRef, bool)>; using SymbolLookupTable = std::map<ObjHandleT, LookupFn>; @@ -43,7 +42,7 @@ public: Expected<ObjHandleT> addObject(ObjectPtr Obj, std::shared_ptr<JITSymbolResolver> Resolver) { - return AddObject(Obj, SymTab); + return AddObject(std::move(Obj), SymTab); } Error removeObject(ObjHandleT H) { @@ -102,8 +101,7 @@ MockObjectLayer::ObjectPtr createTestObject() { B.CreateRet(ConstantInt::getSigned(Type::getInt32Ty(Ctx), 42)); SimpleCompiler IRCompiler(*TM); - return std::make_shared<object::OwningBinary<object::ObjectFile>>( - IRCompiler(*MB.getModule())); + return IRCompiler(*MB.getModule()); } TEST(RemoteObjectLayer, AddObject) { @@ -121,7 +119,7 @@ TEST(RemoteObjectLayer, AddObject) { // Copy the bytes out of the test object: the copy will be used to verify // that the original is correctly transmitted over RPC to the mock layer. - StringRef ObjBytes = TestObject->getBinary()->getData(); + StringRef ObjBytes = TestObject->getBuffer(); std::vector<char> ObjContents(ObjBytes.size()); std::copy(ObjBytes.begin(), ObjBytes.end(), ObjContents.begin()); @@ -134,7 +132,7 @@ TEST(RemoteObjectLayer, AddObject) { MockObjectLayer::SymbolLookupTable &SymTab) { // Check that the received object file content matches the original. - StringRef RPCObjContents = Obj->getBinary()->getData(); + StringRef RPCObjContents = Obj->getBuffer(); EXPECT_EQ(RPCObjContents.size(), ObjContents.size()) << "RPC'd object file has incorrect size"; EXPECT_TRUE(std::equal(RPCObjContents.begin(), RPCObjContents.end(), @@ -159,7 +157,7 @@ TEST(RemoteObjectLayer, AddObject) { }); cantFail(Client.addObject(std::move(TestObject), - std::make_shared<NullResolver>())); + std::make_shared<NullLegacyResolver>())); cantFail(ClientEP.callB<remote::utils::TerminateSession>()); ServerThread.join(); } @@ -205,8 +203,8 @@ TEST(RemoteObjectLayer, AddObjectFailure) { cantFail(ServerEP.handleOne()); }); - auto HandleOrErr = - Client.addObject(std::move(TestObject), std::make_shared<NullResolver>()); + auto HandleOrErr = Client.addObject(std::move(TestObject), + std::make_shared<NullLegacyResolver>()); EXPECT_FALSE(HandleOrErr) << "Expected error from addObject"; @@ -258,8 +256,8 @@ TEST(RemoteObjectLayer, RemoveObject) { cantFail(ServerEP.handleOne()); }); - auto H = cantFail(Client.addObject(std::move(TestObject), - std::make_shared<NullResolver>())); + auto H = cantFail(Client.addObject(std::move(TestObject), + std::make_shared<NullLegacyResolver>())); cantFail(Client.removeObject(H)); @@ -309,8 +307,8 @@ TEST(RemoteObjectLayer, RemoveObjectFailure) { cantFail(ServerEP.handleOne()); }); - auto H = cantFail(Client.addObject(std::move(TestObject), - std::make_shared<NullResolver>())); + auto H = cantFail(Client.addObject(std::move(TestObject), + std::make_shared<NullLegacyResolver>())); auto Err = Client.removeObject(H); EXPECT_TRUE(!!Err) << "Expected error from removeObject"; @@ -374,7 +372,7 @@ TEST(RemoteObjectLayer, FindSymbol) { }); cantFail(Client.addObject(std::move(TestObject), - std::make_shared<NullResolver>())); + std::make_shared<NullLegacyResolver>())); // Check that we can find and materialize a valid symbol. auto Sym1 = Client.findSymbol("foobar", true); @@ -463,7 +461,7 @@ TEST(RemoteObjectLayer, FindSymbolIn) { }); auto H = cantFail(Client.addObject(std::move(TestObject), - std::make_shared<NullResolver>())); + std::make_shared<NullLegacyResolver>())); auto Sym1 = Client.findSymbolIn(H, "foobar", true); @@ -523,7 +521,7 @@ TEST(RemoteObjectLayer, EmitAndFinalize) { }); auto H = cantFail(Client.addObject(std::move(TestObject), - std::make_shared<NullResolver>())); + std::make_shared<NullLegacyResolver>())); auto Err = Client.emitAndFinalize(H); EXPECT_FALSE(!!Err) << "emitAndFinalize should work"; @@ -573,7 +571,7 @@ TEST(RemoteObjectLayer, EmitAndFinalizeFailure) { }); auto H = cantFail(Client.addObject(std::move(TestObject), - std::make_shared<NullResolver>())); + std::make_shared<NullLegacyResolver>())); auto Err = Client.emitAndFinalize(H); EXPECT_TRUE(!!Err) << "emitAndFinalize should work"; diff --git a/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp b/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp index 79929e332182..861a9661223a 100644 --- a/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp +++ b/unittests/ExecutionEngine/Orc/SymbolStringPoolTest.cpp @@ -34,6 +34,12 @@ TEST(SymbolStringPool, UniquingAndComparisons) { (void)(P1 < P3); } +TEST(SymbolStringPool, Dereference) { + SymbolStringPool SP; + auto Foo = SP.intern("foo"); + EXPECT_EQ(*Foo, "foo") << "Equality on dereferenced string failed"; +} + TEST(SymbolStringPool, ClearDeadEntries) { SymbolStringPool SP; { |