From d2755af8bda2e0fd80efb46556485c4cdbe8704a Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Mon, 29 Apr 2013 17:49:40 +0000 Subject: [PATCH] Exposing MCJIT through C API Re-submitting with fix for OCaml dependency problems (removing dependency on SectionMemoryManager when it isn't used). Patch by Fili Pizlo git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@180720 91177308-0d34-0410-b5e6-96231b3b80d8 --- bindings/ocaml/executionengine/Makefile | 2 +- include/llvm-c/ExecutionEngine.h | 28 ++++++ .../ExecutionEngineBindings.cpp | 46 +++++++++ lib/ExecutionEngine/MCJIT/MCJIT.cpp | 3 +- .../ExecutionEngine/MCJIT/CMakeLists.txt | 1 + .../ExecutionEngine/MCJIT/MCJITCAPITest.cpp | 93 +++++++++++++++++++ .../MCJIT/MCJITTestAPICommon.h | 77 +++++++++++++++ .../ExecutionEngine/MCJIT/MCJITTestBase.h | 50 +--------- 8 files changed, 250 insertions(+), 50 deletions(-) create mode 100644 unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp create mode 100644 unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h diff --git a/bindings/ocaml/executionengine/Makefile b/bindings/ocaml/executionengine/Makefile index 45b5049914b..5fa3f22048f 100644 --- a/bindings/ocaml/executionengine/Makefile +++ b/bindings/ocaml/executionengine/Makefile @@ -13,7 +13,7 @@ LEVEL := ../../.. LIBRARYNAME := llvm_executionengine -UsedComponents := executionengine jit interpreter mcjit native +UsedComponents := executionengine jit interpreter native UsedOcamlInterfaces := llvm llvm_target include ../Makefile.ocaml diff --git a/include/llvm-c/ExecutionEngine.h b/include/llvm-c/ExecutionEngine.h index be763312d22..8b654d5a5ca 100644 --- a/include/llvm-c/ExecutionEngine.h +++ b/include/llvm-c/ExecutionEngine.h @@ -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, @@ -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, diff --git a/lib/ExecutionEngine/ExecutionEngineBindings.cpp b/lib/ExecutionEngine/ExecutionEngineBindings.cpp index 3e6dcdf5ba7..44fa92205b2 100644 --- a/lib/ExecutionEngine/ExecutionEngineBindings.cpp +++ b/lib/ExecutionEngine/ExecutionEngineBindings.cpp @@ -152,6 +152,46 @@ 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) + .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) { @@ -196,6 +236,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 ArgVec; for (unsigned I = 0; I != ArgC; ++I) ArgVec.push_back(ArgV[I]); @@ -206,6 +248,8 @@ int LLVMRunFunctionAsMain(LLVMExecutionEngineRef EE, LLVMValueRef F, LLVMGenericValueRef LLVMRunFunction(LLVMExecutionEngineRef EE, LLVMValueRef F, unsigned NumArgs, LLVMGenericValueRef *Args) { + unwrap(EE)->finalizeObject(); + std::vector ArgVec; ArgVec.reserve(NumArgs); for (unsigned I = 0; I != NumArgs; ++I) @@ -268,5 +312,7 @@ void LLVMAddGlobalMapping(LLVMExecutionEngineRef EE, LLVMValueRef Global, } void *LLVMGetPointerToGlobal(LLVMExecutionEngineRef EE, LLVMValueRef Global) { + unwrap(EE)->finalizeObject(); + return unwrap(EE)->getPointerToGlobal(unwrap(Global)); } diff --git a/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/lib/ExecutionEngine/MCJIT/MCJIT.cpp index 9e42cd0441a..f4e5e61aa8e 100644 --- a/lib/ExecutionEngine/MCJIT/MCJIT.cpp +++ b/lib/ExecutionEngine/MCJIT/MCJIT.cpp @@ -14,6 +14,7 @@ #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectBuffer.h" #include "llvm/ExecutionEngine/ObjectImage.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" @@ -46,7 +47,7 @@ ExecutionEngine *MCJIT::createJIT(Module *M, // FIXME: Don't do this here. sys::DynamicLibrary::LoadLibraryPermanently(0, NULL); - return new MCJIT(M, TM, JMM, GVsWithCode); + return new MCJIT(M, TM, JMM ? JMM : new SectionMemoryManager(), GVsWithCode); } MCJIT::MCJIT(Module *m, TargetMachine *tm, RTDyldMemoryManager *MM, diff --git a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt index 9ffe6138ad7..922cb7efd5e 100644 --- a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt +++ b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt @@ -9,6 +9,7 @@ set(LLVM_LINK_COMPONENTS set(MCJITTestsSources MCJITTest.cpp + MCJITCAPITest.cpp MCJITMemoryManagerTest.cpp MCJITObjectCacheTest.cpp ) diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp new file mode 100644 index 00000000000..780fbc1b7b9 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp @@ -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); +} + diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h new file mode 100644 index 00000000000..8160a186f41 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h @@ -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 SupportedArchs; + SmallVector UnsupportedOSs; +}; + +} // namespace llvm + +#endif // MCJIT_TEST_API_COMMON_H + diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h index fc774abd621..b0e98a88def 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h +++ b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h @@ -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" @@ -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() @@ -52,17 +40,7 @@ protected: , 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. @@ -78,26 +56,6 @@ protected: 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)); @@ -232,10 +190,6 @@ protected: IRBuilder<> Builder; JITMemoryManager *MM; - std::string HostTriple; - SmallVector SupportedArchs; - SmallVector UnsupportedOSs; - OwningPtr M; };