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