|  | #include "OrcTestCommon.h" | 
|  | #include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h" | 
|  | #include "llvm/ExecutionEngine/Orc/JITLinkRedirectableSymbolManager.h" | 
|  | #include "llvm/ExecutionEngine/Orc/JITLinkReentryTrampolines.h" | 
|  | #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" | 
|  | #include "llvm/ExecutionEngine/Orc/LLJIT.h" | 
|  | #include "llvm/ExecutionEngine/Orc/LazyReexports.h" | 
|  | #include "llvm/Testing/Support/Error.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace llvm::orc; | 
|  |  | 
|  | class LazyReexportsTest : public CoreAPIsBasedStandardTest {}; | 
|  |  | 
|  | static int dummyTarget() { return 42; } | 
|  |  | 
|  | TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) { | 
|  | // Create a callthrough manager for the host (if possible) and verify that | 
|  | // a call to the lazy call-through: | 
|  | // (1) Materializes the MU. This verifies that the symbol was looked up, and | 
|  | //     that we didn't arrive at the target via some other path | 
|  | // (2) Returns the expected value (which we take as proof that the call | 
|  | //     reached the target). | 
|  |  | 
|  | auto JTMB = JITTargetMachineBuilder::detectHost(); | 
|  |  | 
|  | // Bail out if we can not detect the host. | 
|  | if (!JTMB) { | 
|  | consumeError(JTMB.takeError()); | 
|  | GTEST_SKIP(); | 
|  | } | 
|  |  | 
|  | // Bail out if we can not build a local call-through manager. | 
|  | auto LCTM = createLocalLazyCallThroughManager(JTMB->getTargetTriple(), ES, | 
|  | ExecutorAddr()); | 
|  | if (!LCTM) { | 
|  | consumeError(LCTM.takeError()); | 
|  | GTEST_SKIP(); | 
|  | } | 
|  |  | 
|  | auto DummyTarget = ES.intern("DummyTarget"); | 
|  |  | 
|  | bool DummyTargetMaterialized = false; | 
|  |  | 
|  | cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>( | 
|  | SymbolFlagsMap({{DummyTarget, JITSymbolFlags::Exported}}), | 
|  | [&](std::unique_ptr<MaterializationResponsibility> R) { | 
|  | DummyTargetMaterialized = true; | 
|  | // No dependencies registered, can't fail. | 
|  | cantFail(R->notifyResolved({{DummyTarget, | 
|  | {ExecutorAddr::fromPtr(&dummyTarget), | 
|  | JITSymbolFlags::Exported}}})); | 
|  | cantFail(R->notifyEmitted({})); | 
|  | }))); | 
|  |  | 
|  | unsigned NotifyResolvedCount = 0; | 
|  | auto NotifyResolved = [&](ExecutorAddr ResolvedAddr) { | 
|  | ++NotifyResolvedCount; | 
|  | return Error::success(); | 
|  | }; | 
|  |  | 
|  | auto CallThroughTrampoline = cantFail((*LCTM)->getCallThroughTrampoline( | 
|  | JD, DummyTarget, std::move(NotifyResolved))); | 
|  |  | 
|  | auto CTTPtr = CallThroughTrampoline.toPtr<int (*)()>(); | 
|  |  | 
|  | // Call twice to verify nothing unexpected happens on redundant calls. | 
|  | auto Result = CTTPtr(); | 
|  | (void)CTTPtr(); | 
|  |  | 
|  | EXPECT_TRUE(DummyTargetMaterialized) | 
|  | << "CallThrough did not materialize target"; | 
|  | EXPECT_EQ(NotifyResolvedCount, 1U) | 
|  | << "CallThrough should have generated exactly one 'NotifyResolved' call"; | 
|  | EXPECT_EQ(Result, 42) << "Failed to call through to target"; | 
|  | } | 
|  |  | 
|  | static void *noReentry(void *) { abort(); } | 
|  |  | 
|  | TEST(JITLinkLazyReexportsTest, Basics) { | 
|  | OrcNativeTarget::initialize(); | 
|  |  | 
|  | auto J = LLJITBuilder().create(); | 
|  | if (!J) { | 
|  | dbgs() << toString(J.takeError()) << "\n"; | 
|  | // consumeError(J.takeError()); | 
|  | GTEST_SKIP(); | 
|  | } | 
|  | if (!isa<ObjectLinkingLayer>((*J)->getObjLinkingLayer())) | 
|  | GTEST_SKIP(); | 
|  |  | 
|  | auto &OLL = cast<ObjectLinkingLayer>((*J)->getObjLinkingLayer()); | 
|  |  | 
|  | auto RSMgr = JITLinkRedirectableSymbolManager::Create(OLL); | 
|  | if (!RSMgr) { | 
|  | dbgs() << "Boom for RSMgr\n"; | 
|  | consumeError(RSMgr.takeError()); | 
|  | GTEST_SKIP(); | 
|  | } | 
|  |  | 
|  | auto &ES = (*J)->getExecutionSession(); | 
|  |  | 
|  | auto &JD = ES.createBareJITDylib("JD"); | 
|  | cantFail(JD.define(absoluteSymbols( | 
|  | {{ES.intern("__orc_rt_reentry"), | 
|  | {ExecutorAddr::fromPtr(&noReentry), | 
|  | JITSymbolFlags::Exported | JITSymbolFlags::Callable}}}))); | 
|  |  | 
|  | auto LRMgr = createJITLinkLazyReexportsManager(OLL, **RSMgr, JD); | 
|  | if (!LRMgr) { | 
|  | dbgs() << "Boom for LRMgr\n"; | 
|  | consumeError(LRMgr.takeError()); | 
|  | GTEST_SKIP(); | 
|  | } | 
|  |  | 
|  | auto Foo = ES.intern("foo"); | 
|  | auto Bar = ES.intern("bar"); | 
|  |  | 
|  | auto RT = JD.createResourceTracker(); | 
|  | cantFail(JD.define( | 
|  | lazyReexports( | 
|  | **LRMgr, | 
|  | {{Foo, {Bar, JITSymbolFlags::Exported | JITSymbolFlags::Callable}}}), | 
|  | RT)); | 
|  |  | 
|  | // Check flags after adding Foo -> Bar lazy reexport. | 
|  | auto SF = cantFail( | 
|  | ES.lookupFlags(LookupKind::Static, makeJITDylibSearchOrder(&JD), | 
|  | {{Foo, SymbolLookupFlags::WeaklyReferencedSymbol}})); | 
|  | EXPECT_EQ(SF.size(), 1U); | 
|  | EXPECT_TRUE(SF.count(Foo)); | 
|  | EXPECT_EQ(SF[Foo], JITSymbolFlags::Exported | JITSymbolFlags::Callable); | 
|  |  | 
|  | // Remove reexport without running it. | 
|  | if (auto Err = RT->remove()) { | 
|  | EXPECT_THAT_ERROR(std::move(Err), Succeeded()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check flags after adding Foo -> Bar lazy reexport. | 
|  | SF = cantFail( | 
|  | ES.lookupFlags(LookupKind::Static, makeJITDylibSearchOrder(&JD), | 
|  | {{Foo, SymbolLookupFlags::WeaklyReferencedSymbol}})); | 
|  | EXPECT_EQ(SF.size(), 0U); | 
|  | } |