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 // Create initializer for the images array.
136 SmallVector<Constant *, 4u> ImagesInits;
137 ImagesInits.reserve(Bufs.size());
138 for (ArrayRef<char> Buf : Bufs) {
139 // We embed the full offloading entry so the binary utilities can parse it.
140 auto *Data = ConstantDataArray::get(C, Buf);
141 auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant=*/true,
143 ".llvm_offloading.device_image" + Suffix);
145 Image->setSection(Relocatable ? ".llvm.offloading.relocatable"
146 : ".llvm.offloading");
148
149 StringRef Binary(Buf.data(), Buf.size());
150
151 uint64_t BeginOffset = 0;
152 uint64_t EndOffset = Binary.size();
153
154 // Optionally use an offload binary for its offload dumping support.
155 // The device image struct contains the pointer to the beginning and end of
156 // the image stored inside of the offload binary. There should only be one
157 // of these for each buffer so we parse it out manually.
159 const auto *Header =
160 reinterpret_cast<const object::OffloadBinary::Header *>(
161 Binary.bytes_begin());
162 const auto *Entry =
163 reinterpret_cast<const object::OffloadBinary::Entry *>(
164 Binary.bytes_begin() + Header->EntriesOffset);
165 BeginOffset = Entry->ImageOffset;
166 EndOffset = Entry->ImageOffset + Entry->ImageSize;
167 }
168
169 auto *Begin = ConstantInt::get(getSizeTTy(M), BeginOffset);
170 auto *Size = ConstantInt::get(getSizeTTy(M), EndOffset);
171 auto *ImageB = ConstantExpr::getPtrAdd(Image, Begin);
172 auto *ImageE = ConstantExpr::getPtrAdd(Image, Size);
173
174 ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(M), ImageB,
175 ImageE, EntriesB, EntriesE));
176 }
177
178 // Then create images array.
179 auto *ImagesData = ConstantArray::get(
180 ArrayType::get(getDeviceImageTy(M), ImagesInits.size()), ImagesInits);
181
182 auto *Images =
183 new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true,
185 ".llvm_offloading.device_images" + Suffix);
186 Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
187
188 // And finally create the binary descriptor object.
189 auto *DescInit = ConstantStruct::get(
190 getBinDescTy(M),
191 ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), Images,
192 EntriesB, EntriesE);
193
194 return new GlobalVariable(M, DescInit->getType(), /*isConstant=*/true,
196 ".llvm_offloading.descriptor" + Suffix);
197}
198
199Function *createUnregisterFunction(Module &M, GlobalVariable *BinDesc,
200 StringRef Suffix, StringRef UnregFuncName) {
201 LLVMContext &C = M.getContext();
202 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
203 auto *Func =
205 ".llvm_offloading.descriptor_unreg" + Suffix, &M);
206 Func->setSection(getStartupSection(M.getTargetTriple()));
207
208 // Get the unregister function declaration.
209 auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
210 /*isVarArg*/ false);
211 FunctionCallee UnRegFuncC = M.getOrInsertFunction(UnregFuncName, UnRegFuncTy);
212
213 // Construct function body
214 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
215 Builder.CreateCall(UnRegFuncC, BinDesc);
216 Builder.CreateRetVoid();
217
218 return Func;
219}
220
221void createRegisterFunction(Module &M, GlobalVariable *BinDesc,
222 StringRef Suffix, StringRef RegFuncName,
223 StringRef UnregFuncName) {
224 LLVMContext &C = M.getContext();
225 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
227 ".llvm_offloading.descriptor_reg" + Suffix, &M);
228 Func->setSection(getStartupSection(M.getTargetTriple()));
229
230 // Get the register function declaration.
231 auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
232 /*isVarArg*/ false);
233 FunctionCallee RegFuncC = M.getOrInsertFunction(RegFuncName, RegFuncTy);
234
235 auto *AtExitTy = FunctionType::get(
236 Type::getInt32Ty(C), PointerType::getUnqual(C), /*isVarArg=*/false);
237 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
238
239 Function *UnregFunc =
240 createUnregisterFunction(M, BinDesc, Suffix, UnregFuncName);
241
242 // Construct function body
243 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
244
245 Builder.CreateCall(RegFuncC, BinDesc);
246
247 // Register the destructors with 'atexit'. This is expected by the CUDA
248 // runtime and ensures that we clean up before dynamic objects are destroyed.
249 // This needs to be done after plugin initialization to ensure that it is
250 // called before the plugin runtime is destroyed.
251 Builder.CreateCall(AtExit, UnregFunc);
252 Builder.CreateRetVoid();
253
254 // Add this function to constructors.
255 appendToGlobalCtors(M, Func, /*Priority=*/101);
256}
257
258// struct fatbin_wrapper {
259// int32_t magic;
260// int32_t version;
261// void *image;
262// void *reserved;
263//};
264StructType *getFatbinWrapperTy(Module &M) {
265 LLVMContext &C = M.getContext();
266 StructType *FatbinTy = StructType::getTypeByName(C, "fatbin_wrapper");
267 if (!FatbinTy)
268 FatbinTy = StructType::create(
269 "fatbin_wrapper", Type::getInt32Ty(C), Type::getInt32Ty(C),
271 return FatbinTy;
272}
273
274/// Embed the image \p Image into the module \p M so it can be found by the
275/// runtime.
276GlobalVariable *createFatbinDesc(Module &M, ArrayRef<char> Image, bool IsHIP,
277 StringRef Suffix) {
278 LLVMContext &C = M.getContext();
279 llvm::Type *Int8PtrTy = PointerType::getUnqual(C);
280 const llvm::Triple &Triple = M.getTargetTriple();
281
282 // Create the global string containing the fatbinary.
283 StringRef FatbinConstantSection =
284 IsHIP ? (Triple.isMacOSX() ? "__HIP,__hip_fatbin" : ".hip_fatbin")
285 : (Triple.isMacOSX() ? "__NV_CUDA,__nv_fatbin" : ".nv_fatbin");
286 auto *Data = ConstantDataArray::get(C, Image);
287 auto *Fatbin = new GlobalVariable(M, Data->getType(), /*isConstant*/ true,
289 ".fatbin_image" + Suffix);
290 Fatbin->setSection(FatbinConstantSection);
291
292 // Create the fatbinary wrapper
293 StringRef FatbinWrapperSection =
294 IsHIP ? (Triple.isMacOSX() ? "__HIP,__fatbin" : ".hipFatBinSegment")
295 : (Triple.isMacOSX() ? "__NV_CUDA,__fatbin" : ".nvFatBinSegment");
296 Constant *FatbinWrapper[] = {
297 ConstantInt::get(Type::getInt32Ty(C), IsHIP ? HIPFatMagic : CudaFatMagic),
298 ConstantInt::get(Type::getInt32Ty(C), 1),
301
302 Constant *FatbinInitializer =
303 ConstantStruct::get(getFatbinWrapperTy(M), FatbinWrapper);
304
305 auto *FatbinDesc =
306 new GlobalVariable(M, getFatbinWrapperTy(M),
307 /*isConstant*/ true, GlobalValue::InternalLinkage,
308 FatbinInitializer, ".fatbin_wrapper" + Suffix);
309 FatbinDesc->setSection(FatbinWrapperSection);
310 FatbinDesc->setAlignment(Align(8));
311 FatbinDesc->setNoSanitizeMetadata();
312
313 return FatbinDesc;
314}
315
316/// Create the register globals function. We will iterate all of the offloading
317/// entries stored at the begin / end symbols and register them according to
318/// their type. This creates the following function in IR:
319///
320/// extern struct __tgt_offload_entry __start_cuda_offloading_entries;
321/// extern struct __tgt_offload_entry __stop_cuda_offloading_entries;
322///
323/// extern void __cudaRegisterFunction(void **, void *, void *, void *, int,
324/// void *, void *, void *, void *, int *);
325/// extern void __cudaRegisterVar(void **, void *, void *, void *, int32_t,
326/// int64_t, int32_t, int32_t);
327///
328/// void __cudaRegisterTest(void **fatbinHandle) {
329/// for (struct __tgt_offload_entry *entry = &__start_cuda_offloading_entries;
330/// entry != &__stop_cuda_offloading_entries; ++entry) {
331/// if (entry->Kind != OFK_CUDA)
332/// continue
333///
334/// if (!entry->Size)
335/// __cudaRegisterFunction(fatbinHandle, entry->addr, entry->name,
336/// entry->name, -1, 0, 0, 0, 0, 0);
337/// else
338/// __cudaRegisterVar(fatbinHandle, entry->addr, entry->name, entry->name,
339/// 0, entry->size, 0, 0);
340/// }
341/// }
342Function *createRegisterGlobalsFunction(Module &M, bool IsHIP,
343 EntryArrayTy EntryArray,
344 StringRef Suffix,
345 bool EmitSurfacesAndTextures) {
346 LLVMContext &C = M.getContext();
347 auto [EntriesB, EntriesE] = EntryArray;
348
349 // Get the __cudaRegisterFunction function declaration.
350 PointerType *Int8PtrTy = PointerType::get(C, 0);
351 PointerType *Int8PtrPtrTy = PointerType::get(C, 0);
352 PointerType *Int32PtrTy = PointerType::get(C, 0);
353 auto *RegFuncTy = FunctionType::get(
355 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
356 Int8PtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Int32PtrTy},
357 /*isVarArg*/ false);
358 FunctionCallee RegFunc = M.getOrInsertFunction(
359 IsHIP ? "__hipRegisterFunction" : "__cudaRegisterFunction", RegFuncTy);
360
361 // Get the __cudaRegisterVar function declaration.
362 auto *RegVarTy = FunctionType::get(
364 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
366 /*isVarArg*/ false);
367 FunctionCallee RegVar = M.getOrInsertFunction(
368 IsHIP ? "__hipRegisterVar" : "__cudaRegisterVar", RegVarTy);
369
370 // Get the __cudaRegisterSurface function declaration.
371 FunctionType *RegManagedVarTy =
373 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy,
375 /*isVarArg=*/false);
376 FunctionCallee RegManagedVar = M.getOrInsertFunction(
377 IsHIP ? "__hipRegisterManagedVar" : "__cudaRegisterManagedVar",
378 RegManagedVarTy);
379
380 // Get the __cudaRegisterSurface function declaration.
381 FunctionType *RegSurfaceTy =
383 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy,
385 /*isVarArg=*/false);
386 FunctionCallee RegSurface = M.getOrInsertFunction(
387 IsHIP ? "__hipRegisterSurface" : "__cudaRegisterSurface", RegSurfaceTy);
388
389 // Get the __cudaRegisterTexture function declaration.
390 FunctionType *RegTextureTy = FunctionType::get(
392 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
394 /*isVarArg=*/false);
395 FunctionCallee RegTexture = M.getOrInsertFunction(
396 IsHIP ? "__hipRegisterTexture" : "__cudaRegisterTexture", RegTextureTy);
397
398 auto *RegGlobalsTy = FunctionType::get(Type::getVoidTy(C), Int8PtrPtrTy,
399 /*isVarArg*/ false);
400 auto *RegGlobalsFn =
402 IsHIP ? ".hip.globals_reg" : ".cuda.globals_reg", &M);
403 RegGlobalsFn->setSection(getStartupSection(M.getTargetTriple()));
404
405 // Create the loop to register all the entries.
406 IRBuilder<> Builder(BasicBlock::Create(C, "entry", RegGlobalsFn));
407 Builder.disableGEPCanonicalization();
408 auto *EntryBB = BasicBlock::Create(C, "while.entry", RegGlobalsFn);
409 auto *IfKindBB = BasicBlock::Create(C, "if.kind", RegGlobalsFn);
410 auto *IfThenBB = BasicBlock::Create(C, "if.then", RegGlobalsFn);
411 auto *IfElseBB = BasicBlock::Create(C, "if.else", RegGlobalsFn);
412 auto *SwGlobalBB = BasicBlock::Create(C, "sw.global", RegGlobalsFn);
413 auto *SwManagedBB = BasicBlock::Create(C, "sw.managed", RegGlobalsFn);
414 auto *SwSurfaceBB = BasicBlock::Create(C, "sw.surface", RegGlobalsFn);
415 auto *SwTextureBB = BasicBlock::Create(C, "sw.texture", RegGlobalsFn);
416 auto *IfEndBB = BasicBlock::Create(C, "if.end", RegGlobalsFn);
417 auto *ExitBB = BasicBlock::Create(C, "while.end", RegGlobalsFn);
418
419 auto *EntryCmp = Builder.CreateICmpNE(EntriesB, EntriesE);
420 Builder.CreateCondBr(EntryCmp, EntryBB, ExitBB);
421 Builder.SetInsertPoint(EntryBB);
422 auto *Entry = Builder.CreatePHI(PointerType::getUnqual(C), 2, "entry");
423 auto *AddrPtr =
424 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
425 {ConstantInt::get(Type::getInt32Ty(C), 0),
426 ConstantInt::get(Type::getInt32Ty(C), 4)});
427 auto *Addr = Builder.CreateLoad(Int8PtrTy, AddrPtr, "addr");
428 auto *AuxAddrPtr =
429 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
430 {ConstantInt::get(Type::getInt32Ty(C), 0),
431 ConstantInt::get(Type::getInt32Ty(C), 8)});
432 auto *AuxAddr = Builder.CreateLoad(Int8PtrTy, AuxAddrPtr, "aux_addr");
433 auto *KindPtr =
434 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
435 {ConstantInt::get(Type::getInt32Ty(C), 0),
436 ConstantInt::get(Type::getInt32Ty(C), 2)});
437 auto *Kind = Builder.CreateLoad(Type::getInt16Ty(C), KindPtr, "kind");
438 auto *NamePtr =
439 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
440 {ConstantInt::get(Type::getInt32Ty(C), 0),
441 ConstantInt::get(Type::getInt32Ty(C), 5)});
442 auto *Name = Builder.CreateLoad(Int8PtrTy, NamePtr, "name");
443 auto *SizePtr =
444 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
445 {ConstantInt::get(Type::getInt32Ty(C), 0),
446 ConstantInt::get(Type::getInt32Ty(C), 6)});
447 auto *Size = Builder.CreateLoad(Type::getInt64Ty(C), SizePtr, "size");
448 auto *FlagsPtr =
449 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
450 {ConstantInt::get(Type::getInt32Ty(C), 0),
451 ConstantInt::get(Type::getInt32Ty(C), 3)});
452 auto *Flags = Builder.CreateLoad(Type::getInt32Ty(C), FlagsPtr, "flags");
453 auto *DataPtr =
454 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
455 {ConstantInt::get(Type::getInt32Ty(C), 0),
456 ConstantInt::get(Type::getInt32Ty(C), 7)});
457 auto *Data = Builder.CreateTrunc(
458 Builder.CreateLoad(Type::getInt64Ty(C), DataPtr, "data"),
460 auto *Type = Builder.CreateAnd(
461 Flags, ConstantInt::get(Type::getInt32Ty(C), 0x7), "type");
462
463 // Extract the flags stored in the bit-field and convert them to C booleans.
464 auto *ExternBit = Builder.CreateAnd(
465 Flags, ConstantInt::get(Type::getInt32Ty(C),
467 auto *Extern = Builder.CreateLShr(
468 ExternBit, ConstantInt::get(Type::getInt32Ty(C), 3), "extern");
469 auto *ConstantBit = Builder.CreateAnd(
470 Flags, ConstantInt::get(Type::getInt32Ty(C),
472 auto *Const = Builder.CreateLShr(
473 ConstantBit, ConstantInt::get(Type::getInt32Ty(C), 4), "constant");
474 auto *NormalizedBit = Builder.CreateAnd(
475 Flags, ConstantInt::get(Type::getInt32Ty(C),
477 auto *Normalized = Builder.CreateLShr(
478 NormalizedBit, ConstantInt::get(Type::getInt32Ty(C), 5), "normalized");
479 auto *KindCond = Builder.CreateICmpEQ(
480 Kind, ConstantInt::get(Type::getInt16Ty(C),
483 Builder.CreateCondBr(KindCond, IfKindBB, IfEndBB);
484 Builder.SetInsertPoint(IfKindBB);
485 auto *FnCond = Builder.CreateICmpEQ(
487 Builder.CreateCondBr(FnCond, IfThenBB, IfElseBB);
488
489 // Create kernel registration code.
490 Builder.SetInsertPoint(IfThenBB);
491 Builder.CreateCall(
492 RegFunc,
493 {RegGlobalsFn->arg_begin(), Addr, Name, Name,
497 ConstantPointerNull::get(Int32PtrTy)});
498 Builder.CreateBr(IfEndBB);
499 Builder.SetInsertPoint(IfElseBB);
500
501 auto *Switch = Builder.CreateSwitch(Type, IfEndBB);
502 // Create global variable registration code.
503 Builder.SetInsertPoint(SwGlobalBB);
504 Builder.CreateCall(RegVar,
505 {RegGlobalsFn->arg_begin(), Addr, Name, Name, Extern, Size,
506 Const, ConstantInt::get(Type::getInt32Ty(C), 0)});
507 Builder.CreateBr(IfEndBB);
508 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalEntry),
509 SwGlobalBB);
510
511 // Create managed variable registration code.
512 Builder.SetInsertPoint(SwManagedBB);
513 Builder.CreateCall(RegManagedVar, {RegGlobalsFn->arg_begin(), AuxAddr, Addr,
514 Name, Size, Data});
515 Builder.CreateBr(IfEndBB);
516 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalManagedEntry),
517 SwManagedBB);
518 // Create surface variable registration code.
519 Builder.SetInsertPoint(SwSurfaceBB);
520 if (EmitSurfacesAndTextures)
521 Builder.CreateCall(RegSurface, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
522 Data, Extern});
523 Builder.CreateBr(IfEndBB);
524 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalSurfaceEntry),
525 SwSurfaceBB);
526
527 // Create texture variable registration code.
528 Builder.SetInsertPoint(SwTextureBB);
529 if (EmitSurfacesAndTextures)
530 Builder.CreateCall(RegTexture, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
531 Data, Normalized, Extern});
532 Builder.CreateBr(IfEndBB);
533 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalTextureEntry),
534 SwTextureBB);
535
536 Builder.SetInsertPoint(IfEndBB);
537 auto *NewEntry = Builder.CreateInBoundsGEP(
538 offloading::getEntryTy(M), Entry, ConstantInt::get(getSizeTTy(M), 1));
539 auto *Cmp = Builder.CreateICmpEQ(NewEntry, EntriesE);
540 Entry->addIncoming(EntriesB, &RegGlobalsFn->getEntryBlock());
541 Entry->addIncoming(NewEntry, IfEndBB);
542 Builder.CreateCondBr(Cmp, ExitBB, EntryBB);
543 Builder.SetInsertPoint(ExitBB);
544 Builder.CreateRetVoid();
545
546 return RegGlobalsFn;
547}
548
549// Create the constructor and destructor to register the fatbinary with the CUDA
550// runtime.
551void createRegisterFatbinFunction(Module &M, GlobalVariable *FatbinDesc,
552 bool IsHIP, EntryArrayTy EntryArray,
553 StringRef Suffix,
554 bool EmitSurfacesAndTextures) {
555 LLVMContext &C = M.getContext();
556 auto *CtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
557 auto *CtorFunc = Function::Create(
559 (IsHIP ? ".hip.fatbin_reg" : ".cuda.fatbin_reg") + Suffix, &M);
560 CtorFunc->setSection(getStartupSection(M.getTargetTriple()));
561
562 auto *DtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
563 auto *DtorFunc = Function::Create(
565 (IsHIP ? ".hip.fatbin_unreg" : ".cuda.fatbin_unreg") + Suffix, &M);
566 DtorFunc->setSection(getStartupSection(M.getTargetTriple()));
567
568 auto *PtrTy = PointerType::getUnqual(C);
569
570 // Get the __cudaRegisterFatBinary function declaration.
571 auto *RegFatTy = FunctionType::get(PtrTy, PtrTy, /*isVarArg=*/false);
572 FunctionCallee RegFatbin = M.getOrInsertFunction(
573 IsHIP ? "__hipRegisterFatBinary" : "__cudaRegisterFatBinary", RegFatTy);
574 // Get the __cudaRegisterFatBinaryEnd function declaration.
575 auto *RegFatEndTy =
576 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
577 FunctionCallee RegFatbinEnd =
578 M.getOrInsertFunction("__cudaRegisterFatBinaryEnd", RegFatEndTy);
579 // Get the __cudaUnregisterFatBinary function declaration.
580 auto *UnregFatTy =
581 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
582 FunctionCallee UnregFatbin = M.getOrInsertFunction(
583 IsHIP ? "__hipUnregisterFatBinary" : "__cudaUnregisterFatBinary",
584 UnregFatTy);
585
586 auto *AtExitTy =
587 FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
588 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
589
590 auto *BinaryHandleGlobal = new llvm::GlobalVariable(
591 M, PtrTy, false, llvm::GlobalValue::InternalLinkage,
593 (IsHIP ? ".hip.binary_handle" : ".cuda.binary_handle") + Suffix);
594
595 // Create the constructor to register this image with the runtime.
596 IRBuilder<> CtorBuilder(BasicBlock::Create(C, "entry", CtorFunc));
597 CallInst *Handle = CtorBuilder.CreateCall(
598 RegFatbin,
600 CtorBuilder.CreateAlignedStore(
601 Handle, BinaryHandleGlobal,
602 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
603 CtorBuilder.CreateCall(createRegisterGlobalsFunction(M, IsHIP, EntryArray,
604 Suffix,
605 EmitSurfacesAndTextures),
606 Handle);
607 if (!IsHIP)
608 CtorBuilder.CreateCall(RegFatbinEnd, Handle);
609 CtorBuilder.CreateCall(AtExit, DtorFunc);
610 CtorBuilder.CreateRetVoid();
611
612 // Create the destructor to unregister the image with the runtime. We cannot
613 // use a standard global destructor after CUDA 9.2 so this must be called by
614 // `atexit()` instead.
615 IRBuilder<> DtorBuilder(BasicBlock::Create(C, "entry", DtorFunc));
616 LoadInst *BinaryHandle = DtorBuilder.CreateAlignedLoad(
617 PtrTy, BinaryHandleGlobal,
618 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
619 DtorBuilder.CreateCall(UnregFatbin, BinaryHandle);
620 DtorBuilder.CreateRetVoid();
621
622 // Add this function to constructors.
623 appendToGlobalCtors(M, CtorFunc, /*Priority=*/101);
624}
625
626/// SYCLWrapper helper class that creates all LLVM IRs wrapping given images.
627class SYCLWrapper {
628public:
629 SYCLWrapper(Module &M, const SYCLJITOptions &Options, bool IsFinalizedImage)
630 : M(M), C(M.getContext()), Options(Options),
631 IsFinalizedImage(IsFinalizedImage) {}
632
633 /// Embeds \p Buffer (a raw OffloadBinary) as a global constant and returns
634 /// a pair of (Start, Size), where Start points to the beginning of the
635 /// embedded data and Size is its length in bytes.
636 std::pair<Constant *, Constant *> embedBinary(ArrayRef<char> Buffer) {
637 Constant *Arr = ConstantDataArray::get(C, Buffer);
638 GlobalVariable *BinaryGV = new GlobalVariable(
639 M, Arr->getType(), /*isConstant=*/true, GlobalValue::InternalLinkage,
640 Arr, ".sycl_offloading.binary");
641 BinaryGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
642 // The linker wrapper scans ".llvm.offloading" for device code to link, so
643 // an already finalized image must go elsewhere to avoid being linked again.
644 BinaryGV->setSection(IsFinalizedImage ? ".sycl_fatbin"
645 : ".llvm.offloading");
646
647 IntegerType *Int64Ty = Type::getInt64Ty(C);
648 Constant *Size = ConstantInt::get(Int64Ty, Buffer.size());
649 return {BinaryGV, Size};
650 }
651
652 Function *createRegisterFatbinFunction(Constant *Start, Constant *Size) {
653 FunctionType *FuncTy =
654 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
656 Twine("sycl") + ".descriptor_reg", &M);
657 Func->setSection(getStartupSection(M.getTargetTriple()));
658
659 PointerType *PtrTy = PointerType::getUnqual(C);
660 IntegerType *Int64Ty = Type::getInt64Ty(C);
661 FunctionType *RegFuncTy =
662 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
663 /*isVarArg=*/false);
664 FunctionCallee RegFuncC =
665 M.getOrInsertFunction("__sycl_register_lib", RegFuncTy);
666
667 FunctionType *AtExitTy =
668 FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
669 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
670
671 Function *UnregFunc = createUnregisterFunction(Start, Size);
672
673 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
674 Builder.CreateCall(RegFuncC, {Start, Size});
675
676 // Unregister with 'atexit'. The handler is installed after
677 // __sycl_register_lib has brought the runtime's own exit-time cleanup into
678 // the atexit chain, so it is ordered ahead of that cleanup.
679 Builder.CreateCall(AtExit, UnregFunc);
680 Builder.CreateRetVoid();
681
682 return Func;
683 }
684
685private:
686 Function *createUnregisterFunction(Constant *Start, Constant *Size) {
687 FunctionType *FuncTy =
688 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
690 "sycl.descriptor_unreg", &M);
691 Func->setSection(getStartupSection(M.getTargetTriple()));
692
693 PointerType *PtrTy = PointerType::getUnqual(C);
694 IntegerType *Int64Ty = Type::getInt64Ty(C);
695 FunctionType *UnRegFuncTy =
696 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
697 /*isVarArg=*/false);
698 FunctionCallee UnRegFuncC =
699 M.getOrInsertFunction("__sycl_unregister_lib", UnRegFuncTy);
700
701 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
702 Builder.CreateCall(UnRegFuncC, {Start, Size});
703 Builder.CreateRetVoid();
704
705 return Func;
706 }
707
708 Module &M;
709 LLVMContext &C;
710 SYCLJITOptions Options;
711 bool IsFinalizedImage;
712}; // end of SYCLWrapper
713
714} // namespace
715
716/// Wraps the images and registers them with the runtime through the given
717/// registration entry points. Shared by the OpenMP and OpenACC wrappers, which
718/// differ only in the names of those entry points.
720 offloading::EntryArrayTy EntryArray,
721 StringRef Suffix, bool Relocatable,
722 StringRef RegFuncName,
723 StringRef UnregFuncName) {
725 createBinDesc(M, Images, EntryArray, Suffix, Relocatable);
726 if (!Desc)
728 "No binary descriptors created.");
729 createRegisterFunction(M, Desc, Suffix, RegFuncName, UnregFuncName);
730 return Error::success();
731}
732
734 EntryArrayTy EntryArray,
735 llvm::StringRef Suffix, bool Relocatable) {
736 return wrapOmpAccBinaries(M, Images, EntryArray, Suffix, Relocatable,
737 "__tgt_register_lib", "__tgt_unregister_lib");
738}
739
741 ArrayRef<ArrayRef<char>> Images,
742 EntryArrayTy EntryArray,
743 bool Relocatable) {
744 return wrapOmpAccBinaries(M, Images, EntryArray, /*Suffix=*/"", Relocatable,
745 "__tgt_acc_register_lib",
746 "__tgt_acc_unregister_lib");
747}
748
750 EntryArrayTy EntryArray,
751 llvm::StringRef Suffix,
752 bool EmitSurfacesAndTextures) {
753 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/false, Suffix);
754 if (!Desc)
756 "No fatbin section created.");
757
758 createRegisterFatbinFunction(M, Desc, /*IsHip=*/false, EntryArray, Suffix,
759 EmitSurfacesAndTextures);
760 return Error::success();
761}
762
764 EntryArrayTy EntryArray, llvm::StringRef Suffix,
765 bool EmitSurfacesAndTextures) {
766 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/true, Suffix);
767 if (!Desc)
769 "No fatbin section created.");
770
771 createRegisterFatbinFunction(M, Desc, /*IsHip=*/true, EntryArray, Suffix,
772 EmitSurfacesAndTextures);
773 return Error::success();
774}
775
778 bool IsFinalizedImage,
779 Function **RegistrationFunc) {
780 SYCLWrapper W(M, Options, IsFinalizedImage);
781 auto [Start, Size] = W.embedBinary(Buffer);
782 Function *RegisterFunc = W.createRegisterFatbinFunction(Start, Size);
783 if (RegistrationFunc) {
784 *RegistrationFunc = RegisterFunc;
785 return Error::success();
786 }
787
788 appendToGlobalCtors(M, RegisterFunc, /*Priority=*/101);
789 return Error::success();
790}
uint64_t 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
static Error wrapOmpAccBinaries(Module &M, ArrayRef< ArrayRef< char > > Images, offloading::EntryArrayTy EntryArray, StringRef Suffix, bool Relocatable, StringRef RegFuncName, StringRef UnregFuncName)
Wraps the images and registers them with the runtime through the given registration entry points.
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 * getPtrAdd(Constant *Ptr, Constant *Offset, GEPNoWrapFlags NW=GEPNoWrapFlags::none(), std::optional< ConstantRange > InRange=std::nullopt, Type *OnlyIfReduced=nullptr)
Create a getelementptr i8, ptr, offset constant expression.
Definition Constants.h:1518
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:44
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:169
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
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2987
Class to represent integer types.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:76
An instruction for reading from memory.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
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:887
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:778
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:662
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:683
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:300
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
static LLVM_ABI IntegerType * getInt16Ty(LLVMContext &C)
Definition Type.cpp:298
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
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...
LLVM_ABI llvm::Error wrapOpenACCBinaries(llvm::Module &M, llvm::ArrayRef< llvm::ArrayRef< char > > Images, EntryArrayTy EntryArray, bool Relocatable=false)
Wraps the input device images into the module M as global symbols and registers the images with the O...
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) -> IRBuilder< FolderTy, InserterTy >
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:371
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