|  | //===- MCJITMultipeModuleTest.cpp - Unit tests for the MCJIT ----*- C++ -*-===// | 
|  | // | 
|  | //                     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 "MCJITTestBase.h" | 
|  | #include "llvm/ExecutionEngine/MCJIT.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class MCJITMultipleModuleTest : public testing::Test, public MCJITTestBase {}; | 
|  |  | 
|  | // 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB; | 
|  | createTwoModuleCase(A, FA, B, FB); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB; | 
|  | createTwoModuleCase(A, FA, B, FB); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB; | 
|  | createTwoModuleExternCase(A, FA, B, FB); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB; | 
|  | createTwoModuleExternCase(A, FA, B, FB); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA1, *FA2, *FB; | 
|  | createTwoModuleExternCase(A, FA1, B, FB); | 
|  | FA2 = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(A.get(), FA1); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | 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, Internal Global GVC, Function FB loads GVB }, | 
|  | // execute FB then FA, also check that the global variables are properly accesible | 
|  | // through the ExecutionEngine APIs | 
|  | TEST_F(MCJITMultipleModuleTest, two_module_global_variables_case) { | 
|  | SKIP_UNSUPPORTED_PLATFORM; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB; | 
|  | GlobalVariable *GVA, *GVB, *GVC; | 
|  | 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)); | 
|  |  | 
|  | GVC = insertGlobalInt32(B.get(), "GVC", initialNum); | 
|  | GVC->setLinkage(GlobalValue::InternalLinkage); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | EXPECT_EQ(GVA, TheJIT->FindGlobalVariableNamed("GVA")); | 
|  | EXPECT_EQ(GVB, TheJIT->FindGlobalVariableNamed("GVB")); | 
|  | EXPECT_EQ(GVC, TheJIT->FindGlobalVariableNamed("GVC",true)); | 
|  | EXPECT_EQ(nullptr, TheJIT->FindGlobalVariableNamed("GVC")); | 
|  |  | 
|  | uint64_t FBPtr = TheJIT->getFunctionAddress(FB->getName().str()); | 
|  | TheJIT->finalizeObject(); | 
|  | EXPECT_TRUE(0 != FBPtr); | 
|  | int32_t(*FuncPtr)() = (int32_t(*)())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(*)())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) { | 
|  | SKIP_UNSUPPORTED_PLATFORM; | 
|  |  | 
|  | std::unique_ptr<Module> A, B, C; | 
|  | Function *FA, *FB, *FC; | 
|  | createThreeModuleCase(A, FA, B, FB, C, FC); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  | TheJIT->addModule(std::move(C)); | 
|  |  | 
|  | 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) { | 
|  | SKIP_UNSUPPORTED_PLATFORM; | 
|  |  | 
|  | std::unique_ptr<Module> A, B, C; | 
|  | Function *FA, *FB, *FC; | 
|  | createThreeModuleCase(A, FA, B, FB, C, FC); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  | TheJIT->addModule(std::move(C)); | 
|  |  | 
|  | 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) { | 
|  | SKIP_UNSUPPORTED_PLATFORM; | 
|  |  | 
|  | std::unique_ptr<Module> A, B, C; | 
|  | Function *FA, *FB, *FC; | 
|  | createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  | TheJIT->addModule(std::move(C)); | 
|  |  | 
|  | 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) { | 
|  | SKIP_UNSUPPORTED_PLATFORM; | 
|  |  | 
|  | std::unique_ptr<Module> A, B, C; | 
|  | Function *FA, *FB, *FC; | 
|  | createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  | TheJIT->addModule(std::move(C)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB1, *FB2; | 
|  | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB1, *FB2; | 
|  | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB1, *FB2; | 
|  | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str()); | 
|  | checkAccumulate(ptr); | 
|  |  | 
|  | ptr = TheJIT->getFunctionAddress(FB2->getName().str()); | 
|  | checkAccumulate(ptr); | 
|  | } | 
|  |  | 
|  | // Test that FindFunctionNamed finds the definition of | 
|  | // a function in the correct module. We check two functions | 
|  | // in two different modules, to make sure that for at least | 
|  | // one of them MCJIT had to ignore the extern declaration. | 
|  | TEST_F(MCJITMultipleModuleTest, FindFunctionNamed_test) { | 
|  | SKIP_UNSUPPORTED_PLATFORM; | 
|  |  | 
|  | std::unique_ptr<Module> A, B; | 
|  | Function *FA, *FB1, *FB2; | 
|  | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); | 
|  |  | 
|  | createJIT(std::move(A)); | 
|  | TheJIT->addModule(std::move(B)); | 
|  |  | 
|  | EXPECT_EQ(FA, TheJIT->FindFunctionNamed(FA->getName().data())); | 
|  | EXPECT_EQ(FB1, TheJIT->FindFunctionNamed(FB1->getName().data())); | 
|  | } | 
|  |  | 
|  | } // end anonymous namespace |