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. 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. 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 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 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 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 74 std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) { 75 return getSPIRVStringOperand(MI, StartIndex); 76 } 77 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 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 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 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 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. 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 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 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 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 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 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 234 bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) { 235 return MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && 236 MI.getIntrinsicID() == IntrinsicID; 237 } 238 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. 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 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 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 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 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 335 static bool isOpenCLBuiltinType(const StructType *SType) { 336 return SType->isOpaque() && SType->hasName() && 337 SType->getName().startswith("opencl."); 338 } 339 340 static bool isSPIRVBuiltinType(const StructType *SType) { 341 return SType->isOpaque() && SType->hasName() && 342 SType->getName().startswith("spirv."); 343 } 344 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 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