From 6cfed36338d7728076ddbc1331908b887a4302d3 Mon Sep 17 00:00:00 2001 From: Filip Pizlo Date: Wed, 22 May 2013 02:46:43 +0000 Subject: [PATCH] Expose the RTDyldMemoryManager through the C API. This allows clients of the C API to provide their own way of allocating JIT memory (both code and data) and finalizing memory permissions (page protections, cache flush). git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@182448 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm-c/Core.h | 1 + include/llvm-c/ExecutionEngine.h | 28 +++ .../ExecutionEngine/RTDyldMemoryManager.h | 4 + .../ExecutionEngineBindings.cpp | 114 +++++++++++- lib/IR/Core.cpp | 4 + .../ExecutionEngine/MCJIT/MCJITCAPITest.cpp | 163 ++++++++++++++---- 6 files changed, 273 insertions(+), 41 deletions(-) diff --git a/include/llvm-c/Core.h b/include/llvm-c/Core.h index 6b62f33261e..0920c5d0b0d 100644 --- a/include/llvm-c/Core.h +++ b/include/llvm-c/Core.h @@ -411,6 +411,7 @@ void LLVMShutdown(); /*===-- Error handling ----------------------------------------------------===*/ +char *LLVMCreateMessage(const char *Message); void LLVMDisposeMessage(char *Message); diff --git a/include/llvm-c/ExecutionEngine.h b/include/llvm-c/ExecutionEngine.h index 8fae77def44..50fdb6bd735 100644 --- a/include/llvm-c/ExecutionEngine.h +++ b/include/llvm-c/ExecutionEngine.h @@ -40,12 +40,14 @@ void LLVMLinkInInterpreter(void); typedef struct LLVMOpaqueGenericValue *LLVMGenericValueRef; typedef struct LLVMOpaqueExecutionEngine *LLVMExecutionEngineRef; +typedef struct LLVMOpaqueMCJITMemoryManager *LLVMMCJITMemoryManagerRef; struct LLVMMCJITCompilerOptions { unsigned OptLevel; LLVMCodeModel CodeModel; LLVMBool NoFramePointerElim; LLVMBool EnableFastISel; + LLVMMCJITMemoryManagerRef MCJMM; }; /*===-- Operations on generic values --------------------------------------===*/ @@ -167,6 +169,32 @@ void LLVMAddGlobalMapping(LLVMExecutionEngineRef EE, LLVMValueRef Global, void *LLVMGetPointerToGlobal(LLVMExecutionEngineRef EE, LLVMValueRef Global); +/*===-- Operations on memory managers -------------------------------------===*/ + +/** + * Create a simple custom MCJIT memory manager. This memory manager can + * intercept allocations in a module-oblivious way. This will return NULL + * if any of the passed functions are NULL. + * + * @param Opaque An opaque client object to pass back to the callbacks. + * @param AllocateCodeSection Allocate a block of memory for executable code. + * @param AllocateDataSection Allocate a block of memory for data. + * @param FinalizeMemory Set page permissions and flush cache. Return 0 on + * success, 1 on error. + */ +LLVMMCJITMemoryManagerRef LLVMCreateSimpleMCJITMemoryManager( + void *Opaque, + uint8_t *(*AllocateCodeSection)(void *Opaque, + uintptr_t Size, unsigned Alignment, + unsigned SectionID), + uint8_t *(*AllocateDataSection)(void *Opaque, + uintptr_t Size, unsigned Alignment, + unsigned SectionID, LLVMBool IsReadOnly), + LLVMBool (*FinalizeMemory)(void *Opaque, char **ErrMsg), + void (*Destroy)(void *Opaque)); + +void LLVMDisposeMCJITMemoryManager(LLVMMCJITMemoryManagerRef MM); + /** * @} */ diff --git a/include/llvm/ExecutionEngine/RTDyldMemoryManager.h b/include/llvm/ExecutionEngine/RTDyldMemoryManager.h index 2e1fc565128..99e2594f3cc 100644 --- a/include/llvm/ExecutionEngine/RTDyldMemoryManager.h +++ b/include/llvm/ExecutionEngine/RTDyldMemoryManager.h @@ -71,6 +71,10 @@ public: virtual bool finalizeMemory(std::string *ErrMsg = 0) = 0; }; +// Create wrappers for C Binding types (see CBindingWrapping.h). +DEFINE_SIMPLE_CONVERSION_FUNCTIONS( + RTDyldMemoryManager, LLVMMCJITMemoryManagerRef) + } // namespace llvm #endif // LLVM_EXECUTIONENGINE_RT_DYLD_MEMORY_MANAGER_H diff --git a/lib/ExecutionEngine/ExecutionEngineBindings.cpp b/lib/ExecutionEngine/ExecutionEngineBindings.cpp index f9b08a01ea7..88e73bf253e 100644 --- a/lib/ExecutionEngine/ExecutionEngineBindings.cpp +++ b/lib/ExecutionEngine/ExecutionEngineBindings.cpp @@ -15,6 +15,7 @@ #include "llvm-c/ExecutionEngine.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/RTDyldMemoryManager.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Module.h" #include "llvm/Support/ErrorHandling.h" @@ -157,10 +158,8 @@ LLVMBool LLVMCreateJITCompilerForModule(LLVMExecutionEngineRef *OutJIT, void LLVMInitializeMCJITCompilerOptions(LLVMMCJITCompilerOptions *PassedOptions, size_t SizeOfPassedOptions) { LLVMMCJITCompilerOptions options; - options.OptLevel = 0; + memset(&options, 0, sizeof(options)); // Most fields are zero by default. options.CodeModel = LLVMCodeModelJITDefault; - options.NoFramePointerElim = false; - options.EnableFastISel = false; memcpy(PassedOptions, &options, std::min(sizeof(options), SizeOfPassedOptions)); @@ -199,6 +198,8 @@ LLVMBool LLVMCreateMCJITCompilerForModule( .setOptLevel((CodeGenOpt::Level)options.OptLevel) .setCodeModel(unwrap(options.CodeModel)) .setTargetOptions(targetOptions); + if (options.MCJMM) + builder.setMCJITMemoryManager(unwrap(options.MCJMM)); if (ExecutionEngine *JIT = builder.create()) { *OutJIT = wrap(JIT); return 0; @@ -332,3 +333,110 @@ void *LLVMGetPointerToGlobal(LLVMExecutionEngineRef EE, LLVMValueRef Global) { return unwrap(EE)->getPointerToGlobal(unwrap(Global)); } + +/*===-- Operations on memory managers -------------------------------------===*/ + +namespace { + +struct SimpleBindingMMFunctions { + uint8_t *(*AllocateCodeSection)(void *Opaque, + uintptr_t Size, unsigned Alignment, + unsigned SectionID); + uint8_t *(*AllocateDataSection)(void *Opaque, + uintptr_t Size, unsigned Alignment, + unsigned SectionID, LLVMBool IsReadOnly); + LLVMBool (*FinalizeMemory)(void *Opaque, char **ErrMsg); + void (*Destroy)(void *Opaque); +}; + +class SimpleBindingMemoryManager : public RTDyldMemoryManager { +public: + SimpleBindingMemoryManager(const SimpleBindingMMFunctions& Functions, + void *Opaque); + virtual ~SimpleBindingMemoryManager(); + + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, + bool isReadOnly); + + virtual bool finalizeMemory(std::string *ErrMsg); + +private: + SimpleBindingMMFunctions Functions; + void *Opaque; +}; + +SimpleBindingMemoryManager::SimpleBindingMemoryManager( + const SimpleBindingMMFunctions& Functions, + void *Opaque) + : Functions(Functions), Opaque(Opaque) { + assert(Functions.AllocateCodeSection && + "No AllocateCodeSection function provided!"); + assert(Functions.AllocateDataSection && + "No AllocateDataSection function provided!"); + assert(Functions.FinalizeMemory && + "No FinalizeMemory function provided!"); + assert(Functions.Destroy && + "No Destroy function provided!"); +} + +SimpleBindingMemoryManager::~SimpleBindingMemoryManager() { + Functions.Destroy(Opaque); +} + +uint8_t *SimpleBindingMemoryManager::allocateCodeSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID) { + return Functions.AllocateCodeSection(Opaque, Size, Alignment, SectionID); +} + +uint8_t *SimpleBindingMemoryManager::allocateDataSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID, bool isReadOnly) { + return Functions.AllocateDataSection(Opaque, Size, Alignment, SectionID, + isReadOnly); +} + +bool SimpleBindingMemoryManager::finalizeMemory(std::string *ErrMsg) { + char *errMsgCString = 0; + bool result = Functions.FinalizeMemory(Opaque, &errMsgCString); + assert((result || !errMsgCString) && + "Did not expect an error message if FinalizeMemory succeeded"); + if (errMsgCString) { + if (ErrMsg) + *ErrMsg = errMsgCString; + free(errMsgCString); + } + return result; +} + +} // anonymous namespace + +LLVMMCJITMemoryManagerRef LLVMCreateSimpleMCJITMemoryManager( + void *Opaque, + uint8_t *(*AllocateCodeSection)(void *Opaque, + uintptr_t Size, unsigned Alignment, + unsigned SectionID), + uint8_t *(*AllocateDataSection)(void *Opaque, + uintptr_t Size, unsigned Alignment, + unsigned SectionID, LLVMBool IsReadOnly), + LLVMBool (*FinalizeMemory)(void *Opaque, char **ErrMsg), + void (*Destroy)(void *Opaque)) { + + if (!AllocateCodeSection || !AllocateDataSection || !FinalizeMemory || + !Destroy) + return NULL; + + SimpleBindingMMFunctions functions; + functions.AllocateCodeSection = AllocateCodeSection; + functions.AllocateDataSection = AllocateDataSection; + functions.FinalizeMemory = FinalizeMemory; + functions.Destroy = Destroy; + return wrap(new SimpleBindingMemoryManager(functions, Opaque)); +} + +void LLVMDisposeMCJITMemoryManager(LLVMMCJITMemoryManagerRef MM) { + delete unwrap(MM); +} + diff --git a/lib/IR/Core.cpp b/lib/IR/Core.cpp index 889d5742906..66610bd7f5b 100644 --- a/lib/IR/Core.cpp +++ b/lib/IR/Core.cpp @@ -58,6 +58,10 @@ void LLVMShutdown() { /*===-- Error handling ----------------------------------------------------===*/ +char *LLVMCreateMessage(const char *Message) { + return strdup(Message); +} + void LLVMDisposeMessage(char *Message) { free(Message); } diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp index e49af05a235..c434a7c0b25 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp @@ -17,12 +17,46 @@ #include "llvm-c/ExecutionEngine.h" #include "llvm-c/Target.h" #include "llvm-c/Transforms/Scalar.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/Support/Host.h" #include "MCJITTestAPICommon.h" #include "gtest/gtest.h" using namespace llvm; +static bool didCallAllocateCodeSection; + +static uint8_t *roundTripAllocateCodeSection(void *object, uintptr_t size, + unsigned alignment, + unsigned sectionID) { + didCallAllocateCodeSection = true; + return static_cast(object)->allocateCodeSection( + size, alignment, sectionID); +} + +static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size, + unsigned alignment, + unsigned sectionID, + LLVMBool isReadOnly) { + return static_cast(object)->allocateDataSection( + size, alignment, sectionID, isReadOnly); +} + +static LLVMBool roundTripFinalizeMemory(void *object, char **errMsg) { + std::string errMsgString; + bool result = + static_cast(object)->finalizeMemory(&errMsgString); + if (result) { + *errMsg = LLVMCreateMessage(errMsgString.c_str()); + return 1; + } + return 0; +} + +static void roundTripDestroy(void *object) { + delete static_cast(object); +} + class MCJITCAPITest : public testing::Test, public MCJITTestAPICommon { protected: MCJITCAPITest() { @@ -46,60 +80,113 @@ protected: // that they will fail the MCJIT C API tests. UnsupportedOSs.push_back(Triple::Cygwin); } -}; - -TEST_F(MCJITCAPITest, simple_function) { - SKIP_UNSUPPORTED_PLATFORM; - char *error = 0; + virtual void SetUp() { + didCallAllocateCodeSection = false; + Module = 0; + Function = 0; + Engine = 0; + Error = 0; + } - // Creates a function that returns 42, compiles it, and runs it. + virtual void TearDown() { + if (Engine) + LLVMDisposeExecutionEngine(Engine); + else if (Module) + LLVMDisposeModule(Module); + } - LLVMModuleRef module = LLVMModuleCreateWithName("simple_module"); - - LLVMSetTarget(module, HostTriple.c_str()); + 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); + } - LLVMValueRef function = LLVMAddFunction( - module, "simple_function", LLVMFunctionType(LLVMInt32Type(), 0, 0, 0)); - LLVMSetFunctionCallConv(function, LLVMCCallConv); + void buildMCJITOptions() { + LLVMInitializeMCJITCompilerOptions(&Options, sizeof(Options)); + Options.OptLevel = 2; + + // Just ensure that this field still exists. + Options.NoFramePointerElim = false; + } - LLVMBasicBlockRef entry = LLVMAppendBasicBlock(function, "entry"); - LLVMBuilderRef builder = LLVMCreateBuilder(); - LLVMPositionBuilderAtEnd(builder, entry); - LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 42, 0)); + void useRoundTripSectionMemoryManager() { + Options.MCJMM = LLVMCreateSimpleMCJITMemoryManager( + new SectionMemoryManager(), + roundTripAllocateCodeSection, + roundTripAllocateDataSection, + roundTripFinalizeMemory, + roundTripDestroy); + } - LLVMVerifyModule(module, LLVMAbortProcessAction, &error); - LLVMDisposeMessage(error); + void buildMCJITEngine() { + ASSERT_EQ( + 0, LLVMCreateMCJITCompilerForModule(&Engine, Module, &Options, + sizeof(Options), &Error)); + } - LLVMDisposeBuilder(builder); + void buildAndRunPasses() { + LLVMPassManagerRef pass = LLVMCreatePassManager(); + LLVMAddTargetData(LLVMGetExecutionEngineTargetData(Engine), pass); + LLVMAddConstantPropagationPass(pass); + LLVMAddInstructionCombiningPass(pass); + LLVMRunPassManager(pass, Module); + LLVMDisposePassManager(pass); + } - LLVMMCJITCompilerOptions options; - LLVMInitializeMCJITCompilerOptions(&options, sizeof(options)); - options.OptLevel = 2; + LLVMModuleRef Module; + LLVMValueRef Function; + LLVMMCJITCompilerOptions Options; + LLVMExecutionEngineRef Engine; + char *Error; +}; + +TEST_F(MCJITCAPITest, simple_function) { + SKIP_UNSUPPORTED_PLATFORM; - // Just ensure that this field still exists. - options.NoFramePointerElim = false; + buildSimpleFunction(); + buildMCJITOptions(); + buildMCJITEngine(); + buildAndRunPasses(); + + union { + void *raw; + int (*usable)(); + } functionPointer; + functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); - LLVMExecutionEngineRef engine; - ASSERT_EQ( - 0, LLVMCreateMCJITCompilerForModule(&engine, module, &options, - sizeof(options), &error)); + EXPECT_EQ(42, functionPointer.usable()); +} + +TEST_F(MCJITCAPITest, custom_memory_manager) { + SKIP_UNSUPPORTED_PLATFORM; - LLVMPassManagerRef pass = LLVMCreatePassManager(); - LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), pass); - LLVMAddConstantPropagationPass(pass); - LLVMAddInstructionCombiningPass(pass); - LLVMRunPassManager(pass, module); - LLVMDisposePassManager(pass); + buildSimpleFunction(); + buildMCJITOptions(); + useRoundTripSectionMemoryManager(); + buildMCJITEngine(); + buildAndRunPasses(); union { void *raw; int (*usable)(); } functionPointer; - functionPointer.raw = LLVMGetPointerToGlobal(engine, function); + functionPointer.raw = LLVMGetPointerToGlobal(Engine, Function); EXPECT_EQ(42, functionPointer.usable()); - - LLVMDisposeExecutionEngine(engine); + EXPECT_TRUE(didCallAllocateCodeSection); } - -- 2.34.1