1 //===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
22 class MCJITTest
: public testing::Test
, public MCJITTestBase
{
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) {
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) {
41 //EXPECT_NE(0, TheJIT->getObjectImage())
42 // << "Unable to generate executable loaded object image";
46 TEST_F(MCJITTest
, global_variable
) {
47 SKIP_UNSUPPORTED_PLATFORM
;
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
;
84 Function
*Main
= insertMainFunction(M
.get(), 6);
85 createJIT(std::move(M
));
86 uint64_t ptr
= TheJIT
->getFunctionAddress(Main
->getName().str());
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),
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(
131 FunctionType::get(Builder.getInt32Ty(), {}, false),
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);
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
)));
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),
178 Value
*innerResult
= Builder
.CreateCall(Inner
, {});
179 endFunctionWithRet(Outer
, innerResult
);
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),
217 startFunction(M
.get(), FunctionType::get(Builder
.getInt32Ty(), {}, false),
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.
233 llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str
.c_str());
238 UnresolvedExternal
= str
;
239 return (void *)(uintptr_t)-1;
241 TheJIT
->InstallLazyFunctionCreator(UnresolvedHandler
);
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);
256 insertExternalReferenceToFunction(M
.get(), Int32VoidFnTy
, "\1Foo1");
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.
275 llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str
.c_str());
279 UnresolvedExternals
.push_back(str
);
280 return (void *)(uintptr_t)-1;
282 TheJIT
->InstallLazyFunctionCreator(UnresolvedHandler
);
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