1//===- Utility.cpp ------ Collection of generic offloading utilities ------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/Frontend/Offloading/Utility.h"
10#include "llvm/IR/Constants.h"
11#include "llvm/IR/GlobalValue.h"
12#include "llvm/IR/GlobalVariable.h"
13#include "llvm/IR/Value.h"
14#include "llvm/Transforms/Utils/ModuleUtils.h"
15
16using namespace llvm;
17using namespace llvm::offloading;
18
19StructType *offloading::getEntryTy(Module &M) {
20 LLVMContext &C = M.getContext();
21 StructType *EntryTy =
22 StructType::getTypeByName(C, Name: "struct.__tgt_offload_entry");
23 if (!EntryTy)
24 EntryTy = StructType::create(
25 Name: "struct.__tgt_offload_entry", elt1: PointerType::getUnqual(C),
26 elts: PointerType::getUnqual(C), elts: M.getDataLayout().getIntPtrType(C),
27 elts: Type::getInt32Ty(C), elts: Type::getInt32Ty(C));
28 return EntryTy;
29}
30
31// TODO: Rework this interface to be more generic.
32std::pair<Constant *, GlobalVariable *>
33offloading::getOffloadingEntryInitializer(Module &M, Constant *Addr,
34 StringRef Name, uint64_t Size,
35 int32_t Flags, int32_t Data) {
36 llvm::Triple Triple(M.getTargetTriple());
37 Type *Int8PtrTy = PointerType::getUnqual(C&: M.getContext());
38 Type *Int32Ty = Type::getInt32Ty(C&: M.getContext());
39 Type *SizeTy = M.getDataLayout().getIntPtrType(C&: M.getContext());
40
41 Constant *AddrName = ConstantDataArray::getString(Context&: M.getContext(), Initializer: Name);
42
43 StringRef Prefix =
44 Triple.isNVPTX() ? "$offloading$entry_name" : ".offloading.entry_name";
45
46 // Create the constant string used to look up the symbol in the device.
47 auto *Str =
48 new GlobalVariable(M, AddrName->getType(), /*isConstant=*/true,
49 GlobalValue::InternalLinkage, AddrName, Prefix);
50 Str->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
51
52 // Construct the offloading entry.
53 Constant *EntryData[] = {
54 ConstantExpr::getPointerBitCastOrAddrSpaceCast(C: Addr, Ty: Int8PtrTy),
55 ConstantExpr::getPointerBitCastOrAddrSpaceCast(C: Str, Ty: Int8PtrTy),
56 ConstantInt::get(Ty: SizeTy, V: Size),
57 ConstantInt::get(Ty: Int32Ty, V: Flags),
58 ConstantInt::get(Ty: Int32Ty, V: Data),
59 };
60 Constant *EntryInitializer = ConstantStruct::get(T: getEntryTy(M), V: EntryData);
61 return {EntryInitializer, Str};
62}
63
64void offloading::emitOffloadingEntry(Module &M, Constant *Addr, StringRef Name,
65 uint64_t Size, int32_t Flags, int32_t Data,
66 StringRef SectionName) {
67 llvm::Triple Triple(M.getTargetTriple());
68
69 auto [EntryInitializer, NameGV] =
70 getOffloadingEntryInitializer(M, Addr, Name, Size, Flags, Data);
71
72 StringRef Prefix =
73 Triple.isNVPTX() ? "$offloading$entry$" : ".offloading.entry.";
74 auto *Entry = new GlobalVariable(
75 M, getEntryTy(M),
76 /*isConstant=*/true, GlobalValue::WeakAnyLinkage, EntryInitializer,
77 Prefix + Name, nullptr, GlobalValue::NotThreadLocal,
78 M.getDataLayout().getDefaultGlobalsAddressSpace());
79
80 // The entry has to be created in the section the linker expects it to be.
81 if (Triple.isOSBinFormatCOFF())
82 Entry->setSection((SectionName + "$OE").str());
83 else
84 Entry->setSection(SectionName);
85 Entry->setAlignment(Align(1));
86}
87
88std::pair<GlobalVariable *, GlobalVariable *>
89offloading::getOffloadEntryArray(Module &M, StringRef SectionName) {
90 llvm::Triple Triple(M.getTargetTriple());
91
92 auto *ZeroInitilaizer =
93 ConstantAggregateZero::get(Ty: ArrayType::get(ElementType: getEntryTy(M), NumElements: 0u));
94 auto *EntryInit = Triple.isOSBinFormatCOFF() ? ZeroInitilaizer : nullptr;
95 auto *EntryType = ArrayType::get(ElementType: getEntryTy(M), NumElements: 0);
96 auto Linkage = Triple.isOSBinFormatCOFF() ? GlobalValue::WeakODRLinkage
97 : GlobalValue::ExternalLinkage;
98
99 auto *EntriesB =
100 new GlobalVariable(M, EntryType, /*isConstant=*/true, Linkage, EntryInit,
101 "__start_" + SectionName);
102 EntriesB->setVisibility(GlobalValue::HiddenVisibility);
103 auto *EntriesE =
104 new GlobalVariable(M, EntryType, /*isConstant=*/true, Linkage, EntryInit,
105 "__stop_" + SectionName);
106 EntriesE->setVisibility(GlobalValue::HiddenVisibility);
107
108 if (Triple.isOSBinFormatELF()) {
109 // We assume that external begin/end symbols that we have created above will
110 // be defined by the linker. This is done whenever a section name with a
111 // valid C-identifier is present. We define a dummy variable here to force
112 // the linker to always provide these symbols.
113 auto *DummyEntry = new GlobalVariable(
114 M, ZeroInitilaizer->getType(), true, GlobalVariable::InternalLinkage,
115 ZeroInitilaizer, "__dummy." + SectionName);
116 DummyEntry->setSection(SectionName);
117 appendToCompilerUsed(M, Values: DummyEntry);
118 } else {
119 // The COFF linker will merge sections containing a '$' together into a
120 // single section. The order of entries in this section will be sorted
121 // alphabetically by the characters following the '$' in the name. Set the
122 // sections here to ensure that the beginning and end symbols are sorted.
123 EntriesB->setSection((SectionName + "$OA").str());
124 EntriesE->setSection((SectionName + "$OZ").str());
125 }
126
127 return std::make_pair(x&: EntriesB, y&: EntriesE);
128}
129