xref: /llvm-project/llvm/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp (revision d768bf994f508d7eaf9541a568be3d71096febf5)
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