xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/SPIRV/SPIRVUtils.cpp (revision 753f127f3ace09432b2baeffd71a308760641a62)
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