Tests for MCJIT multiple module support
authorAndrew Kaylor <andrew.kaylor@intel.com>
Tue, 1 Oct 2013 01:48:36 +0000 (01:48 +0000)
committerAndrew Kaylor <andrew.kaylor@intel.com>
Tue, 1 Oct 2013 01:48:36 +0000 (01:48 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191723 91177308-0d34-0410-b5e6-96231b3b80d8

unittests/ExecutionEngine/MCJIT/CMakeLists.txt
unittests/ExecutionEngine/MCJIT/MCJITMultipleModuleTest.cpp [new file with mode: 0644]
unittests/ExecutionEngine/MCJIT/MCJITTest.cpp
unittests/ExecutionEngine/MCJIT/MCJITTestBase.h

index 922cb7efd5eb6687b8217e2866467e7fdcb68485..911b604a90a0d1d6fc12a0bece54f030ed5afd65 100644 (file)
@@ -11,6 +11,7 @@ set(MCJITTestsSources
   MCJITTest.cpp
   MCJITCAPITest.cpp
   MCJITMemoryManagerTest.cpp
+  MCJITMultipleModuleTest.cpp
   MCJITObjectCacheTest.cpp
   )
 
diff --git a/unittests/ExecutionEngine/MCJIT/MCJITMultipleModuleTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITMultipleModuleTest.cpp
new file mode 100644 (file)
index 0000000..a7fc7fe
--- /dev/null
@@ -0,0 +1,387 @@
+//===- MCJITMultipeModuleTest.cpp - Unit tests for the MCJIT---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This test suite verifies MCJIT for handling multiple modules in a single
+// ExecutionEngine by building multiple modules, making function calls across
+// modules, accessing global variables, etc.
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/MCJIT.h"
+#include "MCJITTestBase.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+class MCJITMultipleModuleTest : public testing::Test, public MCJITTestBase {};
+
+namespace {
+
+// FIXME: ExecutionEngine has no support empty modules
+/*
+TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  createJIT(M.take());
+  // JIT-compile
+  EXPECT_NE(0, TheJIT->getObjectImage())
+    << "Unable to generate executable loaded object image";
+
+  TheJIT->addModule(createEmptyModule("<other module>"));
+  TheJIT->addModule(createEmptyModule("<other other module>"));
+
+  // JIT again
+  EXPECT_NE(0, TheJIT->getObjectImage())
+    << "Unable to generate executable loaded object image";
+}
+*/
+
+// Helper Function to test add operation
+void checkAdd(uint64_t ptr) {
+  ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function.";
+  int (*AddPtr)(int, int) = (int (*)(int, int))ptr;
+  EXPECT_EQ(0, AddPtr(0, 0));
+  EXPECT_EQ(1, AddPtr(1, 0));
+  EXPECT_EQ(3, AddPtr(1, 2));
+  EXPECT_EQ(-5, AddPtr(-2, -3));
+  EXPECT_EQ(30, AddPtr(10, 20));
+  EXPECT_EQ(-30, AddPtr(-10, -20));
+  EXPECT_EQ(-40, AddPtr(-10, -30));
+}
+
+void checkAccumulate(uint64_t ptr) {
+  ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function.";
+  int32_t (*FPtr)(int32_t) = (int32_t (*)(int32_t))(intptr_t)ptr;
+  EXPECT_EQ(0, FPtr(0));
+  EXPECT_EQ(1, FPtr(1));
+  EXPECT_EQ(3, FPtr(2));
+  EXPECT_EQ(6, FPtr(3));
+  EXPECT_EQ(10, FPtr(4));
+  EXPECT_EQ(15, FPtr(5));
+}
+
+// FIXME: ExecutionEngine has no support empty modules
+/*
+TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  createJIT(M.take());
+  // JIT-compile
+  EXPECT_NE(0, TheJIT->getObjectImage())
+    << "Unable to generate executable loaded object image";
+
+  TheJIT->addModule(createEmptyModule("<other module>"));
+  TheJIT->addModule(createEmptyModule("<other other module>"));
+
+  // JIT again
+  EXPECT_NE(0, TheJIT->getObjectImage())
+    << "Unable to generate executable loaded object image";
+}
+*/
+
+// Module A { Function FA },
+// Module B { Function FB },
+// execute FA then FB
+TEST_F(MCJITMultipleModuleTest, two_module_case) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB;
+  createTwoModuleCase(A, FA, B, FB);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Function FA },
+// Module B { Function FB },
+// execute FB then FA
+TEST_F(MCJITMultipleModuleTest, two_module_reverse_case) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB;
+  createTwoModuleCase(A, FA, B, FB);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  TheJIT->finalizeObject();
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Function FA },
+// Module B { Extern FA, Function FB which calls FA },
+// execute FB then FA
+TEST_F(MCJITMultipleModuleTest, two_module_extern_reverse_case) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB;
+  createTwoModuleExternCase(A, FA, B, FB);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  TheJIT->finalizeObject();
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Function FA },
+// Module B { Extern FA, Function FB which calls FA },
+// execute FA then FB
+TEST_F(MCJITMultipleModuleTest, two_module_extern_case) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB;
+  createTwoModuleExternCase(A, FA, B, FB);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Function FA1, Function FA2 which calls FA1 },
+// Module B { Extern FA1, Function FB which calls FA1 },
+// execute FB then FA2
+TEST_F(MCJITMultipleModuleTest, two_module_consecutive_call_case) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA1, *FA2, *FB;
+  createTwoModuleExternCase(A, FA1, B, FB);
+  FA2 = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(A.get(), FA1);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  TheJIT->finalizeObject();
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FA2->getName().str());
+  checkAdd(ptr);
+}
+
+// TODO:
+// Module A { Extern Global GVB, Global Variable GVA, Function FA loads GVB },
+// Module B { Extern Global GVA, Global Variable GVB, Function FB loads GVA },
+
+
+// Module A { Global Variable GVA, Function FA loads GVA },
+// Module B { Global Variable GVB, Function FB loads GVB },
+// execute FB then FA
+TEST_F(MCJITMultipleModuleTest, two_module_global_variables_case) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB;
+  GlobalVariable *GVA, *GVB;
+  A.reset(createEmptyModule("A"));
+  B.reset(createEmptyModule("B"));
+
+  int32_t initialNum = 7;
+  GVA = insertGlobalInt32(A.get(), "GVA", initialNum);
+  GVB = insertGlobalInt32(B.get(), "GVB", initialNum);
+  FA = startFunction<int32_t(void)>(A.get(), "FA");
+  endFunctionWithRet(FA, Builder.CreateLoad(GVA));
+  FB = startFunction<int32_t(void)>(B.get(), "FB");
+  endFunctionWithRet(FB, Builder.CreateLoad(GVB));
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t FBPtr = TheJIT->getFunctionAddress(FB->getName().str());
+  TheJIT->finalizeObject();
+  EXPECT_TRUE(0 != FBPtr);
+  int32_t(*FuncPtr)(void) = (int32_t(*)(void))FBPtr;
+  EXPECT_EQ(initialNum, FuncPtr())
+    << "Invalid value for global returned from JITted function in module B";
+
+  uint64_t FAPtr = TheJIT->getFunctionAddress(FA->getName().str());
+  EXPECT_TRUE(0 != FAPtr);
+  FuncPtr = (int32_t(*)(void))FAPtr;
+  EXPECT_EQ(initialNum, FuncPtr())
+    << "Invalid value for global returned from JITted function in module A";
+}
+
+// Module A { Function FA },
+// Module B { Extern FA, Function FB which calls FA },
+// Module C { Extern FA, Function FC which calls FA },
+// execute FC, FB, FA
+TEST_F(MCJITMultipleModuleTest, three_module_case) {
+  OwningPtr<Module> A, B, C;
+  Function *FA, *FB, *FC;
+  createThreeModuleCase(A, FA, B, FB, C, FC);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+  TheJIT->addModule(C.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Function FA },
+// Module B { Extern FA, Function FB which calls FA },
+// Module C { Extern FA, Function FC which calls FA },
+// execute FA, FB, FC
+TEST_F(MCJITMultipleModuleTest, three_module_case_reverse_order) {
+  OwningPtr<Module> A, B, C;
+  Function *FA, *FB, *FC;
+  createThreeModuleCase(A, FA, B, FB, C, FC);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+  TheJIT->addModule(C.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FC->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Function FA },
+// Module B { Extern FA, Function FB which calls FA },
+// Module C { Extern FB, Function FC which calls FB },
+// execute FC, FB, FA
+TEST_F(MCJITMultipleModuleTest, three_module_chain_case) {
+  OwningPtr<Module> A, B, C;
+  Function *FA, *FB, *FC;
+  createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+  TheJIT->addModule(C.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Function FA },
+// Module B { Extern FA, Function FB which calls FA },
+// Module C { Extern FB, Function FC which calls FB },
+// execute FA, FB, FC
+TEST_F(MCJITMultipleModuleTest, three_modules_chain_case_reverse_order) {
+  OwningPtr<Module> A, B, C;
+  Function *FA, *FB, *FC;
+  createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+  TheJIT->addModule(C.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB->getName().str());
+  checkAdd(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FC->getName().str());
+  checkAdd(ptr);
+}
+
+// Module A { Extern FB, Function FA which calls FB1 },
+// Module B { Extern FA, Function FB1, Function FB2 which calls FA },
+// execute FA, then FB1
+// FIXME: this test case is not supported by MCJIT
+TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB1, *FB2;
+  createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAccumulate(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB1->getName().str());
+  checkAccumulate(ptr);
+}
+
+// Module A { Extern FB, Function FA which calls FB1 },
+// Module B { Extern FA, Function FB1, Function FB2 which calls FA },
+// execute FB1 then FA
+// FIXME: this test case is not supported by MCJIT
+TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case_reverse_order) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB1, *FB2;
+  createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str());
+  checkAccumulate(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FA->getName().str());
+  checkAccumulate(ptr);
+}
+
+// Module A { Extern FB1, Function FA which calls FB1 },
+// Module B { Extern FA, Function FB1, Function FB2 which calls FA },
+// execute FB1 then FB2
+// FIXME: this test case is not supported by MCJIT
+TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case3) {
+  SKIP_UNSUPPORTED_PLATFORM;
+
+  OwningPtr<Module> A, B;
+  Function *FA, *FB1, *FB2;
+  createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);
+
+  createJIT(A.take());
+  TheJIT->addModule(B.take());
+
+  uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str());
+  checkAccumulate(ptr);
+
+  ptr = TheJIT->getFunctionAddress(FB2->getName().str());
+  checkAccumulate(ptr);
+}
+}
index 43a82984c45840c647573d4a9ef06388e74e861e..7ccd2546c063b0250427b85261db3d56465239ae 100644 (file)
@@ -28,13 +28,20 @@ protected:
 
 namespace {
 
+// FIXME: Ensure creating an execution engine does not crash when constructed
+//        with a null module.
+/*
+TEST_F(MCJITTest, null_module) {
+  createJIT(0);
+}
+*/
+
 // FIXME: In order to JIT an empty module, there needs to be
 // an interface to ExecutionEngine that forces compilation but
-// does require retrieval of a pointer to a function/global.
+// does not require retrieval of a pointer to a function/global.
 /*
 TEST_F(MCJITTest, empty_module) {
   createJIT(M.take());
-  TheJIT->finalizeObject();
   //EXPECT_NE(0, TheJIT->getObjectImage())
   //  << "Unable to generate executable loaded object image";
 }
@@ -47,7 +54,6 @@ TEST_F(MCJITTest, global_variable) {
   GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue);
   createJIT(M.take());
   void *globalPtr =  TheJIT->getPointerToGlobal(Global);
-  TheJIT->finalizeObject();
   EXPECT_TRUE(0 != globalPtr)
     << "Unable to get pointer to global value from JIT";
 
@@ -60,15 +66,19 @@ TEST_F(MCJITTest, add_function) {
 
   Function *F = insertAddFunction(M.get());
   createJIT(M.take());
-  void *addPtr = TheJIT->getPointerToFunction(F);
-  TheJIT->finalizeObject();
+  uint64_t addPtr = TheJIT->getFunctionAddress(F->getName().str());
   EXPECT_TRUE(0 != addPtr)
     << "Unable to get pointer to function from JIT";
 
-  int (*AddPtrTy)(int, int) = (int(*)(int, int))(intptr_t)addPtr;
-  EXPECT_EQ(0, AddPtrTy(0, 0));
-  EXPECT_EQ(3, AddPtrTy(1, 2));
-  EXPECT_EQ(-5, AddPtrTy(-2, -3));
+  ASSERT_TRUE(addPtr != 0) << "Unable to get pointer to function .";
+  int (*AddPtr)(int, int) = (int(*)(int, int))addPtr ;
+  EXPECT_EQ(0,   AddPtr(0, 0));
+  EXPECT_EQ(1,   AddPtr(1, 0));
+  EXPECT_EQ(3,   AddPtr(1, 2));
+  EXPECT_EQ(-5,  AddPtr(-2, -3));
+  EXPECT_EQ(30,  AddPtr(10, 20));
+  EXPECT_EQ(-30, AddPtr(-10, -20));
+  EXPECT_EQ(-40, AddPtr(-10, -30));
 }
 
 TEST_F(MCJITTest, run_main) {
@@ -77,12 +87,11 @@ TEST_F(MCJITTest, run_main) {
   int rc = 6;
   Function *Main = insertMainFunction(M.get(), 6);
   createJIT(M.take());
-  void *vPtr = TheJIT->getPointerToFunction(Main);
-  TheJIT->finalizeObject();
-  EXPECT_TRUE(0 != vPtr)
+  uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str());
+  EXPECT_TRUE(0 != ptr)
     << "Unable to get pointer to main() from JIT";
 
-  int (*FuncPtr)(void) = (int(*)(void))(intptr_t)vPtr;
+  int (*FuncPtr)(void) = (int(*)(void))ptr;
   int returnCode = FuncPtr();
   EXPECT_EQ(returnCode, rc);
 }
@@ -99,11 +108,10 @@ TEST_F(MCJITTest, return_global) {
   endFunctionWithRet(ReturnGlobal, ReadGlobal);
 
   createJIT(M.take());
-  void *rgvPtr = TheJIT->getPointerToFunction(ReturnGlobal);
-  TheJIT->finalizeObject();
+  uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str());
   EXPECT_TRUE(0 != rgvPtr);
 
-  int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)rgvPtr;
+  int32_t(*FuncPtr)(void) = (int32_t(*)(void))rgvPtr;
   EXPECT_EQ(initialNum, FuncPtr())
     << "Invalid value for global returned from JITted function";
 }
@@ -131,10 +139,9 @@ TEST_F(MCJITTest, increment_global) {
 
   createJIT(M.take());
   void *gvPtr = TheJIT->getPointerToGlobal(GV);
-  TheJIT->finalizeObject();
   EXPECT_EQ(initialNum, *(int32_t*)gvPtr);
 
-  void *vPtr = TheJIT->getPointerToFunction(IncrementGlobal);
+  void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str());
   EXPECT_TRUE(0 != vPtr)
     << "Unable to get pointer to main() from JIT";
 
@@ -172,67 +179,15 @@ TEST_F(MCJITTest, multiple_functions) {
   }
 
   createJIT(M.take());
-  void *vPtr = TheJIT->getPointerToFunction(Outer);
-  TheJIT->finalizeObject();
-  EXPECT_TRUE(0 != vPtr)
+  uint64_t ptr = TheJIT->getFunctionAddress(Outer->getName().str());
+  EXPECT_TRUE(0 != ptr)
     << "Unable to get pointer to outer function from JIT";
 
-  int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr;
+  int32_t(*FuncPtr)(void) = (int32_t(*)(void))ptr;
   EXPECT_EQ(innerRetVal, FuncPtr())
     << "Incorrect result returned from function";
 }
 
 #endif /*!defined(__arm__)*/
 
-// FIXME: ExecutionEngine has no support empty modules
-/*
-TEST_F(MCJITTest, multiple_empty_modules) {
-  SKIP_UNSUPPORTED_PLATFORM;
-
-  createJIT(M.take());
-  // JIT-compile
-  EXPECT_NE(0, TheJIT->getObjectImage())
-    << "Unable to generate executable loaded object image";
-
-  TheJIT->addModule(createEmptyModule("<other module>"));
-  TheJIT->addModule(createEmptyModule("<other other module>"));
-
-  // JIT again
-  EXPECT_NE(0, TheJIT->getObjectImage())
-    << "Unable to generate executable loaded object image";
-}
-*/
-
-// FIXME: MCJIT must support multiple modules
-/*
-TEST_F(MCJITTest, multiple_modules) {
-  SKIP_UNSUPPORTED_PLATFORM;
-
-  Function *Callee = insertAddFunction(M.get());
-  createJIT(M.take());
-
-  // caller function is defined in a different module
-  M.reset(createEmptyModule("<caller module>"));
-
-  Function *CalleeRef = insertExternalReferenceToFunction(M.get(), Callee);
-  Function *Caller = insertSimpleCallFunction(M.get(), CalleeRef);
-
-  TheJIT->addModule(M.take());
-
-  // get a function pointer in a module that was not used in EE construction
-  void *vPtr = TheJIT->getPointerToFunction(Caller);
-  TheJIT->finalizeObject();
-  EXPECT_NE(0, vPtr)
-    << "Unable to get pointer to caller function from JIT";
-
-  int(*FuncPtr)(int, int) = (int(*)(int, int))(intptr_t)vPtr;
-  EXPECT_EQ(0, FuncPtr(0, 0));
-  EXPECT_EQ(30, FuncPtr(10, 20));
-  EXPECT_EQ(-30, FuncPtr(-10, -20));
-
-  // ensure caller is destroyed before callee (free use before def)
-  M.reset();
-}
-*/
-
 }
