1//===- MCJITTest.cpp - Unit tests for the MCJIT ---------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This test suite verifies basic MCJIT functionality when invoked form the C
11// API.
12//
13//===----------------------------------------------------------------------===//
14
15#include "llvm-c/Analysis.h"
16#include "MCJITTestAPICommon.h"
17#include "llvm-c/Core.h"
18#include "llvm-c/ExecutionEngine.h"
19#include "llvm-c/Target.h"
20#include "llvm-c/Transforms/PassManagerBuilder.h"
21#include "llvm-c/Transforms/Scalar.h"
22#include "llvm/ExecutionEngine/SectionMemoryManager.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/Host.h"
25#include "gtest/gtest.h"
26
27using namespace llvm;
28
29static bool didCallAllocateCodeSection;
30static bool didAllocateCompactUnwindSection;
31static bool didCallYield;
32
33static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size,
34                                             unsigned alignment,
35                                             unsigned sectionID,
36                                             const char *sectionName) {
37  didCallAllocateCodeSection = true;
38  return static_cast<SectionMemoryManager*>(object)->allocateCodeSection(
39    size, alignment, sectionID, sectionName);
40}
41
42static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size,
43                                             unsigned alignment,
44                                             unsigned sectionID,
45                                             const char *sectionName,
46                                             LLVMBool isReadOnly) {
47  if (!strcmp(sectionName, "__compact_unwind"))
48    didAllocateCompactUnwindSection = true;
49  return static_cast<SectionMemoryManager*>(object)->allocateDataSection(
50    size, alignment, sectionID, sectionName, isReadOnly);
51}
52
53static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) {
54  std::string errMsgString;
55  bool result =
56    static_cast<SectionMemoryManager*>(object)->finalizeMemory(&errMsgString);
57  if (result) {
58    *errMsg = LLVMCreateMessage(errMsgString.c_str());
59    return 1;
60  }
61  return 0;
62}
63
64static void roundTripDestroy(void *object) {
65  delete static_cast<SectionMemoryManager*>(object);
66}
67
68static void yield(LLVMContextRef, void *) {
69  didCallYield = true;
70}
71
72namespace {
73
74// memory manager to test reserve allocation space callback
75class TestReserveAllocationSpaceMemoryManager: public SectionMemoryManager {
76public:
77  uintptr_t ReservedCodeSize;
78  uintptr_t UsedCodeSize;
79  uintptr_t ReservedDataSizeRO;
80  uintptr_t UsedDataSizeRO;
81  uintptr_t ReservedDataSizeRW;
82  uintptr_t UsedDataSizeRW;
83
84  TestReserveAllocationSpaceMemoryManager() :
85    ReservedCodeSize(0), UsedCodeSize(0), ReservedDataSizeRO(0),
86    UsedDataSizeRO(0), ReservedDataSizeRW(0), UsedDataSizeRW(0) {
87  }
88
89  virtual bool needsToReserveAllocationSpace() {
90    return true;
91  }
92
93  virtual void reserveAllocationSpace(
94      uintptr_t CodeSize, uintptr_t DataSizeRO, uintptr_t DataSizeRW) {
95    ReservedCodeSize = CodeSize;
96    ReservedDataSizeRO = DataSizeRO;
97    ReservedDataSizeRW = DataSizeRW;
98  }
99
100  void useSpace(uintptr_t* UsedSize, uintptr_t Size, unsigned Alignment) {
101    uintptr_t AlignedSize = (Size + Alignment - 1) / Alignment * Alignment;
102    uintptr_t AlignedBegin = (*UsedSize + Alignment - 1) / Alignment * Alignment;
103    *UsedSize = AlignedBegin + AlignedSize;
104  }
105
106  virtual uint8_t* allocateDataSection(uintptr_t Size, unsigned Alignment,
107      unsigned SectionID, StringRef SectionName, bool IsReadOnly) {
108    useSpace(IsReadOnly ? &UsedDataSizeRO : &UsedDataSizeRW, Size, Alignment);
109    return SectionMemoryManager::allocateDataSection(Size, Alignment,
110      SectionID, SectionName, IsReadOnly);
111  }
112
113  uint8_t* allocateCodeSection(uintptr_t Size, unsigned Alignment,
114      unsigned SectionID, StringRef SectionName) {
115    useSpace(&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(Triple::aarch64);
128    SupportedArchs.push_back(Triple::arm);
129    SupportedArchs.push_back(Triple::mips);
130    SupportedArchs.push_back(Triple::x86);
131    SupportedArchs.push_back(Triple::x86_64);
132
133    // Some architectures have sub-architectures in which tests will fail, like
134    // ARM. These two vectors will define if they do have sub-archs (to avoid
135    // extra work for those who don't), and if so, if they are listed to work
136    HasSubArchs.push_back(Triple::arm);
137    SupportedSubArchs.push_back("armv6");
138    SupportedSubArchs.push_back("armv7");
139
140    // The operating systems below are known to be sufficiently incompatible
141    // that they will fail the MCJIT C API tests.
142    UnsupportedOSs.push_back(Triple::Cygwin);
143
144    UnsupportedEnvironments.push_back(Triple::Cygnus);
145  }
146
147  virtual void SetUp() {
148    didCallAllocateCodeSection = false;
149    didAllocateCompactUnwindSection = false;
150    didCallYield = false;
151    Module = nullptr;
152    Function = nullptr;
153    Engine = nullptr;
154    Error = nullptr;
155  }
156
157  virtual void TearDown() {
158    if (Engine)
159      LLVMDisposeExecutionEngine(Engine);
160    else if (Module)
161      LLVMDisposeModule(Module);
162  }
163
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
184  void buildFunctionThatUsesStackmap() {
185    Module = LLVMModuleCreateWithName("simple_module");
186
187    LLVMSetTarget(Module, HostTriple.c_str());
188
189    LLVMTypeRef stackmapParamTypes[] = { LLVMInt64Type(), LLVMInt32Type() };
190    LLVMValueRef stackmap = LLVMAddFunction(
191      Module, "llvm.experimental.stackmap",
192      LLVMFunctionType(LLVMVoidType(), stackmapParamTypes, 2, 1));
193    LLVMSetLinkage(stackmap, LLVMExternalLinkage);
194
195    Function = LLVMAddFunction(Module, "simple_function",
196                              LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));
197
198    LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry");
199    LLVMBuilderRef builder = LLVMCreateBuilder();
200    LLVMPositionBuilderAtEnd(builder, entry);
201    LLVMValueRef stackmapArgs[] = {
202      LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt32Type(), 5, 0),
203      LLVMConstInt(LLVMInt32Type(), 42, 0)
204    };
205    LLVMBuildCall(builder, stackmap, stackmapArgs, 3, "");
206    LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0));
207
208    LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
209    LLVMDisposeMessage(Error);
210
211    LLVMDisposeBuilder(builder);
212  }
213
214  void buildModuleWithCodeAndData() {
215    Module = LLVMModuleCreateWithName("simple_module");
216
217    LLVMSetTarget(Module, HostTriple.c_str());
218
219    // build a global int32 variable initialized to 42.
220    LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "intVal");
221    LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0));
222
223    {
224        Function = LLVMAddFunction(Module, "getGlobal",
225                              LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0));
226        LLVMSetFunctionCallConv(Function, LLVMCCallConv);
227
228        LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "entry");
229        LLVMBuilderRef Builder = LLVMCreateBuilder();
230        LLVMPositionBuilderAtEnd(Builder, Entry);
231
232        LLVMValueRef IntVal = LLVMBuildLoad(Builder, GlobalVar, "intVal");
233        LLVMBuildRet(Builder, IntVal);
234
235        LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
236        LLVMDisposeMessage(Error);
237
238        LLVMDisposeBuilder(Builder);
239    }
240
241    {
242        LLVMTypeRef ParamTypes[] = { LLVMInt32Type() };
243        Function2 = LLVMAddFunction(
244          Module, "setGlobal", LLVMFunctionType(LLVMVoidType(), ParamTypes, 1, 0));
245        LLVMSetFunctionCallConv(Function2, LLVMCCallConv);
246
247        LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function2, "entry");
248        LLVMBuilderRef Builder = LLVMCreateBuilder();
249        LLVMPositionBuilderAtEnd(Builder, Entry);
250
251        LLVMValueRef Arg = LLVMGetParam(Function2, 0);
252        LLVMBuildStore(Builder, Arg, GlobalVar);
253        LLVMBuildRetVoid(Builder);
254
255        LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error);
256        LLVMDisposeMessage(Error);
257
258        LLVMDisposeBuilder(Builder);
259    }
260  }
261
262  void buildMCJITOptions() {
263    LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options));
264    Options.OptLevel = 2;
265
266    // Just ensure that this field still exists.
267    Options.NoFramePointerElim = false;
268  }
269
270  void useRoundTripSectionMemoryManager() {
271    Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager(
272      new SectionMemoryManager(),
273      roundTripAllocateCodeSection,
274      roundTripAllocateDataSection,
275      roundTripFinalizeMemory,
276      roundTripDestroy);
277  }
278
279  void buildMCJITEngine() {
280    ASSERT_EQ(
281      0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options,
282                                          sizeof(Options), &Error));
283  }
284
285  void buildAndRunPasses() {
286    LLVMPassManagerRef pass = LLVMCreatePassManager();
287    LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), pass);
288    LLVMAddConstantPropagationPass(pass);
289    LLVMAddInstructionCombiningPass(pass);
290    LLVMRunPassManager(pass, Module);
291    LLVMDisposePassManager(pass);
292  }
293
294  void buildAndRunOptPasses() {
295    LLVMPassManagerBuilderRef passBuilder;
296
297    passBuilder = LLVMPassManagerBuilderCreate();
298    LLVMPassManagerBuilderSetOptLevel(passBuilder, 2);
299    LLVMPassManagerBuilderSetSizeLevel(passBuilder, 0);
300
301    LLVMPassManagerRef functionPasses =
302      LLVMCreateFunctionPassManagerForModule(Module);
303    LLVMPassManagerRef modulePasses =
304      LLVMCreatePassManager();
305
306    LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), modulePasses);
307
308    LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder,
309                                                      functionPasses);
310    LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses);
311
312    LLVMPassManagerBuilderDispose(passBuilder);
313
314    LLVMInitializeFunctionPassManager(functionPasses);
315    for (LLVMValueRef value = LLVMGetFirstFunction(Module);
316         value; value = LLVMGetNextFunction(value))
317      LLVMRunFunctionPassManager(functionPasses, value);
318    LLVMFinalizeFunctionPassManager(functionPasses);
319
320    LLVMRunPassManager(modulePasses, Module);
321
322    LLVMDisposePassManager(functionPasses);
323    LLVMDisposePassManager(modulePasses);
324  }
325
326  LLVMModuleRef Module;
327  LLVMValueRef Function;
328  LLVMValueRef Function2;
329  LLVMMCJITCompilerOptions Options;
330  LLVMExecutionEngineRef Engine;
331  char *Error;
332};
333} // end anonymous namespace
334
335TEST_F(MCJITCAPITest, simple_function) {
336  SKIP_UNSUPPORTED_PLATFORM;
337
338  buildSimpleFunction();
339  buildMCJITOptions();
340  buildMCJITEngine();
341  buildAndRunPasses();
342
343  union {
344    void *raw;
345    int (*usable)();
346  } functionPointer;
347  functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function);
348
349  EXPECT_EQ(42, functionPointer.usable());
350}
351
352TEST_F(MCJITCAPITest, custom_memory_manager) {
353  SKIP_UNSUPPORTED_PLATFORM;
354
355  buildSimpleFunction();
356  buildMCJITOptions();
357  useRoundTripSectionMemoryManager();
358  buildMCJITEngine();
359  buildAndRunPasses();
360
361  union {
362    void *raw;
363    int (*usable)();
364  } functionPointer;
365  functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function);
366
367  EXPECT_EQ(42, functionPointer.usable());
368  EXPECT_TRUE(didCallAllocateCodeSection);
369}
370
371TEST_F(MCJITCAPITest, stackmap_creates_compact_unwind_on_darwin) {
372  SKIP_UNSUPPORTED_PLATFORM;
373
374  // This test is also not supported on non-x86 platforms.
375  if (Triple(HostTriple).getArch() != Triple::x86_64)
376    return;
377
378  buildFunctionThatUsesStackmap();
379  buildMCJITOptions();
380  useRoundTripSectionMemoryManager();
381  buildMCJITEngine();
382  buildAndRunOptPasses();
383
384  union {
385    void *raw;
386    int (*usable)();
387  } functionPointer;
388  functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function);
389
390  EXPECT_EQ(42, functionPointer.usable());
391  EXPECT_TRUE(didCallAllocateCodeSection);
392
393  // Up to this point, the test is specific only to X86-64. But this next
394  // expectation is only valid on Darwin because it assumes that unwind
395  // data is made available only through compact_unwind. It would be
396  // worthwhile to extend this to handle non-Darwin platforms, in which
397  // case you'd want to look for an eh_frame or something.
398  //
399  // FIXME: Currently, MCJIT relies on a configure-time check to determine which
400  // sections to emit. The JIT client should have runtime control over this.
401  EXPECT_TRUE(
402    Triple(HostTriple).getOS() != Triple::Darwin ||
403    Triple(HostTriple).isMacOSXVersionLT(10, 7) ||
404    didAllocateCompactUnwindSection);
405}
406
407TEST_F(MCJITCAPITest, reserve_allocation_space) {
408  SKIP_UNSUPPORTED_PLATFORM;
409
410  TestReserveAllocationSpaceMemoryManager* MM = new TestReserveAllocationSpaceMemoryManager();
411
412  buildModuleWithCodeAndData();
413  buildMCJITOptions();
414  Options.MCJMM = wrap(MM);
415  buildMCJITEngine();
416  buildAndRunPasses();
417
418  union {
419    void *raw;
420    int (*usable)();
421  } GetGlobalFct;
422  GetGlobalFct.raw = LLVMGetPointerToGlobal(Engine, Function);
423
424  union {
425    void *raw;
426    void (*usable)(int);
427  } SetGlobalFct;
428  SetGlobalFct.raw = LLVMGetPointerToGlobal(Engine, Function2);
429
430  SetGlobalFct.usable(789);
431  EXPECT_EQ(789, GetGlobalFct.usable());
432  EXPECT_LE(MM->UsedCodeSize, MM->ReservedCodeSize);
433  EXPECT_LE(MM->UsedDataSizeRO, MM->ReservedDataSizeRO);
434  EXPECT_LE(MM->UsedDataSizeRW, MM->ReservedDataSizeRW);
435  EXPECT_TRUE(MM->UsedCodeSize > 0);
436  EXPECT_TRUE(MM->UsedDataSizeRW > 0);
437}
438
439TEST_F(MCJITCAPITest, yield) {
440  SKIP_UNSUPPORTED_PLATFORM;
441
442  buildSimpleFunction();
443  buildMCJITOptions();
444  buildMCJITEngine();
445  LLVMContextRef C = LLVMGetGlobalContext();
446  LLVMContextSetYieldCallback(C, yield, nullptr);
447  buildAndRunPasses();
448
449  union {
450    void *raw;
451    int (*usable)();
452  } functionPointer;
453  functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function);
454
455  EXPECT_EQ(42, functionPointer.usable());
456  EXPECT_TRUE(didCallYield);
457}
458
459