xref: /llvm-project/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp (revision 300deebf41d2da96701fe29c0faa8025b7efa120)
1 #include "OrcTestCommon.h"
2 #include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
3 #include "llvm/ExecutionEngine/Orc/JITLinkRedirectableSymbolManager.h"
4 #include "llvm/ExecutionEngine/Orc/JITLinkReentryTrampolines.h"
5 #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
6 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
7 #include "llvm/ExecutionEngine/Orc/LazyReexports.h"
8 #include "llvm/Testing/Support/Error.h"
9 #include "gtest/gtest.h"
10 
11 using namespace llvm;
12 using namespace llvm::orc;
13 
14 class LazyReexportsTest : public CoreAPIsBasedStandardTest {};
15 
16 static int dummyTarget() { return 42; }
17 
18 TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) {
19   // Create a callthrough manager for the host (if possible) and verify that
20   // a call to the lazy call-through:
21   // (1) Materializes the MU. This verifies that the symbol was looked up, and
22   //     that we didn't arrive at the target via some other path
23   // (2) Returns the expected value (which we take as proof that the call
24   //     reached the target).
25 
26   auto JTMB = JITTargetMachineBuilder::detectHost();
27 
28   // Bail out if we can not detect the host.
29   if (!JTMB) {
30     consumeError(JTMB.takeError());
31     GTEST_SKIP();
32   }
33 
34   // Bail out if we can not build a local call-through manager.
35   auto LCTM = createLocalLazyCallThroughManager(JTMB->getTargetTriple(), ES,
36                                                 ExecutorAddr());
37   if (!LCTM) {
38     consumeError(LCTM.takeError());
39     GTEST_SKIP();
40   }
41 
42   auto DummyTarget = ES.intern("DummyTarget");
43 
44   bool DummyTargetMaterialized = false;
45 
46   cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>(
47       SymbolFlagsMap({{DummyTarget, JITSymbolFlags::Exported}}),
48       [&](std::unique_ptr<MaterializationResponsibility> R) {
49         DummyTargetMaterialized = true;
50         // No dependencies registered, can't fail.
51         cantFail(R->notifyResolved({{DummyTarget,
52                                      {ExecutorAddr::fromPtr(&dummyTarget),
53                                       JITSymbolFlags::Exported}}}));
54         cantFail(R->notifyEmitted({}));
55       })));
56 
57   unsigned NotifyResolvedCount = 0;
58   auto NotifyResolved = [&](ExecutorAddr ResolvedAddr) {
59     ++NotifyResolvedCount;
60     return Error::success();
61   };
62 
63   auto CallThroughTrampoline = cantFail((*LCTM)->getCallThroughTrampoline(
64       JD, DummyTarget, std::move(NotifyResolved)));
65 
66   auto CTTPtr = CallThroughTrampoline.toPtr<int (*)()>();
67 
68   // Call twice to verify nothing unexpected happens on redundant calls.
69   auto Result = CTTPtr();
70   (void)CTTPtr();
71 
72   EXPECT_TRUE(DummyTargetMaterialized)
73       << "CallThrough did not materialize target";
74   EXPECT_EQ(NotifyResolvedCount, 1U)
75       << "CallThrough should have generated exactly one 'NotifyResolved' call";
76   EXPECT_EQ(Result, 42) << "Failed to call through to target";
77 }
78 
79 static void *noReentry(void *) { abort(); }
80 
81 TEST(JITLinkLazyReexportsTest, Basics) {
82   OrcNativeTarget::initialize();
83 
84   auto J = LLJITBuilder().create();
85   if (!J) {
86     dbgs() << toString(J.takeError()) << "\n";
87     // consumeError(J.takeError());
88     GTEST_SKIP();
89   }
90   if (!isa<ObjectLinkingLayer>((*J)->getObjLinkingLayer()))
91     GTEST_SKIP();
92 
93   auto &OLL = cast<ObjectLinkingLayer>((*J)->getObjLinkingLayer());
94 
95   auto RSMgr = JITLinkRedirectableSymbolManager::Create(OLL);
96   if (!RSMgr) {
97     dbgs() << "Boom for RSMgr\n";
98     consumeError(RSMgr.takeError());
99     GTEST_SKIP();
100   }
101 
102   auto &ES = (*J)->getExecutionSession();
103 
104   auto &JD = ES.createBareJITDylib("JD");
105   cantFail(JD.define(absoluteSymbols(
106       {{ES.intern("__orc_rt_reentry"),
107         {ExecutorAddr::fromPtr(&noReentry),
108          JITSymbolFlags::Exported | JITSymbolFlags::Callable}}})));
109 
110   auto LRMgr = createJITLinkLazyReexportsManager(OLL, **RSMgr, JD);
111   if (!LRMgr) {
112     dbgs() << "Boom for LRMgr\n";
113     consumeError(LRMgr.takeError());
114     GTEST_SKIP();
115   }
116 
117   auto Foo = ES.intern("foo");
118   auto Bar = ES.intern("bar");
119 
120   auto RT = JD.createResourceTracker();
121   cantFail(JD.define(
122       lazyReexports(
123           **LRMgr,
124           {{Foo, {Bar, JITSymbolFlags::Exported | JITSymbolFlags::Callable}}}),
125       RT));
126 
127   // Check flags after adding Foo -> Bar lazy reexport.
128   auto SF = cantFail(
129       ES.lookupFlags(LookupKind::Static, makeJITDylibSearchOrder(&JD),
130                      {{Foo, SymbolLookupFlags::WeaklyReferencedSymbol}}));
131   EXPECT_EQ(SF.size(), 1U);
132   EXPECT_TRUE(SF.count(Foo));
133   EXPECT_EQ(SF[Foo], JITSymbolFlags::Exported | JITSymbolFlags::Callable);
134 
135   // Remove reexport without running it.
136   if (auto Err = RT->remove()) {
137     EXPECT_THAT_ERROR(std::move(Err), Succeeded());
138     return;
139   }
140 
141   // Check flags after adding Foo -> Bar lazy reexport.
142   SF = cantFail(
143       ES.lookupFlags(LookupKind::Static, makeJITDylibSearchOrder(&JD),
144                      {{Foo, SymbolLookupFlags::WeaklyReferencedSymbol}}));
145   EXPECT_EQ(SF.size(), 0U);
146 }
147