index 5debb8b57858aea93abfdf0d57a2bb1adb89cb39..b42a9c0980db1ea26ab0dfaf14e81b7dd6ba3c8c 100644 (file)
@@ -119,12 +119,13 @@ protected:
   // Inserts an declaration to a function defined elsewhere
   Function *insertExternalReferenceToFunction(Module *M, Function *Func) {
     Function *Result = Function::Create(Func->getFunctionType(),
-                                        GlobalValue::AvailableExternallyLinkage,
+                                        GlobalValue::ExternalLinkage,
                                         Func->getName(), M);
     return Result;
   }
 
   // Inserts a global variable of type int32
+  // FIXME: make this a template function to support any type
   GlobalVariable *insertGlobalInt32(Module *M,
                                     StringRef name,
                                     int32_t InitialValue) {
@@ -138,11 +139,148 @@ protected:
                                                 name);
     return Global;
   }
+
+  // Inserts a function
+  //   int32_t recursive_add(int32_t num) {
+  //     if (num == 0) {
+  //       return num;
+  //     } else {
+  //       int32_t recursive_param = num - 1;
+  //       return num + Helper(recursive_param);
+  //     }
+  //   }
+  // NOTE: if Helper is left as the default parameter, Helper == recursive_add.
+  Function *insertAccumulateFunction(Module *M,
+                                              Function *Helper = 0,
+                                              StringRef Name = "accumulate") {
+    Function *Result = startFunction<int32_t(int32_t)>(M, Name);
+    if (Helper == 0)
+      Helper = Result;
+
+    BasicBlock *BaseCase = BasicBlock::Create(Context, "", Result);
+    BasicBlock *RecursiveCase = BasicBlock::Create(Context, "", Result);
+
+    // if (num == 0)
+    Value *Param = Result->arg_begin();
+    Value *Zero = ConstantInt::get(Context, APInt(32, 0));
+    Builder.CreateCondBr(Builder.CreateICmpEQ(Param, Zero),
+                         BaseCase, RecursiveCase);
+
+    //   return num;
+    Builder.SetInsertPoint(BaseCase);
+    Builder.CreateRet(Param);
+
+    //   int32_t recursive_param = num - 1;
+    //   return Helper(recursive_param);
+    Builder.SetInsertPoint(RecursiveCase);
+    Value *One = ConstantInt::get(Context, APInt(32, 1));
+    Value *RecursiveParam = Builder.CreateSub(Param, One);
+    Value *RecursiveReturn = Builder.CreateCall(Helper, RecursiveParam);
+    Value *Accumulator = Builder.CreateAdd(Param, RecursiveReturn);
+    Builder.CreateRet(Accumulator);
+
+    return Result;
+  }
+
+  // Populates Modules A and B:
+  // Module A { Extern FB1, Function FA which calls FB1 },
+  // Module B { Extern FA, Function FB1, Function FB2 which calls FA },
+  void createCrossModuleRecursiveCase(OwningPtr<Module> &A,
+                                      Function *&FA,
+                                      OwningPtr<Module> &B,
+                                      Function *&FB1,
+                                      Function *&FB2) {
+    // Define FB1 in B.
+    B.reset(createEmptyModule("B"));
+    FB1 = insertAccumulateFunction(B.get(), 0, "FB1");
+
+    // Declare FB1 in A (as an external).
+    A.reset(createEmptyModule("A"));
+    Function *FB1Extern = insertExternalReferenceToFunction(A.get(), FB1);
+
+    // Define FA in A (with a call to FB1).
+    FA = insertAccumulateFunction(A.get(), FB1Extern, "FA");
+
+    // Declare FA in B (as an external)
+    Function *FAExtern = insertExternalReferenceToFunction(B.get(), FA);
+
+    // Define FB2 in B (with a call to FA)
+    FB2 = insertAccumulateFunction(B.get(), FAExtern, "FB2");
+  }
+
+  // Module A { Function FA },
+  // Module B { Extern FA, Function FB which calls FA },
+  // Module C { Extern FB, Function FC which calls FB },
+  void createThreeModuleChainedCallsCase(OwningPtr<Module> &A,
+                             Function *&FA,
+                             OwningPtr<Module> &B,
+                             Function *&FB,
+                             OwningPtr<Module> &C,
+                             Function *&FC) {
+    A.reset(createEmptyModule("A"));
+    FA = insertAddFunction(A.get());
+
+    B.reset(createEmptyModule("B"));
+    Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);
+    FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B);
+
+    C.reset(createEmptyModule("C"));
+    Function *FBExtern_in_C = insertExternalReferenceToFunction(C.get(), FB);
+    FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FBExtern_in_C);
+  }
+
+
+  // Module A { Function FA },
+  // Populates Modules A and B:
+  // Module B { Function FB }
+  void createTwoModuleCase(OwningPtr<Module> &A, Function *&FA,
+                           OwningPtr<Module> &B, Function *&FB) {
+    A.reset(createEmptyModule("A"));
+    FA = insertAddFunction(A.get());
+
+    B.reset(createEmptyModule("B"));
+    FB = insertAddFunction(B.get());
+  }
+
+  // Module A { Function FA },
+  // Module B { Extern FA, Function FB which calls FA }
+  void createTwoModuleExternCase(OwningPtr<Module> &A, Function *&FA,
+                                 OwningPtr<Module> &B, Function *&FB) {
+    A.reset(createEmptyModule("A"));
+    FA = insertAddFunction(A.get());
+
+    B.reset(createEmptyModule("B"));
+    Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);
+    FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(),
+                                                             FAExtern_in_B);
+  }
+
+  // Module A { Function FA },
+  // Module B { Extern FA, Function FB which calls FA },
+  // Module C { Extern FB, Function FC which calls FA },
+  void createThreeModuleCase(OwningPtr<Module> &A,
+                             Function *&FA,
+                             OwningPtr<Module> &B,
+                             Function *&FB,
+                             OwningPtr<Module> &C,
+                             Function *&FC) {
+    A.reset(createEmptyModule("A"));
+    FA = insertAddFunction(A.get());
+
+    B.reset(createEmptyModule("B"));
+    Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);
+    FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B);
+
+    C.reset(createEmptyModule("C"));
+    Function *FAExtern_in_C = insertExternalReferenceToFunction(C.get(), FA);
+    FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FAExtern_in_C);
+  }
 };
 
+
 class MCJITTestBase : public MCJITTestAPICommon, public TrivialModuleBuilder {
 protected:
-  
+
   MCJITTestBase()
     : TrivialModuleBuilder(HostTriple)
     , OptLevel(CodeGenOpt::None)