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. 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. 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 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 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 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 73 std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) { 74 return getSPIRVStringOperand(MI, StartIndex); 75 } 76 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 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 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 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 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. 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 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 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 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 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 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 231 bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) { 232 return MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && 233 MI.getIntrinsicID() == IntrinsicID; 234 } 235 236 Type *getMDOperandAsType(const MDNode *N, unsigned I) { 237 return cast<ValueAsMetadata>(N->getOperand(I))->getType(); 238 } 239