LLVM 24.0.0git
OffloadWrapper.cpp
Go to the documentation of this file.
1//===- OffloadWrapper.cpp ---------------------------------------*- 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
10#include "llvm/ADT/ArrayRef.h"
12#include "llvm/ADT/StringRef.h"
13#include "llvm/ADT/Twine.h"
16#include "llvm/IR/Constants.h"
19#include "llvm/IR/IRBuilder.h"
20#include "llvm/IR/LLVMContext.h"
21#include "llvm/IR/Module.h"
22#include "llvm/IR/Type.h"
24#include "llvm/Support/Error.h"
28
29#include <utility>
30
31using namespace llvm;
32using namespace llvm::object;
33using namespace llvm::offloading;
34
35namespace {
36/// Magic number that begins the section containing the CUDA fatbinary.
37constexpr unsigned CudaFatMagic = 0x466243b1;
38constexpr unsigned HIPFatMagic = 0x48495046;
39
41 return M.getDataLayout().getIntPtrType(M.getContext());
42}
43
44/// Returns the appropriate startup section for registration functions.
45/// Mach-O uses "__TEXT,__StaticInit"; ELF/COFF use ".text.startup".
46StringRef getStartupSection(const Triple &T) {
47 return T.isOSBinFormatMachO() ? "__TEXT,__StaticInit" : ".text.startup";
48}
49
50// struct __tgt_device_image {
51// void *ImageStart;
52// void *ImageEnd;
53// __tgt_offload_entry *EntriesBegin;
54// __tgt_offload_entry *EntriesEnd;
55// };
56StructType *getDeviceImageTy(Module &M) {
57 LLVMContext &C = M.getContext();
58 StructType *ImageTy = StructType::getTypeByName(C, "__tgt_device_image");
59 if (!ImageTy)
60 ImageTy =
61 StructType::create("__tgt_device_image", PointerType::getUnqual(C),
64 return ImageTy;
65}
66
67PointerType *getDeviceImagePtrTy(Module &M) {
68 return PointerType::getUnqual(M.getContext());
69}
70
71// struct __tgt_bin_desc {
72// int32_t NumDeviceImages;
73// __tgt_device_image *DeviceImages;
74// __tgt_offload_entry *HostEntriesBegin;
75// __tgt_offload_entry *HostEntriesEnd;
76// };
77StructType *getBinDescTy(Module &M) {
78 LLVMContext &C = M.getContext();
79 StructType *DescTy = StructType::getTypeByName(C, "__tgt_bin_desc");
80 if (!DescTy)
81 DescTy = StructType::create(
82 "__tgt_bin_desc", Type::getInt32Ty(C), getDeviceImagePtrTy(M),
84 return DescTy;
85}
86
87PointerType *getBinDescPtrTy(Module &M) {
88 return PointerType::getUnqual(M.getContext());
89}
90
91/// Creates binary descriptor for the given device images. Binary descriptor
92/// is an object that is passed to the offloading runtime at program startup
93/// and it describes all device images available in the executable or shared
94/// library. It is defined as follows
95///
96/// __attribute__((visibility("hidden")))
97/// extern __tgt_offload_entry *__start_llvm_offload_entries;
98/// __attribute__((visibility("hidden")))
99/// extern __tgt_offload_entry *__stop_llvm_offload_entries;
100///
101/// static const char Image0[] = { <Bufs.front() contents> };
102/// ...
103/// static const char ImageN[] = { <Bufs.back() contents> };
104///
105/// static const __tgt_device_image Images[] = {
106/// {
107/// Image0, /*ImageStart*/
108/// Image0 + sizeof(Image0), /*ImageEnd*/
109/// __start_llvm_offload_entries, /*EntriesBegin*/
110/// __stop_llvm_offload_entries /*EntriesEnd*/
111/// },
112/// ...
113/// {
114/// ImageN, /*ImageStart*/
115/// ImageN + sizeof(ImageN), /*ImageEnd*/
116/// __start_llvm_offload_entries, /*EntriesBegin*/
117/// __stop_llvm_offload_entries /*EntriesEnd*/
118/// }
119/// };
120///
121/// static const __tgt_bin_desc BinDesc = {
122/// sizeof(Images) / sizeof(Images[0]), /*NumDeviceImages*/
123/// Images, /*DeviceImages*/
124/// __start_llvm_offload_entries, /*HostEntriesBegin*/
125/// __stop_llvm_offload_entries /*HostEntriesEnd*/
126/// };
127///
128/// Global variable that represents BinDesc is returned.
129GlobalVariable *createBinDesc(Module &M, ArrayRef<ArrayRef<char>> Bufs,
130 EntryArrayTy EntryArray, StringRef Suffix,
131 bool Relocatable) {
132 LLVMContext &C = M.getContext();
133 auto [EntriesB, EntriesE] = EntryArray;
134
135 auto *Zero = ConstantInt::get(getSizeTTy(M), 0u);
136
137 // Create initializer for the images array.
138 SmallVector<Constant *, 4u> ImagesInits;
139 ImagesInits.reserve(Bufs.size());
140 for (ArrayRef<char> Buf : Bufs) {
141 // We embed the full offloading entry so the binary utilities can parse it.
142 auto *Data = ConstantDataArray::get(C, Buf);
143 auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant=*/true,
145 ".omp_offloading.device_image" + Suffix);
147 Image->setSection(Relocatable ? ".llvm.offloading.relocatable"
148 : ".llvm.offloading");
150
151 StringRef Binary(Buf.data(), Buf.size());
152
153 uint64_t BeginOffset = 0;
154 uint64_t EndOffset = Binary.size();
155
156 // Optionally use an offload binary for its offload dumping support.
157 // The device image struct contains the pointer to the beginning and end of
158 // the image stored inside of the offload binary. There should only be one
159 // of these for each buffer so we parse it out manually.
161 const auto *Header =
162 reinterpret_cast<const object::OffloadBinary::Header *>(
163 Binary.bytes_begin());
164 const auto *Entry =
165 reinterpret_cast<const object::OffloadBinary::Entry *>(
166 Binary.bytes_begin() + Header->EntriesOffset);
167 BeginOffset = Entry->ImageOffset;
168 EndOffset = Entry->ImageOffset + Entry->ImageSize;
169 }
170
171 auto *Begin = ConstantInt::get(getSizeTTy(M), BeginOffset);
172 auto *Size = ConstantInt::get(getSizeTTy(M), EndOffset);
173 Constant *ZeroBegin[] = {Zero, Begin};
174 Constant *ZeroSize[] = {Zero, Size};
175
176 auto *ImageB =
177 ConstantExpr::getGetElementPtr(Image->getValueType(), Image, ZeroBegin);
178 auto *ImageE =
179 ConstantExpr::getGetElementPtr(Image->getValueType(), Image, ZeroSize);
180
181 ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(M), ImageB,
182 ImageE, EntriesB, EntriesE));
183 }
184
185 // Then create images array.
186 auto *ImagesData = ConstantArray::get(
187 ArrayType::get(getDeviceImageTy(M), ImagesInits.size()), ImagesInits);
188
189 auto *Images =
190 new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true,
192 ".omp_offloading.device_images" + Suffix);
193 Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
194
195 // And finally create the binary descriptor object.
196 auto *DescInit = ConstantStruct::get(
197 getBinDescTy(M),
198 ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), Images,
199 EntriesB, EntriesE);
200
201 return new GlobalVariable(M, DescInit->getType(), /*isConstant=*/true,
203 ".omp_offloading.descriptor" + Suffix);
204}
205
206Function *createUnregisterFunction(Module &M, GlobalVariable *BinDesc,
207 StringRef Suffix) {
208 LLVMContext &C = M.getContext();
209 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
210 auto *Func =
212 ".omp_offloading.descriptor_unreg" + Suffix, &M);
213 Func->setSection(getStartupSection(M.getTargetTriple()));
214
215 // Get __tgt_unregister_lib function declaration.
216 auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
217 /*isVarArg*/ false);
218 FunctionCallee UnRegFuncC =
219 M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy);
220
221 // Construct function body
222 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
223 Builder.CreateCall(UnRegFuncC, BinDesc);
224 Builder.CreateRetVoid();
225
226 return Func;
227}
228
229void createRegisterFunction(Module &M, GlobalVariable *BinDesc,
230 StringRef Suffix) {
231 LLVMContext &C = M.getContext();
232 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
234 ".omp_offloading.descriptor_reg" + Suffix, &M);
235 Func->setSection(getStartupSection(M.getTargetTriple()));
236
237 // Get __tgt_register_lib function declaration.
238 auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
239 /*isVarArg*/ false);
240 FunctionCallee RegFuncC =
241 M.getOrInsertFunction("__tgt_register_lib", RegFuncTy);
242
243 auto *AtExitTy = FunctionType::get(
244 Type::getInt32Ty(C), PointerType::getUnqual(C), /*isVarArg=*/false);
245 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
246
247 Function *UnregFunc = createUnregisterFunction(M, BinDesc, Suffix);
248
249 // Construct function body
250 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
251
252 Builder.CreateCall(RegFuncC, BinDesc);
253
254 // Register the destructors with 'atexit'. This is expected by the CUDA
255 // runtime and ensures that we clean up before dynamic objects are destroyed.
256 // This needs to be done after plugin initialization to ensure that it is
257 // called before the plugin runtime is destroyed.
258 Builder.CreateCall(AtExit, UnregFunc);
259 Builder.CreateRetVoid();
260
261 // Add this function to constructors.
262 appendToGlobalCtors(M, Func, /*Priority=*/101);
263}
264
265// struct fatbin_wrapper {
266// int32_t magic;
267// int32_t version;
268// void *image;
269// void *reserved;
270//};
271StructType *getFatbinWrapperTy(Module &M) {
272 LLVMContext &C = M.getContext();
273 StructType *FatbinTy = StructType::getTypeByName(C, "fatbin_wrapper");
274 if (!FatbinTy)
275 FatbinTy = StructType::create(
276 "fatbin_wrapper", Type::getInt32Ty(C), Type::getInt32Ty(C),
278 return FatbinTy;
279}
280
281/// Embed the image \p Image into the module \p M so it can be found by the
282/// runtime.
283GlobalVariable *createFatbinDesc(Module &M, ArrayRef<char> Image, bool IsHIP,
284 StringRef Suffix) {
285 LLVMContext &C = M.getContext();
286 llvm::Type *Int8PtrTy = PointerType::getUnqual(C);
287 const llvm::Triple &Triple = M.getTargetTriple();
288
289 // Create the global string containing the fatbinary.
290 StringRef FatbinConstantSection =
291 IsHIP ? (Triple.isMacOSX() ? "__HIP,__hip_fatbin" : ".hip_fatbin")
292 : (Triple.isMacOSX() ? "__NV_CUDA,__nv_fatbin" : ".nv_fatbin");
293 auto *Data = ConstantDataArray::get(C, Image);
294 auto *Fatbin = new GlobalVariable(M, Data->getType(), /*isConstant*/ true,
296 ".fatbin_image" + Suffix);
297 Fatbin->setSection(FatbinConstantSection);
298
299 // Create the fatbinary wrapper
300 StringRef FatbinWrapperSection =
301 IsHIP ? (Triple.isMacOSX() ? "__HIP,__fatbin" : ".hipFatBinSegment")
302 : (Triple.isMacOSX() ? "__NV_CUDA,__fatbin" : ".nvFatBinSegment");
303 Constant *FatbinWrapper[] = {
304 ConstantInt::get(Type::getInt32Ty(C), IsHIP ? HIPFatMagic : CudaFatMagic),
305 ConstantInt::get(Type::getInt32Ty(C), 1),
308
309 Constant *FatbinInitializer =
310 ConstantStruct::get(getFatbinWrapperTy(M), FatbinWrapper);
311
312 auto *FatbinDesc =
313 new GlobalVariable(M, getFatbinWrapperTy(M),
314 /*isConstant*/ true, GlobalValue::InternalLinkage,
315 FatbinInitializer, ".fatbin_wrapper" + Suffix);
316 FatbinDesc->setSection(FatbinWrapperSection);
317 FatbinDesc->setAlignment(Align(8));
318 FatbinDesc->setNoSanitizeMetadata();
319
320 return FatbinDesc;
321}
322
323/// Create the register globals function. We will iterate all of the offloading
324/// entries stored at the begin / end symbols and register them according to
325/// their type. This creates the following function in IR:
326///
327/// extern struct __tgt_offload_entry __start_cuda_offloading_entries;
328/// extern struct __tgt_offload_entry __stop_cuda_offloading_entries;
329///
330/// extern void __cudaRegisterFunction(void **, void *, void *, void *, int,
331/// void *, void *, void *, void *, int *);
332/// extern void __cudaRegisterVar(void **, void *, void *, void *, int32_t,
333/// int64_t, int32_t, int32_t);
334///
335/// void __cudaRegisterTest(void **fatbinHandle) {
336/// for (struct __tgt_offload_entry *entry = &__start_cuda_offloading_entries;
337/// entry != &__stop_cuda_offloading_entries; ++entry) {
338/// if (entry->Kind != OFK_CUDA)
339/// continue
340///
341/// if (!entry->Size)
342/// __cudaRegisterFunction(fatbinHandle, entry->addr, entry->name,
343/// entry->name, -1, 0, 0, 0, 0, 0);
344/// else
345/// __cudaRegisterVar(fatbinHandle, entry->addr, entry->name, entry->name,
346/// 0, entry->size, 0, 0);
347/// }
348/// }
349Function *createRegisterGlobalsFunction(Module &M, bool IsHIP,
350 EntryArrayTy EntryArray,
351 StringRef Suffix,
352 bool EmitSurfacesAndTextures) {
353 LLVMContext &C = M.getContext();
354 auto [EntriesB, EntriesE] = EntryArray;
355
356 // Get the __cudaRegisterFunction function declaration.
357 PointerType *Int8PtrTy = PointerType::get(C, 0);
358 PointerType *Int8PtrPtrTy = PointerType::get(C, 0);
359 PointerType *Int32PtrTy = PointerType::get(C, 0);
360 auto *RegFuncTy = FunctionType::get(
362 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
363 Int8PtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Int32PtrTy},
364 /*isVarArg*/ false);
365 FunctionCallee RegFunc = M.getOrInsertFunction(
366 IsHIP ? "__hipRegisterFunction" : "__cudaRegisterFunction", RegFuncTy);
367
368 // Get the __cudaRegisterVar function declaration.
369 auto *RegVarTy = FunctionType::get(
371 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
373 /*isVarArg*/ false);
374 FunctionCallee RegVar = M.getOrInsertFunction(
375 IsHIP ? "__hipRegisterVar" : "__cudaRegisterVar", RegVarTy);
376
377 // Get the __cudaRegisterSurface function declaration.
378 FunctionType *RegManagedVarTy =
380 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy,
382 /*isVarArg=*/false);
383 FunctionCallee RegManagedVar = M.getOrInsertFunction(
384 IsHIP ? "__hipRegisterManagedVar" : "__cudaRegisterManagedVar",
385 RegManagedVarTy);
386
387 // Get the __cudaRegisterSurface function declaration.
388 FunctionType *RegSurfaceTy =
390 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy,
392 /*isVarArg=*/false);
393 FunctionCallee RegSurface = M.getOrInsertFunction(
394 IsHIP ? "__hipRegisterSurface" : "__cudaRegisterSurface", RegSurfaceTy);
395
396 // Get the __cudaRegisterTexture function declaration.
397 FunctionType *RegTextureTy = FunctionType::get(
399 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
401 /*isVarArg=*/false);
402 FunctionCallee RegTexture = M.getOrInsertFunction(
403 IsHIP ? "__hipRegisterTexture" : "__cudaRegisterTexture", RegTextureTy);
404
405 auto *RegGlobalsTy = FunctionType::get(Type::getVoidTy(C), Int8PtrPtrTy,
406 /*isVarArg*/ false);
407 auto *RegGlobalsFn =
409 IsHIP ? ".hip.globals_reg" : ".cuda.globals_reg", &M);
410 RegGlobalsFn->setSection(getStartupSection(M.getTargetTriple()));
411
412 // Create the loop to register all the entries.
413 IRBuilder<> Builder(BasicBlock::Create(C, "entry", RegGlobalsFn));
414 auto *EntryBB = BasicBlock::Create(C, "while.entry", RegGlobalsFn);
415 auto *IfKindBB = BasicBlock::Create(C, "if.kind", RegGlobalsFn);
416 auto *IfThenBB = BasicBlock::Create(C, "if.then", RegGlobalsFn);
417 auto *IfElseBB = BasicBlock::Create(C, "if.else", RegGlobalsFn);
418 auto *SwGlobalBB = BasicBlock::Create(C, "sw.global", RegGlobalsFn);
419 auto *SwManagedBB = BasicBlock::Create(C, "sw.managed", RegGlobalsFn);
420 auto *SwSurfaceBB = BasicBlock::Create(C, "sw.surface", RegGlobalsFn);
421 auto *SwTextureBB = BasicBlock::Create(C, "sw.texture", RegGlobalsFn);
422 auto *IfEndBB = BasicBlock::Create(C, "if.end", RegGlobalsFn);
423 auto *ExitBB = BasicBlock::Create(C, "while.end", RegGlobalsFn);
424
425 auto *EntryCmp = Builder.CreateICmpNE(EntriesB, EntriesE);
426 Builder.CreateCondBr(EntryCmp, EntryBB, ExitBB);
427 Builder.SetInsertPoint(EntryBB);
428 auto *Entry = Builder.CreatePHI(PointerType::getUnqual(C), 2, "entry");
429 auto *AddrPtr =
430 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
431 {ConstantInt::get(Type::getInt32Ty(C), 0),
432 ConstantInt::get(Type::getInt32Ty(C), 4)});
433 auto *Addr = Builder.CreateLoad(Int8PtrTy, AddrPtr, "addr");
434 auto *AuxAddrPtr =
435 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
436 {ConstantInt::get(Type::getInt32Ty(C), 0),
437 ConstantInt::get(Type::getInt32Ty(C), 8)});
438 auto *AuxAddr = Builder.CreateLoad(Int8PtrTy, AuxAddrPtr, "aux_addr");
439 auto *KindPtr =
440 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
441 {ConstantInt::get(Type::getInt32Ty(C), 0),
442 ConstantInt::get(Type::getInt32Ty(C), 2)});
443 auto *Kind = Builder.CreateLoad(Type::getInt16Ty(C), KindPtr, "kind");
444 auto *NamePtr =
445 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
446 {ConstantInt::get(Type::getInt32Ty(C), 0),
447 ConstantInt::get(Type::getInt32Ty(C), 5)});
448 auto *Name = Builder.CreateLoad(Int8PtrTy, NamePtr, "name");
449 auto *SizePtr =
450 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
451 {ConstantInt::get(Type::getInt32Ty(C), 0),
452 ConstantInt::get(Type::getInt32Ty(C), 6)});
453 auto *Size = Builder.CreateLoad(Type::getInt64Ty(C), SizePtr, "size");
454 auto *FlagsPtr =
455 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
456 {ConstantInt::get(Type::getInt32Ty(C), 0),
457 ConstantInt::get(Type::getInt32Ty(C), 3)});
458 auto *Flags = Builder.CreateLoad(Type::getInt32Ty(C), FlagsPtr, "flags");
459 auto *DataPtr =
460 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
461 {ConstantInt::get(Type::getInt32Ty(C), 0),
462 ConstantInt::get(Type::getInt32Ty(C), 7)});
463 auto *Data = Builder.CreateTrunc(
464 Builder.CreateLoad(Type::getInt64Ty(C), DataPtr, "data"),
466 auto *Type = Builder.CreateAnd(
467 Flags, ConstantInt::get(Type::getInt32Ty(C), 0x7), "type");
468
469 // Extract the flags stored in the bit-field and convert them to C booleans.
470 auto *ExternBit = Builder.CreateAnd(
471 Flags, ConstantInt::get(Type::getInt32Ty(C),
473 auto *Extern = Builder.CreateLShr(
474 ExternBit, ConstantInt::get(Type::getInt32Ty(C), 3), "extern");
475 auto *ConstantBit = Builder.CreateAnd(
476 Flags, ConstantInt::get(Type::getInt32Ty(C),
478 auto *Const = Builder.CreateLShr(
479 ConstantBit, ConstantInt::get(Type::getInt32Ty(C), 4), "constant");
480 auto *NormalizedBit = Builder.CreateAnd(
481 Flags, ConstantInt::get(Type::getInt32Ty(C),
483 auto *Normalized = Builder.CreateLShr(
484 NormalizedBit, ConstantInt::get(Type::getInt32Ty(C), 5), "normalized");
485 auto *KindCond = Builder.CreateICmpEQ(
486 Kind, ConstantInt::get(Type::getInt16Ty(C),
489 Builder.CreateCondBr(KindCond, IfKindBB, IfEndBB);
490 Builder.SetInsertPoint(IfKindBB);
491 auto *FnCond = Builder.CreateICmpEQ(
493 Builder.CreateCondBr(FnCond, IfThenBB, IfElseBB);
494
495 // Create kernel registration code.
496 Builder.SetInsertPoint(IfThenBB);
497 Builder.CreateCall(
498 RegFunc,
499 {RegGlobalsFn->arg_begin(), Addr, Name, Name,
503 ConstantPointerNull::get(Int32PtrTy)});
504 Builder.CreateBr(IfEndBB);
505 Builder.SetInsertPoint(IfElseBB);
506
507 auto *Switch = Builder.CreateSwitch(Type, IfEndBB);
508 // Create global variable registration code.
509 Builder.SetInsertPoint(SwGlobalBB);
510 Builder.CreateCall(RegVar,
511 {RegGlobalsFn->arg_begin(), Addr, Name, Name, Extern, Size,
512 Const, ConstantInt::get(Type::getInt32Ty(C), 0)});
513 Builder.CreateBr(IfEndBB);
514 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalEntry),
515 SwGlobalBB);
516
517 // Create managed variable registration code.
518 Builder.SetInsertPoint(SwManagedBB);
519 Builder.CreateCall(RegManagedVar, {RegGlobalsFn->arg_begin(), AuxAddr, Addr,
520 Name, Size, Data});
521 Builder.CreateBr(IfEndBB);
522 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalManagedEntry),
523 SwManagedBB);
524 // Create surface variable registration code.
525 Builder.SetInsertPoint(SwSurfaceBB);
526 if (EmitSurfacesAndTextures)
527 Builder.CreateCall(RegSurface, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
528 Data, Extern});
529 Builder.CreateBr(IfEndBB);
530 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalSurfaceEntry),
531 SwSurfaceBB);
532
533 // Create texture variable registration code.
534 Builder.SetInsertPoint(SwTextureBB);
535 if (EmitSurfacesAndTextures)
536 Builder.CreateCall(RegTexture, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
537 Data, Normalized, Extern});
538 Builder.CreateBr(IfEndBB);
539 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalTextureEntry),
540 SwTextureBB);
541
542 Builder.SetInsertPoint(IfEndBB);
543 auto *NewEntry = Builder.CreateInBoundsGEP(
544 offloading::getEntryTy(M), Entry, ConstantInt::get(getSizeTTy(M), 1));
545 auto *Cmp = Builder.CreateICmpEQ(NewEntry, EntriesE);
546 Entry->addIncoming(EntriesB, &RegGlobalsFn->getEntryBlock());
547 Entry->addIncoming(NewEntry, IfEndBB);
548 Builder.CreateCondBr(Cmp, ExitBB, EntryBB);
549 Builder.SetInsertPoint(ExitBB);
550 Builder.CreateRetVoid();
551
552 return RegGlobalsFn;
553}
554
555// Create the constructor and destructor to register the fatbinary with the CUDA
556// runtime.
557void createRegisterFatbinFunction(Module &M, GlobalVariable *FatbinDesc,
558 bool IsHIP, EntryArrayTy EntryArray,
559 StringRef Suffix,
560 bool EmitSurfacesAndTextures) {
561 LLVMContext &C = M.getContext();
562 auto *CtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
563 auto *CtorFunc = Function::Create(
565 (IsHIP ? ".hip.fatbin_reg" : ".cuda.fatbin_reg") + Suffix, &M);
566 CtorFunc->setSection(getStartupSection(M.getTargetTriple()));
567
568 auto *DtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
569 auto *DtorFunc = Function::Create(
571 (IsHIP ? ".hip.fatbin_unreg" : ".cuda.fatbin_unreg") + Suffix, &M);
572 DtorFunc->setSection(getStartupSection(M.getTargetTriple()));
573
574 auto *PtrTy = PointerType::getUnqual(C);
575
576 // Get the __cudaRegisterFatBinary function declaration.
577 auto *RegFatTy = FunctionType::get(PtrTy, PtrTy, /*isVarArg=*/false);
578 FunctionCallee RegFatbin = M.getOrInsertFunction(
579 IsHIP ? "__hipRegisterFatBinary" : "__cudaRegisterFatBinary", RegFatTy);
580 // Get the __cudaRegisterFatBinaryEnd function declaration.
581 auto *RegFatEndTy =
582 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
583 FunctionCallee RegFatbinEnd =
584 M.getOrInsertFunction("__cudaRegisterFatBinaryEnd", RegFatEndTy);
585 // Get the __cudaUnregisterFatBinary function declaration.
586 auto *UnregFatTy =
587 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
588 FunctionCallee UnregFatbin = M.getOrInsertFunction(
589 IsHIP ? "__hipUnregisterFatBinary" : "__cudaUnregisterFatBinary",
590 UnregFatTy);
591
592 auto *AtExitTy =
593 FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
594 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
595
596 auto *BinaryHandleGlobal = new llvm::GlobalVariable(
597 M, PtrTy, false, llvm::GlobalValue::InternalLinkage,
599 (IsHIP ? ".hip.binary_handle" : ".cuda.binary_handle") + Suffix);
600
601 // Create the constructor to register this image with the runtime.
602 IRBuilder<> CtorBuilder(BasicBlock::Create(C, "entry", CtorFunc));
603 CallInst *Handle = CtorBuilder.CreateCall(
604 RegFatbin,
606 CtorBuilder.CreateAlignedStore(
607 Handle, BinaryHandleGlobal,
608 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
609 CtorBuilder.CreateCall(createRegisterGlobalsFunction(M, IsHIP, EntryArray,
610 Suffix,
611 EmitSurfacesAndTextures),
612 Handle);
613 if (!IsHIP)
614 CtorBuilder.CreateCall(RegFatbinEnd, Handle);
615 CtorBuilder.CreateCall(AtExit, DtorFunc);
616 CtorBuilder.CreateRetVoid();
617
618 // Create the destructor to unregister the image with the runtime. We cannot
619 // use a standard global destructor after CUDA 9.2 so this must be called by
620 // `atexit()` instead.
621 IRBuilder<> DtorBuilder(BasicBlock::Create(C, "entry", DtorFunc));
622 LoadInst *BinaryHandle = DtorBuilder.CreateAlignedLoad(
623 PtrTy, BinaryHandleGlobal,
624 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
625 DtorBuilder.CreateCall(UnregFatbin, BinaryHandle);
626 DtorBuilder.CreateRetVoid();
627
628 // Add this function to constructors.
629 appendToGlobalCtors(M, CtorFunc, /*Priority=*/101);
630}
631
632/// SYCLWrapper helper class that creates all LLVM IRs wrapping given images.
633class SYCLWrapper {
634public:
635 SYCLWrapper(Module &M, const SYCLJITOptions &Options, bool IsFinalizedImage)
636 : M(M), C(M.getContext()), Options(Options),
637 IsFinalizedImage(IsFinalizedImage) {}
638
639 /// Embeds \p Buffer (a raw OffloadBinary) as a global constant and returns
640 /// a pair of (Start, Size), where Start points to the beginning of the
641 /// embedded data and Size is its length in bytes.
642 std::pair<Constant *, Constant *> embedBinary(ArrayRef<char> Buffer) {
643 Constant *Arr = ConstantDataArray::get(C, Buffer);
644 GlobalVariable *BinaryGV = new GlobalVariable(
645 M, Arr->getType(), /*isConstant=*/true, GlobalValue::InternalLinkage,
646 Arr, ".sycl_offloading.binary");
647 BinaryGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
648 // The linker wrapper scans ".llvm.offloading" for device code to link, so
649 // an already finalized image must go elsewhere to avoid being linked again.
650 BinaryGV->setSection(IsFinalizedImage ? ".sycl_fatbin"
651 : ".llvm.offloading");
652
653 IntegerType *Int64Ty = Type::getInt64Ty(C);
654 Constant *Zero = ConstantInt::get(Int64Ty, 0);
655 Constant *Size = ConstantInt::get(Int64Ty, Buffer.size());
657 BinaryGV->getValueType(), BinaryGV, ArrayRef<Constant *>{Zero, Zero});
658 return {Start, Size};
659 }
660
661 Function *createRegisterFatbinFunction(Constant *Start, Constant *Size) {
662 FunctionType *FuncTy =
663 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
665 Twine("sycl") + ".descriptor_reg", &M);
666 Func->setSection(getStartupSection(M.getTargetTriple()));
667
668 PointerType *PtrTy = PointerType::getUnqual(C);
669 IntegerType *Int64Ty = Type::getInt64Ty(C);
670 FunctionType *RegFuncTy =
671 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
672 /*isVarArg=*/false);
673 FunctionCallee RegFuncC =
674 M.getOrInsertFunction("__sycl_register_lib", RegFuncTy);
675
676 FunctionType *AtExitTy =
677 FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
678 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
679
680 Function *UnregFunc = createUnregisterFunction(Start, Size);
681
682 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
683 Builder.CreateCall(RegFuncC, {Start, Size});
684
685 // Unregister with 'atexit'. The handler is installed after
686 // __sycl_register_lib has brought the runtime's own exit-time cleanup into
687 // the atexit chain, so it is ordered ahead of that cleanup.
688 Builder.CreateCall(AtExit, UnregFunc);
689 Builder.CreateRetVoid();
690
691 return Func;
692 }
693
694private:
695 Function *createUnregisterFunction(Constant *Start, Constant *Size) {
696 FunctionType *FuncTy =
697 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
699 "sycl.descriptor_unreg", &M);
700 Func->setSection(getStartupSection(M.getTargetTriple()));
701
702 PointerType *PtrTy = PointerType::getUnqual(C);
703 IntegerType *Int64Ty = Type::getInt64Ty(C);
704 FunctionType *UnRegFuncTy =
705 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
706 /*isVarArg=*/false);
707 FunctionCallee UnRegFuncC =
708 M.getOrInsertFunction("__sycl_unregister_lib", UnRegFuncTy);
709
710 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
711 Builder.CreateCall(UnRegFuncC, {Start, Size});
712 Builder.CreateRetVoid();
713
714 return Func;
715 }
716
717 Module &M;
718 LLVMContext &C;
719 SYCLJITOptions Options;
720 bool IsFinalizedImage;
721}; // end of SYCLWrapper
722
723} // namespace
724
726 EntryArrayTy EntryArray,
727 llvm::StringRef Suffix, bool Relocatable) {
729 createBinDesc(M, Images, EntryArray, Suffix, Relocatable);
730 if (!Desc)
732 "No binary descriptors created.");
733 createRegisterFunction(M, Desc, Suffix);
734 return Error::success();
735}
736
738 EntryArrayTy EntryArray,
739 llvm::StringRef Suffix,
740 bool EmitSurfacesAndTextures) {
741 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/false, Suffix);
742 if (!Desc)
744 "No fatbin section created.");
745
746 createRegisterFatbinFunction(M, Desc, /*IsHip=*/false, EntryArray, Suffix,
747 EmitSurfacesAndTextures);
748 return Error::success();
749}
750
752 EntryArrayTy EntryArray, llvm::StringRef Suffix,
753 bool EmitSurfacesAndTextures) {
754 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/true, Suffix);
755 if (!Desc)
757 "No fatbin section created.");
758
759 createRegisterFatbinFunction(M, Desc, /*IsHip=*/true, EntryArray, Suffix,
760 EmitSurfacesAndTextures);
761 return Error::success();
762}
763
766 bool IsFinalizedImage,
767 Function **RegistrationFunc) {
768 SYCLWrapper W(M, Options, IsFinalizedImage);
769 auto [Start, Size] = W.embedBinary(Buffer);
770 Function *RegisterFunc = W.createRegisterFatbinFunction(Start, Size);
771 if (RegistrationFunc) {
772 *RegistrationFunc = RegisterFunc;
773 return Error::success();
774 }
775
776 appendToGlobalCtors(M, RegisterFunc, /*Priority=*/101);
777 return Error::success();
778}
unsigned uint64_t
static IntegerType * getSizeTTy(IRBuilderBase &B, const TargetLibraryInfo *TLI)
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Module.h This file contains the declarations for the Module class.
static LVOptions Options
Definition LVOptions.cpp:25
#define T
Func getContext().diagnose(DiagnosticInfoUnsupported(Func
This file defines the SmallVector class.
@ ConstantBit
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
This class represents a function call, abstracting a target machine's calling convention.
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static Constant * get(LLVMContext &Context, ArrayRef< ElementTy > Elts)
get() constructor - Return a constant with array type with an element count and element type matching...
Definition Constants.h:878
static LLVM_ABI Constant * getPointerBitCastOrAddrSpaceCast(Constant *C, Type *Ty)
Create a BitCast or AddrSpaceCast for a pointer type depending on the address space.
static Constant * getGetElementPtr(Type *Ty, Constant *C, ArrayRef< Constant * > IdxList, GEPNoWrapFlags NW=GEPNoWrapFlags::none(), std::optional< ConstantRange > InRange=std::nullopt, Type *OnlyIfReducedTy=nullptr)
Getelementptr form.
Definition Constants.h:1470
static LLVM_ABI ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:43
static LLVM_ABI Constant * getAllOnesValue(Type *Ty)
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:168
LLVM_ABI void setSection(StringRef S)
Change the section for this global.
Definition Globals.cpp:348
void setUnnamedAddr(UnnamedAddr Val)
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
Type * getValueType() const
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2903
Class to represent integer types.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
An instruction for reading from memory.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
static LLVM_ABI PointerType * get(LLVMContext &C, unsigned AddressSpace)
This constructs an opaque pointer to an object in a numbered address space.
Definition Type.cpp:911
void reserve(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Class to represent struct types.
static LLVM_ABI StructType * getTypeByName(LLVMContext &C, StringRef Name)
Return the type with the specified name, or null if there is none by that name.
Definition Type.cpp:802
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:683
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isMacOSX() const
Is this a Mac OS X triple.
Definition Triple.h:680
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
Definition Type.cpp:310
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:309
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:282
static LLVM_ABI IntegerType * getInt16Ty(LLVMContext &C)
Definition Type.cpp:308
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
static uint64_t getAlignment()
@ Switch
The "resume-switch" lowering, where there are separate resume and destroy functions that are shared b...
Definition CoroShape.h:32
LLVM_ABI StructType * getEntryTy(Module &M)
Returns the type of the offloading entry we use to store kernels and globals that will be registered ...
Definition Utility.cpp:27
@ OffloadGlobalSurfaceEntry
Mark the entry as a surface variable.
Definition Utility.h:61
@ OffloadGlobalTextureEntry
Mark the entry as a texture variable.
Definition Utility.h:63
@ OffloadGlobalNormalized
Mark the entry as being a normalized surface.
Definition Utility.h:69
@ OffloadGlobalEntry
Mark the entry as a global entry.
Definition Utility.h:57
@ OffloadGlobalManagedEntry
Mark the entry as a managed global variable.
Definition Utility.h:59
@ OffloadGlobalExtern
Mark the entry as being extern.
Definition Utility.h:65
@ OffloadGlobalConstant
Mark the entry as being constant.
Definition Utility.h:67
LLVM_ABI llvm::Error wrapSYCLBinaries(llvm::Module &M, llvm::ArrayRef< char > Buffer, SYCLJITOptions Options=SYCLJITOptions(), bool IsFinalizedImage=false, llvm::Function **RegistrationFunc=nullptr)
Wraps OffloadBinaries in the given Buffers into the module M as global symbols and registers the imag...
LLVM_ABI llvm::Error wrapOpenMPBinaries(llvm::Module &M, llvm::ArrayRef< llvm::ArrayRef< char > > Images, EntryArrayTy EntryArray, llvm::StringRef Suffix="", bool Relocatable=false)
Wraps the input device images into the module M as global symbols and registers the images with the O...
std::pair< Constant *, Constant * > EntryArrayTy
LLVM_ABI llvm::Error wrapHIPBinary(llvm::Module &M, llvm::ArrayRef< char > Images, EntryArrayTy EntryArray, llvm::StringRef Suffix="", bool EmitSurfacesAndTextures=true)
Wraps the input bundled image into the module M as global symbols and registers the images with the H...
LLVM_ABI llvm::Error wrapCudaBinary(llvm::Module &M, llvm::ArrayRef< char > Images, EntryArrayTy EntryArray, llvm::StringRef Suffix="", bool EmitSurfacesAndTextures=true)
Wraps the input fatbinary image into the module M as global symbols and registers the images with the...
NodeAddr< FuncNode * > Func
Definition RDFGraph.h:393
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI file_magic identify_magic(StringRef magic)
Identify the type of a binary file based on how magical it is.
Definition Magic.cpp:33
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
Op::Description Desc
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
ArrayRef(const T &OneElt) -> ArrayRef< T >
LLVM_ABI void appendToGlobalCtors(Module &M, Function *F, int Priority, Constant *Data=nullptr)
Append F to the list of global ctors of module M with the given Priority.
@ Extern
Replace returns with jump to thunk, don't emit thunk.
Definition CodeGen.h:230
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
@ offload_binary
LLVM offload object file.
Definition Magic.h:58