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