aboutsummaryrefslogtreecommitdiffstats
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)
downloadexternal_llvm-489393d7b92107cc3de17d8dbe7dd11ab7395fdc.zip
external_llvm-489393d7b92107cc3de17d8dbe7dd11ab7395fdc.tar.gz
external_llvm-489393d7b92107cc3de17d8dbe7dd11ab7395fdc.tar.bz2
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 73197af..cb9a0a1 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 84cfd23..b0f0d07 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 688a162..02ec1c3 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 69518b6..5ff7eb1 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 d7f38ef..902f676 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 6b13c90..d56161f 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 785b5b9..55ce16c 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 66417a7..c992509 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 8fe7ab8..cfc3680 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 70ccdcc..9820493 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 0f20819..24dd013 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 0000000..8f29918
--- /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);
+
+}