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 | |
16 | using namespace llvm; |
17 | using namespace llvm::offloading; |
18 | |
19 | StructType *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. |
32 | std::pair<Constant *, GlobalVariable *> |
33 | offloading::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 | |
64 | void 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 | |
88 | std::pair<GlobalVariable *, GlobalVariable *> |
89 | offloading::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 | |