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