aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Yasskin <jyasskin@google.com>2009-07-08 21:59:57 +0000
committerJeffrey Yasskin <jyasskin@google.com>2009-07-08 21:59:57 +0000
commit489393d7b92107cc3de17d8dbe7dd11ab7395fdc (patch)
treec71a1374a4b25820422f596ca8e686c01514596a
parente41dec60fa1ad6a46ec46c848ae1a8f2f7497e12 (diff)
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
-rw-r--r--include/llvm/CodeGen/JITCodeEmitter.h5
-rw-r--r--include/llvm/ExecutionEngine/ExecutionEngine.h20
-rw-r--r--include/llvm/ExecutionEngine/JITMemoryManager.h11
-rw-r--r--lib/ExecutionEngine/ExecutionEngine.cpp7
-rw-r--r--lib/ExecutionEngine/Interpreter/Interpreter.cpp4
-rw-r--r--lib/ExecutionEngine/Interpreter/Interpreter.h3
-rw-r--r--lib/ExecutionEngine/JIT/JIT.cpp78
-rw-r--r--lib/ExecutionEngine/JIT/JIT.h21
-rw-r--r--lib/ExecutionEngine/JIT/JITEmitter.cpp10
-rw-r--r--lib/ExecutionEngine/JIT/JITMemoryManager.cpp73
-rw-r--r--lib/ExecutionEngine/JIT/TargetSelect.cpp5
-rw-r--r--unittests/ExecutionEngine/JIT/JITTest.cpp126
12 files changed, 291 insertions, 72 deletions
diff --git a/include/llvm/CodeGen/JITCodeEmitter.h b/include/llvm/CodeGen/JITCodeEmitter.h
index 73197af1af..cb9a0a1fa9 100644
--- a/include/llvm/CodeGen/JITCodeEmitter.h
+++ b/include/llvm/CodeGen/JITCodeEmitter.h
@@ -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.
diff --git a/include/llvm/ExecutionEngine/ExecutionEngine.h b/include/llvm/ExecutionEngine/ExecutionEngine.h
index 84cfd23677..b0f0d07cf1 100644
--- a/include/llvm/ExecutionEngine/ExecutionEngine.h
+++ b/include/llvm/ExecutionEngine/ExecutionEngine.h
@@ -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
diff --git a/include/llvm/ExecutionEngine/JITMemoryManager.h b/include/llvm/ExecutionEngine/JITMemoryManager.h
index 688a1626d2..02ec1c3a50 100644
--- a/include/llvm/ExecutionEngine/JITMemoryManager.h
+++ b/include/llvm/ExecutionEngine/JITMemoryManager.h
@@ -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;
diff --git a/lib/ExecutionEngine/ExecutionEngine.cpp b/lib/ExecutionEngine/ExecutionEngine.cpp
index 69518b6584..5ff7eb17cb 100644
--- a/lib/ExecutionEngine/ExecutionEngine.cpp
+++ b/lib/ExecutionEngine/ExecutionEngine.cpp
@@ -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;
}
diff --git a/lib/ExecutionEngine/Interpreter/Interpreter.cpp b/lib/ExecutionEngine/Interpreter/Interpreter.cpp
index d7f38ef548..902f6768dc 100644
--- a/lib/ExecutionEngine/Interpreter/Interpreter.cpp
+++ b/lib/ExecutionEngine/Interpreter/Interpreter.cpp
@@ -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;
}
-
diff --git a/lib/ExecutionEngine/Interpreter/Interpreter.h b/lib/ExecutionEngine/Interpreter/Interpreter.h
index 6b13c90f66..d56161f294 100644
--- a/lib/ExecutionEngine/Interpreter/Interpreter.h
+++ b/lib/ExecutionEngine/Interpreter/Interpreter.h
@@ -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.
///
diff --git a/lib/ExecutionEngine/JIT/JIT.cpp b/lib/ExecutionEngine/JIT/JIT.cpp
index 785b5b9284..55ce16c354 100644
--- a/lib/ExecutionEngine/JIT/JIT.cpp
+++ b/lib/ExecutionEngine/JIT/JIT.cpp
@@ -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) {
diff --git a/lib/ExecutionEngine/JIT/JIT.h b/lib/ExecutionEngine/JIT/JIT.h
index 66417a71b2..c992509b08 100644
--- a/lib/ExecutionEngine/JIT/JIT.h
+++ b/lib/ExecutionEngine/JIT/JIT.h
@@ -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
diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp
index 8fe7ab848b..cfc3680e8a 100644
--- a/lib/ExecutionEngine/JIT/JITEmitter.cpp
+++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp
@@ -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;
diff --git a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp
index 70ccdccb80..9820493828 100644
--- a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp
+++ b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp
@@ -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) {
diff --git a/lib/ExecutionEngine/JIT/TargetSelect.cpp b/lib/ExecutionEngine/JIT/TargetSelect.cpp
index 0f20819307..24dd013639 100644
--- a/lib/ExecutionEngine/JIT/TargetSelect.cpp
+++ b/lib/ExecutionEngine/JIT/TargetSelect.cpp
@@ -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";
diff --git a/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp
new file mode 100644
index 0000000000..8f29918a99
--- /dev/null
+++ b/unittests/ExecutionEngine/JIT/JITTest.cpp
@@ -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);
+
+}