From 2d6d585c85ce8c56461f17b7b49fff24eed7b8fb Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Thu, 4 Oct 2012 20:29:44 +0000 Subject: [PATCH] Adding MCJIT and MemoryBuffer unit tests Patch by Daniel Malea. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@165246 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/ExecutionEngine/CMakeLists.txt | 1 + .../ExecutionEngine/MCJIT/CMakeLists.txt | 25 ++ unittests/ExecutionEngine/MCJIT/MCJITTest.cpp | 226 ++++++++++++++++ .../ExecutionEngine/MCJIT/MCJITTestBase.h | 245 ++++++++++++++++++ .../ExecutionEngine/MCJIT/MCJITTests.def | 1 + unittests/ExecutionEngine/MCJIT/Makefile | 18 ++ .../MCJIT/SectionMemoryManager.cpp | 135 ++++++++++ .../MCJIT/SectionMemoryManager.h | 117 +++++++++ unittests/ExecutionEngine/Makefile | 2 +- unittests/Support/CMakeLists.txt | 1 + unittests/Support/MemoryBufferTest.cpp | 99 +++++++ 11 files changed, 869 insertions(+), 1 deletion(-) create mode 100644 unittests/ExecutionEngine/MCJIT/CMakeLists.txt create mode 100644 unittests/ExecutionEngine/MCJIT/MCJITTest.cpp create mode 100644 unittests/ExecutionEngine/MCJIT/MCJITTestBase.h create mode 100644 unittests/ExecutionEngine/MCJIT/MCJITTests.def create mode 100644 unittests/ExecutionEngine/MCJIT/Makefile create mode 100644 unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp create mode 100644 unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h create mode 100644 unittests/Support/MemoryBufferTest.cpp diff --git a/unittests/ExecutionEngine/CMakeLists.txt b/unittests/ExecutionEngine/CMakeLists.txt index 5fffadd4ca0..ed7f10a23c8 100644 --- a/unittests/ExecutionEngine/CMakeLists.txt +++ b/unittests/ExecutionEngine/CMakeLists.txt @@ -7,3 +7,4 @@ add_llvm_unittest(ExecutionEngineTests ) add_subdirectory(JIT) +add_subdirectory(MCJIT) diff --git a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt new file mode 100644 index 00000000000..3e9c5b631e4 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt @@ -0,0 +1,25 @@ +set(LLVM_LINK_COMPONENTS + asmparser + bitreader + bitwriter + mcjit + jit + nativecodegen + ) + +set(MCJITTestsSources + MCJITTest.cpp + SectionMemoryManager.cpp + ) + +if(MSVC) + list(APPEND MCJITTestsSources MCJITTests.def) +endif() + +add_llvm_unittest(MCJITTests + ${MCJITTestsSources} + ) + +if(MINGW OR CYGWIN) + set_property(TARGET MCJITTests PROPERTY LINK_FLAGS -Wl,--export-all-symbols) +endif() diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp new file mode 100644 index 00000000000..4644bf3c262 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp @@ -0,0 +1,226 @@ +//===- 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 such as making function +// calls, using global variables, and compiling multpile modules. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/MCJIT.h" +#include "MCJITTestBase.h" +#include "SectionMemoryManager.h" +#include "gtest/gtest.h" + +using namespace llvm; + +class MCJITTest : public testing::Test, public MCJITTestBase { +protected: + + virtual void SetUp() { + M.reset(createEmptyModule("
")); + } +}; + +namespace { + +// FIXME: In order to JIT an empty module, there needs to be +// an interface to ExecutionEngine that forces compilation but +// does require retrieval of a pointer to a function/global. +/* +TEST_F(MCJITTest, empty_module) { + createJIT(M.take()); + //EXPECT_NE(0, TheJIT->getObjectImage()) + // << "Unable to generate executable loaded object image"; +} +*/ + +TEST_F(MCJITTest, global_variable) { + SKIP_UNSUPPORTED_PLATFORM; + + int initialValue = 5; + GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue); + createJIT(M.take()); + void *globalPtr = TheJIT->getPointerToGlobal(Global); + EXPECT_TRUE(0 != globalPtr) + << "Unable to get pointer to global value from JIT"; + + EXPECT_EQ(initialValue, *(int32_t*)globalPtr) + << "Unexpected initial value of global"; +} + +TEST_F(MCJITTest, add_function) { + SKIP_UNSUPPORTED_PLATFORM; + + Function *F = insertAddFunction(M.get()); + createJIT(M.take()); + void *addPtr = TheJIT->getPointerToFunction(F); + EXPECT_TRUE(0 != addPtr) + << "Unable to get pointer to function from JIT"; + + int (*AddPtrTy)(int, int) = (int(*)(int, int))(intptr_t)addPtr; + EXPECT_EQ(0, AddPtrTy(0, 0)); + EXPECT_EQ(3, AddPtrTy(1, 2)); + EXPECT_EQ(-5, AddPtrTy(-2, -3)); +} + +TEST_F(MCJITTest, run_main) { + SKIP_UNSUPPORTED_PLATFORM; + + int rc = 6; + Function *Main = insertMainFunction(M.get(), 6); + createJIT(M.take()); + void *vPtr = TheJIT->getPointerToFunction(Main); + 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, rc); +} + +TEST_F(MCJITTest, return_global) { + SKIP_UNSUPPORTED_PLATFORM; + + int32_t initialNum = 7; + GlobalVariable *GV = insertGlobalInt32(M.get(), "myglob", initialNum); + + Function *ReturnGlobal = startFunction(M.get(), + "ReturnGlobal"); + Value *ReadGlobal = Builder.CreateLoad(GV); + endFunctionWithRet(ReturnGlobal, ReadGlobal); + + createJIT(M.take()); + void *rgvPtr = TheJIT->getPointerToFunction(ReturnGlobal); + EXPECT_TRUE(0 != rgvPtr); + + int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)rgvPtr; + EXPECT_EQ(initialNum, FuncPtr()) + << "Invalid value for global returned from JITted function"; +} + +// FIXME: This case fails due to a bug with getPointerToGlobal(). +// The bug is due to MCJIT not having an implementation of getPointerToGlobal() +// which results in falling back on the ExecutionEngine implementation that +// allocates a new memory block for the global instead of using the same +// global variable that is emitted by MCJIT. Hence, the pointer (gvPtr below) +// has the correct initial value, but updates to the real global (accessed by +// JITted code) are not propagated. Instead, getPointerToGlobal() should return +// a pointer into the loaded ObjectImage to reference the emitted global. +/* +TEST_F(MCJITTest, increment_global) { + SKIP_UNSUPPORTED_PLATFORM; + + int32_t initialNum = 5; + Function *IncrementGlobal = startFunction(M.get(), "IncrementGlobal"); + GlobalVariable *GV = insertGlobalInt32(M.get(), "my_global", initialNum); + Value *DerefGV = Builder.CreateLoad(GV); + Value *AddResult = Builder.CreateAdd(DerefGV, + ConstantInt::get(Context, APInt(32, 1))); + Builder.CreateStore(AddResult, GV); + endFunctionWithRet(IncrementGlobal, AddResult); + + createJIT(M.take()); + void *gvPtr = TheJIT->getPointerToGlobal(GV); + EXPECT_EQ(initialNum, *(int32_t*)gvPtr); + + void *vPtr = TheJIT->getPointerToFunction(IncrementGlobal); + EXPECT_TRUE(0 != vPtr) + << "Unable to get pointer to main() from JIT"; + + int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr; + + for(int i = 1; i < 3; ++i) { + int32_t result = FuncPtr(); + EXPECT_EQ(initialNum + i, result); // OK + EXPECT_EQ(initialNum + i, *(int32_t*)gvPtr); // FAILS + } +} +*/ + +TEST_F(MCJITTest, multiple_functions) { + SKIP_UNSUPPORTED_PLATFORM; + + unsigned int numLevels = 23; + int32_t innerRetVal= 5; + + Function *Inner = startFunction(M.get(), "Inner"); + endFunctionWithRet(Inner, ConstantInt::get(Context, APInt(32, innerRetVal))); + + Function *Outer; + for (unsigned int i = 0; i < numLevels; ++i) { + std::stringstream funcName; + funcName << "level_" << i; + Outer = startFunction(M.get(), funcName.str()); + Value *innerResult = Builder.CreateCall(Inner); + endFunctionWithRet(Outer, innerResult); + + Inner = Outer; + } + + createJIT(M.take()); + void *vPtr = TheJIT->getPointerToFunction(Outer); + EXPECT_TRUE(0 != vPtr) + << "Unable to get pointer to outer function from JIT"; + + int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr; + EXPECT_EQ(innerRetVal, FuncPtr()) + << "Incorrect result returned from function"; +} + +// FIXME: ExecutionEngine has no support empty modules +/* +TEST_F(MCJITTest, multiple_empty_modules) { + SKIP_UNSUPPORTED_PLATFORM; + + createJIT(M.take()); + // JIT-compile + EXPECT_NE(0, TheJIT->getObjectImage()) + << "Unable to generate executable loaded object image"; + + TheJIT->addModule(createEmptyModule("")); + TheJIT->addModule(createEmptyModule("")); + + // JIT again + EXPECT_NE(0, TheJIT->getObjectImage()) + << "Unable to generate executable loaded object image"; +} +*/ + +// FIXME: MCJIT must support multiple modules +/* +TEST_F(MCJITTest, multiple_modules) { + SKIP_UNSUPPORTED_PLATFORM; + + Function *Callee = insertAddFunction(M.get()); + createJIT(M.take()); + + // caller function is defined in a different module + M.reset(createEmptyModule("")); + + Function *CalleeRef = insertExternalReferenceToFunction(M.get(), Callee); + Function *Caller = insertSimpleCallFunction(M.get(), CalleeRef); + + TheJIT->addModule(M.take()); + + // get a function pointer in a module that was not used in EE construction + void *vPtr = TheJIT->getPointerToFunction(Caller); + EXPECT_NE(0, vPtr) + << "Unable to get pointer to caller function from JIT"; + + int(*FuncPtr)(int, int) = (int(*)(int, int))(intptr_t)vPtr; + EXPECT_EQ(0, FuncPtr(0, 0)); + EXPECT_EQ(30, FuncPtr(10, 20)); + EXPECT_EQ(-30, FuncPtr(-10, -20)); + + // ensure caller is destroyed before callee (free use before def) + M.reset(); +} +*/ + +} diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h new file mode 100644 index 00000000000..9b4a4ac3cf0 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h @@ -0,0 +1,245 @@ +//===- 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 common functionality required by the MCJIT unit tests, +// as well as logic to skip tests on unsupported architectures and operating +// systems. +// +//===----------------------------------------------------------------------===// + + +#ifndef MCJIT_TEST_BASE_H +#define MCJIT_TEST_BASE_H + +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Config/config.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Function.h" +#include "llvm/IRBuilder.h" +#include "llvm/LLVMContext.h" +#include "llvm/Module.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/TypeBuilder.h" + +#include "SectionMemoryManager.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 MCJITTestBase { +protected: + + MCJITTestBase() + : OptLevel(CodeGenOpt::None) + , RelocModel(Reloc::Default) + , CodeModel(CodeModel::Default) + , MArch("") + , Builder(Context) + , MM(new SectionMemoryManager) + , HostTriple(LLVM_HOSTTRIPLE) + { + 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. + 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 incompatible with MCJIT as + // they are copied from the test/ExecutionEngine/MCJIT/lit.local.cfg and + // should be kept in sync. + UnsupportedOSs.push_back(Triple::Cygwin); + 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)); + return M; + } + + template + Function *startFunction(Module *M, StringRef Name) { + Function *Result = Function::Create( + TypeBuilder::get(Context), + GlobalValue::ExternalLinkage, Name, M); + + BasicBlock *BB = BasicBlock::Create(Context, Name, Result); + Builder.SetInsertPoint(BB); + + return Result; + } + + void endFunctionWithRet(Function *Func, Value *RetValue) { + Builder.CreateRet(RetValue); + } + + // Inserts a simple function that invokes Callee and takes the same arguments: + // int Caller(...) { return Callee(...); } + template + Function *insertSimpleCallFunction(Module *M, Function *Callee) { + Function *Result = startFunction(M, "caller"); + + SmallVector CallArgs; + + Function::arg_iterator arg_iter = Result->arg_begin(); + for(;arg_iter != Result->arg_end(); ++arg_iter) + CallArgs.push_back(arg_iter); + + Value *ReturnCode = Builder.CreateCall(Callee, CallArgs); + Builder.CreateRet(ReturnCode); + return Result; + } + + // Inserts a function named 'main' that returns a uint32_t: + // int32_t main() { return X; } + // where X is given by returnCode + Function *insertMainFunction(Module *M, uint32_t returnCode) { + Function *Result = startFunction(M, "main"); + + Value *ReturnVal = ConstantInt::get(Context, APInt(32, returnCode)); + endFunctionWithRet(Result, ReturnVal); + + return Result; + } + + // Inserts a function + // int32_t add(int32_t a, int32_t b) { return a + b; } + // in the current module and returns a pointer to it. + Function *insertAddFunction(Module *M, StringRef Name = "add") { + Function *Result = startFunction(M, Name); + + Function::arg_iterator args = Result->arg_begin(); + Value *Arg1 = args; + Value *Arg2 = ++args; + Value *AddResult = Builder.CreateAdd(Arg1, Arg2); + + endFunctionWithRet(Result, AddResult); + + return Result; + } + + // Inserts an declaration to a function defined elsewhere + Function *insertExternalReferenceToFunction(Module *M, StringRef Name, + FunctionType *FuncTy) { + Function *Result = Function::Create(FuncTy, + GlobalValue::ExternalLinkage, + Name, M); + return Result; + } + + // Inserts an declaration to a function defined elsewhere + Function *insertExternalReferenceToFunction(Module *M, Function *Func) { + Function *Result = Function::Create(Func->getFunctionType(), + GlobalValue::AvailableExternallyLinkage, + Func->getName(), M); + return Result; + } + + // Inserts a global variable of type int32 + GlobalVariable *insertGlobalInt32(Module *M, + StringRef name, + int32_t InitialValue) { + Type *GlobalTy = TypeBuilder, true>::get(Context); + Constant *IV = ConstantInt::get(Context, APInt(32, InitialValue)); + GlobalVariable *Global = new GlobalVariable(*M, + GlobalTy, + false, + GlobalValue::ExternalLinkage, + IV, + name); + return Global; + } + + void createJIT(Module *M) { + + // Due to the EngineBuilder constructor, it is required to have a Module + // in order to construct an ExecutionEngine (i.e. MCJIT) + assert(M != 0 && "a non-null Module must be provided to create MCJIT"); + + EngineBuilder EB(M); + std::string Error; + TheJIT.reset(EB.setEngineKind(EngineKind::JIT) + .setUseMCJIT(true) /* can this be folded into the EngineKind enum? */ + .setJITMemoryManager(MM) + .setErrorStr(&Error) + .setOptLevel(CodeGenOpt::None) + .setAllocateGVsWithCode(false) /*does this do anything?*/ + .setCodeModel(CodeModel::JITDefault) + .setRelocationModel(Reloc::Default) + .setMArch(MArch) + .setMCPU(sys::getHostCPUName()) + //.setMAttrs(MAttrs) + .create()); + // At this point, we cannot modify the module any more. + assert(TheJIT.get() != NULL && "error creating MCJIT with EngineBuilder"); + } + + LLVMContext Context; + CodeGenOpt::Level OptLevel; + Reloc::Model RelocModel; + CodeModel::Model CodeModel; + StringRef MArch; + SmallVector MAttrs; + OwningPtr TM; + OwningPtr TheJIT; + IRBuilder<> Builder; + JITMemoryManager *MM; + + std::string HostTriple; + SmallVector SupportedArchs; + SmallVector UnsupportedOSs; + + OwningPtr M; +}; + +} // namespace llvm + +#endif // MCJIT_TEST_H diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTests.def b/unittests/ExecutionEngine/MCJIT/MCJITTests.def new file mode 100644 index 00000000000..aabd2247c04 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/MCJITTests.def @@ -0,0 +1 @@ +EXPORTS diff --git a/unittests/ExecutionEngine/MCJIT/Makefile b/unittests/ExecutionEngine/MCJIT/Makefile new file mode 100644 index 00000000000..454f83099d4 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/Makefile @@ -0,0 +1,18 @@ +##===- unittests/ExecutionEngine/MCJIT/Makefile ------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../.. +TESTNAME = MCJIT +LINK_COMPONENTS := core jit mcjit native support + +include $(LEVEL)/Makefile.config +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest + +# Permit these tests to use the MCJIT's symbolic lookup. +LD.Flags += $(RDYNAMIC) diff --git a/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp new file mode 100644 index 00000000000..7f3cf2455ec --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp @@ -0,0 +1,135 @@ +//===-- SectionMemoryManager.cpp - The memory manager for MCJIT -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the implementation of the section-based memory manager +// used by MCJIT. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Config/config.h" +#include "llvm/Support/DynamicLibrary.h" + +#include "SectionMemoryManager.h" + +#ifdef __linux__ +// These includes used by SectionMemoryManager::getPointerToNamedFunction() +// for Glibc trickery. Look comments in this function for more information. +#ifdef HAVE_SYS_STAT_H +#include +#endif +#include +#include +#endif + +namespace llvm { + +uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) { + if (!Alignment) + Alignment = 16; + uint8_t *Addr = (uint8_t*)calloc((Size + Alignment - 1)/Alignment, Alignment); + AllocatedDataMem.push_back(sys::MemoryBlock(Addr, Size)); + return Addr; +} + +uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) { + if (!Alignment) + Alignment = 16; + unsigned NeedAllocate = Alignment * ((Size + Alignment - 1)/Alignment + 1); + uintptr_t Addr = 0; + // Look in the list of free code memory regions and use a block there if one + // is available. + for (int i = 0, e = FreeCodeMem.size(); i != e; ++i) { + sys::MemoryBlock &MB = FreeCodeMem[i]; + if (MB.size() >= NeedAllocate) { + Addr = (uintptr_t)MB.base(); + uintptr_t EndOfBlock = Addr + MB.size(); + // Align the address. + Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); + // Store cutted free memory block. + FreeCodeMem[i] = sys::MemoryBlock((void*)(Addr + Size), + EndOfBlock - Addr - Size); + return (uint8_t*)Addr; + } + } + + // No pre-allocated free block was large enough. Allocate a new memory region. + sys::MemoryBlock MB = sys::Memory::AllocateRWX(NeedAllocate, 0, 0); + + AllocatedCodeMem.push_back(MB); + Addr = (uintptr_t)MB.base(); + uintptr_t EndOfBlock = Addr + MB.size(); + // Align the address. + Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); + // The AllocateRWX may allocate much more memory than we need. In this case, + // we store the unused memory as a free memory block. + unsigned FreeSize = EndOfBlock-Addr-Size; + if (FreeSize > 16) + FreeCodeMem.push_back(sys::MemoryBlock((void*)(Addr + Size), FreeSize)); + + // Return aligned address + return (uint8_t*)Addr; +} + +void SectionMemoryManager::invalidateInstructionCache() { + for (int i = 0, e = AllocatedCodeMem.size(); i != e; ++i) + sys::Memory::InvalidateInstructionCache(AllocatedCodeMem[i].base(), + AllocatedCodeMem[i].size()); +} + +void *SectionMemoryManager::getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure) { +#if defined(__linux__) + //===--------------------------------------------------------------------===// + // Function stubs that are invoked instead of certain library calls + // + // Force the following functions to be linked in to anything that uses the + // JIT. This is a hack designed to work around the all-too-clever Glibc + // strategy of making these functions work differently when inlined vs. when + // not inlined, and hiding their real definitions in a separate archive file + // that the dynamic linker can't see. For more info, search for + // 'libc_nonshared.a' on Google, or read http://llvm.org/PR274. + if (Name == "stat") return (void*)(intptr_t)&stat; + if (Name == "fstat") return (void*)(intptr_t)&fstat; + if (Name == "lstat") return (void*)(intptr_t)&lstat; + if (Name == "stat64") return (void*)(intptr_t)&stat64; + if (Name == "fstat64") return (void*)(intptr_t)&fstat64; + if (Name == "lstat64") return (void*)(intptr_t)&lstat64; + if (Name == "atexit") return (void*)(intptr_t)&atexit; + if (Name == "mknod") return (void*)(intptr_t)&mknod; +#endif // __linux__ + + const char *NameStr = Name.c_str(); + void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); + if (Ptr) return Ptr; + + // If it wasn't found and if it starts with an underscore ('_') character, + // try again without the underscore. + if (NameStr[0] == '_') { + Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr+1); + if (Ptr) return Ptr; + } + + if (AbortOnFailure) + report_fatal_error("Program used external function '" + Name + + "' which could not be resolved!"); + return 0; +} + +SectionMemoryManager::~SectionMemoryManager() { + for (unsigned i = 0, e = AllocatedCodeMem.size(); i != e; ++i) + sys::Memory::ReleaseRWX(AllocatedCodeMem[i]); + for (unsigned i = 0, e = AllocatedDataMem.size(); i != e; ++i) + free(AllocatedDataMem[i].base()); +} + +} // namespace llvm diff --git a/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h new file mode 100644 index 00000000000..fb6c0348b14 --- /dev/null +++ b/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h @@ -0,0 +1,117 @@ +//===-- SectionMemoryManager.h - Memory allocator for MCJIT -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of a section-based memory manager used by +// the MCJIT execution engine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H +#define LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Memory.h" + +namespace llvm { + +// Section-based memory manager for MCJIT +class SectionMemoryManager : public JITMemoryManager { + +public: + + SectionMemoryManager() { } + ~SectionMemoryManager(); + + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + virtual void *getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure = true); + + // Invalidate instruction cache for code sections. Some platforms with + // separate data cache and instruction cache require explicit cache flush, + // otherwise JIT code manipulations (like resolved relocations) will get to + // the data cache but not to the instruction cache. + virtual void invalidateInstructionCache(); + +private: + + SmallVector AllocatedDataMem; + SmallVector AllocatedCodeMem; + SmallVector FreeCodeMem; + +public: + /// + /// Functions below are not used by MCJIT, but must be implemented because + /// they are declared as pure virtuals in the base class. + /// + + virtual void setMemoryWritable() { + llvm_unreachable("Unexpected call!"); + } + virtual void setMemoryExecutable() { + llvm_unreachable("Unexpected call!"); + } + virtual void setPoisonMemory(bool poison) { + llvm_unreachable("Unexpected call!"); + } + virtual void AllocateGOT() { + llvm_unreachable("Unexpected call!"); + } + virtual uint8_t *getGOTBase() const { + llvm_unreachable("Unexpected call!"); + return 0; + } + virtual uint8_t *startFunctionBody(const Function *F, + uintptr_t &ActualSize){ + llvm_unreachable("Unexpected call!"); + return 0; + } + virtual uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, + unsigned Alignment) { + llvm_unreachable("Unexpected call!"); + return 0; + } + virtual void endFunctionBody(const Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd) { + llvm_unreachable("Unexpected call!"); + } + virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { + llvm_unreachable("Unexpected call!"); + return 0; + } + virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) { + llvm_unreachable("Unexpected call!"); + return 0; + } + virtual void deallocateFunctionBody(void *Body) { + llvm_unreachable("Unexpected call!"); + } + virtual uint8_t *startExceptionTable(const Function *F, + uintptr_t &ActualSize) { + llvm_unreachable("Unexpected call!"); + return 0; + } + virtual void endExceptionTable(const Function *F, uint8_t *TableStart, + uint8_t *TableEnd, uint8_t *FrameRegister) { + llvm_unreachable("Unexpected call!"); + } + virtual void deallocateExceptionTable(void *ET) { + llvm_unreachable("Unexpected call!"); + } +}; + +} + +#endif // LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H diff --git a/unittests/ExecutionEngine/Makefile b/unittests/ExecutionEngine/Makefile index 63508d2399b..ca1195631a2 100644 --- a/unittests/ExecutionEngine/Makefile +++ b/unittests/ExecutionEngine/Makefile @@ -10,7 +10,7 @@ LEVEL = ../.. TESTNAME = ExecutionEngine LINK_COMPONENTS :=interpreter -PARALLEL_DIRS = JIT +PARALLEL_DIRS = JIT MCJIT include $(LEVEL)/Makefile.config include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt index e562fbb18d6..09a0ea50d74 100644 --- a/unittests/Support/CMakeLists.txt +++ b/unittests/Support/CMakeLists.txt @@ -17,6 +17,7 @@ add_llvm_unittest(SupportTests LeakDetectorTest.cpp ManagedStatic.cpp MathExtrasTest.cpp + MemoryBufferTest.cpp MemoryTest.cpp Path.cpp RegexTest.cpp diff --git a/unittests/Support/MemoryBufferTest.cpp b/unittests/Support/MemoryBufferTest.cpp new file mode 100644 index 00000000000..6c78cd80e8b --- /dev/null +++ b/unittests/Support/MemoryBufferTest.cpp @@ -0,0 +1,99 @@ +//===- llvm/unittest/Support/MemoryBufferTest.cpp - MemoryBuffer tests ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements unit tests for the MemoryBuffer support class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/ADT/OwningPtr.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +class MemoryBufferTest : public testing::Test { +protected: + MemoryBufferTest() + : data("this is some data") + { } + + virtual void SetUp() { } + + typedef OwningPtr OwningBuffer; + + std::string data; +}; + +namespace { + +TEST_F(MemoryBufferTest, get) { + // Default name and null-terminator flag + OwningBuffer MB1(MemoryBuffer::getMemBuffer(data)); + EXPECT_TRUE(0 != MB1.get()); + + // RequiresNullTerminator = false + OwningBuffer MB2(MemoryBuffer::getMemBuffer(data, "one", false)); + EXPECT_TRUE(0 != MB2.get()); + + // RequiresNullTerminator = true + OwningBuffer MB3(MemoryBuffer::getMemBuffer(data, "two", true)); + EXPECT_TRUE(0 != MB3.get()); + + // verify all 3 buffers point to the same address + EXPECT_EQ(MB1->getBufferStart(), MB2->getBufferStart()); + EXPECT_EQ(MB2->getBufferStart(), MB3->getBufferStart()); + + // verify the original data is unmodified after deleting the buffers + MB1.reset(); + MB2.reset(); + MB3.reset(); + EXPECT_EQ("this is some data", data); +} + +TEST_F(MemoryBufferTest, copy) { + // copy with no name + OwningBuffer MBC1(MemoryBuffer::getMemBufferCopy(data)); + EXPECT_TRUE(0 != MBC1.get()); + + // copy with a name + OwningBuffer MBC2(MemoryBuffer::getMemBufferCopy(data, "copy")); + EXPECT_TRUE(0 != MBC2.get()); + + // verify the two copies do not point to the same place + EXPECT_NE(MBC1->getBufferStart(), MBC2->getBufferStart()); +} + +TEST_F(MemoryBufferTest, make_new) { + // 0-sized buffer + OwningBuffer Zero(MemoryBuffer::getNewUninitMemBuffer(0)); + EXPECT_TRUE(0 != Zero.get()); + + // uninitialized buffer with no name + OwningBuffer One(MemoryBuffer::getNewUninitMemBuffer(321)); + EXPECT_TRUE(0 != One.get()); + + // uninitialized buffer with name + OwningBuffer Two(MemoryBuffer::getNewUninitMemBuffer(123, "bla")); + EXPECT_TRUE(0 != Two.get()); + + // 0-initialized buffer with no name + OwningBuffer Three(MemoryBuffer::getNewMemBuffer(321, data)); + EXPECT_TRUE(0 != Three.get()); + for (size_t i = 0; i < 321; ++i) + EXPECT_EQ(0, Three->getBufferStart()[0]); + + // 0-initialized buffer with name + OwningBuffer Four(MemoryBuffer::getNewMemBuffer(123, "zeros")); + EXPECT_TRUE(0 != Four.get()); + for (size_t i = 0; i < 123; ++i) + EXPECT_EQ(0, Four->getBufferStart()[0]); +} + +}