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 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 95 void buildOpName(Register Target, const StringRef &Name, 96 MachineIRBuilder &MIRBuilder) { 97 if (!Name.empty()) { 98 auto MIB = MIRBuilder.buildInstr(SPIRV::OpName).addUse(Target); 99 addStringImm(Name, MIB); 100 } 101 } 102 103 static void finishBuildOpDecorate(MachineInstrBuilder &MIB, 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 112 void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, 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 121 void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII, 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. 133 unsigned 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 152 SPIRV::StorageClass::StorageClass 153 addressSpaceToStorageClass(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 172 SPIRV::MemorySemantics::MemorySemantics 173 getMemSemanticsForStorageClass(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 191 SPIRV::MemorySemantics::MemorySemantics getMemSemantics(AtomicOrdering Ord) { 192 switch (Ord) { 193 case AtomicOrdering::Acquire: 194 return SPIRV::MemorySemantics::Acquire; 195 case AtomicOrdering::Release: 196 return SPIRV::MemorySemantics::Release; 197 case AtomicOrdering::AcquireRelease: 198 return SPIRV::MemorySemantics::AcquireRelease; 199 case AtomicOrdering::SequentiallyConsistent: 200 return SPIRV::MemorySemantics::SequentiallyConsistent; 201 case AtomicOrdering::Unordered: 202 case AtomicOrdering::Monotonic: 203 case AtomicOrdering::NotAtomic: 204 return SPIRV::MemorySemantics::None; 205 } 206 llvm_unreachable(nullptr); 207 } 208 209 MachineInstr *getDefInstrMaybeConstant(Register &ConstReg, 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 223 uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) { 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 229 bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) { 230 return MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && 231 MI.getIntrinsicID() == IntrinsicID; 232 } 233 234 Type *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. 240 static 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 264 static 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 271 static 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 278 static bool isNonMangledOCLBuiltin(StringRef Name) { 279 if (!Name.startswith("__")) 280 return false; 281 282 return isEnqueueKernelBI(Name) || isKernelQueryBI(Name) || 283 isPipeOrAddressSpaceCastBI(Name.drop_front(2)) || 284 Name == "__translate_sampler_initializer"; 285 } 286 287 std::string getOclOrSpirvBuiltinDemangledName(StringRef Name) { 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 325 const 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 332 static bool hasBuiltinTypePrefix(StringRef Name) { 333 if (Name.starts_with("opencl.") || Name.starts_with("spirv.")) 334 return true; 335 return false; 336 } 337 338 bool 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 350