[docs] Fix build-docs.sh
[llvm-project.git] / llvm / unittests / ExecutionEngine / MCJIT / MCJITTest.cpp
blob14099074598a83c3513b48670aea5ebdb53b21a6
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This test suite verifies basic MCJIT functionality such as making function
10 // calls, using global variables, and compiling multpile modules.
12 //===----------------------------------------------------------------------===//
14 #include "MCJITTestBase.h"
15 #include "llvm/Support/DynamicLibrary.h"
16 #include "gtest/gtest.h"
18 using namespace llvm;
20 namespace {
22 class MCJITTest : public testing::Test, public MCJITTestBase {
23 protected:
24 void SetUp() override { M.reset(createEmptyModule("<main>")); }
27 // FIXME: Ensure creating an execution engine does not crash when constructed
28 // with a null module.
30 TEST_F(MCJITTest, null_module) {
31 createJIT(0);
35 // FIXME: In order to JIT an empty module, there needs to be
36 // an interface to ExecutionEngine that forces compilation but
37 // does not require retrieval of a pointer to a function/global.
39 TEST_F(MCJITTest, empty_module) {
40 createJIT(M.take());
41 //EXPECT_NE(0, TheJIT->getObjectImage())
42 // << "Unable to generate executable loaded object image";
46 TEST_F(MCJITTest, global_variable) {
47 SKIP_UNSUPPORTED_PLATFORM;
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";
56 EXPECT_EQ(initialValue, *(int32_t*)globalPtr)
57 << "Unexpected initial value of global";
60 TEST_F(MCJITTest, add_function) {
61 SKIP_UNSUPPORTED_PLATFORM;
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";
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));
80 TEST_F(MCJITTest, run_main) {
81 SKIP_UNSUPPORTED_PLATFORM;
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";
90 int (*FuncPtr)() = (int(*)())ptr;
91 int returnCode = FuncPtr();
92 EXPECT_EQ(returnCode, rc);
95 TEST_F(MCJITTest, return_global) {
96 SKIP_UNSUPPORTED_PLATFORM;
98 int32_t initialNum = 7;
99 GlobalVariable *GV = insertGlobalInt32(M.get(), "myglob", initialNum);
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);
107 createJIT(std::move(M));
108 uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str());
109 EXPECT_TRUE(0 != rgvPtr);
111 int32_t(*FuncPtr)() = (int32_t(*)())rgvPtr;
112 EXPECT_EQ(initialNum, FuncPtr())
113 << "Invalid value for global returned from JITted function";
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 that
119 // allocates a new memory block for the global instead of using the same
120 // 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 by
122 // JITted code) are not propagated. Instead, getPointerToGlobal() should return
123 // a pointer into the loaded ObjectImage to reference the emitted global.
125 TEST_F(MCJITTest, increment_global) {
126 SKIP_UNSUPPORTED_PLATFORM;
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);
140 createJIT(M.take());
141 void *gvPtr = TheJIT->getPointerToGlobal(GV);
142 EXPECT_EQ(initialNum, *(int32_t*)gvPtr);
144 void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str());
145 EXPECT_TRUE(0 != vPtr)
146 << "Unable to get pointer to main() from JIT";
148 int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr;
150 for(int i = 1; i < 3; ++i) {
151 int32_t result = FuncPtr();
152 EXPECT_EQ(initialNum + i, result); // OK
153 EXPECT_EQ(initialNum + i, *(int32_t*)gvPtr); // FAILS
158 // PR16013: XFAIL this test on ARM, which currently can't handle multiple relocations.
159 #if !defined(__arm__)
161 TEST_F(MCJITTest, multiple_functions) {
162 SKIP_UNSUPPORTED_PLATFORM;
164 unsigned int numLevels = 23;
165 int32_t innerRetVal= 5;
167 Function *Inner = startFunction(
168 M.get(), FunctionType::get(Builder.getInt32Ty(), {}, false), "Inner");
169 endFunctionWithRet(Inner, ConstantInt::get(Context, APInt(32, innerRetVal)));
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);
181 Inner = Outer;
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";
189 int32_t(*FuncPtr)() = (int32_t(*)())ptr;
190 EXPECT_EQ(innerRetVal, FuncPtr())
191 << "Incorrect result returned from function";
194 #endif /*!defined(__arm__)*/
196 TEST_F(MCJITTest, multiple_decl_lookups) {
197 SKIP_UNSUPPORTED_PLATFORM;
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);
205 EXPECT_TRUE(A != nullptr) << "Failed lookup - test not correctly configured.";
206 EXPECT_EQ(A, B) << "Repeat calls to getPointerToFunction fail.";
209 typedef void * (*FunctionHandlerPtr)(const std::string &str);
211 TEST_F(MCJITTest, lazy_function_creator_pointer) {
212 SKIP_UNSUPPORTED_PLATFORM;
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);
222 createJIT(std::move(M));
224 // Set up the lazy function creator that records the name of the last
225 // unresolved external function found in the module. Using a function pointer
226 // 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 as
230 // unresolved. This solves an issue on ARM where '__aeabi_*' function names
231 // are passed to this handler.
232 void *symbol =
233 llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str());
234 if (symbol) {
235 return symbol;
238 UnresolvedExternal = str;
239 return (void *)(uintptr_t)-1;
241 TheJIT->InstallLazyFunctionCreator(UnresolvedHandler);
243 // JIT the module.
244 TheJIT->finalizeObject();
246 // Verify that our handler was called.
247 EXPECT_EQ(UnresolvedExternal, "Foo");
250 TEST_F(MCJITTest, lazy_function_creator_lambda) {
251 SKIP_UNSUPPORTED_PLATFORM;
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);
265 createJIT(std::move(M));
267 // Set up the lazy function creator that records the name of unresolved
268 // 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 as
272 // unresolved. This solves an issue on ARM where '__aeabi_*' function names
273 // are passed to this handler.
274 void *symbol =
275 llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str());
276 if (symbol) {
277 return symbol;
279 UnresolvedExternals.push_back(str);
280 return (void *)(uintptr_t)-1;
282 TheJIT->InstallLazyFunctionCreator(UnresolvedHandler);
284 // JIT the module.
285 TheJIT->finalizeObject();
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);
294 } // end anonymous namespace