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"
30
31#include <memory>
32#include <utility>
33
34using namespace llvm;
35using namespace llvm::object;
36using namespace llvm::offloading;
37
38namespace {
39/// Magic number that begins the section containing the CUDA fatbinary.
40constexpr unsigned CudaFatMagic = 0x466243b1;
41constexpr unsigned HIPFatMagic = 0x48495046;
42
44 return M.getDataLayout().getIntPtrType(M.getContext());
45}
46
47/// Returns the appropriate startup section for registration functions.
48/// Mach-O uses "__TEXT,__StaticInit"; ELF/COFF use ".text.startup".
49StringRef getStartupSection(const Triple &T) {
50 return T.isOSBinFormatMachO() ? "__TEXT,__StaticInit" : ".text.startup";
51}
52
53// struct __tgt_device_image {
54// void *ImageStart;
55// void *ImageEnd;
56// __tgt_offload_entry *EntriesBegin;
57// __tgt_offload_entry *EntriesEnd;
58// };
59StructType *getDeviceImageTy(Module &M) {
60 LLVMContext &C = M.getContext();
61 StructType *ImageTy = StructType::getTypeByName(C, "__tgt_device_image");
62 if (!ImageTy)
63 ImageTy =
64 StructType::create("__tgt_device_image", PointerType::getUnqual(C),
67 return ImageTy;
68}
69
70PointerType *getDeviceImagePtrTy(Module &M) {
71 return PointerType::getUnqual(M.getContext());
72}
73
74// struct __tgt_bin_desc {
75// int32_t NumDeviceImages;
76// __tgt_device_image *DeviceImages;
77// __tgt_offload_entry *HostEntriesBegin;
78// __tgt_offload_entry *HostEntriesEnd;
79// };
80StructType *getBinDescTy(Module &M) {
81 LLVMContext &C = M.getContext();
82 StructType *DescTy = StructType::getTypeByName(C, "__tgt_bin_desc");
83 if (!DescTy)
84 DescTy = StructType::create(
85 "__tgt_bin_desc", Type::getInt32Ty(C), getDeviceImagePtrTy(M),
87 return DescTy;
88}
89
90PointerType *getBinDescPtrTy(Module &M) {
91 return PointerType::getUnqual(M.getContext());
92}
93
94/// Creates binary descriptor for the given device images. Binary descriptor
95/// is an object that is passed to the offloading runtime at program startup
96/// and it describes all device images available in the executable or shared
97/// library. It is defined as follows
98///
99/// __attribute__((visibility("hidden")))
100/// extern __tgt_offload_entry *__start_llvm_offload_entries;
101/// __attribute__((visibility("hidden")))
102/// extern __tgt_offload_entry *__stop_llvm_offload_entries;
103///
104/// static const char Image0[] = { <Bufs.front() contents> };
105/// ...
106/// static const char ImageN[] = { <Bufs.back() contents> };
107///
108/// static const __tgt_device_image Images[] = {
109/// {
110/// Image0, /*ImageStart*/
111/// Image0 + sizeof(Image0), /*ImageEnd*/
112/// __start_llvm_offload_entries, /*EntriesBegin*/
113/// __stop_llvm_offload_entries /*EntriesEnd*/
114/// },
115/// ...
116/// {
117/// ImageN, /*ImageStart*/
118/// ImageN + sizeof(ImageN), /*ImageEnd*/
119/// __start_llvm_offload_entries, /*EntriesBegin*/
120/// __stop_llvm_offload_entries /*EntriesEnd*/
121/// }
122/// };
123///
124/// static const __tgt_bin_desc BinDesc = {
125/// sizeof(Images) / sizeof(Images[0]), /*NumDeviceImages*/
126/// Images, /*DeviceImages*/
127/// __start_llvm_offload_entries, /*HostEntriesBegin*/
128/// __stop_llvm_offload_entries /*HostEntriesEnd*/
129/// };
130///
131/// Global variable that represents BinDesc is returned.
132GlobalVariable *createBinDesc(Module &M, ArrayRef<ArrayRef<char>> Bufs,
133 EntryArrayTy EntryArray, StringRef Suffix,
134 bool Relocatable) {
135 LLVMContext &C = M.getContext();
136 auto [EntriesB, EntriesE] = EntryArray;
137
138 auto *Zero = ConstantInt::get(getSizeTTy(M), 0u);
139
140 // Create initializer for the images array.
141 SmallVector<Constant *, 4u> ImagesInits;
142 ImagesInits.reserve(Bufs.size());
143 for (ArrayRef<char> Buf : Bufs) {
144 // We embed the full offloading entry so the binary utilities can parse it.
145 auto *Data = ConstantDataArray::get(C, Buf);
146 auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant=*/true,
148 ".omp_offloading.device_image" + Suffix);
150 Image->setSection(Relocatable ? ".llvm.offloading.relocatable"
151 : ".llvm.offloading");
153
154 StringRef Binary(Buf.data(), Buf.size());
155
156 uint64_t BeginOffset = 0;
157 uint64_t EndOffset = Binary.size();
158
159 // Optionally use an offload binary for its offload dumping support.
160 // The device image struct contains the pointer to the beginning and end of
161 // the image stored inside of the offload binary. There should only be one
162 // of these for each buffer so we parse it out manually.
164 const auto *Header =
165 reinterpret_cast<const object::OffloadBinary::Header *>(
166 Binary.bytes_begin());
167 const auto *Entry =
168 reinterpret_cast<const object::OffloadBinary::Entry *>(
169 Binary.bytes_begin() + Header->EntriesOffset);
170 BeginOffset = Entry->ImageOffset;
171 EndOffset = Entry->ImageOffset + Entry->ImageSize;
172 }
173
174 auto *Begin = ConstantInt::get(getSizeTTy(M), BeginOffset);
175 auto *Size = ConstantInt::get(getSizeTTy(M), EndOffset);
176 Constant *ZeroBegin[] = {Zero, Begin};
177 Constant *ZeroSize[] = {Zero, Size};
178
179 auto *ImageB =
180 ConstantExpr::getGetElementPtr(Image->getValueType(), Image, ZeroBegin);
181 auto *ImageE =
182 ConstantExpr::getGetElementPtr(Image->getValueType(), Image, ZeroSize);
183
184 ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(M), ImageB,
185 ImageE, EntriesB, EntriesE));
186 }
187
188 // Then create images array.
189 auto *ImagesData = ConstantArray::get(
190 ArrayType::get(getDeviceImageTy(M), ImagesInits.size()), ImagesInits);
191
192 auto *Images =
193 new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true,
195 ".omp_offloading.device_images" + Suffix);
196 Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
197
198 // And finally create the binary descriptor object.
199 auto *DescInit = ConstantStruct::get(
200 getBinDescTy(M),
201 ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), Images,
202 EntriesB, EntriesE);
203
204 return new GlobalVariable(M, DescInit->getType(), /*isConstant=*/true,
206 ".omp_offloading.descriptor" + Suffix);
207}
208
209Function *createUnregisterFunction(Module &M, GlobalVariable *BinDesc,
210 StringRef Suffix) {
211 LLVMContext &C = M.getContext();
212 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
213 auto *Func =
215 ".omp_offloading.descriptor_unreg" + Suffix, &M);
216 Func->setSection(getStartupSection(M.getTargetTriple()));
217
218 // Get __tgt_unregister_lib function declaration.
219 auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
220 /*isVarArg*/ false);
221 FunctionCallee UnRegFuncC =
222 M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy);
223
224 // Construct function body
225 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
226 Builder.CreateCall(UnRegFuncC, BinDesc);
227 Builder.CreateRetVoid();
228
229 return Func;
230}
231
232void createRegisterFunction(Module &M, GlobalVariable *BinDesc,
233 StringRef Suffix) {
234 LLVMContext &C = M.getContext();
235 auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
237 ".omp_offloading.descriptor_reg" + Suffix, &M);
238 Func->setSection(getStartupSection(M.getTargetTriple()));
239
240 // Get __tgt_register_lib function declaration.
241 auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(M),
242 /*isVarArg*/ false);
243 FunctionCallee RegFuncC =
244 M.getOrInsertFunction("__tgt_register_lib", RegFuncTy);
245
246 auto *AtExitTy = FunctionType::get(
247 Type::getInt32Ty(C), PointerType::getUnqual(C), /*isVarArg=*/false);
248 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
249
250 Function *UnregFunc = createUnregisterFunction(M, BinDesc, Suffix);
251
252 // Construct function body
253 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
254
255 Builder.CreateCall(RegFuncC, BinDesc);
256
257 // Register the destructors with 'atexit'. This is expected by the CUDA
258 // runtime and ensures that we clean up before dynamic objects are destroyed.
259 // This needs to be done after plugin initialization to ensure that it is
260 // called before the plugin runtime is destroyed.
261 Builder.CreateCall(AtExit, UnregFunc);
262 Builder.CreateRetVoid();
263
264 // Add this function to constructors.
265 appendToGlobalCtors(M, Func, /*Priority=*/101);
266}
267
268// struct fatbin_wrapper {
269// int32_t magic;
270// int32_t version;
271// void *image;
272// void *reserved;
273//};
274StructType *getFatbinWrapperTy(Module &M) {
275 LLVMContext &C = M.getContext();
276 StructType *FatbinTy = StructType::getTypeByName(C, "fatbin_wrapper");
277 if (!FatbinTy)
278 FatbinTy = StructType::create(
279 "fatbin_wrapper", Type::getInt32Ty(C), Type::getInt32Ty(C),
281 return FatbinTy;
282}
283
284/// Embed the image \p Image into the module \p M so it can be found by the
285/// runtime.
286GlobalVariable *createFatbinDesc(Module &M, ArrayRef<char> Image, bool IsHIP,
287 StringRef Suffix) {
288 LLVMContext &C = M.getContext();
289 llvm::Type *Int8PtrTy = PointerType::getUnqual(C);
290 const llvm::Triple &Triple = M.getTargetTriple();
291
292 // Create the global string containing the fatbinary.
293 StringRef FatbinConstantSection =
294 IsHIP ? (Triple.isMacOSX() ? "__HIP,__hip_fatbin" : ".hip_fatbin")
295 : (Triple.isMacOSX() ? "__NV_CUDA,__nv_fatbin" : ".nv_fatbin");
296 auto *Data = ConstantDataArray::get(C, Image);
297 auto *Fatbin = new GlobalVariable(M, Data->getType(), /*isConstant*/ true,
299 ".fatbin_image" + Suffix);
300 Fatbin->setSection(FatbinConstantSection);
301
302 // Create the fatbinary wrapper
303 StringRef FatbinWrapperSection =
304 IsHIP ? (Triple.isMacOSX() ? "__HIP,__fatbin" : ".hipFatBinSegment")
305 : (Triple.isMacOSX() ? "__NV_CUDA,__fatbin" : ".nvFatBinSegment");
306 Constant *FatbinWrapper[] = {
307 ConstantInt::get(Type::getInt32Ty(C), IsHIP ? HIPFatMagic : CudaFatMagic),
308 ConstantInt::get(Type::getInt32Ty(C), 1),
311
312 Constant *FatbinInitializer =
313 ConstantStruct::get(getFatbinWrapperTy(M), FatbinWrapper);
314
315 auto *FatbinDesc =
316 new GlobalVariable(M, getFatbinWrapperTy(M),
317 /*isConstant*/ true, GlobalValue::InternalLinkage,
318 FatbinInitializer, ".fatbin_wrapper" + Suffix);
319 FatbinDesc->setSection(FatbinWrapperSection);
320 FatbinDesc->setAlignment(Align(8));
321 FatbinDesc->setNoSanitizeMetadata();
322
323 return FatbinDesc;
324}
325
326/// Create the register globals function. We will iterate all of the offloading
327/// entries stored at the begin / end symbols and register them according to
328/// their type. This creates the following function in IR:
329///
330/// extern struct __tgt_offload_entry __start_cuda_offloading_entries;
331/// extern struct __tgt_offload_entry __stop_cuda_offloading_entries;
332///
333/// extern void __cudaRegisterFunction(void **, void *, void *, void *, int,
334/// void *, void *, void *, void *, int *);
335/// extern void __cudaRegisterVar(void **, void *, void *, void *, int32_t,
336/// int64_t, int32_t, int32_t);
337///
338/// void __cudaRegisterTest(void **fatbinHandle) {
339/// for (struct __tgt_offload_entry *entry = &__start_cuda_offloading_entries;
340/// entry != &__stop_cuda_offloading_entries; ++entry) {
341/// if (entry->Kind != OFK_CUDA)
342/// continue
343///
344/// if (!entry->Size)
345/// __cudaRegisterFunction(fatbinHandle, entry->addr, entry->name,
346/// entry->name, -1, 0, 0, 0, 0, 0);
347/// else
348/// __cudaRegisterVar(fatbinHandle, entry->addr, entry->name, entry->name,
349/// 0, entry->size, 0, 0);
350/// }
351/// }
352Function *createRegisterGlobalsFunction(Module &M, bool IsHIP,
353 EntryArrayTy EntryArray,
354 StringRef Suffix,
355 bool EmitSurfacesAndTextures) {
356 LLVMContext &C = M.getContext();
357 auto [EntriesB, EntriesE] = EntryArray;
358
359 // Get the __cudaRegisterFunction function declaration.
360 PointerType *Int8PtrTy = PointerType::get(C, 0);
361 PointerType *Int8PtrPtrTy = PointerType::get(C, 0);
362 PointerType *Int32PtrTy = PointerType::get(C, 0);
363 auto *RegFuncTy = FunctionType::get(
365 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
366 Int8PtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Int32PtrTy},
367 /*isVarArg*/ false);
368 FunctionCallee RegFunc = M.getOrInsertFunction(
369 IsHIP ? "__hipRegisterFunction" : "__cudaRegisterFunction", RegFuncTy);
370
371 // Get the __cudaRegisterVar function declaration.
372 auto *RegVarTy = FunctionType::get(
374 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
376 /*isVarArg*/ false);
377 FunctionCallee RegVar = M.getOrInsertFunction(
378 IsHIP ? "__hipRegisterVar" : "__cudaRegisterVar", RegVarTy);
379
380 // Get the __cudaRegisterSurface function declaration.
381 FunctionType *RegManagedVarTy =
383 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy,
385 /*isVarArg=*/false);
386 FunctionCallee RegManagedVar = M.getOrInsertFunction(
387 IsHIP ? "__hipRegisterManagedVar" : "__cudaRegisterManagedVar",
388 RegManagedVarTy);
389
390 // Get the __cudaRegisterSurface function declaration.
391 FunctionType *RegSurfaceTy =
393 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy,
395 /*isVarArg=*/false);
396 FunctionCallee RegSurface = M.getOrInsertFunction(
397 IsHIP ? "__hipRegisterSurface" : "__cudaRegisterSurface", RegSurfaceTy);
398
399 // Get the __cudaRegisterTexture function declaration.
400 FunctionType *RegTextureTy = FunctionType::get(
402 {Int8PtrPtrTy, Int8PtrTy, Int8PtrTy, Int8PtrTy, Type::getInt32Ty(C),
404 /*isVarArg=*/false);
405 FunctionCallee RegTexture = M.getOrInsertFunction(
406 IsHIP ? "__hipRegisterTexture" : "__cudaRegisterTexture", RegTextureTy);
407
408 auto *RegGlobalsTy = FunctionType::get(Type::getVoidTy(C), Int8PtrPtrTy,
409 /*isVarArg*/ false);
410 auto *RegGlobalsFn =
412 IsHIP ? ".hip.globals_reg" : ".cuda.globals_reg", &M);
413 RegGlobalsFn->setSection(getStartupSection(M.getTargetTriple()));
414
415 // Create the loop to register all the entries.
416 IRBuilder<> Builder(BasicBlock::Create(C, "entry", RegGlobalsFn));
417 auto *EntryBB = BasicBlock::Create(C, "while.entry", RegGlobalsFn);
418 auto *IfKindBB = BasicBlock::Create(C, "if.kind", RegGlobalsFn);
419 auto *IfThenBB = BasicBlock::Create(C, "if.then", RegGlobalsFn);
420 auto *IfElseBB = BasicBlock::Create(C, "if.else", RegGlobalsFn);
421 auto *SwGlobalBB = BasicBlock::Create(C, "sw.global", RegGlobalsFn);
422 auto *SwManagedBB = BasicBlock::Create(C, "sw.managed", RegGlobalsFn);
423 auto *SwSurfaceBB = BasicBlock::Create(C, "sw.surface", RegGlobalsFn);
424 auto *SwTextureBB = BasicBlock::Create(C, "sw.texture", RegGlobalsFn);
425 auto *IfEndBB = BasicBlock::Create(C, "if.end", RegGlobalsFn);
426 auto *ExitBB = BasicBlock::Create(C, "while.end", RegGlobalsFn);
427
428 auto *EntryCmp = Builder.CreateICmpNE(EntriesB, EntriesE);
429 Builder.CreateCondBr(EntryCmp, EntryBB, ExitBB);
430 Builder.SetInsertPoint(EntryBB);
431 auto *Entry = Builder.CreatePHI(PointerType::getUnqual(C), 2, "entry");
432 auto *AddrPtr =
433 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
434 {ConstantInt::get(Type::getInt32Ty(C), 0),
435 ConstantInt::get(Type::getInt32Ty(C), 4)});
436 auto *Addr = Builder.CreateLoad(Int8PtrTy, AddrPtr, "addr");
437 auto *AuxAddrPtr =
438 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
439 {ConstantInt::get(Type::getInt32Ty(C), 0),
440 ConstantInt::get(Type::getInt32Ty(C), 8)});
441 auto *AuxAddr = Builder.CreateLoad(Int8PtrTy, AuxAddrPtr, "aux_addr");
442 auto *KindPtr =
443 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
444 {ConstantInt::get(Type::getInt32Ty(C), 0),
445 ConstantInt::get(Type::getInt32Ty(C), 2)});
446 auto *Kind = Builder.CreateLoad(Type::getInt16Ty(C), KindPtr, "kind");
447 auto *NamePtr =
448 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
449 {ConstantInt::get(Type::getInt32Ty(C), 0),
450 ConstantInt::get(Type::getInt32Ty(C), 5)});
451 auto *Name = Builder.CreateLoad(Int8PtrTy, NamePtr, "name");
452 auto *SizePtr =
453 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
454 {ConstantInt::get(Type::getInt32Ty(C), 0),
455 ConstantInt::get(Type::getInt32Ty(C), 6)});
456 auto *Size = Builder.CreateLoad(Type::getInt64Ty(C), SizePtr, "size");
457 auto *FlagsPtr =
458 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
459 {ConstantInt::get(Type::getInt32Ty(C), 0),
460 ConstantInt::get(Type::getInt32Ty(C), 3)});
461 auto *Flags = Builder.CreateLoad(Type::getInt32Ty(C), FlagsPtr, "flags");
462 auto *DataPtr =
463 Builder.CreateInBoundsGEP(offloading::getEntryTy(M), Entry,
464 {ConstantInt::get(Type::getInt32Ty(C), 0),
465 ConstantInt::get(Type::getInt32Ty(C), 7)});
466 auto *Data = Builder.CreateTrunc(
467 Builder.CreateLoad(Type::getInt64Ty(C), DataPtr, "data"),
469 auto *Type = Builder.CreateAnd(
470 Flags, ConstantInt::get(Type::getInt32Ty(C), 0x7), "type");
471
472 // Extract the flags stored in the bit-field and convert them to C booleans.
473 auto *ExternBit = Builder.CreateAnd(
474 Flags, ConstantInt::get(Type::getInt32Ty(C),
476 auto *Extern = Builder.CreateLShr(
477 ExternBit, ConstantInt::get(Type::getInt32Ty(C), 3), "extern");
478 auto *ConstantBit = Builder.CreateAnd(
479 Flags, ConstantInt::get(Type::getInt32Ty(C),
481 auto *Const = Builder.CreateLShr(
482 ConstantBit, ConstantInt::get(Type::getInt32Ty(C), 4), "constant");
483 auto *NormalizedBit = Builder.CreateAnd(
484 Flags, ConstantInt::get(Type::getInt32Ty(C),
486 auto *Normalized = Builder.CreateLShr(
487 NormalizedBit, ConstantInt::get(Type::getInt32Ty(C), 5), "normalized");
488 auto *KindCond = Builder.CreateICmpEQ(
489 Kind, ConstantInt::get(Type::getInt16Ty(C),
492 Builder.CreateCondBr(KindCond, IfKindBB, IfEndBB);
493 Builder.SetInsertPoint(IfKindBB);
494 auto *FnCond = Builder.CreateICmpEQ(
496 Builder.CreateCondBr(FnCond, IfThenBB, IfElseBB);
497
498 // Create kernel registration code.
499 Builder.SetInsertPoint(IfThenBB);
500 Builder.CreateCall(
501 RegFunc,
502 {RegGlobalsFn->arg_begin(), Addr, Name, Name,
506 ConstantPointerNull::get(Int32PtrTy)});
507 Builder.CreateBr(IfEndBB);
508 Builder.SetInsertPoint(IfElseBB);
509
510 auto *Switch = Builder.CreateSwitch(Type, IfEndBB);
511 // Create global variable registration code.
512 Builder.SetInsertPoint(SwGlobalBB);
513 Builder.CreateCall(RegVar,
514 {RegGlobalsFn->arg_begin(), Addr, Name, Name, Extern, Size,
515 Const, ConstantInt::get(Type::getInt32Ty(C), 0)});
516 Builder.CreateBr(IfEndBB);
517 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalEntry),
518 SwGlobalBB);
519
520 // Create managed variable registration code.
521 Builder.SetInsertPoint(SwManagedBB);
522 Builder.CreateCall(RegManagedVar, {RegGlobalsFn->arg_begin(), AuxAddr, Addr,
523 Name, Size, Data});
524 Builder.CreateBr(IfEndBB);
525 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalManagedEntry),
526 SwManagedBB);
527 // Create surface variable registration code.
528 Builder.SetInsertPoint(SwSurfaceBB);
529 if (EmitSurfacesAndTextures)
530 Builder.CreateCall(RegSurface, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
531 Data, Extern});
532 Builder.CreateBr(IfEndBB);
533 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalSurfaceEntry),
534 SwSurfaceBB);
535
536 // Create texture variable registration code.
537 Builder.SetInsertPoint(SwTextureBB);
538 if (EmitSurfacesAndTextures)
539 Builder.CreateCall(RegTexture, {RegGlobalsFn->arg_begin(), Addr, Name, Name,
540 Data, Normalized, Extern});
541 Builder.CreateBr(IfEndBB);
542 Switch->addCase(Builder.getInt32(llvm::offloading::OffloadGlobalTextureEntry),
543 SwTextureBB);
544
545 Builder.SetInsertPoint(IfEndBB);
546 auto *NewEntry = Builder.CreateInBoundsGEP(
547 offloading::getEntryTy(M), Entry, ConstantInt::get(getSizeTTy(M), 1));
548 auto *Cmp = Builder.CreateICmpEQ(NewEntry, EntriesE);
549 Entry->addIncoming(EntriesB, &RegGlobalsFn->getEntryBlock());
550 Entry->addIncoming(NewEntry, IfEndBB);
551 Builder.CreateCondBr(Cmp, ExitBB, EntryBB);
552 Builder.SetInsertPoint(ExitBB);
553 Builder.CreateRetVoid();
554
555 return RegGlobalsFn;
556}
557
558// Create the constructor and destructor to register the fatbinary with the CUDA
559// runtime.
560void createRegisterFatbinFunction(Module &M, GlobalVariable *FatbinDesc,
561 bool IsHIP, EntryArrayTy EntryArray,
562 StringRef Suffix,
563 bool EmitSurfacesAndTextures) {
564 LLVMContext &C = M.getContext();
565 auto *CtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
566 auto *CtorFunc = Function::Create(
568 (IsHIP ? ".hip.fatbin_reg" : ".cuda.fatbin_reg") + Suffix, &M);
569 CtorFunc->setSection(getStartupSection(M.getTargetTriple()));
570
571 auto *DtorFuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
572 auto *DtorFunc = Function::Create(
574 (IsHIP ? ".hip.fatbin_unreg" : ".cuda.fatbin_unreg") + Suffix, &M);
575 DtorFunc->setSection(getStartupSection(M.getTargetTriple()));
576
577 auto *PtrTy = PointerType::getUnqual(C);
578
579 // Get the __cudaRegisterFatBinary function declaration.
580 auto *RegFatTy = FunctionType::get(PtrTy, PtrTy, /*isVarArg=*/false);
581 FunctionCallee RegFatbin = M.getOrInsertFunction(
582 IsHIP ? "__hipRegisterFatBinary" : "__cudaRegisterFatBinary", RegFatTy);
583 // Get the __cudaRegisterFatBinaryEnd function declaration.
584 auto *RegFatEndTy =
585 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
586 FunctionCallee RegFatbinEnd =
587 M.getOrInsertFunction("__cudaRegisterFatBinaryEnd", RegFatEndTy);
588 // Get the __cudaUnregisterFatBinary function declaration.
589 auto *UnregFatTy =
590 FunctionType::get(Type::getVoidTy(C), PtrTy, /*isVarArg=*/false);
591 FunctionCallee UnregFatbin = M.getOrInsertFunction(
592 IsHIP ? "__hipUnregisterFatBinary" : "__cudaUnregisterFatBinary",
593 UnregFatTy);
594
595 auto *AtExitTy =
596 FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
597 FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
598
599 auto *BinaryHandleGlobal = new llvm::GlobalVariable(
600 M, PtrTy, false, llvm::GlobalValue::InternalLinkage,
602 (IsHIP ? ".hip.binary_handle" : ".cuda.binary_handle") + Suffix);
603
604 // Create the constructor to register this image with the runtime.
605 IRBuilder<> CtorBuilder(BasicBlock::Create(C, "entry", CtorFunc));
606 CallInst *Handle = CtorBuilder.CreateCall(
607 RegFatbin,
609 CtorBuilder.CreateAlignedStore(
610 Handle, BinaryHandleGlobal,
611 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
612 CtorBuilder.CreateCall(createRegisterGlobalsFunction(M, IsHIP, EntryArray,
613 Suffix,
614 EmitSurfacesAndTextures),
615 Handle);
616 if (!IsHIP)
617 CtorBuilder.CreateCall(RegFatbinEnd, Handle);
618 CtorBuilder.CreateCall(AtExit, DtorFunc);
619 CtorBuilder.CreateRetVoid();
620
621 // Create the destructor to unregister the image with the runtime. We cannot
622 // use a standard global destructor after CUDA 9.2 so this must be called by
623 // `atexit()` instead.
624 IRBuilder<> DtorBuilder(BasicBlock::Create(C, "entry", DtorFunc));
625 LoadInst *BinaryHandle = DtorBuilder.CreateAlignedLoad(
626 PtrTy, BinaryHandleGlobal,
627 Align(M.getDataLayout().getPointerTypeSize(PtrTy)));
628 DtorBuilder.CreateCall(UnregFatbin, BinaryHandle);
629 DtorBuilder.CreateRetVoid();
630
631 // Add this function to constructors.
632 appendToGlobalCtors(M, CtorFunc, /*Priority=*/101);
633}
634
635/// SYCLWrapper helper class that creates all LLVM IRs wrapping given images.
636class SYCLWrapper {
637public:
638 SYCLWrapper(Module &M, const SYCLJITOptions &Options)
639 : M(M), C(M.getContext()), Options(Options) {}
640
641 /// Embeds \p Buffer (a raw OffloadBinary) as a global constant and returns
642 /// a pair of (Start, Size), where Start points to the beginning of the
643 /// embedded data and Size is its length in bytes.
644 std::pair<Constant *, Constant *> embedBinary(ArrayRef<char> Buffer) {
645 Constant *Arr = ConstantDataArray::get(C, Buffer);
646 GlobalVariable *BinaryGV = new GlobalVariable(
647 M, Arr->getType(), /*isConstant=*/true, GlobalValue::InternalLinkage,
648 Arr, ".sycl_offloading.binary");
649 BinaryGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
650 BinaryGV->setSection(".llvm.offloading");
651
652 IntegerType *Int64Ty = Type::getInt64Ty(C);
653 Constant *Zero = ConstantInt::get(Int64Ty, 0);
654 Constant *Size = ConstantInt::get(Int64Ty, Buffer.size());
656 BinaryGV->getValueType(), BinaryGV, ArrayRef<Constant *>{Zero, Zero});
657 return {Start, Size};
658 }
659
660 void createRegisterFatbinFunction(Constant *Start, Constant *Size) {
661 FunctionType *FuncTy =
662 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
664 Twine("sycl") + ".descriptor_reg", &M);
665 Func->setSection(getStartupSection(M.getTargetTriple()));
666
667 PointerType *PtrTy = PointerType::getUnqual(C);
668 IntegerType *Int64Ty = Type::getInt64Ty(C);
669 FunctionType *RegFuncTy =
670 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
671 /*isVarArg=*/false);
672 FunctionCallee RegFuncC =
673 M.getOrInsertFunction("__sycl_register_lib", RegFuncTy);
674
675 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
676 Builder.CreateCall(RegFuncC, {Start, Size});
677 Builder.CreateRetVoid();
678
679 appendToGlobalCtors(M, Func, /*Priority*/ 1);
680 }
681
682 void createUnregisterFunction(Constant *Start, Constant *Size) {
683 FunctionType *FuncTy =
684 FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
686 "sycl.descriptor_unreg", &M);
687 Func->setSection(getStartupSection(M.getTargetTriple()));
688
689 PointerType *PtrTy = PointerType::getUnqual(C);
690 IntegerType *Int64Ty = Type::getInt64Ty(C);
691 FunctionType *UnRegFuncTy =
692 FunctionType::get(Type::getVoidTy(C), {PtrTy, Int64Ty},
693 /*isVarArg=*/false);
694 FunctionCallee UnRegFuncC =
695 M.getOrInsertFunction("__sycl_unregister_lib", UnRegFuncTy);
696
697 IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
698 Builder.CreateCall(UnRegFuncC, {Start, Size});
699 Builder.CreateRetVoid();
700
701 appendToGlobalDtors(M, Func, /*Priority*/ 1);
702 }
703
704private:
705 Module &M;
706 LLVMContext &C;
707 SYCLJITOptions Options;
708}; // end of SYCLWrapper
709
710} // namespace
711
713 EntryArrayTy EntryArray,
714 llvm::StringRef Suffix, bool Relocatable) {
716 createBinDesc(M, Images, EntryArray, Suffix, Relocatable);
717 if (!Desc)
719 "No binary descriptors created.");
720 createRegisterFunction(M, Desc, Suffix);
721 return Error::success();
722}
723
725 EntryArrayTy EntryArray,
726 llvm::StringRef Suffix,
727 bool EmitSurfacesAndTextures) {
728 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/false, Suffix);
729 if (!Desc)
731 "No fatbin section created.");
732
733 createRegisterFatbinFunction(M, Desc, /*IsHip=*/false, EntryArray, Suffix,
734 EmitSurfacesAndTextures);
735 return Error::success();
736}
737
739 EntryArrayTy EntryArray, llvm::StringRef Suffix,
740 bool EmitSurfacesAndTextures) {
741 GlobalVariable *Desc = createFatbinDesc(M, Image, /*IsHip=*/true, Suffix);
742 if (!Desc)
744 "No fatbin section created.");
745
746 createRegisterFatbinFunction(M, Desc, /*IsHip=*/true, EntryArray, Suffix,
747 EmitSurfacesAndTextures);
748 return Error::success();
749}
750
753 SYCLWrapper W(M, Options);
754 auto [Start, Size] = W.embedBinary(Buffer);
755 W.createRegisterFatbinFunction(Start, Size);
756 W.createUnregisterFunction(Start, Size);
757 return Error::success();
758}
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
Machine Check Debug Module
#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:2893
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:679
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:28
LLVM_ABI llvm::Error wrapSYCLBinaries(llvm::Module &M, llvm::ArrayRef< char > Buffer, SYCLJITOptions Options=SYCLJITOptions())
Wraps OffloadBinaries in the given Buffers into the module M as global symbols and registers the imag...
@ 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 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< GlobalVariable *, GlobalVariable * > 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.
LLVM_ABI void appendToGlobalDtors(Module &M, Function *F, int Priority, Constant *Data=nullptr)
Same as appendToGlobalCtors(), but for global dtors.
@ 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