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 ".omp_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 ".omp_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 ".omp_offloading.descriptor" + Suffix);
197}
198
199Function *createUnregisterFunction(Module &M, GlobalVariable *BinDesc,
200 StringRef Suffix) {
201 LLVMContext &C = M.getContext();
202 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
203 auto *Func =
205 ".omp_offloading.descriptor_unreg" + Suffix, &M);
206 Func->setSection(getStartupSection(M.getTargetTriple()));
207
208 // Get __tgt_unregister_lib function declaration.
209 auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
210 /*isVarArg*/ false);
211 FunctionCallee UnRegFuncC =
212 M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy);
213
214 // Construct function body
215 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
216 Builder.CreateCall(UnRegFuncC, BinDesc);
217 Builder.CreateRetVoid();
218
219 return Func;
220}
221
222void createRegisterFunction(Module &M, GlobalVariable *BinDesc,
223 StringRef Suffix) {
224 LLVMContext &C = M.getContext();
225 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
227 ".omp_offloading.descriptor_reg" + Suffix, &M);
228 Func->setSection(getStartupSection(M.getTargetTriple()));
229
230 // Get __tgt_register_lib function declaration.
231 auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
232 /*isVarArg*/ false);
233 FunctionCallee RegFuncC =
234 M.getOrInsertFunction("__tgt_register_lib", RegFuncTy);
235
236 auto *AtExitTy = FunctionType::get(
237 Type::getInt32Ty(C), PointerType::getUnqual(C), /*isVarArg=*/false);
238 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
239
240 Function *UnregFunc = createUnregisterFunction(M, BinDesc, Suffix);
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 auto *EntryBB = BasicBlock::Create(C, "while.entry", RegGlobalsFn);
408 auto *IfKindBB = BasicBlock::Create(C, "if.kind", RegGlobalsFn);
409 auto *IfThenBB = BasicBlock::Create(C, "if.then", RegGlobalsFn);
410 auto *IfElseBB = BasicBlock::Create(C, "if.else", RegGlobalsFn);
411 auto *SwGlobalBB = BasicBlock::Create(C, "sw.global", RegGlobalsFn);
412 auto *SwManagedBB = BasicBlock::Create(C, "sw.managed", RegGlobalsFn);
413 auto *SwSurfaceBB = BasicBlock::Create(C, "sw.surface", RegGlobalsFn);
414 auto *SwTextureBB = BasicBlock::Create(C, "sw.texture", RegGlobalsFn);
415 auto *IfEndBB = BasicBlock::Create(C, "if.end", RegGlobalsFn);
416 auto *ExitBB = BasicBlock::Create(C, "while.end", RegGlobalsFn);
417
418 auto *EntryCmp = Builder.CreateICmpNE(EntriesB, EntriesE);
419 Builder.CreateCondBr(EntryCmp, EntryBB, ExitBB);
420 Builder.SetInsertPoint(EntryBB);
421 auto *Entry = Builder.CreatePHI(PointerType::getUnqual(C), 2, "entry");
422 auto *AddrPtr =
423 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
424 {ConstantInt::get(Type::getInt32Ty(C), 0),
425 ConstantInt::get(Type::getInt32Ty(C), 4)});
426 auto *Addr = Builder.CreateLoad(Int8PtrTy, AddrPtr, "addr");
427 auto *AuxAddrPtr =
428 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
429 {ConstantInt::get(Type::getInt32Ty(C), 0),
430 ConstantInt::get(Type::getInt32Ty(C), 8)});
431 auto *AuxAddr = Builder.CreateLoad(Int8PtrTy, AuxAddrPtr, "aux_addr");
432 auto *KindPtr =
433 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
434 {ConstantInt::get(Type::getInt32Ty(C), 0),
435 ConstantInt::get(Type::getInt32Ty(C), 2)});
436 auto *Kind = Builder.CreateLoad(Type::getInt16Ty(C), KindPtr, "kind");
437 auto *NamePtr =
438 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
439 {ConstantInt::get(Type::getInt32Ty(C), 0),
440 ConstantInt::get(Type::getInt32Ty(C), 5)});
441 auto *Name = Builder.CreateLoad(Int8PtrTy, NamePtr, "name");
442 auto *SizePtr =
443 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
444 {ConstantInt::get(Type::getInt32Ty(C), 0),
445 ConstantInt::get(Type::getInt32Ty(C), 6)});
446 auto *Size = Builder.CreateLoad(Type::getInt64Ty(C), SizePtr, "size");
447 auto *FlagsPtr =
448 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
449 {ConstantInt::get(Type::getInt32Ty(C), 0),
450 ConstantInt::get(Type::getInt32Ty(C), 3)});
451 auto *Flags = Builder.CreateLoad(Type::getInt32Ty(C), FlagsPtr, "flags");
452 auto *DataPtr =
453 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
454 {ConstantInt::get(Type::getInt32Ty(C), 0),
455 ConstantInt::get(Type::getInt32Ty(C), 7)});
456 auto *Data = Builder.CreateTrunc(
457 Builder.CreateLoad(Type::getInt64Ty(C), DataPtr, "data"),
459 auto *Type = Builder.CreateAnd(
460 Flags, ConstantInt::get(Type::getInt32Ty(C), 0x7), "type");
461
462 // Extract the flags stored in the bit-field and convert them to C booleans.
463 auto *ExternBit = Builder.CreateAnd(
464 Flags, ConstantInt::get(Type::getInt32Ty(C),
466 auto *Extern = Builder.CreateLShr(
467 ExternBit, ConstantInt::get(Type::getInt32Ty(C), 3), "extern");
468 auto *ConstantBit = Builder.CreateAnd(
469 Flags, ConstantInt::get(Type::getInt32Ty(C),
471 auto *Const = Builder.CreateLShr(
472 ConstantBit, ConstantInt::get(Type::getInt32Ty(C), 4), "constant");
473 auto *NormalizedBit = Builder.CreateAnd(
474 Flags, ConstantInt::get(Type::getInt32Ty(C),
476 auto *Normalized = Builder.CreateLShr(
477 NormalizedBit, ConstantInt::get(Type::getInt32Ty(C), 5), "normalized");
478 auto *KindCond = Builder.CreateICmpEQ(
479 Kind, ConstantInt::get(Type::getInt16Ty(C),
482 Builder.CreateCondBr(KindCond, IfKindBB, IfEndBB);
483 Builder.SetInsertPoint(IfKindBB);
484 auto *FnCond = Builder.CreateICmpEQ(
486 Builder.CreateCondBr(FnCond, IfThenBB, IfElseBB);
487
488 // Create kernel registration code.
489 Builder.SetInsertPoint(IfThenBB);
490 Builder.CreateCall(
491 RegFunc,
492 {RegGlobalsFn->arg_begin(), Addr, Name, Name,
496 ConstantPointerNull::get(Int32PtrTy)});
497 Builder.CreateBr(IfEndBB);
498 Builder.SetInsertPoint(IfElseBB);
499
500 auto *Switch = Builder.CreateSwitch(Type, IfEndBB);
501 // Create global variable registration code.
502 Builder.SetInsertPoint(SwGlobalBB);
503 Builder.CreateCall(RegVar,
504 {RegGlobalsFn->arg_begin(), Addr, Name, Name, Extern, Size,
505 Const, ConstantInt::get(Type::getInt32Ty(C), 0)});
506 Builder.CreateBr(IfEndBB);
507 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalEntry),
508 SwGlobalBB);
509
510 // Create managed variable registration code.
511 Builder.SetInsertPoint(SwManagedBB);
512 Builder.CreateCall(RegManagedVar, {RegGlobalsFn->arg_begin(), AuxAddr, Addr,
513 Name, Size, Data});
514 Builder.CreateBr(IfEndBB);
515 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalManagedEntry),
516 SwManagedBB);
517 // Create surface variable registration code.
518 Builder.SetInsertPoint(SwSurfaceBB);
519 if (EmitSurfacesAndTextures)
520 Builder.CreateCall(RegSurface, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
521 Data, Extern});
522 Builder.CreateBr(IfEndBB);
523 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalSurfaceEntry),
524 SwSurfaceBB);
525
526 // Create texture variable registration code.
527 Builder.SetInsertPoint(SwTextureBB);
528 if (EmitSurfacesAndTextures)
529 Builder.CreateCall(RegTexture, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
530 Data, Normalized, Extern});
531 Builder.CreateBr(IfEndBB);
532 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalTextureEntry),
533 SwTextureBB);
534
535 Builder.SetInsertPoint(IfEndBB);
536 auto *NewEntry = Builder.CreateInBoundsGEP(
537 offloading::getEntryTy(M), Entry, ConstantInt::get(getSizeTTy(M), 1));
538 auto *Cmp = Builder.CreateICmpEQ(NewEntry, EntriesE);
539 Entry->addIncoming(EntriesB, &RegGlobalsFn->getEntryBlock());
540 Entry->addIncoming(NewEntry, IfEndBB);
541 Builder.CreateCondBr(Cmp, ExitBB, EntryBB);
542 Builder.SetInsertPoint(ExitBB);
543 Builder.CreateRetVoid();
544
545 return RegGlobalsFn;
546}
547
548// Create the constructor and destructor to register the fatbinary with the CUDA
549// runtime.
550void createRegisterFatbinFunction(Module &M, GlobalVariable *FatbinDesc,
551 bool IsHIP, EntryArrayTy EntryArray,
552 StringRef Suffix,
553 bool EmitSurfacesAndTextures) {
554 LLVMContext &C = M.getContext();
555 auto *CtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
556 auto *CtorFunc = Function::Create(
558 (IsHIP ? ".hip.fatbin_reg" : ".cuda.fatbin_reg") + Suffix, &M);
559 CtorFunc->setSection(getStartupSection(M.getTargetTriple()));
560
561 auto *DtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
562 auto *DtorFunc = Function::Create(
564 (IsHIP ? ".hip.fatbin_unreg" : ".cuda.fatbin_unreg") + Suffix, &M);
565 DtorFunc->setSection(getStartupSection(M.getTargetTriple()));
566
567 auto *PtrTy = PointerType::getUnqual(C);
568
569 // Get the __cudaRegisterFatBinary function declaration.
570 auto *RegFatTy = FunctionType::get(PtrTy, PtrTy, /*isVarArg=*/false);
571 FunctionCallee RegFatbin = M.getOrInsertFunction(
572 IsHIP ? "__hipRegisterFatBinary" : "__cudaRegisterFatBinary", RegFatTy);
573 // Get the __cudaRegisterFatBinaryEnd function declaration.
574 auto *RegFatEndTy =
575 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
576 FunctionCallee RegFatbinEnd =
577 M.getOrInsertFunction("__cudaRegisterFatBinaryEnd", RegFatEndTy);
578 // Get the __cudaUnregisterFatBinary function declaration.
579 auto *UnregFatTy =
580 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
581 FunctionCallee UnregFatbin = M.getOrInsertFunction(
582 IsHIP ? "__hipUnregisterFatBinary" : "__cudaUnregisterFatBinary",
583 UnregFatTy);
584
585 auto *AtExitTy =
586 FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
587 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
588
589 auto *BinaryHandleGlobal = new llvm::GlobalVariable(
590 M, PtrTy, false, llvm::GlobalValue::InternalLinkage,
592 (IsHIP ? ".hip.binary_handle" : ".cuda.binary_handle") + Suffix);
593
594 // Create the constructor to register this image with the runtime.
595 IRBuilder<> CtorBuilder(BasicBlock::Create(C, "entry", CtorFunc));
596 CallInst *Handle = CtorBuilder.CreateCall(
597 RegFatbin,
599 CtorBuilder.CreateAlignedStore(
600 Handle, BinaryHandleGlobal,
601 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
602 CtorBuilder.CreateCall(createRegisterGlobalsFunction(M, IsHIP, EntryArray,
603 Suffix,
604 EmitSurfacesAndTextures),
605 Handle);
606 if (!IsHIP)
607 CtorBuilder.CreateCall(RegFatbinEnd, Handle);
608 CtorBuilder.CreateCall(AtExit, DtorFunc);
609 CtorBuilder.CreateRetVoid();
610
611 // Create the destructor to unregister the image with the runtime. We cannot
612 // use a standard global destructor after CUDA 9.2 so this must be called by
613 // `atexit()` instead.
614 IRBuilder<> DtorBuilder(BasicBlock::Create(C, "entry", DtorFunc));
615 LoadInst *BinaryHandle = DtorBuilder.CreateAlignedLoad(
616 PtrTy, BinaryHandleGlobal,
617 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
618 DtorBuilder.CreateCall(UnregFatbin, BinaryHandle);
619 DtorBuilder.CreateRetVoid();
620
621 // Add this function to constructors.
622 appendToGlobalCtors(M, CtorFunc, /*Priority=*/101);
623}
624
625/// SYCLWrapper helper class that creates all LLVM IRs wrapping given images.
626class SYCLWrapper {
627public:
628 SYCLWrapper(Module &M, const SYCLJITOptions &Options, bool IsFinalizedImage)
629 : M(M), C(M.getContext()), Options(Options),
630 IsFinalizedImage(IsFinalizedImage) {}
631
632 /// Embeds \p Buffer (a raw OffloadBinary) as a global constant and returns
633 /// a pair of (Start, Size), where Start points to the beginning of the
634 /// embedded data and Size is its length in bytes.
635 std::pair<Constant *, Constant *> embedBinary(ArrayRef<char> Buffer) {
636 Constant *Arr = ConstantDataArray::get(C, Buffer);
637 GlobalVariable *BinaryGV = new GlobalVariable(
638 M, Arr->getType(), /*isConstant=*/true, GlobalValue::InternalLinkage,
639 Arr, ".sycl_offloading.binary");
640 BinaryGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
641 // The linker wrapper scans ".llvm.offloading" for device code to link, so
642 // an already finalized image must go elsewhere to avoid being linked again.
643 BinaryGV->setSection(IsFinalizedImage ? ".sycl_fatbin"
644 : ".llvm.offloading");
645
646 IntegerType *Int64Ty = Type::getInt64Ty(C);
647 Constant *Size = ConstantInt::get(Int64Ty, Buffer.size());
648 return {BinaryGV, Size};
649 }
650
651 Function *createRegisterFatbinFunction(Constant *Start, Constant *Size) {
652 FunctionType *FuncTy =
653 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
655 Twine("sycl") + ".descriptor_reg", &M);
656 Func->setSection(getStartupSection(M.getTargetTriple()));
657
658 PointerType *PtrTy = PointerType::getUnqual(C);
659 IntegerType *Int64Ty = Type::getInt64Ty(C);
660 FunctionType *RegFuncTy =
661 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
662 /*isVarArg=*/false);
663 FunctionCallee RegFuncC =
664 M.getOrInsertFunction("__sycl_register_lib", RegFuncTy);
665
666 FunctionType *AtExitTy =
667 FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
668 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
669
670 Function *UnregFunc = createUnregisterFunction(Start, Size);
671
672 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
673 Builder.CreateCall(RegFuncC, {Start, Size});
674
675 // Unregister with 'atexit'. The handler is installed after
676 // __sycl_register_lib has brought the runtime's own exit-time cleanup into
677 // the atexit chain, so it is ordered ahead of that cleanup.
678 Builder.CreateCall(AtExit, UnregFunc);
679 Builder.CreateRetVoid();
680
681 return Func;
682 }
683
684private:
685 Function *createUnregisterFunction(Constant *Start, Constant *Size) {
686 FunctionType *FuncTy =
687 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
689 "sycl.descriptor_unreg", &M);
690 Func->setSection(getStartupSection(M.getTargetTriple()));
691
692 PointerType *PtrTy = PointerType::getUnqual(C);
693 IntegerType *Int64Ty = Type::getInt64Ty(C);
694 FunctionType *UnRegFuncTy =
695 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
696 /*isVarArg=*/false);
697 FunctionCallee UnRegFuncC =
698 M.getOrInsertFunction("__sycl_unregister_lib", UnRegFuncTy);
699
700 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
701 Builder.CreateCall(UnRegFuncC, {Start, Size});
702 Builder.CreateRetVoid();
703
704 return Func;
705 }
706
707 Module &M;
708 LLVMContext &C;
709 SYCLJITOptions Options;
710 bool IsFinalizedImage;
711}; // end of SYCLWrapper
712
713} // namespace
714
716 EntryArrayTy EntryArray,
717 llvm::StringRef Suffix, bool Relocatable) {
719 createBinDesc(M, Images, EntryArray, Suffix, Relocatable);
720 if (!Desc)
722 "No binary descriptors created.");
723 createRegisterFunction(M, Desc, Suffix);
724 return Error::success();
725}
726
728 EntryArrayTy EntryArray,
729 llvm::StringRef Suffix,
730 bool EmitSurfacesAndTextures) {
731 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/false, Suffix);
732 if (!Desc)
734 "No fatbin section created.");
735
736 createRegisterFatbinFunction(M, Desc, /*IsHip=*/false, EntryArray, Suffix,
737 EmitSurfacesAndTextures);
738 return Error::success();
739}
740
742 EntryArrayTy EntryArray, llvm::StringRef Suffix,
743 bool EmitSurfacesAndTextures) {
744 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/true, Suffix);
745 if (!Desc)
747 "No fatbin section created.");
748
749 createRegisterFatbinFunction(M, Desc, /*IsHip=*/true, EntryArray, Suffix,
750 EmitSurfacesAndTextures);
751 return Error::success();
752}
753
756 bool IsFinalizedImage,
757 Function **RegistrationFunc) {
758 SYCLWrapper W(M, Options, IsFinalizedImage);
759 auto [Start, Size] = W.embedBinary(Buffer);
760 Function *RegisterFunc = W.createRegisterFatbinFunction(Start, Size);
761 if (RegistrationFunc) {
762 *RegistrationFunc = RegisterFunc;
763 return Error::success();
764 }
765
766 appendToGlobalCtors(M, RegisterFunc, /*Priority=*/101);
767 return Error::success();
768}
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 * 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:1497
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: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:2908
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: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:681
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...
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 >
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:258
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