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