1 //===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This test suite verifies basic MCJIT functionality such as making function 11 // calls, using global variables, and compiling multpile modules. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "llvm/ExecutionEngine/MCJIT.h" 16 #include "llvm/Support/DynamicLibrary.h" 17 #include "MCJITTestBase.h" 18 #include "gtest/gtest.h" 19 20 using namespace llvm; 21 22 namespace { 23 24 class MCJITTest : public testing::Test, public MCJITTestBase { 25 protected: 26 void SetUp() override { M.reset(createEmptyModule("<main>")); } 27 }; 28 29 // FIXME: Ensure creating an execution engine does not crash when constructed 30 // with a null module. 31 /* 32 TEST_F(MCJITTest, null_module) { 33 createJIT(0); 34 } 35 */ 36 37 // FIXME: In order to JIT an empty module, there needs to be 38 // an interface to ExecutionEngine that forces compilation but 39 // does not require retrieval of a pointer to a function/global. 40 /* 41 TEST_F(MCJITTest, empty_module) { 42 createJIT(M.take()); 43 //EXPECT_NE(0, TheJIT->getObjectImage()) 44 // << "Unable to generate executable loaded object image"; 45 } 46 */ 47 48 TEST_F(MCJITTest, global_variable) { 49 SKIP_UNSUPPORTED_PLATFORM; 50 51 int initialValue = 5; 52 GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue); 53 createJIT(std::move(M)); 54 void *globalPtr = TheJIT->getPointerToGlobal(Global); 55 EXPECT_TRUE(nullptr != globalPtr) 56 << "Unable to get pointer to global value from JIT"; 57 58 EXPECT_EQ(initialValue, *(int32_t*)globalPtr) 59 << "Unexpected initial value of global"; 60 } 61 62 TEST_F(MCJITTest, add_function) { 63 SKIP_UNSUPPORTED_PLATFORM; 64 65 Function *F = insertAddFunction(M.get()); 66 createJIT(std::move(M)); 67 uint64_t addPtr = TheJIT->getFunctionAddress(F->getName().str()); 68 EXPECT_TRUE(0 != addPtr) 69 << "Unable to get pointer to function from JIT"; 70 71 ASSERT_TRUE(addPtr != 0) << "Unable to get pointer to function ."; 72 int (*AddPtr)(int, int) = (int(*)(int, int))addPtr ; 73 EXPECT_EQ(0, AddPtr(0, 0)); 74 EXPECT_EQ(1, AddPtr(1, 0)); 75 EXPECT_EQ(3, AddPtr(1, 2)); 76 EXPECT_EQ(-5, AddPtr(-2, -3)); 77 EXPECT_EQ(30, AddPtr(10, 20)); 78 EXPECT_EQ(-30, AddPtr(-10, -20)); 79 EXPECT_EQ(-40, AddPtr(-10, -30)); 80 } 81 82 TEST_F(MCJITTest, run_main) { 83 SKIP_UNSUPPORTED_PLATFORM; 84 85 int rc = 6; 86 Function *Main = insertMainFunction(M.get(), 6); 87 createJIT(std::move(M)); 88 uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str()); 89 EXPECT_TRUE(0 != ptr) 90 << "Unable to get pointer to main() from JIT"; 91 92 int (*FuncPtr)() = (int(*)())ptr; 93 int returnCode = FuncPtr(); 94 EXPECT_EQ(returnCode, rc); 95 } 96 97 TEST_F(MCJITTest, return_global) { 98 SKIP_UNSUPPORTED_PLATFORM; 99 100 int32_t initialNum = 7; 101 GlobalVariable *GV = insertGlobalInt32(M.get(), "myglob", initialNum); 102 103 Function *ReturnGlobal = startFunction<int32_t(void)>(M.get(), 104 "ReturnGlobal"); 105 Value *ReadGlobal = Builder.CreateLoad(GV); 106 endFunctionWithRet(ReturnGlobal, ReadGlobal); 107 108 createJIT(std::move(M)); 109 uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str()); 110 EXPECT_TRUE(0 != rgvPtr); 111 112 int32_t(*FuncPtr)() = (int32_t(*)())rgvPtr; 113 EXPECT_EQ(initialNum, FuncPtr()) 114 << "Invalid value for global returned from JITted function"; 115 } 116 117 // FIXME: This case fails due to a bug with getPointerToGlobal(). 118 // The bug is due to MCJIT not having an implementation of getPointerToGlobal() 119 // which results in falling back on the ExecutionEngine implementation that 120 // allocates a new memory block for the global instead of using the same 121 // global variable that is emitted by MCJIT. Hence, the pointer (gvPtr below) 122 // has the correct initial value, but updates to the real global (accessed by 123 // JITted code) are not propagated. Instead, getPointerToGlobal() should return 124 // a pointer into the loaded ObjectImage to reference the emitted global. 125 /* 126 TEST_F(MCJITTest, increment_global) { 127 SKIP_UNSUPPORTED_PLATFORM; 128 129 int32_t initialNum = 5; 130 Function *IncrementGlobal = startFunction<int32_t(void)>(M.get(), "IncrementGlobal"); 131 GlobalVariable *GV = insertGlobalInt32(M.get(), "my_global", initialNum); 132 Value *DerefGV = Builder.CreateLoad(GV); 133 Value *AddResult = Builder.CreateAdd(DerefGV, 134 ConstantInt::get(Context, APInt(32, 1))); 135 Builder.CreateStore(AddResult, GV); 136 endFunctionWithRet(IncrementGlobal, AddResult); 137 138 createJIT(M.take()); 139 void *gvPtr = TheJIT->getPointerToGlobal(GV); 140 EXPECT_EQ(initialNum, *(int32_t*)gvPtr); 141 142 void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str()); 143 EXPECT_TRUE(0 != vPtr) 144 << "Unable to get pointer to main() from JIT"; 145 146 int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr; 147 148 for(int i = 1; i < 3; ++i) { 149 int32_t result = FuncPtr(); 150 EXPECT_EQ(initialNum + i, result); // OK 151 EXPECT_EQ(initialNum + i, *(int32_t*)gvPtr); // FAILS 152 } 153 } 154 */ 155 156 // PR16013: XFAIL this test on ARM, which currently can't handle multiple relocations. 157 #if !defined(__arm__) 158 159 TEST_F(MCJITTest, multiple_functions) { 160 SKIP_UNSUPPORTED_PLATFORM; 161 162 unsigned int numLevels = 23; 163 int32_t innerRetVal= 5; 164 165 Function *Inner = startFunction<int32_t(void)>(M.get(), "Inner"); 166 endFunctionWithRet(Inner, ConstantInt::get(Context, APInt(32, innerRetVal))); 167 168 Function *Outer; 169 for (unsigned int i = 0; i < numLevels; ++i) { 170 std::stringstream funcName; 171 funcName << "level_" << i; 172 Outer = startFunction<int32_t(void)>(M.get(), funcName.str()); 173 Value *innerResult = Builder.CreateCall(Inner, {}); 174 endFunctionWithRet(Outer, innerResult); 175 176 Inner = Outer; 177 } 178 179 createJIT(std::move(M)); 180 uint64_t ptr = TheJIT->getFunctionAddress(Outer->getName().str()); 181 EXPECT_TRUE(0 != ptr) 182 << "Unable to get pointer to outer function from JIT"; 183 184 int32_t(*FuncPtr)() = (int32_t(*)())ptr; 185 EXPECT_EQ(innerRetVal, FuncPtr()) 186 << "Incorrect result returned from function"; 187 } 188 189 #endif /*!defined(__arm__)*/ 190 191 TEST_F(MCJITTest, multiple_decl_lookups) { 192 SKIP_UNSUPPORTED_PLATFORM; 193 194 Function *Foo = insertExternalReferenceToFunction<void(void)>(M.get(), "_exit"); 195 createJIT(std::move(M)); 196 void *A = TheJIT->getPointerToFunction(Foo); 197 void *B = TheJIT->getPointerToFunction(Foo); 198 199 EXPECT_TRUE(A != nullptr) << "Failed lookup - test not correctly configured."; 200 EXPECT_EQ(A, B) << "Repeat calls to getPointerToFunction fail."; 201 } 202 203 typedef void * (*FunctionHandlerPtr)(const std::string &str); 204 205 TEST_F(MCJITTest, lazy_function_creator_pointer) { 206 SKIP_UNSUPPORTED_PLATFORM; 207 208 Function *Foo = insertExternalReferenceToFunction<int32_t(void)>(M.get(), 209 "\1Foo"); 210 startFunction<int32_t(void)>(M.get(), "Parent"); 211 CallInst *Call = Builder.CreateCall(Foo, {}); 212 Builder.CreateRet(Call); 213 214 createJIT(std::move(M)); 215 216 // Set up the lazy function creator that records the name of the last 217 // unresolved external function found in the module. Using a function pointer 218 // prevents us from capturing local variables, which is why this is static. 219 static std::string UnresolvedExternal; 220 FunctionHandlerPtr UnresolvedHandler = [] (const std::string &str) { 221 // Try to resolve the function in the current process before marking it as 222 // unresolved. This solves an issue on ARM where '__aeabi_*' function names 223 // are passed to this handler. 224 void *symbol = 225 llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str()); 226 if (symbol) { 227 return symbol; 228 } 229 230 UnresolvedExternal = str; 231 return (void *)(uintptr_t)-1; 232 }; 233 TheJIT->InstallLazyFunctionCreator(UnresolvedHandler); 234 235 // JIT the module. 236 TheJIT->finalizeObject(); 237 238 // Verify that our handler was called. 239 EXPECT_EQ(UnresolvedExternal, "Foo"); 240 } 241 242 TEST_F(MCJITTest, lazy_function_creator_lambda) { 243 SKIP_UNSUPPORTED_PLATFORM; 244 245 Function *Foo1 = insertExternalReferenceToFunction<int32_t(void)>(M.get(), 246 "\1Foo1"); 247 Function *Foo2 = insertExternalReferenceToFunction<int32_t(void)>(M.get(), 248 "\1Foo2"); 249 startFunction<int32_t(void)>(M.get(), "Parent"); 250 CallInst *Call1 = Builder.CreateCall(Foo1, {}); 251 CallInst *Call2 = Builder.CreateCall(Foo2, {}); 252 Value *Result = Builder.CreateAdd(Call1, Call2); 253 Builder.CreateRet(Result); 254 255 createJIT(std::move(M)); 256 257 // Set up the lazy function creator that records the name of unresolved 258 // external functions in the module. 259 std::vector<std::string> UnresolvedExternals; 260 auto UnresolvedHandler = [&UnresolvedExternals] (const std::string &str) { 261 // Try to resolve the function in the current process before marking it as 262 // unresolved. This solves an issue on ARM where '__aeabi_*' function names 263 // are passed to this handler. 264 void *symbol = 265 llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str()); 266 if (symbol) { 267 return symbol; 268 } 269 UnresolvedExternals.push_back(str); 270 return (void *)(uintptr_t)-1; 271 }; 272 TheJIT->InstallLazyFunctionCreator(UnresolvedHandler); 273 274 // JIT the module. 275 TheJIT->finalizeObject(); 276 277 // Verify that our handler was called for each unresolved function. 278 auto I = UnresolvedExternals.begin(), E = UnresolvedExternals.end(); 279 EXPECT_EQ(UnresolvedExternals.size(), 2u); 280 EXPECT_FALSE(std::find(I, E, "Foo1") == E); 281 EXPECT_FALSE(std::find(I, E, "Foo2") == E); 282 } 283 284 } // end anonymous namespace 285