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/IR/IntrinsicsSPIRV.h"
22
23 using namespace llvm;
24
25 // The following functions are used to add these string literals as a series of
26 // 32-bit integer operands with the correct format, and unpack them if necessary
27 // when making string comparisons in compiler passes.
28 // SPIR-V requires null-terminated UTF-8 strings padded to 32-bit alignment.
convertCharsToWord(const StringRef & Str,unsigned i)29 static uint32_t convertCharsToWord(const StringRef &Str, unsigned i) {
30 uint32_t Word = 0u; // Build up this 32-bit word from 4 8-bit chars.
31 for (unsigned WordIndex = 0; WordIndex < 4; ++WordIndex) {
32 unsigned StrIndex = i + WordIndex;
33 uint8_t CharToAdd = 0; // Initilize char as padding/null.
34 if (StrIndex < Str.size()) { // If it's within the string, get a real char.
35 CharToAdd = Str[StrIndex];
36 }
37 Word |= (CharToAdd << (WordIndex * 8));
38 }
39 return Word;
40 }
41
42 // Get length including padding and null terminator.
getPaddedLen(const StringRef & Str)43 static size_t getPaddedLen(const StringRef &Str) {
44 const size_t Len = Str.size() + 1;
45 return (Len % 4 == 0) ? Len : Len + (4 - (Len % 4));
46 }
47
addStringImm(const StringRef & Str,MCInst & Inst)48 void addStringImm(const StringRef &Str, MCInst &Inst) {
49 const size_t PaddedLen = getPaddedLen(Str);
50 for (unsigned i = 0; i < PaddedLen; i += 4) {
51 // Add an operand for the 32-bits of chars or padding.
52 Inst.addOperand(MCOperand::createImm(convertCharsToWord(Str, i)));
53 }
54 }
55
addStringImm(const StringRef & Str,MachineInstrBuilder & MIB)56 void addStringImm(const StringRef &Str, MachineInstrBuilder &MIB) {
57 const size_t PaddedLen = getPaddedLen(Str);
58 for (unsigned i = 0; i < PaddedLen; i += 4) {
59 // Add an operand for the 32-bits of chars or padding.
60 MIB.addImm(convertCharsToWord(Str, i));
61 }
62 }
63
addStringImm(const StringRef & Str,IRBuilder<> & B,std::vector<Value * > & Args)64 void addStringImm(const StringRef &Str, IRBuilder<> &B,
65 std::vector<Value *> &Args) {
66 const size_t PaddedLen = getPaddedLen(Str);
67 for (unsigned i = 0; i < PaddedLen; i += 4) {
68 // Add a vector element for the 32-bits of chars or padding.
69 Args.push_back(B.getInt32(convertCharsToWord(Str, i)));
70 }
71 }
72
getStringImm(const MachineInstr & MI,unsigned StartIndex)73 std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) {
74 return getSPIRVStringOperand(MI, StartIndex);
75 }
76
addNumImm(const APInt & Imm,MachineInstrBuilder & MIB)77 void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB) {
78 const auto Bitwidth = Imm.getBitWidth();
79 switch (Bitwidth) {
80 case 1:
81 break; // Already handled.
82 case 8:
83 case 16:
84 case 32:
85 MIB.addImm(Imm.getZExtValue());
86 break;
87 case 64: {
88 uint64_t FullImm = Imm.getZExtValue();
89 uint32_t LowBits = FullImm & 0xffffffff;
90 uint32_t HighBits = (FullImm >> 32) & 0xffffffff;
91 MIB.addImm(LowBits).addImm(HighBits);
92 break;
93 }
94 default:
95 report_fatal_error("Unsupported constant bitwidth");
96 }
97 }
98
buildOpName(Register Target,const StringRef & Name,MachineIRBuilder & MIRBuilder)99 void buildOpName(Register Target, const StringRef &Name,
100 MachineIRBuilder &MIRBuilder) {
101 if (!Name.empty()) {
102 auto MIB = MIRBuilder.buildInstr(SPIRV::OpName).addUse(Target);
103 addStringImm(Name, MIB);
104 }
105 }
106
finishBuildOpDecorate(MachineInstrBuilder & MIB,const std::vector<uint32_t> & DecArgs,StringRef StrImm)107 static void finishBuildOpDecorate(MachineInstrBuilder &MIB,
108 const std::vector<uint32_t> &DecArgs,
109 StringRef StrImm) {
110 if (!StrImm.empty())
111 addStringImm(StrImm, MIB);
112 for (const auto &DecArg : DecArgs)
113 MIB.addImm(DecArg);
114 }
115
buildOpDecorate(Register Reg,MachineIRBuilder & MIRBuilder,llvm::SPIRV::Decoration Dec,const std::vector<uint32_t> & DecArgs,StringRef StrImm)116 void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder,
117 llvm::SPIRV::Decoration Dec,
118 const std::vector<uint32_t> &DecArgs, StringRef StrImm) {
119 auto MIB = MIRBuilder.buildInstr(SPIRV::OpDecorate)
120 .addUse(Reg)
121 .addImm(static_cast<uint32_t>(Dec));
122 finishBuildOpDecorate(MIB, DecArgs, StrImm);
123 }
124
buildOpDecorate(Register Reg,MachineInstr & I,const SPIRVInstrInfo & TII,llvm::SPIRV::Decoration Dec,const std::vector<uint32_t> & DecArgs,StringRef StrImm)125 void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII,
126 llvm::SPIRV::Decoration Dec,
127 const std::vector<uint32_t> &DecArgs, StringRef StrImm) {
128 MachineBasicBlock &MBB = *I.getParent();
129 auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpDecorate))
130 .addUse(Reg)
131 .addImm(static_cast<uint32_t>(Dec));
132 finishBuildOpDecorate(MIB, DecArgs, StrImm);
133 }
134
135 // TODO: maybe the following two functions should be handled in the subtarget
136 // to allow for different OpenCL vs Vulkan handling.
storageClassToAddressSpace(SPIRV::StorageClass SC)137 unsigned storageClassToAddressSpace(SPIRV::StorageClass SC) {
138 switch (SC) {
139 case SPIRV::StorageClass::Function:
140 return 0;
141 case SPIRV::StorageClass::CrossWorkgroup:
142 return 1;
143 case SPIRV::StorageClass::UniformConstant:
144 return 2;
145 case SPIRV::StorageClass::Workgroup:
146 return 3;
147 case SPIRV::StorageClass::Generic:
148 return 4;
149 case SPIRV::StorageClass::Input:
150 return 7;
151 default:
152 llvm_unreachable("Unable to get address space id");
153 }
154 }
155
addressSpaceToStorageClass(unsigned AddrSpace)156 SPIRV::StorageClass addressSpaceToStorageClass(unsigned AddrSpace) {
157 switch (AddrSpace) {
158 case 0:
159 return SPIRV::StorageClass::Function;
160 case 1:
161 return SPIRV::StorageClass::CrossWorkgroup;
162 case 2:
163 return SPIRV::StorageClass::UniformConstant;
164 case 3:
165 return SPIRV::StorageClass::Workgroup;
166 case 4:
167 return SPIRV::StorageClass::Generic;
168 case 7:
169 return SPIRV::StorageClass::Input;
170 default:
171 llvm_unreachable("Unknown address space");
172 }
173 }
174
getMemSemanticsForStorageClass(SPIRV::StorageClass SC)175 SPIRV::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass SC) {
176 switch (SC) {
177 case SPIRV::StorageClass::StorageBuffer:
178 case SPIRV::StorageClass::Uniform:
179 return SPIRV::MemorySemantics::UniformMemory;
180 case SPIRV::StorageClass::Workgroup:
181 return SPIRV::MemorySemantics::WorkgroupMemory;
182 case SPIRV::StorageClass::CrossWorkgroup:
183 return SPIRV::MemorySemantics::CrossWorkgroupMemory;
184 case SPIRV::StorageClass::AtomicCounter:
185 return SPIRV::MemorySemantics::AtomicCounterMemory;
186 case SPIRV::StorageClass::Image:
187 return SPIRV::MemorySemantics::ImageMemory;
188 default:
189 return SPIRV::MemorySemantics::None;
190 }
191 }
192
getMemSemantics(AtomicOrdering Ord)193 SPIRV::MemorySemantics getMemSemantics(AtomicOrdering Ord) {
194 switch (Ord) {
195 case AtomicOrdering::Acquire:
196 return SPIRV::MemorySemantics::Acquire;
197 case AtomicOrdering::Release:
198 return SPIRV::MemorySemantics::Release;
199 case AtomicOrdering::AcquireRelease:
200 return SPIRV::MemorySemantics::AcquireRelease;
201 case AtomicOrdering::SequentiallyConsistent:
202 return SPIRV::MemorySemantics::SequentiallyConsistent;
203 case AtomicOrdering::Unordered:
204 case AtomicOrdering::Monotonic:
205 case AtomicOrdering::NotAtomic:
206 default:
207 return SPIRV::MemorySemantics::None;
208 }
209 }
210
getDefInstrMaybeConstant(Register & ConstReg,const MachineRegisterInfo * MRI)211 MachineInstr *getDefInstrMaybeConstant(Register &ConstReg,
212 const MachineRegisterInfo *MRI) {
213 MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg);
214 if (ConstInstr->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
215 ConstInstr->getIntrinsicID() == Intrinsic::spv_track_constant) {
216 ConstReg = ConstInstr->getOperand(2).getReg();
217 ConstInstr = MRI->getVRegDef(ConstReg);
218 } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) {
219 ConstReg = ConstInstr->getOperand(1).getReg();
220 ConstInstr = MRI->getVRegDef(ConstReg);
221 }
222 return ConstInstr;
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(MachineInstr & MI,Intrinsic::ID IntrinsicID)231 bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) {
232 return MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
233 MI.getIntrinsicID() == IntrinsicID;
234 }
235
getMDOperandAsType(const MDNode * N,unsigned I)236 Type *getMDOperandAsType(const MDNode *N, unsigned I) {
237 return cast<ValueAsMetadata>(N->getOperand(I))->getType();
238 }
239