mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-29 06:30:39 +00:00
Add an option to allocate JITed global data separately from code. By
default, this option is not enabled to support clients who rely on this behavior. Fixes http://llvm.org/PR4483 A patch to allocate additional memory for globals after we run out is forthcoming. Patch by Reid Kleckner! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@75059 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
e41dec60fa
commit
489393d7b9
@ -267,6 +267,11 @@ public:
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// allocateGlobal - Allocate memory for a global. Unlike allocateSpace,
|
||||
/// this method does not allocate memory in the current output buffer,
|
||||
/// because a global may live longer than the current function.
|
||||
virtual void *allocateGlobal(uintptr_t Size, unsigned Alignment) = 0;
|
||||
|
||||
/// StartMachineBasicBlock - This should be called by the target when a new
|
||||
/// basic block is about to be emitted. This way the MCE knows where the
|
||||
/// start of the block is, and can implement getMachineBasicBlockAddress.
|
||||
|
@ -87,7 +87,8 @@ protected:
|
||||
// libraries, the JIT and Interpreter set these functions to ctor pointers
|
||||
// at startup time if they are linked in.
|
||||
typedef ExecutionEngine *(*EECtorFn)(ModuleProvider*, std::string*,
|
||||
CodeGenOpt::Level OptLevel);
|
||||
CodeGenOpt::Level OptLevel,
|
||||
bool GVsWithCode);
|
||||
static EECtorFn JITCtor, InterpCtor;
|
||||
|
||||
/// LazyFunctionCreator - If an unknown function is needed, this function
|
||||
@ -118,8 +119,18 @@ public:
|
||||
bool ForceInterpreter = false,
|
||||
std::string *ErrorStr = 0,
|
||||
CodeGenOpt::Level OptLevel =
|
||||
CodeGenOpt::Default);
|
||||
|
||||
CodeGenOpt::Default,
|
||||
// Allocating globals with code breaks
|
||||
// freeMachineCodeForFunction and is probably
|
||||
// unsafe and bad for performance. However,
|
||||
// we have clients who depend on this
|
||||
// behavior, so we must support it.
|
||||
// Eventually, when we're willing to break
|
||||
// some backwards compatability, this flag
|
||||
// should be flipped to false, so that by
|
||||
// default freeMachineCodeForFunction works.
|
||||
bool GVsWithCode = true);
|
||||
|
||||
/// create - This is the factory method for creating an execution engine which
|
||||
/// is appropriate for the current machine. This takes ownership of the
|
||||
/// module.
|
||||
@ -132,7 +143,8 @@ public:
|
||||
std::string *ErrorStr = 0,
|
||||
JITMemoryManager *JMM = 0,
|
||||
CodeGenOpt::Level OptLevel =
|
||||
CodeGenOpt::Default);
|
||||
CodeGenOpt::Default,
|
||||
bool GVsWithCode = true);
|
||||
|
||||
/// addModuleProvider - Add a ModuleProvider to the list of modules that we
|
||||
/// can JIT from. Note that this takes ownership of the ModuleProvider: when
|
||||
|
@ -28,6 +28,7 @@ protected:
|
||||
bool HasGOT;
|
||||
bool SizeRequired;
|
||||
public:
|
||||
|
||||
JITMemoryManager() : HasGOT(false), SizeRequired(false) {}
|
||||
virtual ~JITMemoryManager();
|
||||
|
||||
@ -43,6 +44,11 @@ public:
|
||||
/// start execution, the code pages may need permissions changed.
|
||||
virtual void setMemoryExecutable(void) = 0;
|
||||
|
||||
/// setPoisonMemory - Setting this flag to true makes the memory manager
|
||||
/// garbage values over freed memory. This is useful for testing and
|
||||
/// debugging, and is be turned on by default in debug mode.
|
||||
virtual void setPoisonMemory(bool poison) = 0;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Global Offset Table Management
|
||||
//===--------------------------------------------------------------------===//
|
||||
@ -114,7 +120,10 @@ public:
|
||||
|
||||
/// allocateSpace - Allocate a memory block of the given size.
|
||||
virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) = 0;
|
||||
|
||||
|
||||
/// allocateGlobal - Allocate memory for a global.
|
||||
virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) = 0;
|
||||
|
||||
/// deallocateMemForFunction - Free JIT memory for the specified function.
|
||||
/// This is never called when the JIT is currently emitting a function.
|
||||
virtual void deallocateMemForFunction(const Function *F) = 0;
|
||||
|
@ -384,7 +384,8 @@ int ExecutionEngine::runFunctionAsMain(Function *Fn,
|
||||
ExecutionEngine *ExecutionEngine::create(ModuleProvider *MP,
|
||||
bool ForceInterpreter,
|
||||
std::string *ErrorStr,
|
||||
CodeGenOpt::Level OptLevel) {
|
||||
CodeGenOpt::Level OptLevel,
|
||||
bool GVsWithCode) {
|
||||
ExecutionEngine *EE = 0;
|
||||
|
||||
// Make sure we can resolve symbols in the program as well. The zero arg
|
||||
@ -394,11 +395,11 @@ ExecutionEngine *ExecutionEngine::create(ModuleProvider *MP,
|
||||
|
||||
// Unless the interpreter was explicitly selected, try making a JIT.
|
||||
if (!ForceInterpreter && JITCtor)
|
||||
EE = JITCtor(MP, ErrorStr, OptLevel);
|
||||
EE = JITCtor(MP, ErrorStr, OptLevel, GVsWithCode);
|
||||
|
||||
// If we can't make a JIT, make an interpreter instead.
|
||||
if (EE == 0 && InterpCtor)
|
||||
EE = InterpCtor(MP, ErrorStr, OptLevel);
|
||||
EE = InterpCtor(MP, ErrorStr, OptLevel, GVsWithCode);
|
||||
|
||||
return EE;
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ extern "C" void LLVMLinkInInterpreter() { }
|
||||
/// create - Create a new interpreter object. This can never fail.
|
||||
///
|
||||
ExecutionEngine *Interpreter::create(ModuleProvider *MP, std::string* ErrStr,
|
||||
CodeGenOpt::Level OptLevel /*unused*/) {
|
||||
CodeGenOpt::Level OptLevel, /*unused*/
|
||||
bool GVsWithCode /* unused */) {
|
||||
// Tell this ModuleProvide to materialize and release the module
|
||||
if (!MP->materializeModule(ErrStr))
|
||||
// We got an error, just return 0
|
||||
@ -98,4 +99,3 @@ Interpreter::runFunction(Function *F,
|
||||
|
||||
return ExitValue;
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,8 @@ public:
|
||||
/// create - Create an interpreter ExecutionEngine. This can never fail.
|
||||
///
|
||||
static ExecutionEngine *create(ModuleProvider *M, std::string *ErrorStr = 0,
|
||||
CodeGenOpt::Level = CodeGenOpt::Default);
|
||||
CodeGenOpt::Level = CodeGenOpt::Default,
|
||||
bool GVsWithCode = true);
|
||||
|
||||
/// run - Start execution with the specified function and arguments.
|
||||
///
|
||||
|
@ -197,8 +197,10 @@ void DarwinRegisterFrame(void* FrameBegin) {
|
||||
ExecutionEngine *ExecutionEngine::createJIT(ModuleProvider *MP,
|
||||
std::string *ErrorStr,
|
||||
JITMemoryManager *JMM,
|
||||
CodeGenOpt::Level OptLevel) {
|
||||
ExecutionEngine *EE = JIT::createJIT(MP, ErrorStr, JMM, OptLevel);
|
||||
CodeGenOpt::Level OptLevel,
|
||||
bool GVsWithCode) {
|
||||
ExecutionEngine *EE = JIT::createJIT(MP, ErrorStr, JMM, OptLevel,
|
||||
GVsWithCode);
|
||||
if (!EE) return 0;
|
||||
|
||||
// Make sure we can resolve symbols in the program as well. The zero arg
|
||||
@ -208,8 +210,8 @@ ExecutionEngine *ExecutionEngine::createJIT(ModuleProvider *MP,
|
||||
}
|
||||
|
||||
JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji,
|
||||
JITMemoryManager *JMM, CodeGenOpt::Level OptLevel)
|
||||
: ExecutionEngine(MP), TM(tm), TJI(tji) {
|
||||
JITMemoryManager *JMM, CodeGenOpt::Level OptLevel, bool GVsWithCode)
|
||||
: ExecutionEngine(MP), TM(tm), TJI(tji), AllocateGVsWithCode(GVsWithCode) {
|
||||
setTargetData(TM.getTargetData());
|
||||
|
||||
jitstate = new JITState(MP);
|
||||
@ -677,37 +679,11 @@ void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) {
|
||||
}
|
||||
addGlobalMapping(GV, Ptr);
|
||||
} else {
|
||||
// GlobalVariable's which are not "constant" will cause trouble in a server
|
||||
// situation. It's returned in the same block of memory as code which may
|
||||
// not be writable.
|
||||
if (isGVCompilationDisabled() && !GV->isConstant()) {
|
||||
cerr << "Compilation of non-internal GlobalValue is disabled!\n";
|
||||
abort();
|
||||
}
|
||||
// If the global hasn't been emitted to memory yet, allocate space and
|
||||
// emit it into memory. It goes in the same array as the generated
|
||||
// code, jump tables, etc.
|
||||
const Type *GlobalType = GV->getType()->getElementType();
|
||||
size_t S = getTargetData()->getTypeAllocSize(GlobalType);
|
||||
size_t A = getTargetData()->getPreferredAlignment(GV);
|
||||
if (GV->isThreadLocal()) {
|
||||
MutexGuard locked(lock);
|
||||
Ptr = TJI.allocateThreadLocalMemory(S);
|
||||
} else if (TJI.allocateSeparateGVMemory()) {
|
||||
if (A <= 8) {
|
||||
Ptr = malloc(S);
|
||||
} else {
|
||||
// Allocate S+A bytes of memory, then use an aligned pointer within that
|
||||
// space.
|
||||
Ptr = malloc(S+A);
|
||||
unsigned MisAligned = ((intptr_t)Ptr & (A-1));
|
||||
Ptr = (char*)Ptr + (MisAligned ? (A-MisAligned) : 0);
|
||||
}
|
||||
} else {
|
||||
Ptr = JCE->allocateSpace(S, A);
|
||||
}
|
||||
// emit it into memory.
|
||||
Ptr = getMemoryForGV(GV);
|
||||
addGlobalMapping(GV, Ptr);
|
||||
EmitGlobalVariable(GV);
|
||||
EmitGlobalVariable(GV); // Initialize the variable.
|
||||
}
|
||||
return Ptr;
|
||||
}
|
||||
@ -742,14 +718,42 @@ void *JIT::recompileAndRelinkFunction(Function *F) {
|
||||
/// on the target.
|
||||
///
|
||||
char* JIT::getMemoryForGV(const GlobalVariable* GV) {
|
||||
const Type *ElTy = GV->getType()->getElementType();
|
||||
size_t GVSize = (size_t)getTargetData()->getTypeAllocSize(ElTy);
|
||||
char *Ptr;
|
||||
|
||||
// GlobalVariable's which are not "constant" will cause trouble in a server
|
||||
// situation. It's returned in the same block of memory as code which may
|
||||
// not be writable.
|
||||
if (isGVCompilationDisabled() && !GV->isConstant()) {
|
||||
cerr << "Compilation of non-internal GlobalValue is disabled!\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
// Some applications require globals and code to live together, so they may
|
||||
// be allocated into the same buffer, but in general globals are allocated
|
||||
// through the memory manager which puts them near the code but not in the
|
||||
// same buffer.
|
||||
const Type *GlobalType = GV->getType()->getElementType();
|
||||
size_t S = getTargetData()->getTypeAllocSize(GlobalType);
|
||||
size_t A = getTargetData()->getPreferredAlignment(GV);
|
||||
if (GV->isThreadLocal()) {
|
||||
MutexGuard locked(lock);
|
||||
return TJI.allocateThreadLocalMemory(GVSize);
|
||||
Ptr = TJI.allocateThreadLocalMemory(S);
|
||||
} else if (TJI.allocateSeparateGVMemory()) {
|
||||
if (A <= 8) {
|
||||
Ptr = (char*)malloc(S);
|
||||
} else {
|
||||
// Allocate S+A bytes of memory, then use an aligned pointer within that
|
||||
// space.
|
||||
Ptr = (char*)malloc(S+A);
|
||||
unsigned MisAligned = ((intptr_t)Ptr & (A-1));
|
||||
Ptr = Ptr + (MisAligned ? (A-MisAligned) : 0);
|
||||
}
|
||||
} else if (AllocateGVsWithCode) {
|
||||
Ptr = (char*)JCE->allocateSpace(S, A);
|
||||
} else {
|
||||
return new char[GVSize];
|
||||
Ptr = (char*)JCE->allocateGlobal(S, A);
|
||||
}
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
void JIT::addPendingFunction(Function *F) {
|
||||
|
@ -55,10 +55,16 @@ class JIT : public ExecutionEngine {
|
||||
JITCodeEmitter *JCE; // JCE object
|
||||
std::vector<JITEventListener*> EventListeners;
|
||||
|
||||
/// AllocateGVsWithCode - Some applications require that global variables and
|
||||
/// code be allocated into the same region of memory, in which case this flag
|
||||
/// should be set to true. Doing so breaks freeMachineCodeForFunction.
|
||||
bool AllocateGVsWithCode;
|
||||
|
||||
JITState *jitstate;
|
||||
|
||||
JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji,
|
||||
JITMemoryManager *JMM, CodeGenOpt::Level OptLevel);
|
||||
JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji,
|
||||
JITMemoryManager *JMM, CodeGenOpt::Level OptLevel,
|
||||
bool AllocateGVsWithCode);
|
||||
public:
|
||||
~JIT();
|
||||
|
||||
@ -75,8 +81,9 @@ public:
|
||||
///
|
||||
static ExecutionEngine *create(ModuleProvider *MP, std::string *Err,
|
||||
CodeGenOpt::Level OptLevel =
|
||||
CodeGenOpt::Default) {
|
||||
return createJIT(MP, Err, 0, OptLevel);
|
||||
CodeGenOpt::Default,
|
||||
bool AllocateGVsWithCode = true) {
|
||||
return createJIT(MP, Err, 0, OptLevel, AllocateGVsWithCode);
|
||||
}
|
||||
|
||||
virtual void addModuleProvider(ModuleProvider *MP);
|
||||
@ -151,9 +158,11 @@ public:
|
||||
/// getCodeEmitter - Return the code emitter this JIT is emitting into.
|
||||
JITCodeEmitter *getCodeEmitter() const { return JCE; }
|
||||
|
||||
static ExecutionEngine *createJIT(ModuleProvider *MP, std::string *Err,
|
||||
static ExecutionEngine *createJIT(ModuleProvider *MP,
|
||||
std::string *Err,
|
||||
JITMemoryManager *JMM,
|
||||
CodeGenOpt::Level OptLevel);
|
||||
CodeGenOpt::Level OptLevel,
|
||||
bool AllocateGVsWithCode);
|
||||
|
||||
|
||||
// Run the JIT on F and return information about the generated code
|
||||
|
@ -527,6 +527,11 @@ namespace {
|
||||
/// allocate a new one of the given size.
|
||||
virtual void *allocateSpace(uintptr_t Size, unsigned Alignment);
|
||||
|
||||
/// allocateGlobal - Allocate memory for a global. Unlike allocateSpace,
|
||||
/// this method does not allocate memory in the current output buffer,
|
||||
/// because a global may live longer than the current function.
|
||||
virtual void *allocateGlobal(uintptr_t Size, unsigned Alignment);
|
||||
|
||||
virtual void addRelocation(const MachineRelocation &MR) {
|
||||
Relocations.push_back(MR);
|
||||
}
|
||||
@ -1161,6 +1166,11 @@ void* JITEmitter::allocateSpace(uintptr_t Size, unsigned Alignment) {
|
||||
return CurBufferPtr;
|
||||
}
|
||||
|
||||
void* JITEmitter::allocateGlobal(uintptr_t Size, unsigned Alignment) {
|
||||
// Delegate this call through the memory manager.
|
||||
return MemMgr->allocateGlobal(Size, Alignment);
|
||||
}
|
||||
|
||||
void JITEmitter::emitConstantPool(MachineConstantPool *MCP) {
|
||||
if (TheJIT->getJITInfo().hasCustomConstantPool())
|
||||
return;
|
||||
|
@ -251,6 +251,8 @@ namespace {
|
||||
/// middle of emitting a function, and we don't know how large the function we
|
||||
/// are emitting is.
|
||||
class VISIBILITY_HIDDEN DefaultJITMemoryManager : public JITMemoryManager {
|
||||
bool PoisonMemory; // Whether to poison freed memory.
|
||||
|
||||
std::vector<sys::MemoryBlock> Blocks; // Memory blocks allocated by the JIT
|
||||
FreeRangeHeader *FreeMemoryList; // Circular list of free blocks.
|
||||
|
||||
@ -258,6 +260,7 @@ namespace {
|
||||
MemoryRangeHeader *CurBlock;
|
||||
|
||||
uint8_t *CurStubPtr, *StubBase;
|
||||
uint8_t *CurGlobalPtr, *GlobalEnd;
|
||||
uint8_t *GOTBase; // Target Specific reserved memory
|
||||
void *DlsymTable; // Stub external symbol information
|
||||
|
||||
@ -324,7 +327,7 @@ namespace {
|
||||
CurBlock = FreeMemoryList;
|
||||
FreeMemoryList = FreeMemoryList->AllocateBlock();
|
||||
|
||||
uint8_t *result = (uint8_t *)CurBlock+1;
|
||||
uint8_t *result = (uint8_t *)(CurBlock + 1);
|
||||
|
||||
if (Alignment == 0) Alignment = 1;
|
||||
result = (uint8_t*)(((intptr_t)result+Alignment-1) &
|
||||
@ -336,6 +339,30 @@ namespace {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// allocateGlobal - Allocate memory for a global. Unlike allocateSpace,
|
||||
/// this method does not touch the current block and can be called at any
|
||||
/// time.
|
||||
uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) {
|
||||
uint8_t *Result = CurGlobalPtr;
|
||||
|
||||
// Align the pointer.
|
||||
if (Alignment == 0) Alignment = 1;
|
||||
Result = (uint8_t*)(((uintptr_t)Result + Alignment-1) &
|
||||
~(uintptr_t)(Alignment-1));
|
||||
|
||||
// Move the current global pointer forward.
|
||||
CurGlobalPtr += Result - CurGlobalPtr + Size;
|
||||
|
||||
// Check for overflow.
|
||||
if (CurGlobalPtr > GlobalEnd) {
|
||||
// FIXME: Allocate more memory.
|
||||
fprintf(stderr, "JIT ran out of memory for globals!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// startExceptionTable - Use startFunctionBody to allocate memory for the
|
||||
/// function's exception table.
|
||||
uint8_t* startExceptionTable(const Function* F, uintptr_t &ActualSize) {
|
||||
@ -375,12 +402,12 @@ namespace {
|
||||
// Find the block that is allocated for this function.
|
||||
MemoryRangeHeader *MemRange = I->second;
|
||||
assert(MemRange->ThisAllocated && "Block isn't allocated!");
|
||||
|
||||
|
||||
// Fill the buffer with garbage!
|
||||
#ifndef NDEBUG
|
||||
memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange));
|
||||
#endif
|
||||
|
||||
if (PoisonMemory) {
|
||||
memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange));
|
||||
}
|
||||
|
||||
// Free the memory.
|
||||
FreeMemoryList = MemRange->FreeBlock(FreeMemoryList);
|
||||
|
||||
@ -393,12 +420,12 @@ namespace {
|
||||
// Find the block that is allocated for this function.
|
||||
MemRange = I->second;
|
||||
assert(MemRange->ThisAllocated && "Block isn't allocated!");
|
||||
|
||||
|
||||
// Fill the buffer with garbage!
|
||||
#ifndef NDEBUG
|
||||
memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange));
|
||||
#endif
|
||||
|
||||
if (PoisonMemory) {
|
||||
memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange));
|
||||
}
|
||||
|
||||
// Free the memory.
|
||||
FreeMemoryList = MemRange->FreeBlock(FreeMemoryList);
|
||||
|
||||
@ -420,10 +447,22 @@ namespace {
|
||||
for (unsigned i = 0, e = Blocks.size(); i != e; ++i)
|
||||
sys::Memory::setExecutable(Blocks[i]);
|
||||
}
|
||||
|
||||
/// setPoisonMemory - Controls whether we write garbage over freed memory.
|
||||
///
|
||||
void setPoisonMemory(bool poison) {
|
||||
PoisonMemory = poison;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
DefaultJITMemoryManager::DefaultJITMemoryManager() {
|
||||
#ifdef NDEBUG
|
||||
PoisonMemory = true;
|
||||
#else
|
||||
PoisonMemory = false;
|
||||
#endif
|
||||
|
||||
// Allocate a 16M block of memory for functions.
|
||||
#if defined(__APPLE__) && defined(__arm__)
|
||||
sys::MemoryBlock MemBlock = getNewMemoryBlock(4 << 20);
|
||||
@ -433,11 +472,13 @@ DefaultJITMemoryManager::DefaultJITMemoryManager() {
|
||||
|
||||
uint8_t *MemBase = static_cast<uint8_t*>(MemBlock.base());
|
||||
|
||||
// Allocate stubs backwards from the base, allocate functions forward
|
||||
// from the base.
|
||||
// Allocate stubs backwards to the base, globals forward from the stubs, and
|
||||
// functions forward after globals.
|
||||
StubBase = MemBase;
|
||||
CurStubPtr = MemBase + 512*1024; // Use 512k for stubs, working backwards.
|
||||
|
||||
CurGlobalPtr = CurStubPtr; // Use 2M for globals, working forwards.
|
||||
GlobalEnd = CurGlobalPtr + 2*1024*1024;
|
||||
|
||||
// We set up the memory chunk with 4 mem regions, like this:
|
||||
// [ START
|
||||
// [ Free #0 ] -> Large space to allocate functions from.
|
||||
@ -474,7 +515,7 @@ DefaultJITMemoryManager::DefaultJITMemoryManager() {
|
||||
// Add a FreeRangeHeader to the start of the function body region, indicating
|
||||
// that the space is free. Mark the previous block allocated so we never look
|
||||
// at it.
|
||||
FreeRangeHeader *Mem0 = (FreeRangeHeader*)CurStubPtr;
|
||||
FreeRangeHeader *Mem0 = (FreeRangeHeader*)GlobalEnd;
|
||||
Mem0->ThisAllocated = 0;
|
||||
Mem0->PrevAllocated = 1;
|
||||
Mem0->BlockSize = (char*)Mem1-(char*)Mem0;
|
||||
@ -522,7 +563,7 @@ uint8_t *DefaultJITMemoryManager::allocateStub(const GlobalValue* F,
|
||||
|
||||
sys::MemoryBlock DefaultJITMemoryManager::getNewMemoryBlock(unsigned size) {
|
||||
// Allocate a new block close to the last one.
|
||||
const sys::MemoryBlock *BOld = Blocks.empty() ? 0 : &Blocks.front();
|
||||
const sys::MemoryBlock *BOld = Blocks.empty() ? 0 : &Blocks.back();
|
||||
std::string ErrMsg;
|
||||
sys::MemoryBlock B = sys::Memory::AllocateRWX(size, BOld, &ErrMsg);
|
||||
if (B.base() == 0) {
|
||||
|
@ -43,7 +43,8 @@ MAttrs("mattr",
|
||||
///
|
||||
ExecutionEngine *JIT::createJIT(ModuleProvider *MP, std::string *ErrorStr,
|
||||
JITMemoryManager *JMM,
|
||||
CodeGenOpt::Level OptLevel) {
|
||||
CodeGenOpt::Level OptLevel,
|
||||
bool AllocateGVsWithCode) {
|
||||
const TargetMachineRegistry::entry *TheArch = MArch;
|
||||
if (TheArch == 0) {
|
||||
std::string Error;
|
||||
@ -75,7 +76,7 @@ ExecutionEngine *JIT::createJIT(ModuleProvider *MP, std::string *ErrorStr,
|
||||
|
||||
// If the target supports JIT code generation, return a new JIT now.
|
||||
if (TargetJITInfo *TJ = Target->getJITInfo())
|
||||
return new JIT(MP, *Target, *TJ, JMM, OptLevel);
|
||||
return new JIT(MP, *Target, *TJ, JMM, OptLevel, AllocateGVsWithCode);
|
||||
|
||||
if (ErrorStr)
|
||||
*ErrorStr = "target does not support JIT code generation";
|
||||
|
126
unittests/ExecutionEngine/JIT/JITTest.cpp
Normal file
126
unittests/ExecutionEngine/JIT/JITTest.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
//===- JITEmitter.cpp - Unit tests for the JIT code emitter ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/BasicBlock.h"
|
||||
#include "llvm/Constant.h"
|
||||
#include "llvm/Constants.h"
|
||||
#include "llvm/DerivedTypes.h"
|
||||
#include "llvm/ExecutionEngine/JIT.h"
|
||||
#include "llvm/ExecutionEngine/JITMemoryManager.h"
|
||||
#include "llvm/Function.h"
|
||||
#include "llvm/GlobalValue.h"
|
||||
#include "llvm/GlobalVariable.h"
|
||||
#include "llvm/Module.h"
|
||||
#include "llvm/ModuleProvider.h"
|
||||
#include "llvm/Support/IRBuilder.h"
|
||||
#include "llvm/Target/TargetSelect.h"
|
||||
#include "llvm/Type.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
Function *makeReturnGlobal(std::string Name, GlobalVariable *G, Module *M) {
|
||||
std::vector<const Type*> params;
|
||||
const FunctionType *FTy = FunctionType::get(Type::VoidTy, params, false);
|
||||
Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, Name, M);
|
||||
BasicBlock *Entry = BasicBlock::Create("entry", F);
|
||||
IRBuilder<> builder(Entry);
|
||||
Value *Load = builder.CreateLoad(G);
|
||||
const Type *GTy = G->getType()->getElementType();
|
||||
Value *Add = builder.CreateAdd(Load, ConstantInt::get(GTy, 1LL));
|
||||
builder.CreateStore(Add, G);
|
||||
builder.CreateRet(Add);
|
||||
return F;
|
||||
}
|
||||
|
||||
// Regression test for a bug. The JIT used to allocate globals inside the same
|
||||
// memory block used for the function, and when the function code was freed,
|
||||
// the global was left in the same place. This test allocates a function
|
||||
// that uses and global, deallocates it, and then makes sure that the global
|
||||
// stays alive after that.
|
||||
TEST(JIT, GlobalInFunction) {
|
||||
LLVMContext context;
|
||||
Module *M = new Module("<main>", context);
|
||||
ExistingModuleProvider *MP = new ExistingModuleProvider(M);
|
||||
|
||||
JITMemoryManager *MemMgr = JITMemoryManager::CreateDefaultMemManager();
|
||||
// Tell the memory manager to poison freed memory so that accessing freed
|
||||
// memory is more easily tested.
|
||||
MemMgr->setPoisonMemory(true);
|
||||
std::string Error;
|
||||
OwningPtr<ExecutionEngine> JIT(ExecutionEngine::createJIT(
|
||||
MP,
|
||||
&Error,
|
||||
MemMgr,
|
||||
CodeGenOpt::Default,
|
||||
false)); // This last argument enables the fix.
|
||||
ASSERT_EQ(Error, "");
|
||||
|
||||
// Create a global variable.
|
||||
const Type *GTy = Type::Int32Ty;
|
||||
GlobalVariable *G = new GlobalVariable(
|
||||
*M,
|
||||
GTy,
|
||||
false, // Not constant.
|
||||
GlobalValue::InternalLinkage,
|
||||
Constant::getNullValue(GTy),
|
||||
"myglobal");
|
||||
|
||||
// Make a function that points to a global.
|
||||
Function *F1 = makeReturnGlobal("F1", G, M);
|
||||
|
||||
// Get the pointer to the native code to force it to JIT the function and
|
||||
// allocate space for the global.
|
||||
void (*F1Ptr)();
|
||||
// Hack to avoid ISO C++ warning about casting function pointers.
|
||||
*(void**)(void*)&F1Ptr = JIT->getPointerToFunction(F1);
|
||||
|
||||
// Since F1 was codegen'd, a pointer to G should be available.
|
||||
int32_t *GPtr = (int32_t*)JIT->getPointerToGlobalIfAvailable(G);
|
||||
ASSERT_NE((int32_t*)NULL, GPtr);
|
||||
EXPECT_EQ(0, *GPtr);
|
||||
|
||||
// F1() should increment G.
|
||||
F1Ptr();
|
||||
EXPECT_EQ(1, *GPtr);
|
||||
|
||||
// Make a second function identical to the first, referring to the same
|
||||
// global.
|
||||
Function *F2 = makeReturnGlobal("F2", G, M);
|
||||
// Hack to avoid ISO C++ warning about casting function pointers.
|
||||
void (*F2Ptr)();
|
||||
*(void**)(void*)&F2Ptr = JIT->getPointerToFunction(F2);
|
||||
|
||||
// F2() should increment G.
|
||||
F2Ptr();
|
||||
EXPECT_EQ(2, *GPtr);
|
||||
|
||||
// Deallocate F1.
|
||||
JIT->freeMachineCodeForFunction(F1);
|
||||
|
||||
// F2() should *still* increment G.
|
||||
F2Ptr();
|
||||
EXPECT_EQ(3, *GPtr);
|
||||
}
|
||||
|
||||
// TODO(rnk): This seems to only run once for both tests, which is unexpected.
|
||||
// That works just fine, but we shouldn't duplicate the code.
|
||||
class JITEnvironment : public testing::Environment {
|
||||
virtual void SetUp() {
|
||||
// Required for ExecutionEngine::createJIT to create a JIT.
|
||||
InitializeNativeTarget();
|
||||
}
|
||||
};
|
||||
testing::Environment* const jit_env =
|
||||
testing::AddGlobalTestEnvironment(new JITEnvironment);
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user