1 //===--- OrcCAPITest.cpp - Unit tests for the OrcJIT v2 C API ---*- 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 #include "llvm-c/Core.h"
10 #include "llvm-c/Error.h"
11 #include "llvm-c/LLJIT.h"
12 #include "llvm-c/Orc.h"
13 #include "gtest/gtest.h"
14 
15 #include "llvm/ADT/Triple.h"
16 #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
17 #include "llvm/IR/LLVMContext.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/IRReader/IRReader.h"
20 #include "llvm/Support/Error.h"
21 #include "llvm/Support/SourceMgr.h"
22 #include <string>
23 
24 using namespace llvm;
25 using namespace llvm::orc;
26 
27 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThreadSafeModule, LLVMOrcThreadSafeModuleRef)
28 
29 // OrcCAPITestBase contains several helper methods and pointers for unit tests
30 // written for the LLVM-C API. It provides the following helpers:
31 //
32 // 1. Jit: an LLVMOrcLLJIT instance which is freed upon test exit
33 // 2. ExecutionSession: the LLVMOrcExecutionSession for the JIT
34 // 3. MainDylib: the main JITDylib for the LLJIT instance
35 // 4. materializationUnitFn: function pointer to an empty function, used for
36 //                           materialization unit testing
37 // 5. definitionGeneratorFn: function pointer for a basic
38 //                           LLVMOrcCAPIDefinitionGeneratorTryToGenerateFunction
39 // 6. createTestModule: helper method for creating a basic thread-safe-module
40 class OrcCAPITestBase : public testing::Test {
41 protected:
42   LLVMOrcLLJITRef Jit = nullptr;
43   LLVMOrcExecutionSessionRef ExecutionSession = nullptr;
44   LLVMOrcJITDylibRef MainDylib = nullptr;
45 
46 public:
47   static void SetUpTestCase() {
48     LLVMInitializeNativeTarget();
49     LLVMInitializeNativeAsmParser();
50     LLVMInitializeNativeAsmPrinter();
51 
52     // Attempt to set up a JIT instance once to verify that we can.
53     LLVMOrcJITTargetMachineBuilderRef JTMB = nullptr;
54     if (LLVMErrorRef E = LLVMOrcJITTargetMachineBuilderDetectHost(&JTMB)) {
55       // If setup fails then disable these tests.
56       LLVMConsumeError(E);
57       TargetSupported = false;
58       return;
59     }
60 
61     // Capture the target triple. We'll use it for both verification that
62     // this target is *supposed* to be supported, and error messages in
63     // the case that it fails anyway.
64     char *TT = LLVMOrcJITTargetMachineBuilderGetTargetTriple(JTMB);
65     TargetTriple = TT;
66     LLVMOrcJITTargetMachineBuilderDisposeTargetTriple(JTMB, TT);
67 
68     if (!isSupported(TargetTriple)) {
69       // If this triple isn't supported then bail out.
70       TargetSupported = false;
71       LLVMOrcDisposeJITTargetMachineBuilder(JTMB);
72       return;
73     }
74 
75     LLVMOrcLLJITBuilderRef Builder = LLVMOrcCreateLLJITBuilder();
76     LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(Builder, JTMB);
77     LLVMOrcLLJITRef J;
78     if (LLVMErrorRef E = LLVMOrcCreateLLJIT(&J, Builder)) {
79       // If setup fails then disable these tests.
80       TargetSupported = false;
81       LLVMConsumeError(E);
82       return;
83     }
84 
85     LLVMOrcDisposeLLJIT(J);
86     TargetSupported = true;
87   }
88 
89   void SetUp() override {
90     if (!TargetSupported)
91       return;
92 
93     LLVMOrcJITTargetMachineBuilderRef JTMB = nullptr;
94     LLVMErrorRef E1 = LLVMOrcJITTargetMachineBuilderDetectHost(&JTMB);
95     assert(E1 == LLVMErrorSuccess && "Expected call to detect host to succeed");
96     (void)E1;
97 
98     LLVMOrcLLJITBuilderRef Builder = LLVMOrcCreateLLJITBuilder();
99     LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(Builder, JTMB);
100     LLVMErrorRef E2 = LLVMOrcCreateLLJIT(&Jit, Builder);
101     assert(E2 == LLVMErrorSuccess &&
102            "Expected call to create LLJIT to succeed");
103     (void)E2;
104     ExecutionSession = LLVMOrcLLJITGetExecutionSession(Jit);
105     MainDylib = LLVMOrcLLJITGetMainJITDylib(Jit);
106   }
107   void TearDown() override {
108     LLVMOrcDisposeLLJIT(Jit);
109     Jit = nullptr;
110   }
111 
112 protected:
113   static bool isSupported(StringRef Triple) {
114     // TODO: Print error messages in failure logs, use them to audit this list.
115     // Some architectures may be unsupportable or missing key components, but
116     // some may just be failing due to bugs in this testcase.
117     if (Triple.startswith("armv7") || Triple.startswith("armv8l"))
118       return false;
119     llvm::Triple T(Triple);
120     if (T.isOSAIX() && T.isPPC64())
121       return false;
122     return true;
123   }
124 
125   static void materializationUnitFn() {}
126 
127   // Stub definition generator, where all Names are materialized from the
128   // materializationUnitFn() test function and defined into the JIT Dylib
129   static LLVMErrorRef
130   definitionGeneratorFn(LLVMOrcDefinitionGeneratorRef G, void *Ctx,
131                         LLVMOrcLookupStateRef *LS, LLVMOrcLookupKind K,
132                         LLVMOrcJITDylibRef JD, LLVMOrcJITDylibLookupFlags F,
133                         LLVMOrcCLookupSet Names, size_t NamesCount) {
134     for (size_t I = 0; I < NamesCount; I++) {
135       LLVMOrcCLookupSetElement Element = Names[I];
136       LLVMOrcJITTargetAddress Addr =
137           (LLVMOrcJITTargetAddress)(&materializationUnitFn);
138       LLVMJITSymbolFlags Flags = {LLVMJITSymbolGenericFlagsWeak, 0};
139       LLVMJITEvaluatedSymbol Sym = {Addr, Flags};
140       LLVMOrcRetainSymbolStringPoolEntry(Element.Name);
141       LLVMJITCSymbolMapPair Pair = {Element.Name, Sym};
142       LLVMJITCSymbolMapPair Pairs[] = {Pair};
143       LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Pairs, 1);
144       LLVMErrorRef Err = LLVMOrcJITDylibDefine(JD, MU);
145       if (Err)
146         return Err;
147     }
148     return LLVMErrorSuccess;
149   }
150 
151   static Error createSMDiagnosticError(llvm::SMDiagnostic &Diag) {
152     std::string Msg;
153     {
154       raw_string_ostream OS(Msg);
155       Diag.print("", OS);
156     }
157     return make_error<StringError>(std::move(Msg), inconvertibleErrorCode());
158   }
159 
160   // Create an LLVM IR module from the given StringRef.
161   static Expected<std::unique_ptr<Module>>
162   parseTestModule(LLVMContext &Ctx, StringRef Source, StringRef Name) {
163     assert(TargetSupported &&
164            "Attempted to create module for unsupported target");
165     SMDiagnostic Err;
166     if (auto M = parseIR(MemoryBufferRef(Source, Name), Err, Ctx))
167       return std::move(M);
168     return createSMDiagnosticError(Err);
169   }
170 
171   // returns the sum of its two parameters
172   static LLVMOrcThreadSafeModuleRef createTestModule(StringRef Source,
173                                                      StringRef Name) {
174     auto Ctx = std::make_unique<LLVMContext>();
175     auto M = cantFail(parseTestModule(*Ctx, Source, Name));
176     return wrap(new ThreadSafeModule(std::move(M), std::move(Ctx)));
177   }
178 
179   static LLVMMemoryBufferRef createTestObject(StringRef Source,
180                                               StringRef Name) {
181     auto Ctx = std::make_unique<LLVMContext>();
182     auto M = cantFail(parseTestModule(*Ctx, Source, Name));
183 
184     auto JTMB = cantFail(JITTargetMachineBuilder::detectHost());
185     M->setDataLayout(cantFail(JTMB.getDefaultDataLayoutForTarget()));
186     auto TM = cantFail(JTMB.createTargetMachine());
187 
188     SimpleCompiler SC(*TM);
189     auto ObjBuffer = cantFail(SC(*M));
190     return wrap(ObjBuffer.release());
191   }
192 
193   static std::string TargetTriple;
194   static bool TargetSupported;
195 };
196 
197 std::string OrcCAPITestBase::TargetTriple;
198 bool OrcCAPITestBase::TargetSupported = false;
199 
200 namespace {
201 
202 constexpr StringRef SumExample =
203     R"(
204     define i32 @sum(i32 %x, i32 %y) {
205     entry:
206       %r = add nsw i32 %x, %y
207       ret i32 %r
208     }
209   )";
210 
211 } // end anonymous namespace.
212 
213 // Consumes the given error ref and returns the string error message.
214 static std::string toString(LLVMErrorRef E) {
215   char *ErrMsg = LLVMGetErrorMessage(E);
216   std::string Result(ErrMsg);
217   LLVMDisposeErrorMessage(ErrMsg);
218   return Result;
219 }
220 
221 TEST_F(OrcCAPITestBase, SymbolStringPoolUniquing) {
222   if (!Jit) {
223     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
224     return;
225   }
226 
227   LLVMOrcSymbolStringPoolEntryRef E1 =
228       LLVMOrcExecutionSessionIntern(ExecutionSession, "aaa");
229   LLVMOrcSymbolStringPoolEntryRef E2 =
230       LLVMOrcExecutionSessionIntern(ExecutionSession, "aaa");
231   LLVMOrcSymbolStringPoolEntryRef E3 =
232       LLVMOrcExecutionSessionIntern(ExecutionSession, "bbb");
233   const char *SymbolName = LLVMOrcSymbolStringPoolEntryStr(E1);
234   ASSERT_EQ(E1, E2) << "String pool entries are not unique";
235   ASSERT_NE(E1, E3) << "Unique symbol pool entries are equal";
236   ASSERT_STREQ("aaa", SymbolName) << "String value of symbol is not equal";
237   LLVMOrcReleaseSymbolStringPoolEntry(E1);
238   LLVMOrcReleaseSymbolStringPoolEntry(E2);
239   LLVMOrcReleaseSymbolStringPoolEntry(E3);
240 }
241 
242 TEST_F(OrcCAPITestBase, JITDylibLookup) {
243   if (!Jit) {
244     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
245     return;
246   }
247   LLVMOrcJITDylibRef DoesNotExist =
248       LLVMOrcExecutionSessionGetJITDylibByName(ExecutionSession, "test");
249   ASSERT_FALSE(!!DoesNotExist);
250   LLVMOrcJITDylibRef L1 =
251       LLVMOrcExecutionSessionCreateBareJITDylib(ExecutionSession, "test");
252   LLVMOrcJITDylibRef L2 =
253       LLVMOrcExecutionSessionGetJITDylibByName(ExecutionSession, "test");
254   ASSERT_EQ(L1, L2) << "Located JIT Dylib is not equal to original";
255 }
256 
257 TEST_F(OrcCAPITestBase, MaterializationUnitCreation) {
258   if (!Jit) {
259     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
260     return;
261   }
262 
263   LLVMOrcSymbolStringPoolEntryRef Name =
264       LLVMOrcLLJITMangleAndIntern(Jit, "test");
265   LLVMJITSymbolFlags Flags = {LLVMJITSymbolGenericFlagsWeak, 0};
266   LLVMOrcJITTargetAddress Addr =
267       (LLVMOrcJITTargetAddress)(&materializationUnitFn);
268   LLVMJITEvaluatedSymbol Sym = {Addr, Flags};
269   LLVMJITCSymbolMapPair Pair = {Name, Sym};
270   LLVMJITCSymbolMapPair Pairs[] = {Pair};
271   LLVMOrcMaterializationUnitRef MU = LLVMOrcAbsoluteSymbols(Pairs, 1);
272   LLVMOrcJITDylibDefine(MainDylib, MU);
273   LLVMOrcJITTargetAddress OutAddr;
274   if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &OutAddr, "test"))
275     FAIL() << "Failed to look up \"test\" symbol (triple = " << TargetTriple
276            << "): " << toString(E);
277   ASSERT_EQ(Addr, OutAddr);
278 }
279 
280 TEST_F(OrcCAPITestBase, DefinitionGenerators) {
281   if (!Jit) {
282     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
283     return;
284   }
285 
286   LLVMOrcDefinitionGeneratorRef Gen =
287       LLVMOrcCreateCustomCAPIDefinitionGenerator(&definitionGeneratorFn,
288                                                  nullptr);
289   LLVMOrcJITDylibAddGenerator(MainDylib, Gen);
290   LLVMOrcJITTargetAddress OutAddr;
291   if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &OutAddr, "test"))
292     FAIL() << "The DefinitionGenerator did not create symbol \"test\" "
293            << "(triple = " << TargetTriple << "): " << toString(E);
294   LLVMOrcJITTargetAddress ExpectedAddr =
295       (LLVMOrcJITTargetAddress)(&materializationUnitFn);
296   ASSERT_EQ(ExpectedAddr, OutAddr);
297 }
298 
299 TEST_F(OrcCAPITestBase, ResourceTrackerDefinitionLifetime) {
300   if (!Jit) {
301     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
302     return;
303   }
304 
305   // This test case ensures that all symbols loaded into a JITDylib with a
306   // ResourceTracker attached are cleared from the JITDylib once the RT is
307   // removed.
308   LLVMOrcResourceTrackerRef RT =
309       LLVMOrcJITDylibCreateResourceTracker(MainDylib);
310   LLVMOrcThreadSafeModuleRef TSM = createTestModule(SumExample, "sum.ll");
311   if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModuleWithRT(Jit, RT, TSM))
312     FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple
313            << "): " << toString(E);
314   LLVMOrcJITTargetAddress TestFnAddr;
315   if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &TestFnAddr, "sum"))
316     FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple
317            << "): " << toString(E);
318   ASSERT_TRUE(!!TestFnAddr);
319   LLVMOrcResourceTrackerRemove(RT);
320   LLVMOrcJITTargetAddress OutAddr;
321   LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &OutAddr, "sum");
322   ASSERT_TRUE(Err);
323   LLVMConsumeError(Err);
324 
325   ASSERT_FALSE(OutAddr);
326   LLVMOrcReleaseResourceTracker(RT);
327 }
328 
329 TEST_F(OrcCAPITestBase, ResourceTrackerTransfer) {
330   if (!Jit) {
331     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
332     return;
333   }
334 
335   LLVMOrcResourceTrackerRef DefaultRT =
336       LLVMOrcJITDylibGetDefaultResourceTracker(MainDylib);
337   LLVMOrcResourceTrackerRef RT2 =
338       LLVMOrcJITDylibCreateResourceTracker(MainDylib);
339   LLVMOrcThreadSafeModuleRef TSM = createTestModule(SumExample, "sum.ll");
340   if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModuleWithRT(Jit, DefaultRT, TSM))
341     FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple
342            << "): " << toString(E);
343   LLVMOrcJITTargetAddress Addr;
344   if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &Addr, "sum"))
345     FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple
346            << "): " << toString(E);
347   LLVMOrcResourceTrackerTransferTo(DefaultRT, RT2);
348   LLVMErrorRef Err = LLVMOrcLLJITLookup(Jit, &Addr, "sum");
349   ASSERT_FALSE(Err);
350   LLVMOrcReleaseResourceTracker(RT2);
351 }
352 
353 TEST_F(OrcCAPITestBase, AddObjectBuffer) {
354   if (!Jit) {
355     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
356     return;
357   }
358 
359   LLVMOrcObjectLayerRef ObjLinkingLayer = LLVMOrcLLJITGetObjLinkingLayer(Jit);
360   LLVMMemoryBufferRef ObjBuffer = createTestObject(SumExample, "sum.ll");
361 
362   if (LLVMErrorRef E = LLVMOrcObjectLayerAddObjectFile(ObjLinkingLayer,
363                                                        MainDylib, ObjBuffer))
364     FAIL() << "Failed to add object file to ObjLinkingLayer (triple = "
365            << TargetTriple << "): " << toString(E);
366 
367   LLVMOrcJITTargetAddress SumAddr;
368   if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &SumAddr, "sum"))
369     FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple
370            << "): " << toString(E);
371   ASSERT_TRUE(!!SumAddr);
372 }
373 
374 TEST_F(OrcCAPITestBase, ExecutionTest) {
375   if (!Jit) {
376     // TODO: Use GTEST_SKIP() when GTest is updated to version 1.10.0
377     return;
378   }
379 
380   using SumFunctionType = int32_t (*)(int32_t, int32_t);
381 
382   // This test performs OrcJIT compilation of a simple sum module
383   LLVMInitializeNativeAsmPrinter();
384   LLVMOrcThreadSafeModuleRef TSM = createTestModule(SumExample, "sum.ll");
385   if (LLVMErrorRef E = LLVMOrcLLJITAddLLVMIRModule(Jit, MainDylib, TSM))
386     FAIL() << "Failed to add LLVM IR module to LLJIT (triple = " << TargetTriple
387            << ")" << toString(E);
388   LLVMOrcJITTargetAddress TestFnAddr;
389   if (LLVMErrorRef E = LLVMOrcLLJITLookup(Jit, &TestFnAddr, "sum"))
390     FAIL() << "Symbol \"sum\" was not added into JIT (triple = " << TargetTriple
391            << "): " << toString(E);
392   auto *SumFn = (SumFunctionType)(TestFnAddr);
393   int32_t Result = SumFn(1, 1);
394   ASSERT_EQ(2, Result);
395 }
396