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
roundTripAllocateCodeSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName)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, alignment, sectionID, sectionName);
38 }
39
roundTripAllocateDataSection(void * object,uintptr_t size,unsigned alignment,unsigned sectionID,const char * sectionName,LLVMBool isReadOnly)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(sectionName, "__compact_unwind"))
46 didAllocateCompactUnwindSection = true;
47 return static_cast<SectionMemoryManager*>(object)->allocateDataSection(
48 size, alignment, sectionID, sectionName, isReadOnly);
49 }
50
roundTripFinalizeMemory(void * object,char ** errMsg)51 static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) {
52 std::string errMsgString;
53 bool result =
54 static_cast<SectionMemoryManager*>(object)->finalizeMemory(&errMsgString);
55 if (result) {
56 *errMsg = LLVMCreateMessage(errMsgString.c_str());
57 return 1;
58 }
59 return 0;
60 }
61
roundTripDestroy(void * object)62 static void roundTripDestroy(void *object) {
63 delete static_cast<SectionMemoryManager*>(object);
64 }
65
yield(LLVMContextRef,void *)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
TestReserveAllocationSpaceMemoryManager()82 TestReserveAllocationSpaceMemoryManager() :
83 ReservedCodeSize(0), UsedCodeSize(0), ReservedDataSizeRO(0),
84 UsedDataSizeRO(0), ReservedDataSizeRW(0), UsedDataSizeRW(0) {
85 }
86
needsToReserveAllocationSpace()87 bool needsToReserveAllocationSpace() override { return true; }
88
reserveAllocationSpace(uintptr_t CodeSize,Align CodeAlign,uintptr_t DataSizeRO,Align RODataAlign,uintptr_t DataSizeRW,Align RWDataAlign)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
useSpace(uintptr_t * UsedSize,uintptr_t Size,unsigned Alignment)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
allocateDataSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName,bool IsReadOnly)104 uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
105 unsigned SectionID, StringRef SectionName,
106 bool IsReadOnly) override {
107 useSpace(IsReadOnly ? &UsedDataSizeRO : &UsedDataSizeRW, Size, Alignment);
108 return SectionMemoryManager::allocateDataSection(Size, Alignment,
109 SectionID, SectionName, IsReadOnly);
110 }
111
allocateCodeSection(uintptr_t Size,unsigned Alignment,unsigned SectionID,StringRef SectionName)112 uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
113 unsigned SectionID,
114 StringRef SectionName) override {
115 useSpace(&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:
MCJITCAPITest()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(Triple::aarch64);
128 SupportedArchs.push_back(Triple::arm);
129 SupportedArchs.push_back(Triple::mips);
130 SupportedArchs.push_back(Triple::mips64);
131 SupportedArchs.push_back(Triple::mips64el);
132 SupportedArchs.push_back(Triple::x86);
133 SupportedArchs.push_back(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(Triple::arm);
139 SupportedSubArchs.push_back("armv6");
140 SupportedSubArchs.push_back("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(Triple::Cygnus);
145 }
146
SetUp()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
TearDown()157 void TearDown() override {
158 if (Engine)
159 LLVMDisposeExecutionEngine(Engine);
160 else if (Module)
161 LLVMDisposeModule(Module);
162 }
163
buildSimpleFunction()164 void buildSimpleFunction() {
165 Module = LLVMModuleCreateWithName("simple_module");
166
167 LLVMSetTarget(Module, HostTriple.c_str());
168
169 Function = LLVMAddFunction(Module, "simple_function",
170 LLVMFunctionType(LLVMInt32Type(), nullptr,0, 0));
171 LLVMSetFunctionCallConv(Function, LLVMCCallConv);
172
173 LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry");
174 LLVMBuilderRef builder = LLVMCreateBuilder();
175 LLVMPositionBuilderAtEnd(builder, entry);
176 LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0));
177
178 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
179 LLVMDisposeMessage(Error);
180
181 LLVMDisposeBuilder(builder);
182 }
183
buildFunctionThatUsesStackmap()184 void buildFunctionThatUsesStackmap() {
185 Module = LLVMModuleCreateWithName("simple_module");
186
187 LLVMSetTarget(Module, HostTriple.c_str());
188
189 LLVMTypeRef stackmapParamTypes[] = { LLVMInt64Type(), LLVMInt32Type() };
190 LLVMTypeRef stackmapTy =
191 LLVMFunctionType(LLVMVoidType(), stackmapParamTypes, 2, 1);
192 LLVMValueRef stackmap = LLVMAddFunction(
193 Module, "llvm.experimental.stackmap", stackmapTy);
194 LLVMSetLinkage(stackmap, LLVMExternalLinkage);
195
196 Function = LLVMAddFunction(Module, "simple_function",
197 LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));
198
199 LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry");
200 LLVMBuilderRef builder = LLVMCreateBuilder();
201 LLVMPositionBuilderAtEnd(builder, entry);
202 LLVMValueRef stackmapArgs[] = {
203 LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt32Type(), 5, 0),
204 LLVMConstInt(LLVMInt32Type(), 42, 0)
205 };
206 LLVMBuildCall2(builder, stackmapTy, stackmap, stackmapArgs, 3, "");
207 LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0));
208
209 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
210 LLVMDisposeMessage(Error);
211
212 LLVMDisposeBuilder(builder);
213 }
214
buildModuleWithCodeAndData()215 void buildModuleWithCodeAndData() {
216 Module = LLVMModuleCreateWithName("simple_module");
217
218 LLVMSetTarget(Module, HostTriple.c_str());
219
220 // build a global int32 variable initialized to 42.
221 LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "intVal");
222 LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0));
223
224 {
225 Function = LLVMAddFunction(Module, "getGlobal",
226 LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));
227 LLVMSetFunctionCallConv(Function, LLVMCCallConv);
228
229 LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "entry");
230 LLVMBuilderRef Builder = LLVMCreateBuilder();
231 LLVMPositionBuilderAtEnd(Builder, Entry);
232
233 LLVMValueRef IntVal =
234 LLVMBuildLoad2(Builder, LLVMInt32Type(), GlobalVar, "intVal");
235 LLVMBuildRet(Builder, IntVal);
236
237 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
238 LLVMDisposeMessage(Error);
239
240 LLVMDisposeBuilder(Builder);
241 }
242
243 {
244 LLVMTypeRef ParamTypes[] = { LLVMInt32Type() };
245 Function2 = LLVMAddFunction(
246 Module, "setGlobal", LLVMFunctionType(LLVMVoidType(), ParamTypes, 1, 0));
247 LLVMSetFunctionCallConv(Function2, LLVMCCallConv);
248
249 LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function2, "entry");
250 LLVMBuilderRef Builder = LLVMCreateBuilder();
251 LLVMPositionBuilderAtEnd(Builder, Entry);
252
253 LLVMValueRef Arg = LLVMGetParam(Function2, 0);
254 LLVMBuildStore(Builder, Arg, GlobalVar);
255 LLVMBuildRetVoid(Builder);
256
257 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
258 LLVMDisposeMessage(Error);
259
260 LLVMDisposeBuilder(Builder);
261 }
262 }
263
buildMCJITOptions()264 void buildMCJITOptions() {
265 LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options));
266 Options.OptLevel = 2;
267
268 // Just ensure that this field still exists.
269 Options.NoFramePointerElim = false;
270 }
271
useRoundTripSectionMemoryManager()272 void useRoundTripSectionMemoryManager() {
273 Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager(
274 new SectionMemoryManager(),
275 roundTripAllocateCodeSection,
276 roundTripAllocateDataSection,
277 roundTripFinalizeMemory,
278 roundTripDestroy);
279 }
280
buildMCJITEngine()281 void buildMCJITEngine() {
282 ASSERT_EQ(
283 0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options,
284 sizeof(Options), &Error));
285 }
286
buildAndRunPasses()287 void buildAndRunPasses() {
288 LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions();
289 if (LLVMErrorRef E =
290 LLVMRunPasses(Module, "instcombine", nullptr, Options)) {
291 char *Msg = LLVMGetErrorMessage(E);
292 LLVMConsumeError(E);
293 LLVMDisposePassBuilderOptions(Options);
294 FAIL() << "Failed to run passes: " << Msg;
295 }
296
297 LLVMDisposePassBuilderOptions(Options);
298 }
299
buildAndRunOptPasses()300 void buildAndRunOptPasses() {
301 LLVMPassBuilderOptionsRef Options = LLVMCreatePassBuilderOptions();
302 if (LLVMErrorRef E =
303 LLVMRunPasses(Module, "default<O2>", nullptr, Options)) {
304 char *Msg = LLVMGetErrorMessage(E);
305 LLVMConsumeError(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
TEST_F(MCJITCAPITest,simple_function)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(Engine, Function)));
332
333 EXPECT_EQ(42, functionPointer());
334 }
335
TEST_F(MCJITCAPITest,gva)336 TEST_F(MCJITCAPITest, gva) {
337 SKIP_UNSUPPORTED_PLATFORM;
338
339 Module = LLVMModuleCreateWithName("simple_module");
340 LLVMSetTarget(Module, HostTriple.c_str());
341 LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "simple_value");
342 LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0));
343
344 buildMCJITOptions();
345 buildMCJITEngine();
346 buildAndRunPasses();
347
348 uint64_t raw = LLVMGetGlobalValueAddress(Engine, "simple_value");
349 int32_t *usable = (int32_t *) raw;
350
351 EXPECT_EQ(42, *usable);
352 }
353
TEST_F(MCJITCAPITest,gfa)354 TEST_F(MCJITCAPITest, gfa) {
355 SKIP_UNSUPPORTED_PLATFORM;
356
357 buildSimpleFunction();
358 buildMCJITOptions();
359 buildMCJITEngine();
360 buildAndRunPasses();
361
362 uint64_t raw = LLVMGetFunctionAddress(Engine, "simple_function");
363 int (*usable)() = (int (*)()) raw;
364
365 EXPECT_EQ(42, usable());
366 }
367
TEST_F(MCJITCAPITest,custom_memory_manager)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(Engine, Function)));
379
380 EXPECT_EQ(42, functionPointer());
381 EXPECT_TRUE(didCallAllocateCodeSection);
382 }
383
TEST_F(MCJITCAPITest,stackmap_creates_compact_unwind_on_darwin)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(Engine, 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
TEST_F(MCJITCAPITest,MAYBE_reserve_allocation_space)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(MM);
431 buildMCJITEngine();
432 buildAndRunPasses();
433
434 auto GetGlobalFct = reinterpret_cast<int (*)()>(
435 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));
436
437 auto SetGlobalFct = reinterpret_cast<void (*)(int)>(
438 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, 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
TEST_F(MCJITCAPITest,yield)449 TEST_F(MCJITCAPITest, yield) {
450 SKIP_UNSUPPORTED_PLATFORM;
451
452 buildSimpleFunction();
453 buildMCJITOptions();
454 buildMCJITEngine();
455 LLVMContextRef C = LLVMGetGlobalContext();
456 LLVMContextSetYieldCallback(C, yield, nullptr);
457 buildAndRunPasses();
458
459 auto *functionPointer = reinterpret_cast<int (*)()>(
460 reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function)));
461
462 EXPECT_EQ(42, functionPointer());
463 EXPECT_TRUE(didCallYield);
464 }
465
localTestFunc()466 static int localTestFunc() {
467 return 42;
468 }
469
TEST_F(MCJITCAPITest,addGlobalMapping)470 TEST_F(MCJITCAPITest, addGlobalMapping) {
471 SKIP_UNSUPPORTED_PLATFORM;
472
473 Module = LLVMModuleCreateWithName("testModule");
474 LLVMSetTarget(Module, HostTriple.c_str());
475 LLVMTypeRef FunctionType = LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0);
476 LLVMValueRef MappedFn = LLVMAddFunction(Module, "mapped_fn", FunctionType);
477
478 Function = LLVMAddFunction(Module, "test_fn", FunctionType);
479 LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "");
480 LLVMBuilderRef Builder = LLVMCreateBuilder();
481 LLVMPositionBuilderAtEnd(Builder, Entry);
482 LLVMValueRef RetVal =
483 LLVMBuildCall2(Builder, FunctionType, MappedFn, nullptr, 0, "");
484 LLVMBuildRet(Builder, RetVal);
485 LLVMDisposeBuilder(Builder);
486
487 LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
488 LLVMDisposeMessage(Error);
489
490 buildMCJITOptions();
491 buildMCJITEngine();
492
493 LLVMAddGlobalMapping(
494 Engine, MappedFn,
495 reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(&localTestFunc)));
496
497 buildAndRunPasses();
498
499 uint64_t raw = LLVMGetFunctionAddress(Engine, "test_fn");
500 int (*usable)() = (int (*)()) raw;
501
502 EXPECT_EQ(42, usable());
503 }
504