diff options
Diffstat (limited to 'unittests/ExecutionEngine/MCJIT')
-rw-r--r-- | unittests/ExecutionEngine/MCJIT/CMakeLists.txt | 2 | ||||
-rw-r--r-- | unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp | 97 | ||||
-rw-r--r-- | unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp | 240 | ||||
-rw-r--r-- | unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h | 77 | ||||
-rw-r--r-- | unittests/ExecutionEngine/MCJIT/MCJITTestBase.h | 50 |
5 files changed, 418 insertions, 48 deletions
diff --git a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt index c6b1f77..922cb7e 100644 --- a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt +++ b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt @@ -9,7 +9,9 @@ set(LLVM_LINK_COMPONENTS set(MCJITTestsSources MCJITTest.cpp + MCJITCAPITest.cpp MCJITMemoryManagerTest.cpp + MCJITObjectCacheTest.cpp ) if(MSVC) diff --git a/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp new file mode 100644 index 0000000..07ea1af --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITCAPITest.cpp @@ -0,0 +1,97 @@ +//===- 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"); + + 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); + + union { + void *raw; + int (*usable)(); + } functionPointer; + functionPointer.raw = LLVMGetPointerToGlobal(engine, function); + + EXPECT_EQ(42, functionPointer.usable()); + + LLVMDisposeExecutionEngine(engine); +} + diff --git a/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp new file mode 100644 index 0000000..0061e30 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp @@ -0,0 +1,240 @@ +//===- MCJITObjectCacheTest.cpp - Unit tests for MCJIT object caching -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "MCJITTestBase.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class TestObjectCache : public ObjectCache { +public: + TestObjectCache() : DuplicateInserted(false) { } + + virtual ~TestObjectCache() { + // Free any buffers we've allocated. + SmallVector<MemoryBuffer *, 2>::iterator it, end; + end = AllocatedBuffers.end(); + for (it = AllocatedBuffers.begin(); it != end; ++it) { + delete *it; + } + AllocatedBuffers.clear(); + } + + virtual void notifyObjectCompiled(const Module *M, const MemoryBuffer *Obj) { + // If we've seen this module before, note that. + const std::string ModuleID = M->getModuleIdentifier(); + if (ObjMap.find(ModuleID) != ObjMap.end()) + DuplicateInserted = true; + // Store a copy of the buffer in our map. + ObjMap[ModuleID] = copyBuffer(Obj); + } + + // Test-harness-specific functions + bool wereDuplicatesInserted() { return DuplicateInserted; } + + bool wasModuleLookedUp(const Module *M) { + return ModulesLookedUp.find(M->getModuleIdentifier()) + != ModulesLookedUp.end(); + } + + const MemoryBuffer* getObjectInternal(const Module* M) { + // Look for the module in our map. + const std::string ModuleID = M->getModuleIdentifier(); + StringMap<const MemoryBuffer *>::iterator it = ObjMap.find(ModuleID); + if (it == ObjMap.end()) + return 0; + return it->second; + } + +protected: + virtual const MemoryBuffer* getObject(const Module* M) { + const MemoryBuffer* BufferFound = getObjectInternal(M); + ModulesLookedUp.insert(M->getModuleIdentifier()); + return BufferFound; + } + +private: + MemoryBuffer *copyBuffer(const MemoryBuffer *Buf) { + // Create a local copy of the buffer. + MemoryBuffer *NewBuffer = MemoryBuffer::getMemBufferCopy(Buf->getBuffer()); + AllocatedBuffers.push_back(NewBuffer); + return NewBuffer; + } + + StringMap<const MemoryBuffer *> ObjMap; + StringSet<> ModulesLookedUp; + SmallVector<MemoryBuffer *, 2> AllocatedBuffers; + bool DuplicateInserted; +}; + +class MCJITObjectCacheTest : public testing::Test, public MCJITTestBase { +protected: + + enum { + OriginalRC = 6, + ReplacementRC = 7 + }; + + virtual void SetUp() { + M.reset(createEmptyModule("<main>")); + Main = insertMainFunction(M.get(), OriginalRC); + } + + void compileAndRun(int ExpectedRC = OriginalRC) { + // This function shouldn't be called until after SetUp. + ASSERT_TRUE(0 != TheJIT); + ASSERT_TRUE(0 != Main); + + TheJIT->finalizeObject(); + void *vPtr = TheJIT->getPointerToFunction(Main); + + static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache(); + + EXPECT_TRUE(0 != vPtr) + << "Unable to get pointer to main() from JIT"; + + int (*FuncPtr)(void) = (int(*)(void))(intptr_t)vPtr; + int returnCode = FuncPtr(); + EXPECT_EQ(returnCode, ExpectedRC); + } + + Function *Main; +}; + +TEST_F(MCJITObjectCacheTest, SetNullObjectCache) { + SKIP_UNSUPPORTED_PLATFORM; + + createJIT(M.take()); + + TheJIT->setObjectCache(NULL); + + compileAndRun(); +} + + +TEST_F(MCJITObjectCacheTest, VerifyBasicObjectCaching) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<TestObjectCache> Cache(new TestObjectCache); + + // Save a copy of the module pointer before handing it off to MCJIT. + const Module * SavedModulePointer = M.get(); + + createJIT(M.take()); + + TheJIT->setObjectCache(Cache.get()); + + // Verify that our object cache does not contain the module yet. + const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SavedModulePointer); + EXPECT_EQ(0, ObjBuffer); + + compileAndRun(); + + // Verify that MCJIT tried to look-up this module in the cache. + EXPECT_TRUE(Cache->wasModuleLookedUp(SavedModulePointer)); + + // Verify that our object cache now contains the module. + ObjBuffer = Cache->getObjectInternal(SavedModulePointer); + EXPECT_TRUE(0 != ObjBuffer); + + // Verify that the cache was only notified once. + EXPECT_FALSE(Cache->wereDuplicatesInserted()); +} + +TEST_F(MCJITObjectCacheTest, VerifyLoadFromCache) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<TestObjectCache> Cache(new TestObjectCache); + + // Compile this module with an MCJIT engine + createJIT(M.take()); + TheJIT->setObjectCache(Cache.get()); + TheJIT->finalizeObject(); + + // Destroy the MCJIT engine we just used + TheJIT.reset(); + + // Create a new memory manager. + MM = new SectionMemoryManager; + + // Create a new module and save it. Use a different return code so we can + // tell if MCJIT compiled this module or used the cache. + M.reset(createEmptyModule("<main>")); + Main = insertMainFunction(M.get(), ReplacementRC); + const Module * SecondModulePointer = M.get(); + + // Create a new MCJIT instance to load this module then execute it. + createJIT(M.take()); + TheJIT->setObjectCache(Cache.get()); + compileAndRun(); + + // Verify that MCJIT tried to look-up this module in the cache. + EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer)); + + // Verify that MCJIT didn't try to cache this again. + EXPECT_FALSE(Cache->wereDuplicatesInserted()); +} + +TEST_F(MCJITObjectCacheTest, VerifyNonLoadFromCache) { + SKIP_UNSUPPORTED_PLATFORM; + + OwningPtr<TestObjectCache> Cache(new TestObjectCache); + + // Compile this module with an MCJIT engine + createJIT(M.take()); + TheJIT->setObjectCache(Cache.get()); + TheJIT->finalizeObject(); + + // Destroy the MCJIT engine we just used + TheJIT.reset(); + + // Create a new memory manager. + MM = new SectionMemoryManager; + + // Create a new module and save it. Use a different return code so we can + // tell if MCJIT compiled this module or used the cache. Note that we use + // a new module name here so the module shouldn't be found in the cache. + M.reset(createEmptyModule("<not-main>")); + Main = insertMainFunction(M.get(), ReplacementRC); + const Module * SecondModulePointer = M.get(); + + // Create a new MCJIT instance to load this module then execute it. + createJIT(M.take()); + TheJIT->setObjectCache(Cache.get()); + + // Verify that our object cache does not contain the module yet. + const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SecondModulePointer); + EXPECT_EQ(0, ObjBuffer); + + // Run the function and look for the replacement return code. + compileAndRun(ReplacementRC); + + // Verify that MCJIT tried to look-up this module in the cache. + EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer)); + + // Verify that our object cache now contains the module. + ObjBuffer = Cache->getObjectInternal(SecondModulePointer); + EXPECT_TRUE(0 != ObjBuffer); + + // Verify that MCJIT didn't try to cache this again. + EXPECT_FALSE(Cache->wereDuplicatesInserted()); +} + +} // Namespace + diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h b/unittests/ExecutionEngine/MCJIT/MCJITTestAPICommon.h new file mode 100644 index 0000000..8160a18 --- /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<Triple::ArchType, 4> SupportedArchs; + SmallVector<Triple::OSType, 4> UnsupportedOSs; +}; + +} // namespace llvm + +#endif // MCJIT_TEST_API_COMMON_H + diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h index fc774ab..b0e98a8 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<Triple::ArchType, 4> SupportedArchs; - SmallVector<Triple::OSType, 4> UnsupportedOSs; - OwningPtr<Module> M; }; |