| //===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // This test suite verifies basic MCJIT functionality when invoked form the C | 
 | // API. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "MCJITTestAPICommon.h" | 
 | #include "llvm-c/Analysis.h" | 
 | #include "llvm-c/Core.h" | 
 | #include "llvm-c/ExecutionEngine.h" | 
 | #include "llvm-c/Target.h" | 
 | #include "llvm-c/Transforms/PassManagerBuilder.h" | 
 | #include "llvm-c/Transforms/Scalar.h" | 
 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" | 
 | #include "llvm/Support/Debug.h" | 
 | #include "llvm/Support/Host.h" | 
 | #include "gtest/gtest.h" | 
 |  | 
 | using namespace llvm; | 
 |  | 
 | static bool didCallAllocateCodeSection; | 
 | static bool didAllocateCompactUnwindSection; | 
 | static bool didCallYield; | 
 |  | 
 | static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size, | 
 |                                              unsigned alignment, | 
 |                                              unsigned sectionID, | 
 |                                              const char *sectionName) { | 
 |   didCallAllocateCodeSection = true; | 
 |   return static_cast<SectionMemoryManager*>(object)->allocateCodeSection( | 
 |     size, alignment, sectionID, sectionName); | 
 | } | 
 |  | 
 | static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size, | 
 |                                              unsigned alignment, | 
 |                                              unsigned sectionID, | 
 |                                              const char *sectionName, | 
 |                                              LLVMBool isReadOnly) { | 
 |   if (!strcmp(sectionName, "__compact_unwind")) | 
 |     didAllocateCompactUnwindSection = true; | 
 |   return static_cast<SectionMemoryManager*>(object)->allocateDataSection( | 
 |     size, alignment, sectionID, sectionName, isReadOnly); | 
 | } | 
 |  | 
 | static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) { | 
 |   std::string errMsgString; | 
 |   bool result = | 
 |     static_cast<SectionMemoryManager*>(object)->finalizeMemory(&errMsgString); | 
 |   if (result) { | 
 |     *errMsg = LLVMCreateMessage(errMsgString.c_str()); | 
 |     return 1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | static void roundTripDestroy(void *object) { | 
 |   delete static_cast<SectionMemoryManager*>(object); | 
 | } | 
 |  | 
 | static void yield(LLVMContextRef, void *) { | 
 |   didCallYield = true; | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | // memory manager to test reserve allocation space callback | 
 | class TestReserveAllocationSpaceMemoryManager: public SectionMemoryManager { | 
 | public: | 
 |   uintptr_t ReservedCodeSize; | 
 |   uintptr_t UsedCodeSize; | 
 |   uintptr_t ReservedDataSizeRO; | 
 |   uintptr_t UsedDataSizeRO; | 
 |   uintptr_t ReservedDataSizeRW; | 
 |   uintptr_t UsedDataSizeRW; | 
 |    | 
 |   TestReserveAllocationSpaceMemoryManager() :  | 
 |     ReservedCodeSize(0), UsedCodeSize(0), ReservedDataSizeRO(0),  | 
 |     UsedDataSizeRO(0), ReservedDataSizeRW(0), UsedDataSizeRW(0) {     | 
 |   } | 
 |  | 
 |   bool needsToReserveAllocationSpace() override { return true; } | 
 |  | 
 |   void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign, | 
 |                               uintptr_t DataSizeRO, uint32_t RODataAlign, | 
 |                               uintptr_t DataSizeRW, | 
 |                               uint32_t RWDataAlign) override { | 
 |     ReservedCodeSize = CodeSize; | 
 |     ReservedDataSizeRO = DataSizeRO; | 
 |     ReservedDataSizeRW = DataSizeRW; | 
 |   } | 
 |  | 
 |   void useSpace(uintptr_t* UsedSize, uintptr_t Size, unsigned Alignment) { | 
 |     uintptr_t AlignedSize = (Size + Alignment - 1) / Alignment * Alignment; | 
 |     uintptr_t AlignedBegin = (*UsedSize + Alignment - 1) / Alignment * Alignment; | 
 |     *UsedSize = AlignedBegin + AlignedSize; | 
 |   } | 
 |  | 
 |   uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, | 
 |                                unsigned SectionID, StringRef SectionName, | 
 |                                bool IsReadOnly) override { | 
 |     useSpace(IsReadOnly ? &UsedDataSizeRO : &UsedDataSizeRW, Size, Alignment); | 
 |     return SectionMemoryManager::allocateDataSection(Size, Alignment,  | 
 |       SectionID, SectionName, IsReadOnly); | 
 |   } | 
 |  | 
 |   uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, | 
 |                                unsigned SectionID, | 
 |                                StringRef SectionName) override { | 
 |     useSpace(&UsedCodeSize, Size, Alignment); | 
 |     return SectionMemoryManager::allocateCodeSection(Size, Alignment,  | 
 |       SectionID, SectionName); | 
 |   } | 
 | }; | 
 |  | 
 | class MCJITCAPITest : public testing::Test, public MCJITTestAPICommon { | 
 | protected: | 
 |   MCJITCAPITest() { | 
 |     // The architectures below are known to be compatible with MCJIT as they | 
 |     // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be | 
 |     // kept in sync. | 
 |     SupportedArchs.push_back(Triple::aarch64); | 
 |     SupportedArchs.push_back(Triple::arm); | 
 |     SupportedArchs.push_back(Triple::mips); | 
 |     SupportedArchs.push_back(Triple::mips64); | 
 |     SupportedArchs.push_back(Triple::mips64el); | 
 |     SupportedArchs.push_back(Triple::x86); | 
 |     SupportedArchs.push_back(Triple::x86_64); | 
 |  | 
 |     // Some architectures have sub-architectures in which tests will fail, like | 
 |     // ARM. These two vectors will define if they do have sub-archs (to avoid | 
 |     // extra work for those who don't), and if so, if they are listed to work | 
 |     HasSubArchs.push_back(Triple::arm); | 
 |     SupportedSubArchs.push_back("armv6"); | 
 |     SupportedSubArchs.push_back("armv7"); | 
 |  | 
 |     // The operating systems below are known to be sufficiently incompatible | 
 |     // that they will fail the MCJIT C API tests. | 
 |     UnsupportedEnvironments.push_back(Triple::Cygnus); | 
 |   } | 
 |  | 
 |   void SetUp() override { | 
 |     didCallAllocateCodeSection = false; | 
 |     didAllocateCompactUnwindSection = false; | 
 |     didCallYield = false; | 
 |     Module = nullptr; | 
 |     Function = nullptr; | 
 |     Engine = nullptr; | 
 |     Error = nullptr; | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 |     if (Engine) | 
 |       LLVMDisposeExecutionEngine(Engine); | 
 |     else if (Module) | 
 |       LLVMDisposeModule(Module); | 
 |   } | 
 |    | 
 |   void buildSimpleFunction() { | 
 |     Module = LLVMModuleCreateWithName("simple_module"); | 
 |      | 
 |     LLVMSetTarget(Module, HostTriple.c_str()); | 
 |      | 
 |     Function = LLVMAddFunction(Module, "simple_function", | 
 |                                LLVMFunctionType(LLVMInt32Type(), nullptr,0, 0)); | 
 |     LLVMSetFunctionCallConv(Function, LLVMCCallConv); | 
 |      | 
 |     LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry"); | 
 |     LLVMBuilderRef builder = LLVMCreateBuilder(); | 
 |     LLVMPositionBuilderAtEnd(builder, entry); | 
 |     LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0)); | 
 |      | 
 |     LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error); | 
 |     LLVMDisposeMessage(Error); | 
 |      | 
 |     LLVMDisposeBuilder(builder); | 
 |   } | 
 |    | 
 |   void buildFunctionThatUsesStackmap() { | 
 |     Module = LLVMModuleCreateWithName("simple_module"); | 
 |      | 
 |     LLVMSetTarget(Module, HostTriple.c_str()); | 
 |      | 
 |     LLVMTypeRef stackmapParamTypes[] = { LLVMInt64Type(), LLVMInt32Type() }; | 
 |     LLVMValueRef stackmap = LLVMAddFunction( | 
 |       Module, "llvm.experimental.stackmap", | 
 |       LLVMFunctionType(LLVMVoidType(), stackmapParamTypes, 2, 1)); | 
 |     LLVMSetLinkage(stackmap, LLVMExternalLinkage); | 
 |      | 
 |     Function = LLVMAddFunction(Module, "simple_function", | 
 |                               LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0)); | 
 |      | 
 |     LLVMBasicBlockRef entry = LLVMAppendBasicBlock(Function, "entry"); | 
 |     LLVMBuilderRef builder = LLVMCreateBuilder(); | 
 |     LLVMPositionBuilderAtEnd(builder, entry); | 
 |     LLVMValueRef stackmapArgs[] = { | 
 |       LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt32Type(), 5, 0), | 
 |       LLVMConstInt(LLVMInt32Type(), 42, 0) | 
 |     }; | 
 |     LLVMBuildCall(builder, stackmap, stackmapArgs, 3, ""); | 
 |     LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0)); | 
 |      | 
 |     LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error); | 
 |     LLVMDisposeMessage(Error); | 
 |      | 
 |     LLVMDisposeBuilder(builder); | 
 |   } | 
 |    | 
 |   void buildModuleWithCodeAndData() { | 
 |     Module = LLVMModuleCreateWithName("simple_module"); | 
 |      | 
 |     LLVMSetTarget(Module, HostTriple.c_str()); | 
 |      | 
 |     // build a global int32 variable initialized to 42. | 
 |     LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "intVal");     | 
 |     LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0)); | 
 |      | 
 |     { | 
 |         Function = LLVMAddFunction(Module, "getGlobal", | 
 |                               LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0)); | 
 |         LLVMSetFunctionCallConv(Function, LLVMCCallConv); | 
 |          | 
 |         LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, "entry"); | 
 |         LLVMBuilderRef Builder = LLVMCreateBuilder(); | 
 |         LLVMPositionBuilderAtEnd(Builder, Entry); | 
 |          | 
 |         LLVMValueRef IntVal = LLVMBuildLoad(Builder, GlobalVar, "intVal"); | 
 |         LLVMBuildRet(Builder, IntVal); | 
 |          | 
 |         LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error); | 
 |         LLVMDisposeMessage(Error); | 
 |          | 
 |         LLVMDisposeBuilder(Builder); | 
 |     } | 
 |      | 
 |     { | 
 |         LLVMTypeRef ParamTypes[] = { LLVMInt32Type() }; | 
 |         Function2 = LLVMAddFunction( | 
 |           Module, "setGlobal", LLVMFunctionType(LLVMVoidType(), ParamTypes, 1, 0)); | 
 |         LLVMSetFunctionCallConv(Function2, LLVMCCallConv); | 
 |          | 
 |         LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function2, "entry"); | 
 |         LLVMBuilderRef Builder = LLVMCreateBuilder(); | 
 |         LLVMPositionBuilderAtEnd(Builder, Entry); | 
 |          | 
 |         LLVMValueRef Arg = LLVMGetParam(Function2, 0); | 
 |         LLVMBuildStore(Builder, Arg, GlobalVar); | 
 |         LLVMBuildRetVoid(Builder); | 
 |          | 
 |         LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error); | 
 |         LLVMDisposeMessage(Error); | 
 |          | 
 |         LLVMDisposeBuilder(Builder); | 
 |     } | 
 |   } | 
 |    | 
 |   void buildMCJITOptions() { | 
 |     LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options)); | 
 |     Options.OptLevel = 2; | 
 |      | 
 |     // Just ensure that this field still exists. | 
 |     Options.NoFramePointerElim = false; | 
 |   } | 
 |    | 
 |   void useRoundTripSectionMemoryManager() { | 
 |     Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager( | 
 |       new SectionMemoryManager(), | 
 |       roundTripAllocateCodeSection, | 
 |       roundTripAllocateDataSection, | 
 |       roundTripFinalizeMemory, | 
 |       roundTripDestroy); | 
 |   } | 
 |  | 
 |   void buildMCJITEngine() { | 
 |     ASSERT_EQ( | 
 |       0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options, | 
 |                                           sizeof(Options), &Error)); | 
 |   } | 
 |    | 
 |   void buildAndRunPasses() { | 
 |     LLVMPassManagerRef pass = LLVMCreatePassManager(); | 
 |     LLVMAddConstantPropagationPass(pass); | 
 |     LLVMAddInstructionCombiningPass(pass); | 
 |     LLVMRunPassManager(pass, Module); | 
 |     LLVMDisposePassManager(pass); | 
 |   } | 
 |    | 
 |   void buildAndRunOptPasses() { | 
 |     LLVMPassManagerBuilderRef passBuilder; | 
 |      | 
 |     passBuilder = LLVMPassManagerBuilderCreate(); | 
 |     LLVMPassManagerBuilderSetOptLevel(passBuilder, 2); | 
 |     LLVMPassManagerBuilderSetSizeLevel(passBuilder, 0); | 
 |      | 
 |     LLVMPassManagerRef functionPasses = | 
 |       LLVMCreateFunctionPassManagerForModule(Module); | 
 |     LLVMPassManagerRef modulePasses = | 
 |       LLVMCreatePassManager(); | 
 |      | 
 |     LLVMPassManagerBuilderPopulateFunctionPassManager(passBuilder, | 
 |                                                       functionPasses); | 
 |     LLVMPassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses); | 
 |      | 
 |     LLVMPassManagerBuilderDispose(passBuilder); | 
 |      | 
 |     LLVMInitializeFunctionPassManager(functionPasses); | 
 |     for (LLVMValueRef value = LLVMGetFirstFunction(Module); | 
 |          value; value = LLVMGetNextFunction(value)) | 
 |       LLVMRunFunctionPassManager(functionPasses, value); | 
 |     LLVMFinalizeFunctionPassManager(functionPasses); | 
 |      | 
 |     LLVMRunPassManager(modulePasses, Module); | 
 |      | 
 |     LLVMDisposePassManager(functionPasses); | 
 |     LLVMDisposePassManager(modulePasses); | 
 |   } | 
 |    | 
 |   LLVMModuleRef Module; | 
 |   LLVMValueRef Function; | 
 |   LLVMValueRef Function2; | 
 |   LLVMMCJITCompilerOptions Options; | 
 |   LLVMExecutionEngineRef Engine; | 
 |   char *Error; | 
 | }; | 
 | } // end anonymous namespace | 
 |  | 
 | TEST_F(MCJITCAPITest, simple_function) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |    | 
 |   buildSimpleFunction(); | 
 |   buildMCJITOptions(); | 
 |   buildMCJITEngine(); | 
 |   buildAndRunPasses(); | 
 |  | 
 |   auto *functionPointer = reinterpret_cast<int (*)()>( | 
 |       reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function))); | 
 |  | 
 |   EXPECT_EQ(42, functionPointer()); | 
 | } | 
 |  | 
 | TEST_F(MCJITCAPITest, gva) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |  | 
 |   Module = LLVMModuleCreateWithName("simple_module"); | 
 |   LLVMSetTarget(Module, HostTriple.c_str()); | 
 |   LLVMValueRef GlobalVar = LLVMAddGlobal(Module, LLVMInt32Type(), "simple_value"); | 
 |   LLVMSetInitializer(GlobalVar, LLVMConstInt(LLVMInt32Type(), 42, 0)); | 
 |  | 
 |   buildMCJITOptions(); | 
 |   buildMCJITEngine(); | 
 |   buildAndRunPasses(); | 
 |  | 
 |   uint64_t raw = LLVMGetGlobalValueAddress(Engine, "simple_value"); | 
 |   int32_t *usable  = (int32_t *) raw; | 
 |  | 
 |   EXPECT_EQ(42, *usable); | 
 | } | 
 |  | 
 | TEST_F(MCJITCAPITest, gfa) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |  | 
 |   buildSimpleFunction(); | 
 |   buildMCJITOptions(); | 
 |   buildMCJITEngine(); | 
 |   buildAndRunPasses(); | 
 |  | 
 |   uint64_t raw = LLVMGetFunctionAddress(Engine, "simple_function"); | 
 |   int (*usable)() = (int (*)()) raw; | 
 |  | 
 |   EXPECT_EQ(42, usable()); | 
 | } | 
 |  | 
 | TEST_F(MCJITCAPITest, custom_memory_manager) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |    | 
 |   buildSimpleFunction(); | 
 |   buildMCJITOptions(); | 
 |   useRoundTripSectionMemoryManager(); | 
 |   buildMCJITEngine(); | 
 |   buildAndRunPasses(); | 
 |  | 
 |   auto *functionPointer = reinterpret_cast<int (*)()>( | 
 |       reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function))); | 
 |  | 
 |   EXPECT_EQ(42, functionPointer()); | 
 |   EXPECT_TRUE(didCallAllocateCodeSection); | 
 | } | 
 |  | 
 | TEST_F(MCJITCAPITest, stackmap_creates_compact_unwind_on_darwin) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |    | 
 |   // This test is also not supported on non-x86 platforms. | 
 |   if (Triple(HostTriple).getArch() != Triple::x86_64) | 
 |     return; | 
 |    | 
 |   buildFunctionThatUsesStackmap(); | 
 |   buildMCJITOptions(); | 
 |   useRoundTripSectionMemoryManager(); | 
 |   buildMCJITEngine(); | 
 |   buildAndRunOptPasses(); | 
 |  | 
 |   auto *functionPointer = reinterpret_cast<int (*)()>( | 
 |       reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function))); | 
 |  | 
 |   EXPECT_EQ(42, functionPointer()); | 
 |   EXPECT_TRUE(didCallAllocateCodeSection); | 
 |    | 
 |   // Up to this point, the test is specific only to X86-64. But this next | 
 |   // expectation is only valid on Darwin because it assumes that unwind | 
 |   // data is made available only through compact_unwind. It would be | 
 |   // worthwhile to extend this to handle non-Darwin platforms, in which | 
 |   // case you'd want to look for an eh_frame or something. | 
 |   // | 
 |   // FIXME: Currently, MCJIT relies on a configure-time check to determine which | 
 |   // sections to emit. The JIT client should have runtime control over this. | 
 |   EXPECT_TRUE( | 
 |     Triple(HostTriple).getOS() != Triple::Darwin || | 
 |     Triple(HostTriple).isMacOSXVersionLT(10, 7) || | 
 |     didAllocateCompactUnwindSection); | 
 | } | 
 |  | 
 | TEST_F(MCJITCAPITest, reserve_allocation_space) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |    | 
 |   TestReserveAllocationSpaceMemoryManager* MM = new TestReserveAllocationSpaceMemoryManager(); | 
 |    | 
 |   buildModuleWithCodeAndData(); | 
 |   buildMCJITOptions(); | 
 |   Options.MCJMM = wrap(MM); | 
 |   buildMCJITEngine(); | 
 |   buildAndRunPasses(); | 
 |  | 
 |   auto GetGlobalFct = reinterpret_cast<int (*)()>( | 
 |       reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function))); | 
 |  | 
 |   auto SetGlobalFct = reinterpret_cast<void (*)(int)>( | 
 |       reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function2))); | 
 |  | 
 |   SetGlobalFct(789); | 
 |   EXPECT_EQ(789, GetGlobalFct()); | 
 |   EXPECT_LE(MM->UsedCodeSize, MM->ReservedCodeSize); | 
 |   EXPECT_LE(MM->UsedDataSizeRO, MM->ReservedDataSizeRO); | 
 |   EXPECT_LE(MM->UsedDataSizeRW, MM->ReservedDataSizeRW); | 
 |   EXPECT_TRUE(MM->UsedCodeSize > 0);  | 
 |   EXPECT_TRUE(MM->UsedDataSizeRW > 0); | 
 | } | 
 |  | 
 | TEST_F(MCJITCAPITest, yield) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |  | 
 |   buildSimpleFunction(); | 
 |   buildMCJITOptions(); | 
 |   buildMCJITEngine(); | 
 |   LLVMContextRef C = LLVMGetGlobalContext(); | 
 |   LLVMContextSetYieldCallback(C, yield, nullptr); | 
 |   buildAndRunPasses(); | 
 |  | 
 |   auto *functionPointer = reinterpret_cast<int (*)()>( | 
 |       reinterpret_cast<uintptr_t>(LLVMGetPointerToGlobal(Engine, Function))); | 
 |  | 
 |   EXPECT_EQ(42, functionPointer()); | 
 |   EXPECT_TRUE(didCallYield); | 
 | } | 
 |  | 
 | static int localTestFunc() { | 
 |   return 42; | 
 | } | 
 |  | 
 | TEST_F(MCJITCAPITest, addGlobalMapping) { | 
 |   SKIP_UNSUPPORTED_PLATFORM; | 
 |  | 
 |   Module = LLVMModuleCreateWithName("testModule"); | 
 |   LLVMSetTarget(Module, HostTriple.c_str()); | 
 |   LLVMTypeRef FunctionType = LLVMFunctionType(LLVMInt32Type(), nullptr, 0, 0); | 
 |   LLVMValueRef MappedFn = LLVMAddFunction(Module, "mapped_fn", FunctionType); | 
 |  | 
 |   Function = LLVMAddFunction(Module, "test_fn", FunctionType); | 
 |   LLVMBasicBlockRef Entry = LLVMAppendBasicBlock(Function, ""); | 
 |   LLVMBuilderRef Builder = LLVMCreateBuilder(); | 
 |   LLVMPositionBuilderAtEnd(Builder, Entry); | 
 |   LLVMValueRef RetVal = LLVMBuildCall(Builder, MappedFn, nullptr, 0, ""); | 
 |   LLVMBuildRet(Builder, RetVal); | 
 |   LLVMDisposeBuilder(Builder); | 
 |  | 
 |   LLVMVerifyModule(Module, LLVMAbortProcessAction, &Error); | 
 |   LLVMDisposeMessage(Error); | 
 |  | 
 |   buildMCJITOptions(); | 
 |   buildMCJITEngine(); | 
 |  | 
 |   LLVMAddGlobalMapping( | 
 |       Engine, MappedFn, | 
 |       reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(&localTestFunc))); | 
 |  | 
 |   buildAndRunPasses(); | 
 |  | 
 |   uint64_t raw = LLVMGetFunctionAddress(Engine, "test_fn"); | 
 |   int (*usable)() = (int (*)()) raw; | 
 |  | 
 |   EXPECT_EQ(42, usable()); | 
 | } |