|  | //===- MCJITTest.cpp - Unit tests for the MCJIT ---------------------------===// | 
|  | // | 
|  | //                     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 "llvm-c/Analysis.h" | 
|  | #include "MCJITTestAPICommon.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 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); | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | } | 
|  |  | 
|  | virtual bool needsToReserveAllocationSpace() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | virtual void reserveAllocationSpace( | 
|  | uintptr_t CodeSize, uintptr_t DataSizeRO, uintptr_t DataSizeRW) { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | virtual uint8_t* allocateDataSection(uintptr_t Size, unsigned Alignment, | 
|  | unsigned SectionID, StringRef SectionName, bool IsReadOnly) { | 
|  | 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) { | 
|  | 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::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. | 
|  | UnsupportedOSs.push_back(Triple::Cygwin); | 
|  | } | 
|  |  | 
|  | virtual void SetUp() { | 
|  | didCallAllocateCodeSection = false; | 
|  | didAllocateCompactUnwindSection = false; | 
|  | Module = 0; | 
|  | Function = 0; | 
|  | Engine = 0; | 
|  | Error = 0; | 
|  | } | 
|  |  | 
|  | virtual void TearDown() { | 
|  | 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(), 0, 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(), 0, 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(), 0, 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(); | 
|  | LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), pass); | 
|  | 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(); | 
|  |  | 
|  | LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), modulePasses); | 
|  |  | 
|  | 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(); | 
|  |  | 
|  | union { | 
|  | void *raw; | 
|  | int (*usable)(); | 
|  | } functionPointer; | 
|  | functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); | 
|  |  | 
|  | EXPECT_EQ(42, functionPointer.usable()); | 
|  | } | 
|  |  | 
|  | TEST_F(MCJITCAPITest, custom_memory_manager) { | 
|  | SKIP_UNSUPPORTED_PLATFORM; | 
|  |  | 
|  | buildSimpleFunction(); | 
|  | buildMCJITOptions(); | 
|  | useRoundTripSectionMemoryManager(); | 
|  | buildMCJITEngine(); | 
|  | buildAndRunPasses(); | 
|  |  | 
|  | union { | 
|  | void *raw; | 
|  | int (*usable)(); | 
|  | } functionPointer; | 
|  | functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); | 
|  |  | 
|  | EXPECT_EQ(42, functionPointer.usable()); | 
|  | 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(); | 
|  |  | 
|  | union { | 
|  | void *raw; | 
|  | int (*usable)(); | 
|  | } functionPointer; | 
|  | functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); | 
|  |  | 
|  | EXPECT_EQ(42, functionPointer.usable()); | 
|  | 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(); | 
|  |  | 
|  | union { | 
|  | void *raw; | 
|  | int (*usable)(); | 
|  | } GetGlobalFct; | 
|  | GetGlobalFct.raw = LLVMGetPointerToGlobal(Engine, Function); | 
|  |  | 
|  | union { | 
|  | void *raw; | 
|  | void (*usable)(int); | 
|  | } SetGlobalFct; | 
|  | SetGlobalFct.raw = LLVMGetPointerToGlobal(Engine, Function2); | 
|  |  | 
|  | SetGlobalFct.usable(789); | 
|  | EXPECT_EQ(789, GetGlobalFct.usable()); | 
|  | 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); | 
|  | } |