LLVM 17.0.0git
SPIRVUtils.cpp
Go to the documentation of this file.
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"
15#include "SPIRV.h"
16#include "SPIRVInstrInfo.h"
17#include "llvm/ADT/StringRef.h"
22#include "llvm/IR/IntrinsicsSPIRV.h"
23
24namespace 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.
30static 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.
44static 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
49void 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.
54 }
55}
56
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
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
74std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) {
75 return getSPIRVStringOperand(MI, StartIndex);
76}
77
78void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB) {
79 const auto Bitwidth = Imm.getBitWidth();
80 if (Bitwidth == 1)
81 return; // Already handled
82 else if (Bitwidth <= 32) {
83 MIB.addImm(Imm.getZExtValue());
84 return;
85 } else if (Bitwidth <= 64) {
86 uint64_t FullImm = Imm.getZExtValue();
87 uint32_t LowBits = FullImm & 0xffffffff;
88 uint32_t HighBits = (FullImm >> 32) & 0xffffffff;
89 MIB.addImm(LowBits).addImm(HighBits);
90 return;
91 }
92 report_fatal_error("Unsupported constant bitwidth");
93}
94
96 MachineIRBuilder &MIRBuilder) {
97 if (!Name.empty()) {
98 auto MIB = MIRBuilder.buildInstr(SPIRV::OpName).addUse(Target);
99 addStringImm(Name, MIB);
100 }
101}
102
104 const std::vector<uint32_t> &DecArgs,
105 StringRef StrImm) {
106 if (!StrImm.empty())
107 addStringImm(StrImm, MIB);
108 for (const auto &DecArg : DecArgs)
109 MIB.addImm(DecArg);
110}
111
113 SPIRV::Decoration::Decoration Dec,
114 const std::vector<uint32_t> &DecArgs, StringRef StrImm) {
115 auto MIB = MIRBuilder.buildInstr(SPIRV::OpDecorate)
116 .addUse(Reg)
117 .addImm(static_cast<uint32_t>(Dec));
118 finishBuildOpDecorate(MIB, DecArgs, StrImm);
119}
120
122 SPIRV::Decoration::Decoration Dec,
123 const std::vector<uint32_t> &DecArgs, StringRef StrImm) {
124 MachineBasicBlock &MBB = *I.getParent();
125 auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpDecorate))
126 .addUse(Reg)
127 .addImm(static_cast<uint32_t>(Dec));
128 finishBuildOpDecorate(MIB, DecArgs, StrImm);
129}
130
131// TODO: maybe the following two functions should be handled in the subtarget
132// to allow for different OpenCL vs Vulkan handling.
133unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC) {
134 switch (SC) {
135 case SPIRV::StorageClass::Function:
136 return 0;
137 case SPIRV::StorageClass::CrossWorkgroup:
138 return 1;
139 case SPIRV::StorageClass::UniformConstant:
140 return 2;
141 case SPIRV::StorageClass::Workgroup:
142 return 3;
143 case SPIRV::StorageClass::Generic:
144 return 4;
145 case SPIRV::StorageClass::Input:
146 return 7;
147 default:
148 llvm_unreachable("Unable to get address space id");
149 }
150}
151
152SPIRV::StorageClass::StorageClass
153addressSpaceToStorageClass(unsigned AddrSpace) {
154 switch (AddrSpace) {
155 case 0:
156 return SPIRV::StorageClass::Function;
157 case 1:
158 return SPIRV::StorageClass::CrossWorkgroup;
159 case 2:
160 return SPIRV::StorageClass::UniformConstant;
161 case 3:
162 return SPIRV::StorageClass::Workgroup;
163 case 4:
164 return SPIRV::StorageClass::Generic;
165 case 7:
166 return SPIRV::StorageClass::Input;
167 default:
168 llvm_unreachable("Unknown address space");
169 }
170}
171
172SPIRV::MemorySemantics::MemorySemantics
173getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC) {
174 switch (SC) {
175 case SPIRV::StorageClass::StorageBuffer:
176 case SPIRV::StorageClass::Uniform:
177 return SPIRV::MemorySemantics::UniformMemory;
178 case SPIRV::StorageClass::Workgroup:
179 return SPIRV::MemorySemantics::WorkgroupMemory;
180 case SPIRV::StorageClass::CrossWorkgroup:
181 return SPIRV::MemorySemantics::CrossWorkgroupMemory;
182 case SPIRV::StorageClass::AtomicCounter:
183 return SPIRV::MemorySemantics::AtomicCounterMemory;
184 case SPIRV::StorageClass::Image:
185 return SPIRV::MemorySemantics::ImageMemory;
186 default:
187 return SPIRV::MemorySemantics::None;
188 }
189}
190
191SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord) {
192 switch (Ord) {
194 return SPIRV::MemorySemantics::Acquire;
196 return SPIRV::MemorySemantics::Release;
198 return SPIRV::MemorySemantics::AcquireRelease;
200 return SPIRV::MemorySemantics::SequentiallyConsistent;
204 return SPIRV::MemorySemantics::None;
205 }
206 llvm_unreachable(nullptr);
207}
208
210 const MachineRegisterInfo *MRI) {
211 MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg);
212 if (ConstInstr->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
213 ConstInstr->getIntrinsicID() == Intrinsic::spv_track_constant) {
214 ConstReg = ConstInstr->getOperand(2).getReg();
215 ConstInstr = MRI->getVRegDef(ConstReg);
216 } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) {
217 ConstReg = ConstInstr->getOperand(1).getReg();
218 ConstInstr = MRI->getVRegDef(ConstReg);
219 }
220 return ConstInstr;
221}
222
224 const MachineInstr *MI = getDefInstrMaybeConstant(ConstReg, MRI);
225 assert(MI && MI->getOpcode() == TargetOpcode::G_CONSTANT);
226 return MI->getOperand(1).getCImm()->getValue().getZExtValue();
227}
228
230 return MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
231 MI.getIntrinsicID() == IntrinsicID;
232}
233
234Type *getMDOperandAsType(const MDNode *N, unsigned I) {
235 return cast<ValueAsMetadata>(N->getOperand(I))->getType();
236}
237
238// The set of names is borrowed from the SPIR-V translator.
239// TODO: may be implemented in SPIRVBuiltins.td.
240static bool isPipeOrAddressSpaceCastBI(const StringRef MangledName) {
241 return MangledName == "write_pipe_2" || MangledName == "read_pipe_2" ||
242 MangledName == "write_pipe_2_bl" || MangledName == "read_pipe_2_bl" ||
243 MangledName == "write_pipe_4" || MangledName == "read_pipe_4" ||
244 MangledName == "reserve_write_pipe" ||
245 MangledName == "reserve_read_pipe" ||
246 MangledName == "commit_write_pipe" ||
247 MangledName == "commit_read_pipe" ||
248 MangledName == "work_group_reserve_write_pipe" ||
249 MangledName == "work_group_reserve_read_pipe" ||
250 MangledName == "work_group_commit_write_pipe" ||
251 MangledName == "work_group_commit_read_pipe" ||
252 MangledName == "get_pipe_num_packets_ro" ||
253 MangledName == "get_pipe_max_packets_ro" ||
254 MangledName == "get_pipe_num_packets_wo" ||
255 MangledName == "get_pipe_max_packets_wo" ||
256 MangledName == "sub_group_reserve_write_pipe" ||
257 MangledName == "sub_group_reserve_read_pipe" ||
258 MangledName == "sub_group_commit_write_pipe" ||
259 MangledName == "sub_group_commit_read_pipe" ||
260 MangledName == "to_global" || MangledName == "to_local" ||
261 MangledName == "to_private";
262}
263
264static bool isEnqueueKernelBI(const StringRef MangledName) {
265 return MangledName == "__enqueue_kernel_basic" ||
266 MangledName == "__enqueue_kernel_basic_events" ||
267 MangledName == "__enqueue_kernel_varargs" ||
268 MangledName == "__enqueue_kernel_events_varargs";
269}
270
271static bool isKernelQueryBI(const StringRef MangledName) {
272 return MangledName == "__get_kernel_work_group_size_impl" ||
273 MangledName == "__get_kernel_sub_group_count_for_ndrange_impl" ||
274 MangledName == "__get_kernel_max_sub_group_size_for_ndrange_impl" ||
275 MangledName == "__get_kernel_preferred_work_group_size_multiple_impl";
276}
277
279 if (!Name.startswith("__"))
280 return false;
281
283 isPipeOrAddressSpaceCastBI(Name.drop_front(2)) ||
284 Name == "__translate_sampler_initializer";
285}
286
288 bool IsNonMangledOCL = isNonMangledOCLBuiltin(Name);
289 bool IsNonMangledSPIRV = Name.startswith("__spirv_");
290 bool IsMangled = Name.startswith("_Z");
291
292 if (!IsNonMangledOCL && !IsNonMangledSPIRV && !IsMangled)
293 return std::string();
294
295 // Try to use the itanium demangler.
296 if (char *DemangledName = itaniumDemangle(Name.data())) {
297 std::string Result = DemangledName;
298 free(DemangledName);
299 return Result;
300 }
301 // Otherwise use simple demangling to return the function name.
302 if (IsNonMangledOCL || IsNonMangledSPIRV)
303 return Name.str();
304
305 // Autocheck C++, maybe need to do explicit check of the source language.
306 // OpenCL C++ built-ins are declared in cl namespace.
307 // TODO: consider using 'St' abbriviation for cl namespace mangling.
308 // Similar to ::std:: in C++.
309 size_t Start, Len = 0;
310 size_t DemangledNameLenStart = 2;
311 if (Name.startswith("_ZN")) {
312 // Skip CV and ref qualifiers.
313 size_t NameSpaceStart = Name.find_first_not_of("rVKRO", 3);
314 // All built-ins are in the ::cl:: namespace.
315 if (Name.substr(NameSpaceStart, 11) != "2cl7__spirv")
316 return std::string();
317 DemangledNameLenStart = NameSpaceStart + 11;
318 }
319 Start = Name.find_first_not_of("0123456789", DemangledNameLenStart);
320 Name.substr(DemangledNameLenStart, Start - DemangledNameLenStart)
321 .getAsInteger(10, Len);
322 return Name.substr(Start, Len).str();
323}
324
325const Type *getTypedPtrEltType(const Type *Ty) {
326 auto PType = dyn_cast<PointerType>(Ty);
327 if (!PType || PType->isOpaque())
328 return Ty;
329 return PType->getNonOpaquePointerElementType();
330}
331
333 if (Name.starts_with("opencl.") || Name.starts_with("spirv."))
334 return true;
335 return false;
336}
337
338bool isSpecialOpaqueType(const Type *Ty) {
339 const StructType *SType = dyn_cast<StructType>(getTypedPtrEltType(Ty));
340 if (SType && SType->hasName())
341 return hasBuiltinTypePrefix(SType->getName());
342
343 if (const TargetExtType *EType =
344 dyn_cast<TargetExtType>(getTypedPtrEltType(Ty)))
345 return hasBuiltinTypePrefix(EType->getName());
346
347 return false;
348}
349} // namespace llvm
unsigned const MachineRegisterInfo * MRI
MachineBasicBlock & MBB
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
std::string Name
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition: MD5.cpp:58
This file declares the MachineIRBuilder class.
unsigned Reg
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
Class for arbitrary precision integers.
Definition: APInt.h:75
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition: IRBuilder.h:2564
Instances of this class represent a single low-level machine instruction.
Definition: MCInst.h:184
void addOperand(const MCOperand Op)
Definition: MCInst.h:210
static MCOperand createImm(int64_t Val)
Definition: MCInst.h:141
Metadata node.
Definition: Metadata.h:950
Helper class to build MachineInstr.
MachineInstrBuilder buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addUse(Register RegNo, unsigned Flags=0, unsigned SubReg=0) const
Add a virtual register use operand.
Representation of each machine instruction.
Definition: MachineInstr.h:68
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
Definition: MachineInstr.h:523
const MachineOperand & getOperand(unsigned i) const
Definition: MachineInstr.h:533
unsigned getIntrinsicID() const
Returns the Intrinsic::ID for this instruction.
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
constexpr bool empty() const
empty - Check if the string is empty.
Definition: StringRef.h:134
Class to represent struct types.
Definition: DerivedTypes.h:213
bool hasName() const
Return true if this is a named struct that has a non-empty name.
Definition: DerivedTypes.h:301
StringRef getName() const
Return the name for this struct type if it has an identity.
Definition: Type.cpp:651
Class to represent target extensions types, which are generally unintrospectable from target-independ...
Definition: DerivedTypes.h:750
Target - Wrapper for Target specific information.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
Type * getNonOpaquePointerElementType() const
Only use this method in code that is not reachable with opaque pointers, or part of deprecated method...
Definition: Type.h:423
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
void buildOpName(Register Target, const StringRef &Name, MachineIRBuilder &MIRBuilder)
Definition: SPIRVUtils.cpp:95
unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC)
Definition: SPIRVUtils.cpp:133
std::string getStringImm(const MachineInstr &MI, unsigned StartIndex)
Definition: SPIRVUtils.cpp:74
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
static void finishBuildOpDecorate(MachineInstrBuilder &MIB, const std::vector< uint32_t > &DecArgs, StringRef StrImm)
Definition: SPIRVUtils.cpp:103
static uint32_t convertCharsToWord(const StringRef &Str, unsigned i)
Definition: SPIRVUtils.cpp:30
void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB)
Definition: SPIRVUtils.cpp:78
uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI)
Definition: SPIRVUtils.cpp:223
SPIRV::MemorySemantics::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC)
Definition: SPIRVUtils.cpp:173
std::string getOclOrSpirvBuiltinDemangledName(StringRef Name)
Definition: SPIRVUtils.cpp:287
void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, SPIRV::Decoration::Decoration Dec, const std::vector< uint32_t > &DecArgs, StringRef StrImm)
Definition: SPIRVUtils.cpp:112
std::string getSPIRVStringOperand(const InstType &MI, unsigned StartIndex)
SPIRV::StorageClass::StorageClass addressSpaceToStorageClass(unsigned AddrSpace)
Definition: SPIRVUtils.cpp:153
bool isSpecialOpaqueType(const Type *Ty)
Definition: SPIRVUtils.cpp:338
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:145
bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID)
Definition: SPIRVUtils.cpp:229
static bool isNonMangledOCLBuiltin(StringRef Name)
Definition: SPIRVUtils.cpp:278
AtomicOrdering
Atomic ordering for LLVM's memory model.
static bool isPipeOrAddressSpaceCastBI(const StringRef MangledName)
Definition: SPIRVUtils.cpp:240
const Type * getTypedPtrEltType(const Type *Ty)
Definition: SPIRVUtils.cpp:325
MachineInstr * getDefInstrMaybeConstant(Register &ConstReg, const MachineRegisterInfo *MRI)
Definition: SPIRVUtils.cpp:209
Type * getMDOperandAsType(const MDNode *N, unsigned I)
Definition: SPIRVUtils.cpp:234
char * itaniumDemangle(std::string_view mangled_name)
Returns a non-NULL pointer to a NUL-terminated C style string that should be explicitly freed,...
static bool hasBuiltinTypePrefix(StringRef Name)
Definition: SPIRVUtils.cpp:332
static size_t getPaddedLen(const StringRef &Str)
Definition: SPIRVUtils.cpp:44
void addStringImm(const StringRef &Str, MCInst &Inst)
Definition: SPIRVUtils.cpp:49
static bool isKernelQueryBI(const StringRef MangledName)
Definition: SPIRVUtils.cpp:271
static bool isEnqueueKernelBI(const StringRef MangledName)
Definition: SPIRVUtils.cpp:264
SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord)
Definition: SPIRVUtils.cpp:191
#define N