1 | //===- MCJITMultipeModuleTest.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 MCJIT for handling multiple modules in a single |
10 | // ExecutionEngine by building multiple modules, making function calls across |
11 | // modules, accessing global variables, etc. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "MCJITTestBase.h" |
15 | #include "llvm/ExecutionEngine/MCJIT.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | using namespace llvm; |
19 | |
20 | namespace { |
21 | |
22 | class MCJITMultipleModuleTest : public testing::Test, public MCJITTestBase {}; |
23 | |
24 | // FIXME: ExecutionEngine has no support empty modules |
25 | /* |
26 | TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) { |
27 | SKIP_UNSUPPORTED_PLATFORM; |
28 | |
29 | createJIT(M.take()); |
30 | // JIT-compile |
31 | EXPECT_NE(0, TheJIT->getObjectImage()) |
32 | << "Unable to generate executable loaded object image"; |
33 | |
34 | TheJIT->addModule(createEmptyModule("<other module>")); |
35 | TheJIT->addModule(createEmptyModule("<other other module>")); |
36 | |
37 | // JIT again |
38 | EXPECT_NE(0, TheJIT->getObjectImage()) |
39 | << "Unable to generate executable loaded object image"; |
40 | } |
41 | */ |
42 | |
43 | // Helper Function to test add operation |
44 | void checkAdd(uint64_t ptr) { |
45 | ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function." ; |
46 | int (*AddPtr)(int, int) = (int (*)(int, int))ptr; |
47 | EXPECT_EQ(0, AddPtr(0, 0)); |
48 | EXPECT_EQ(1, AddPtr(1, 0)); |
49 | EXPECT_EQ(3, AddPtr(1, 2)); |
50 | EXPECT_EQ(-5, AddPtr(-2, -3)); |
51 | EXPECT_EQ(30, AddPtr(10, 20)); |
52 | EXPECT_EQ(-30, AddPtr(-10, -20)); |
53 | EXPECT_EQ(-40, AddPtr(-10, -30)); |
54 | } |
55 | |
56 | void checkAccumulate(uint64_t ptr) { |
57 | ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function." ; |
58 | int32_t (*FPtr)(int32_t) = (int32_t (*)(int32_t))(intptr_t)ptr; |
59 | EXPECT_EQ(0, FPtr(0)); |
60 | EXPECT_EQ(1, FPtr(1)); |
61 | EXPECT_EQ(3, FPtr(2)); |
62 | EXPECT_EQ(6, FPtr(3)); |
63 | EXPECT_EQ(10, FPtr(4)); |
64 | EXPECT_EQ(15, FPtr(5)); |
65 | } |
66 | |
67 | // FIXME: ExecutionEngine has no support empty modules |
68 | /* |
69 | TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) { |
70 | SKIP_UNSUPPORTED_PLATFORM; |
71 | |
72 | createJIT(M.take()); |
73 | // JIT-compile |
74 | EXPECT_NE(0, TheJIT->getObjectImage()) |
75 | << "Unable to generate executable loaded object image"; |
76 | |
77 | TheJIT->addModule(createEmptyModule("<other module>")); |
78 | TheJIT->addModule(createEmptyModule("<other other module>")); |
79 | |
80 | // JIT again |
81 | EXPECT_NE(0, TheJIT->getObjectImage()) |
82 | << "Unable to generate executable loaded object image"; |
83 | } |
84 | */ |
85 | |
86 | // Module A { Function FA }, |
87 | // Module B { Function FB }, |
88 | // execute FA then FB |
89 | TEST_F(MCJITMultipleModuleTest, two_module_case) { |
90 | SKIP_UNSUPPORTED_PLATFORM; |
91 | |
92 | std::unique_ptr<Module> A, B; |
93 | Function *FA, *FB; |
94 | createTwoModuleCase(A, FA, B, FB); |
95 | |
96 | createJIT(M: std::move(A)); |
97 | TheJIT->addModule(M: std::move(B)); |
98 | |
99 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
100 | checkAdd(ptr); |
101 | |
102 | ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
103 | checkAdd(ptr); |
104 | } |
105 | |
106 | // Module A { Function FA }, |
107 | // Module B { Function FB }, |
108 | // execute FB then FA |
109 | TEST_F(MCJITMultipleModuleTest, two_module_reverse_case) { |
110 | SKIP_UNSUPPORTED_PLATFORM; |
111 | |
112 | std::unique_ptr<Module> A, B; |
113 | Function *FA, *FB; |
114 | createTwoModuleCase(A, FA, B, FB); |
115 | |
116 | createJIT(M: std::move(A)); |
117 | TheJIT->addModule(M: std::move(B)); |
118 | |
119 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
120 | TheJIT->finalizeObject(); |
121 | checkAdd(ptr); |
122 | |
123 | ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
124 | checkAdd(ptr); |
125 | } |
126 | |
127 | // Module A { Function FA }, |
128 | // Module B { Extern FA, Function FB which calls FA }, |
129 | // execute FB then FA |
130 | TEST_F(MCJITMultipleModuleTest, two_module_extern_reverse_case) { |
131 | SKIP_UNSUPPORTED_PLATFORM; |
132 | |
133 | std::unique_ptr<Module> A, B; |
134 | Function *FA, *FB; |
135 | createTwoModuleExternCase(A, FA, B, FB); |
136 | |
137 | createJIT(M: std::move(A)); |
138 | TheJIT->addModule(M: std::move(B)); |
139 | |
140 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
141 | TheJIT->finalizeObject(); |
142 | checkAdd(ptr); |
143 | |
144 | ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
145 | checkAdd(ptr); |
146 | } |
147 | |
148 | // Module A { Function FA }, |
149 | // Module B { Extern FA, Function FB which calls FA }, |
150 | // execute FA then FB |
151 | TEST_F(MCJITMultipleModuleTest, two_module_extern_case) { |
152 | SKIP_UNSUPPORTED_PLATFORM; |
153 | |
154 | std::unique_ptr<Module> A, B; |
155 | Function *FA, *FB; |
156 | createTwoModuleExternCase(A, FA, B, FB); |
157 | |
158 | createJIT(M: std::move(A)); |
159 | TheJIT->addModule(M: std::move(B)); |
160 | |
161 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
162 | checkAdd(ptr); |
163 | |
164 | ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
165 | checkAdd(ptr); |
166 | } |
167 | |
168 | // Module A { Function FA1, Function FA2 which calls FA1 }, |
169 | // Module B { Extern FA1, Function FB which calls FA1 }, |
170 | // execute FB then FA2 |
171 | TEST_F(MCJITMultipleModuleTest, two_module_consecutive_call_case) { |
172 | SKIP_UNSUPPORTED_PLATFORM; |
173 | |
174 | std::unique_ptr<Module> A, B; |
175 | Function *FA1, *FA2, *FB; |
176 | createTwoModuleExternCase(A, FA&: FA1, B, FB); |
177 | FA2 = insertSimpleCallFunction(M: A.get(), Callee: FA1); |
178 | |
179 | createJIT(M: std::move(A)); |
180 | TheJIT->addModule(M: std::move(B)); |
181 | |
182 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
183 | TheJIT->finalizeObject(); |
184 | checkAdd(ptr); |
185 | |
186 | ptr = TheJIT->getFunctionAddress(Name: FA2->getName().str()); |
187 | checkAdd(ptr); |
188 | } |
189 | |
190 | // TODO: |
191 | // Module A { Extern Global GVB, Global Variable GVA, Function FA loads GVB }, |
192 | // Module B { Extern Global GVA, Global Variable GVB, Function FB loads GVA }, |
193 | |
194 | |
195 | // Module A { Global Variable GVA, Function FA loads GVA }, |
196 | // Module B { Global Variable GVB, Internal Global GVC, Function FB loads GVB }, |
197 | // execute FB then FA, also check that the global variables are properly accesible |
198 | // through the ExecutionEngine APIs |
199 | TEST_F(MCJITMultipleModuleTest, two_module_global_variables_case) { |
200 | SKIP_UNSUPPORTED_PLATFORM; |
201 | |
202 | std::unique_ptr<Module> A, B; |
203 | Function *FA, *FB; |
204 | GlobalVariable *GVA, *GVB, *GVC; |
205 | |
206 | A.reset(p: createEmptyModule(Name: "A" )); |
207 | B.reset(p: createEmptyModule(Name: "B" )); |
208 | |
209 | int32_t initialNum = 7; |
210 | GVA = insertGlobalInt32(M: A.get(), name: "GVA" , InitialValue: initialNum); |
211 | GVB = insertGlobalInt32(M: B.get(), name: "GVB" , InitialValue: initialNum); |
212 | FA = startFunction(M: A.get(), |
213 | FT: FunctionType::get(Result: Builder.getInt32Ty(), Params: {}, isVarArg: false), Name: "FA" ); |
214 | endFunctionWithRet(Func: FA, RetValue: Builder.CreateLoad(Ty: Builder.getInt32Ty(), Ptr: GVA)); |
215 | FB = startFunction(M: B.get(), |
216 | FT: FunctionType::get(Result: Builder.getInt32Ty(), Params: {}, isVarArg: false), Name: "FB" ); |
217 | endFunctionWithRet(Func: FB, RetValue: Builder.CreateLoad(Ty: Builder.getInt32Ty(), Ptr: GVB)); |
218 | |
219 | GVC = insertGlobalInt32(M: B.get(), name: "GVC" , InitialValue: initialNum); |
220 | GVC->setLinkage(GlobalValue::InternalLinkage); |
221 | |
222 | createJIT(M: std::move(A)); |
223 | TheJIT->addModule(M: std::move(B)); |
224 | |
225 | EXPECT_EQ(GVA, TheJIT->FindGlobalVariableNamed("GVA" )); |
226 | EXPECT_EQ(GVB, TheJIT->FindGlobalVariableNamed("GVB" )); |
227 | EXPECT_EQ(GVC, TheJIT->FindGlobalVariableNamed("GVC" ,true)); |
228 | EXPECT_EQ(nullptr, TheJIT->FindGlobalVariableNamed("GVC" )); |
229 | |
230 | uint64_t FBPtr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
231 | TheJIT->finalizeObject(); |
232 | EXPECT_TRUE(0 != FBPtr); |
233 | int32_t(*FuncPtr)() = (int32_t(*)())FBPtr; |
234 | EXPECT_EQ(initialNum, FuncPtr()) |
235 | << "Invalid value for global returned from JITted function in module B" ; |
236 | |
237 | uint64_t FAPtr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
238 | EXPECT_TRUE(0 != FAPtr); |
239 | FuncPtr = (int32_t(*)())FAPtr; |
240 | EXPECT_EQ(initialNum, FuncPtr()) |
241 | << "Invalid value for global returned from JITted function in module A" ; |
242 | } |
243 | |
244 | // Module A { Function FA }, |
245 | // Module B { Extern FA, Function FB which calls FA }, |
246 | // Module C { Extern FA, Function FC which calls FA }, |
247 | // execute FC, FB, FA |
248 | TEST_F(MCJITMultipleModuleTest, three_module_case) { |
249 | SKIP_UNSUPPORTED_PLATFORM; |
250 | |
251 | std::unique_ptr<Module> A, B, C; |
252 | Function *FA, *FB, *FC; |
253 | createThreeModuleCase(A, FA, B, FB, C, FC); |
254 | |
255 | createJIT(M: std::move(A)); |
256 | TheJIT->addModule(M: std::move(B)); |
257 | TheJIT->addModule(M: std::move(C)); |
258 | |
259 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FC->getName().str()); |
260 | checkAdd(ptr); |
261 | |
262 | ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
263 | checkAdd(ptr); |
264 | |
265 | ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
266 | checkAdd(ptr); |
267 | } |
268 | |
269 | // Module A { Function FA }, |
270 | // Module B { Extern FA, Function FB which calls FA }, |
271 | // Module C { Extern FA, Function FC which calls FA }, |
272 | // execute FA, FB, FC |
273 | TEST_F(MCJITMultipleModuleTest, three_module_case_reverse_order) { |
274 | SKIP_UNSUPPORTED_PLATFORM; |
275 | |
276 | std::unique_ptr<Module> A, B, C; |
277 | Function *FA, *FB, *FC; |
278 | createThreeModuleCase(A, FA, B, FB, C, FC); |
279 | |
280 | createJIT(M: std::move(A)); |
281 | TheJIT->addModule(M: std::move(B)); |
282 | TheJIT->addModule(M: std::move(C)); |
283 | |
284 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
285 | checkAdd(ptr); |
286 | |
287 | ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
288 | checkAdd(ptr); |
289 | |
290 | ptr = TheJIT->getFunctionAddress(Name: FC->getName().str()); |
291 | checkAdd(ptr); |
292 | } |
293 | |
294 | // Module A { Function FA }, |
295 | // Module B { Extern FA, Function FB which calls FA }, |
296 | // Module C { Extern FB, Function FC which calls FB }, |
297 | // execute FC, FB, FA |
298 | TEST_F(MCJITMultipleModuleTest, three_module_chain_case) { |
299 | SKIP_UNSUPPORTED_PLATFORM; |
300 | |
301 | std::unique_ptr<Module> A, B, C; |
302 | Function *FA, *FB, *FC; |
303 | createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC); |
304 | |
305 | createJIT(M: std::move(A)); |
306 | TheJIT->addModule(M: std::move(B)); |
307 | TheJIT->addModule(M: std::move(C)); |
308 | |
309 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FC->getName().str()); |
310 | checkAdd(ptr); |
311 | |
312 | ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
313 | checkAdd(ptr); |
314 | |
315 | ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
316 | checkAdd(ptr); |
317 | } |
318 | |
319 | // Module A { Function FA }, |
320 | // Module B { Extern FA, Function FB which calls FA }, |
321 | // Module C { Extern FB, Function FC which calls FB }, |
322 | // execute FA, FB, FC |
323 | TEST_F(MCJITMultipleModuleTest, three_modules_chain_case_reverse_order) { |
324 | SKIP_UNSUPPORTED_PLATFORM; |
325 | |
326 | std::unique_ptr<Module> A, B, C; |
327 | Function *FA, *FB, *FC; |
328 | createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC); |
329 | |
330 | createJIT(M: std::move(A)); |
331 | TheJIT->addModule(M: std::move(B)); |
332 | TheJIT->addModule(M: std::move(C)); |
333 | |
334 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
335 | checkAdd(ptr); |
336 | |
337 | ptr = TheJIT->getFunctionAddress(Name: FB->getName().str()); |
338 | checkAdd(ptr); |
339 | |
340 | ptr = TheJIT->getFunctionAddress(Name: FC->getName().str()); |
341 | checkAdd(ptr); |
342 | } |
343 | |
344 | // Module A { Extern FB, Function FA which calls FB1 }, |
345 | // Module B { Extern FA, Function FB1, Function FB2 which calls FA }, |
346 | // execute FA, then FB1 |
347 | // FIXME: this test case is not supported by MCJIT |
348 | TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case) { |
349 | SKIP_UNSUPPORTED_PLATFORM; |
350 | |
351 | std::unique_ptr<Module> A, B; |
352 | Function *FA, *FB1, *FB2; |
353 | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); |
354 | |
355 | createJIT(M: std::move(A)); |
356 | TheJIT->addModule(M: std::move(B)); |
357 | |
358 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
359 | checkAccumulate(ptr); |
360 | |
361 | ptr = TheJIT->getFunctionAddress(Name: FB1->getName().str()); |
362 | checkAccumulate(ptr); |
363 | } |
364 | |
365 | // Module A { Extern FB, Function FA which calls FB1 }, |
366 | // Module B { Extern FA, Function FB1, Function FB2 which calls FA }, |
367 | // execute FB1 then FA |
368 | // FIXME: this test case is not supported by MCJIT |
369 | TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case_reverse_order) { |
370 | SKIP_UNSUPPORTED_PLATFORM; |
371 | |
372 | std::unique_ptr<Module> A, B; |
373 | Function *FA, *FB1, *FB2; |
374 | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); |
375 | |
376 | createJIT(M: std::move(A)); |
377 | TheJIT->addModule(M: std::move(B)); |
378 | |
379 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FB1->getName().str()); |
380 | checkAccumulate(ptr); |
381 | |
382 | ptr = TheJIT->getFunctionAddress(Name: FA->getName().str()); |
383 | checkAccumulate(ptr); |
384 | } |
385 | |
386 | // Module A { Extern FB1, Function FA which calls FB1 }, |
387 | // Module B { Extern FA, Function FB1, Function FB2 which calls FA }, |
388 | // execute FB1 then FB2 |
389 | // FIXME: this test case is not supported by MCJIT |
390 | TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case3) { |
391 | SKIP_UNSUPPORTED_PLATFORM; |
392 | |
393 | std::unique_ptr<Module> A, B; |
394 | Function *FA, *FB1, *FB2; |
395 | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); |
396 | |
397 | createJIT(M: std::move(A)); |
398 | TheJIT->addModule(M: std::move(B)); |
399 | |
400 | uint64_t ptr = TheJIT->getFunctionAddress(Name: FB1->getName().str()); |
401 | checkAccumulate(ptr); |
402 | |
403 | ptr = TheJIT->getFunctionAddress(Name: FB2->getName().str()); |
404 | checkAccumulate(ptr); |
405 | } |
406 | |
407 | // Test that FindFunctionNamed finds the definition of |
408 | // a function in the correct module. We check two functions |
409 | // in two different modules, to make sure that for at least |
410 | // one of them MCJIT had to ignore the extern declaration. |
411 | TEST_F(MCJITMultipleModuleTest, FindFunctionNamed_test) { |
412 | SKIP_UNSUPPORTED_PLATFORM; |
413 | |
414 | std::unique_ptr<Module> A, B; |
415 | Function *FA, *FB1, *FB2; |
416 | createCrossModuleRecursiveCase(A, FA, B, FB1, FB2); |
417 | |
418 | createJIT(M: std::move(A)); |
419 | TheJIT->addModule(M: std::move(B)); |
420 | |
421 | EXPECT_EQ(FA, TheJIT->FindFunctionNamed(FA->getName().data())); |
422 | EXPECT_EQ(FB1, TheJIT->FindFunctionNamed(FB1->getName().data())); |
423 | } |
424 | |
425 | } // end anonymous namespace |
426 | |