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, MachineInstrBuilder &MIB) { 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 MIB.addImm(convertCharsToWord(Str, i)); 53 } 54 } 55 56 void addStringImm(const StringRef &Str, IRBuilder<> &B, 57 std::vector<Value *> &Args) { 58 const size_t PaddedLen = getPaddedLen(Str); 59 for (unsigned i = 0; i < PaddedLen; i += 4) { 60 // Add a vector element for the 32-bits of chars or padding. 61 Args.push_back(B.getInt32(convertCharsToWord(Str, i))); 62 } 63 } 64 65 std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) { 66 return getSPIRVStringOperand(MI, StartIndex); 67 } 68 69 void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB) { 70 const auto Bitwidth = Imm.getBitWidth(); 71 switch (Bitwidth) { 72 case 1: 73 break; // Already handled. 74 case 8: 75 case 16: 76 case 32: 77 MIB.addImm(Imm.getZExtValue()); 78 break; 79 case 64: { 80 uint64_t FullImm = Imm.getZExtValue(); 81 uint32_t LowBits = FullImm & 0xffffffff; 82 uint32_t HighBits = (FullImm >> 32) & 0xffffffff; 83 MIB.addImm(LowBits).addImm(HighBits); 84 break; 85 } 86 default: 87 report_fatal_error("Unsupported constant bitwidth"); 88 } 89 } 90 91 void buildOpName(Register Target, const StringRef &Name, 92 MachineIRBuilder &MIRBuilder) { 93 if (!Name.empty()) { 94 auto MIB = MIRBuilder.buildInstr(SPIRV::OpName).addUse(Target); 95 addStringImm(Name, MIB); 96 } 97 } 98 99 static void finishBuildOpDecorate(MachineInstrBuilder &MIB, 100 const std::vector<uint32_t> &DecArgs, 101 StringRef StrImm) { 102 if (!StrImm.empty()) 103 addStringImm(StrImm, MIB); 104 for (const auto &DecArg : DecArgs) 105 MIB.addImm(DecArg); 106 } 107 108 void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, 109 llvm::SPIRV::Decoration Dec, 110 const std::vector<uint32_t> &DecArgs, StringRef StrImm) { 111 auto MIB = MIRBuilder.buildInstr(SPIRV::OpDecorate) 112 .addUse(Reg) 113 .addImm(static_cast<uint32_t>(Dec)); 114 finishBuildOpDecorate(MIB, DecArgs, StrImm); 115 } 116 117 void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII, 118 llvm::SPIRV::Decoration Dec, 119 const std::vector<uint32_t> &DecArgs, StringRef StrImm) { 120 MachineBasicBlock &MBB = *I.getParent(); 121 auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpDecorate)) 122 .addUse(Reg) 123 .addImm(static_cast<uint32_t>(Dec)); 124 finishBuildOpDecorate(MIB, DecArgs, StrImm); 125 } 126 127 // TODO: maybe the following two functions should be handled in the subtarget 128 // to allow for different OpenCL vs Vulkan handling. 129 unsigned storageClassToAddressSpace(SPIRV::StorageClass SC) { 130 switch (SC) { 131 case SPIRV::StorageClass::Function: 132 return 0; 133 case SPIRV::StorageClass::CrossWorkgroup: 134 return 1; 135 case SPIRV::StorageClass::UniformConstant: 136 return 2; 137 case SPIRV::StorageClass::Workgroup: 138 return 3; 139 case SPIRV::StorageClass::Generic: 140 return 4; 141 case SPIRV::StorageClass::Input: 142 return 7; 143 default: 144 llvm_unreachable("Unable to get address space id"); 145 } 146 } 147 148 SPIRV::StorageClass addressSpaceToStorageClass(unsigned AddrSpace) { 149 switch (AddrSpace) { 150 case 0: 151 return SPIRV::StorageClass::Function; 152 case 1: 153 return SPIRV::StorageClass::CrossWorkgroup; 154 case 2: 155 return SPIRV::StorageClass::UniformConstant; 156 case 3: 157 return SPIRV::StorageClass::Workgroup; 158 case 4: 159 return SPIRV::StorageClass::Generic; 160 case 7: 161 return SPIRV::StorageClass::Input; 162 default: 163 llvm_unreachable("Unknown address space"); 164 } 165 } 166 167 SPIRV::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass SC) { 168 switch (SC) { 169 case SPIRV::StorageClass::StorageBuffer: 170 case SPIRV::StorageClass::Uniform: 171 return SPIRV::MemorySemantics::UniformMemory; 172 case SPIRV::StorageClass::Workgroup: 173 return SPIRV::MemorySemantics::WorkgroupMemory; 174 case SPIRV::StorageClass::CrossWorkgroup: 175 return SPIRV::MemorySemantics::CrossWorkgroupMemory; 176 case SPIRV::StorageClass::AtomicCounter: 177 return SPIRV::MemorySemantics::AtomicCounterMemory; 178 case SPIRV::StorageClass::Image: 179 return SPIRV::MemorySemantics::ImageMemory; 180 default: 181 return SPIRV::MemorySemantics::None; 182 } 183 } 184 185 MachineInstr *getDefInstrMaybeConstant(Register &ConstReg, 186 const MachineRegisterInfo *MRI) { 187 MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg); 188 if (ConstInstr->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS && 189 ConstInstr->getIntrinsicID() == Intrinsic::spv_track_constant) { 190 ConstReg = ConstInstr->getOperand(2).getReg(); 191 ConstInstr = MRI->getVRegDef(ConstReg); 192 } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) { 193 ConstReg = ConstInstr->getOperand(1).getReg(); 194 ConstInstr = MRI->getVRegDef(ConstReg); 195 } 196 return ConstInstr; 197 } 198 199 uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) { 200 const MachineInstr *MI = getDefInstrMaybeConstant(ConstReg, MRI); 201 assert(MI && MI->getOpcode() == TargetOpcode::G_CONSTANT); 202 return MI->getOperand(1).getCImm()->getValue().getZExtValue(); 203 } 204 205 Type *getMDOperandAsType(const MDNode *N, unsigned I) { 206 return cast<ValueAsMetadata>(N->getOperand(I))->getType(); 207 } 208