LLVM 19.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 "SPIRVSubtarget.h"
18#include "llvm/ADT/StringRef.h"
24#include "llvm/IR/IntrinsicsSPIRV.h"
25
26namespace llvm {
27
28// The following functions are used to add these string literals as a series of
29// 32-bit integer operands with the correct format, and unpack them if necessary
30// when making string comparisons in compiler passes.
31// SPIR-V requires null-terminated UTF-8 strings padded to 32-bit alignment.
32static uint32_t convertCharsToWord(const StringRef &Str, unsigned i) {
33 uint32_t Word = 0u; // Build up this 32-bit word from 4 8-bit chars.
34 for (unsigned WordIndex = 0; WordIndex < 4; ++WordIndex) {
35 unsigned StrIndex = i + WordIndex;
36 uint8_t CharToAdd = 0; // Initilize char as padding/null.
37 if (StrIndex < Str.size()) { // If it's within the string, get a real char.
38 CharToAdd = Str[StrIndex];
39 }
40 Word |= (CharToAdd << (WordIndex * 8));
41 }
42 return Word;
43}
44
45// Get length including padding and null terminator.
46static size_t getPaddedLen(const StringRef &Str) {
47 const size_t Len = Str.size() + 1;
48 return (Len % 4 == 0) ? Len : Len + (4 - (Len % 4));
49}
50
51void addStringImm(const StringRef &Str, MCInst &Inst) {
52 const size_t PaddedLen = getPaddedLen(Str);
53 for (unsigned i = 0; i < PaddedLen; i += 4) {
54 // Add an operand for the 32-bits of chars or padding.
56 }
57}
58
60 const size_t PaddedLen = getPaddedLen(Str);
61 for (unsigned i = 0; i < PaddedLen; i += 4) {
62 // Add an operand for the 32-bits of chars or padding.
63 MIB.addImm(convertCharsToWord(Str, i));
64 }
65}
66
68 std::vector<Value *> &Args) {
69 const size_t PaddedLen = getPaddedLen(Str);
70 for (unsigned i = 0; i < PaddedLen; i += 4) {
71 // Add a vector element for the 32-bits of chars or padding.
72 Args.push_back(B.getInt32(convertCharsToWord(Str, i)));
73 }
74}
75
76std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) {
77 return getSPIRVStringOperand(MI, StartIndex);
78}
79
80void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB) {
81 const auto Bitwidth = Imm.getBitWidth();
82 if (Bitwidth == 1)
83 return; // Already handled
84 else if (Bitwidth <= 32) {
85 MIB.addImm(Imm.getZExtValue());
86 // Asm Printer needs this info to print floating-type correctly
87 if (Bitwidth == 16)
89 return;
90 } else if (Bitwidth <= 64) {
91 uint64_t FullImm = Imm.getZExtValue();
92 uint32_t LowBits = FullImm & 0xffffffff;
93 uint32_t HighBits = (FullImm >> 32) & 0xffffffff;
94 MIB.addImm(LowBits).addImm(HighBits);
95 return;
96 }
97 report_fatal_error("Unsupported constant bitwidth");
98}
99
101 MachineIRBuilder &MIRBuilder) {
102 if (!Name.empty()) {
103 auto MIB = MIRBuilder.buildInstr(SPIRV::OpName).addUse(Target);
104 addStringImm(Name, MIB);
105 }
106}
107
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
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
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.
138unsigned 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::DeviceOnlyINTEL:
151 return 5;
152 case SPIRV::StorageClass::HostOnlyINTEL:
153 return 6;
154 case SPIRV::StorageClass::Input:
155 return 7;
156 default:
157 report_fatal_error("Unable to get address space id");
158 }
159}
160
161SPIRV::StorageClass::StorageClass
162addressSpaceToStorageClass(unsigned AddrSpace, const SPIRVSubtarget &STI) {
163 switch (AddrSpace) {
164 case 0:
165 return SPIRV::StorageClass::Function;
166 case 1:
167 return SPIRV::StorageClass::CrossWorkgroup;
168 case 2:
169 return SPIRV::StorageClass::UniformConstant;
170 case 3:
171 return SPIRV::StorageClass::Workgroup;
172 case 4:
173 return SPIRV::StorageClass::Generic;
174 case 5:
175 return STI.canUseExtension(SPIRV::Extension::SPV_INTEL_usm_storage_classes)
176 ? SPIRV::StorageClass::DeviceOnlyINTEL
177 : SPIRV::StorageClass::CrossWorkgroup;
178 case 6:
179 return STI.canUseExtension(SPIRV::Extension::SPV_INTEL_usm_storage_classes)
180 ? SPIRV::StorageClass::HostOnlyINTEL
181 : SPIRV::StorageClass::CrossWorkgroup;
182 case 7:
183 return SPIRV::StorageClass::Input;
184 default:
185 report_fatal_error("Unknown address space");
186 }
187}
188
189SPIRV::MemorySemantics::MemorySemantics
190getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC) {
191 switch (SC) {
192 case SPIRV::StorageClass::StorageBuffer:
193 case SPIRV::StorageClass::Uniform:
194 return SPIRV::MemorySemantics::UniformMemory;
195 case SPIRV::StorageClass::Workgroup:
196 return SPIRV::MemorySemantics::WorkgroupMemory;
197 case SPIRV::StorageClass::CrossWorkgroup:
198 return SPIRV::MemorySemantics::CrossWorkgroupMemory;
199 case SPIRV::StorageClass::AtomicCounter:
200 return SPIRV::MemorySemantics::AtomicCounterMemory;
201 case SPIRV::StorageClass::Image:
202 return SPIRV::MemorySemantics::ImageMemory;
203 default:
204 return SPIRV::MemorySemantics::None;
205 }
206}
207
208SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord) {
209 switch (Ord) {
211 return SPIRV::MemorySemantics::Acquire;
213 return SPIRV::MemorySemantics::Release;
215 return SPIRV::MemorySemantics::AcquireRelease;
217 return SPIRV::MemorySemantics::SequentiallyConsistent;
221 return SPIRV::MemorySemantics::None;
222 }
223 llvm_unreachable(nullptr);
224}
225
227 const MachineRegisterInfo *MRI) {
228 MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg);
229 if (auto *GI = dyn_cast<GIntrinsic>(ConstInstr)) {
230 if (GI->is(Intrinsic::spv_track_constant)) {
231 ConstReg = ConstInstr->getOperand(2).getReg();
232 return MRI->getVRegDef(ConstReg);
233 }
234 } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) {
235 ConstReg = ConstInstr->getOperand(1).getReg();
236 return MRI->getVRegDef(ConstReg);
237 }
238 return MRI->getVRegDef(ConstReg);
239}
240
242 const MachineInstr *MI = getDefInstrMaybeConstant(ConstReg, MRI);
243 assert(MI && MI->getOpcode() == TargetOpcode::G_CONSTANT);
244 return MI->getOperand(1).getCImm()->getValue().getZExtValue();
245}
246
247bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID) {
248 if (const auto *GI = dyn_cast<GIntrinsic>(&MI))
249 return GI->is(IntrinsicID);
250 return false;
251}
252
253Type *getMDOperandAsType(const MDNode *N, unsigned I) {
254 Type *ElementTy = cast<ValueAsMetadata>(N->getOperand(I))->getType();
255 return toTypedPointer(ElementTy, N->getContext());
256}
257
258// The set of names is borrowed from the SPIR-V translator.
259// TODO: may be implemented in SPIRVBuiltins.td.
260static bool isPipeOrAddressSpaceCastBI(const StringRef MangledName) {
261 return MangledName == "write_pipe_2" || MangledName == "read_pipe_2" ||
262 MangledName == "write_pipe_2_bl" || MangledName == "read_pipe_2_bl" ||
263 MangledName == "write_pipe_4" || MangledName == "read_pipe_4" ||
264 MangledName == "reserve_write_pipe" ||
265 MangledName == "reserve_read_pipe" ||
266 MangledName == "commit_write_pipe" ||
267 MangledName == "commit_read_pipe" ||
268 MangledName == "work_group_reserve_write_pipe" ||
269 MangledName == "work_group_reserve_read_pipe" ||
270 MangledName == "work_group_commit_write_pipe" ||
271 MangledName == "work_group_commit_read_pipe" ||
272 MangledName == "get_pipe_num_packets_ro" ||
273 MangledName == "get_pipe_max_packets_ro" ||
274 MangledName == "get_pipe_num_packets_wo" ||
275 MangledName == "get_pipe_max_packets_wo" ||
276 MangledName == "sub_group_reserve_write_pipe" ||
277 MangledName == "sub_group_reserve_read_pipe" ||
278 MangledName == "sub_group_commit_write_pipe" ||
279 MangledName == "sub_group_commit_read_pipe" ||
280 MangledName == "to_global" || MangledName == "to_local" ||
281 MangledName == "to_private";
282}
283
284static bool isEnqueueKernelBI(const StringRef MangledName) {
285 return MangledName == "__enqueue_kernel_basic" ||
286 MangledName == "__enqueue_kernel_basic_events" ||
287 MangledName == "__enqueue_kernel_varargs" ||
288 MangledName == "__enqueue_kernel_events_varargs";
289}
290
291static bool isKernelQueryBI(const StringRef MangledName) {
292 return MangledName == "__get_kernel_work_group_size_impl" ||
293 MangledName == "__get_kernel_sub_group_count_for_ndrange_impl" ||
294 MangledName == "__get_kernel_max_sub_group_size_for_ndrange_impl" ||
295 MangledName == "__get_kernel_preferred_work_group_size_multiple_impl";
296}
297
299 if (!Name.starts_with("__"))
300 return false;
301
303 isPipeOrAddressSpaceCastBI(Name.drop_front(2)) ||
304 Name == "__translate_sampler_initializer";
305}
306
308 bool IsNonMangledOCL = isNonMangledOCLBuiltin(Name);
309 bool IsNonMangledSPIRV = Name.starts_with("__spirv_");
310 bool IsNonMangledHLSL = Name.starts_with("__hlsl_");
311 bool IsMangled = Name.starts_with("_Z");
312
313 // Otherwise use simple demangling to return the function name.
314 if (IsNonMangledOCL || IsNonMangledSPIRV || IsNonMangledHLSL || !IsMangled)
315 return Name.str();
316
317 // Try to use the itanium demangler.
318 if (char *DemangledName = itaniumDemangle(Name.data())) {
319 std::string Result = DemangledName;
320 free(DemangledName);
321 return Result;
322 }
323
324 // Autocheck C++, maybe need to do explicit check of the source language.
325 // OpenCL C++ built-ins are declared in cl namespace.
326 // TODO: consider using 'St' abbriviation for cl namespace mangling.
327 // Similar to ::std:: in C++.
328 size_t Start, Len = 0;
329 size_t DemangledNameLenStart = 2;
330 if (Name.starts_with("_ZN")) {
331 // Skip CV and ref qualifiers.
332 size_t NameSpaceStart = Name.find_first_not_of("rVKRO", 3);
333 // All built-ins are in the ::cl:: namespace.
334 if (Name.substr(NameSpaceStart, 11) != "2cl7__spirv")
335 return std::string();
336 DemangledNameLenStart = NameSpaceStart + 11;
337 }
338 Start = Name.find_first_not_of("0123456789", DemangledNameLenStart);
339 Name.substr(DemangledNameLenStart, Start - DemangledNameLenStart)
340 .getAsInteger(10, Len);
341 return Name.substr(Start, Len).str();
342}
343
345 if (Name.starts_with("opencl.") || Name.starts_with("ocl_") ||
346 Name.starts_with("spirv."))
347 return true;
348 return false;
349}
350
351bool isSpecialOpaqueType(const Type *Ty) {
352 if (const TargetExtType *EType = dyn_cast<TargetExtType>(Ty))
353 return hasBuiltinTypePrefix(EType->getName());
354
355 return false;
356}
357
358bool isEntryPoint(const Function &F) {
359 // OpenCL handling: any function with the SPIR_KERNEL
360 // calling convention will be a potential entry point.
361 if (F.getCallingConv() == CallingConv::SPIR_KERNEL)
362 return true;
363
364 // HLSL handling: special attribute are emitted from the
365 // front-end.
366 if (F.getFnAttribute("hlsl.shader").isValid())
367 return true;
368
369 return false;
370}
371
373 TypeName.consume_front("atomic_");
374 if (TypeName.consume_front("void"))
375 return Type::getVoidTy(Ctx);
376 else if (TypeName.consume_front("bool"))
377 return Type::getIntNTy(Ctx, 1);
378 else if (TypeName.consume_front("char") ||
379 TypeName.consume_front("unsigned char") ||
380 TypeName.consume_front("uchar"))
381 return Type::getInt8Ty(Ctx);
382 else if (TypeName.consume_front("short") ||
383 TypeName.consume_front("unsigned short") ||
384 TypeName.consume_front("ushort"))
385 return Type::getInt16Ty(Ctx);
386 else if (TypeName.consume_front("int") ||
387 TypeName.consume_front("unsigned int") ||
388 TypeName.consume_front("uint"))
389 return Type::getInt32Ty(Ctx);
390 else if (TypeName.consume_front("long") ||
391 TypeName.consume_front("unsigned long") ||
392 TypeName.consume_front("ulong"))
393 return Type::getInt64Ty(Ctx);
394 else if (TypeName.consume_front("half"))
395 return Type::getHalfTy(Ctx);
396 else if (TypeName.consume_front("float"))
397 return Type::getFloatTy(Ctx);
398 else if (TypeName.consume_front("double"))
399 return Type::getDoubleTy(Ctx);
400
401 // Unable to recognize SPIRV type name
402 return nullptr;
403}
404
405} // namespace llvm
unsigned const MachineRegisterInfo * MRI
MachineBasicBlock & MBB
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
std::string Name
Declares convenience wrapper classes for interpreting MachineInstr instances as specific generic oper...
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition: MD5.cpp:55
#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:76
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition: IRBuilder.h:2666
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:67
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:1067
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.
MachineInstr * getInstr() const
If conversion operators fail, use this method to get the MachineInstr explicitly.
Representation of each machine instruction.
Definition: MachineInstr.h:69
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
Definition: MachineInstr.h:558
void setAsmPrinterFlag(uint8_t Flag)
Set a flag for the AsmPrinter.
Definition: MachineInstr.h:372
const MachineOperand & getOperand(unsigned i) const
Definition: MachineInstr.h:568
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
bool canUseExtension(SPIRV::Extension::Extension E) const
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 target extensions types, which are generally unintrospectable from target-independ...
Definition: DerivedTypes.h:720
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
static Type * getHalfTy(LLVMContext &C)
static Type * getDoubleTy(LLVMContext &C)
static IntegerType * getIntNTy(LLVMContext &C, unsigned N)
static Type * getVoidTy(LLVMContext &C)
static IntegerType * getInt16Ty(LLVMContext &C)
static IntegerType * getInt8Ty(LLVMContext &C)
static IntegerType * getInt32Ty(LLVMContext &C)
static IntegerType * getInt64Ty(LLVMContext &C)
static Type * getFloatTy(LLVMContext &C)
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ SPIR_KERNEL
Used for SPIR kernel functions.
Definition: CallingConv.h:144
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:100
unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC)
Definition: SPIRVUtils.cpp:138
std::string getStringImm(const MachineInstr &MI, unsigned StartIndex)
Definition: SPIRVUtils.cpp:76
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:108
static uint32_t convertCharsToWord(const StringRef &Str, unsigned i)
Definition: SPIRVUtils.cpp:32
void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB)
Definition: SPIRVUtils.cpp:80
uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI)
Definition: SPIRVUtils.cpp:241
SPIRV::MemorySemantics::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC)
Definition: SPIRVUtils.cpp:190
std::string getOclOrSpirvBuiltinDemangledName(StringRef Name)
Definition: SPIRVUtils.cpp:307
void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, SPIRV::Decoration::Decoration Dec, const std::vector< uint32_t > &DecArgs, StringRef StrImm)
Definition: SPIRVUtils.cpp:117
std::string getSPIRVStringOperand(const InstType &MI, unsigned StartIndex)
char * itaniumDemangle(std::string_view mangled_name, bool ParseParams=true)
Returns a non-NULL pointer to a NUL-terminated C style string that should be explicitly freed,...
bool isSpecialOpaqueType(const Type *Ty)
Definition: SPIRVUtils.cpp:351
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:156
static bool isNonMangledOCLBuiltin(StringRef Name)
Definition: SPIRVUtils.cpp:298
bool isEntryPoint(const Function &F)
Definition: SPIRVUtils.cpp:358
SPIRV::StorageClass::StorageClass addressSpaceToStorageClass(unsigned AddrSpace, const SPIRVSubtarget &STI)
Definition: SPIRVUtils.cpp:162
AtomicOrdering
Atomic ordering for LLVM's memory model.
Type * toTypedPointer(Type *Ty, LLVMContext &Ctx)
Definition: SPIRVUtils.h:152
static bool isPipeOrAddressSpaceCastBI(const StringRef MangledName)
Definition: SPIRVUtils.cpp:260
Type * parseBasicTypeName(StringRef &TypeName, LLVMContext &Ctx)
Definition: SPIRVUtils.cpp:372
MachineInstr * getDefInstrMaybeConstant(Register &ConstReg, const MachineRegisterInfo *MRI)
Definition: SPIRVUtils.cpp:226
bool hasBuiltinTypePrefix(StringRef Name)
Definition: SPIRVUtils.cpp:344
Type * getMDOperandAsType(const MDNode *N, unsigned I)
Definition: SPIRVUtils.cpp:253
static size_t getPaddedLen(const StringRef &Str)
Definition: SPIRVUtils.cpp:46
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID)
Definition: SPIRVUtils.cpp:247
void addStringImm(const StringRef &Str, MCInst &Inst)
Definition: SPIRVUtils.cpp:51
static bool isKernelQueryBI(const StringRef MangledName)
Definition: SPIRVUtils.cpp:291
static bool isEnqueueKernelBI(const StringRef MangledName)
Definition: SPIRVUtils.cpp:284
SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord)
Definition: SPIRVUtils.cpp:208
#define N