Skip to content

Commit

Permalink
Exposing MCJIT through C API
Browse files Browse the repository at this point in the history
Patch by Filip Pizlo



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@180229 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
Andrew Kaylor committed Apr 24, 2013
1 parent a264a20 commit 8c31b29
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 48 deletions.
28 changes: 28 additions & 0 deletions include/llvm-c/ExecutionEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@ extern "C" {
*/

void LLVMLinkInJIT(void);
void LLVMLinkInMCJIT(void);
void LLVMLinkInInterpreter(void);

typedef struct LLVMOpaqueGenericValue *LLVMGenericValueRef;
typedef struct LLVMOpaqueExecutionEngine *LLVMExecutionEngineRef;

struct LLVMMCJITCompilerOptions {
unsigned OptLevel;
LLVMBool NoFramePointerElim;
};

/*===-- Operations on generic values --------------------------------------===*/

LLVMGenericValueRef LLVMCreateGenericValueOfInt(LLVMTypeRef Ty,
Expand Down Expand Up @@ -75,6 +81,28 @@ LLVMBool LLVMCreateJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
unsigned OptLevel,
char **OutError);

/**
* Create an MCJIT execution engine for a module, with the given options. It is
* the responsibility of the caller to ensure that all fields in Options up to
* the given SizeOfOptions are initialized. It is correct to pass a smaller value
* of SizeOfOptions that omits some fields, and it is also correct to set any
* field to zero. The canonical way of using this is:
*
* LLVMMCJITCompilerOptions options;
* memset(&options, 0, sizeof(options));
* ... fill in those options you care about
* LLVMCreateMCJITCompilerForModule(&jit, mod, &options, sizeof(options), &error);
*
* Note that this is also correct, though possibly suboptimal:
*
* LLVMCreateMCJITCompilerForModule(&jit, mod, 0, 0, &error);
*/
LLVMBool LLVMCreateMCJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
LLVMModuleRef M,
struct LLVMMCJITCompilerOptions *Options,
size_t SizeOfOptions,
char **OutError);

/** Deprecated: Use LLVMCreateExecutionEngineForModule instead. */
LLVMBool LLVMCreateExecutionEngine(LLVMExecutionEngineRef *OutEE,
LLVMModuleProviderRef MP,
Expand Down
48 changes: 48 additions & 0 deletions lib/ExecutionEngine/ExecutionEngineBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "llvm-c/ExecutionEngine.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/Support/ErrorHandling.h"
#include <cstring>

Expand Down Expand Up @@ -152,6 +153,47 @@ LLVMBool LLVMCreateJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
return 1;
}

LLVMBool LLVMCreateMCJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
LLVMModuleRef M,
LLVMMCJITCompilerOptions *PassedOptions,
size_t SizeOfPassedOptions,
char **OutError) {
LLVMMCJITCompilerOptions options;
// If the user passed a larger sized options struct, then they were compiled
// against a newer LLVM. Tell them that something is wrong.
if (SizeOfPassedOptions > sizeof(options)) {
*OutError = strdup(
"Refusing to use options struct that is larger than my own; assuming LLVM "
"library mismatch.");
return 1;
}

// Defend against the user having an old version of the API by ensuring that
// any fields they didn't see are cleared. We must defend against fields being
// set to the bitwise equivalent of zero, and assume that this means "do the
// default" as if that option hadn't been available.
memset(&options, 0, sizeof(options));
memcpy(&options, PassedOptions, SizeOfPassedOptions);

TargetOptions targetOptions;
targetOptions.NoFramePointerElim = options.NoFramePointerElim;

std::string Error;
EngineBuilder builder(unwrap(M));
builder.setEngineKind(EngineKind::JIT)
.setErrorStr(&Error)
.setUseMCJIT(true)
.setOptLevel((CodeGenOpt::Level)options.OptLevel)
.setJITMemoryManager(new SectionMemoryManager())
.setTargetOptions(targetOptions);
if (ExecutionEngine *JIT = builder.create()) {
*OutJIT = wrap(JIT);
return 0;
}
*OutError = strdup(Error.c_str());
return 1;
}

