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).

llvm-svn: 182408
This commit is contained in:
Filip Pizlo 2013-05-21 20:00:56 +00:00
parent 34b9ee6f3b
commit e1e3f7cc01
6 changed files with 278 additions and 46 deletions

View File

@ -411,6 +411,7 @@ void LLVMShutdown();
/*===-- Error handling ----------------------------------------------------===*/
char *LLVMCreateMessage(const char *Message);
void LLVMDisposeMessage(char *Message);

View 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);
/**
* @}
*/

View 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

View 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);
}

View File

@ -58,6 +58,10 @@ void LLVMShutdown() {
/*===-- Error handling ----------------------------------------------------===*/
char *LLVMCreateMessage(const char *Message) {
return strdup(Message);
}
void LLVMDisposeMessage(char *Message) {
free(Message);
}

View File

@ -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<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);
}
virtual void SetUp() {
didCallAllocateCodeSection = 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 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);
}
LLVMModuleRef Module;
LLVMValueRef Function;
LLVMMCJITCompilerOptions Options;
LLVMExecutionEngineRef Engine;
char *Error;
};
TEST_F(MCJITCAPITest, simple_function) {
SKIP_UNSUPPORTED_PLATFORM;
char *error = 0;
// Creates a function that returns 42, compiles it, and runs it.
LLVMModuleRef module = LLVMModuleCreateWithName("simple_module");
LLVMSetTarget(module, HostTriple.c_str());
LLVMValueRef 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);
LLVMMCJITCompilerOptions options;
LLVMInitializeMCJITCompilerOptions(&options, sizeof(options));
options.OptLevel = 2;
// Just ensure that this field still exists.
options.NoFramePointerElim = false;
LLVMExecutionEngineRef engine;
ASSERT_EQ(
0, LLVMCreateMCJITCompilerForModule(&engine, module, &options,
sizeof(options), &error));
LLVMPassManagerRef pass = LLVMCreatePassManager();
LLVMAddTargetData(LLVMGetExecutionEngineTargetData(engine), pass);
LLVMAddConstantPropagationPass(pass);
LLVMAddInstructionCombiningPass(pass);
LLVMRunPassManager(pass, module);
LLVMDisposePassManager(pass);
buildSimpleFunction();
buildMCJITOptions();
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);
}
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);
}