1 | //===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- 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 | // This test suite verifies basic MCJIT functionality when invoked from the C |
10 | // API. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "MCJITTestAPICommon.h" |
15 | #include "llvm-c/Analysis.h" |
16 | #include "llvm-c/Core.h" |
17 | #include "llvm-c/ExecutionEngine.h" |
18 | #include "llvm-c/Target.h" |
19 | #include "llvm-c/Transforms/PassBuilder.h" |
20 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" |
21 | #include "llvm/Support/Debug.h" |
22 | #include "llvm/TargetParser/Host.h" |
23 | #include "gtest/gtest.h" |
24 | |
25 | using namespace llvm; |
26 | |
27 | static bool didCallAllocateCodeSection; |
28 | static bool didAllocateCompactUnwindSection; |
29 | static bool didCallYield; |
30 | |
31 | static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size, |
32 | unsigned alignment, |
33 | unsigned sectionID, |
34 | const char *sectionName) { |
35 | didCallAllocateCodeSection = true; |
36 | return static_cast<SectionMemoryManager*>(object)->allocateCodeSection( |
37 | Size: size, Alignment: alignment, SectionID: sectionID, SectionName: sectionName); |
38 | } |
39 | |
40 | static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size, |
41 | unsigned alignment, |
42 | unsigned sectionID, |
43 | const char *sectionName, |
44 | LLVMBool isReadOnly) { |
45 | if (!strcmp(s1: sectionName, s2: "__compact_unwind" )) |
46 | didAllocateCompactUnwindSection = true; |
47 | return static_cast<SectionMemoryManager*>(object)->allocateDataSection( |
48 | Size: size, Alignment: alignment, SectionID: sectionID, SectionName: sectionName, isReadOnly); |
49 | } |
50 | |
51 | static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) { |
52 | std::string errMsgString; |
53 | bool result = |
54 | static_cast<SectionMemoryManager*>(object)->finalizeMemory(ErrMsg: &errMsgString); |
55 | if (result) { |
56 | *errMsg = LLVMCreateMessage(Message: errMsgString.c_str()); |
57 | return 1; |
58 | } |
59 | return 0; |
60 | } |
61 | |
62 | static void roundTripDestroy(void *object) { |
63 | delete static_cast<SectionMemoryManager*>(object); |
64 | } |
65 | |
66 | static void yield(LLVMContextRef, void *) { |
67 | didCallYield = true; |
68 | } |
69 | |
70 | namespace { |
71 | |
72 | // memory manager to test reserve allocation space callback |
73 | class TestReserveAllocationSpaceMemoryManager: public SectionMemoryManager { |
74 | public: |
75 | uintptr_t ReservedCodeSize; |
76 | uintptr_t UsedCodeSize; |
77 | uintptr_t ReservedDataSizeRO; |
78 | uintptr_t UsedDataSizeRO; |
79 | uintptr_t ReservedDataSizeRW; |
80 | uintptr_t UsedDataSizeRW; |
81 | |
82 | TestReserveAllocationSpaceMemoryManager() : |
83 | ReservedCodeSize(0), UsedCodeSize(0), ReservedDataSizeRO(0), |
84 | UsedDataSizeRO(0), ReservedDataSizeRW(0), UsedDataSizeRW(0) { |
85 | } |
86 | |
87 | bool needsToReserveAllocationSpace() override { return true; } |
88 | |
89 | void reserveAllocationSpace(uintptr_t CodeSize, Align CodeAlign, |
90 | uintptr_t DataSizeRO, Align RODataAlign, |
91 | uintptr_t DataSizeRW, |
92 | Align RWDataAlign) override { |
93 | ReservedCodeSize = CodeSize; |
94 | ReservedDataSizeRO = DataSizeRO; |
95 | ReservedDataSizeRW = DataSizeRW; |
96 | } |
97 | |
98 | void useSpace(uintptr_t* UsedSize, uintptr_t Size, unsigned Alignment) { |
99 | uintptr_t AlignedSize = (Size + Alignment - 1) / Alignment * Alignment; |
100 | uintptr_t AlignedBegin = (*UsedSize + Alignment - 1) / Alignment * Alignment; |
101 | *UsedSize = AlignedBegin + AlignedSize; |
102 | } |
103 | |
104 | uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, |
105 | unsigned SectionID, StringRef SectionName, |
106 | bool IsReadOnly) override { |
107 | useSpace(UsedSize: IsReadOnly ? &UsedDataSizeRO : &UsedDataSizeRW, Size, Alignment); |
108 | return SectionMemoryManager::allocateDataSection(Size, Alignment, |
109 | SectionID, SectionName, isReadOnly: IsReadOnly); |
110 | } |
111 | |
112 | uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, |
113 | unsigned SectionID, |
114 | StringRef SectionName) override { |
115 | useSpace(UsedSize: &UsedCodeSize, Size, Alignment); |
116 | return SectionMemoryManager::allocateCodeSection(Size, Alignment, |
117 | SectionID, SectionName); |
118 | } |
119 | }; |
120 | |
121 | class MCJITCAPITest : public testing::Test, public MCJITTestAPICommon { |
122 | protected: |
123 | MCJITCAPITest() { |
124 | // The architectures below are known to be compatible with MCJIT as they |
125 | // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be |
126 | // kept in sync. |
127 | SupportedArchs.push_back(Elt: Triple::aarch64); |
128 | SupportedArchs.push_back(Elt: Triple::arm); |
129 | SupportedArchs.push_back(Elt: Triple::mips); |
130 | SupportedArchs.push_back(Elt: Triple::mips64); |
131 | SupportedArchs.push_back(Elt: Triple::mips64el); |
132 | SupportedArchs.push_back(Elt: Triple::x86); |
133 | SupportedArchs.push_back(Elt: Triple::x86_64); |
134 | |
135 | // Some architectures have sub-architectures in which tests will fail, like |
136 | // ARM. These two vectors will define if they do have sub-archs (to avoid |
137 | // extra work for those who don't), and if so, if they are listed to work |
138 | HasSubArchs.push_back(Elt: Triple::arm); |
139 | SupportedSubArchs.push_back(Elt: "armv6" ); |
140 | SupportedSubArchs.push_back(Elt: "armv7" ); |
141 | |
142 | // The operating systems below are known to be sufficiently incompatible |
143 | // that they will fail the MCJIT C API tests. |
144 | UnsupportedEnvironments.push_back(Elt: Triple::Cygnus); |
145 | } |
146 | |
147 | void SetUp() override { |
148 | didCallAllocateCodeSection = false; |
149 | didAllocateCompactUnwindSection = false; |
150 | didCallYield = false; |
151 | Module = nullptr; |
152 | Function = nullptr; |
153 | Engine = nullptr; |
154 | Error = nullptr; |
155 | } |
156 | |
157 | void TearDown() override { |
158 | if (Engine) |
159 | LLVMDisposeExecutionEngine(EE: Engine); |
160 | else if (Module) |
161 | LLVMDisposeModule(M: Module); |
162 | } |
163 | |
164 | void buildSimpleFunction() { |
165 | Module = LLVMModuleCreateWithName(ModuleID: "simple_module" ); |
166 | |
167 | LLVMSetTarget(M: Module, Triple: HostTriple.c_str()); |
168 | |
169 | Function = LLVMAddFunction(M: Module, Name: "simple_function" , |
170 | FunctionTy: LLVMFunctionType(ReturnType: LLVMInt32Type(), ParamTypes: nullptr,ParamCount: 0, IsVarArg: 0)); |
171 | LLVMSetFunctionCallConv(Fn: Function, CC: LLVMCCallConv); |
172 | |
173 | LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Fn: Function, Name: "entry" ); |
174 | LLVMBuilderRef builder = LLVMCreateBuilder(); |
175 | LLVMPositionBuilderAtEnd(Builder: builder, Block: entry); |
176 | LLVMBuildRet(builder, V: LLVMConstInt(IntTy: LLVMInt32Type(), N: 42, SignExtend: 0)); |
177 | |
178 | LLVMVerifyModule(M: Module, Action: LLVMAbortProcessAction, OutMessage: &Error); |
179 | LLVMDisposeMessage(Message: Error); |
180 | |
181 | LLVMDisposeBuilder(Builder: builder); |
182 | } |
183 | |
184 | void buildFunctionThatUsesStackmap() { |
185 | Module = LLVMModuleCreateWithName(ModuleID: "simple_module" ); |
186 | |
187 | LLVMSetTarget(M: Module, Triple: HostTriple.c_str()); |
188 | |
189 | LLVMTypeRef stackmapParamTypes[] = { LLVMInt64Type(), LLVMInt32Type() }; |
190 | LLVMTypeRef stackmapTy = |
191 | LLVMFunctionType(ReturnType: LLVMVoidType(), ParamTypes: stackmapParamTypes, ParamCount: 2, IsVarArg: 1); |
192 | LLVMValueRef stackmap = LLVMAddFunction( |
193 | M: Module, Name: "llvm.experimental.stackmap" , FunctionTy: stackmapTy); |
194 | LLVMSetLinkage(Global: stackmap, Linkage: LLVMExternalLinkage); |
195 | |
196 | Function = LLVMAddFunction(M: Module, Name: "simple_function" , |
197 | FunctionTy: LLVMFunctionType(ReturnType: LLVMInt32Type(), ParamTypes: nullptr, ParamCount: 0, IsVarArg: 0)); |
198 | |
199 | LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Fn: Function, Name: "entry" ); |
200 | LLVMBuilderRef builder = LLVMCreateBuilder(); |
201 | LLVMPositionBuilderAtEnd(Builder: builder, Block: entry); |
202 | LLVMValueRef stackmapArgs[] = { |
203 | LLVMConstInt(IntTy: LLVMInt64Type(), N: 0, SignExtend: 0), LLVMConstInt(IntTy: LLVMInt32Type(), N: 5, SignExtend: 0), |
204 | LLVMConstInt(IntTy: LLVMInt32Type(), N: 42, SignExtend: 0) |
205 | }; |
206 | LLVMBuildCall2(builder, stackmapTy, Fn: stackmap, Args: stackmapArgs, NumArgs: 3, Name: "" ); |
207 | LLVMBuildRet(builder, V: LLVMConstInt(IntTy: LLVMInt32Type(), N: 42, SignExtend: 0)); |
208 | |
209 | LLVMVerifyModule(M: Module, Action: LLVMAbortProcessAction, OutMessage: &Error); |
210 | LLVMDisposeMessage(Message: Error); |
211 | |
212 | LLVMDisposeBuilder(Builder: builder); |
213 | } |
214 | |
215 | void buildModuleWithCodeAndData() { |
216 | Module = LLVMModuleCreateWithName(ModuleID: "simple_module" ); |
217 | |
218 | LLVMSetTarget(M: Module, Triple: HostTriple.c_str()); |
219 | |
220 | // build a global int32 variable initialized to 42. |
221 | LLVMValueRef GlobalVar = LLVMAddGlobal(M: Module, Ty: LLVMInt32Type(), Name: "intVal" ); |
222 | LLVMSetInitializer(GlobalVar, ConstantVal: LLVMConstInt(IntTy: LLVMInt32Type(), N: 42, SignExtend: 0)); |
223 | |
224 | { |
225 | Function = LLVMAddFunction(M: Module, Name: "getGlobal" , |
226 | FunctionTy: LLVMFunctionType(ReturnType: LLVMInt32Type(), ParamTypes: nullptr, ParamCount: 0, IsVarArg: 0)); |
227 | LLVMSetFunctionCallConv(Fn: Function, CC: LLVMCCallConv); |
228 | |
229 | LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Fn: Function, Name: "entry" ); |
230 | LLVMBuilderRef Builder = LLVMCreateBuilder(); |
231 | LLVMPositionBuilderAtEnd(Builder, Block: Entry); |
232 | |
233 | LLVMValueRef IntVal = |
234 | LLVMBuildLoad2(Builder, Ty: LLVMInt32Type(), PointerVal: GlobalVar, Name: "intVal" ); |
235 | LLVMBuildRet(Builder, V: IntVal); |
236 | |
237 | LLVMVerifyModule(M: Module, Action: LLVMAbortProcessAction, OutMessage: &Error); |
238 | LLVMDisposeMessage(Message: Error); |
239 | |
240 | LLVMDisposeBuilder(Builder); |
241 | } |
242 | |
243 | { |
244 | LLVMTypeRef ParamTypes[] = { LLVMInt32Type() }; |
245 | Function2 = LLVMAddFunction( |
246 | M: Module, Name: "setGlobal" , FunctionTy: LLVMFunctionType(ReturnType: LLVMVoidType(), ParamTypes, ParamCount: 1, IsVarArg: 0)); |
247 | LLVMSetFunctionCallConv(Fn: Function2, CC: LLVMCCallConv); |
248 | |
249 | LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Fn: Function2, Name: "entry" ); |
250 | LLVMBuilderRef Builder = LLVMCreateBuilder(); |
251 | LLVMPositionBuilderAtEnd(Builder, Block: Entry); |
252 | |
253 | LLVMValueRef Arg = LLVMGetParam(Fn: Function2, Index: 0); |
254 | LLVMBuildStore(Builder, Val: Arg, Ptr: GlobalVar); |
255 | LLVMBuildRetVoid(Builder); |
256 | |
257 | LLVMVerifyModule(M: Module, Action: LLVMAbortProcessAction, OutMessage: &Error); |
258 | LLVMDisposeMessage(Message: Error); |
259 | |
260 | LLVMDisposeBuilder(Builder); |
261 | } |
262 | } |
263 | |
264 | void buildMCJITOptions() { |
265 | LLVMInitializeMCJITCompilerOptions(Options: &Options, SizeOfOptions: sizeof(Options)); |
266 | Options.OptLevel = 2; |
267 | |
268 | // Just ensure that this field still exists. |
269 | Options.NoFramePointerElim = false; |
270 | } |
271 | |
272 | void useRoundTripSectionMemoryManager() { |
273 | Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager( |
274 | Opaque: new SectionMemoryManager(), |
275 | AllocateCodeSection: roundTripAllocateCodeSection, |
276 | AllocateDataSection: roundTripAllocateDataSection, |
277 | FinalizeMemory: roundTripFinalizeMemory, |
278 | Destroy: roundTripDestroy); |
279 | } |
280 | |
281 | void buildMCJITEngine() { |
282 | ASSERT_EQ( |
283 | 0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options, |
284 | sizeof(Options), &Error)); |
285 | } |
286 | |
287 | void buildAndRunPasses() { |
288 | LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions(); |
289 | if (LLVMErrorRef E = |
290 | LLVMRunPasses(M: Module, Passes: "instcombine" , TM: nullptr, Options)) { |
291 | char *Msg = LLVMGetErrorMessage(Err: E); |
292 | LLVMConsumeError(Err: E); |
293 | LLVMDisposePassBuilderOptions(Options); |
294 | FAIL() << "Failed to run passes: " << Msg; |
295 | } |
296 | |
297 | LLVMDisposePassBuilderOptions(Options); |
298 | } |
299 | |
300 | void buildAndRunOptPasses() { |
301 | LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions(); |
302 | if (LLVMErrorRef E = |
303 | LLVMRunPasses(M: Module, Passes: "default<O2>" , TM: nullptr, Options)) { |
304 | char *Msg = LLVMGetErrorMessage(Err: E); |
305 | LLVMConsumeError(Err: E); |
306 | LLVMDisposePassBuilderOptions(Options); |
307 | FAIL() << "Failed to run passes: " << Msg; |
308 | } |
309 | |
310 | LLVMDisposePassBuilderOptions(Options); |
311 | } |
312 | |
313 | LLVMModuleRef Module; |
314 | LLVMValueRef Function; |
315 | LLVMValueRef Function2; |
316 | LLVMMCJITCompilerOptions Options; |
317 | LLVMExecutionEngineRef Engine; |
318 | char *Error; |
319 | }; |
320 | } // end anonymous namespace |
321 | |
322 | TEST_F(MCJITCAPITest, simple_function) { |
323 | SKIP_UNSUPPORTED_PLATFORM; |
324 | |
325 | buildSimpleFunction(); |
326 | buildMCJITOptions(); |
327 | buildMCJITEngine(); |
328 | buildAndRunPasses(); |
329 | |
330 | auto *functionPointer = reinterpret_cast<int (*)()>( |
331 | reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(EE: Engine, Global: Function))); |
332 | |
333 | EXPECT_EQ(42, functionPointer()); |
334 | } |
335 | |
336 | TEST_F(MCJITCAPITest, gva) { |
337 | SKIP_UNSUPPORTED_PLATFORM; |
338 | |
339 | Module = LLVMModuleCreateWithName(ModuleID: "simple_module" ); |
340 | LLVMSetTarget(M: Module, Triple: HostTriple.c_str()); |
341 | LLVMValueRef GlobalVar = LLVMAddGlobal(M: Module, Ty: LLVMInt32Type(), Name: "simple_value" ); |
342 | LLVMSetInitializer(GlobalVar, ConstantVal: LLVMConstInt(IntTy: LLVMInt32Type(), N: 42, SignExtend: 0)); |
343 | |
344 | buildMCJITOptions(); |
345 | buildMCJITEngine(); |
346 | buildAndRunPasses(); |
347 | |
348 | uint64_t raw = LLVMGetGlobalValueAddress(EE: Engine, Name: "simple_value" ); |
349 | int32_t *usable = (int32_t *) raw; |
350 | |
351 | EXPECT_EQ(42, *usable); |
352 | } |
353 | |
354 | TEST_F(MCJITCAPITest, gfa) { |
355 | SKIP_UNSUPPORTED_PLATFORM; |
356 | |
357 | buildSimpleFunction(); |
358 | buildMCJITOptions(); |
359 | buildMCJITEngine(); |
360 | buildAndRunPasses(); |
361 | |
362 | uint64_t raw = LLVMGetFunctionAddress(EE: Engine, Name: "simple_function" ); |
363 | int (*usable)() = (int (*)()) raw; |
364 | |
365 | EXPECT_EQ(42, usable()); |
366 | } |
367 | |
368 | TEST_F(MCJITCAPITest, custom_memory_manager) { |
369 | SKIP_UNSUPPORTED_PLATFORM; |
370 | |
371 | buildSimpleFunction(); |
372 | buildMCJITOptions(); |
373 | useRoundTripSectionMemoryManager(); |
374 | buildMCJITEngine(); |
375 | buildAndRunPasses(); |
376 | |
377 | auto *functionPointer = reinterpret_cast<int (*)()>( |
378 | reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(EE: Engine, Global: Function))); |
379 | |
380 | EXPECT_EQ(42, functionPointer()); |
381 | EXPECT_TRUE(didCallAllocateCodeSection); |
382 | } |
383 | |
384 | TEST_F(MCJITCAPITest, stackmap_creates_compact_unwind_on_darwin) { |
385 | SKIP_UNSUPPORTED_PLATFORM; |
386 | |
387 | // This test is also not supported on non-x86 platforms. |
388 | if (Triple(HostTriple).getArch() != Triple::x86_64) |
389 | GTEST_SKIP(); |
390 | |
391 | buildFunctionThatUsesStackmap(); |
392 | buildMCJITOptions(); |
393 | useRoundTripSectionMemoryManager(); |
394 | buildMCJITEngine(); |
395 | buildAndRunOptPasses(); |
396 | |
397 | auto *functionPointer = reinterpret_cast<int (*)()>( |
398 | reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(EE: Engine, Global: Function))); |
399 | |
400 | EXPECT_EQ(42, functionPointer()); |
401 | EXPECT_TRUE(didCallAllocateCodeSection); |
402 | |
403 | // Up to this point, the test is specific only to X86-64. But this next |
404 | // expectation is only valid on Darwin because it assumes that unwind |
405 | // data is made available only through compact_unwind. It would be |
406 | // worthwhile to extend this to handle non-Darwin platforms, in which |
407 | // case you'd want to look for an eh_frame or something. |
408 | // |
409 | // FIXME: Currently, MCJIT relies on a configure-time check to determine which |
410 | // sections to emit. The JIT client should have runtime control over this. |
411 | EXPECT_TRUE( |
412 | Triple(HostTriple).getOS() != Triple::Darwin || |
413 | Triple(HostTriple).isMacOSXVersionLT(10, 7) || |
414 | didAllocateCompactUnwindSection); |
415 | } |
416 | |
417 | #if defined(__APPLE__) && defined(__aarch64__) |
418 | // FIXME: Figure out why this fails on mac/arm, PR46647 |
419 | #define MAYBE_reserve_allocation_space DISABLED_reserve_allocation_space |
420 | #else |
421 | #define MAYBE_reserve_allocation_space reserve_allocation_space |
422 | #endif |
423 | TEST_F(MCJITCAPITest, MAYBE_reserve_allocation_space) { |
424 | SKIP_UNSUPPORTED_PLATFORM; |
425 | |
426 | TestReserveAllocationSpaceMemoryManager* MM = new TestReserveAllocationSpaceMemoryManager(); |
427 | |
428 | buildModuleWithCodeAndData(); |
429 | buildMCJITOptions(); |
430 | Options.MCJMM = wrap(P: MM); |
431 | buildMCJITEngine(); |
432 | buildAndRunPasses(); |
433 | |
434 | auto GetGlobalFct = reinterpret_cast<int (*)()>( |
435 | reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(EE: Engine, Global: Function))); |
436 | |
437 | auto SetGlobalFct = reinterpret_cast<void (*)(int)>( |
438 | reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(EE: Engine, Global: Function2))); |
439 | |
440 | SetGlobalFct(789); |
441 | EXPECT_EQ(789, GetGlobalFct()); |
442 | EXPECT_LE(MM->UsedCodeSize, MM->ReservedCodeSize); |
443 | EXPECT_LE(MM->UsedDataSizeRO, MM->ReservedDataSizeRO); |
444 | EXPECT_LE(MM->UsedDataSizeRW, MM->ReservedDataSizeRW); |
445 | EXPECT_TRUE(MM->UsedCodeSize > 0); |
446 | EXPECT_TRUE(MM->UsedDataSizeRW > 0); |
447 | } |
448 | |
449 | TEST_F(MCJITCAPITest, yield) { |
450 | SKIP_UNSUPPORTED_PLATFORM; |
451 | |
452 | buildSimpleFunction(); |
453 | buildMCJITOptions(); |
454 | buildMCJITEngine(); |
455 | LLVMContextRef C = LLVMGetGlobalContext(); |
456 | LLVMContextSetYieldCallback(C, Callback: yield, OpaqueHandle: nullptr); |
457 | buildAndRunPasses(); |
458 | |
459 | auto *functionPointer = reinterpret_cast<int (*)()>( |
460 | reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(EE: Engine, Global: Function))); |
461 | |
462 | EXPECT_EQ(42, functionPointer()); |
463 | EXPECT_TRUE(didCallYield); |
464 | } |
465 | |
466 | static int localTestFunc() { |
467 | return 42; |
468 | } |
469 | |
470 | TEST_F(MCJITCAPITest, addGlobalMapping) { |
471 | SKIP_UNSUPPORTED_PLATFORM; |
472 | |
473 | Module = LLVMModuleCreateWithName(ModuleID: "testModule" ); |
474 | LLVMSetTarget(M: Module, Triple: HostTriple.c_str()); |
475 | LLVMTypeRef FunctionType = LLVMFunctionType(ReturnType: LLVMInt32Type(), ParamTypes: nullptr, ParamCount: 0, IsVarArg: 0); |
476 | LLVMValueRef MappedFn = LLVMAddFunction(M: Module, Name: "mapped_fn" , FunctionTy: FunctionType); |
477 | |
478 | Function = LLVMAddFunction(M: Module, Name: "test_fn" , FunctionTy: FunctionType); |
479 | LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Fn: Function, Name: "" ); |
480 | LLVMBuilderRef Builder = LLVMCreateBuilder(); |
481 | LLVMPositionBuilderAtEnd(Builder, Block: Entry); |
482 | LLVMValueRef RetVal = |
483 | LLVMBuildCall2(Builder, FunctionType, Fn: MappedFn, Args: nullptr, NumArgs: 0, Name: "" ); |
484 | LLVMBuildRet(Builder, V: RetVal); |
485 | LLVMDisposeBuilder(Builder); |
486 | |
487 | LLVMVerifyModule(M: Module, Action: LLVMAbortProcessAction, OutMessage: &Error); |
488 | LLVMDisposeMessage(Message: Error); |
489 | |
490 | buildMCJITOptions(); |
491 | buildMCJITEngine(); |
492 | |
493 | LLVMAddGlobalMapping( |
494 | EE: Engine, Global: MappedFn, |
495 | Addr: reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(&localTestFunc))); |
496 | |
497 | buildAndRunPasses(); |
498 | |
499 | uint64_t raw = LLVMGetFunctionAddress(EE: Engine, Name: "test_fn" ); |
500 | int (*usable)() = (int (*)()) raw; |
501 | |
502 | EXPECT_EQ(42, usable()); |
503 | } |
504 | |