LLVMBool LLVMCreateExecutionEngine(LLVMExecutionEngineRef *OutEE,
LLVMModuleProviderRef MP,
char **OutError) {
Expand Down Expand Up @@ -196,6 +238,8 @@ void LLVMRunStaticDestructors(LLVMExecutionEngineRef EE) {
int LLVMRunFunctionAsMain(LLVMExecutionEngineRef EE, LLVMValueRef F,
unsigned ArgC, const char * const *ArgV,
const char * const *EnvP) {
unwrap(EE)->finalizeObject();

std::vector<std::string> ArgVec;
for (unsigned I = 0; I != ArgC; ++I)
ArgVec.push_back(ArgV[I]);
Expand All @@ -206,6 +250,8 @@ int LLVMRunFunctionAsMain(LLVMExecutionEngineRef EE, LLVMValueRef F,
LLVMGenericValueRef LLVMRunFunction(LLVMExecutionEngineRef EE, LLVMValueRef F,
unsigned NumArgs,
LLVMGenericValueRef *Args) {
unwrap(EE)->finalizeObject();

std::vector<GenericValue> ArgVec;
ArgVec.reserve(NumArgs);
for (unsigned I = 0; I != NumArgs; ++I)
Expand Down Expand Up @@ -268,5 +314,7 @@ void LLVMAddGlobalMapping(LLVMExecutionEngineRef EE, LLVMValueRef Global,
}

void *LLVMGetPointerToGlobal(LLVMExecutionEngineRef EE, LLVMValueRef Global) {
unwrap(EE)->finalizeObject();

return unwrap(EE)->getPointerToGlobal(unwrap<GlobalValue>(Global));
}
1 change: 1 addition & 0 deletions unittests/ExecutionEngine/MCJIT/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ set(LLVM_LINK_COMPONENTS

set(MCJITTestsSources
MCJITTest.cpp
MCJITCAPITest.cpp
MCJITMemoryManagerTest.cpp
MCJITObjectCacheTest.cpp
)
Expand Down
93 changes: 93 additions & 0 deletions unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//===- 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 "llvm-c/Core.h"
#include "llvm-c/ExecutionEngine.h"
#include "llvm-c/Target.h"
#include "llvm-c/Transforms/Scalar.h"
#include "llvm/Support/Host.h"
#include "MCJITTestAPICommon.h"
#include "gtest/gtest.h"

using namespace llvm;

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::arm);
SupportedArchs.push_back(Triple::mips);
SupportedArchs.push_back(Triple::x86);
SupportedArchs.push_back(Triple::x86_64);

// The operating systems below are known to be sufficiently incompatible
// 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;

// Creates a function that returns 42, compiles it, and runs it.

LLVMModuleRef module = LLVMModuleCreateWithName("simple_module");

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;
memset(&options, 0, sizeof(options));
options.OptLevel = 2;
options.NoFramePointerElim = false; // Just ensure that this field still exists.

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

union {
void *raw;
int (*usable)();
} functionPointer;
functionPointer.raw = LLVMGetPointerToGlobal(engine, function);

EXPECT_EQ(42, functionPointer.usable());

LLVMDisposeExecutionEngine(engine);
}

77 changes: 77 additions & 0 deletions unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===- MCJITTestBase.h - Common base class for MCJIT Unit tests ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class implements functionality shared by both MCJIT C API tests, and
// the C++ API tests.
//
//===----------------------------------------------------------------------===//

#ifndef MCJIT_TEST_API_COMMON_H
#define MCJIT_TEST_API_COMMON_H

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetSelect.h"

// Used to skip tests on unsupported architectures and operating systems.
// To skip a test, add this macro at the top of a test-case in a suite that
// inherits from MCJITTestBase. See MCJITTest.cpp for examples.
#define SKIP_UNSUPPORTED_PLATFORM \
do \
if (!ArchSupportsMCJIT() || !OSSupportsMCJIT()) \
return; \
while(0)

namespace llvm {

class MCJITTestAPICommon {
protected:
MCJITTestAPICommon()
: HostTriple(sys::getProcessTriple())
{
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();

#ifdef LLVM_ON_WIN32
// On Windows, generate ELF objects by specifying "-elf" in triple
HostTriple += "-elf";
#endif // LLVM_ON_WIN32
HostTriple = Triple::normalize(HostTriple);
}

/// Returns true if the host architecture is known to support MCJIT
bool ArchSupportsMCJIT() {
Triple Host(HostTriple);
if (std::find(SupportedArchs.begin(), SupportedArchs.end(), Host.getArch())
== SupportedArchs.end()) {
return false;
}
return true;
}

/// Returns true if the host OS is known to support MCJIT
bool OSSupportsMCJIT() {
Triple Host(HostTriple);
if (std::find(UnsupportedOSs.begin(), UnsupportedOSs.end(), Host.getOS())
== UnsupportedOSs.end()) {
return true;
}
return false;
}

std::string HostTriple;
SmallVector<Triple::ArchType, 4> SupportedArchs;
SmallVector<Triple::OSType, 4> UnsupportedOSs;
};

} // namespace llvm

#endif // MCJIT_TEST_API_COMMON_H

50 changes: 2 additions & 48 deletions unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
#ifndef MCJIT_TEST_BASE_H
#define MCJIT_TEST_BASE_H

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Config/config.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
Expand All @@ -28,21 +26,11 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/TypeBuilder.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetSelect.h"

// Used to skip tests on unsupported architectures and operating systems.
// To skip a test, add this macro at the top of a test-case in a suite that
// inherits from MCJITTestBase. See MCJITTest.cpp for examples.
#define SKIP_UNSUPPORTED_PLATFORM \
do \
if (!ArchSupportsMCJIT() || !OSSupportsMCJIT()) \
return; \
while(0);
#include "MCJITTestAPICommon.h"

namespace llvm {

class MCJITTestBase {
class MCJITTestBase : public MCJITTestAPICommon {
protected:

MCJITTestBase()
Expand All @@ -52,17 +40,7 @@ class MCJITTestBase {
, MArch("")
, Builder(Context)
, MM(new SectionMemoryManager)
, HostTriple(sys::getProcessTriple())
{
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();

#ifdef LLVM_ON_WIN32
// On Windows, generate ELF objects by specifying "-elf" in triple
HostTriple += "-elf";
#endif // LLVM_ON_WIN32
HostTriple = Triple::normalize(HostTriple);

// 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.
Expand All @@ -78,26 +56,6 @@ class MCJITTestBase {
UnsupportedOSs.push_back(Triple::Darwin);
}

/// Returns true if the host architecture is known to support MCJIT
bool ArchSupportsMCJIT() {
Triple Host(HostTriple);
if (std::find(SupportedArchs.begin(), SupportedArchs.end(), Host.getArch())
== SupportedArchs.end()) {
return false;
}
return true;
}

/// Returns true if the host OS is known to support MCJIT
bool OSSupportsMCJIT() {
Triple Host(HostTriple);
if (std::find(UnsupportedOSs.begin(), UnsupportedOSs.end(), Host.getOS())
== UnsupportedOSs.end()) {
return true;
}
return false;
}

Module *createEmptyModule(StringRef Name) {
Module * M = new Module(Name, Context);
M->setTargetTriple(Triple::normalize(HostTriple));
Expand Down Expand Up @@ -232,10 +190,6 @@ class MCJITTestBase {
IRBuilder<> Builder;
JITMemoryManager *MM;

std::string HostTriple;
SmallVector<Triple::ArchType, 4> SupportedArchs;
SmallVector<Triple::OSType, 4> UnsupportedOSs;

OwningPtr<Module> M;
};

Expand Down

0 comments on commit 8c31b29

Please sign in to comment.