1 //===--- SPIRVUtils.cpp ---- SPIR-V Utility Functions -----------*- 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 //
9 // This file contains miscellaneous utility functions.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "SPIRVUtils.h"
14 #include "MCTargetDesc/SPIRVBaseInfo.h"
15 #include "SPIRV.h"
16 #include "SPIRVInstrInfo.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
19 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
20 #include "llvm/CodeGen/MachineInstr.h"
21 #include "llvm/CodeGen/MachineInstrBuilder.h"
22 #include "llvm/Demangle/Demangle.h"
23 #include "llvm/IR/IntrinsicsSPIRV.h"
24
25 namespace llvm {
26
27 // The following functions are used to add these string literals as a series of
28 // 32-bit integer operands with the correct format, and unpack them if necessary
29 // when making string comparisons in compiler passes.
30 // SPIR-V requires null-terminated UTF-8 strings padded to 32-bit alignment.
convertCharsToWord(const StringRef & Str,unsigned i)31 static uint32_t convertCharsToWord(const StringRef &Str, unsigned i) {
32 uint32_t Word = 0u; // Build up this 32-bit word from 4 8-bit chars.
33 for (unsigned WordIndex = 0; WordIndex < 4; ++WordIndex) {
34 unsigned StrIndex = i + WordIndex;
35 uint8_t CharToAdd = 0; // Initilize char as padding/null.
36 if (StrIndex < Str.size()) { // If it's within the string, get a real char.
37 CharToAdd = Str[StrIndex];
38 }
39 Word |= (CharToAdd << (WordIndex * 8));
40 }
41 return Word;
42 }
43
44 // Get length including padding and null terminator.
getPaddedLen(const StringRef & Str)45 static size_t getPaddedLen(const StringRef &Str) {
46 const size_t Len = Str.size() + 1;
47 return (Len % 4 == 0) ? Len : Len + (4 - (Len % 4));
48 }
49
addStringImm(const StringRef & Str,MCInst & Inst)50 void addStringImm(const StringRef &Str, MCInst &Inst) {
51 const size_t PaddedLen = getPaddedLen(Str);
52 for (unsigned i = 0; i < PaddedLen; i += 4) {
53 // Add an operand for the 32-bits of chars or padding.
54 Inst.addOperand(MCOperand::createImm(convertCharsToWord(Str, i)));
55 }
56 }
57
addStringImm(const StringRef & Str,MachineInstrBuilder & MIB)58 void addStringImm(const StringRef &Str, MachineInstrBuilder &MIB) {
59 const size_t PaddedLen = getPaddedLen(Str);
60 for (unsigned i = 0; i < PaddedLen; i += 4) {
61 // Add an operand for the 32-bits of chars or padding.
62 MIB.addImm(convertCharsToWord(Str, i));
63 }
64 }
65
addStringImm(const StringRef & Str,IRBuilder<> & B,std::vector<Value * > & Args)66 void addStringImm(const StringRef &Str, IRBuilder<> &B,
67 std::vector<Value *> &Args) {
68 const size_t PaddedLen = getPaddedLen(Str);
69 for (unsigned i = 0; i < PaddedLen; i += 4) {
70 // Add a vector element for the 32-bits of chars or padding.
71 Args.push_back(B.getInt32(convertCharsToWord(Str, i)));
72 }
73 }
74
getStringImm(const MachineInstr & MI,unsigned StartIndex)75 std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) {
76 return getSPIRVStringOperand(MI, StartIndex);
77 }
78
addNumImm(const APInt & Imm,MachineInstrBuilder & MIB)79 void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB) {
80 const auto Bitwidth = Imm.getBitWidth();
81 if (Bitwidth == 1)
82 return; // Already handled
83 else if (Bitwidth <= 32) {
84 MIB.addImm(Imm.getZExtValue());
85 return;
86 } else if (Bitwidth <= 64) {
87 uint64_t FullImm = Imm.getZExtValue();
88 uint32_t LowBits = FullImm & 0xffffffff;
89 uint32_t HighBits = (FullImm >> 32) & 0xffffffff;
90 MIB.addImm(LowBits).addImm(HighBits);
91 return;
92 }
93 report_fatal_error("Unsupported constant bitwidth");
94 }
95
buildOpName(Register Target,const StringRef & Name,MachineIRBuilder & MIRBuilder)96 void buildOpName(Register Target, const StringRef &Name,
97 MachineIRBuilder &MIRBuilder) {
98 if (!Name.empty()) {
99 auto MIB = MIRBuilder.buildInstr(SPIRV::OpName).addUse(Target);
100 addStringImm(Name, MIB);
101 }
102 }
103
finishBuildOpDecorate(MachineInstrBuilder & MIB,const std::vector<uint32_t> & DecArgs,StringRef StrImm)104 static void finishBuildOpDecorate(MachineInstrBuilder &MIB,
105 const std::vector<uint32_t> &DecArgs,
106 StringRef StrImm) {
107 if (!StrImm.empty())
108 addStringImm(StrImm, MIB);
109 for (const auto &DecArg : DecArgs)
110 MIB.addImm(DecArg);
111 }
112
buildOpDecorate(Register Reg,MachineIRBuilder & MIRBuilder,SPIRV::Decoration::Decoration Dec,const std::vector<uint32_t> & DecArgs,StringRef StrImm)113 void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder,
114 SPIRV::Decoration::Decoration Dec,
115 const std::vector<uint32_t> &DecArgs, StringRef StrImm) {
116 auto MIB = MIRBuilder.buildInstr(SPIRV::OpDecorate)
117 .addUse(Reg)
118 .addImm(static_cast<uint32_t>(Dec));
119 finishBuildOpDecorate(MIB, DecArgs, StrImm);
120 }
121
buildOpDecorate(Register Reg,MachineInstr & I,const SPIRVInstrInfo & TII,SPIRV::Decoration::Decoration Dec,const std::vector<uint32_t> & DecArgs,StringRef StrImm)122 void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII,
123 SPIRV::Decoration::Decoration Dec,
124 const std::vector<uint32_t> &DecArgs, StringRef StrImm) {
125 MachineBasicBlock &MBB = *I.getParent();
126 auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpDecorate))
127 .addUse(Reg)
128 .addImm(static_cast<uint32_t>(Dec));
129 finishBuildOpDecorate(MIB, DecArgs, StrImm);
130 }
131
132 // TODO: maybe the following two functions should be handled in the subtarget
133 // to allow for different OpenCL vs Vulkan handling.
storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC)134 unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC) {
135 switch (SC) {
136 case SPIRV::StorageClass::Function:
137 return 0;
138 case SPIRV::StorageClass::CrossWorkgroup:
139 return 1;
140 case SPIRV::StorageClass::UniformConstant:
141 return 2;
142 case SPIRV::StorageClass::Workgroup:
143 return 3;
144 case SPIRV::StorageClass::Generic:
145 return 4;
146 case SPIRV::StorageClass::Input:
147 return 7;
148 default:
149 llvm_unreachable("Unable to get address space id");
150 }
151 }
152
153 SPIRV::StorageClass::StorageClass
addressSpaceToStorageClass(unsigned AddrSpace)154 addressSpaceToStorageClass(unsigned AddrSpace) {
155 switch (AddrSpace) {
156 case 0:
157 return SPIRV::StorageClass::Function;
158 case 1:
159 return SPIRV::StorageClass::CrossWorkgroup;
160 case 2:
161 return SPIRV::StorageClass::UniformConstant;
162 case 3:
163 return SPIRV::StorageClass::Workgroup;
164 case 4:
165 return SPIRV::StorageClass::Generic;
166 case 7:
167 return SPIRV::StorageClass::Input;
168 default:
169 llvm_unreachable("Unknown address space");
170 }
171 }
172
173 SPIRV::MemorySemantics::MemorySemantics
getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC)174 getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC) {
175 switch (SC) {
176 case SPIRV::StorageClass::StorageBuffer:
177 case SPIRV::StorageClass::Uniform:
178 return SPIRV::MemorySemantics::UniformMemory;
179 case SPIRV::StorageClass::Workgroup:
180 return SPIRV::MemorySemantics::WorkgroupMemory;
181 case SPIRV::StorageClass::CrossWorkgroup:
182 return SPIRV::MemorySemantics::CrossWorkgroupMemory;
183 case SPIRV::StorageClass::AtomicCounter:
184 return SPIRV::MemorySemantics::AtomicCounterMemory;
185 case SPIRV::StorageClass::Image:
186 return SPIRV::MemorySemantics::ImageMemory;
187 default:
188 return SPIRV::MemorySemantics::None;
189 }
190 }
191
getMemSemantics(AtomicOrdering Ord)192 SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord) {
193 switch (Ord) {
194 case AtomicOrdering::Acquire:
195 return SPIRV::MemorySemantics::Acquire;
196 case AtomicOrdering::Release:
197 return SPIRV::MemorySemantics::Release;
198 case AtomicOrdering::AcquireRelease:
199 return SPIRV::MemorySemantics::AcquireRelease;
200 case AtomicOrdering::SequentiallyConsistent:
201 return SPIRV::MemorySemantics::SequentiallyConsistent;
202 case AtomicOrdering::Unordered:
203 case AtomicOrdering::Monotonic:
204 case AtomicOrdering::NotAtomic:
205 return SPIRV::MemorySemantics::None;
206 }
207 llvm_unreachable(nullptr);
208 }
209
getDefInstrMaybeConstant(Register & ConstReg,const MachineRegisterInfo * MRI)210 MachineInstr *getDefInstrMaybeConstant(Register &ConstReg,
211 const MachineRegisterInfo *MRI) {
212 MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg);
213 if (auto *GI = dyn_cast<GIntrinsic>(ConstInstr)) {
214 if (GI->is(Intrinsic::spv_track_constant)) {
215 ConstReg = ConstInstr->getOperand(2).getReg();
216 return MRI->getVRegDef(ConstReg);
217 }
218 } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) {
219 ConstReg = ConstInstr->getOperand(1).getReg();
220 return MRI->getVRegDef(ConstReg);
221 }
222 return MRI->getVRegDef(ConstReg);
223 }
224
getIConstVal(Register ConstReg,const MachineRegisterInfo * MRI)225 uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) {
226 const MachineInstr *MI = getDefInstrMaybeConstant(ConstReg, MRI);
227 assert(MI && MI->getOpcode() == TargetOpcode::G_CONSTANT);
228 return MI->getOperand(1).getCImm()->getValue().getZExtValue();
229 }
230
isSpvIntrinsic(const MachineInstr & MI,Intrinsic::ID IntrinsicID)231 bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID) {
232 if (const auto *GI = dyn_cast<GIntrinsic>(&MI))
233 return GI->is(IntrinsicID);
234 return false;
235 }
236
getMDOperandAsType(const MDNode * N,unsigned I)237 Type *getMDOperandAsType(const MDNode *N, unsigned I) {
238 return cast<ValueAsMetadata>(N->getOperand(I))->getType();
239 }
240
241 // The set of names is borrowed from the SPIR-V translator.
242 // TODO: may be implemented in SPIRVBuiltins.td.
isPipeOrAddressSpaceCastBI(const StringRef MangledName)243 static bool isPipeOrAddressSpaceCastBI(const StringRef MangledName) {
244 return MangledName == "write_pipe_2" || MangledName == "read_pipe_2" ||
245 MangledName == "write_pipe_2_bl" || MangledName == "read_pipe_2_bl" ||
246 MangledName == "write_pipe_4" || MangledName == "read_pipe_4" ||
247 MangledName == "reserve_write_pipe" ||
248 MangledName == "reserve_read_pipe" ||
249 MangledName == "commit_write_pipe" ||
250 MangledName == "commit_read_pipe" ||
251 MangledName == "work_group_reserve_write_pipe" ||
252 MangledName == "work_group_reserve_read_pipe" ||
253 MangledName == "work_group_commit_write_pipe" ||
254 MangledName == "work_group_commit_read_pipe" ||
255 MangledName == "get_pipe_num_packets_ro" ||
256 MangledName == "get_pipe_max_packets_ro" ||
257 MangledName == "get_pipe_num_packets_wo" ||
258 MangledName == "get_pipe_max_packets_wo" ||
259 MangledName == "sub_group_reserve_write_pipe" ||
260 MangledName == "sub_group_reserve_read_pipe" ||
261 MangledName == "sub_group_commit_write_pipe" ||
262 MangledName == "sub_group_commit_read_pipe" ||
263 MangledName == "to_global" || MangledName == "to_local" ||
264 MangledName == "to_private";
265 }
266
isEnqueueKernelBI(const StringRef MangledName)267 static bool isEnqueueKernelBI(const StringRef MangledName) {
268 return MangledName == "__enqueue_kernel_basic" ||
269 MangledName == "__enqueue_kernel_basic_events" ||
270 MangledName == "__enqueue_kernel_varargs" ||
271 MangledName == "__enqueue_kernel_events_varargs";
272 }
273
isKernelQueryBI(const StringRef MangledName)274 static bool isKernelQueryBI(const StringRef MangledName) {
275 return MangledName == "__get_kernel_work_group_size_impl" ||
276 MangledName == "__get_kernel_sub_group_count_for_ndrange_impl" ||
277 MangledName == "__get_kernel_max_sub_group_size_for_ndrange_impl" ||
278 MangledName == "__get_kernel_preferred_work_group_size_multiple_impl";
279 }
280
isNonMangledOCLBuiltin(StringRef Name)281 static bool isNonMangledOCLBuiltin(StringRef Name) {
282 if (!Name.starts_with("__"))
283 return false;
284
285 return isEnqueueKernelBI(Name) || isKernelQueryBI(Name) ||
286 isPipeOrAddressSpaceCastBI(Name.drop_front(2)) ||
287 Name == "__translate_sampler_initializer";
288 }
289
getOclOrSpirvBuiltinDemangledName(StringRef Name)290 std::string getOclOrSpirvBuiltinDemangledName(StringRef Name) {
291 bool IsNonMangledOCL = isNonMangledOCLBuiltin(Name);
292 bool IsNonMangledSPIRV = Name.starts_with("__spirv_");
293 bool IsMangled = Name.starts_with("_Z");
294
295 if (!IsNonMangledOCL && !IsNonMangledSPIRV && !IsMangled)
296 return std::string();
297
298 // Try to use the itanium demangler.
299 if (char *DemangledName = itaniumDemangle(Name.data())) {
300 std::string Result = DemangledName;
301 free(DemangledName);
302 return Result;
303 }
304 // Otherwise use simple demangling to return the function name.
305 if (IsNonMangledOCL || IsNonMangledSPIRV)
306 return Name.str();
307
308 // Autocheck C++, maybe need to do explicit check of the source language.
309 // OpenCL C++ built-ins are declared in cl namespace.
310 // TODO: consider using 'St' abbriviation for cl namespace mangling.
311 // Similar to ::std:: in C++.
312 size_t Start, Len = 0;
313 size_t DemangledNameLenStart = 2;
314 if (Name.starts_with("_ZN")) {
315 // Skip CV and ref qualifiers.
316 size_t NameSpaceStart = Name.find_first_not_of("rVKRO", 3);
317 // All built-ins are in the ::cl:: namespace.
318 if (Name.substr(NameSpaceStart, 11) != "2cl7__spirv")
319 return std::string();
320 DemangledNameLenStart = NameSpaceStart + 11;
321 }
322 Start = Name.find_first_not_of("0123456789", DemangledNameLenStart);
323 Name.substr(DemangledNameLenStart, Start - DemangledNameLenStart)
324 .getAsInteger(10, Len);
325 return Name.substr(Start, Len).str();
326 }
327
getTypedPtrEltType(const Type * Ty)328 const Type *getTypedPtrEltType(const Type *Ty) {
329 // TODO: This function requires updating following the opaque pointer
330 // migration.
331 return Ty;
332 }
333
hasBuiltinTypePrefix(StringRef Name)334 bool hasBuiltinTypePrefix(StringRef Name) {
335 if (Name.starts_with("opencl.") || Name.starts_with("spirv."))
336 return true;
337 return false;
338 }
339
isSpecialOpaqueType(const Type * Ty)340 bool isSpecialOpaqueType(const Type *Ty) {
341 const StructType *SType = dyn_cast<StructType>(getTypedPtrEltType(Ty));
342 if (SType && SType->hasName())
343 return hasBuiltinTypePrefix(SType->getName());
344
345 if (const TargetExtType *EType =
346 dyn_cast<TargetExtType>(getTypedPtrEltType(Ty)))
347 return hasBuiltinTypePrefix(EType->getName());
348
349 return false;
350 }
351 } // namespace llvm
352