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