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
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 @@
   /// 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) {