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
25using namespace llvm;
26
27static bool didCallAllocateCodeSection;
28static bool didAllocateCompactUnwindSection;
29static bool didCallYield;
30
31static 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
40static 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
51static 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
62static void roundTripDestroy(void *object) {
63 delete static_cast<SectionMemoryManager*>(object);
64}
65
66static void yield(LLVMContextRef, void *) {
67 didCallYield = true;
68}
69
70namespace {
71
72// memory manager to test reserve allocation space callback
73class TestReserveAllocationSpaceMemoryManager: public SectionMemoryManager {
74public:
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
121class MCJITCAPITest : public testing::Test, public MCJITTestAPICommon {
122protected:
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
322TEST_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
336TEST_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
354TEST_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
368TEST_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
384TEST_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
423TEST_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
449TEST_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
466static int localTestFunc() {
467 return 42;
468}
469
470TEST_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

source code of llvm/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp