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