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