1 | //===--- OrcCAPITest.cpp - Unit tests for the OrcJIT v2 C API ---*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm-c/Core.h" |
10 | #include "llvm-c/Error.h" |
11 | #include "llvm-c/LLJIT.h" |
12 | #include "llvm-c/LLJITUtils.h" |
13 | #include "llvm-c/Orc.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" |
17 | #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" |
18 | #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" |
19 | #include "llvm/IR/LLVMContext.h" |
20 | #include "llvm/IR/Module.h" |
21 | #include "llvm/IRReader/IRReader.h" |
22 | #include "llvm/Support/Error.h" |
23 | #include "llvm/Support/FormatVariadic.h" |
24 | #include "llvm/Support/SourceMgr.h" |
25 | #include "llvm/TargetParser/Triple.h" |
26 | #include "llvm/Testing/Support/Error.h" |
27 | #include <string> |
28 | |
29 | using namespace llvm; |
30 | using namespace llvm::orc; |
31 | |
32 | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ObjectLayer, LLVMOrcObjectLayerRef) |
33 | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThreadSafeModule, LLVMOrcThreadSafeModuleRef) |
34 | |
35 | // OrcCAPITestBase contains several helper methods and pointers for unit tests |
36 | // written for the LLVM-C API. It provides the following helpers: |
37 | // |
38 | // 1. Jit: an LLVMOrcLLJIT instance which is freed upon test exit |
39 | // 2. ExecutionSession: the LLVMOrcExecutionSession for the JIT |
40 | // 3. MainDylib: the main JITDylib for the LLJIT instance |
41 | // 4. materializationUnitFn: function pointer to an empty function, used for |
42 | // materialization unit testing |
43 | // 5. definitionGeneratorFn: function pointer for a basic |
44 | // LLVMOrcCAPIDefinitionGeneratorTryToGenerateFunction |
45 | // 6. createTestModule: helper method for creating a basic thread-safe-module |
46 | class OrcCAPITestBase : public testing::Test { |
47 | protected: |
48 | LLVMOrcLLJITRef Jit = nullptr; |
49 | LLVMOrcExecutionSessionRef ExecutionSession = nullptr; |
50 | LLVMOrcJITDylibRef MainDylib = nullptr; |
51 | |
52 | public: |
53 | static void SetUpTestCase() { |
54 | LLVMInitializeNativeTarget(); |
55 | LLVMInitializeNativeAsmParser(); |
56 | LLVMInitializeNativeAsmPrinter(); |
57 | |
58 | // Attempt to set up a JIT instance once to verify that we can. |
59 | LLVMOrcJITTargetMachineBuilderRef JTMB = nullptr; |
60 | if (LLVMErrorRef E = LLVMOrcJITTargetMachineBuilderDetectHost(Result: &JTMB)) { |
61 | // If setup fails then disable these tests. |
62 | LLVMConsumeError(Err: E); |
63 | TargetSupported = false; |
64 | return; |
65 | } |
66 | |
67 | // Capture the target triple. We'll use it for both verification that |
68 | // this target is *supposed* to be supported, and error messages in |
69 | // the case that it fails anyway. |
70 | char *TT = LLVMOrcJITTargetMachineBuilderGetTargetTriple(JTMB); |
71 | TargetTriple = TT; |
72 | LLVMDisposeMessage(Message: TT); |
73 | |
74 | if (!isSupported(Triple: TargetTriple)) { |
75 | // If this triple isn't supported then bail out. |
76 | TargetSupported = false; |
77 | LLVMOrcDisposeJITTargetMachineBuilder(JTMB); |
78 | return; |
79 | } |
80 | |
81 | LLVMOrcLLJITBuilderRef Builder = LLVMOrcCreateLLJITBuilder(); |
82 | LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(Builder, JTMB); |
83 | LLVMOrcLLJITRef J; |
84 | if (LLVMErrorRef E = LLVMOrcCreateLLJIT(Result: &J, Builder)) { |
85 | // If setup fails then disable these tests. |
86 | TargetSupported = false; |
87 | LLVMConsumeError(Err: E); |
88 | return; |
89 | } |
90 | |
91 | LLVMOrcDisposeLLJIT(J); |
92 | TargetSupported = true; |
93 | } |
94 | |
95 | void SetUp() override { |
96 | if (!TargetSupported) |
97 | GTEST_SKIP(); |
98 | |
99 | LLVMOrcJITTargetMachineBuilderRef JTMB = nullptr; |
100 | LLVMErrorRef E1 = LLVMOrcJITTargetMachineBuilderDetectHost(Result: &JTMB); |
101 | assert(E1 == LLVMErrorSuccess && "Expected call to detect host to succeed" ); |
102 | (void)E1; |
103 | |
104 | LLVMOrcLLJITBuilderRef Builder = LLVMOrcCreateLLJITBuilder(); |
105 | LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(Builder, JTMB); |
106 | LLVMErrorRef E2 = LLVMOrcCreateLLJIT(Result: &Jit, Builder); |
107 | assert(E2 == LLVMErrorSuccess && |
108 | "Expected call to create LLJIT to succeed" ); |
109 | (void)E2; |
110 | ExecutionSession = LLVMOrcLLJITGetExecutionSession(J: Jit); |
111 | MainDylib = LLVMOrcLLJITGetMainJITDylib(J: Jit); |
112 | } |
113 | void TearDown() override { |
114 | // Check whether Jit has already been torn down -- we allow clients to do |
115 | // this manually to check teardown behavior. |
116 | if (Jit) { |
117 | LLVMOrcDisposeLLJIT(J: Jit); |
118 | Jit = nullptr; |
119 | } |
120 | } |
121 | |
122 | protected: |
123 | static bool isSupported(StringRef Triple) { |
124 | // TODO: Print error messages in failure logs, use them to audit this list. |
125 | // Some architectures may be unsupportable or missing key components, but |
126 | // some may just be failing due to bugs in this testcase. |
127 | if (Triple.starts_with(Prefix: "armv7" ) || Triple.starts_with(Prefix: "armv8l" )) |
128 | return false; |
129 | return true; |
130 | } |
131 | |
132 | static void materializationUnitFn() {} |
133 | |
134 | // Stub definition generator, where all Names are materialized from the |
135 | // materializationUnitFn() test function and defined into the JIT Dylib |
136 | static LLVMErrorRef |
137 | definitionGeneratorFn(LLVMOrcDefinitionGeneratorRef G, void *Ctx, |
138 | LLVMOrcLookupStateRef *LS, LLVMOrcLookupKind K, |
139 | LLVMOrcJITDylibRef JD, LLVMOrcJITDylibLookupFlags F, |
140 | LLVMOrcCLookupSet Names, size_t NamesCount) { |
141 | for (size_t I = 0; I < NamesCount; I++) { |
142 | LLVMOrcCLookupSetElement Element = Names[I]; |
143 | LLVMOrcJITTargetAddress Addr = |
144 | (LLVMOrcJITTargetAddress)(&materializationUnitFn); |
145 | LLVMJITSymbolFlags Flags = {.GenericFlags: LLVMJITSymbolGenericFlagsWeak, .TargetFlags: 0}; |
146 | LLVMJITEvaluatedSymbol Sym = {.Address: Addr, .Flags: Flags}; |
147 | LLVMOrcRetainSymbolStringPoolEntry(S: Element.Name); |
148 | LLVMOrcCSymbolMapPair Pair = {.Name: Element.Name, .Sym: Sym}; |
149 | LLVMOrcCSymbolMapPair Pairs[] = {Pair}; |
150 | LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Syms: Pairs, NumPairs: 1); |
151 | LLVMErrorRef Err = LLVMOrcJITDylibDefine(JD, MU); |
152 | if (Err) |
153 | return Err; |
154 | } |
155 | return LLVMErrorSuccess; |
156 | } |
157 | |
158 | static Error createSMDiagnosticError(llvm::SMDiagnostic &Diag) { |
159 | std::string Msg; |
160 | { |
161 | raw_string_ostream OS(Msg); |
162 | Diag.print(ProgName: "" , S&: OS); |
163 | } |
164 | return make_error<StringError>(Args: std::move(Msg), Args: inconvertibleErrorCode()); |
165 | } |
166 | |
167 | // Create an LLVM IR module from the given StringRef. |
168 | static Expected<std::unique_ptr<Module>> |
169 | parseTestModule(LLVMContext &Ctx, StringRef Source, StringRef Name) { |
170 | assert(TargetSupported && |
171 | "Attempted to create module for unsupported target" ); |
172 | SMDiagnostic Err; |
173 | if (auto M = parseIR(Buffer: MemoryBufferRef(Source, Name), Err, Context&: Ctx)) |
174 | return std::move(M); |
175 | return createSMDiagnosticError(Diag&: Err); |
176 | } |
177 | |
178 | // returns the sum of its two parameters |
179 | static LLVMOrcThreadSafeModuleRef createTestModule(StringRef Source, |
180 | StringRef Name) { |
181 | auto Ctx = std::make_unique<LLVMContext>(); |
182 | auto M = cantFail(ValOrErr: parseTestModule(Ctx&: *Ctx, Source, Name)); |
183 | return wrap(P: new ThreadSafeModule(std::move(M), std::move(Ctx))); |
184 | } |
185 | |
186 | static LLVMMemoryBufferRef createTestObject(StringRef Source, |
187 | StringRef Name) { |
188 | auto Ctx = std::make_unique<LLVMContext>(); |
189 | auto M = cantFail(ValOrErr: parseTestModule(Ctx&: *Ctx, Source, Name)); |
190 | |
191 | auto JTMB = cantFail(ValOrErr: JITTargetMachineBuilder::detectHost()); |
192 | M->setDataLayout(cantFail(ValOrErr: JTMB.getDefaultDataLayoutForTarget())); |
193 | auto TM = cantFail(ValOrErr: JTMB.createTargetMachine()); |
194 | |
195 | SimpleCompiler SC(*TM); |
196 | auto ObjBuffer = cantFail(ValOrErr: SC(*M)); |
197 | return wrap(P: ObjBuffer.release()); |
198 | } |
199 | |
200 | static std::string TargetTriple; |
201 | static bool TargetSupported; |
202 | }; |
203 | |
204 | std::string OrcCAPITestBase::TargetTriple; |
205 | bool OrcCAPITestBase::TargetSupported = false; |
206 | |
207 | namespace { |
208 | |
209 | constexpr StringRef SumExample = |
210 | R"( |
211 | define i32 @sum(i32 %x, i32 %y) { |
212 | entry: |
213 | %r = add nsw i32 %x, %y |
214 | ret i32 %r |
215 | } |
216 | )" ; |
217 | |
218 | constexpr StringRef SumDebugExample = |
219 | R"( |
220 | define i32 @sum(i32 %x, i32 %y) { |
221 | entry: |
222 | %r = add nsw i32 %x, %y |
223 | ret i32 %r |
224 | } |
225 | !llvm.module.flags = !{!0} |
226 | !llvm.dbg.cu = !{!1} |
227 | !0 = !{i32 2, !"Debug Info Version", i32 3} |
228 | !1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, emissionKind: FullDebug) |
229 | !2 = !DIFile(filename: "sum.c", directory: "/tmp") |
230 | )" ; |
231 | |
232 | } // end anonymous namespace. |
233 | |
234 | // Consumes the given error ref and returns the string error message. |
235 | static std::string toString(LLVMErrorRef E) { |
236 | char *ErrMsg = LLVMGetErrorMessage(Err: E); |
237 | std::string Result(ErrMsg); |
238 | LLVMDisposeErrorMessage(ErrMsg); |
239 | return Result; |
240 | } |
241 | |
242 | TEST_F(OrcCAPITestBase, SymbolStringPoolUniquing) { |
243 | LLVMOrcSymbolStringPoolEntryRef E1 = |
244 | LLVMOrcExecutionSessionIntern(ES: ExecutionSession, Name: "aaa" ); |
245 | LLVMOrcSymbolStringPoolEntryRef E2 = |
246 | LLVMOrcExecutionSessionIntern(ES: ExecutionSession, Name: "aaa" ); |
247 | LLVMOrcSymbolStringPoolEntryRef E3 = |
248 | LLVMOrcExecutionSessionIntern(ES: ExecutionSession, Name: "bbb" ); |
249 | const char *SymbolName = LLVMOrcSymbolStringPoolEntryStr(S: E1); |
250 | ASSERT_EQ(E1, E2) << "String pool entries are not unique" ; |
251 | ASSERT_NE(E1, E3) << "Unique symbol pool entries are equal" ; |
252 | ASSERT_STREQ("aaa" , SymbolName) << "String value of symbol is not equal" ; |
253 | LLVMOrcReleaseSymbolStringPoolEntry(S: E1); |
254 | LLVMOrcReleaseSymbolStringPoolEntry(S: E2); |
255 | LLVMOrcReleaseSymbolStringPoolEntry(S: E3); |
256 | } |
257 | |
258 | TEST_F(OrcCAPITestBase, JITDylibLookup) { |
259 | LLVMOrcJITDylibRef DoesNotExist = |
260 | LLVMOrcExecutionSessionGetJITDylibByName(ES: ExecutionSession, Name: "test" ); |
261 | ASSERT_FALSE(!!DoesNotExist); |
262 | LLVMOrcJITDylibRef L1 = |
263 | LLVMOrcExecutionSessionCreateBareJITDylib(ES: ExecutionSession, Name: "test" ); |
264 | LLVMOrcJITDylibRef L2 = |
265 | LLVMOrcExecutionSessionGetJITDylibByName(ES: ExecutionSession, Name: "test" ); |
266 | ASSERT_EQ(L1, L2) << "Located JIT Dylib is not equal to original" ; |
267 | } |
268 | |
269 | TEST_F(OrcCAPITestBase, MaterializationUnitCreation) { |
270 | LLVMOrcSymbolStringPoolEntryRef Name = |
271 | LLVMOrcLLJITMangleAndIntern(J: Jit, UnmangledName: "test" ); |
272 | LLVMJITSymbolFlags Flags = {.GenericFlags: LLVMJITSymbolGenericFlagsWeak, .TargetFlags: 0}; |
273 | LLVMOrcJITTargetAddress Addr = |
274 | (LLVMOrcJITTargetAddress)(&materializationUnitFn); |
275 | LLVMJITEvaluatedSymbol Sym = {.Address: Addr, .Flags: Flags}; |
276 | LLVMOrcCSymbolMapPair Pair = {.Name: Name, .Sym: Sym}; |
277 | LLVMOrcCSymbolMapPair Pairs[] = {Pair}; |
278 | LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Syms: Pairs, NumPairs: 1); |
279 | if (LLVMErrorRef E = LLVMOrcJITDylibDefine(JD: MainDylib, MU)) |
280 | FAIL() << "Unexpected error while adding \"test\" symbol (triple = " |
281 | << TargetTriple << "): " << toString(E); |
282 | LLVMOrcJITTargetAddress OutAddr; |
283 | if (LLVMErrorRef E = LLVMOrcLLJITLookup(J: Jit, Result: &OutAddr, Name: "test" )) |
284 | FAIL() << "Failed to look up \"test\" symbol (triple = " << TargetTriple |
285 | << "): " << toString(E); |
286 | ASSERT_EQ(Addr, OutAddr); |
287 | } |
288 | |
289 | struct ExecutionSessionLookupHelper { |
290 | bool ExpectSuccess = true; |
291 | bool CallbackReceived = false; |
292 | size_t NumExpectedPairs; |
293 | LLVMOrcCSymbolMapPair *ExpectedMapping; |
294 | }; |
295 | |
296 | static void executionSessionLookupHandlerCallback(LLVMErrorRef Err, |
297 | LLVMOrcCSymbolMapPairs Result, |
298 | size_t NumPairs, |
299 | void *RawCtx) { |
300 | auto *Ctx = static_cast<ExecutionSessionLookupHelper *>(RawCtx); |
301 | Ctx->CallbackReceived = true; |
302 | if (Ctx->ExpectSuccess) { |
303 | EXPECT_THAT_ERROR(unwrap(Err), Succeeded()); |
304 | EXPECT_EQ(NumPairs, Ctx->NumExpectedPairs) |
305 | << "Expected " << Ctx->NumExpectedPairs << " entries in result, got " |
306 | << NumPairs; |
307 | auto ExpectedMappingEnd = Ctx->ExpectedMapping + Ctx->NumExpectedPairs; |
308 | for (unsigned I = 0; I != NumPairs; ++I) { |
309 | auto J = |
310 | std::find_if(first: Ctx->ExpectedMapping, last: ExpectedMappingEnd, |
311 | pred: [N = Result[I].Name](const LLVMOrcCSymbolMapPair &Val) { |
312 | return Val.Name == N; |
313 | }); |
314 | EXPECT_NE(J, ExpectedMappingEnd) |
315 | << "Missing symbol \"" |
316 | << LLVMOrcSymbolStringPoolEntryStr(S: Result[I].Name) << "\"" ; |
317 | if (J != ExpectedMappingEnd) { |
318 | EXPECT_EQ(Result[I].Sym.Address, J->Sym.Address) |
319 | << "Result map for \"" << Result[I].Name |
320 | << "\" differs from expected value: " |
321 | << formatv(Fmt: "{0:x} vs {1:x}" , Vals&: Result[I].Sym.Address, Vals&: J->Sym.Address); |
322 | } |
323 | } |
324 | } else |
325 | EXPECT_THAT_ERROR(unwrap(Err), Failed()); |
326 | } |
327 | |
328 | TEST_F(OrcCAPITestBase, ExecutionSessionLookup_Success) { |
329 | // Test a successful generic lookup. We will look up three symbols over two |
330 | // JITDylibs: { "Foo" (Required), "Bar" (Weakly-ref), "Baz" (Required) } over |
331 | // { MainJITDylib (Exported-only), ExtraJD (All symbols) }. |
332 | // |
333 | // Foo will be defined as exported in MainJD. |
334 | // Bar will be defined as non-exported in MainJD. |
335 | // Baz will be defined as non-exported in ExtraJD. |
336 | // |
337 | // This will require (1) that we find the regular exported symbol Foo in |
338 | // MainJD, (2) that we *don't* find the non-exported symbol Bar in MainJD |
339 | // but also don't error (since it's weakly referenced), and (3) that we |
340 | // find the non-exported symbol Baz in ExtraJD (since we're searching all |
341 | // symbols in ExtraJD). |
342 | |
343 | ExecutionSessionLookupHelper H; |
344 | LLVMOrcSymbolStringPoolEntryRef Foo = LLVMOrcLLJITMangleAndIntern(J: Jit, UnmangledName: "Foo" ); |
345 | LLVMOrcSymbolStringPoolEntryRef Bar = LLVMOrcLLJITMangleAndIntern(J: Jit, UnmangledName: "Bar" ); |
346 | LLVMOrcSymbolStringPoolEntryRef Baz = LLVMOrcLLJITMangleAndIntern(J: Jit, UnmangledName: "Baz" ); |
347 | |
348 | // Create ExtraJD. |
349 | LLVMOrcJITDylibRef = nullptr; |
350 | if (auto E = LLVMOrcExecutionSessionCreateJITDylib(ES: ExecutionSession, Result: &ExtraJD, |
351 | Name: "ExtraJD" )) { |
352 | FAIL() << "Unexpected error while creating JITDylib \"ExtraJD\" (triple = " |
353 | << TargetTriple << "): " << toString(E); |
354 | return; |
355 | } |
356 | |
357 | // Add exported symbols "Foo" and "Bar" to Main JITDylib. |
358 | LLVMOrcRetainSymbolStringPoolEntry(S: Foo); |
359 | LLVMOrcRetainSymbolStringPoolEntry(S: Bar); |
360 | LLVMOrcCSymbolMapPair MainJDPairs[] = { |
361 | {.Name: Foo, .Sym: {.Address: 0x1, .Flags: {.GenericFlags: LLVMJITSymbolGenericFlagsExported, .TargetFlags: 0}}}, |
362 | {.Name: Bar, .Sym: {.Address: 0x2, .Flags: {.GenericFlags: LLVMJITSymbolGenericFlagsNone, .TargetFlags: 0}}}}; |
363 | LLVMOrcMaterializationUnitRef MainJDMU = |
364 | LLVMOrcAbsoluteSymbols(Syms: MainJDPairs, NumPairs: 2); |
365 | if (LLVMErrorRef E = LLVMOrcJITDylibDefine(JD: MainDylib, MU: MainJDMU)) |
366 | FAIL() << "Unexpected error while adding MainDylib symbols (triple = " |
367 | << TargetTriple << "): " << toString(E); |
368 | |
369 | // Add non-exported symbol "Baz" to ExtraJD. |
370 | LLVMOrcRetainSymbolStringPoolEntry(S: Baz); |
371 | LLVMOrcCSymbolMapPair [] = { |
372 | {.Name: Baz, .Sym: {.Address: 0x3, .Flags: {.GenericFlags: LLVMJITSymbolGenericFlagsNone, .TargetFlags: 0}}}}; |
373 | LLVMOrcMaterializationUnitRef = |
374 | LLVMOrcAbsoluteSymbols(Syms: ExtraJDPairs, NumPairs: 1); |
375 | if (LLVMErrorRef E = LLVMOrcJITDylibDefine(JD: ExtraJD, MU: ExtraJDMU)) |
376 | FAIL() << "Unexpected error while adding ExtraJD symbols (triple = " |
377 | << TargetTriple << "): " << toString(E); |
378 | |
379 | // Create expected mapping for result: |
380 | LLVMOrcCSymbolMapPair ExpectedMapping[] = { |
381 | {.Name: Foo, .Sym: {.Address: 0x1, .Flags: {.GenericFlags: LLVMJITSymbolGenericFlagsExported, .TargetFlags: 0}}}, |
382 | {.Name: Baz, .Sym: {.Address: 0x3, .Flags: {.GenericFlags: LLVMJITSymbolGenericFlagsNone, .TargetFlags: 0}}}}; |
383 | H.ExpectedMapping = ExpectedMapping; |
384 | H.NumExpectedPairs = 2; |
385 | |
386 | // Issue the lookup. We're using the default same-thread dispatch, so the |
387 | // handler should have run by the time we return from this call. |
388 | LLVMOrcCJITDylibSearchOrderElement SO[] = { |
389 | {.JD: MainDylib, .JDLookupFlags: LLVMOrcJITDylibLookupFlagsMatchExportedSymbolsOnly}, |
390 | {.JD: ExtraJD, .JDLookupFlags: LLVMOrcJITDylibLookupFlagsMatchAllSymbols}}; |
391 | |
392 | LLVMOrcRetainSymbolStringPoolEntry(S: Foo); |
393 | LLVMOrcRetainSymbolStringPoolEntry(S: Bar); |
394 | LLVMOrcRetainSymbolStringPoolEntry(S: Baz); |
395 | LLVMOrcCLookupSetElement LS[] = { |
396 | {.Name: Foo, .LookupFlags: LLVMOrcSymbolLookupFlagsRequiredSymbol}, |
397 | {.Name: Bar, .LookupFlags: LLVMOrcSymbolLookupFlagsWeaklyReferencedSymbol}, |
398 | {.Name: Baz, .LookupFlags: LLVMOrcSymbolLookupFlagsRequiredSymbol}}; |
399 | LLVMOrcExecutionSessionLookup(ES: ExecutionSession, K: LLVMOrcLookupKindStatic, SearchOrder: SO, |
400 | SearchOrderSize: 2, Symbols: LS, SymbolsSize: 3, HandleResult: executionSessionLookupHandlerCallback, |
401 | Ctx: &H); |
402 | |
403 | EXPECT_TRUE(H.CallbackReceived) << "Lookup callback never received" ; |
404 | |
405 | // Release our local string ptrs. |
406 | LLVMOrcReleaseSymbolStringPoolEntry(S: Baz); |
407 | LLVMOrcReleaseSymbolStringPoolEntry(S: Bar); |
408 | LLVMOrcReleaseSymbolStringPoolEntry(S: Foo); |
409 | } |
410 | |
411 | TEST_F(OrcCAPITestBase, ExecutionSessionLookup_Failure) { |
412 | // Test generic lookup failure case. We will look up a symbol in MainDylib |
413 | // without defining it. We expect this to result in a symbol-not-found error. |
414 | |
415 | ExecutionSessionLookupHelper H; |
416 | H.ExpectSuccess = false; |
417 | |
418 | LLVMOrcCJITDylibSearchOrderElement SO[] = { |
419 | {.JD: MainDylib, .JDLookupFlags: LLVMOrcJITDylibLookupFlagsMatchExportedSymbolsOnly}}; |
420 | LLVMOrcCLookupSetElement LS[] = {{.Name: LLVMOrcLLJITMangleAndIntern(J: Jit, UnmangledName: "Foo" ), |
421 | .LookupFlags: LLVMOrcSymbolLookupFlagsRequiredSymbol}}; |
422 | LLVMOrcExecutionSessionLookup(ES: ExecutionSession, K: LLVMOrcLookupKindStatic, SearchOrder: SO, |
423 | SearchOrderSize: 1, Symbols: LS, SymbolsSize: 1, HandleResult: executionSessionLookupHandlerCallback, |
424 | Ctx: &H); |
425 | |
426 | EXPECT_TRUE(H.CallbackReceived) << "Lookup callback never received" ; |
427 | } |
428 | |
429 | TEST_F(OrcCAPITestBase, DefinitionGenerators) { |
430 | LLVMOrcDefinitionGeneratorRef Gen = |
431 | LLVMOrcCreateCustomCAPIDefinitionGenerator(F: &definitionGeneratorFn, |
432 | Ctx: nullptr, Dispose: nullptr); |
433 | LLVMOrcJITDylibAddGenerator(JD: MainDylib, DG: Gen); |
434 | LLVMOrcJITTargetAddress OutAddr; |
435 | if (LLVMErrorRef E = LLVMOrcLLJITLookup(J: Jit, Result: &OutAddr, Name: "test" )) |
436 | FAIL() << "The DefinitionGenerator did not create symbol \"test\" " |
437 | << "(triple = " << TargetTriple << "): " << toString(E); |
438 | LLVMOrcJITTargetAddress ExpectedAddr = |
439 | (LLVMOrcJITTargetAddress)(&materializationUnitFn); |
440 | ASSERT_EQ(ExpectedAddr, OutAddr); |
441 | } |
442 | |
443 | #if defined(_AIX) |
444 | TEST_F(OrcCAPITestBase, DISABLED_ResourceTrackerDefinitionLifetime) { |
445 | #else |
446 | TEST_F(OrcCAPITestBase, ResourceTrackerDefinitionLifetime) { |
447 | #endif |
448 | // This test case ensures that all symbols loaded into a JITDylib with a |
449 | // ResourceTracker attached are cleared from the JITDylib once the RT is |
450 | // removed. |
451 | LLVMOrcResourceTrackerRef RT = |
452 | LLVMOrcJITDylibCreateResourceTracker(JD: MainDylib); |
453 | LLVMOrcThreadSafeModuleRef TSM = createTestModule(Source: SumExample, Name: "sum.ll" ); |
454 | if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModuleWithRT(J: Jit, JD: RT, TSM)) |
455 | FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple |
456 | << "): " << toString(E); |
457 | LLVMOrcJITTargetAddress TestFnAddr; |
458 | if (LLVMErrorRef E = LLVMOrcLLJITLookup(J: Jit, Result: &TestFnAddr, Name: "sum" )) |
459 | FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple |
460 | << "): " << toString(E); |
461 | ASSERT_TRUE(!!TestFnAddr); |
462 | LLVMOrcResourceTrackerRemove(RT); |
463 | LLVMOrcJITTargetAddress OutAddr; |
464 | LLVMErrorRef Err = LLVMOrcLLJITLookup(J: Jit, Result: &OutAddr, Name: "sum" ); |
465 | ASSERT_TRUE(Err); |
466 | LLVMConsumeError(Err); |
467 | |
468 | ASSERT_FALSE(OutAddr); |
469 | LLVMOrcReleaseResourceTracker(RT); |
470 | } |
471 | |
472 | #if defined(_AIX) |
473 | TEST_F(OrcCAPITestBase, DISABLED_ResourceTrackerTransfer) { |
474 | #else |
475 | TEST_F(OrcCAPITestBase, ResourceTrackerTransfer) { |
476 | #endif |
477 | LLVMOrcResourceTrackerRef DefaultRT = |
478 | LLVMOrcJITDylibGetDefaultResourceTracker(JD: MainDylib); |
479 | LLVMOrcResourceTrackerRef RT2 = |
480 | LLVMOrcJITDylibCreateResourceTracker(JD: MainDylib); |
481 | LLVMOrcThreadSafeModuleRef TSM = createTestModule(Source: SumExample, Name: "sum.ll" ); |
482 | if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModuleWithRT(J: Jit, JD: DefaultRT, TSM)) |
483 | FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple |
484 | << "): " << toString(E); |
485 | LLVMOrcJITTargetAddress Addr; |
486 | if (LLVMErrorRef E = LLVMOrcLLJITLookup(J: Jit, Result: &Addr, Name: "sum" )) |
487 | FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple |
488 | << "): " << toString(E); |
489 | LLVMOrcResourceTrackerTransferTo(SrcRT: DefaultRT, DstRT: RT2); |
490 | LLVMErrorRef Err = LLVMOrcLLJITLookup(J: Jit, Result: &Addr, Name: "sum" ); |
491 | ASSERT_FALSE(Err); |
492 | LLVMOrcReleaseResourceTracker(RT: RT2); |
493 | } |
494 | |
495 | #if defined(_AIX) |
496 | TEST_F(OrcCAPITestBase, DISABLED_AddObjectBuffer) { |
497 | #else |
498 | TEST_F(OrcCAPITestBase, AddObjectBuffer) { |
499 | #endif |
500 | LLVMOrcObjectLayerRef ObjLinkingLayer = LLVMOrcLLJITGetObjLinkingLayer(J: Jit); |
501 | LLVMMemoryBufferRef ObjBuffer = createTestObject(Source: SumExample, Name: "sum.ll" ); |
502 | |
503 | if (LLVMErrorRef E = LLVMOrcObjectLayerAddObjectFile(ObjLayer: ObjLinkingLayer, |
504 | JD: MainDylib, ObjBuffer)) |
505 | FAIL() << "Failed to add object file to ObjLinkingLayer (triple = " |
506 | << TargetTriple << "): " << toString(E); |
507 | |
508 | LLVMOrcJITTargetAddress SumAddr; |
509 | if (LLVMErrorRef E = LLVMOrcLLJITLookup(J: Jit, Result: &SumAddr, Name: "sum" )) |
510 | FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple |
511 | << "): " << toString(E); |
512 | ASSERT_TRUE(!!SumAddr); |
513 | } |
514 | |
515 | // JITLink debug support plugins put information about JITed code in this GDB |
516 | // JIT Interface global from OrcTargetProcess. |
517 | extern "C" struct jit_descriptor __jit_debug_descriptor; |
518 | |
519 | static void *findLastDebugDescriptorEntryPtr() { |
520 | struct jit_code_entry *Last = __jit_debug_descriptor.first_entry; |
521 | while (Last && Last->next_entry) |
522 | Last = Last->next_entry; |
523 | return Last; |
524 | } |
525 | |
526 | #if defined(_AIX) or not(defined(__ELF__) or defined(__MACH__)) |
527 | TEST_F(OrcCAPITestBase, DISABLED_EnableDebugSupport) { |
528 | #else |
529 | static LLVM_ATTRIBUTE_USED void linkComponents() { |
530 | errs() << "Linking in runtime functions\n" |
531 | << (void *)&llvm_orc_registerJITLoaderGDBWrapper << '\n' |
532 | << (void *)&llvm_orc_registerJITLoaderGDBAllocAction << '\n'; |
533 | } |
534 | TEST_F(OrcCAPITestBase, EnableDebugSupport) { |
535 | #endif |
536 | void *Before = findLastDebugDescriptorEntryPtr(); |
537 | LLVMMemoryBufferRef ObjBuffer = createTestObject(Source: SumDebugExample, Name: "sum.ll" ); |
538 | LLVMOrcObjectLayerRef ObjLayer = LLVMOrcLLJITGetObjLinkingLayer(J: Jit); |
539 | |
540 | if (LLVMErrorRef E = LLVMOrcLLJITEnableDebugSupport(J: Jit)) { |
541 | EXPECT_FALSE(isa<ObjectLinkingLayer>(unwrap(ObjLayer))) |
542 | << "Error testing LLJIT debug support " |
543 | << "(triple = " << TargetTriple << "): " << toString(E); |
544 | GTEST_SKIP() << "LLJIT C bindings provide debug support only for JITLink" ; |
545 | } |
546 | |
547 | if (LLVMErrorRef E = |
548 | LLVMOrcObjectLayerAddObjectFile(ObjLayer, JD: MainDylib, ObjBuffer)) |
549 | FAIL() << "Failed to add object file to ObjLinkingLayer (triple = " |
550 | << TargetTriple << "): " << toString(E); |
551 | |
552 | LLVMOrcJITTargetAddress SumAddr; |
553 | if (LLVMErrorRef E = LLVMOrcLLJITLookup(J: Jit, Result: &SumAddr, Name: "sum" )) |
554 | FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple |
555 | << "): " << toString(E); |
556 | |
557 | void *After = findLastDebugDescriptorEntryPtr(); |
558 | ASSERT_NE(Before, After); |
559 | } |
560 | |
561 | #if defined(_AIX) |
562 | TEST_F(OrcCAPITestBase, DISABLED_ExecutionTest) { |
563 | #else |
564 | TEST_F(OrcCAPITestBase, ExecutionTest) { |
565 | #endif |
566 | using SumFunctionType = int32_t (*)(int32_t, int32_t); |
567 | |
568 | // This test performs OrcJIT compilation of a simple sum module |
569 | LLVMInitializeNativeAsmPrinter(); |
570 | LLVMOrcThreadSafeModuleRef TSM = createTestModule(Source: SumExample, Name: "sum.ll" ); |
571 | if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModule(J: Jit, JD: MainDylib, TSM)) |
572 | FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple |
573 | << ")" << toString(E); |
574 | LLVMOrcJITTargetAddress TestFnAddr; |
575 | if (LLVMErrorRef E = LLVMOrcLLJITLookup(J: Jit, Result: &TestFnAddr, Name: "sum" )) |
576 | FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple |
577 | << "): " << toString(E); |
578 | auto *SumFn = (SumFunctionType)(TestFnAddr); |
579 | int32_t Result = SumFn(1, 1); |
580 | ASSERT_EQ(2, Result); |
581 | } |
582 | |
583 | void Destroy(void *Ctx) {} |
584 | |
585 | void TargetFn() {} |
586 | |
587 | void Materialize(void *Ctx, LLVMOrcMaterializationResponsibilityRef MR) { |
588 | LLVMOrcJITDylibRef JD = |
589 | LLVMOrcMaterializationResponsibilityGetTargetDylib(MR); |
590 | ASSERT_TRUE(!!JD); |
591 | |
592 | LLVMOrcExecutionSessionRef ES = |
593 | LLVMOrcMaterializationResponsibilityGetExecutionSession(MR); |
594 | ASSERT_TRUE(!!ES); |
595 | |
596 | LLVMOrcSymbolStringPoolEntryRef InitSym = |
597 | LLVMOrcMaterializationResponsibilityGetInitializerSymbol(MR); |
598 | ASSERT_TRUE(!InitSym); |
599 | |
600 | size_t NumSymbols; |
601 | LLVMOrcCSymbolFlagsMapPairs Symbols = |
602 | LLVMOrcMaterializationResponsibilityGetSymbols(MR, NumPairs: &NumSymbols); |
603 | |
604 | ASSERT_TRUE(!!Symbols); |
605 | ASSERT_EQ(NumSymbols, (size_t)1); |
606 | |
607 | LLVMOrcSymbolStringPoolEntryRef *RequestedSymbols = |
608 | LLVMOrcMaterializationResponsibilityGetRequestedSymbols(MR, NumSymbols: &NumSymbols); |
609 | |
610 | ASSERT_TRUE(!!RequestedSymbols); |
611 | ASSERT_EQ(NumSymbols, (size_t)1); |
612 | |
613 | LLVMOrcCSymbolFlagsMapPair TargetSym = Symbols[0]; |
614 | |
615 | ASSERT_EQ(RequestedSymbols[0], TargetSym.Name); |
616 | LLVMOrcRetainSymbolStringPoolEntry(S: TargetSym.Name); |
617 | |
618 | LLVMOrcDisposeCSymbolFlagsMap(Pairs: Symbols); |
619 | LLVMOrcDisposeSymbols(Symbols: RequestedSymbols); |
620 | |
621 | LLVMOrcJITTargetAddress Addr = (LLVMOrcJITTargetAddress)(&TargetFn); |
622 | |
623 | LLVMJITSymbolFlags Flags = { |
624 | .GenericFlags: LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, .TargetFlags: 0}; |
625 | ASSERT_EQ(TargetSym.Flags.GenericFlags, Flags.GenericFlags); |
626 | ASSERT_EQ(TargetSym.Flags.TargetFlags, Flags.TargetFlags); |
627 | |
628 | LLVMJITEvaluatedSymbol Sym = {.Address: Addr, .Flags: Flags}; |
629 | |
630 | LLVMOrcLLJITRef J = (LLVMOrcLLJITRef)Ctx; |
631 | |
632 | LLVMOrcSymbolStringPoolEntryRef OtherSymbol = |
633 | LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "other" ); |
634 | LLVMOrcSymbolStringPoolEntryRef DependencySymbol = |
635 | LLVMOrcLLJITMangleAndIntern(J, UnmangledName: "dependency" ); |
636 | |
637 | LLVMOrcRetainSymbolStringPoolEntry(S: OtherSymbol); |
638 | LLVMOrcRetainSymbolStringPoolEntry(S: DependencySymbol); |
639 | LLVMOrcCSymbolFlagsMapPair NewSymbols[] = { |
640 | {.Name: OtherSymbol, .Flags: Flags}, |
641 | {.Name: DependencySymbol, .Flags: Flags}, |
642 | }; |
643 | LLVMOrcMaterializationResponsibilityDefineMaterializing(MR, Pairs: NewSymbols, NumPairs: 2); |
644 | |
645 | LLVMOrcRetainSymbolStringPoolEntry(S: OtherSymbol); |
646 | LLVMOrcMaterializationResponsibilityRef OtherMR = NULL; |
647 | { |
648 | LLVMErrorRef Err = LLVMOrcMaterializationResponsibilityDelegate( |
649 | MR, Symbols: &OtherSymbol, NumSymbols: 1, Result: &OtherMR); |
650 | if (Err) { |
651 | char *ErrMsg = LLVMGetErrorMessage(Err); |
652 | fprintf(stderr, format: "Error: %s\n" , ErrMsg); |
653 | LLVMDisposeErrorMessage(ErrMsg); |
654 | LLVMOrcMaterializationResponsibilityFailMaterialization(MR); |
655 | LLVMOrcDisposeMaterializationResponsibility(MR); |
656 | return; |
657 | } |
658 | } |
659 | assert(OtherMR); |
660 | |
661 | LLVMOrcCSymbolMapPair OtherPair = {.Name: OtherSymbol, .Sym: Sym}; |
662 | LLVMOrcMaterializationUnitRef OtherMU = LLVMOrcAbsoluteSymbols(Syms: &OtherPair, NumPairs: 1); |
663 | // OtherSymbol is no longer owned by us |
664 | { |
665 | LLVMErrorRef Err = |
666 | LLVMOrcMaterializationResponsibilityReplace(MR: OtherMR, MU: OtherMU); |
667 | if (Err) { |
668 | char *ErrMsg = LLVMGetErrorMessage(Err); |
669 | fprintf(stderr, format: "Error: %s\n" , ErrMsg); |
670 | LLVMDisposeErrorMessage(ErrMsg); |
671 | |
672 | LLVMOrcMaterializationResponsibilityFailMaterialization(MR: OtherMR); |
673 | LLVMOrcMaterializationResponsibilityFailMaterialization(MR); |
674 | |
675 | LLVMOrcDisposeMaterializationResponsibility(MR: OtherMR); |
676 | LLVMOrcDisposeMaterializationResponsibility(MR); |
677 | LLVMOrcDisposeMaterializationUnit(MU: OtherMU); |
678 | return; |
679 | } |
680 | } |
681 | LLVMOrcDisposeMaterializationResponsibility(MR: OtherMR); |
682 | |
683 | // FIXME: Implement async lookup |
684 | LLVMOrcRetainSymbolStringPoolEntry(S: DependencySymbol); |
685 | LLVMOrcCSymbolMapPair Pair = {.Name: DependencySymbol, .Sym: Sym}; |
686 | LLVMOrcMaterializationResponsibilityNotifyResolved(MR, Symbols: &Pair, NumPairs: 1); |
687 | // DependencySymbol no longer owned by us |
688 | |
689 | Pair = {.Name: TargetSym.Name, .Sym: Sym}; |
690 | LLVMOrcMaterializationResponsibilityNotifyResolved(MR, Symbols: &Pair, NumPairs: 1); |
691 | |
692 | LLVMOrcRetainSymbolStringPoolEntry(S: TargetSym.Name); |
693 | LLVMOrcCDependenceMapPair Dependency = {.JD: JD, .Names: {.Symbols: &DependencySymbol, .Length: 1}}; |
694 | LLVMOrcCSymbolDependenceGroup DependenceSet = { |
695 | /*.Symbols = */ {/*.Symbols = */ &TargetSym.Name, /* .Length = */ 1}, |
696 | /* .Dependencies = */ &Dependency, |
697 | /* .NumDependencies = */ 1}; |
698 | |
699 | LLVMOrcMaterializationResponsibilityNotifyEmitted(MR, SymbolDepGroups: &DependenceSet, NumSymbolDepGroups: 1); |
700 | LLVMOrcDisposeMaterializationResponsibility(MR); |
701 | } |
702 | |
703 | TEST_F(OrcCAPITestBase, MaterializationResponsibility) { |
704 | LLVMJITSymbolFlags Flags = { |
705 | .GenericFlags: LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlagsCallable, .TargetFlags: 0}; |
706 | LLVMOrcCSymbolFlagsMapPair Sym = {.Name: LLVMOrcLLJITMangleAndIntern(J: Jit, UnmangledName: "foo" ), |
707 | .Flags: Flags}; |
708 | |
709 | LLVMOrcMaterializationUnitRef MU = LLVMOrcCreateCustomMaterializationUnit( |
710 | Name: "MU" , Ctx: (void *)Jit, Syms: &Sym, NumSyms: 1, NULL, Materialize: &Materialize, NULL, Destroy: &Destroy); |
711 | LLVMOrcJITDylibRef JD = LLVMOrcLLJITGetMainJITDylib(J: Jit); |
712 | LLVMOrcJITDylibDefine(JD, MU); |
713 | |
714 | LLVMOrcJITTargetAddress Addr; |
715 | if (LLVMErrorRef Err = LLVMOrcLLJITLookup(J: Jit, Result: &Addr, Name: "foo" )) { |
716 | FAIL() << "foo was not materialized " << toString(E: Err); |
717 | } |
718 | ASSERT_TRUE(!!Addr); |
719 | ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn); |
720 | |
721 | if (LLVMErrorRef Err = LLVMOrcLLJITLookup(J: Jit, Result: &Addr, Name: "other" )) { |
722 | FAIL() << "other was not materialized " << toString(E: Err); |
723 | } |
724 | ASSERT_TRUE(!!Addr); |
725 | ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn); |
726 | |
727 | if (LLVMErrorRef Err = LLVMOrcLLJITLookup(J: Jit, Result: &Addr, Name: "dependency" )) { |
728 | FAIL() << "dependency was not materialized " << toString(E: Err); |
729 | } |
730 | ASSERT_TRUE(!!Addr); |
731 | ASSERT_EQ(Addr, (LLVMOrcJITTargetAddress)&TargetFn); |
732 | } |
733 | |
734 | struct SuspendedLookupContext { |
735 | std::function<void()> AsyncWork; |
736 | LLVMOrcSymbolStringPoolEntryRef NameToGenerate; |
737 | JITTargetAddress AddrToGenerate; |
738 | |
739 | bool Disposed = false; |
740 | bool QueryCompleted = true; |
741 | }; |
742 | |
743 | static LLVMErrorRef TryToGenerateWithSuspendedLookup( |
744 | LLVMOrcDefinitionGeneratorRef GeneratorObj, void *RawCtx, |
745 | LLVMOrcLookupStateRef *LookupState, LLVMOrcLookupKind Kind, |
746 | LLVMOrcJITDylibRef JD, LLVMOrcJITDylibLookupFlags JDLookupFlags, |
747 | LLVMOrcCLookupSet LookupSet, size_t LookupSetSize) { |
748 | |
749 | auto *Ctx = static_cast<SuspendedLookupContext *>(RawCtx); |
750 | |
751 | assert(LookupSetSize == 1); |
752 | assert(LookupSet[0].Name == Ctx->NameToGenerate); |
753 | |
754 | LLVMJITEvaluatedSymbol Sym = {.Address: 0x1234, .Flags: {.GenericFlags: LLVMJITSymbolGenericFlagsExported, .TargetFlags: 0}}; |
755 | LLVMOrcRetainSymbolStringPoolEntry(S: LookupSet[0].Name); |
756 | LLVMOrcCSymbolMapPair Pair = {.Name: LookupSet[0].Name, .Sym: Sym}; |
757 | LLVMOrcCSymbolMapPair Pairs[] = {Pair}; |
758 | LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Syms: Pairs, NumPairs: 1); |
759 | |
760 | // Capture and reset LookupState to suspend the lookup. We'll continue it in |
761 | // the SuspendedLookup testcase below. |
762 | Ctx->AsyncWork = [LS = *LookupState, JD, MU]() { |
763 | LLVMErrorRef Err = LLVMOrcJITDylibDefine(JD, MU); |
764 | LLVMOrcLookupStateContinueLookup(S: LS, Err); |
765 | }; |
766 | *LookupState = nullptr; |
767 | return LLVMErrorSuccess; |
768 | } |
769 | |
770 | static void DisposeSuspendedLookupContext(void *Ctx) { |
771 | static_cast<SuspendedLookupContext *>(Ctx)->Disposed = true; |
772 | } |
773 | |
774 | static void |
775 | suspendLookupTestLookupHandlerCallback(LLVMErrorRef Err, |
776 | LLVMOrcCSymbolMapPairs Result, |
777 | size_t NumPairs, void *RawCtx) { |
778 | if (Err) { |
779 | FAIL() << "Suspended DefinitionGenerator did not create symbol \"foo\": " |
780 | << toString(E: Err); |
781 | return; |
782 | } |
783 | |
784 | EXPECT_EQ(NumPairs, 1U) |
785 | << "Unexpected number of result entries: expected 1, got " << NumPairs; |
786 | |
787 | auto *Ctx = static_cast<SuspendedLookupContext *>(RawCtx); |
788 | EXPECT_EQ(Result[0].Name, Ctx->NameToGenerate); |
789 | EXPECT_EQ(Result[0].Sym.Address, Ctx->AddrToGenerate); |
790 | |
791 | Ctx->QueryCompleted = true; |
792 | } |
793 | |
794 | TEST_F(OrcCAPITestBase, SuspendedLookup) { |
795 | // Test that we can suspend lookup in a custom generator. |
796 | SuspendedLookupContext Ctx; |
797 | Ctx.NameToGenerate = LLVMOrcLLJITMangleAndIntern(J: Jit, UnmangledName: "foo" ); |
798 | Ctx.AddrToGenerate = 0x1234; |
799 | |
800 | // Add generator. |
801 | LLVMOrcJITDylibAddGenerator(JD: MainDylib, |
802 | DG: LLVMOrcCreateCustomCAPIDefinitionGenerator( |
803 | F: &TryToGenerateWithSuspendedLookup, Ctx: &Ctx, |
804 | Dispose: DisposeSuspendedLookupContext)); |
805 | |
806 | // Expect no work to do before the lookup. |
807 | EXPECT_FALSE(Ctx.AsyncWork) << "Unexpected generator work before lookup" ; |
808 | |
809 | // Issue lookup. This should trigger the generator, but generation should |
810 | // be suspended. |
811 | LLVMOrcCJITDylibSearchOrderElement SO[] = { |
812 | {.JD: MainDylib, .JDLookupFlags: LLVMOrcJITDylibLookupFlagsMatchExportedSymbolsOnly}}; |
813 | LLVMOrcRetainSymbolStringPoolEntry(S: Ctx.NameToGenerate); |
814 | LLVMOrcCLookupSetElement LS[] = { |
815 | {.Name: Ctx.NameToGenerate, .LookupFlags: LLVMOrcSymbolLookupFlagsRequiredSymbol}}; |
816 | LLVMOrcExecutionSessionLookup(ES: ExecutionSession, K: LLVMOrcLookupKindStatic, SearchOrder: SO, |
817 | SearchOrderSize: 1, Symbols: LS, SymbolsSize: 1, |
818 | HandleResult: suspendLookupTestLookupHandlerCallback, Ctx: &Ctx); |
819 | |
820 | // Expect that we now have generator work to do. |
821 | EXPECT_TRUE(Ctx.AsyncWork) |
822 | << "Failed to generator (or failed to suspend generator)" ; |
823 | |
824 | // Do the work. This should allow the query to complete. |
825 | Ctx.AsyncWork(); |
826 | |
827 | // Check that the query completed. |
828 | EXPECT_TRUE(Ctx.QueryCompleted); |
829 | |
830 | // Release our local copy of the string. |
831 | LLVMOrcReleaseSymbolStringPoolEntry(S: Ctx.NameToGenerate); |
832 | |
833 | // Explicitly tear down the JIT. |
834 | LLVMOrcDisposeLLJIT(J: Jit); |
835 | Jit = nullptr; |
836 | |
837 | // Check that the generator context was "destroyed". |
838 | EXPECT_TRUE(Ctx.Disposed); |
839 | } |
840 | |