xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1bdd1243dSDimitry Andric //===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===//
2bdd1243dSDimitry Andric //
3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bdd1243dSDimitry Andric //
7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
8bdd1243dSDimitry Andric //
9bdd1243dSDimitry Andric // This file implements lowering builtin function calls and types using their
10bdd1243dSDimitry Andric // demangled names and TableGen records.
11bdd1243dSDimitry Andric //
12bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
13bdd1243dSDimitry Andric 
14bdd1243dSDimitry Andric #include "SPIRVBuiltins.h"
15bdd1243dSDimitry Andric #include "SPIRV.h"
16*0fca6ea1SDimitry Andric #include "SPIRVSubtarget.h"
17bdd1243dSDimitry Andric #include "SPIRVUtils.h"
1806c3fb27SDimitry Andric #include "llvm/ADT/StringExtras.h"
19bdd1243dSDimitry Andric #include "llvm/Analysis/ValueTracking.h"
20bdd1243dSDimitry Andric #include "llvm/IR/IntrinsicsSPIRV.h"
21bdd1243dSDimitry Andric #include <string>
22bdd1243dSDimitry Andric #include <tuple>
23bdd1243dSDimitry Andric 
24bdd1243dSDimitry Andric #define DEBUG_TYPE "spirv-builtins"
25bdd1243dSDimitry Andric 
26bdd1243dSDimitry Andric namespace llvm {
27bdd1243dSDimitry Andric namespace SPIRV {
28bdd1243dSDimitry Andric #define GET_BuiltinGroup_DECL
29bdd1243dSDimitry Andric #include "SPIRVGenTables.inc"
30bdd1243dSDimitry Andric 
31bdd1243dSDimitry Andric struct DemangledBuiltin {
32bdd1243dSDimitry Andric   StringRef Name;
33bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
34bdd1243dSDimitry Andric   BuiltinGroup Group;
35bdd1243dSDimitry Andric   uint8_t MinNumArgs;
36bdd1243dSDimitry Andric   uint8_t MaxNumArgs;
37bdd1243dSDimitry Andric };
38bdd1243dSDimitry Andric 
39bdd1243dSDimitry Andric #define GET_DemangledBuiltins_DECL
40bdd1243dSDimitry Andric #define GET_DemangledBuiltins_IMPL
41bdd1243dSDimitry Andric 
42bdd1243dSDimitry Andric struct IncomingCall {
43bdd1243dSDimitry Andric   const std::string BuiltinName;
44bdd1243dSDimitry Andric   const DemangledBuiltin *Builtin;
45bdd1243dSDimitry Andric 
46bdd1243dSDimitry Andric   const Register ReturnRegister;
47bdd1243dSDimitry Andric   const SPIRVType *ReturnType;
48bdd1243dSDimitry Andric   const SmallVectorImpl<Register> &Arguments;
49bdd1243dSDimitry Andric 
50bdd1243dSDimitry Andric   IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin,
51bdd1243dSDimitry Andric                const Register ReturnRegister, const SPIRVType *ReturnType,
52bdd1243dSDimitry Andric                const SmallVectorImpl<Register> &Arguments)
53bdd1243dSDimitry Andric       : BuiltinName(BuiltinName), Builtin(Builtin),
54bdd1243dSDimitry Andric         ReturnRegister(ReturnRegister), ReturnType(ReturnType),
55bdd1243dSDimitry Andric         Arguments(Arguments) {}
56*0fca6ea1SDimitry Andric 
57*0fca6ea1SDimitry Andric   bool isSpirvOp() const { return BuiltinName.rfind("__spirv_", 0) == 0; }
58bdd1243dSDimitry Andric };
59bdd1243dSDimitry Andric 
60bdd1243dSDimitry Andric struct NativeBuiltin {
61bdd1243dSDimitry Andric   StringRef Name;
62bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
63bdd1243dSDimitry Andric   uint32_t Opcode;
64bdd1243dSDimitry Andric };
65bdd1243dSDimitry Andric 
66bdd1243dSDimitry Andric #define GET_NativeBuiltins_DECL
67bdd1243dSDimitry Andric #define GET_NativeBuiltins_IMPL
68bdd1243dSDimitry Andric 
69bdd1243dSDimitry Andric struct GroupBuiltin {
70bdd1243dSDimitry Andric   StringRef Name;
71bdd1243dSDimitry Andric   uint32_t Opcode;
72bdd1243dSDimitry Andric   uint32_t GroupOperation;
73bdd1243dSDimitry Andric   bool IsElect;
74bdd1243dSDimitry Andric   bool IsAllOrAny;
75bdd1243dSDimitry Andric   bool IsAllEqual;
76bdd1243dSDimitry Andric   bool IsBallot;
77bdd1243dSDimitry Andric   bool IsInverseBallot;
78bdd1243dSDimitry Andric   bool IsBallotBitExtract;
79bdd1243dSDimitry Andric   bool IsBallotFindBit;
80bdd1243dSDimitry Andric   bool IsLogical;
81bdd1243dSDimitry Andric   bool NoGroupOperation;
82bdd1243dSDimitry Andric   bool HasBoolArg;
83bdd1243dSDimitry Andric };
84bdd1243dSDimitry Andric 
85bdd1243dSDimitry Andric #define GET_GroupBuiltins_DECL
86bdd1243dSDimitry Andric #define GET_GroupBuiltins_IMPL
87bdd1243dSDimitry Andric 
88*0fca6ea1SDimitry Andric struct IntelSubgroupsBuiltin {
89*0fca6ea1SDimitry Andric   StringRef Name;
90*0fca6ea1SDimitry Andric   uint32_t Opcode;
91*0fca6ea1SDimitry Andric   bool IsBlock;
92*0fca6ea1SDimitry Andric   bool IsWrite;
93*0fca6ea1SDimitry Andric };
94*0fca6ea1SDimitry Andric 
95*0fca6ea1SDimitry Andric #define GET_IntelSubgroupsBuiltins_DECL
96*0fca6ea1SDimitry Andric #define GET_IntelSubgroupsBuiltins_IMPL
97*0fca6ea1SDimitry Andric 
98*0fca6ea1SDimitry Andric struct AtomicFloatingBuiltin {
99*0fca6ea1SDimitry Andric   StringRef Name;
100*0fca6ea1SDimitry Andric   uint32_t Opcode;
101*0fca6ea1SDimitry Andric };
102*0fca6ea1SDimitry Andric 
103*0fca6ea1SDimitry Andric #define GET_AtomicFloatingBuiltins_DECL
104*0fca6ea1SDimitry Andric #define GET_AtomicFloatingBuiltins_IMPL
105*0fca6ea1SDimitry Andric struct GroupUniformBuiltin {
106*0fca6ea1SDimitry Andric   StringRef Name;
107*0fca6ea1SDimitry Andric   uint32_t Opcode;
108*0fca6ea1SDimitry Andric   bool IsLogical;
109*0fca6ea1SDimitry Andric };
110*0fca6ea1SDimitry Andric 
111*0fca6ea1SDimitry Andric #define GET_GroupUniformBuiltins_DECL
112*0fca6ea1SDimitry Andric #define GET_GroupUniformBuiltins_IMPL
113*0fca6ea1SDimitry Andric 
114bdd1243dSDimitry Andric struct GetBuiltin {
115bdd1243dSDimitry Andric   StringRef Name;
116bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
117bdd1243dSDimitry Andric   BuiltIn::BuiltIn Value;
118bdd1243dSDimitry Andric };
119bdd1243dSDimitry Andric 
120bdd1243dSDimitry Andric using namespace BuiltIn;
121bdd1243dSDimitry Andric #define GET_GetBuiltins_DECL
122bdd1243dSDimitry Andric #define GET_GetBuiltins_IMPL
123bdd1243dSDimitry Andric 
124bdd1243dSDimitry Andric struct ImageQueryBuiltin {
125bdd1243dSDimitry Andric   StringRef Name;
126bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
127bdd1243dSDimitry Andric   uint32_t Component;
128bdd1243dSDimitry Andric };
129bdd1243dSDimitry Andric 
130bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_DECL
131bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_IMPL
132bdd1243dSDimitry Andric 
133bdd1243dSDimitry Andric struct ConvertBuiltin {
134bdd1243dSDimitry Andric   StringRef Name;
135bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
136bdd1243dSDimitry Andric   bool IsDestinationSigned;
137bdd1243dSDimitry Andric   bool IsSaturated;
138bdd1243dSDimitry Andric   bool IsRounded;
139*0fca6ea1SDimitry Andric   bool IsBfloat16;
140bdd1243dSDimitry Andric   FPRoundingMode::FPRoundingMode RoundingMode;
141bdd1243dSDimitry Andric };
142bdd1243dSDimitry Andric 
143bdd1243dSDimitry Andric struct VectorLoadStoreBuiltin {
144bdd1243dSDimitry Andric   StringRef Name;
145bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
146bdd1243dSDimitry Andric   uint32_t Number;
147*0fca6ea1SDimitry Andric   uint32_t ElementCount;
148bdd1243dSDimitry Andric   bool IsRounded;
149bdd1243dSDimitry Andric   FPRoundingMode::FPRoundingMode RoundingMode;
150bdd1243dSDimitry Andric };
151bdd1243dSDimitry Andric 
152bdd1243dSDimitry Andric using namespace FPRoundingMode;
153bdd1243dSDimitry Andric #define GET_ConvertBuiltins_DECL
154bdd1243dSDimitry Andric #define GET_ConvertBuiltins_IMPL
155bdd1243dSDimitry Andric 
156bdd1243dSDimitry Andric using namespace InstructionSet;
157bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_DECL
158bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_IMPL
159bdd1243dSDimitry Andric 
160bdd1243dSDimitry Andric #define GET_CLMemoryScope_DECL
161bdd1243dSDimitry Andric #define GET_CLSamplerAddressingMode_DECL
162bdd1243dSDimitry Andric #define GET_CLMemoryFenceFlags_DECL
163bdd1243dSDimitry Andric #define GET_ExtendedBuiltins_DECL
164bdd1243dSDimitry Andric #include "SPIRVGenTables.inc"
165bdd1243dSDimitry Andric } // namespace SPIRV
166bdd1243dSDimitry Andric 
167bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
168bdd1243dSDimitry Andric // Misc functions for looking up builtins and veryfying requirements using
169bdd1243dSDimitry Andric // TableGen records
170bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
171bdd1243dSDimitry Andric 
172*0fca6ea1SDimitry Andric namespace SPIRV {
173*0fca6ea1SDimitry Andric /// Parses the name part of the demangled builtin call.
174*0fca6ea1SDimitry Andric std::string lookupBuiltinNameHelper(StringRef DemangledCall) {
175*0fca6ea1SDimitry Andric   const static std::string PassPrefix = "(anonymous namespace)::";
176*0fca6ea1SDimitry Andric   std::string BuiltinName;
177*0fca6ea1SDimitry Andric   // Itanium Demangler result may have "(anonymous namespace)::" prefix
178*0fca6ea1SDimitry Andric   if (DemangledCall.starts_with(PassPrefix.c_str()))
179*0fca6ea1SDimitry Andric     BuiltinName = DemangledCall.substr(PassPrefix.length());
180*0fca6ea1SDimitry Andric   else
181*0fca6ea1SDimitry Andric     BuiltinName = DemangledCall;
182bdd1243dSDimitry Andric   // Extract the builtin function name and types of arguments from the call
183bdd1243dSDimitry Andric   // skeleton.
184*0fca6ea1SDimitry Andric   BuiltinName = BuiltinName.substr(0, BuiltinName.find('('));
185*0fca6ea1SDimitry Andric 
186*0fca6ea1SDimitry Andric   // Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR
187*0fca6ea1SDimitry Andric   if (BuiltinName.rfind("__spirv_ocl_", 0) == 0)
188*0fca6ea1SDimitry Andric     BuiltinName = BuiltinName.substr(12);
189bdd1243dSDimitry Andric 
190bdd1243dSDimitry Andric   // Check if the extracted name contains type information between angle
191bdd1243dSDimitry Andric   // brackets. If so, the builtin is an instantiated template - needs to have
192bdd1243dSDimitry Andric   // the information after angle brackets and return type removed.
193bdd1243dSDimitry Andric   if (BuiltinName.find('<') && BuiltinName.back() == '>') {
194bdd1243dSDimitry Andric     BuiltinName = BuiltinName.substr(0, BuiltinName.find('<'));
1955f757f3fSDimitry Andric     BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(' ') + 1);
196bdd1243dSDimitry Andric   }
197bdd1243dSDimitry Andric 
198bdd1243dSDimitry Andric   // Check if the extracted name begins with "__spirv_ImageSampleExplicitLod"
199bdd1243dSDimitry Andric   // contains return type information at the end "_R<type>", if so extract the
200bdd1243dSDimitry Andric   // plain builtin name without the type information.
201bdd1243dSDimitry Andric   if (StringRef(BuiltinName).contains("__spirv_ImageSampleExplicitLod") &&
202bdd1243dSDimitry Andric       StringRef(BuiltinName).contains("_R")) {
203bdd1243dSDimitry Andric     BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R"));
204bdd1243dSDimitry Andric   }
205bdd1243dSDimitry Andric 
206*0fca6ea1SDimitry Andric   return BuiltinName;
207*0fca6ea1SDimitry Andric }
208*0fca6ea1SDimitry Andric } // namespace SPIRV
209*0fca6ea1SDimitry Andric 
210*0fca6ea1SDimitry Andric /// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
211*0fca6ea1SDimitry Andric /// the provided \p DemangledCall and specified \p Set.
212*0fca6ea1SDimitry Andric ///
213*0fca6ea1SDimitry Andric /// The lookup follows the following algorithm, returning the first successful
214*0fca6ea1SDimitry Andric /// match:
215*0fca6ea1SDimitry Andric /// 1. Search with the plain demangled name (expecting a 1:1 match).
216*0fca6ea1SDimitry Andric /// 2. Search with the prefix before or suffix after the demangled name
217*0fca6ea1SDimitry Andric /// signyfying the type of the first argument.
218*0fca6ea1SDimitry Andric ///
219*0fca6ea1SDimitry Andric /// \returns Wrapper around the demangled call and found builtin definition.
220*0fca6ea1SDimitry Andric static std::unique_ptr<const SPIRV::IncomingCall>
221*0fca6ea1SDimitry Andric lookupBuiltin(StringRef DemangledCall,
222*0fca6ea1SDimitry Andric               SPIRV::InstructionSet::InstructionSet Set,
223*0fca6ea1SDimitry Andric               Register ReturnRegister, const SPIRVType *ReturnType,
224*0fca6ea1SDimitry Andric               const SmallVectorImpl<Register> &Arguments) {
225*0fca6ea1SDimitry Andric   std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall);
226*0fca6ea1SDimitry Andric 
227bdd1243dSDimitry Andric   SmallVector<StringRef, 10> BuiltinArgumentTypes;
228bdd1243dSDimitry Andric   StringRef BuiltinArgs =
229bdd1243dSDimitry Andric       DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
230bdd1243dSDimitry Andric   BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false);
231bdd1243dSDimitry Andric 
232bdd1243dSDimitry Andric   // Look up the builtin in the defined set. Start with the plain demangled
233bdd1243dSDimitry Andric   // name, expecting a 1:1 match in the defined builtin set.
234bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin;
235bdd1243dSDimitry Andric   if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set)))
236bdd1243dSDimitry Andric     return std::make_unique<SPIRV::IncomingCall>(
237bdd1243dSDimitry Andric         BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
238bdd1243dSDimitry Andric 
239bdd1243dSDimitry Andric   // If the initial look up was unsuccessful and the demangled call takes at
240bdd1243dSDimitry Andric   // least 1 argument, add a prefix or suffix signifying the type of the first
241bdd1243dSDimitry Andric   // argument and repeat the search.
242bdd1243dSDimitry Andric   if (BuiltinArgumentTypes.size() >= 1) {
243bdd1243dSDimitry Andric     char FirstArgumentType = BuiltinArgumentTypes[0][0];
244bdd1243dSDimitry Andric     // Prefix to be added to the builtin's name for lookup.
245bdd1243dSDimitry Andric     // For example, OpenCL "abs" taking an unsigned value has a prefix "u_".
246bdd1243dSDimitry Andric     std::string Prefix;
247bdd1243dSDimitry Andric 
248bdd1243dSDimitry Andric     switch (FirstArgumentType) {
249bdd1243dSDimitry Andric     // Unsigned:
250bdd1243dSDimitry Andric     case 'u':
251bdd1243dSDimitry Andric       if (Set == SPIRV::InstructionSet::OpenCL_std)
252bdd1243dSDimitry Andric         Prefix = "u_";
253bdd1243dSDimitry Andric       else if (Set == SPIRV::InstructionSet::GLSL_std_450)
254bdd1243dSDimitry Andric         Prefix = "u";
255bdd1243dSDimitry Andric       break;
256bdd1243dSDimitry Andric     // Signed:
257bdd1243dSDimitry Andric     case 'c':
258bdd1243dSDimitry Andric     case 's':
259bdd1243dSDimitry Andric     case 'i':
260bdd1243dSDimitry Andric     case 'l':
261bdd1243dSDimitry Andric       if (Set == SPIRV::InstructionSet::OpenCL_std)
262bdd1243dSDimitry Andric         Prefix = "s_";
263bdd1243dSDimitry Andric       else if (Set == SPIRV::InstructionSet::GLSL_std_450)
264bdd1243dSDimitry Andric         Prefix = "s";
265bdd1243dSDimitry Andric       break;
266bdd1243dSDimitry Andric     // Floating-point:
267bdd1243dSDimitry Andric     case 'f':
268bdd1243dSDimitry Andric     case 'd':
269bdd1243dSDimitry Andric     case 'h':
270bdd1243dSDimitry Andric       if (Set == SPIRV::InstructionSet::OpenCL_std ||
271bdd1243dSDimitry Andric           Set == SPIRV::InstructionSet::GLSL_std_450)
272bdd1243dSDimitry Andric         Prefix = "f";
273bdd1243dSDimitry Andric       break;
274bdd1243dSDimitry Andric     }
275bdd1243dSDimitry Andric 
276bdd1243dSDimitry Andric     // If argument-type name prefix was added, look up the builtin again.
277bdd1243dSDimitry Andric     if (!Prefix.empty() &&
278bdd1243dSDimitry Andric         (Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set)))
279bdd1243dSDimitry Andric       return std::make_unique<SPIRV::IncomingCall>(
280bdd1243dSDimitry Andric           BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
281bdd1243dSDimitry Andric 
282bdd1243dSDimitry Andric     // If lookup with a prefix failed, find a suffix to be added to the
283bdd1243dSDimitry Andric     // builtin's name for lookup. For example, OpenCL "group_reduce_max" taking
284bdd1243dSDimitry Andric     // an unsigned value has a suffix "u".
285bdd1243dSDimitry Andric     std::string Suffix;
286bdd1243dSDimitry Andric 
287bdd1243dSDimitry Andric     switch (FirstArgumentType) {
288bdd1243dSDimitry Andric     // Unsigned:
289bdd1243dSDimitry Andric     case 'u':
290bdd1243dSDimitry Andric       Suffix = "u";
291bdd1243dSDimitry Andric       break;
292bdd1243dSDimitry Andric     // Signed:
293bdd1243dSDimitry Andric     case 'c':
294bdd1243dSDimitry Andric     case 's':
295bdd1243dSDimitry Andric     case 'i':
296bdd1243dSDimitry Andric     case 'l':
297bdd1243dSDimitry Andric       Suffix = "s";
298bdd1243dSDimitry Andric       break;
299bdd1243dSDimitry Andric     // Floating-point:
300bdd1243dSDimitry Andric     case 'f':
301bdd1243dSDimitry Andric     case 'd':
302bdd1243dSDimitry Andric     case 'h':
303bdd1243dSDimitry Andric       Suffix = "f";
304bdd1243dSDimitry Andric       break;
305bdd1243dSDimitry Andric     }
306bdd1243dSDimitry Andric 
307bdd1243dSDimitry Andric     // If argument-type name suffix was added, look up the builtin again.
308bdd1243dSDimitry Andric     if (!Suffix.empty() &&
309bdd1243dSDimitry Andric         (Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set)))
310bdd1243dSDimitry Andric       return std::make_unique<SPIRV::IncomingCall>(
311bdd1243dSDimitry Andric           BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
312bdd1243dSDimitry Andric   }
313bdd1243dSDimitry Andric 
314bdd1243dSDimitry Andric   // No builtin with such name was found in the set.
315bdd1243dSDimitry Andric   return nullptr;
316bdd1243dSDimitry Andric }
317bdd1243dSDimitry Andric 
318*0fca6ea1SDimitry Andric static MachineInstr *getBlockStructInstr(Register ParamReg,
319*0fca6ea1SDimitry Andric                                          MachineRegisterInfo *MRI) {
320*0fca6ea1SDimitry Andric   // We expect the following sequence of instructions:
321*0fca6ea1SDimitry Andric   //   %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)
322*0fca6ea1SDimitry Andric   //   or       = G_GLOBAL_VALUE @block_literal_global
323*0fca6ea1SDimitry Andric   //   %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0
324*0fca6ea1SDimitry Andric   //   %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)
325*0fca6ea1SDimitry Andric   MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
326*0fca6ea1SDimitry Andric   assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
327*0fca6ea1SDimitry Andric          MI->getOperand(1).isReg());
328*0fca6ea1SDimitry Andric   Register BitcastReg = MI->getOperand(1).getReg();
329*0fca6ea1SDimitry Andric   MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
330*0fca6ea1SDimitry Andric   assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&
331*0fca6ea1SDimitry Andric          BitcastMI->getOperand(2).isReg());
332*0fca6ea1SDimitry Andric   Register ValueReg = BitcastMI->getOperand(2).getReg();
333*0fca6ea1SDimitry Andric   MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);
334*0fca6ea1SDimitry Andric   return ValueMI;
335*0fca6ea1SDimitry Andric }
336*0fca6ea1SDimitry Andric 
337*0fca6ea1SDimitry Andric // Return an integer constant corresponding to the given register and
338*0fca6ea1SDimitry Andric // defined in spv_track_constant.
339*0fca6ea1SDimitry Andric // TODO: maybe unify with prelegalizer pass.
340*0fca6ea1SDimitry Andric static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) {
341*0fca6ea1SDimitry Andric   MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);
342*0fca6ea1SDimitry Andric   assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&
343*0fca6ea1SDimitry Andric          DefMI->getOperand(2).isReg());
344*0fca6ea1SDimitry Andric   MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());
345*0fca6ea1SDimitry Andric   assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&
346*0fca6ea1SDimitry Andric          DefMI2->getOperand(1).isCImm());
347*0fca6ea1SDimitry Andric   return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();
348*0fca6ea1SDimitry Andric }
349*0fca6ea1SDimitry Andric 
350*0fca6ea1SDimitry Andric // Return type of the instruction result from spv_assign_type intrinsic.
351*0fca6ea1SDimitry Andric // TODO: maybe unify with prelegalizer pass.
352*0fca6ea1SDimitry Andric static const Type *getMachineInstrType(MachineInstr *MI) {
353*0fca6ea1SDimitry Andric   MachineInstr *NextMI = MI->getNextNode();
354*0fca6ea1SDimitry Andric   if (!NextMI)
355*0fca6ea1SDimitry Andric     return nullptr;
356*0fca6ea1SDimitry Andric   if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
357*0fca6ea1SDimitry Andric     if ((NextMI = NextMI->getNextNode()) == nullptr)
358*0fca6ea1SDimitry Andric       return nullptr;
359*0fca6ea1SDimitry Andric   Register ValueReg = MI->getOperand(0).getReg();
360*0fca6ea1SDimitry Andric   if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) &&
361*0fca6ea1SDimitry Andric        !isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) ||
362*0fca6ea1SDimitry Andric       NextMI->getOperand(1).getReg() != ValueReg)
363*0fca6ea1SDimitry Andric     return nullptr;
364*0fca6ea1SDimitry Andric   Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
365*0fca6ea1SDimitry Andric   assert(Ty && "Type is expected");
366*0fca6ea1SDimitry Andric   return Ty;
367*0fca6ea1SDimitry Andric }
368*0fca6ea1SDimitry Andric 
369*0fca6ea1SDimitry Andric static const Type *getBlockStructType(Register ParamReg,
370*0fca6ea1SDimitry Andric                                       MachineRegisterInfo *MRI) {
371*0fca6ea1SDimitry Andric   // In principle, this information should be passed to us from Clang via
372*0fca6ea1SDimitry Andric   // an elementtype attribute. However, said attribute requires that
373*0fca6ea1SDimitry Andric   // the function call be an intrinsic, which is not. Instead, we rely on being
374*0fca6ea1SDimitry Andric   // able to trace this to the declaration of a variable: OpenCL C specification
375*0fca6ea1SDimitry Andric   // section 6.12.5 should guarantee that we can do this.
376*0fca6ea1SDimitry Andric   MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);
377*0fca6ea1SDimitry Andric   if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
378*0fca6ea1SDimitry Andric     return MI->getOperand(1).getGlobal()->getType();
379*0fca6ea1SDimitry Andric   assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
380*0fca6ea1SDimitry Andric          "Blocks in OpenCL C must be traceable to allocation site");
381*0fca6ea1SDimitry Andric   return getMachineInstrType(MI);
382*0fca6ea1SDimitry Andric }
383*0fca6ea1SDimitry Andric 
384bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
385bdd1243dSDimitry Andric // Helper functions for building misc instructions
386bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
387bdd1243dSDimitry Andric 
388bdd1243dSDimitry Andric /// Helper function building either a resulting scalar or vector bool register
389bdd1243dSDimitry Andric /// depending on the expected \p ResultType.
390bdd1243dSDimitry Andric ///
391bdd1243dSDimitry Andric /// \returns Tuple of the resulting register and its type.
392bdd1243dSDimitry Andric static std::tuple<Register, SPIRVType *>
393bdd1243dSDimitry Andric buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType,
394bdd1243dSDimitry Andric                   SPIRVGlobalRegistry *GR) {
395bdd1243dSDimitry Andric   LLT Type;
396bdd1243dSDimitry Andric   SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
397bdd1243dSDimitry Andric 
398bdd1243dSDimitry Andric   if (ResultType->getOpcode() == SPIRV::OpTypeVector) {
399bdd1243dSDimitry Andric     unsigned VectorElements = ResultType->getOperand(2).getImm();
400bdd1243dSDimitry Andric     BoolType =
401bdd1243dSDimitry Andric         GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder);
402bdd1243dSDimitry Andric     const FixedVectorType *LLVMVectorType =
403bdd1243dSDimitry Andric         cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType));
404bdd1243dSDimitry Andric     Type = LLT::vector(LLVMVectorType->getElementCount(), 1);
405bdd1243dSDimitry Andric   } else {
406bdd1243dSDimitry Andric     Type = LLT::scalar(1);
407bdd1243dSDimitry Andric   }
408bdd1243dSDimitry Andric 
409bdd1243dSDimitry Andric   Register ResultRegister =
410bdd1243dSDimitry Andric       MIRBuilder.getMRI()->createGenericVirtualRegister(Type);
41106c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(ResultRegister, &SPIRV::IDRegClass);
412bdd1243dSDimitry Andric   GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF());
413bdd1243dSDimitry Andric   return std::make_tuple(ResultRegister, BoolType);
414bdd1243dSDimitry Andric }
415bdd1243dSDimitry Andric 
416bdd1243dSDimitry Andric /// Helper function for building either a vector or scalar select instruction
417bdd1243dSDimitry Andric /// depending on the expected \p ResultType.
418bdd1243dSDimitry Andric static bool buildSelectInst(MachineIRBuilder &MIRBuilder,
419bdd1243dSDimitry Andric                             Register ReturnRegister, Register SourceRegister,
420bdd1243dSDimitry Andric                             const SPIRVType *ReturnType,
421bdd1243dSDimitry Andric                             SPIRVGlobalRegistry *GR) {
422bdd1243dSDimitry Andric   Register TrueConst, FalseConst;
423bdd1243dSDimitry Andric 
424bdd1243dSDimitry Andric   if (ReturnType->getOpcode() == SPIRV::OpTypeVector) {
425bdd1243dSDimitry Andric     unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType);
42606c3fb27SDimitry Andric     uint64_t AllOnes = APInt::getAllOnes(Bits).getZExtValue();
427bdd1243dSDimitry Andric     TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType);
428bdd1243dSDimitry Andric     FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType);
429bdd1243dSDimitry Andric   } else {
430bdd1243dSDimitry Andric     TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType);
431bdd1243dSDimitry Andric     FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType);
432bdd1243dSDimitry Andric   }
433bdd1243dSDimitry Andric   return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst,
434bdd1243dSDimitry Andric                                 FalseConst);
435bdd1243dSDimitry Andric }
436bdd1243dSDimitry Andric 
437bdd1243dSDimitry Andric /// Helper function for building a load instruction loading into the
438bdd1243dSDimitry Andric /// \p DestinationReg.
439bdd1243dSDimitry Andric static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister,
440bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
441bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR, LLT LowLevelType,
442bdd1243dSDimitry Andric                               Register DestinationReg = Register(0)) {
443bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
444bdd1243dSDimitry Andric   if (!DestinationReg.isValid()) {
445bdd1243dSDimitry Andric     DestinationReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
446bdd1243dSDimitry Andric     MRI->setType(DestinationReg, LLT::scalar(32));
447bdd1243dSDimitry Andric     GR->assignSPIRVTypeToVReg(BaseType, DestinationReg, MIRBuilder.getMF());
448bdd1243dSDimitry Andric   }
449bdd1243dSDimitry Andric   // TODO: consider using correct address space and alignment (p0 is canonical
450bdd1243dSDimitry Andric   // type for selection though).
451bdd1243dSDimitry Andric   MachinePointerInfo PtrInfo = MachinePointerInfo();
452bdd1243dSDimitry Andric   MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align());
453bdd1243dSDimitry Andric   return DestinationReg;
454bdd1243dSDimitry Andric }
455bdd1243dSDimitry Andric 
456bdd1243dSDimitry Andric /// Helper function for building a load instruction for loading a builtin global
457bdd1243dSDimitry Andric /// variable of \p BuiltinValue value.
458*0fca6ea1SDimitry Andric static Register buildBuiltinVariableLoad(
459*0fca6ea1SDimitry Andric     MachineIRBuilder &MIRBuilder, SPIRVType *VariableType,
460*0fca6ea1SDimitry Andric     SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType,
461*0fca6ea1SDimitry Andric     Register Reg = Register(0), bool isConst = true, bool hasLinkageTy = true) {
462bdd1243dSDimitry Andric   Register NewRegister =
463bdd1243dSDimitry Andric       MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass);
464bdd1243dSDimitry Andric   MIRBuilder.getMRI()->setType(NewRegister,
465bdd1243dSDimitry Andric                                LLT::pointer(0, GR->getPointerSize()));
466bdd1243dSDimitry Andric   SPIRVType *PtrType = GR->getOrCreateSPIRVPointerType(
467bdd1243dSDimitry Andric       VariableType, MIRBuilder, SPIRV::StorageClass::Input);
468bdd1243dSDimitry Andric   GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
469bdd1243dSDimitry Andric 
470bdd1243dSDimitry Andric   // Set up the global OpVariable with the necessary builtin decorations.
471bdd1243dSDimitry Andric   Register Variable = GR->buildGlobalVariable(
472bdd1243dSDimitry Andric       NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
473*0fca6ea1SDimitry Andric       SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst,
474*0fca6ea1SDimitry Andric       /* HasLinkageTy */ hasLinkageTy, SPIRV::LinkageType::Import, MIRBuilder,
475*0fca6ea1SDimitry Andric       false);
476bdd1243dSDimitry Andric 
477bdd1243dSDimitry Andric   // Load the value from the global variable.
478bdd1243dSDimitry Andric   Register LoadedRegister =
479bdd1243dSDimitry Andric       buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg);
480bdd1243dSDimitry Andric   MIRBuilder.getMRI()->setType(LoadedRegister, LLType);
481bdd1243dSDimitry Andric   return LoadedRegister;
482bdd1243dSDimitry Andric }
483bdd1243dSDimitry Andric 
484bdd1243dSDimitry Andric /// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg
485bdd1243dSDimitry Andric /// and its definition, set the new register as a destination of the definition,
486bdd1243dSDimitry Andric /// assign SPIRVType to both registers. If SpirvTy is provided, use it as
487bdd1243dSDimitry Andric /// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in
488bdd1243dSDimitry Andric /// SPIRVPreLegalizer.cpp.
489bdd1243dSDimitry Andric extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
490bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR,
491bdd1243dSDimitry Andric                                   MachineIRBuilder &MIB,
492bdd1243dSDimitry Andric                                   MachineRegisterInfo &MRI);
493bdd1243dSDimitry Andric 
494bdd1243dSDimitry Andric // TODO: Move to TableGen.
495bdd1243dSDimitry Andric static SPIRV::MemorySemantics::MemorySemantics
496bdd1243dSDimitry Andric getSPIRVMemSemantics(std::memory_order MemOrder) {
497bdd1243dSDimitry Andric   switch (MemOrder) {
498bdd1243dSDimitry Andric   case std::memory_order::memory_order_relaxed:
499bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::None;
500bdd1243dSDimitry Andric   case std::memory_order::memory_order_acquire:
501bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::Acquire;
502bdd1243dSDimitry Andric   case std::memory_order::memory_order_release:
503bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::Release;
504bdd1243dSDimitry Andric   case std::memory_order::memory_order_acq_rel:
505bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::AcquireRelease;
506bdd1243dSDimitry Andric   case std::memory_order::memory_order_seq_cst:
507bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::SequentiallyConsistent;
508bdd1243dSDimitry Andric   default:
509*0fca6ea1SDimitry Andric     report_fatal_error("Unknown CL memory scope");
510bdd1243dSDimitry Andric   }
511bdd1243dSDimitry Andric }
512bdd1243dSDimitry Andric 
513bdd1243dSDimitry Andric static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) {
514bdd1243dSDimitry Andric   switch (ClScope) {
515bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_work_item:
516bdd1243dSDimitry Andric     return SPIRV::Scope::Invocation;
517bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_work_group:
518bdd1243dSDimitry Andric     return SPIRV::Scope::Workgroup;
519bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_device:
520bdd1243dSDimitry Andric     return SPIRV::Scope::Device;
521bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_all_svm_devices:
522bdd1243dSDimitry Andric     return SPIRV::Scope::CrossDevice;
523bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_sub_group:
524bdd1243dSDimitry Andric     return SPIRV::Scope::Subgroup;
525bdd1243dSDimitry Andric   }
526*0fca6ea1SDimitry Andric   report_fatal_error("Unknown CL memory scope");
527bdd1243dSDimitry Andric }
528bdd1243dSDimitry Andric 
529bdd1243dSDimitry Andric static Register buildConstantIntReg(uint64_t Val, MachineIRBuilder &MIRBuilder,
530bdd1243dSDimitry Andric                                     SPIRVGlobalRegistry *GR,
531bdd1243dSDimitry Andric                                     unsigned BitWidth = 32) {
532bdd1243dSDimitry Andric   SPIRVType *IntType = GR->getOrCreateSPIRVIntegerType(BitWidth, MIRBuilder);
533bdd1243dSDimitry Andric   return GR->buildConstantInt(Val, MIRBuilder, IntType);
534bdd1243dSDimitry Andric }
535bdd1243dSDimitry Andric 
536bdd1243dSDimitry Andric static Register buildScopeReg(Register CLScopeRegister,
53706c3fb27SDimitry Andric                               SPIRV::Scope::Scope Scope,
538bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
539bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR,
54006c3fb27SDimitry Andric                               MachineRegisterInfo *MRI) {
54106c3fb27SDimitry Andric   if (CLScopeRegister.isValid()) {
542bdd1243dSDimitry Andric     auto CLScope =
543bdd1243dSDimitry Andric         static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI));
54406c3fb27SDimitry Andric     Scope = getSPIRVScope(CLScope);
545bdd1243dSDimitry Andric 
54606c3fb27SDimitry Andric     if (CLScope == static_cast<unsigned>(Scope)) {
54706c3fb27SDimitry Andric       MRI->setRegClass(CLScopeRegister, &SPIRV::IDRegClass);
548bdd1243dSDimitry Andric       return CLScopeRegister;
54906c3fb27SDimitry Andric     }
55006c3fb27SDimitry Andric   }
551bdd1243dSDimitry Andric   return buildConstantIntReg(Scope, MIRBuilder, GR);
552bdd1243dSDimitry Andric }
553bdd1243dSDimitry Andric 
554bdd1243dSDimitry Andric static Register buildMemSemanticsReg(Register SemanticsRegister,
55506c3fb27SDimitry Andric                                      Register PtrRegister, unsigned &Semantics,
55606c3fb27SDimitry Andric                                      MachineIRBuilder &MIRBuilder,
557bdd1243dSDimitry Andric                                      SPIRVGlobalRegistry *GR) {
55806c3fb27SDimitry Andric   if (SemanticsRegister.isValid()) {
55906c3fb27SDimitry Andric     MachineRegisterInfo *MRI = MIRBuilder.getMRI();
560bdd1243dSDimitry Andric     std::memory_order Order =
561bdd1243dSDimitry Andric         static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI));
56206c3fb27SDimitry Andric     Semantics =
563bdd1243dSDimitry Andric         getSPIRVMemSemantics(Order) |
564bdd1243dSDimitry Andric         getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
565bdd1243dSDimitry Andric 
56606c3fb27SDimitry Andric     if (Order == Semantics) {
56706c3fb27SDimitry Andric       MRI->setRegClass(SemanticsRegister, &SPIRV::IDRegClass);
568bdd1243dSDimitry Andric       return SemanticsRegister;
56906c3fb27SDimitry Andric     }
57006c3fb27SDimitry Andric   }
57106c3fb27SDimitry Andric   return buildConstantIntReg(Semantics, MIRBuilder, GR);
572bdd1243dSDimitry Andric }
573bdd1243dSDimitry Andric 
574*0fca6ea1SDimitry Andric static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode,
575*0fca6ea1SDimitry Andric                                const SPIRV::IncomingCall *Call,
576*0fca6ea1SDimitry Andric                                Register TypeReg,
577*0fca6ea1SDimitry Andric                                ArrayRef<uint32_t> ImmArgs = {}) {
578*0fca6ea1SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
579*0fca6ea1SDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode);
580*0fca6ea1SDimitry Andric   if (TypeReg.isValid())
581*0fca6ea1SDimitry Andric     MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
582*0fca6ea1SDimitry Andric   unsigned Sz = Call->Arguments.size() - ImmArgs.size();
583*0fca6ea1SDimitry Andric   for (unsigned i = 0; i < Sz; ++i) {
584*0fca6ea1SDimitry Andric     Register ArgReg = Call->Arguments[i];
585*0fca6ea1SDimitry Andric     if (!MRI->getRegClassOrNull(ArgReg))
586*0fca6ea1SDimitry Andric       MRI->setRegClass(ArgReg, &SPIRV::IDRegClass);
587*0fca6ea1SDimitry Andric     MIB.addUse(ArgReg);
588*0fca6ea1SDimitry Andric   }
589*0fca6ea1SDimitry Andric   for (uint32_t ImmArg : ImmArgs)
590*0fca6ea1SDimitry Andric     MIB.addImm(ImmArg);
591*0fca6ea1SDimitry Andric   return true;
592*0fca6ea1SDimitry Andric }
593*0fca6ea1SDimitry Andric 
594bdd1243dSDimitry Andric /// Helper function for translating atomic init to OpStore.
595bdd1243dSDimitry Andric static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call,
596bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder) {
597*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
598*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0));
599*0fca6ea1SDimitry Andric 
600bdd1243dSDimitry Andric   assert(Call->Arguments.size() == 2 &&
601bdd1243dSDimitry Andric          "Need 2 arguments for atomic init translation");
60206c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
60306c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
604bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpStore)
605bdd1243dSDimitry Andric       .addUse(Call->Arguments[0])
606bdd1243dSDimitry Andric       .addUse(Call->Arguments[1]);
607bdd1243dSDimitry Andric   return true;
608bdd1243dSDimitry Andric }
609bdd1243dSDimitry Andric 
610bdd1243dSDimitry Andric /// Helper function for building an atomic load instruction.
611bdd1243dSDimitry Andric static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call,
612bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
613bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
614*0fca6ea1SDimitry Andric   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
615*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
616*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg);
617*0fca6ea1SDimitry Andric 
618bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
61906c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass);
620bdd1243dSDimitry Andric   // TODO: if true insert call to __translate_ocl_memory_sccope before
621bdd1243dSDimitry Andric   // OpAtomicLoad and the function implementation. We can use Translator's
622bdd1243dSDimitry Andric   // output for transcoding/atomic_explicit_arguments.cl as an example.
623bdd1243dSDimitry Andric   Register ScopeRegister;
62406c3fb27SDimitry Andric   if (Call->Arguments.size() > 1) {
625bdd1243dSDimitry Andric     ScopeRegister = Call->Arguments[1];
62606c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(ScopeRegister, &SPIRV::IDRegClass);
62706c3fb27SDimitry Andric   } else
628bdd1243dSDimitry Andric     ScopeRegister = buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);
629bdd1243dSDimitry Andric 
630bdd1243dSDimitry Andric   Register MemSemanticsReg;
631bdd1243dSDimitry Andric   if (Call->Arguments.size() > 2) {
632bdd1243dSDimitry Andric     // TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad.
633bdd1243dSDimitry Andric     MemSemanticsReg = Call->Arguments[2];
63406c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);
635bdd1243dSDimitry Andric   } else {
636bdd1243dSDimitry Andric     int Semantics =
637bdd1243dSDimitry Andric         SPIRV::MemorySemantics::SequentiallyConsistent |
638bdd1243dSDimitry Andric         getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
639bdd1243dSDimitry Andric     MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
640bdd1243dSDimitry Andric   }
641bdd1243dSDimitry Andric 
642bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
643bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
644*0fca6ea1SDimitry Andric       .addUse(TypeReg)
645bdd1243dSDimitry Andric       .addUse(PtrRegister)
646bdd1243dSDimitry Andric       .addUse(ScopeRegister)
647bdd1243dSDimitry Andric       .addUse(MemSemanticsReg);
648bdd1243dSDimitry Andric   return true;
649bdd1243dSDimitry Andric }
650bdd1243dSDimitry Andric 
651bdd1243dSDimitry Andric /// Helper function for building an atomic store instruction.
652bdd1243dSDimitry Andric static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call,
653bdd1243dSDimitry Andric                                  MachineIRBuilder &MIRBuilder,
654bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
655*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
656*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call, Register(0));
657*0fca6ea1SDimitry Andric 
658bdd1243dSDimitry Andric   Register ScopeRegister =
659bdd1243dSDimitry Andric       buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);
660bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
66106c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass);
662bdd1243dSDimitry Andric   int Semantics =
663bdd1243dSDimitry Andric       SPIRV::MemorySemantics::SequentiallyConsistent |
664bdd1243dSDimitry Andric       getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
665bdd1243dSDimitry Andric   Register MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
66606c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
667bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
668bdd1243dSDimitry Andric       .addUse(PtrRegister)
669bdd1243dSDimitry Andric       .addUse(ScopeRegister)
670bdd1243dSDimitry Andric       .addUse(MemSemanticsReg)
671bdd1243dSDimitry Andric       .addUse(Call->Arguments[1]);
672bdd1243dSDimitry Andric   return true;
673bdd1243dSDimitry Andric }
674bdd1243dSDimitry Andric 
675bdd1243dSDimitry Andric /// Helper function for building an atomic compare-exchange instruction.
676*0fca6ea1SDimitry Andric static bool buildAtomicCompareExchangeInst(
677*0fca6ea1SDimitry Andric     const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin,
678*0fca6ea1SDimitry Andric     unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
679*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
680*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
681*0fca6ea1SDimitry Andric                               GR->getSPIRVTypeID(Call->ReturnType));
682*0fca6ea1SDimitry Andric 
683bdd1243dSDimitry Andric   bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg");
684bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
685bdd1243dSDimitry Andric 
686bdd1243dSDimitry Andric   Register ObjectPtr = Call->Arguments[0];   // Pointer (volatile A *object.)
687bdd1243dSDimitry Andric   Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).
688bdd1243dSDimitry Andric   Register Desired = Call->Arguments[2];     // Value (C Desired).
68906c3fb27SDimitry Andric   MRI->setRegClass(ObjectPtr, &SPIRV::IDRegClass);
69006c3fb27SDimitry Andric   MRI->setRegClass(ExpectedArg, &SPIRV::IDRegClass);
69106c3fb27SDimitry Andric   MRI->setRegClass(Desired, &SPIRV::IDRegClass);
692bdd1243dSDimitry Andric   SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
693bdd1243dSDimitry Andric   LLT DesiredLLT = MRI->getType(Desired);
694bdd1243dSDimitry Andric 
695bdd1243dSDimitry Andric   assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() ==
696bdd1243dSDimitry Andric          SPIRV::OpTypePointer);
697bdd1243dSDimitry Andric   unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode();
698*0fca6ea1SDimitry Andric   (void)ExpectedType;
699bdd1243dSDimitry Andric   assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt
700bdd1243dSDimitry Andric                    : ExpectedType == SPIRV::OpTypePointer);
701bdd1243dSDimitry Andric   assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
702bdd1243dSDimitry Andric 
703bdd1243dSDimitry Andric   SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
704bdd1243dSDimitry Andric   assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected");
705bdd1243dSDimitry Andric   auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
706bdd1243dSDimitry Andric       SpvObjectPtrTy->getOperand(1).getImm());
707bdd1243dSDimitry Andric   auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
708bdd1243dSDimitry Andric 
709bdd1243dSDimitry Andric   Register MemSemEqualReg;
710bdd1243dSDimitry Andric   Register MemSemUnequalReg;
711bdd1243dSDimitry Andric   uint64_t MemSemEqual =
712bdd1243dSDimitry Andric       IsCmpxchg
713bdd1243dSDimitry Andric           ? SPIRV::MemorySemantics::None
714bdd1243dSDimitry Andric           : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
715bdd1243dSDimitry Andric   uint64_t MemSemUnequal =
716bdd1243dSDimitry Andric       IsCmpxchg
717bdd1243dSDimitry Andric           ? SPIRV::MemorySemantics::None
718bdd1243dSDimitry Andric           : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
719bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 4) {
720bdd1243dSDimitry Andric     assert(Call->Arguments.size() >= 5 &&
721bdd1243dSDimitry Andric            "Need 5+ args for explicit atomic cmpxchg");
722bdd1243dSDimitry Andric     auto MemOrdEq =
723bdd1243dSDimitry Andric         static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));
724bdd1243dSDimitry Andric     auto MemOrdNeq =
725bdd1243dSDimitry Andric         static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));
726bdd1243dSDimitry Andric     MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;
727bdd1243dSDimitry Andric     MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;
728bdd1243dSDimitry Andric     if (MemOrdEq == MemSemEqual)
729bdd1243dSDimitry Andric       MemSemEqualReg = Call->Arguments[3];
730bdd1243dSDimitry Andric     if (MemOrdNeq == MemSemEqual)
731bdd1243dSDimitry Andric       MemSemUnequalReg = Call->Arguments[4];
73206c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass);
73306c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[4], &SPIRV::IDRegClass);
734bdd1243dSDimitry Andric   }
735bdd1243dSDimitry Andric   if (!MemSemEqualReg.isValid())
736bdd1243dSDimitry Andric     MemSemEqualReg = buildConstantIntReg(MemSemEqual, MIRBuilder, GR);
737bdd1243dSDimitry Andric   if (!MemSemUnequalReg.isValid())
738bdd1243dSDimitry Andric     MemSemUnequalReg = buildConstantIntReg(MemSemUnequal, MIRBuilder, GR);
739bdd1243dSDimitry Andric 
740bdd1243dSDimitry Andric   Register ScopeReg;
741bdd1243dSDimitry Andric   auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;
742bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 6) {
743bdd1243dSDimitry Andric     assert(Call->Arguments.size() == 6 &&
744bdd1243dSDimitry Andric            "Extra args for explicit atomic cmpxchg");
745bdd1243dSDimitry Andric     auto ClScope = static_cast<SPIRV::CLMemoryScope>(
746bdd1243dSDimitry Andric         getIConstVal(Call->Arguments[5], MRI));
747bdd1243dSDimitry Andric     Scope = getSPIRVScope(ClScope);
748bdd1243dSDimitry Andric     if (ClScope == static_cast<unsigned>(Scope))
749bdd1243dSDimitry Andric       ScopeReg = Call->Arguments[5];
75006c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[5], &SPIRV::IDRegClass);
751bdd1243dSDimitry Andric   }
752bdd1243dSDimitry Andric   if (!ScopeReg.isValid())
753bdd1243dSDimitry Andric     ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);
754bdd1243dSDimitry Andric 
755bdd1243dSDimitry Andric   Register Expected = IsCmpxchg
756bdd1243dSDimitry Andric                           ? ExpectedArg
757bdd1243dSDimitry Andric                           : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder,
758bdd1243dSDimitry Andric                                           GR, LLT::scalar(32));
759bdd1243dSDimitry Andric   MRI->setType(Expected, DesiredLLT);
760bdd1243dSDimitry Andric   Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)
761bdd1243dSDimitry Andric                             : Call->ReturnRegister;
76206c3fb27SDimitry Andric   if (!MRI->getRegClassOrNull(Tmp))
76306c3fb27SDimitry Andric     MRI->setRegClass(Tmp, &SPIRV::IDRegClass);
764bdd1243dSDimitry Andric   GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());
765bdd1243dSDimitry Andric 
766bdd1243dSDimitry Andric   SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
767bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
768bdd1243dSDimitry Andric       .addDef(Tmp)
769bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(IntTy))
770bdd1243dSDimitry Andric       .addUse(ObjectPtr)
771bdd1243dSDimitry Andric       .addUse(ScopeReg)
772bdd1243dSDimitry Andric       .addUse(MemSemEqualReg)
773bdd1243dSDimitry Andric       .addUse(MemSemUnequalReg)
774bdd1243dSDimitry Andric       .addUse(Desired)
775bdd1243dSDimitry Andric       .addUse(Expected);
776bdd1243dSDimitry Andric   if (!IsCmpxchg) {
777bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);
778bdd1243dSDimitry Andric     MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);
779bdd1243dSDimitry Andric   }
780bdd1243dSDimitry Andric   return true;
781bdd1243dSDimitry Andric }
782bdd1243dSDimitry Andric 
783*0fca6ea1SDimitry Andric /// Helper function for building atomic instructions.
784bdd1243dSDimitry Andric static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
785bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
786bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
787*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
788*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
789*0fca6ea1SDimitry Andric                               GR->getSPIRVTypeID(Call->ReturnType));
790*0fca6ea1SDimitry Andric 
79106c3fb27SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
79206c3fb27SDimitry Andric   Register ScopeRegister =
79306c3fb27SDimitry Andric       Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register();
794bdd1243dSDimitry Andric 
79506c3fb27SDimitry Andric   assert(Call->Arguments.size() <= 4 &&
796bdd1243dSDimitry Andric          "Too many args for explicit atomic RMW");
79706c3fb27SDimitry Andric   ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup,
79806c3fb27SDimitry Andric                                 MIRBuilder, GR, MRI);
799bdd1243dSDimitry Andric 
800bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
801bdd1243dSDimitry Andric   unsigned Semantics = SPIRV::MemorySemantics::None;
80206c3fb27SDimitry Andric   MRI->setRegClass(PtrRegister, &SPIRV::IDRegClass);
80306c3fb27SDimitry Andric   Register MemSemanticsReg =
80406c3fb27SDimitry Andric       Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
80506c3fb27SDimitry Andric   MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
80606c3fb27SDimitry Andric                                          Semantics, MIRBuilder, GR);
80706c3fb27SDimitry Andric   MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
808*0fca6ea1SDimitry Andric   Register ValueReg = Call->Arguments[1];
809*0fca6ea1SDimitry Andric   Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);
810*0fca6ea1SDimitry Andric   // support cl_ext_float_atomics
811*0fca6ea1SDimitry Andric   if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {
812*0fca6ea1SDimitry Andric     if (Opcode == SPIRV::OpAtomicIAdd) {
813*0fca6ea1SDimitry Andric       Opcode = SPIRV::OpAtomicFAddEXT;
814*0fca6ea1SDimitry Andric     } else if (Opcode == SPIRV::OpAtomicISub) {
815*0fca6ea1SDimitry Andric       // Translate OpAtomicISub applied to a floating type argument to
816*0fca6ea1SDimitry Andric       // OpAtomicFAddEXT with the negative value operand
817*0fca6ea1SDimitry Andric       Opcode = SPIRV::OpAtomicFAddEXT;
818*0fca6ea1SDimitry Andric       Register NegValueReg =
819*0fca6ea1SDimitry Andric           MRI->createGenericVirtualRegister(MRI->getType(ValueReg));
820*0fca6ea1SDimitry Andric       MRI->setRegClass(NegValueReg, &SPIRV::IDRegClass);
821*0fca6ea1SDimitry Andric       GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg,
822*0fca6ea1SDimitry Andric                                 MIRBuilder.getMF());
823*0fca6ea1SDimitry Andric       MIRBuilder.buildInstr(TargetOpcode::G_FNEG)
824*0fca6ea1SDimitry Andric           .addDef(NegValueReg)
825*0fca6ea1SDimitry Andric           .addUse(ValueReg);
826*0fca6ea1SDimitry Andric       insertAssignInstr(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder,
827*0fca6ea1SDimitry Andric                         MIRBuilder.getMF().getRegInfo());
828*0fca6ea1SDimitry Andric       ValueReg = NegValueReg;
829*0fca6ea1SDimitry Andric     }
830*0fca6ea1SDimitry Andric   }
831bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
832bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
833*0fca6ea1SDimitry Andric       .addUse(ValueTypeReg)
834bdd1243dSDimitry Andric       .addUse(PtrRegister)
835bdd1243dSDimitry Andric       .addUse(ScopeRegister)
836bdd1243dSDimitry Andric       .addUse(MemSemanticsReg)
837*0fca6ea1SDimitry Andric       .addUse(ValueReg);
838*0fca6ea1SDimitry Andric   return true;
839*0fca6ea1SDimitry Andric }
840*0fca6ea1SDimitry Andric 
841*0fca6ea1SDimitry Andric /// Helper function for building an atomic floating-type instruction.
842*0fca6ea1SDimitry Andric static bool buildAtomicFloatingRMWInst(const SPIRV::IncomingCall *Call,
843*0fca6ea1SDimitry Andric                                        unsigned Opcode,
844*0fca6ea1SDimitry Andric                                        MachineIRBuilder &MIRBuilder,
845*0fca6ea1SDimitry Andric                                        SPIRVGlobalRegistry *GR) {
846*0fca6ea1SDimitry Andric   assert(Call->Arguments.size() == 4 &&
847*0fca6ea1SDimitry Andric          "Wrong number of atomic floating-type builtin");
848*0fca6ea1SDimitry Andric 
849*0fca6ea1SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
850*0fca6ea1SDimitry Andric 
851*0fca6ea1SDimitry Andric   Register PtrReg = Call->Arguments[0];
852*0fca6ea1SDimitry Andric   MRI->setRegClass(PtrReg, &SPIRV::IDRegClass);
853*0fca6ea1SDimitry Andric 
854*0fca6ea1SDimitry Andric   Register ScopeReg = Call->Arguments[1];
855*0fca6ea1SDimitry Andric   MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
856*0fca6ea1SDimitry Andric 
857*0fca6ea1SDimitry Andric   Register MemSemanticsReg = Call->Arguments[2];
858*0fca6ea1SDimitry Andric   MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);
859*0fca6ea1SDimitry Andric 
860*0fca6ea1SDimitry Andric   Register ValueReg = Call->Arguments[3];
861*0fca6ea1SDimitry Andric   MRI->setRegClass(ValueReg, &SPIRV::IDRegClass);
862*0fca6ea1SDimitry Andric 
863*0fca6ea1SDimitry Andric   MIRBuilder.buildInstr(Opcode)
864*0fca6ea1SDimitry Andric       .addDef(Call->ReturnRegister)
865*0fca6ea1SDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
866*0fca6ea1SDimitry Andric       .addUse(PtrReg)
867*0fca6ea1SDimitry Andric       .addUse(ScopeReg)
868*0fca6ea1SDimitry Andric       .addUse(MemSemanticsReg)
869*0fca6ea1SDimitry Andric       .addUse(ValueReg);
870bdd1243dSDimitry Andric   return true;
871bdd1243dSDimitry Andric }
872bdd1243dSDimitry Andric 
873bdd1243dSDimitry Andric /// Helper function for building atomic flag instructions (e.g.
874bdd1243dSDimitry Andric /// OpAtomicFlagTestAndSet).
875bdd1243dSDimitry Andric static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,
876bdd1243dSDimitry Andric                                 unsigned Opcode, MachineIRBuilder &MIRBuilder,
877bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
878*0fca6ea1SDimitry Andric   bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet;
879*0fca6ea1SDimitry Andric   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
880*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
881*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
882*0fca6ea1SDimitry Andric                               IsSet ? TypeReg : Register(0));
883*0fca6ea1SDimitry Andric 
88406c3fb27SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
885bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
886bdd1243dSDimitry Andric   unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent;
88706c3fb27SDimitry Andric   Register MemSemanticsReg =
88806c3fb27SDimitry Andric       Call->Arguments.size() >= 2 ? Call->Arguments[1] : Register();
88906c3fb27SDimitry Andric   MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
89006c3fb27SDimitry Andric                                          Semantics, MIRBuilder, GR);
891bdd1243dSDimitry Andric 
892bdd1243dSDimitry Andric   assert((Opcode != SPIRV::OpAtomicFlagClear ||
893bdd1243dSDimitry Andric           (Semantics != SPIRV::MemorySemantics::Acquire &&
894bdd1243dSDimitry Andric            Semantics != SPIRV::MemorySemantics::AcquireRelease)) &&
895bdd1243dSDimitry Andric          "Invalid memory order argument!");
896bdd1243dSDimitry Andric 
89706c3fb27SDimitry Andric   Register ScopeRegister =
89806c3fb27SDimitry Andric       Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
89906c3fb27SDimitry Andric   ScopeRegister =
90006c3fb27SDimitry Andric       buildScopeReg(ScopeRegister, SPIRV::Scope::Device, MIRBuilder, GR, MRI);
901bdd1243dSDimitry Andric 
902bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode);
903*0fca6ea1SDimitry Andric   if (IsSet)
904*0fca6ea1SDimitry Andric     MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
905bdd1243dSDimitry Andric 
906bdd1243dSDimitry Andric   MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);
907bdd1243dSDimitry Andric   return true;
908bdd1243dSDimitry Andric }
909bdd1243dSDimitry Andric 
910bdd1243dSDimitry Andric /// Helper function for building barriers, i.e., memory/control ordering
911bdd1243dSDimitry Andric /// operations.
912bdd1243dSDimitry Andric static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
913bdd1243dSDimitry Andric                              MachineIRBuilder &MIRBuilder,
914bdd1243dSDimitry Andric                              SPIRVGlobalRegistry *GR) {
915*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
916*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
917*0fca6ea1SDimitry Andric 
91806c3fb27SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
919bdd1243dSDimitry Andric   unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);
920bdd1243dSDimitry Andric   unsigned MemSemantics = SPIRV::MemorySemantics::None;
921bdd1243dSDimitry Andric 
922bdd1243dSDimitry Andric   if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
923bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
924bdd1243dSDimitry Andric 
925bdd1243dSDimitry Andric   if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)
926bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;
927bdd1243dSDimitry Andric 
928bdd1243dSDimitry Andric   if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)
929bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::ImageMemory;
930bdd1243dSDimitry Andric 
931bdd1243dSDimitry Andric   if (Opcode == SPIRV::OpMemoryBarrier) {
932bdd1243dSDimitry Andric     std::memory_order MemOrder =
933bdd1243dSDimitry Andric         static_cast<std::memory_order>(getIConstVal(Call->Arguments[1], MRI));
934bdd1243dSDimitry Andric     MemSemantics = getSPIRVMemSemantics(MemOrder) | MemSemantics;
935bdd1243dSDimitry Andric   } else {
936bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;
937bdd1243dSDimitry Andric   }
938bdd1243dSDimitry Andric 
939bdd1243dSDimitry Andric   Register MemSemanticsReg;
94006c3fb27SDimitry Andric   if (MemFlags == MemSemantics) {
941bdd1243dSDimitry Andric     MemSemanticsReg = Call->Arguments[0];
94206c3fb27SDimitry Andric     MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);
94306c3fb27SDimitry Andric   } else
944bdd1243dSDimitry Andric     MemSemanticsReg = buildConstantIntReg(MemSemantics, MIRBuilder, GR);
945bdd1243dSDimitry Andric 
946bdd1243dSDimitry Andric   Register ScopeReg;
947bdd1243dSDimitry Andric   SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
948bdd1243dSDimitry Andric   SPIRV::Scope::Scope MemScope = Scope;
949bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 2) {
950bdd1243dSDimitry Andric     assert(
951bdd1243dSDimitry Andric         ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||
952bdd1243dSDimitry Andric          (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&
953bdd1243dSDimitry Andric         "Extra args for explicitly scoped barrier");
954bdd1243dSDimitry Andric     Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]
955bdd1243dSDimitry Andric                                                            : Call->Arguments[1];
956bdd1243dSDimitry Andric     SPIRV::CLMemoryScope CLScope =
957bdd1243dSDimitry Andric         static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
958bdd1243dSDimitry Andric     MemScope = getSPIRVScope(CLScope);
959bdd1243dSDimitry Andric     if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
960bdd1243dSDimitry Andric         (Opcode == SPIRV::OpMemoryBarrier))
961bdd1243dSDimitry Andric       Scope = MemScope;
962bdd1243dSDimitry Andric 
96306c3fb27SDimitry Andric     if (CLScope == static_cast<unsigned>(Scope)) {
964bdd1243dSDimitry Andric       ScopeReg = Call->Arguments[1];
96506c3fb27SDimitry Andric       MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
96606c3fb27SDimitry Andric     }
967bdd1243dSDimitry Andric   }
968bdd1243dSDimitry Andric 
969bdd1243dSDimitry Andric   if (!ScopeReg.isValid())
970bdd1243dSDimitry Andric     ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);
971bdd1243dSDimitry Andric 
972bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);
973bdd1243dSDimitry Andric   if (Opcode != SPIRV::OpMemoryBarrier)
974bdd1243dSDimitry Andric     MIB.addUse(buildConstantIntReg(MemScope, MIRBuilder, GR));
975bdd1243dSDimitry Andric   MIB.addUse(MemSemanticsReg);
976bdd1243dSDimitry Andric   return true;
977bdd1243dSDimitry Andric }
978bdd1243dSDimitry Andric 
979bdd1243dSDimitry Andric static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
980bdd1243dSDimitry Andric   switch (dim) {
981bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_1D:
982bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Buffer:
983bdd1243dSDimitry Andric     return 1;
984bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_2D:
985bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Cube:
986bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Rect:
987bdd1243dSDimitry Andric     return 2;
988bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_3D:
989bdd1243dSDimitry Andric     return 3;
990bdd1243dSDimitry Andric   default:
991*0fca6ea1SDimitry Andric     report_fatal_error("Cannot get num components for given Dim");
992bdd1243dSDimitry Andric   }
993bdd1243dSDimitry Andric }
994bdd1243dSDimitry Andric 
995bdd1243dSDimitry Andric /// Helper function for obtaining the number of size components.
996bdd1243dSDimitry Andric static unsigned getNumSizeComponents(SPIRVType *imgType) {
997bdd1243dSDimitry Andric   assert(imgType->getOpcode() == SPIRV::OpTypeImage);
998bdd1243dSDimitry Andric   auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());
999bdd1243dSDimitry Andric   unsigned numComps = getNumComponentsForDim(dim);
1000bdd1243dSDimitry Andric   bool arrayed = imgType->getOperand(4).getImm() == 1;
1001bdd1243dSDimitry Andric   return arrayed ? numComps + 1 : numComps;
1002bdd1243dSDimitry Andric }
1003bdd1243dSDimitry Andric 
1004bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
1005bdd1243dSDimitry Andric // Implementation functions for each builtin group
1006bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
1007bdd1243dSDimitry Andric 
1008bdd1243dSDimitry Andric static bool generateExtInst(const SPIRV::IncomingCall *Call,
1009bdd1243dSDimitry Andric                             MachineIRBuilder &MIRBuilder,
1010bdd1243dSDimitry Andric                             SPIRVGlobalRegistry *GR) {
1011bdd1243dSDimitry Andric   // Lookup the extended instruction number in the TableGen records.
1012bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1013bdd1243dSDimitry Andric   uint32_t Number =
1014bdd1243dSDimitry Andric       SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number;
1015bdd1243dSDimitry Andric 
1016bdd1243dSDimitry Andric   // Build extended instruction.
1017bdd1243dSDimitry Andric   auto MIB =
1018bdd1243dSDimitry Andric       MIRBuilder.buildInstr(SPIRV::OpExtInst)
1019bdd1243dSDimitry Andric           .addDef(Call->ReturnRegister)
1020bdd1243dSDimitry Andric           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1021bdd1243dSDimitry Andric           .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1022bdd1243dSDimitry Andric           .addImm(Number);
1023bdd1243dSDimitry Andric 
1024bdd1243dSDimitry Andric   for (auto Argument : Call->Arguments)
1025bdd1243dSDimitry Andric     MIB.addUse(Argument);
1026bdd1243dSDimitry Andric   return true;
1027bdd1243dSDimitry Andric }
1028bdd1243dSDimitry Andric 
1029bdd1243dSDimitry Andric static bool generateRelationalInst(const SPIRV::IncomingCall *Call,
1030bdd1243dSDimitry Andric                                    MachineIRBuilder &MIRBuilder,
1031bdd1243dSDimitry Andric                                    SPIRVGlobalRegistry *GR) {
1032bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1033bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1034bdd1243dSDimitry Andric   unsigned Opcode =
1035bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1036bdd1243dSDimitry Andric 
1037bdd1243dSDimitry Andric   Register CompareRegister;
1038bdd1243dSDimitry Andric   SPIRVType *RelationType;
1039bdd1243dSDimitry Andric   std::tie(CompareRegister, RelationType) =
1040bdd1243dSDimitry Andric       buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1041bdd1243dSDimitry Andric 
1042bdd1243dSDimitry Andric   // Build relational instruction.
1043bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode)
1044bdd1243dSDimitry Andric                  .addDef(CompareRegister)
1045bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(RelationType));
1046bdd1243dSDimitry Andric 
1047bdd1243dSDimitry Andric   for (auto Argument : Call->Arguments)
1048bdd1243dSDimitry Andric     MIB.addUse(Argument);
1049bdd1243dSDimitry Andric 
1050bdd1243dSDimitry Andric   // Build select instruction.
1051bdd1243dSDimitry Andric   return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,
1052bdd1243dSDimitry Andric                          Call->ReturnType, GR);
1053bdd1243dSDimitry Andric }
1054bdd1243dSDimitry Andric 
1055bdd1243dSDimitry Andric static bool generateGroupInst(const SPIRV::IncomingCall *Call,
1056bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
1057bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR) {
1058bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1059bdd1243dSDimitry Andric   const SPIRV::GroupBuiltin *GroupBuiltin =
1060bdd1243dSDimitry Andric       SPIRV::lookupGroupBuiltin(Builtin->Name);
1061*0fca6ea1SDimitry Andric 
106206c3fb27SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1063*0fca6ea1SDimitry Andric   if (Call->isSpirvOp()) {
1064*0fca6ea1SDimitry Andric     if (GroupBuiltin->NoGroupOperation)
1065*0fca6ea1SDimitry Andric       return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call,
1066*0fca6ea1SDimitry Andric                                 GR->getSPIRVTypeID(Call->ReturnType));
1067*0fca6ea1SDimitry Andric 
1068*0fca6ea1SDimitry Andric     // Group Operation is a literal
1069*0fca6ea1SDimitry Andric     Register GroupOpReg = Call->Arguments[1];
1070*0fca6ea1SDimitry Andric     const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI);
1071*0fca6ea1SDimitry Andric     if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT)
1072*0fca6ea1SDimitry Andric       report_fatal_error(
1073*0fca6ea1SDimitry Andric           "Group Operation parameter must be an integer constant");
1074*0fca6ea1SDimitry Andric     uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue();
1075*0fca6ea1SDimitry Andric     Register ScopeReg = Call->Arguments[0];
1076*0fca6ea1SDimitry Andric     if (!MRI->getRegClassOrNull(ScopeReg))
1077*0fca6ea1SDimitry Andric       MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
1078*0fca6ea1SDimitry Andric     auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1079*0fca6ea1SDimitry Andric                    .addDef(Call->ReturnRegister)
1080*0fca6ea1SDimitry Andric                    .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1081*0fca6ea1SDimitry Andric                    .addUse(ScopeReg)
1082*0fca6ea1SDimitry Andric                    .addImm(GrpOp);
1083*0fca6ea1SDimitry Andric     for (unsigned i = 2; i < Call->Arguments.size(); ++i) {
1084*0fca6ea1SDimitry Andric       Register ArgReg = Call->Arguments[i];
1085*0fca6ea1SDimitry Andric       if (!MRI->getRegClassOrNull(ArgReg))
1086*0fca6ea1SDimitry Andric         MRI->setRegClass(ArgReg, &SPIRV::IDRegClass);
1087*0fca6ea1SDimitry Andric       MIB.addUse(ArgReg);
1088*0fca6ea1SDimitry Andric     }
1089*0fca6ea1SDimitry Andric     return true;
1090*0fca6ea1SDimitry Andric   }
1091*0fca6ea1SDimitry Andric 
1092bdd1243dSDimitry Andric   Register Arg0;
1093bdd1243dSDimitry Andric   if (GroupBuiltin->HasBoolArg) {
1094bdd1243dSDimitry Andric     Register ConstRegister = Call->Arguments[0];
1095bdd1243dSDimitry Andric     auto ArgInstruction = getDefInstrMaybeConstant(ConstRegister, MRI);
1096*0fca6ea1SDimitry Andric     (void)ArgInstruction;
1097bdd1243dSDimitry Andric     // TODO: support non-constant bool values.
1098bdd1243dSDimitry Andric     assert(ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT &&
1099bdd1243dSDimitry Andric            "Only constant bool value args are supported");
1100bdd1243dSDimitry Andric     if (GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode() !=
1101bdd1243dSDimitry Andric         SPIRV::OpTypeBool)
1102bdd1243dSDimitry Andric       Arg0 = GR->buildConstantInt(getIConstVal(ConstRegister, MRI), MIRBuilder,
1103bdd1243dSDimitry Andric                                   GR->getOrCreateSPIRVBoolType(MIRBuilder));
1104bdd1243dSDimitry Andric   }
1105bdd1243dSDimitry Andric 
1106bdd1243dSDimitry Andric   Register GroupResultRegister = Call->ReturnRegister;
1107bdd1243dSDimitry Andric   SPIRVType *GroupResultType = Call->ReturnType;
1108bdd1243dSDimitry Andric 
1109bdd1243dSDimitry Andric   // TODO: maybe we need to check whether the result type is already boolean
1110bdd1243dSDimitry Andric   // and in this case do not insert select instruction.
1111bdd1243dSDimitry Andric   const bool HasBoolReturnTy =
1112bdd1243dSDimitry Andric       GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||
1113bdd1243dSDimitry Andric       GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||
1114bdd1243dSDimitry Andric       GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;
1115bdd1243dSDimitry Andric 
1116bdd1243dSDimitry Andric   if (HasBoolReturnTy)
1117bdd1243dSDimitry Andric     std::tie(GroupResultRegister, GroupResultType) =
1118bdd1243dSDimitry Andric         buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1119bdd1243dSDimitry Andric 
11205f757f3fSDimitry Andric   auto Scope = Builtin->Name.starts_with("sub_group") ? SPIRV::Scope::Subgroup
1121bdd1243dSDimitry Andric                                                       : SPIRV::Scope::Workgroup;
1122bdd1243dSDimitry Andric   Register ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR);
1123bdd1243dSDimitry Andric 
1124bdd1243dSDimitry Andric   // Build work/sub group instruction.
1125bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1126bdd1243dSDimitry Andric                  .addDef(GroupResultRegister)
1127bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(GroupResultType))
1128bdd1243dSDimitry Andric                  .addUse(ScopeRegister);
1129bdd1243dSDimitry Andric 
1130bdd1243dSDimitry Andric   if (!GroupBuiltin->NoGroupOperation)
1131bdd1243dSDimitry Andric     MIB.addImm(GroupBuiltin->GroupOperation);
1132bdd1243dSDimitry Andric   if (Call->Arguments.size() > 0) {
1133bdd1243dSDimitry Andric     MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);
113406c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
113506c3fb27SDimitry Andric     for (unsigned i = 1; i < Call->Arguments.size(); i++) {
1136bdd1243dSDimitry Andric       MIB.addUse(Call->Arguments[i]);
113706c3fb27SDimitry Andric       MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass);
113806c3fb27SDimitry Andric     }
1139bdd1243dSDimitry Andric   }
1140bdd1243dSDimitry Andric 
1141bdd1243dSDimitry Andric   // Build select instruction.
1142bdd1243dSDimitry Andric   if (HasBoolReturnTy)
1143bdd1243dSDimitry Andric     buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,
1144bdd1243dSDimitry Andric                     Call->ReturnType, GR);
1145bdd1243dSDimitry Andric   return true;
1146bdd1243dSDimitry Andric }
1147bdd1243dSDimitry Andric 
1148*0fca6ea1SDimitry Andric static bool generateIntelSubgroupsInst(const SPIRV::IncomingCall *Call,
1149*0fca6ea1SDimitry Andric                                        MachineIRBuilder &MIRBuilder,
1150*0fca6ea1SDimitry Andric                                        SPIRVGlobalRegistry *GR) {
1151*0fca6ea1SDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1152*0fca6ea1SDimitry Andric   MachineFunction &MF = MIRBuilder.getMF();
1153*0fca6ea1SDimitry Andric   const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1154*0fca6ea1SDimitry Andric   if (!ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1155*0fca6ea1SDimitry Andric     std::string DiagMsg = std::string(Builtin->Name) +
1156*0fca6ea1SDimitry Andric                           ": the builtin requires the following SPIR-V "
1157*0fca6ea1SDimitry Andric                           "extension: SPV_INTEL_subgroups";
1158*0fca6ea1SDimitry Andric     report_fatal_error(DiagMsg.c_str(), false);
1159*0fca6ea1SDimitry Andric   }
1160*0fca6ea1SDimitry Andric   const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups =
1161*0fca6ea1SDimitry Andric       SPIRV::lookupIntelSubgroupsBuiltin(Builtin->Name);
1162*0fca6ea1SDimitry Andric 
1163*0fca6ea1SDimitry Andric   uint32_t OpCode = IntelSubgroups->Opcode;
1164*0fca6ea1SDimitry Andric   if (Call->isSpirvOp()) {
1165*0fca6ea1SDimitry Andric     bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL &&
1166*0fca6ea1SDimitry Andric                  OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL;
1167*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, OpCode, Call,
1168*0fca6ea1SDimitry Andric                               IsSet ? GR->getSPIRVTypeID(Call->ReturnType)
1169*0fca6ea1SDimitry Andric                                     : Register(0));
1170*0fca6ea1SDimitry Andric   }
1171*0fca6ea1SDimitry Andric 
1172*0fca6ea1SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1173*0fca6ea1SDimitry Andric   if (IntelSubgroups->IsBlock) {
1174*0fca6ea1SDimitry Andric     // Minimal number or arguments set in TableGen records is 1
1175*0fca6ea1SDimitry Andric     if (SPIRVType *Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) {
1176*0fca6ea1SDimitry Andric       if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) {
1177*0fca6ea1SDimitry Andric         // TODO: add required validation from the specification:
1178*0fca6ea1SDimitry Andric         // "'Image' must be an object whose type is OpTypeImage with a 'Sampled'
1179*0fca6ea1SDimitry Andric         // operand of 0 or 2. If the 'Sampled' operand is 2, then some
1180*0fca6ea1SDimitry Andric         // dimensions require a capability."
1181*0fca6ea1SDimitry Andric         switch (OpCode) {
1182*0fca6ea1SDimitry Andric         case SPIRV::OpSubgroupBlockReadINTEL:
1183*0fca6ea1SDimitry Andric           OpCode = SPIRV::OpSubgroupImageBlockReadINTEL;
1184*0fca6ea1SDimitry Andric           break;
1185*0fca6ea1SDimitry Andric         case SPIRV::OpSubgroupBlockWriteINTEL:
1186*0fca6ea1SDimitry Andric           OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL;
1187*0fca6ea1SDimitry Andric           break;
1188*0fca6ea1SDimitry Andric         }
1189*0fca6ea1SDimitry Andric       }
1190*0fca6ea1SDimitry Andric     }
1191*0fca6ea1SDimitry Andric   }
1192*0fca6ea1SDimitry Andric 
1193*0fca6ea1SDimitry Andric   // TODO: opaque pointers types should be eventually resolved in such a way
1194*0fca6ea1SDimitry Andric   // that validation of block read is enabled with respect to the following
1195*0fca6ea1SDimitry Andric   // specification requirement:
1196*0fca6ea1SDimitry Andric   // "'Result Type' may be a scalar or vector type, and its component type must
1197*0fca6ea1SDimitry Andric   // be equal to the type pointed to by 'Ptr'."
1198*0fca6ea1SDimitry Andric   // For example, function parameter type should not be default i8 pointer, but
1199*0fca6ea1SDimitry Andric   // depend on the result type of the instruction where it is used as a pointer
1200*0fca6ea1SDimitry Andric   // argument of OpSubgroupBlockReadINTEL
1201*0fca6ea1SDimitry Andric 
1202*0fca6ea1SDimitry Andric   // Build Intel subgroups instruction
1203*0fca6ea1SDimitry Andric   MachineInstrBuilder MIB =
1204*0fca6ea1SDimitry Andric       IntelSubgroups->IsWrite
1205*0fca6ea1SDimitry Andric           ? MIRBuilder.buildInstr(OpCode)
1206*0fca6ea1SDimitry Andric           : MIRBuilder.buildInstr(OpCode)
1207*0fca6ea1SDimitry Andric                 .addDef(Call->ReturnRegister)
1208*0fca6ea1SDimitry Andric                 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1209*0fca6ea1SDimitry Andric   for (size_t i = 0; i < Call->Arguments.size(); ++i) {
1210*0fca6ea1SDimitry Andric     MIB.addUse(Call->Arguments[i]);
1211*0fca6ea1SDimitry Andric     MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass);
1212*0fca6ea1SDimitry Andric   }
1213*0fca6ea1SDimitry Andric 
1214*0fca6ea1SDimitry Andric   return true;
1215*0fca6ea1SDimitry Andric }
1216*0fca6ea1SDimitry Andric 
1217*0fca6ea1SDimitry Andric static bool generateGroupUniformInst(const SPIRV::IncomingCall *Call,
1218*0fca6ea1SDimitry Andric                                      MachineIRBuilder &MIRBuilder,
1219*0fca6ea1SDimitry Andric                                      SPIRVGlobalRegistry *GR) {
1220*0fca6ea1SDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1221*0fca6ea1SDimitry Andric   MachineFunction &MF = MIRBuilder.getMF();
1222*0fca6ea1SDimitry Andric   const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1223*0fca6ea1SDimitry Andric   if (!ST->canUseExtension(
1224*0fca6ea1SDimitry Andric           SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {
1225*0fca6ea1SDimitry Andric     std::string DiagMsg = std::string(Builtin->Name) +
1226*0fca6ea1SDimitry Andric                           ": the builtin requires the following SPIR-V "
1227*0fca6ea1SDimitry Andric                           "extension: SPV_KHR_uniform_group_instructions";
1228*0fca6ea1SDimitry Andric     report_fatal_error(DiagMsg.c_str(), false);
1229*0fca6ea1SDimitry Andric   }
1230*0fca6ea1SDimitry Andric   const SPIRV::GroupUniformBuiltin *GroupUniform =
1231*0fca6ea1SDimitry Andric       SPIRV::lookupGroupUniformBuiltin(Builtin->Name);
1232*0fca6ea1SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1233*0fca6ea1SDimitry Andric 
1234*0fca6ea1SDimitry Andric   Register GroupResultReg = Call->ReturnRegister;
1235*0fca6ea1SDimitry Andric   MRI->setRegClass(GroupResultReg, &SPIRV::IDRegClass);
1236*0fca6ea1SDimitry Andric 
1237*0fca6ea1SDimitry Andric   // Scope
1238*0fca6ea1SDimitry Andric   Register ScopeReg = Call->Arguments[0];
1239*0fca6ea1SDimitry Andric   MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
1240*0fca6ea1SDimitry Andric 
1241*0fca6ea1SDimitry Andric   // Group Operation
1242*0fca6ea1SDimitry Andric   Register ConstGroupOpReg = Call->Arguments[1];
1243*0fca6ea1SDimitry Andric   const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI);
1244*0fca6ea1SDimitry Andric   if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT)
1245*0fca6ea1SDimitry Andric     report_fatal_error(
1246*0fca6ea1SDimitry Andric         "expect a constant group operation for a uniform group instruction",
1247*0fca6ea1SDimitry Andric         false);
1248*0fca6ea1SDimitry Andric   const MachineOperand &ConstOperand = Const->getOperand(1);
1249*0fca6ea1SDimitry Andric   if (!ConstOperand.isCImm())
1250*0fca6ea1SDimitry Andric     report_fatal_error("uniform group instructions: group operation must be an "
1251*0fca6ea1SDimitry Andric                        "integer constant",
1252*0fca6ea1SDimitry Andric                        false);
1253*0fca6ea1SDimitry Andric 
1254*0fca6ea1SDimitry Andric   // Value
1255*0fca6ea1SDimitry Andric   Register ValueReg = Call->Arguments[2];
1256*0fca6ea1SDimitry Andric   MRI->setRegClass(ValueReg, &SPIRV::IDRegClass);
1257*0fca6ea1SDimitry Andric 
1258*0fca6ea1SDimitry Andric   auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode)
1259*0fca6ea1SDimitry Andric                  .addDef(GroupResultReg)
1260*0fca6ea1SDimitry Andric                  .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1261*0fca6ea1SDimitry Andric                  .addUse(ScopeReg);
1262*0fca6ea1SDimitry Andric   addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1263*0fca6ea1SDimitry Andric   MIB.addUse(ValueReg);
1264*0fca6ea1SDimitry Andric 
1265*0fca6ea1SDimitry Andric   return true;
1266*0fca6ea1SDimitry Andric }
1267*0fca6ea1SDimitry Andric 
1268*0fca6ea1SDimitry Andric static bool generateKernelClockInst(const SPIRV::IncomingCall *Call,
1269*0fca6ea1SDimitry Andric                                     MachineIRBuilder &MIRBuilder,
1270*0fca6ea1SDimitry Andric                                     SPIRVGlobalRegistry *GR) {
1271*0fca6ea1SDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1272*0fca6ea1SDimitry Andric   MachineFunction &MF = MIRBuilder.getMF();
1273*0fca6ea1SDimitry Andric   const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1274*0fca6ea1SDimitry Andric   if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) {
1275*0fca6ea1SDimitry Andric     std::string DiagMsg = std::string(Builtin->Name) +
1276*0fca6ea1SDimitry Andric                           ": the builtin requires the following SPIR-V "
1277*0fca6ea1SDimitry Andric                           "extension: SPV_KHR_shader_clock";
1278*0fca6ea1SDimitry Andric     report_fatal_error(DiagMsg.c_str(), false);
1279*0fca6ea1SDimitry Andric   }
1280*0fca6ea1SDimitry Andric 
1281*0fca6ea1SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1282*0fca6ea1SDimitry Andric   Register ResultReg = Call->ReturnRegister;
1283*0fca6ea1SDimitry Andric   MRI->setRegClass(ResultReg, &SPIRV::IDRegClass);
1284*0fca6ea1SDimitry Andric 
1285*0fca6ea1SDimitry Andric   // Deduce the `Scope` operand from the builtin function name.
1286*0fca6ea1SDimitry Andric   SPIRV::Scope::Scope ScopeArg =
1287*0fca6ea1SDimitry Andric       StringSwitch<SPIRV::Scope::Scope>(Builtin->Name)
1288*0fca6ea1SDimitry Andric           .EndsWith("device", SPIRV::Scope::Scope::Device)
1289*0fca6ea1SDimitry Andric           .EndsWith("work_group", SPIRV::Scope::Scope::Workgroup)
1290*0fca6ea1SDimitry Andric           .EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup);
1291*0fca6ea1SDimitry Andric   Register ScopeReg = buildConstantIntReg(ScopeArg, MIRBuilder, GR);
1292*0fca6ea1SDimitry Andric 
1293*0fca6ea1SDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
1294*0fca6ea1SDimitry Andric       .addDef(ResultReg)
1295*0fca6ea1SDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1296*0fca6ea1SDimitry Andric       .addUse(ScopeReg);
1297*0fca6ea1SDimitry Andric 
1298*0fca6ea1SDimitry Andric   return true;
1299*0fca6ea1SDimitry Andric }
1300*0fca6ea1SDimitry Andric 
1301bdd1243dSDimitry Andric // These queries ask for a single size_t result for a given dimension index, e.g
1302bdd1243dSDimitry Andric // size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to
1303bdd1243dSDimitry Andric // these values are all vec3 types, so we need to extract the correct index or
1304bdd1243dSDimitry Andric // return defaultVal (0 or 1 depending on the query). We also handle extending
1305bdd1243dSDimitry Andric // or tuncating in case size_t does not match the expected result type's
1306bdd1243dSDimitry Andric // bitwidth.
1307bdd1243dSDimitry Andric //
1308bdd1243dSDimitry Andric // For a constant index >= 3 we generate:
1309bdd1243dSDimitry Andric //  %res = OpConstant %SizeT 0
1310bdd1243dSDimitry Andric //
1311bdd1243dSDimitry Andric // For other indices we generate:
1312bdd1243dSDimitry Andric //  %g = OpVariable %ptr_V3_SizeT Input
1313bdd1243dSDimitry Andric //  OpDecorate %g BuiltIn XXX
1314bdd1243dSDimitry Andric //  OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"
1315bdd1243dSDimitry Andric //  OpDecorate %g Constant
1316bdd1243dSDimitry Andric //  %loadedVec = OpLoad %V3_SizeT %g
1317bdd1243dSDimitry Andric //
1318bdd1243dSDimitry Andric //  Then, if the index is constant < 3, we generate:
1319bdd1243dSDimitry Andric //    %res = OpCompositeExtract %SizeT %loadedVec idx
1320bdd1243dSDimitry Andric //  If the index is dynamic, we generate:
1321bdd1243dSDimitry Andric //    %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx
1322bdd1243dSDimitry Andric //    %cmp = OpULessThan %bool %idx %const_3
1323bdd1243dSDimitry Andric //    %res = OpSelect %SizeT %cmp %tmp %const_0
1324bdd1243dSDimitry Andric //
1325bdd1243dSDimitry Andric //  If the bitwidth of %res does not match the expected return type, we add an
1326bdd1243dSDimitry Andric //  extend or truncate.
1327bdd1243dSDimitry Andric static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call,
1328bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
1329bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR,
1330bdd1243dSDimitry Andric                               SPIRV::BuiltIn::BuiltIn BuiltinValue,
1331bdd1243dSDimitry Andric                               uint64_t DefaultValue) {
1332bdd1243dSDimitry Andric   Register IndexRegister = Call->Arguments[0];
1333bdd1243dSDimitry Andric   const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
1334bdd1243dSDimitry Andric   const unsigned PointerSize = GR->getPointerSize();
1335bdd1243dSDimitry Andric   const SPIRVType *PointerSizeType =
1336bdd1243dSDimitry Andric       GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);
1337bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1338bdd1243dSDimitry Andric   auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);
1339bdd1243dSDimitry Andric 
1340bdd1243dSDimitry Andric   // Set up the final register to do truncation or extension on at the end.
1341bdd1243dSDimitry Andric   Register ToTruncate = Call->ReturnRegister;
1342bdd1243dSDimitry Andric 
1343bdd1243dSDimitry Andric   // If the index is constant, we can statically determine if it is in range.
1344bdd1243dSDimitry Andric   bool IsConstantIndex =
1345bdd1243dSDimitry Andric       IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;
1346bdd1243dSDimitry Andric 
1347bdd1243dSDimitry Andric   // If it's out of range (max dimension is 3), we can just return the constant
1348bdd1243dSDimitry Andric   // default value (0 or 1 depending on which query function).
1349bdd1243dSDimitry Andric   if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {
135006c3fb27SDimitry Andric     Register DefaultReg = Call->ReturnRegister;
1351bdd1243dSDimitry Andric     if (PointerSize != ResultWidth) {
135206c3fb27SDimitry Andric       DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
135306c3fb27SDimitry Andric       MRI->setRegClass(DefaultReg, &SPIRV::IDRegClass);
135406c3fb27SDimitry Andric       GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg,
1355bdd1243dSDimitry Andric                                 MIRBuilder.getMF());
135606c3fb27SDimitry Andric       ToTruncate = DefaultReg;
1357bdd1243dSDimitry Andric     }
1358bdd1243dSDimitry Andric     auto NewRegister =
1359bdd1243dSDimitry Andric         GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
136006c3fb27SDimitry Andric     MIRBuilder.buildCopy(DefaultReg, NewRegister);
1361bdd1243dSDimitry Andric   } else { // If it could be in range, we need to load from the given builtin.
1362bdd1243dSDimitry Andric     auto Vec3Ty =
1363bdd1243dSDimitry Andric         GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder);
1364bdd1243dSDimitry Andric     Register LoadedVector =
1365bdd1243dSDimitry Andric         buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,
1366bdd1243dSDimitry Andric                                  LLT::fixed_vector(3, PointerSize));
1367bdd1243dSDimitry Andric     // Set up the vreg to extract the result to (possibly a new temporary one).
1368bdd1243dSDimitry Andric     Register Extracted = Call->ReturnRegister;
1369bdd1243dSDimitry Andric     if (!IsConstantIndex || PointerSize != ResultWidth) {
1370bdd1243dSDimitry Andric       Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
137106c3fb27SDimitry Andric       MRI->setRegClass(Extracted, &SPIRV::IDRegClass);
1372bdd1243dSDimitry Andric       GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());
1373bdd1243dSDimitry Andric     }
1374bdd1243dSDimitry Andric     // Use Intrinsic::spv_extractelt so dynamic vs static extraction is
1375bdd1243dSDimitry Andric     // handled later: extr = spv_extractelt LoadedVector, IndexRegister.
1376bdd1243dSDimitry Andric     MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(
13775f757f3fSDimitry Andric         Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false);
1378bdd1243dSDimitry Andric     ExtractInst.addUse(LoadedVector).addUse(IndexRegister);
1379bdd1243dSDimitry Andric 
1380bdd1243dSDimitry Andric     // If the index is dynamic, need check if it's < 3, and then use a select.
1381bdd1243dSDimitry Andric     if (!IsConstantIndex) {
1382bdd1243dSDimitry Andric       insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder,
1383bdd1243dSDimitry Andric                         *MRI);
1384bdd1243dSDimitry Andric 
1385bdd1243dSDimitry Andric       auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);
1386bdd1243dSDimitry Andric       auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
1387bdd1243dSDimitry Andric 
1388bdd1243dSDimitry Andric       Register CompareRegister =
1389bdd1243dSDimitry Andric           MRI->createGenericVirtualRegister(LLT::scalar(1));
139006c3fb27SDimitry Andric       MRI->setRegClass(CompareRegister, &SPIRV::IDRegClass);
1391bdd1243dSDimitry Andric       GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());
1392bdd1243dSDimitry Andric 
1393bdd1243dSDimitry Andric       // Use G_ICMP to check if idxVReg < 3.
1394bdd1243dSDimitry Andric       MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister,
1395bdd1243dSDimitry Andric                            GR->buildConstantInt(3, MIRBuilder, IndexType));
1396bdd1243dSDimitry Andric 
1397bdd1243dSDimitry Andric       // Get constant for the default value (0 or 1 depending on which
1398bdd1243dSDimitry Andric       // function).
1399bdd1243dSDimitry Andric       Register DefaultRegister =
1400bdd1243dSDimitry Andric           GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
1401bdd1243dSDimitry Andric 
1402bdd1243dSDimitry Andric       // Get a register for the selection result (possibly a new temporary one).
1403bdd1243dSDimitry Andric       Register SelectionResult = Call->ReturnRegister;
1404bdd1243dSDimitry Andric       if (PointerSize != ResultWidth) {
1405bdd1243dSDimitry Andric         SelectionResult =
1406bdd1243dSDimitry Andric             MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
140706c3fb27SDimitry Andric         MRI->setRegClass(SelectionResult, &SPIRV::IDRegClass);
1408bdd1243dSDimitry Andric         GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,
1409bdd1243dSDimitry Andric                                   MIRBuilder.getMF());
1410bdd1243dSDimitry Andric       }
1411bdd1243dSDimitry Andric       // Create the final G_SELECT to return the extracted value or the default.
1412bdd1243dSDimitry Andric       MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,
1413bdd1243dSDimitry Andric                              DefaultRegister);
1414bdd1243dSDimitry Andric       ToTruncate = SelectionResult;
1415bdd1243dSDimitry Andric     } else {
1416bdd1243dSDimitry Andric       ToTruncate = Extracted;
1417bdd1243dSDimitry Andric     }
1418bdd1243dSDimitry Andric   }
1419bdd1243dSDimitry Andric   // Alter the result's bitwidth if it does not match the SizeT value extracted.
1420bdd1243dSDimitry Andric   if (PointerSize != ResultWidth)
1421bdd1243dSDimitry Andric     MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);
1422bdd1243dSDimitry Andric   return true;
1423bdd1243dSDimitry Andric }
1424bdd1243dSDimitry Andric 
1425bdd1243dSDimitry Andric static bool generateBuiltinVar(const SPIRV::IncomingCall *Call,
1426bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
1427bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
1428bdd1243dSDimitry Andric   // Lookup the builtin variable record.
1429bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1430bdd1243dSDimitry Andric   SPIRV::BuiltIn::BuiltIn Value =
1431bdd1243dSDimitry Andric       SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1432bdd1243dSDimitry Andric 
1433bdd1243dSDimitry Andric   if (Value == SPIRV::BuiltIn::GlobalInvocationId)
1434bdd1243dSDimitry Andric     return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);
1435bdd1243dSDimitry Andric 
1436bdd1243dSDimitry Andric   // Build a load instruction for the builtin variable.
1437bdd1243dSDimitry Andric   unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);
1438bdd1243dSDimitry Andric   LLT LLType;
1439bdd1243dSDimitry Andric   if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector)
1440bdd1243dSDimitry Andric     LLType =
1441bdd1243dSDimitry Andric         LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth);
1442bdd1243dSDimitry Andric   else
1443bdd1243dSDimitry Andric     LLType = LLT::scalar(BitWidth);
1444bdd1243dSDimitry Andric 
1445bdd1243dSDimitry Andric   return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,
1446bdd1243dSDimitry Andric                                   LLType, Call->ReturnRegister);
1447bdd1243dSDimitry Andric }
1448bdd1243dSDimitry Andric 
1449bdd1243dSDimitry Andric static bool generateAtomicInst(const SPIRV::IncomingCall *Call,
1450bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
1451bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
1452bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1453bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1454bdd1243dSDimitry Andric   unsigned Opcode =
1455bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1456bdd1243dSDimitry Andric 
1457bdd1243dSDimitry Andric   switch (Opcode) {
1458bdd1243dSDimitry Andric   case SPIRV::OpStore:
1459bdd1243dSDimitry Andric     return buildAtomicInitInst(Call, MIRBuilder);
1460bdd1243dSDimitry Andric   case SPIRV::OpAtomicLoad:
1461bdd1243dSDimitry Andric     return buildAtomicLoadInst(Call, MIRBuilder, GR);
1462bdd1243dSDimitry Andric   case SPIRV::OpAtomicStore:
1463bdd1243dSDimitry Andric     return buildAtomicStoreInst(Call, MIRBuilder, GR);
1464bdd1243dSDimitry Andric   case SPIRV::OpAtomicCompareExchange:
1465bdd1243dSDimitry Andric   case SPIRV::OpAtomicCompareExchangeWeak:
1466*0fca6ea1SDimitry Andric     return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder,
1467*0fca6ea1SDimitry Andric                                           GR);
1468bdd1243dSDimitry Andric   case SPIRV::OpAtomicIAdd:
1469bdd1243dSDimitry Andric   case SPIRV::OpAtomicISub:
1470bdd1243dSDimitry Andric   case SPIRV::OpAtomicOr:
1471bdd1243dSDimitry Andric   case SPIRV::OpAtomicXor:
1472bdd1243dSDimitry Andric   case SPIRV::OpAtomicAnd:
1473bdd1243dSDimitry Andric   case SPIRV::OpAtomicExchange:
1474bdd1243dSDimitry Andric     return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
1475bdd1243dSDimitry Andric   case SPIRV::OpMemoryBarrier:
1476bdd1243dSDimitry Andric     return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
1477bdd1243dSDimitry Andric   case SPIRV::OpAtomicFlagTestAndSet:
1478bdd1243dSDimitry Andric   case SPIRV::OpAtomicFlagClear:
1479bdd1243dSDimitry Andric     return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);
1480bdd1243dSDimitry Andric   default:
1481*0fca6ea1SDimitry Andric     if (Call->isSpirvOp())
1482*0fca6ea1SDimitry Andric       return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1483*0fca6ea1SDimitry Andric                                 GR->getSPIRVTypeID(Call->ReturnType));
1484*0fca6ea1SDimitry Andric     return false;
1485*0fca6ea1SDimitry Andric   }
1486*0fca6ea1SDimitry Andric }
1487*0fca6ea1SDimitry Andric 
1488*0fca6ea1SDimitry Andric static bool generateAtomicFloatingInst(const SPIRV::IncomingCall *Call,
1489*0fca6ea1SDimitry Andric                                        MachineIRBuilder &MIRBuilder,
1490*0fca6ea1SDimitry Andric                                        SPIRVGlobalRegistry *GR) {
1491*0fca6ea1SDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1492*0fca6ea1SDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1493*0fca6ea1SDimitry Andric   unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->Name)->Opcode;
1494*0fca6ea1SDimitry Andric 
1495*0fca6ea1SDimitry Andric   switch (Opcode) {
1496*0fca6ea1SDimitry Andric   case SPIRV::OpAtomicFAddEXT:
1497*0fca6ea1SDimitry Andric   case SPIRV::OpAtomicFMinEXT:
1498*0fca6ea1SDimitry Andric   case SPIRV::OpAtomicFMaxEXT:
1499*0fca6ea1SDimitry Andric     return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR);
1500*0fca6ea1SDimitry Andric   default:
1501bdd1243dSDimitry Andric     return false;
1502bdd1243dSDimitry Andric   }
1503bdd1243dSDimitry Andric }
1504bdd1243dSDimitry Andric 
1505bdd1243dSDimitry Andric static bool generateBarrierInst(const SPIRV::IncomingCall *Call,
1506bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
1507bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
1508bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1509bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1510bdd1243dSDimitry Andric   unsigned Opcode =
1511bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1512bdd1243dSDimitry Andric 
1513bdd1243dSDimitry Andric   return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
1514bdd1243dSDimitry Andric }
1515bdd1243dSDimitry Andric 
1516*0fca6ea1SDimitry Andric static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call,
1517*0fca6ea1SDimitry Andric                                   MachineIRBuilder &MIRBuilder) {
1518*0fca6ea1SDimitry Andric   MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)
1519*0fca6ea1SDimitry Andric       .addDef(Call->ReturnRegister)
1520*0fca6ea1SDimitry Andric       .addUse(Call->Arguments[0]);
1521*0fca6ea1SDimitry Andric   return true;
1522*0fca6ea1SDimitry Andric }
1523*0fca6ea1SDimitry Andric 
1524bdd1243dSDimitry Andric static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call,
1525bdd1243dSDimitry Andric                                   MachineIRBuilder &MIRBuilder,
1526bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR) {
1527*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
1528*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call,
1529*0fca6ea1SDimitry Andric                               GR->getSPIRVTypeID(Call->ReturnType));
1530bdd1243dSDimitry Andric   unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode();
1531bdd1243dSDimitry Andric   bool IsVec = Opcode == SPIRV::OpTypeVector;
1532bdd1243dSDimitry Andric   // Use OpDot only in case of vector args and OpFMul in case of scalar args.
1533bdd1243dSDimitry Andric   MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS)
1534bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
1535bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1536bdd1243dSDimitry Andric       .addUse(Call->Arguments[0])
1537bdd1243dSDimitry Andric       .addUse(Call->Arguments[1]);
1538bdd1243dSDimitry Andric   return true;
1539bdd1243dSDimitry Andric }
1540bdd1243dSDimitry Andric 
1541*0fca6ea1SDimitry Andric static bool generateWaveInst(const SPIRV::IncomingCall *Call,
1542*0fca6ea1SDimitry Andric                              MachineIRBuilder &MIRBuilder,
1543*0fca6ea1SDimitry Andric                              SPIRVGlobalRegistry *GR) {
1544*0fca6ea1SDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1545*0fca6ea1SDimitry Andric   SPIRV::BuiltIn::BuiltIn Value =
1546*0fca6ea1SDimitry Andric       SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1547*0fca6ea1SDimitry Andric 
1548*0fca6ea1SDimitry Andric   // For now, we only support a single Wave intrinsic with a single return type.
1549*0fca6ea1SDimitry Andric   assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt);
1550*0fca6ea1SDimitry Andric   LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType));
1551*0fca6ea1SDimitry Andric 
1552*0fca6ea1SDimitry Andric   return buildBuiltinVariableLoad(
1553*0fca6ea1SDimitry Andric       MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister,
1554*0fca6ea1SDimitry Andric       /* isConst= */ false, /* hasLinkageTy= */ false);
1555*0fca6ea1SDimitry Andric }
1556*0fca6ea1SDimitry Andric 
1557bdd1243dSDimitry Andric static bool generateGetQueryInst(const SPIRV::IncomingCall *Call,
1558bdd1243dSDimitry Andric                                  MachineIRBuilder &MIRBuilder,
1559bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
1560bdd1243dSDimitry Andric   // Lookup the builtin record.
1561bdd1243dSDimitry Andric   SPIRV::BuiltIn::BuiltIn Value =
1562bdd1243dSDimitry Andric       SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value;
1563bdd1243dSDimitry Andric   uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize ||
1564bdd1243dSDimitry Andric                         Value == SPIRV::BuiltIn::WorkgroupSize ||
1565bdd1243dSDimitry Andric                         Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);
1566bdd1243dSDimitry Andric   return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0);
1567bdd1243dSDimitry Andric }
1568bdd1243dSDimitry Andric 
1569bdd1243dSDimitry Andric static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call,
1570bdd1243dSDimitry Andric                                        MachineIRBuilder &MIRBuilder,
1571bdd1243dSDimitry Andric                                        SPIRVGlobalRegistry *GR) {
1572bdd1243dSDimitry Andric   // Lookup the image size query component number in the TableGen records.
1573bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1574bdd1243dSDimitry Andric   uint32_t Component =
1575bdd1243dSDimitry Andric       SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component;
1576bdd1243dSDimitry Andric   // Query result may either be a vector or a scalar. If return type is not a
1577bdd1243dSDimitry Andric   // vector, expect only a single size component. Otherwise get the number of
1578bdd1243dSDimitry Andric   // expected components.
1579bdd1243dSDimitry Andric   SPIRVType *RetTy = Call->ReturnType;
1580bdd1243dSDimitry Andric   unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector
1581bdd1243dSDimitry Andric                                           ? RetTy->getOperand(2).getImm()
1582bdd1243dSDimitry Andric                                           : 1;
1583bdd1243dSDimitry Andric   // Get the actual number of query result/size components.
1584bdd1243dSDimitry Andric   SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1585bdd1243dSDimitry Andric   unsigned NumActualRetComponents = getNumSizeComponents(ImgType);
1586bdd1243dSDimitry Andric   Register QueryResult = Call->ReturnRegister;
1587bdd1243dSDimitry Andric   SPIRVType *QueryResultType = Call->ReturnType;
1588bdd1243dSDimitry Andric   if (NumExpectedRetComponents != NumActualRetComponents) {
1589bdd1243dSDimitry Andric     QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(
1590bdd1243dSDimitry Andric         LLT::fixed_vector(NumActualRetComponents, 32));
159106c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::IDRegClass);
1592bdd1243dSDimitry Andric     SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
1593bdd1243dSDimitry Andric     QueryResultType = GR->getOrCreateSPIRVVectorType(
1594bdd1243dSDimitry Andric         IntTy, NumActualRetComponents, MIRBuilder);
1595bdd1243dSDimitry Andric     GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());
1596bdd1243dSDimitry Andric   }
1597bdd1243dSDimitry Andric   bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;
1598bdd1243dSDimitry Andric   unsigned Opcode =
1599bdd1243dSDimitry Andric       IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;
160006c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
1601bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode)
1602bdd1243dSDimitry Andric                  .addDef(QueryResult)
1603bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(QueryResultType))
1604bdd1243dSDimitry Andric                  .addUse(Call->Arguments[0]);
1605bdd1243dSDimitry Andric   if (!IsDimBuf)
1606bdd1243dSDimitry Andric     MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Lod id.
1607bdd1243dSDimitry Andric   if (NumExpectedRetComponents == NumActualRetComponents)
1608bdd1243dSDimitry Andric     return true;
1609bdd1243dSDimitry Andric   if (NumExpectedRetComponents == 1) {
1610bdd1243dSDimitry Andric     // Only 1 component is expected, build OpCompositeExtract instruction.
1611bdd1243dSDimitry Andric     unsigned ExtractedComposite =
1612bdd1243dSDimitry Andric         Component == 3 ? NumActualRetComponents - 1 : Component;
1613bdd1243dSDimitry Andric     assert(ExtractedComposite < NumActualRetComponents &&
1614bdd1243dSDimitry Andric            "Invalid composite index!");
1615*0fca6ea1SDimitry Andric     Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
1616*0fca6ea1SDimitry Andric     SPIRVType *NewType = nullptr;
1617*0fca6ea1SDimitry Andric     if (QueryResultType->getOpcode() == SPIRV::OpTypeVector) {
1618*0fca6ea1SDimitry Andric       Register NewTypeReg = QueryResultType->getOperand(1).getReg();
1619*0fca6ea1SDimitry Andric       if (TypeReg != NewTypeReg &&
1620*0fca6ea1SDimitry Andric           (NewType = GR->getSPIRVTypeForVReg(NewTypeReg)) != nullptr)
1621*0fca6ea1SDimitry Andric         TypeReg = NewTypeReg;
1622*0fca6ea1SDimitry Andric     }
1623bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1624bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1625*0fca6ea1SDimitry Andric         .addUse(TypeReg)
1626bdd1243dSDimitry Andric         .addUse(QueryResult)
1627bdd1243dSDimitry Andric         .addImm(ExtractedComposite);
1628*0fca6ea1SDimitry Andric     if (NewType != nullptr)
1629*0fca6ea1SDimitry Andric       insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
1630*0fca6ea1SDimitry Andric                         MIRBuilder.getMF().getRegInfo());
1631bdd1243dSDimitry Andric   } else {
1632bdd1243dSDimitry Andric     // More than 1 component is expected, fill a new vector.
1633bdd1243dSDimitry Andric     auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)
1634bdd1243dSDimitry Andric                    .addDef(Call->ReturnRegister)
1635bdd1243dSDimitry Andric                    .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1636bdd1243dSDimitry Andric                    .addUse(QueryResult)
1637bdd1243dSDimitry Andric                    .addUse(QueryResult);
1638bdd1243dSDimitry Andric     for (unsigned i = 0; i < NumExpectedRetComponents; ++i)
1639bdd1243dSDimitry Andric       MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);
1640bdd1243dSDimitry Andric   }
1641bdd1243dSDimitry Andric   return true;
1642bdd1243dSDimitry Andric }
1643bdd1243dSDimitry Andric 
1644bdd1243dSDimitry Andric static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call,
1645bdd1243dSDimitry Andric                                        MachineIRBuilder &MIRBuilder,
1646bdd1243dSDimitry Andric                                        SPIRVGlobalRegistry *GR) {
1647bdd1243dSDimitry Andric   assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&
1648bdd1243dSDimitry Andric          "Image samples query result must be of int type!");
1649bdd1243dSDimitry Andric 
1650bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1651bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1652bdd1243dSDimitry Andric   unsigned Opcode =
1653bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1654bdd1243dSDimitry Andric 
1655bdd1243dSDimitry Andric   Register Image = Call->Arguments[0];
165606c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Image, &SPIRV::IDRegClass);
1657bdd1243dSDimitry Andric   SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(
1658bdd1243dSDimitry Andric       GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());
1659*0fca6ea1SDimitry Andric   (void)ImageDimensionality;
1660bdd1243dSDimitry Andric 
1661bdd1243dSDimitry Andric   switch (Opcode) {
1662bdd1243dSDimitry Andric   case SPIRV::OpImageQuerySamples:
1663bdd1243dSDimitry Andric     assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&
1664bdd1243dSDimitry Andric            "Image must be of 2D dimensionality");
1665bdd1243dSDimitry Andric     break;
1666bdd1243dSDimitry Andric   case SPIRV::OpImageQueryLevels:
1667bdd1243dSDimitry Andric     assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||
1668bdd1243dSDimitry Andric             ImageDimensionality == SPIRV::Dim::DIM_2D ||
1669bdd1243dSDimitry Andric             ImageDimensionality == SPIRV::Dim::DIM_3D ||
1670bdd1243dSDimitry Andric             ImageDimensionality == SPIRV::Dim::DIM_Cube) &&
1671bdd1243dSDimitry Andric            "Image must be of 1D/2D/3D/Cube dimensionality");
1672bdd1243dSDimitry Andric     break;
1673bdd1243dSDimitry Andric   }
1674bdd1243dSDimitry Andric 
1675bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
1676bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
1677bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1678bdd1243dSDimitry Andric       .addUse(Image);
1679bdd1243dSDimitry Andric   return true;
1680bdd1243dSDimitry Andric }
1681bdd1243dSDimitry Andric 
1682bdd1243dSDimitry Andric // TODO: Move to TableGen.
1683bdd1243dSDimitry Andric static SPIRV::SamplerAddressingMode::SamplerAddressingMode
1684bdd1243dSDimitry Andric getSamplerAddressingModeFromBitmask(unsigned Bitmask) {
1685bdd1243dSDimitry Andric   switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {
1686bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_CLAMP:
1687bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::Clamp;
1688bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:
1689bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::ClampToEdge;
1690bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_REPEAT:
1691bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::Repeat;
1692bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:
1693bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::RepeatMirrored;
1694bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_NONE:
1695bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::None;
1696bdd1243dSDimitry Andric   default:
1697*0fca6ea1SDimitry Andric     report_fatal_error("Unknown CL address mode");
1698bdd1243dSDimitry Andric   }
1699bdd1243dSDimitry Andric }
1700bdd1243dSDimitry Andric 
1701bdd1243dSDimitry Andric static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {
1702bdd1243dSDimitry Andric   return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;
1703bdd1243dSDimitry Andric }
1704bdd1243dSDimitry Andric 
1705bdd1243dSDimitry Andric static SPIRV::SamplerFilterMode::SamplerFilterMode
1706bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(unsigned Bitmask) {
1707bdd1243dSDimitry Andric   if (Bitmask & SPIRV::CLK_FILTER_LINEAR)
1708bdd1243dSDimitry Andric     return SPIRV::SamplerFilterMode::Linear;
1709bdd1243dSDimitry Andric   if (Bitmask & SPIRV::CLK_FILTER_NEAREST)
1710bdd1243dSDimitry Andric     return SPIRV::SamplerFilterMode::Nearest;
1711bdd1243dSDimitry Andric   return SPIRV::SamplerFilterMode::Nearest;
1712bdd1243dSDimitry Andric }
1713bdd1243dSDimitry Andric 
1714bdd1243dSDimitry Andric static bool generateReadImageInst(const StringRef DemangledCall,
1715bdd1243dSDimitry Andric                                   const SPIRV::IncomingCall *Call,
1716bdd1243dSDimitry Andric                                   MachineIRBuilder &MIRBuilder,
1717bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR) {
1718bdd1243dSDimitry Andric   Register Image = Call->Arguments[0];
1719bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
172006c3fb27SDimitry Andric   MRI->setRegClass(Image, &SPIRV::IDRegClass);
172106c3fb27SDimitry Andric   MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
172206c3fb27SDimitry Andric   bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler");
172306c3fb27SDimitry Andric   bool HasMsaa = DemangledCall.contains_insensitive("msaa");
172406c3fb27SDimitry Andric   if (HasOclSampler || HasMsaa)
172506c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);
172606c3fb27SDimitry Andric   if (HasOclSampler) {
1727bdd1243dSDimitry Andric     Register Sampler = Call->Arguments[1];
1728bdd1243dSDimitry Andric 
1729bdd1243dSDimitry Andric     if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&
1730bdd1243dSDimitry Andric         getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {
1731bdd1243dSDimitry Andric       uint64_t SamplerMask = getIConstVal(Sampler, MRI);
1732bdd1243dSDimitry Andric       Sampler = GR->buildConstantSampler(
1733bdd1243dSDimitry Andric           Register(), getSamplerAddressingModeFromBitmask(SamplerMask),
1734bdd1243dSDimitry Andric           getSamplerParamFromBitmask(SamplerMask),
1735bdd1243dSDimitry Andric           getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder,
1736bdd1243dSDimitry Andric           GR->getSPIRVTypeForVReg(Sampler));
1737bdd1243dSDimitry Andric     }
1738bdd1243dSDimitry Andric     SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1739bdd1243dSDimitry Andric     SPIRVType *SampledImageType =
1740bdd1243dSDimitry Andric         GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1741bdd1243dSDimitry Andric     Register SampledImage = MRI->createVirtualRegister(&SPIRV::IDRegClass);
1742bdd1243dSDimitry Andric 
1743bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1744bdd1243dSDimitry Andric         .addDef(SampledImage)
1745bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(SampledImageType))
1746bdd1243dSDimitry Andric         .addUse(Image)
1747bdd1243dSDimitry Andric         .addUse(Sampler);
1748bdd1243dSDimitry Andric 
1749bdd1243dSDimitry Andric     Register Lod = GR->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()),
1750bdd1243dSDimitry Andric                                        MIRBuilder);
1751bdd1243dSDimitry Andric     SPIRVType *TempType = Call->ReturnType;
1752bdd1243dSDimitry Andric     bool NeedsExtraction = false;
1753bdd1243dSDimitry Andric     if (TempType->getOpcode() != SPIRV::OpTypeVector) {
1754bdd1243dSDimitry Andric       TempType =
1755bdd1243dSDimitry Andric           GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder);
1756bdd1243dSDimitry Andric       NeedsExtraction = true;
1757bdd1243dSDimitry Andric     }
1758bdd1243dSDimitry Andric     LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(TempType));
1759bdd1243dSDimitry Andric     Register TempRegister = MRI->createGenericVirtualRegister(LLType);
176006c3fb27SDimitry Andric     MRI->setRegClass(TempRegister, &SPIRV::IDRegClass);
1761bdd1243dSDimitry Andric     GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());
1762bdd1243dSDimitry Andric 
1763bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1764bdd1243dSDimitry Andric         .addDef(NeedsExtraction ? TempRegister : Call->ReturnRegister)
1765bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(TempType))
1766bdd1243dSDimitry Andric         .addUse(SampledImage)
1767bdd1243dSDimitry Andric         .addUse(Call->Arguments[2]) // Coordinate.
1768bdd1243dSDimitry Andric         .addImm(SPIRV::ImageOperand::Lod)
1769bdd1243dSDimitry Andric         .addUse(Lod);
1770bdd1243dSDimitry Andric 
1771bdd1243dSDimitry Andric     if (NeedsExtraction)
1772bdd1243dSDimitry Andric       MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1773bdd1243dSDimitry Andric           .addDef(Call->ReturnRegister)
1774bdd1243dSDimitry Andric           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1775bdd1243dSDimitry Andric           .addUse(TempRegister)
1776bdd1243dSDimitry Andric           .addImm(0);
177706c3fb27SDimitry Andric   } else if (HasMsaa) {
1778bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageRead)
1779bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1780bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1781bdd1243dSDimitry Andric         .addUse(Image)
1782bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]) // Coordinate.
1783bdd1243dSDimitry Andric         .addImm(SPIRV::ImageOperand::Sample)
1784bdd1243dSDimitry Andric         .addUse(Call->Arguments[2]);
1785bdd1243dSDimitry Andric   } else {
1786bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageRead)
1787bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1788bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1789bdd1243dSDimitry Andric         .addUse(Image)
1790bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]); // Coordinate.
1791bdd1243dSDimitry Andric   }
1792bdd1243dSDimitry Andric   return true;
1793bdd1243dSDimitry Andric }
1794bdd1243dSDimitry Andric 
1795bdd1243dSDimitry Andric static bool generateWriteImageInst(const SPIRV::IncomingCall *Call,
1796bdd1243dSDimitry Andric                                    MachineIRBuilder &MIRBuilder,
1797bdd1243dSDimitry Andric                                    SPIRVGlobalRegistry *GR) {
179806c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
179906c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
180006c3fb27SDimitry Andric   MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);
1801bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpImageWrite)
1802bdd1243dSDimitry Andric       .addUse(Call->Arguments[0])  // Image.
1803bdd1243dSDimitry Andric       .addUse(Call->Arguments[1])  // Coordinate.
1804bdd1243dSDimitry Andric       .addUse(Call->Arguments[2]); // Texel.
1805bdd1243dSDimitry Andric   return true;
1806bdd1243dSDimitry Andric }
1807bdd1243dSDimitry Andric 
1808bdd1243dSDimitry Andric static bool generateSampleImageInst(const StringRef DemangledCall,
1809bdd1243dSDimitry Andric                                     const SPIRV::IncomingCall *Call,
1810bdd1243dSDimitry Andric                                     MachineIRBuilder &MIRBuilder,
1811bdd1243dSDimitry Andric                                     SPIRVGlobalRegistry *GR) {
181206c3fb27SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1813bdd1243dSDimitry Andric   if (Call->Builtin->Name.contains_insensitive(
1814bdd1243dSDimitry Andric           "__translate_sampler_initializer")) {
1815bdd1243dSDimitry Andric     // Build sampler literal.
181606c3fb27SDimitry Andric     uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI);
1817bdd1243dSDimitry Andric     Register Sampler = GR->buildConstantSampler(
1818bdd1243dSDimitry Andric         Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),
1819bdd1243dSDimitry Andric         getSamplerParamFromBitmask(Bitmask),
1820bdd1243dSDimitry Andric         getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType);
1821bdd1243dSDimitry Andric     return Sampler.isValid();
1822bdd1243dSDimitry Andric   } else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) {
1823bdd1243dSDimitry Andric     // Create OpSampledImage.
1824bdd1243dSDimitry Andric     Register Image = Call->Arguments[0];
1825bdd1243dSDimitry Andric     SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1826bdd1243dSDimitry Andric     SPIRVType *SampledImageType =
1827bdd1243dSDimitry Andric         GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1828bdd1243dSDimitry Andric     Register SampledImage =
1829bdd1243dSDimitry Andric         Call->ReturnRegister.isValid()
1830bdd1243dSDimitry Andric             ? Call->ReturnRegister
183106c3fb27SDimitry Andric             : MRI->createVirtualRegister(&SPIRV::IDRegClass);
1832bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1833bdd1243dSDimitry Andric         .addDef(SampledImage)
1834bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(SampledImageType))
1835bdd1243dSDimitry Andric         .addUse(Image)
1836bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]); // Sampler.
1837bdd1243dSDimitry Andric     return true;
1838bdd1243dSDimitry Andric   } else if (Call->Builtin->Name.contains_insensitive(
1839bdd1243dSDimitry Andric                  "__spirv_ImageSampleExplicitLod")) {
1840bdd1243dSDimitry Andric     // Sample an image using an explicit level of detail.
1841bdd1243dSDimitry Andric     std::string ReturnType = DemangledCall.str();
1842bdd1243dSDimitry Andric     if (DemangledCall.contains("_R")) {
1843bdd1243dSDimitry Andric       ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);
1844bdd1243dSDimitry Andric       ReturnType = ReturnType.substr(0, ReturnType.find('('));
1845bdd1243dSDimitry Andric     }
1846*0fca6ea1SDimitry Andric     SPIRVType *Type =
1847*0fca6ea1SDimitry Andric         Call->ReturnType
1848*0fca6ea1SDimitry Andric             ? Call->ReturnType
1849*0fca6ea1SDimitry Andric             : GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder);
1850*0fca6ea1SDimitry Andric     if (!Type) {
1851*0fca6ea1SDimitry Andric       std::string DiagMsg =
1852*0fca6ea1SDimitry Andric           "Unable to recognize SPIRV type name: " + ReturnType;
1853*0fca6ea1SDimitry Andric       report_fatal_error(DiagMsg.c_str());
1854*0fca6ea1SDimitry Andric     }
185506c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
185606c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
185706c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass);
185806c3fb27SDimitry Andric 
1859bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1860bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1861bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Type))
1862bdd1243dSDimitry Andric         .addUse(Call->Arguments[0]) // Image.
1863bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]) // Coordinate.
1864bdd1243dSDimitry Andric         .addImm(SPIRV::ImageOperand::Lod)
1865bdd1243dSDimitry Andric         .addUse(Call->Arguments[3]);
1866bdd1243dSDimitry Andric     return true;
1867bdd1243dSDimitry Andric   }
1868bdd1243dSDimitry Andric   return false;
1869bdd1243dSDimitry Andric }
1870bdd1243dSDimitry Andric 
1871bdd1243dSDimitry Andric static bool generateSelectInst(const SPIRV::IncomingCall *Call,
1872bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder) {
1873bdd1243dSDimitry Andric   MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],
1874bdd1243dSDimitry Andric                          Call->Arguments[1], Call->Arguments[2]);
1875bdd1243dSDimitry Andric   return true;
1876bdd1243dSDimitry Andric }
1877bdd1243dSDimitry Andric 
1878*0fca6ea1SDimitry Andric static bool generateConstructInst(const SPIRV::IncomingCall *Call,
1879*0fca6ea1SDimitry Andric                                   MachineIRBuilder &MIRBuilder,
1880*0fca6ea1SDimitry Andric                                   SPIRVGlobalRegistry *GR) {
1881*0fca6ea1SDimitry Andric   return buildOpFromWrapper(MIRBuilder, SPIRV::OpCompositeConstruct, Call,
1882*0fca6ea1SDimitry Andric                             GR->getSPIRVTypeID(Call->ReturnType));
1883*0fca6ea1SDimitry Andric }
1884*0fca6ea1SDimitry Andric 
1885*0fca6ea1SDimitry Andric static bool generateCoopMatrInst(const SPIRV::IncomingCall *Call,
1886*0fca6ea1SDimitry Andric                                  MachineIRBuilder &MIRBuilder,
1887*0fca6ea1SDimitry Andric                                  SPIRVGlobalRegistry *GR) {
1888*0fca6ea1SDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1889*0fca6ea1SDimitry Andric   unsigned Opcode =
1890*0fca6ea1SDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1891*0fca6ea1SDimitry Andric   bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR;
1892*0fca6ea1SDimitry Andric   unsigned ArgSz = Call->Arguments.size();
1893*0fca6ea1SDimitry Andric   unsigned LiteralIdx = 0;
1894*0fca6ea1SDimitry Andric   if (Opcode == SPIRV::OpCooperativeMatrixLoadKHR && ArgSz > 3)
1895*0fca6ea1SDimitry Andric     LiteralIdx = 3;
1896*0fca6ea1SDimitry Andric   else if (Opcode == SPIRV::OpCooperativeMatrixStoreKHR && ArgSz > 4)
1897*0fca6ea1SDimitry Andric     LiteralIdx = 4;
1898*0fca6ea1SDimitry Andric   SmallVector<uint32_t, 1> ImmArgs;
1899*0fca6ea1SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1900*0fca6ea1SDimitry Andric   if (LiteralIdx > 0)
1901*0fca6ea1SDimitry Andric     ImmArgs.push_back(getConstFromIntrinsic(Call->Arguments[LiteralIdx], MRI));
1902*0fca6ea1SDimitry Andric   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
1903*0fca6ea1SDimitry Andric   if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) {
1904*0fca6ea1SDimitry Andric     SPIRVType *CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1905*0fca6ea1SDimitry Andric     if (!CoopMatrType)
1906*0fca6ea1SDimitry Andric       report_fatal_error("Can't find a register's type definition");
1907*0fca6ea1SDimitry Andric     MIRBuilder.buildInstr(Opcode)
1908*0fca6ea1SDimitry Andric         .addDef(Call->ReturnRegister)
1909*0fca6ea1SDimitry Andric         .addUse(TypeReg)
1910*0fca6ea1SDimitry Andric         .addUse(CoopMatrType->getOperand(0).getReg());
1911*0fca6ea1SDimitry Andric     return true;
1912*0fca6ea1SDimitry Andric   }
1913*0fca6ea1SDimitry Andric   return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1914*0fca6ea1SDimitry Andric                             IsSet ? TypeReg : Register(0), ImmArgs);
1915*0fca6ea1SDimitry Andric }
1916*0fca6ea1SDimitry Andric 
1917bdd1243dSDimitry Andric static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call,
1918bdd1243dSDimitry Andric                                      MachineIRBuilder &MIRBuilder,
1919bdd1243dSDimitry Andric                                      SPIRVGlobalRegistry *GR) {
1920bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1921bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1922bdd1243dSDimitry Andric   unsigned Opcode =
1923bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1924bdd1243dSDimitry Andric   const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1925bdd1243dSDimitry Andric 
1926bdd1243dSDimitry Andric   switch (Opcode) {
1927bdd1243dSDimitry Andric   case SPIRV::OpSpecConstant: {
1928bdd1243dSDimitry Andric     // Build the SpecID decoration.
1929bdd1243dSDimitry Andric     unsigned SpecId =
1930bdd1243dSDimitry Andric         static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));
1931bdd1243dSDimitry Andric     buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,
1932bdd1243dSDimitry Andric                     {SpecId});
1933bdd1243dSDimitry Andric     // Determine the constant MI.
1934bdd1243dSDimitry Andric     Register ConstRegister = Call->Arguments[1];
1935bdd1243dSDimitry Andric     const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);
1936bdd1243dSDimitry Andric     assert(Const &&
1937bdd1243dSDimitry Andric            (Const->getOpcode() == TargetOpcode::G_CONSTANT ||
1938bdd1243dSDimitry Andric             Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&
1939bdd1243dSDimitry Andric            "Argument should be either an int or floating-point constant");
1940bdd1243dSDimitry Andric     // Determine the opcode and built the OpSpec MI.
1941bdd1243dSDimitry Andric     const MachineOperand &ConstOperand = Const->getOperand(1);
1942bdd1243dSDimitry Andric     if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {
1943bdd1243dSDimitry Andric       assert(ConstOperand.isCImm() && "Int constant operand is expected");
1944bdd1243dSDimitry Andric       Opcode = ConstOperand.getCImm()->getValue().getZExtValue()
1945bdd1243dSDimitry Andric                    ? SPIRV::OpSpecConstantTrue
1946bdd1243dSDimitry Andric                    : SPIRV::OpSpecConstantFalse;
1947bdd1243dSDimitry Andric     }
1948bdd1243dSDimitry Andric     auto MIB = MIRBuilder.buildInstr(Opcode)
1949bdd1243dSDimitry Andric                    .addDef(Call->ReturnRegister)
1950bdd1243dSDimitry Andric                    .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1951bdd1243dSDimitry Andric 
1952bdd1243dSDimitry Andric     if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {
1953bdd1243dSDimitry Andric       if (Const->getOpcode() == TargetOpcode::G_CONSTANT)
1954bdd1243dSDimitry Andric         addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1955bdd1243dSDimitry Andric       else
1956bdd1243dSDimitry Andric         addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);
1957bdd1243dSDimitry Andric     }
1958bdd1243dSDimitry Andric     return true;
1959bdd1243dSDimitry Andric   }
1960bdd1243dSDimitry Andric   case SPIRV::OpSpecConstantComposite: {
1961bdd1243dSDimitry Andric     auto MIB = MIRBuilder.buildInstr(Opcode)
1962bdd1243dSDimitry Andric                    .addDef(Call->ReturnRegister)
1963bdd1243dSDimitry Andric                    .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1964bdd1243dSDimitry Andric     for (unsigned i = 0; i < Call->Arguments.size(); i++)
1965bdd1243dSDimitry Andric       MIB.addUse(Call->Arguments[i]);
1966bdd1243dSDimitry Andric     return true;
1967bdd1243dSDimitry Andric   }
1968bdd1243dSDimitry Andric   default:
1969bdd1243dSDimitry Andric     return false;
1970bdd1243dSDimitry Andric   }
1971bdd1243dSDimitry Andric }
1972bdd1243dSDimitry Andric 
197306c3fb27SDimitry Andric static bool buildNDRange(const SPIRV::IncomingCall *Call,
197406c3fb27SDimitry Andric                          MachineIRBuilder &MIRBuilder,
197506c3fb27SDimitry Andric                          SPIRVGlobalRegistry *GR) {
197606c3fb27SDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
197706c3fb27SDimitry Andric   MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
197806c3fb27SDimitry Andric   SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
197906c3fb27SDimitry Andric   assert(PtrType->getOpcode() == SPIRV::OpTypePointer &&
198006c3fb27SDimitry Andric          PtrType->getOperand(2).isReg());
198106c3fb27SDimitry Andric   Register TypeReg = PtrType->getOperand(2).getReg();
198206c3fb27SDimitry Andric   SPIRVType *StructType = GR->getSPIRVTypeForVReg(TypeReg);
198306c3fb27SDimitry Andric   MachineFunction &MF = MIRBuilder.getMF();
198406c3fb27SDimitry Andric   Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
198506c3fb27SDimitry Andric   GR->assignSPIRVTypeToVReg(StructType, TmpReg, MF);
198606c3fb27SDimitry Andric   // Skip the first arg, it's the destination pointer. OpBuildNDRange takes
198706c3fb27SDimitry Andric   // three other arguments, so pass zero constant on absence.
198806c3fb27SDimitry Andric   unsigned NumArgs = Call->Arguments.size();
198906c3fb27SDimitry Andric   assert(NumArgs >= 2);
199006c3fb27SDimitry Andric   Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2];
199106c3fb27SDimitry Andric   MRI->setRegClass(GlobalWorkSize, &SPIRV::IDRegClass);
199206c3fb27SDimitry Andric   Register LocalWorkSize =
199306c3fb27SDimitry Andric       NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3];
199406c3fb27SDimitry Andric   if (LocalWorkSize.isValid())
199506c3fb27SDimitry Andric     MRI->setRegClass(LocalWorkSize, &SPIRV::IDRegClass);
199606c3fb27SDimitry Andric   Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1];
199706c3fb27SDimitry Andric   if (GlobalWorkOffset.isValid())
199806c3fb27SDimitry Andric     MRI->setRegClass(GlobalWorkOffset, &SPIRV::IDRegClass);
199906c3fb27SDimitry Andric   if (NumArgs < 4) {
200006c3fb27SDimitry Andric     Register Const;
200106c3fb27SDimitry Andric     SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize);
200206c3fb27SDimitry Andric     if (SpvTy->getOpcode() == SPIRV::OpTypePointer) {
200306c3fb27SDimitry Andric       MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize);
200406c3fb27SDimitry Andric       assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) &&
200506c3fb27SDimitry Andric              DefInstr->getOperand(3).isReg());
200606c3fb27SDimitry Andric       Register GWSPtr = DefInstr->getOperand(3).getReg();
200706c3fb27SDimitry Andric       if (!MRI->getRegClassOrNull(GWSPtr))
200806c3fb27SDimitry Andric         MRI->setRegClass(GWSPtr, &SPIRV::IDRegClass);
200906c3fb27SDimitry Andric       // TODO: Maybe simplify generation of the type of the fields.
2010*0fca6ea1SDimitry Andric       unsigned Size = Call->Builtin->Name == "ndrange_3D" ? 3 : 2;
201106c3fb27SDimitry Andric       unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32;
201206c3fb27SDimitry Andric       Type *BaseTy = IntegerType::get(MF.getFunction().getContext(), BitWidth);
201306c3fb27SDimitry Andric       Type *FieldTy = ArrayType::get(BaseTy, Size);
201406c3fb27SDimitry Andric       SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder);
201506c3fb27SDimitry Andric       GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::IDRegClass);
201606c3fb27SDimitry Andric       GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize, MF);
201706c3fb27SDimitry Andric       MIRBuilder.buildInstr(SPIRV::OpLoad)
201806c3fb27SDimitry Andric           .addDef(GlobalWorkSize)
201906c3fb27SDimitry Andric           .addUse(GR->getSPIRVTypeID(SpvFieldTy))
202006c3fb27SDimitry Andric           .addUse(GWSPtr);
2021*0fca6ea1SDimitry Andric       const SPIRVSubtarget &ST =
2022*0fca6ea1SDimitry Andric           cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
2023*0fca6ea1SDimitry Andric       Const = GR->getOrCreateConstIntArray(0, Size, *MIRBuilder.getInsertPt(),
2024*0fca6ea1SDimitry Andric                                            SpvFieldTy, *ST.getInstrInfo());
202506c3fb27SDimitry Andric     } else {
202606c3fb27SDimitry Andric       Const = GR->buildConstantInt(0, MIRBuilder, SpvTy);
202706c3fb27SDimitry Andric     }
202806c3fb27SDimitry Andric     if (!LocalWorkSize.isValid())
202906c3fb27SDimitry Andric       LocalWorkSize = Const;
203006c3fb27SDimitry Andric     if (!GlobalWorkOffset.isValid())
203106c3fb27SDimitry Andric       GlobalWorkOffset = Const;
203206c3fb27SDimitry Andric   }
203306c3fb27SDimitry Andric   assert(LocalWorkSize.isValid() && GlobalWorkOffset.isValid());
203406c3fb27SDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
203506c3fb27SDimitry Andric       .addDef(TmpReg)
203606c3fb27SDimitry Andric       .addUse(TypeReg)
203706c3fb27SDimitry Andric       .addUse(GlobalWorkSize)
203806c3fb27SDimitry Andric       .addUse(LocalWorkSize)
203906c3fb27SDimitry Andric       .addUse(GlobalWorkOffset);
204006c3fb27SDimitry Andric   return MIRBuilder.buildInstr(SPIRV::OpStore)
204106c3fb27SDimitry Andric       .addUse(Call->Arguments[0])
204206c3fb27SDimitry Andric       .addUse(TmpReg);
204306c3fb27SDimitry Andric }
204406c3fb27SDimitry Andric 
2045bdd1243dSDimitry Andric // TODO: maybe move to the global register.
2046bdd1243dSDimitry Andric static SPIRVType *
2047bdd1243dSDimitry Andric getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder,
2048bdd1243dSDimitry Andric                                    SPIRVGlobalRegistry *GR) {
2049bdd1243dSDimitry Andric   LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
2050bdd1243dSDimitry Andric   Type *OpaqueType = StructType::getTypeByName(Context, "spirv.DeviceEvent");
2051bdd1243dSDimitry Andric   if (!OpaqueType)
2052bdd1243dSDimitry Andric     OpaqueType = StructType::getTypeByName(Context, "opencl.clk_event_t");
2053bdd1243dSDimitry Andric   if (!OpaqueType)
2054bdd1243dSDimitry Andric     OpaqueType = StructType::create(Context, "spirv.DeviceEvent");
2055bdd1243dSDimitry Andric   unsigned SC0 = storageClassToAddressSpace(SPIRV::StorageClass::Function);
2056bdd1243dSDimitry Andric   unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2057bdd1243dSDimitry Andric   Type *PtrType = PointerType::get(PointerType::get(OpaqueType, SC0), SC1);
2058bdd1243dSDimitry Andric   return GR->getOrCreateSPIRVType(PtrType, MIRBuilder);
2059bdd1243dSDimitry Andric }
2060bdd1243dSDimitry Andric 
2061bdd1243dSDimitry Andric static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call,
2062bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
2063bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
2064bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2065bdd1243dSDimitry Andric   const DataLayout &DL = MIRBuilder.getDataLayout();
2066*0fca6ea1SDimitry Andric   bool IsSpirvOp = Call->isSpirvOp();
2067*0fca6ea1SDimitry Andric   bool HasEvents = Call->Builtin->Name.contains("events") || IsSpirvOp;
2068bdd1243dSDimitry Andric   const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
2069bdd1243dSDimitry Andric 
2070bdd1243dSDimitry Andric   // Make vararg instructions before OpEnqueueKernel.
2071bdd1243dSDimitry Andric   // Local sizes arguments: Sizes of block invoke arguments. Clang generates
2072bdd1243dSDimitry Andric   // local size operands as an array, so we need to unpack them.
2073bdd1243dSDimitry Andric   SmallVector<Register, 16> LocalSizes;
2074*0fca6ea1SDimitry Andric   if (Call->Builtin->Name.contains("_varargs") || IsSpirvOp) {
2075bdd1243dSDimitry Andric     const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6;
2076bdd1243dSDimitry Andric     Register GepReg = Call->Arguments[LocalSizeArrayIdx];
2077bdd1243dSDimitry Andric     MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg);
2078bdd1243dSDimitry Andric     assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) &&
2079bdd1243dSDimitry Andric            GepMI->getOperand(3).isReg());
2080bdd1243dSDimitry Andric     Register ArrayReg = GepMI->getOperand(3).getReg();
2081bdd1243dSDimitry Andric     MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg);
2082bdd1243dSDimitry Andric     const Type *LocalSizeTy = getMachineInstrType(ArrayMI);
2083bdd1243dSDimitry Andric     assert(LocalSizeTy && "Local size type is expected");
2084bdd1243dSDimitry Andric     const uint64_t LocalSizeNum =
2085bdd1243dSDimitry Andric         cast<ArrayType>(LocalSizeTy)->getNumElements();
2086bdd1243dSDimitry Andric     unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2087bdd1243dSDimitry Andric     const LLT LLType = LLT::pointer(SC, GR->getPointerSize());
2088bdd1243dSDimitry Andric     const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType(
2089bdd1243dSDimitry Andric         Int32Ty, MIRBuilder, SPIRV::StorageClass::Function);
2090bdd1243dSDimitry Andric     for (unsigned I = 0; I < LocalSizeNum; ++I) {
209106c3fb27SDimitry Andric       Register Reg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
209206c3fb27SDimitry Andric       MRI->setType(Reg, LLType);
2093bdd1243dSDimitry Andric       GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF());
20945f757f3fSDimitry Andric       auto GEPInst = MIRBuilder.buildIntrinsic(
20955f757f3fSDimitry Andric           Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false);
2096bdd1243dSDimitry Andric       GEPInst
2097bdd1243dSDimitry Andric           .addImm(GepMI->getOperand(2).getImm())          // In bound.
2098bdd1243dSDimitry Andric           .addUse(ArrayMI->getOperand(0).getReg())        // Alloca.
2099bdd1243dSDimitry Andric           .addUse(buildConstantIntReg(0, MIRBuilder, GR)) // Indices.
2100bdd1243dSDimitry Andric           .addUse(buildConstantIntReg(I, MIRBuilder, GR));
2101bdd1243dSDimitry Andric       LocalSizes.push_back(Reg);
2102bdd1243dSDimitry Andric     }
2103bdd1243dSDimitry Andric   }
2104bdd1243dSDimitry Andric 
2105bdd1243dSDimitry Andric   // SPIRV OpEnqueueKernel instruction has 10+ arguments.
2106bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)
2107bdd1243dSDimitry Andric                  .addDef(Call->ReturnRegister)
2108bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(Int32Ty));
2109bdd1243dSDimitry Andric 
2110bdd1243dSDimitry Andric   // Copy all arguments before block invoke function pointer.
2111bdd1243dSDimitry Andric   const unsigned BlockFIdx = HasEvents ? 6 : 3;
2112bdd1243dSDimitry Andric   for (unsigned i = 0; i < BlockFIdx; i++)
2113bdd1243dSDimitry Andric     MIB.addUse(Call->Arguments[i]);
2114bdd1243dSDimitry Andric 
2115bdd1243dSDimitry Andric   // If there are no event arguments in the original call, add dummy ones.
2116bdd1243dSDimitry Andric   if (!HasEvents) {
2117bdd1243dSDimitry Andric     MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Dummy num events.
2118bdd1243dSDimitry Andric     Register NullPtr = GR->getOrCreateConstNullPtr(
2119bdd1243dSDimitry Andric         MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR));
2120bdd1243dSDimitry Andric     MIB.addUse(NullPtr); // Dummy wait events.
2121bdd1243dSDimitry Andric     MIB.addUse(NullPtr); // Dummy ret event.
2122bdd1243dSDimitry Andric   }
2123bdd1243dSDimitry Andric 
2124bdd1243dSDimitry Andric   MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI);
2125bdd1243dSDimitry Andric   assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
2126bdd1243dSDimitry Andric   // Invoke: Pointer to invoke function.
2127bdd1243dSDimitry Andric   MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal());
2128bdd1243dSDimitry Andric 
2129bdd1243dSDimitry Andric   Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1];
2130bdd1243dSDimitry Andric   // Param: Pointer to block literal.
2131bdd1243dSDimitry Andric   MIB.addUse(BlockLiteralReg);
2132bdd1243dSDimitry Andric 
2133bdd1243dSDimitry Andric   Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
2134bdd1243dSDimitry Andric   // TODO: these numbers should be obtained from block literal structure.
2135bdd1243dSDimitry Andric   // Param Size: Size of block literal structure.
2136bdd1243dSDimitry Andric   MIB.addUse(buildConstantIntReg(DL.getTypeStoreSize(PType), MIRBuilder, GR));
2137bdd1243dSDimitry Andric   // Param Aligment: Aligment of block literal structure.
2138bdd1243dSDimitry Andric   MIB.addUse(
213906c3fb27SDimitry Andric       buildConstantIntReg(DL.getPrefTypeAlign(PType).value(), MIRBuilder, GR));
2140bdd1243dSDimitry Andric 
2141bdd1243dSDimitry Andric   for (unsigned i = 0; i < LocalSizes.size(); i++)
2142bdd1243dSDimitry Andric     MIB.addUse(LocalSizes[i]);
2143bdd1243dSDimitry Andric   return true;
2144bdd1243dSDimitry Andric }
2145bdd1243dSDimitry Andric 
2146bdd1243dSDimitry Andric static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,
2147bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
2148bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
2149bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
2150bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2151bdd1243dSDimitry Andric   unsigned Opcode =
2152bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2153bdd1243dSDimitry Andric 
2154bdd1243dSDimitry Andric   switch (Opcode) {
2155bdd1243dSDimitry Andric   case SPIRV::OpRetainEvent:
2156bdd1243dSDimitry Andric   case SPIRV::OpReleaseEvent:
215706c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
2158bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);
2159bdd1243dSDimitry Andric   case SPIRV::OpCreateUserEvent:
2160bdd1243dSDimitry Andric   case SPIRV::OpGetDefaultQueue:
2161bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
2162bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
2163bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2164bdd1243dSDimitry Andric   case SPIRV::OpIsValidEvent:
216506c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
2166bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
2167bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
2168bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2169bdd1243dSDimitry Andric         .addUse(Call->Arguments[0]);
2170bdd1243dSDimitry Andric   case SPIRV::OpSetUserEventStatus:
217106c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
217206c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
2173bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
2174bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
2175bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]);
2176bdd1243dSDimitry Andric   case SPIRV::OpCaptureEventProfilingInfo:
217706c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
217806c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
217906c3fb27SDimitry Andric     MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);
2180bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
2181bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
2182bdd1243dSDimitry Andric         .addUse(Call->Arguments[1])
2183bdd1243dSDimitry Andric         .addUse(Call->Arguments[2]);
218406c3fb27SDimitry Andric   case SPIRV::OpBuildNDRange:
218506c3fb27SDimitry Andric     return buildNDRange(Call, MIRBuilder, GR);
2186bdd1243dSDimitry Andric   case SPIRV::OpEnqueueKernel:
2187bdd1243dSDimitry Andric     return buildEnqueueKernel(Call, MIRBuilder, GR);
2188bdd1243dSDimitry Andric   default:
2189bdd1243dSDimitry Andric     return false;
2190bdd1243dSDimitry Andric   }
2191bdd1243dSDimitry Andric }
2192bdd1243dSDimitry Andric 
2193bdd1243dSDimitry Andric static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,
2194bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
2195bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR) {
2196bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
2197bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2198bdd1243dSDimitry Andric   unsigned Opcode =
2199bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2200*0fca6ea1SDimitry Andric 
2201*0fca6ea1SDimitry Andric   bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;
2202*0fca6ea1SDimitry Andric   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2203*0fca6ea1SDimitry Andric   if (Call->isSpirvOp())
2204*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2205*0fca6ea1SDimitry Andric                               IsSet ? TypeReg : Register(0));
2206*0fca6ea1SDimitry Andric 
2207bdd1243dSDimitry Andric   auto Scope = buildConstantIntReg(SPIRV::Scope::Workgroup, MIRBuilder, GR);
2208bdd1243dSDimitry Andric 
2209bdd1243dSDimitry Andric   switch (Opcode) {
2210*0fca6ea1SDimitry Andric   case SPIRV::OpGroupAsyncCopy: {
2211*0fca6ea1SDimitry Andric     SPIRVType *NewType =
2212*0fca6ea1SDimitry Andric         Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent
2213*0fca6ea1SDimitry Andric             ? nullptr
2214*0fca6ea1SDimitry Andric             : GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder);
2215*0fca6ea1SDimitry Andric     Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);
2216*0fca6ea1SDimitry Andric     unsigned NumArgs = Call->Arguments.size();
2217*0fca6ea1SDimitry Andric     Register EventReg = Call->Arguments[NumArgs - 1];
2218*0fca6ea1SDimitry Andric     bool Res = MIRBuilder.buildInstr(Opcode)
2219bdd1243dSDimitry Andric                    .addDef(Call->ReturnRegister)
2220*0fca6ea1SDimitry Andric                    .addUse(TypeReg)
2221bdd1243dSDimitry Andric                    .addUse(Scope)
2222bdd1243dSDimitry Andric                    .addUse(Call->Arguments[0])
2223bdd1243dSDimitry Andric                    .addUse(Call->Arguments[1])
2224bdd1243dSDimitry Andric                    .addUse(Call->Arguments[2])
2225*0fca6ea1SDimitry Andric                    .addUse(Call->Arguments.size() > 4
2226*0fca6ea1SDimitry Andric                                ? Call->Arguments[3]
2227*0fca6ea1SDimitry Andric                                : buildConstantIntReg(1, MIRBuilder, GR))
2228*0fca6ea1SDimitry Andric                    .addUse(EventReg);
2229*0fca6ea1SDimitry Andric     if (NewType != nullptr)
2230*0fca6ea1SDimitry Andric       insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
2231*0fca6ea1SDimitry Andric                         MIRBuilder.getMF().getRegInfo());
2232*0fca6ea1SDimitry Andric     return Res;
2233*0fca6ea1SDimitry Andric   }
2234bdd1243dSDimitry Andric   case SPIRV::OpGroupWaitEvents:
2235bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
2236bdd1243dSDimitry Andric         .addUse(Scope)
2237bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
2238bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]);
2239bdd1243dSDimitry Andric   default:
2240bdd1243dSDimitry Andric     return false;
2241bdd1243dSDimitry Andric   }
2242bdd1243dSDimitry Andric }
2243bdd1243dSDimitry Andric 
2244bdd1243dSDimitry Andric static bool generateConvertInst(const StringRef DemangledCall,
2245bdd1243dSDimitry Andric                                 const SPIRV::IncomingCall *Call,
2246bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
2247bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
2248bdd1243dSDimitry Andric   // Lookup the conversion builtin in the TableGen records.
2249bdd1243dSDimitry Andric   const SPIRV::ConvertBuiltin *Builtin =
2250bdd1243dSDimitry Andric       SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set);
2251bdd1243dSDimitry Andric 
2252*0fca6ea1SDimitry Andric   if (!Builtin && Call->isSpirvOp()) {
2253*0fca6ea1SDimitry Andric     const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2254*0fca6ea1SDimitry Andric     unsigned Opcode =
2255*0fca6ea1SDimitry Andric         SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2256*0fca6ea1SDimitry Andric     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2257*0fca6ea1SDimitry Andric                               GR->getSPIRVTypeID(Call->ReturnType));
2258*0fca6ea1SDimitry Andric   }
2259*0fca6ea1SDimitry Andric 
2260bdd1243dSDimitry Andric   if (Builtin->IsSaturated)
2261bdd1243dSDimitry Andric     buildOpDecorate(Call->ReturnRegister, MIRBuilder,
2262bdd1243dSDimitry Andric                     SPIRV::Decoration::SaturatedConversion, {});
2263bdd1243dSDimitry Andric   if (Builtin->IsRounded)
2264bdd1243dSDimitry Andric     buildOpDecorate(Call->ReturnRegister, MIRBuilder,
226506c3fb27SDimitry Andric                     SPIRV::Decoration::FPRoundingMode,
226606c3fb27SDimitry Andric                     {(unsigned)Builtin->RoundingMode});
2267bdd1243dSDimitry Andric 
2268*0fca6ea1SDimitry Andric   std::string NeedExtMsg;              // no errors if empty
2269*0fca6ea1SDimitry Andric   bool IsRightComponentsNumber = true; // check if input/output accepts vectors
2270bdd1243dSDimitry Andric   unsigned Opcode = SPIRV::OpNop;
2271bdd1243dSDimitry Andric   if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {
2272bdd1243dSDimitry Andric     // Int -> ...
2273bdd1243dSDimitry Andric     if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2274bdd1243dSDimitry Andric       // Int -> Int
2275bdd1243dSDimitry Andric       if (Builtin->IsSaturated)
2276bdd1243dSDimitry Andric         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS
2277bdd1243dSDimitry Andric                                               : SPIRV::OpSatConvertSToU;
2278bdd1243dSDimitry Andric       else
2279bdd1243dSDimitry Andric         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert
2280bdd1243dSDimitry Andric                                               : SPIRV::OpSConvert;
2281bdd1243dSDimitry Andric     } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
2282bdd1243dSDimitry Andric                                           SPIRV::OpTypeFloat)) {
2283bdd1243dSDimitry Andric       // Int -> Float
2284*0fca6ea1SDimitry Andric       if (Builtin->IsBfloat16) {
2285*0fca6ea1SDimitry Andric         const auto *ST = static_cast<const SPIRVSubtarget *>(
2286*0fca6ea1SDimitry Andric             &MIRBuilder.getMF().getSubtarget());
2287*0fca6ea1SDimitry Andric         if (!ST->canUseExtension(
2288*0fca6ea1SDimitry Andric                 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
2289*0fca6ea1SDimitry Andric           NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
2290*0fca6ea1SDimitry Andric         IsRightComponentsNumber =
2291*0fca6ea1SDimitry Andric             GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
2292*0fca6ea1SDimitry Andric             GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
2293*0fca6ea1SDimitry Andric         Opcode = SPIRV::OpConvertBF16ToFINTEL;
2294*0fca6ea1SDimitry Andric       } else {
2295bdd1243dSDimitry Andric         bool IsSourceSigned =
2296bdd1243dSDimitry Andric             DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';
2297bdd1243dSDimitry Andric         Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;
2298bdd1243dSDimitry Andric       }
2299*0fca6ea1SDimitry Andric     }
2300bdd1243dSDimitry Andric   } else if (GR->isScalarOrVectorOfType(Call->Arguments[0],
2301bdd1243dSDimitry Andric                                         SPIRV::OpTypeFloat)) {
2302bdd1243dSDimitry Andric     // Float -> ...
2303*0fca6ea1SDimitry Andric     if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2304bdd1243dSDimitry Andric       // Float -> Int
2305*0fca6ea1SDimitry Andric       if (Builtin->IsBfloat16) {
2306*0fca6ea1SDimitry Andric         const auto *ST = static_cast<const SPIRVSubtarget *>(
2307*0fca6ea1SDimitry Andric             &MIRBuilder.getMF().getSubtarget());
2308*0fca6ea1SDimitry Andric         if (!ST->canUseExtension(
2309*0fca6ea1SDimitry Andric                 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
2310*0fca6ea1SDimitry Andric           NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
2311*0fca6ea1SDimitry Andric         IsRightComponentsNumber =
2312*0fca6ea1SDimitry Andric             GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
2313*0fca6ea1SDimitry Andric             GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
2314*0fca6ea1SDimitry Andric         Opcode = SPIRV::OpConvertFToBF16INTEL;
2315*0fca6ea1SDimitry Andric       } else {
2316bdd1243dSDimitry Andric         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS
2317bdd1243dSDimitry Andric                                               : SPIRV::OpConvertFToU;
2318*0fca6ea1SDimitry Andric       }
2319*0fca6ea1SDimitry Andric     } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
2320*0fca6ea1SDimitry Andric                                           SPIRV::OpTypeFloat)) {
2321bdd1243dSDimitry Andric       // Float -> Float
2322bdd1243dSDimitry Andric       Opcode = SPIRV::OpFConvert;
2323bdd1243dSDimitry Andric     }
2324*0fca6ea1SDimitry Andric   }
2325bdd1243dSDimitry Andric 
2326*0fca6ea1SDimitry Andric   if (!NeedExtMsg.empty()) {
2327*0fca6ea1SDimitry Andric     std::string DiagMsg = std::string(Builtin->Name) +
2328*0fca6ea1SDimitry Andric                           ": the builtin requires the following SPIR-V "
2329*0fca6ea1SDimitry Andric                           "extension: " +
2330*0fca6ea1SDimitry Andric                           NeedExtMsg;
2331*0fca6ea1SDimitry Andric     report_fatal_error(DiagMsg.c_str(), false);
2332*0fca6ea1SDimitry Andric   }
2333*0fca6ea1SDimitry Andric   if (!IsRightComponentsNumber) {
2334*0fca6ea1SDimitry Andric     std::string DiagMsg =
2335*0fca6ea1SDimitry Andric         std::string(Builtin->Name) +
2336*0fca6ea1SDimitry Andric         ": result and argument must have the same number of components";
2337*0fca6ea1SDimitry Andric     report_fatal_error(DiagMsg.c_str(), false);
2338*0fca6ea1SDimitry Andric   }
2339bdd1243dSDimitry Andric   assert(Opcode != SPIRV::OpNop &&
2340bdd1243dSDimitry Andric          "Conversion between the types not implemented!");
2341bdd1243dSDimitry Andric 
2342bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
2343bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
2344bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2345bdd1243dSDimitry Andric       .addUse(Call->Arguments[0]);
2346bdd1243dSDimitry Andric   return true;
2347bdd1243dSDimitry Andric }
2348bdd1243dSDimitry Andric 
2349bdd1243dSDimitry Andric static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call,
2350bdd1243dSDimitry Andric                                         MachineIRBuilder &MIRBuilder,
2351bdd1243dSDimitry Andric                                         SPIRVGlobalRegistry *GR) {
2352bdd1243dSDimitry Andric   // Lookup the vector load/store builtin in the TableGen records.
2353bdd1243dSDimitry Andric   const SPIRV::VectorLoadStoreBuiltin *Builtin =
2354bdd1243dSDimitry Andric       SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2355bdd1243dSDimitry Andric                                           Call->Builtin->Set);
2356bdd1243dSDimitry Andric   // Build extended instruction.
2357bdd1243dSDimitry Andric   auto MIB =
2358bdd1243dSDimitry Andric       MIRBuilder.buildInstr(SPIRV::OpExtInst)
2359bdd1243dSDimitry Andric           .addDef(Call->ReturnRegister)
2360bdd1243dSDimitry Andric           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2361bdd1243dSDimitry Andric           .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
2362bdd1243dSDimitry Andric           .addImm(Builtin->Number);
2363bdd1243dSDimitry Andric   for (auto Argument : Call->Arguments)
2364bdd1243dSDimitry Andric     MIB.addUse(Argument);
2365*0fca6ea1SDimitry Andric   if (Builtin->Name.contains("load") && Builtin->ElementCount > 1)
2366*0fca6ea1SDimitry Andric     MIB.addImm(Builtin->ElementCount);
2367bdd1243dSDimitry Andric 
2368bdd1243dSDimitry Andric   // Rounding mode should be passed as a last argument in the MI for builtins
2369bdd1243dSDimitry Andric   // like "vstorea_halfn_r".
2370bdd1243dSDimitry Andric   if (Builtin->IsRounded)
2371bdd1243dSDimitry Andric     MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));
2372bdd1243dSDimitry Andric   return true;
2373bdd1243dSDimitry Andric }
2374bdd1243dSDimitry Andric 
2375bdd1243dSDimitry Andric static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call,
2376bdd1243dSDimitry Andric                                   MachineIRBuilder &MIRBuilder,
2377bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR) {
2378bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
2379bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2380bdd1243dSDimitry Andric   unsigned Opcode =
2381bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2382bdd1243dSDimitry Andric   bool IsLoad = Opcode == SPIRV::OpLoad;
2383bdd1243dSDimitry Andric   // Build the instruction.
2384bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode);
2385bdd1243dSDimitry Andric   if (IsLoad) {
2386bdd1243dSDimitry Andric     MIB.addDef(Call->ReturnRegister);
2387bdd1243dSDimitry Andric     MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
2388bdd1243dSDimitry Andric   }
2389bdd1243dSDimitry Andric   // Add a pointer to the value to load/store.
2390bdd1243dSDimitry Andric   MIB.addUse(Call->Arguments[0]);
2391bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
239206c3fb27SDimitry Andric   MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
239306c3fb27SDimitry Andric   // Add a value to store.
239406c3fb27SDimitry Andric   if (!IsLoad) {
239506c3fb27SDimitry Andric     MIB.addUse(Call->Arguments[1]);
239606c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
239706c3fb27SDimitry Andric   }
239806c3fb27SDimitry Andric   // Add optional memory attributes and an alignment.
2399bdd1243dSDimitry Andric   unsigned NumArgs = Call->Arguments.size();
240006c3fb27SDimitry Andric   if ((IsLoad && NumArgs >= 2) || NumArgs >= 3) {
2401bdd1243dSDimitry Andric     MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI));
240206c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[IsLoad ? 1 : 2], &SPIRV::IDRegClass);
240306c3fb27SDimitry Andric   }
240406c3fb27SDimitry Andric   if ((IsLoad && NumArgs >= 3) || NumArgs >= 4) {
2405bdd1243dSDimitry Andric     MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI));
240606c3fb27SDimitry Andric     MRI->setRegClass(Call->Arguments[IsLoad ? 2 : 3], &SPIRV::IDRegClass);
240706c3fb27SDimitry Andric   }
2408bdd1243dSDimitry Andric   return true;
2409bdd1243dSDimitry Andric }
2410bdd1243dSDimitry Andric 
2411bdd1243dSDimitry Andric namespace SPIRV {
2412*0fca6ea1SDimitry Andric // Try to find a builtin function attributes by a demangled function name and
2413*0fca6ea1SDimitry Andric // return a tuple <builtin group, op code, ext instruction number>, or a special
2414*0fca6ea1SDimitry Andric // tuple value <-1, 0, 0> if the builtin function is not found.
2415*0fca6ea1SDimitry Andric // Not all builtin functions are supported, only those with a ready-to-use op
2416*0fca6ea1SDimitry Andric // code or instruction number defined in TableGen.
2417*0fca6ea1SDimitry Andric // TODO: consider a major rework of mapping demangled calls into a builtin
2418*0fca6ea1SDimitry Andric // functions to unify search and decrease number of individual cases.
2419*0fca6ea1SDimitry Andric std::tuple<int, unsigned, unsigned>
2420*0fca6ea1SDimitry Andric mapBuiltinToOpcode(const StringRef DemangledCall,
2421*0fca6ea1SDimitry Andric                    SPIRV::InstructionSet::InstructionSet Set) {
2422*0fca6ea1SDimitry Andric   Register Reg;
2423*0fca6ea1SDimitry Andric   SmallVector<Register> Args;
2424*0fca6ea1SDimitry Andric   std::unique_ptr<const IncomingCall> Call =
2425*0fca6ea1SDimitry Andric       lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args);
2426*0fca6ea1SDimitry Andric   if (!Call)
2427*0fca6ea1SDimitry Andric     return std::make_tuple(-1, 0, 0);
2428*0fca6ea1SDimitry Andric 
2429*0fca6ea1SDimitry Andric   switch (Call->Builtin->Group) {
2430*0fca6ea1SDimitry Andric   case SPIRV::Relational:
2431*0fca6ea1SDimitry Andric   case SPIRV::Atomic:
2432*0fca6ea1SDimitry Andric   case SPIRV::Barrier:
2433*0fca6ea1SDimitry Andric   case SPIRV::CastToPtr:
2434*0fca6ea1SDimitry Andric   case SPIRV::ImageMiscQuery:
2435*0fca6ea1SDimitry Andric   case SPIRV::SpecConstant:
2436*0fca6ea1SDimitry Andric   case SPIRV::Enqueue:
2437*0fca6ea1SDimitry Andric   case SPIRV::AsyncCopy:
2438*0fca6ea1SDimitry Andric   case SPIRV::LoadStore:
2439*0fca6ea1SDimitry Andric   case SPIRV::CoopMatr:
2440*0fca6ea1SDimitry Andric     if (const auto *R =
2441*0fca6ea1SDimitry Andric             SPIRV::lookupNativeBuiltin(Call->Builtin->Name, Call->Builtin->Set))
2442*0fca6ea1SDimitry Andric       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2443*0fca6ea1SDimitry Andric     break;
2444*0fca6ea1SDimitry Andric   case SPIRV::Extended:
2445*0fca6ea1SDimitry Andric     if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->Name,
2446*0fca6ea1SDimitry Andric                                                      Call->Builtin->Set))
2447*0fca6ea1SDimitry Andric       return std::make_tuple(Call->Builtin->Group, 0, R->Number);
2448*0fca6ea1SDimitry Andric     break;
2449*0fca6ea1SDimitry Andric   case SPIRV::VectorLoadStore:
2450*0fca6ea1SDimitry Andric     if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2451*0fca6ea1SDimitry Andric                                                             Call->Builtin->Set))
2452*0fca6ea1SDimitry Andric       return std::make_tuple(SPIRV::Extended, 0, R->Number);
2453*0fca6ea1SDimitry Andric     break;
2454*0fca6ea1SDimitry Andric   case SPIRV::Group:
2455*0fca6ea1SDimitry Andric     if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->Name))
2456*0fca6ea1SDimitry Andric       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2457*0fca6ea1SDimitry Andric     break;
2458*0fca6ea1SDimitry Andric   case SPIRV::AtomicFloating:
2459*0fca6ea1SDimitry Andric     if (const auto *R = SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->Name))
2460*0fca6ea1SDimitry Andric       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2461*0fca6ea1SDimitry Andric     break;
2462*0fca6ea1SDimitry Andric   case SPIRV::IntelSubgroups:
2463*0fca6ea1SDimitry Andric     if (const auto *R = SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->Name))
2464*0fca6ea1SDimitry Andric       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2465*0fca6ea1SDimitry Andric     break;
2466*0fca6ea1SDimitry Andric   case SPIRV::GroupUniform:
2467*0fca6ea1SDimitry Andric     if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->Name))
2468*0fca6ea1SDimitry Andric       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2469*0fca6ea1SDimitry Andric     break;
2470*0fca6ea1SDimitry Andric   case SPIRV::WriteImage:
2471*0fca6ea1SDimitry Andric     return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0);
2472*0fca6ea1SDimitry Andric   case SPIRV::Select:
2473*0fca6ea1SDimitry Andric     return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0);
2474*0fca6ea1SDimitry Andric   case SPIRV::Construct:
2475*0fca6ea1SDimitry Andric     return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct,
2476*0fca6ea1SDimitry Andric                            0);
2477*0fca6ea1SDimitry Andric   case SPIRV::KernelClock:
2478*0fca6ea1SDimitry Andric     return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0);
2479*0fca6ea1SDimitry Andric   default:
2480*0fca6ea1SDimitry Andric     return std::make_tuple(-1, 0, 0);
2481*0fca6ea1SDimitry Andric   }
2482*0fca6ea1SDimitry Andric   return std::make_tuple(-1, 0, 0);
2483*0fca6ea1SDimitry Andric }
2484*0fca6ea1SDimitry Andric 
2485bdd1243dSDimitry Andric std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
2486bdd1243dSDimitry Andric                                  SPIRV::InstructionSet::InstructionSet Set,
2487bdd1243dSDimitry Andric                                  MachineIRBuilder &MIRBuilder,
2488bdd1243dSDimitry Andric                                  const Register OrigRet, const Type *OrigRetTy,
2489bdd1243dSDimitry Andric                                  const SmallVectorImpl<Register> &Args,
2490bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
2491bdd1243dSDimitry Andric   LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");
2492bdd1243dSDimitry Andric 
2493bdd1243dSDimitry Andric   // SPIR-V type and return register.
2494bdd1243dSDimitry Andric   Register ReturnRegister = OrigRet;
2495bdd1243dSDimitry Andric   SPIRVType *ReturnType = nullptr;
2496bdd1243dSDimitry Andric   if (OrigRetTy && !OrigRetTy->isVoidTy()) {
2497bdd1243dSDimitry Andric     ReturnType = GR->assignTypeToVReg(OrigRetTy, OrigRet, MIRBuilder);
249806c3fb27SDimitry Andric     if (!MIRBuilder.getMRI()->getRegClassOrNull(ReturnRegister))
249906c3fb27SDimitry Andric       MIRBuilder.getMRI()->setRegClass(ReturnRegister, &SPIRV::IDRegClass);
2500bdd1243dSDimitry Andric   } else if (OrigRetTy && OrigRetTy->isVoidTy()) {
2501bdd1243dSDimitry Andric     ReturnRegister = MIRBuilder.getMRI()->createVirtualRegister(&IDRegClass);
2502bdd1243dSDimitry Andric     MIRBuilder.getMRI()->setType(ReturnRegister, LLT::scalar(32));
2503bdd1243dSDimitry Andric     ReturnType = GR->assignTypeToVReg(OrigRetTy, ReturnRegister, MIRBuilder);
2504bdd1243dSDimitry Andric   }
2505bdd1243dSDimitry Andric 
2506bdd1243dSDimitry Andric   // Lookup the builtin in the TableGen records.
2507bdd1243dSDimitry Andric   std::unique_ptr<const IncomingCall> Call =
2508bdd1243dSDimitry Andric       lookupBuiltin(DemangledCall, Set, ReturnRegister, ReturnType, Args);
2509bdd1243dSDimitry Andric 
2510bdd1243dSDimitry Andric   if (!Call) {
2511bdd1243dSDimitry Andric     LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
2512bdd1243dSDimitry Andric     return std::nullopt;
2513bdd1243dSDimitry Andric   }
2514bdd1243dSDimitry Andric 
2515bdd1243dSDimitry Andric   // TODO: check if the provided args meet the builtin requirments.
2516bdd1243dSDimitry Andric   assert(Args.size() >= Call->Builtin->MinNumArgs &&
2517bdd1243dSDimitry Andric          "Too few arguments to generate the builtin");
2518bdd1243dSDimitry Andric   if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs)
2519bdd1243dSDimitry Andric     LLVM_DEBUG(dbgs() << "More arguments provided than required!\n");
2520bdd1243dSDimitry Andric 
2521bdd1243dSDimitry Andric   // Match the builtin with implementation based on the grouping.
2522bdd1243dSDimitry Andric   switch (Call->Builtin->Group) {
2523bdd1243dSDimitry Andric   case SPIRV::Extended:
2524bdd1243dSDimitry Andric     return generateExtInst(Call.get(), MIRBuilder, GR);
2525bdd1243dSDimitry Andric   case SPIRV::Relational:
2526bdd1243dSDimitry Andric     return generateRelationalInst(Call.get(), MIRBuilder, GR);
2527bdd1243dSDimitry Andric   case SPIRV::Group:
2528bdd1243dSDimitry Andric     return generateGroupInst(Call.get(), MIRBuilder, GR);
2529bdd1243dSDimitry Andric   case SPIRV::Variable:
2530bdd1243dSDimitry Andric     return generateBuiltinVar(Call.get(), MIRBuilder, GR);
2531bdd1243dSDimitry Andric   case SPIRV::Atomic:
2532bdd1243dSDimitry Andric     return generateAtomicInst(Call.get(), MIRBuilder, GR);
2533*0fca6ea1SDimitry Andric   case SPIRV::AtomicFloating:
2534*0fca6ea1SDimitry Andric     return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);
2535bdd1243dSDimitry Andric   case SPIRV::Barrier:
2536bdd1243dSDimitry Andric     return generateBarrierInst(Call.get(), MIRBuilder, GR);
2537*0fca6ea1SDimitry Andric   case SPIRV::CastToPtr:
2538*0fca6ea1SDimitry Andric     return generateCastToPtrInst(Call.get(), MIRBuilder);
2539bdd1243dSDimitry Andric   case SPIRV::Dot:
2540bdd1243dSDimitry Andric     return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);
2541*0fca6ea1SDimitry Andric   case SPIRV::Wave:
2542*0fca6ea1SDimitry Andric     return generateWaveInst(Call.get(), MIRBuilder, GR);
2543bdd1243dSDimitry Andric   case SPIRV::GetQuery:
2544bdd1243dSDimitry Andric     return generateGetQueryInst(Call.get(), MIRBuilder, GR);
2545bdd1243dSDimitry Andric   case SPIRV::ImageSizeQuery:
2546bdd1243dSDimitry Andric     return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);
2547bdd1243dSDimitry Andric   case SPIRV::ImageMiscQuery:
2548bdd1243dSDimitry Andric     return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);
2549bdd1243dSDimitry Andric   case SPIRV::ReadImage:
2550bdd1243dSDimitry Andric     return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2551bdd1243dSDimitry Andric   case SPIRV::WriteImage:
2552bdd1243dSDimitry Andric     return generateWriteImageInst(Call.get(), MIRBuilder, GR);
2553bdd1243dSDimitry Andric   case SPIRV::SampleImage:
2554bdd1243dSDimitry Andric     return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2555bdd1243dSDimitry Andric   case SPIRV::Select:
2556bdd1243dSDimitry Andric     return generateSelectInst(Call.get(), MIRBuilder);
2557*0fca6ea1SDimitry Andric   case SPIRV::Construct:
2558*0fca6ea1SDimitry Andric     return generateConstructInst(Call.get(), MIRBuilder, GR);
2559bdd1243dSDimitry Andric   case SPIRV::SpecConstant:
2560bdd1243dSDimitry Andric     return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
2561bdd1243dSDimitry Andric   case SPIRV::Enqueue:
2562bdd1243dSDimitry Andric     return generateEnqueueInst(Call.get(), MIRBuilder, GR);
2563bdd1243dSDimitry Andric   case SPIRV::AsyncCopy:
2564bdd1243dSDimitry Andric     return generateAsyncCopy(Call.get(), MIRBuilder, GR);
2565bdd1243dSDimitry Andric   case SPIRV::Convert:
2566bdd1243dSDimitry Andric     return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
2567bdd1243dSDimitry Andric   case SPIRV::VectorLoadStore:
2568bdd1243dSDimitry Andric     return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);
2569bdd1243dSDimitry Andric   case SPIRV::LoadStore:
2570bdd1243dSDimitry Andric     return generateLoadStoreInst(Call.get(), MIRBuilder, GR);
2571*0fca6ea1SDimitry Andric   case SPIRV::IntelSubgroups:
2572*0fca6ea1SDimitry Andric     return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR);
2573*0fca6ea1SDimitry Andric   case SPIRV::GroupUniform:
2574*0fca6ea1SDimitry Andric     return generateGroupUniformInst(Call.get(), MIRBuilder, GR);
2575*0fca6ea1SDimitry Andric   case SPIRV::KernelClock:
2576*0fca6ea1SDimitry Andric     return generateKernelClockInst(Call.get(), MIRBuilder, GR);
2577*0fca6ea1SDimitry Andric   case SPIRV::CoopMatr:
2578*0fca6ea1SDimitry Andric     return generateCoopMatrInst(Call.get(), MIRBuilder, GR);
2579bdd1243dSDimitry Andric   }
2580bdd1243dSDimitry Andric   return false;
2581bdd1243dSDimitry Andric }
2582bdd1243dSDimitry Andric 
2583*0fca6ea1SDimitry Andric Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall,
2584*0fca6ea1SDimitry Andric                                        unsigned ArgIdx, LLVMContext &Ctx) {
2585*0fca6ea1SDimitry Andric   SmallVector<StringRef, 10> BuiltinArgsTypeStrs;
2586*0fca6ea1SDimitry Andric   StringRef BuiltinArgs =
2587*0fca6ea1SDimitry Andric       DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
2588*0fca6ea1SDimitry Andric   BuiltinArgs.split(BuiltinArgsTypeStrs, ',', -1, false);
2589*0fca6ea1SDimitry Andric   if (ArgIdx >= BuiltinArgsTypeStrs.size())
2590*0fca6ea1SDimitry Andric     return nullptr;
2591*0fca6ea1SDimitry Andric   StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim();
2592*0fca6ea1SDimitry Andric 
2593*0fca6ea1SDimitry Andric   // Parse strings representing OpenCL builtin types.
2594*0fca6ea1SDimitry Andric   if (hasBuiltinTypePrefix(TypeStr)) {
2595*0fca6ea1SDimitry Andric     // OpenCL builtin types in demangled call strings have the following format:
2596*0fca6ea1SDimitry Andric     // e.g. ocl_image2d_ro
2597*0fca6ea1SDimitry Andric     [[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_");
2598*0fca6ea1SDimitry Andric     assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix");
2599*0fca6ea1SDimitry Andric 
2600*0fca6ea1SDimitry Andric     // Check if this is pointer to a builtin type and not just pointer
2601*0fca6ea1SDimitry Andric     // representing a builtin type. In case it is a pointer to builtin type,
2602*0fca6ea1SDimitry Andric     // this will require additional handling in the method calling
2603*0fca6ea1SDimitry Andric     // parseBuiltinCallArgumentBaseType(...) as this function only retrieves the
2604*0fca6ea1SDimitry Andric     // base types.
2605*0fca6ea1SDimitry Andric     if (TypeStr.ends_with("*"))
2606*0fca6ea1SDimitry Andric       TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));
2607*0fca6ea1SDimitry Andric 
2608*0fca6ea1SDimitry Andric     return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",
2609*0fca6ea1SDimitry Andric                                                Ctx);
2610*0fca6ea1SDimitry Andric   }
2611*0fca6ea1SDimitry Andric 
2612*0fca6ea1SDimitry Andric   // Parse type name in either "typeN" or "type vector[N]" format, where
2613*0fca6ea1SDimitry Andric   // N is the number of elements of the vector.
2614*0fca6ea1SDimitry Andric   Type *BaseType;
2615*0fca6ea1SDimitry Andric   unsigned VecElts = 0;
2616*0fca6ea1SDimitry Andric 
2617*0fca6ea1SDimitry Andric   BaseType = parseBasicTypeName(TypeStr, Ctx);
2618*0fca6ea1SDimitry Andric   if (!BaseType)
2619*0fca6ea1SDimitry Andric     // Unable to recognize SPIRV type name.
2620*0fca6ea1SDimitry Andric     return nullptr;
2621*0fca6ea1SDimitry Andric 
2622*0fca6ea1SDimitry Andric   // Handle "typeN*" or "type vector[N]*".
2623*0fca6ea1SDimitry Andric   TypeStr.consume_back("*");
2624*0fca6ea1SDimitry Andric 
2625*0fca6ea1SDimitry Andric   if (TypeStr.consume_front(" vector["))
2626*0fca6ea1SDimitry Andric     TypeStr = TypeStr.substr(0, TypeStr.find(']'));
2627*0fca6ea1SDimitry Andric 
2628*0fca6ea1SDimitry Andric   TypeStr.getAsInteger(10, VecElts);
2629*0fca6ea1SDimitry Andric   if (VecElts > 0)
2630*0fca6ea1SDimitry Andric     BaseType = VectorType::get(
2631*0fca6ea1SDimitry Andric         BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);
2632*0fca6ea1SDimitry Andric 
2633*0fca6ea1SDimitry Andric   return BaseType;
2634*0fca6ea1SDimitry Andric }
2635*0fca6ea1SDimitry Andric 
263606c3fb27SDimitry Andric struct BuiltinType {
2637bdd1243dSDimitry Andric   StringRef Name;
2638bdd1243dSDimitry Andric   uint32_t Opcode;
2639bdd1243dSDimitry Andric };
2640bdd1243dSDimitry Andric 
264106c3fb27SDimitry Andric #define GET_BuiltinTypes_DECL
264206c3fb27SDimitry Andric #define GET_BuiltinTypes_IMPL
2643bdd1243dSDimitry Andric 
264406c3fb27SDimitry Andric struct OpenCLType {
2645bdd1243dSDimitry Andric   StringRef Name;
264606c3fb27SDimitry Andric   StringRef SpirvTypeLiteral;
2647bdd1243dSDimitry Andric };
2648bdd1243dSDimitry Andric 
264906c3fb27SDimitry Andric #define GET_OpenCLTypes_DECL
265006c3fb27SDimitry Andric #define GET_OpenCLTypes_IMPL
2651bdd1243dSDimitry Andric 
2652bdd1243dSDimitry Andric #include "SPIRVGenTables.inc"
2653bdd1243dSDimitry Andric } // namespace SPIRV
2654bdd1243dSDimitry Andric 
2655bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
265606c3fb27SDimitry Andric // Misc functions for parsing builtin types.
2657bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
2658bdd1243dSDimitry Andric 
265906c3fb27SDimitry Andric static Type *parseTypeString(const StringRef Name, LLVMContext &Context) {
26605f757f3fSDimitry Andric   if (Name.starts_with("void"))
266106c3fb27SDimitry Andric     return Type::getVoidTy(Context);
26625f757f3fSDimitry Andric   else if (Name.starts_with("int") || Name.starts_with("uint"))
266306c3fb27SDimitry Andric     return Type::getInt32Ty(Context);
26645f757f3fSDimitry Andric   else if (Name.starts_with("float"))
266506c3fb27SDimitry Andric     return Type::getFloatTy(Context);
26665f757f3fSDimitry Andric   else if (Name.starts_with("half"))
266706c3fb27SDimitry Andric     return Type::getHalfTy(Context);
2668*0fca6ea1SDimitry Andric   report_fatal_error("Unable to recognize type!");
2669bdd1243dSDimitry Andric }
2670bdd1243dSDimitry Andric 
2671bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
2672bdd1243dSDimitry Andric // Implementation functions for builtin types.
2673bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
2674bdd1243dSDimitry Andric 
267506c3fb27SDimitry Andric static SPIRVType *getNonParameterizedType(const TargetExtType *ExtensionType,
267606c3fb27SDimitry Andric                                           const SPIRV::BuiltinType *TypeRecord,
2677bdd1243dSDimitry Andric                                           MachineIRBuilder &MIRBuilder,
2678bdd1243dSDimitry Andric                                           SPIRVGlobalRegistry *GR) {
2679bdd1243dSDimitry Andric   unsigned Opcode = TypeRecord->Opcode;
2680bdd1243dSDimitry Andric   // Create or get an existing type from GlobalRegistry.
268106c3fb27SDimitry Andric   return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode);
2682bdd1243dSDimitry Andric }
2683bdd1243dSDimitry Andric 
2684bdd1243dSDimitry Andric static SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder,
2685bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
2686bdd1243dSDimitry Andric   // Create or get an existing type from GlobalRegistry.
2687bdd1243dSDimitry Andric   return GR->getOrCreateOpTypeSampler(MIRBuilder);
2688bdd1243dSDimitry Andric }
2689bdd1243dSDimitry Andric 
269006c3fb27SDimitry Andric static SPIRVType *getPipeType(const TargetExtType *ExtensionType,
2691bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
2692bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR) {
269306c3fb27SDimitry Andric   assert(ExtensionType->getNumIntParameters() == 1 &&
269406c3fb27SDimitry Andric          "Invalid number of parameters for SPIR-V pipe builtin!");
2695bdd1243dSDimitry Andric   // Create or get an existing type from GlobalRegistry.
269606c3fb27SDimitry Andric   return GR->getOrCreateOpTypePipe(MIRBuilder,
269706c3fb27SDimitry Andric                                    SPIRV::AccessQualifier::AccessQualifier(
269806c3fb27SDimitry Andric                                        ExtensionType->getIntParameter(0)));
2699bdd1243dSDimitry Andric }
2700bdd1243dSDimitry Andric 
2701*0fca6ea1SDimitry Andric static SPIRVType *getCoopMatrType(const TargetExtType *ExtensionType,
2702*0fca6ea1SDimitry Andric                                   MachineIRBuilder &MIRBuilder,
2703*0fca6ea1SDimitry Andric                                   SPIRVGlobalRegistry *GR) {
2704*0fca6ea1SDimitry Andric   assert(ExtensionType->getNumIntParameters() == 4 &&
2705*0fca6ea1SDimitry Andric          "Invalid number of parameters for SPIR-V coop matrices builtin!");
2706*0fca6ea1SDimitry Andric   assert(ExtensionType->getNumTypeParameters() == 1 &&
2707*0fca6ea1SDimitry Andric          "SPIR-V coop matrices builtin type must have a type parameter!");
2708*0fca6ea1SDimitry Andric   const SPIRVType *ElemType =
2709*0fca6ea1SDimitry Andric       GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
2710*0fca6ea1SDimitry Andric   // Create or get an existing type from GlobalRegistry.
2711*0fca6ea1SDimitry Andric   return GR->getOrCreateOpTypeCoopMatr(
2712*0fca6ea1SDimitry Andric       MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0),
2713*0fca6ea1SDimitry Andric       ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
2714*0fca6ea1SDimitry Andric       ExtensionType->getIntParameter(3));
2715*0fca6ea1SDimitry Andric }
2716*0fca6ea1SDimitry Andric 
2717bdd1243dSDimitry Andric static SPIRVType *
271806c3fb27SDimitry Andric getImageType(const TargetExtType *ExtensionType,
271906c3fb27SDimitry Andric              const SPIRV::AccessQualifier::AccessQualifier Qualifier,
2720bdd1243dSDimitry Andric              MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
272106c3fb27SDimitry Andric   assert(ExtensionType->getNumTypeParameters() == 1 &&
272206c3fb27SDimitry Andric          "SPIR-V image builtin type must have sampled type parameter!");
272306c3fb27SDimitry Andric   const SPIRVType *SampledType =
272406c3fb27SDimitry Andric       GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
272506c3fb27SDimitry Andric   assert(ExtensionType->getNumIntParameters() == 7 &&
272606c3fb27SDimitry Andric          "Invalid number of parameters for SPIR-V image builtin!");
272706c3fb27SDimitry Andric   // Create or get an existing type from GlobalRegistry.
2728bdd1243dSDimitry Andric   return GR->getOrCreateOpTypeImage(
272906c3fb27SDimitry Andric       MIRBuilder, SampledType,
273006c3fb27SDimitry Andric       SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)),
273106c3fb27SDimitry Andric       ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
273206c3fb27SDimitry Andric       ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4),
273306c3fb27SDimitry Andric       SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)),
273406c3fb27SDimitry Andric       Qualifier == SPIRV::AccessQualifier::WriteOnly
2735bdd1243dSDimitry Andric           ? SPIRV::AccessQualifier::WriteOnly
273606c3fb27SDimitry Andric           : SPIRV::AccessQualifier::AccessQualifier(
273706c3fb27SDimitry Andric                 ExtensionType->getIntParameter(6)));
2738bdd1243dSDimitry Andric }
2739bdd1243dSDimitry Andric 
274006c3fb27SDimitry Andric static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType,
2741bdd1243dSDimitry Andric                                       MachineIRBuilder &MIRBuilder,
2742bdd1243dSDimitry Andric                                       SPIRVGlobalRegistry *GR) {
274306c3fb27SDimitry Andric   SPIRVType *OpaqueImageType = getImageType(
274406c3fb27SDimitry Andric       OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder, GR);
274506c3fb27SDimitry Andric   // Create or get an existing type from GlobalRegistry.
274606c3fb27SDimitry Andric   return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
2747bdd1243dSDimitry Andric }
2748bdd1243dSDimitry Andric 
2749bdd1243dSDimitry Andric namespace SPIRV {
2750*0fca6ea1SDimitry Andric TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,
2751*0fca6ea1SDimitry Andric                                                    LLVMContext &Context) {
27525f757f3fSDimitry Andric   StringRef NameWithParameters = TypeName;
27535f757f3fSDimitry Andric 
27545f757f3fSDimitry Andric   // Pointers-to-opaque-structs representing OpenCL types are first translated
27555f757f3fSDimitry Andric   // to equivalent SPIR-V types. OpenCL builtin type names should have the
27565f757f3fSDimitry Andric   // following format: e.g. %opencl.event_t
27575f757f3fSDimitry Andric   if (NameWithParameters.starts_with("opencl.")) {
27585f757f3fSDimitry Andric     const SPIRV::OpenCLType *OCLTypeRecord =
27595f757f3fSDimitry Andric         SPIRV::lookupOpenCLType(NameWithParameters);
27605f757f3fSDimitry Andric     if (!OCLTypeRecord)
27615f757f3fSDimitry Andric       report_fatal_error("Missing TableGen record for OpenCL type: " +
27625f757f3fSDimitry Andric                          NameWithParameters);
27635f757f3fSDimitry Andric     NameWithParameters = OCLTypeRecord->SpirvTypeLiteral;
27645f757f3fSDimitry Andric     // Continue with the SPIR-V builtin type...
27655f757f3fSDimitry Andric   }
27665f757f3fSDimitry Andric 
27675f757f3fSDimitry Andric   // Names of the opaque structs representing a SPIR-V builtins without
27685f757f3fSDimitry Andric   // parameters should have the following format: e.g. %spirv.Event
27695f757f3fSDimitry Andric   assert(NameWithParameters.starts_with("spirv.") &&
27705f757f3fSDimitry Andric          "Unknown builtin opaque type!");
27715f757f3fSDimitry Andric 
27725f757f3fSDimitry Andric   // Parameterized SPIR-V builtins names follow this format:
27735f757f3fSDimitry Andric   // e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._0
2774cb14a3feSDimitry Andric   if (!NameWithParameters.contains('_'))
2775*0fca6ea1SDimitry Andric     return TargetExtType::get(Context, NameWithParameters);
27765f757f3fSDimitry Andric 
27775f757f3fSDimitry Andric   SmallVector<StringRef> Parameters;
27785f757f3fSDimitry Andric   unsigned BaseNameLength = NameWithParameters.find('_') - 1;
27795f757f3fSDimitry Andric   SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_");
27805f757f3fSDimitry Andric 
27815f757f3fSDimitry Andric   SmallVector<Type *, 1> TypeParameters;
27825f757f3fSDimitry Andric   bool HasTypeParameter = !isDigit(Parameters[0][0]);
27835f757f3fSDimitry Andric   if (HasTypeParameter)
2784*0fca6ea1SDimitry Andric     TypeParameters.push_back(parseTypeString(Parameters[0], Context));
27855f757f3fSDimitry Andric   SmallVector<unsigned> IntParameters;
27865f757f3fSDimitry Andric   for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) {
27875f757f3fSDimitry Andric     unsigned IntParameter = 0;
27885f757f3fSDimitry Andric     bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter);
2789*0fca6ea1SDimitry Andric     (void)ValidLiteral;
27905f757f3fSDimitry Andric     assert(ValidLiteral &&
27915f757f3fSDimitry Andric            "Invalid format of SPIR-V builtin parameter literal!");
27925f757f3fSDimitry Andric     IntParameters.push_back(IntParameter);
27935f757f3fSDimitry Andric   }
2794*0fca6ea1SDimitry Andric   return TargetExtType::get(Context,
27955f757f3fSDimitry Andric                             NameWithParameters.substr(0, BaseNameLength),
27965f757f3fSDimitry Andric                             TypeParameters, IntParameters);
27975f757f3fSDimitry Andric }
27985f757f3fSDimitry Andric 
279906c3fb27SDimitry Andric SPIRVType *lowerBuiltinType(const Type *OpaqueType,
2800bdd1243dSDimitry Andric                             SPIRV::AccessQualifier::AccessQualifier AccessQual,
2801bdd1243dSDimitry Andric                             MachineIRBuilder &MIRBuilder,
2802bdd1243dSDimitry Andric                             SPIRVGlobalRegistry *GR) {
280306c3fb27SDimitry Andric   // In LLVM IR, SPIR-V and OpenCL builtin types are represented as either
280406c3fb27SDimitry Andric   // target(...) target extension types or pointers-to-opaque-structs. The
280506c3fb27SDimitry Andric   // approach relying on structs is deprecated and works only in the non-opaque
280606c3fb27SDimitry Andric   // pointer mode (-opaque-pointers=0).
280706c3fb27SDimitry Andric   // In order to maintain compatibility with LLVM IR generated by older versions
280806c3fb27SDimitry Andric   // of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are
280906c3fb27SDimitry Andric   // "translated" to target extension types. This translation is temporary and
281006c3fb27SDimitry Andric   // will be removed in the future release of LLVM.
281106c3fb27SDimitry Andric   const TargetExtType *BuiltinType = dyn_cast<TargetExtType>(OpaqueType);
281206c3fb27SDimitry Andric   if (!BuiltinType)
28135f757f3fSDimitry Andric     BuiltinType = parseBuiltinTypeNameToTargetExtType(
2814*0fca6ea1SDimitry Andric         OpaqueType->getStructName().str(), MIRBuilder.getContext());
281506c3fb27SDimitry Andric 
2816bdd1243dSDimitry Andric   unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
2817bdd1243dSDimitry Andric 
281806c3fb27SDimitry Andric   const StringRef Name = BuiltinType->getName();
2819bdd1243dSDimitry Andric   LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
2820bdd1243dSDimitry Andric 
2821bdd1243dSDimitry Andric   // Lookup the demangled builtin type in the TableGen records.
282206c3fb27SDimitry Andric   const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
2823bdd1243dSDimitry Andric   if (!TypeRecord)
2824bdd1243dSDimitry Andric     report_fatal_error("Missing TableGen record for builtin type: " + Name);
2825bdd1243dSDimitry Andric 
2826bdd1243dSDimitry Andric   // "Lower" the BuiltinType into TargetType. The following get<...>Type methods
282706c3fb27SDimitry Andric   // use the implementation details from TableGen records or TargetExtType
282806c3fb27SDimitry Andric   // parameters to either create a new OpType<...> machine instruction or get an
282906c3fb27SDimitry Andric   // existing equivalent SPIRVType from GlobalRegistry.
2830bdd1243dSDimitry Andric   SPIRVType *TargetType;
2831bdd1243dSDimitry Andric   switch (TypeRecord->Opcode) {
2832bdd1243dSDimitry Andric   case SPIRV::OpTypeImage:
283306c3fb27SDimitry Andric     TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
2834bdd1243dSDimitry Andric     break;
2835bdd1243dSDimitry Andric   case SPIRV::OpTypePipe:
283606c3fb27SDimitry Andric     TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
2837bdd1243dSDimitry Andric     break;
2838bdd1243dSDimitry Andric   case SPIRV::OpTypeDeviceEvent:
2839bdd1243dSDimitry Andric     TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
2840bdd1243dSDimitry Andric     break;
2841bdd1243dSDimitry Andric   case SPIRV::OpTypeSampler:
2842bdd1243dSDimitry Andric     TargetType = getSamplerType(MIRBuilder, GR);
2843bdd1243dSDimitry Andric     break;
2844bdd1243dSDimitry Andric   case SPIRV::OpTypeSampledImage:
284506c3fb27SDimitry Andric     TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
2846bdd1243dSDimitry Andric     break;
2847*0fca6ea1SDimitry Andric   case SPIRV::OpTypeCooperativeMatrixKHR:
2848*0fca6ea1SDimitry Andric     TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
2849*0fca6ea1SDimitry Andric     break;
2850bdd1243dSDimitry Andric   default:
285106c3fb27SDimitry Andric     TargetType =
285206c3fb27SDimitry Andric         getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
2853bdd1243dSDimitry Andric     break;
2854bdd1243dSDimitry Andric   }
2855bdd1243dSDimitry Andric 
2856bdd1243dSDimitry Andric   // Emit OpName instruction if a new OpType<...> instruction was added
2857bdd1243dSDimitry Andric   // (equivalent type was not found in GlobalRegistry).
2858bdd1243dSDimitry Andric   if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
285906c3fb27SDimitry Andric     buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder);
2860bdd1243dSDimitry Andric 
2861bdd1243dSDimitry Andric   return TargetType;
2862bdd1243dSDimitry Andric }
2863bdd1243dSDimitry Andric } // namespace SPIRV
2864bdd1243dSDimitry Andric } // namespace llvm
2865