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/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);
+
+}