mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-10-11 23:25:15 +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(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 = ../..
|
LEVEL = ../..
|
||||||
TESTNAME = ExecutionEngine
|
TESTNAME = ExecutionEngine
|
||||||
LINK_COMPONENTS :=interpreter
|
LINK_COMPONENTS :=interpreter
|
||||||
PARALLEL_DIRS = JIT
|
PARALLEL_DIRS = JIT MCJIT
|
||||||
|
|
||||||
include $(LEVEL)/Makefile.config
|
include $(LEVEL)/Makefile.config
|
||||||
include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
|
include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
|
||||||
|
@ -17,6 +17,7 @@ add_llvm_unittest(SupportTests
|
|||||||
LeakDetectorTest.cpp
|
LeakDetectorTest.cpp
|
||||||
ManagedStatic.cpp
|
ManagedStatic.cpp
|
||||||
MathExtrasTest.cpp
|
MathExtrasTest.cpp
|
||||||
|
MemoryBufferTest.cpp
|
||||||
MemoryTest.cpp
|
MemoryTest.cpp
|
||||||
Path.cpp
|
Path.cpp
|
||||||
RegexTest.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