aboutsummaryrefslogtreecommitdiffstats
path: root/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp')
-rw-r--r--unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp239
1 files changed, 237 insertions, 2 deletions
diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
index 46d6d9b..3813d59 100644
--- a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
+++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
@@ -13,18 +13,21 @@
//===----------------------------------------------------------------------===//
#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 "MCJITTestAPICommon.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,
@@ -40,6 +43,8 @@ static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size,
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);
}
@@ -60,6 +65,54 @@ static void roundTripDestroy(void *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() {
@@ -82,10 +135,13 @@ protected:
// The operating systems below are known to be sufficiently incompatible
// that they will fail the MCJIT C API tests.
UnsupportedOSs.push_back(Triple::Cygwin);
+
+ UnsupportedEnvironments.push_back(Triple::Cygnus);
}
virtual void SetUp() {
didCallAllocateCodeSection = false;
+ didAllocateCompactUnwindSection = false;
Module = 0;
Function = 0;
Engine = 0;
@@ -119,6 +175,84 @@ protected:
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;
@@ -135,7 +269,7 @@ protected:
roundTripFinalizeMemory,
roundTripDestroy);
}
-
+
void buildMCJITEngine() {
ASSERT_EQ(
0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options,
@@ -151,8 +285,41 @@ protected:
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;
@@ -194,3 +361,71 @@ TEST_F(MCJITCAPITest, custom_memory_manager) {
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);
+}