mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-28 22:20:37 +00:00
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
This commit is contained in:
parent
55977f6c37
commit
2d6d585c85
@ -7,3 +7,4 @@ add_llvm_unittest(ExecutionEngineTests
|
||||
)
|
||||
|
||||
add_subdirectory(JIT)
|
||||
add_subdirectory(MCJIT)
|
||||
|
25
unittests/ExecutionEngine/MCJIT/CMakeLists.txt
Normal file
25
unittests/ExecutionEngine/MCJIT/CMakeLists.txt
Normal file
@ -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()
|
226
unittests/ExecutionEngine/MCJIT/MCJITTest.cpp
Normal file
226
unittests/ExecutionEngine/MCJIT/MCJITTest.cpp
Normal file
@ -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("<main>"));
|
||||
}
|
||||
};
|
||||
|
||||
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<int32_t(void)>(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<int32_t(void)>(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<int32_t(void)>(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<int32_t(void)>(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("<other module>"));
|
||||
TheJIT->addModule(createEmptyModule("<other other module>"));
|
||||
|
||||
// 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("<caller module>"));
|
||||
|
||||
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();
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
245
unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
Normal file
245
unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
Normal file
@ -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<typename FuncType>
|
||||
Function *startFunction(Module *M, StringRef Name) {
|
||||
Function *Result = Function::Create(
|
||||
TypeBuilder<FuncType, false>::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<typename Signature>
|
||||
Function *insertSimpleCallFunction(Module *M, Function *Callee) {
|
||||
Function *Result = startFunction<Signature>(M, "caller");
|
||||
|
||||
SmallVector<Value*, 1> 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<int32_t(void)>(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<int32_t(int32_t, int32_t)>(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<types::i<32>, 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<std::string, 1> MAttrs;
|
||||
OwningPtr<TargetMachine> TM;
|
||||
OwningPtr<ExecutionEngine> TheJIT;
|
||||
IRBuilder<> Builder;
|
||||
JITMemoryManager *MM;
|
||||
|
||||
std::string HostTriple;
|
||||
SmallVector<Triple::ArchType, 4> SupportedArchs;
|
||||
SmallVector<Triple::OSType, 4> UnsupportedOSs;
|
||||
|
||||
OwningPtr<Module> M;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // MCJIT_TEST_H
|
1
unittests/ExecutionEngine/MCJIT/MCJITTests.def
Normal file
1
unittests/ExecutionEngine/MCJIT/MCJITTests.def
Normal file
@ -0,0 +1 @@
|
||||
EXPORTS
|
18
unittests/ExecutionEngine/MCJIT/Makefile
Normal file
18
unittests/ExecutionEngine/MCJIT/Makefile
Normal file
@ -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)
|
135
unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp
Normal file
135
unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp
Normal file
@ -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 <sys/stat.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#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
|
117
unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h
Normal file
117
unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h
Normal file
@ -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<sys::MemoryBlock, 16> AllocatedDataMem;
|
||||
SmallVector<sys::MemoryBlock, 16> AllocatedCodeMem;
|
||||
SmallVector<sys::MemoryBlock, 16> 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
|
@ -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
|
||||
|
@ -17,6 +17,7 @@ add_llvm_unittest(SupportTests
|
||||
LeakDetectorTest.cpp
|
||||
ManagedStatic.cpp
|
||||
MathExtrasTest.cpp
|
||||
MemoryBufferTest.cpp
|
||||
MemoryTest.cpp
|
||||
Path.cpp
|
||||
RegexTest.cpp
|
||||
|
99
unittests/Support/MemoryBufferTest.cpp
Normal file
99
unittests/Support/MemoryBufferTest.cpp
Normal file
@ -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<MemoryBuffer> 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]);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user