Expose the RTDyldMemoryManager through the C API. This allows clients of
authorFilip Pizlo <fpizlo@apple.com>
Wed, 22 May 2013 02:46:43 +0000 (02:46 +0000)
committerFilip Pizlo <fpizlo@apple.com>
Wed, 22 May 2013 02:46:43 +0000 (02:46 +0000)
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
include/llvm-c/ExecutionEngine.h
include/llvm/ExecutionEngine/RTDyldMemoryManager.h
lib/ExecutionEngine/ExecutionEngineBindings.cpp
lib/IR/Core.cpp
unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp

index 6b62f33261ea1a60c5055c9119dc832b56db0d26..0920c5d0b0dc339d172bca8ba2c45a4db8f16ab4 100644 (file)
@@ -411,6 +411,7 @@ void LLVMShutdown();
 
 /*===-- Error handling ----------------------------------------------------===*/
 
+char *LLVMCreateMessage(const char *Message);
 void LLVMDisposeMessage(char *Message);
 
 
index 8fae77def443b97e05d27bfc8ef430f9a2e949c1..50fdb6bd7356e14d39961395a471cba646427d54 100644 (file)
@@ -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);
+
 /**
  * @}
  */
index 2e1fc565128daee966dd9eaa6a11db9d744cbaca..99e2594f3cc7e2727fe47a722100275181281a8c 100644 (file)
@@ -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
index f9b08a01ea7d61b0ad942dc7c9dcf3d25f7a7ba7..88e73bf253e496d2d46d316b714237212751569a 100644 (file)
@@ -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<GlobalValue>(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);
+}
+
index 889d5742906aae51bfd9bb02ca972c65b3651cb8..66610bd7f5bdf9836608de1774ca0686c07a2b28 100644 (file)
@@ -58,6 +58,10 @@ void LLVMShutdown() {
 
 /*===-- Error handling ----------------------------------------------------===*/
 
+char *LLVMCreateMessage(const char *Message) {
+  return strdup(Message);
+}
+
 void LLVMDisposeMessage(char *Message) {
   free(Message);
 }
index e49af05a235db05cb8be636b41870d5908e436d2..c434a7c0b25b149e121dd8d3bec8b31051143c42 100644 (file)
 #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<SectionMemoryManager*>(object)->allocateCodeSection(
+    size, alignment, sectionID);
+}
+
+static uint8_t *roundTripAllocateDataSection(void *object, uintptr_t size,
+                                             unsigned alignment,
+                                             unsigned sectionID,
+                                             LLVMBool isReadOnly) {
+  return static_cast<SectionMemoryManager*>(object)->allocateDataSection(
+    size, alignment, sectionID, 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);
+}
+
 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);
 }
-