[llvm-commits] [llvm] r75059 - in /llvm/trunk: include/llvm/CodeGen/ include/llvm/ExecutionEngine/ lib/ExecutionEngine/ lib/ExecutionEngine/Interpreter/ lib/ExecutionEngine/JIT/ unittests/ExecutionEngine/JIT/

Jeffrey Yasskin jyasskin at google.com
Wed Jul 8 15:00:04 PDT 2009


Author: jyasskin
Date: Wed Jul  8 16:59:57 2009
New Revision: 75059

URL: http://llvm.org/viewvc/llvm-project?rev=75059&view=rev
Log:
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!

Added:
    llvm/trunk/unittests/ExecutionEngine/JIT/JITTest.cpp
Modified:
    llvm/trunk/include/llvm/CodeGen/JITCodeEmitter.h
    llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h
    llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h
    llvm/trunk/lib/ExecutionEngine/ExecutionEngine.cpp
    llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.cpp
    llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.h
    llvm/trunk/lib/ExecutionEngine/JIT/JIT.cpp
    llvm/trunk/lib/ExecutionEngine/JIT/JIT.h
    llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp
    llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp
    llvm/trunk/lib/ExecutionEngine/JIT/TargetSelect.cpp

Modified: llvm/trunk/include/llvm/CodeGen/JITCodeEmitter.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/CodeGen/JITCodeEmitter.h?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/include/llvm/CodeGen/JITCodeEmitter.h (original)
+++ llvm/trunk/include/llvm/CodeGen/JITCodeEmitter.h Wed Jul  8 16:59:57 2009
@@ -267,6 +267,11 @@
     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.

Modified: llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h (original)
+++ llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h Wed Jul  8 16:59:57 2009
@@ -87,7 +87,8 @@
   // 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 @@
                                  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 @@
                                     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

Modified: llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h (original)
+++ llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h Wed Jul  8 16:59:57 2009
@@ -28,6 +28,7 @@
   bool HasGOT;
   bool SizeRequired;
 public:
+
   JITMemoryManager() : HasGOT(false), SizeRequired(false) {}
   virtual ~JITMemoryManager();
   
@@ -43,6 +44,11 @@
   /// 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 @@
 
   /// 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;

Modified: llvm/trunk/lib/ExecutionEngine/ExecutionEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/ExecutionEngine.cpp?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/ExecutionEngine.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/ExecutionEngine.cpp Wed Jul  8 16:59:57 2009
@@ -384,7 +384,8 @@
 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 @@
 
   // 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;
 }

Modified: llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.cpp?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.cpp Wed Jul  8 16:59:57 2009
@@ -34,7 +34,8 @@
 /// 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 @@
 
   return ExitValue;
 }
-

Modified: llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.h?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.h (original)
+++ llvm/trunk/lib/ExecutionEngine/Interpreter/Interpreter.h Wed Jul  8 16:59:57 2009
@@ -108,7 +108,8 @@
   /// 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.
   ///

Modified: llvm/trunk/lib/ExecutionEngine/JIT/JIT.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/JIT/JIT.cpp?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/JIT/JIT.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/JIT/JIT.cpp Wed Jul  8 16:59:57 2009
@@ -197,8 +197,10 @@
 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 @@
 }
 
 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 @@
     }
     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 @@
 /// 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) {

Modified: llvm/trunk/lib/ExecutionEngine/JIT/JIT.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/JIT/JIT.h?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/JIT/JIT.h (original)
+++ llvm/trunk/lib/ExecutionEngine/JIT/JIT.h Wed Jul  8 16:59:57 2009
@@ -55,10 +55,16 @@
   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 @@
   ///
   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 @@
   /// 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

Modified: llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp Wed Jul  8 16:59:57 2009
@@ -527,6 +527,11 @@
     /// 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 @@
   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;

Modified: llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp Wed Jul  8 16:59:57 2009
@@ -251,6 +251,8 @@
   /// 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 @@
     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 @@
       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 @@
       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 @@
       // 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 @@
       // 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 @@
       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 @@
 
   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 @@
   // 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 @@
 
 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) {

Modified: llvm/trunk/lib/ExecutionEngine/JIT/TargetSelect.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/JIT/TargetSelect.cpp?rev=75059&r1=75058&r2=75059&view=diff

==============================================================================
--- llvm/trunk/lib/ExecutionEngine/JIT/TargetSelect.cpp (original)
+++ llvm/trunk/lib/ExecutionEngine/JIT/TargetSelect.cpp Wed Jul  8 16:59:57 2009
@@ -43,7 +43,8 @@
 ///
 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 @@
 
   // 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";

Added: llvm/trunk/unittests/ExecutionEngine/JIT/JITTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/JIT/JITTest.cpp?rev=75059&view=auto

==============================================================================
--- llvm/trunk/unittests/ExecutionEngine/JIT/JITTest.cpp (added)
+++ llvm/trunk/unittests/ExecutionEngine/JIT/JITTest.cpp Wed Jul  8 16:59:57 2009
@@ -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);
+
+}





More information about the llvm-commits mailing list