xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1*bdd1243dSDimitry Andric //===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===//
2*bdd1243dSDimitry Andric //
3*bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*bdd1243dSDimitry Andric //
7*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
8*bdd1243dSDimitry Andric //
9*bdd1243dSDimitry Andric // This file implements lowering builtin function calls and types using their
10*bdd1243dSDimitry Andric // demangled names and TableGen records.
11*bdd1243dSDimitry Andric //
12*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
13*bdd1243dSDimitry Andric 
14*bdd1243dSDimitry Andric #include "SPIRVBuiltins.h"
15*bdd1243dSDimitry Andric #include "SPIRV.h"
16*bdd1243dSDimitry Andric #include "SPIRVUtils.h"
17*bdd1243dSDimitry Andric #include "llvm/Analysis/ValueTracking.h"
18*bdd1243dSDimitry Andric #include "llvm/IR/IntrinsicsSPIRV.h"
19*bdd1243dSDimitry Andric #include <string>
20*bdd1243dSDimitry Andric #include <tuple>
21*bdd1243dSDimitry Andric 
22*bdd1243dSDimitry Andric #define DEBUG_TYPE "spirv-builtins"
23*bdd1243dSDimitry Andric 
24*bdd1243dSDimitry Andric namespace llvm {
25*bdd1243dSDimitry Andric namespace SPIRV {
26*bdd1243dSDimitry Andric #define GET_BuiltinGroup_DECL
27*bdd1243dSDimitry Andric #include "SPIRVGenTables.inc"
28*bdd1243dSDimitry Andric 
29*bdd1243dSDimitry Andric struct DemangledBuiltin {
30*bdd1243dSDimitry Andric   StringRef Name;
31*bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
32*bdd1243dSDimitry Andric   BuiltinGroup Group;
33*bdd1243dSDimitry Andric   uint8_t MinNumArgs;
34*bdd1243dSDimitry Andric   uint8_t MaxNumArgs;
35*bdd1243dSDimitry Andric };
36*bdd1243dSDimitry Andric 
37*bdd1243dSDimitry Andric #define GET_DemangledBuiltins_DECL
38*bdd1243dSDimitry Andric #define GET_DemangledBuiltins_IMPL
39*bdd1243dSDimitry Andric 
40*bdd1243dSDimitry Andric struct IncomingCall {
41*bdd1243dSDimitry Andric   const std::string BuiltinName;
42*bdd1243dSDimitry Andric   const DemangledBuiltin *Builtin;
43*bdd1243dSDimitry Andric 
44*bdd1243dSDimitry Andric   const Register ReturnRegister;
45*bdd1243dSDimitry Andric   const SPIRVType *ReturnType;
46*bdd1243dSDimitry Andric   const SmallVectorImpl<Register> &Arguments;
47*bdd1243dSDimitry Andric 
48*bdd1243dSDimitry Andric   IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin,
49*bdd1243dSDimitry Andric                const Register ReturnRegister, const SPIRVType *ReturnType,
50*bdd1243dSDimitry Andric                const SmallVectorImpl<Register> &Arguments)
51*bdd1243dSDimitry Andric       : BuiltinName(BuiltinName), Builtin(Builtin),
52*bdd1243dSDimitry Andric         ReturnRegister(ReturnRegister), ReturnType(ReturnType),
53*bdd1243dSDimitry Andric         Arguments(Arguments) {}
54*bdd1243dSDimitry Andric };
55*bdd1243dSDimitry Andric 
56*bdd1243dSDimitry Andric struct NativeBuiltin {
57*bdd1243dSDimitry Andric   StringRef Name;
58*bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
59*bdd1243dSDimitry Andric   uint32_t Opcode;
60*bdd1243dSDimitry Andric };
61*bdd1243dSDimitry Andric 
62*bdd1243dSDimitry Andric #define GET_NativeBuiltins_DECL
63*bdd1243dSDimitry Andric #define GET_NativeBuiltins_IMPL
64*bdd1243dSDimitry Andric 
65*bdd1243dSDimitry Andric struct GroupBuiltin {
66*bdd1243dSDimitry Andric   StringRef Name;
67*bdd1243dSDimitry Andric   uint32_t Opcode;
68*bdd1243dSDimitry Andric   uint32_t GroupOperation;
69*bdd1243dSDimitry Andric   bool IsElect;
70*bdd1243dSDimitry Andric   bool IsAllOrAny;
71*bdd1243dSDimitry Andric   bool IsAllEqual;
72*bdd1243dSDimitry Andric   bool IsBallot;
73*bdd1243dSDimitry Andric   bool IsInverseBallot;
74*bdd1243dSDimitry Andric   bool IsBallotBitExtract;
75*bdd1243dSDimitry Andric   bool IsBallotFindBit;
76*bdd1243dSDimitry Andric   bool IsLogical;
77*bdd1243dSDimitry Andric   bool NoGroupOperation;
78*bdd1243dSDimitry Andric   bool HasBoolArg;
79*bdd1243dSDimitry Andric };
80*bdd1243dSDimitry Andric 
81*bdd1243dSDimitry Andric #define GET_GroupBuiltins_DECL
82*bdd1243dSDimitry Andric #define GET_GroupBuiltins_IMPL
83*bdd1243dSDimitry Andric 
84*bdd1243dSDimitry Andric struct GetBuiltin {
85*bdd1243dSDimitry Andric   StringRef Name;
86*bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
87*bdd1243dSDimitry Andric   BuiltIn::BuiltIn Value;
88*bdd1243dSDimitry Andric };
89*bdd1243dSDimitry Andric 
90*bdd1243dSDimitry Andric using namespace BuiltIn;
91*bdd1243dSDimitry Andric #define GET_GetBuiltins_DECL
92*bdd1243dSDimitry Andric #define GET_GetBuiltins_IMPL
93*bdd1243dSDimitry Andric 
94*bdd1243dSDimitry Andric struct ImageQueryBuiltin {
95*bdd1243dSDimitry Andric   StringRef Name;
96*bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
97*bdd1243dSDimitry Andric   uint32_t Component;
98*bdd1243dSDimitry Andric };
99*bdd1243dSDimitry Andric 
100*bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_DECL
101*bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_IMPL
102*bdd1243dSDimitry Andric 
103*bdd1243dSDimitry Andric struct ConvertBuiltin {
104*bdd1243dSDimitry Andric   StringRef Name;
105*bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
106*bdd1243dSDimitry Andric   bool IsDestinationSigned;
107*bdd1243dSDimitry Andric   bool IsSaturated;
108*bdd1243dSDimitry Andric   bool IsRounded;
109*bdd1243dSDimitry Andric   FPRoundingMode::FPRoundingMode RoundingMode;
110*bdd1243dSDimitry Andric };
111*bdd1243dSDimitry Andric 
112*bdd1243dSDimitry Andric struct VectorLoadStoreBuiltin {
113*bdd1243dSDimitry Andric   StringRef Name;
114*bdd1243dSDimitry Andric   InstructionSet::InstructionSet Set;
115*bdd1243dSDimitry Andric   uint32_t Number;
116*bdd1243dSDimitry Andric   bool IsRounded;
117*bdd1243dSDimitry Andric   FPRoundingMode::FPRoundingMode RoundingMode;
118*bdd1243dSDimitry Andric };
119*bdd1243dSDimitry Andric 
120*bdd1243dSDimitry Andric using namespace FPRoundingMode;
121*bdd1243dSDimitry Andric #define GET_ConvertBuiltins_DECL
122*bdd1243dSDimitry Andric #define GET_ConvertBuiltins_IMPL
123*bdd1243dSDimitry Andric 
124*bdd1243dSDimitry Andric using namespace InstructionSet;
125*bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_DECL
126*bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_IMPL
127*bdd1243dSDimitry Andric 
128*bdd1243dSDimitry Andric #define GET_CLMemoryScope_DECL
129*bdd1243dSDimitry Andric #define GET_CLSamplerAddressingMode_DECL
130*bdd1243dSDimitry Andric #define GET_CLMemoryFenceFlags_DECL
131*bdd1243dSDimitry Andric #define GET_ExtendedBuiltins_DECL
132*bdd1243dSDimitry Andric #include "SPIRVGenTables.inc"
133*bdd1243dSDimitry Andric } // namespace SPIRV
134*bdd1243dSDimitry Andric 
135*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
136*bdd1243dSDimitry Andric // Misc functions for looking up builtins and veryfying requirements using
137*bdd1243dSDimitry Andric // TableGen records
138*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
139*bdd1243dSDimitry Andric 
140*bdd1243dSDimitry Andric /// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
141*bdd1243dSDimitry Andric /// the provided \p DemangledCall and specified \p Set.
142*bdd1243dSDimitry Andric ///
143*bdd1243dSDimitry Andric /// The lookup follows the following algorithm, returning the first successful
144*bdd1243dSDimitry Andric /// match:
145*bdd1243dSDimitry Andric /// 1. Search with the plain demangled name (expecting a 1:1 match).
146*bdd1243dSDimitry Andric /// 2. Search with the prefix before or suffix after the demangled name
147*bdd1243dSDimitry Andric /// signyfying the type of the first argument.
148*bdd1243dSDimitry Andric ///
149*bdd1243dSDimitry Andric /// \returns Wrapper around the demangled call and found builtin definition.
150*bdd1243dSDimitry Andric static std::unique_ptr<const SPIRV::IncomingCall>
151*bdd1243dSDimitry Andric lookupBuiltin(StringRef DemangledCall,
152*bdd1243dSDimitry Andric               SPIRV::InstructionSet::InstructionSet Set,
153*bdd1243dSDimitry Andric               Register ReturnRegister, const SPIRVType *ReturnType,
154*bdd1243dSDimitry Andric               const SmallVectorImpl<Register> &Arguments) {
155*bdd1243dSDimitry Andric   // Extract the builtin function name and types of arguments from the call
156*bdd1243dSDimitry Andric   // skeleton.
157*bdd1243dSDimitry Andric   std::string BuiltinName =
158*bdd1243dSDimitry Andric       DemangledCall.substr(0, DemangledCall.find('(')).str();
159*bdd1243dSDimitry Andric 
160*bdd1243dSDimitry Andric   // Check if the extracted name contains type information between angle
161*bdd1243dSDimitry Andric   // brackets. If so, the builtin is an instantiated template - needs to have
162*bdd1243dSDimitry Andric   // the information after angle brackets and return type removed.
163*bdd1243dSDimitry Andric   if (BuiltinName.find('<') && BuiltinName.back() == '>') {
164*bdd1243dSDimitry Andric     BuiltinName = BuiltinName.substr(0, BuiltinName.find('<'));
165*bdd1243dSDimitry Andric     BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(" ") + 1);
166*bdd1243dSDimitry Andric   }
167*bdd1243dSDimitry Andric 
168*bdd1243dSDimitry Andric   // Check if the extracted name begins with "__spirv_ImageSampleExplicitLod"
169*bdd1243dSDimitry Andric   // contains return type information at the end "_R<type>", if so extract the
170*bdd1243dSDimitry Andric   // plain builtin name without the type information.
171*bdd1243dSDimitry Andric   if (StringRef(BuiltinName).contains("__spirv_ImageSampleExplicitLod") &&
172*bdd1243dSDimitry Andric       StringRef(BuiltinName).contains("_R")) {
173*bdd1243dSDimitry Andric     BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R"));
174*bdd1243dSDimitry Andric   }
175*bdd1243dSDimitry Andric 
176*bdd1243dSDimitry Andric   SmallVector<StringRef, 10> BuiltinArgumentTypes;
177*bdd1243dSDimitry Andric   StringRef BuiltinArgs =
178*bdd1243dSDimitry Andric       DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
179*bdd1243dSDimitry Andric   BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false);
180*bdd1243dSDimitry Andric 
181*bdd1243dSDimitry Andric   // Look up the builtin in the defined set. Start with the plain demangled
182*bdd1243dSDimitry Andric   // name, expecting a 1:1 match in the defined builtin set.
183*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin;
184*bdd1243dSDimitry Andric   if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set)))
185*bdd1243dSDimitry Andric     return std::make_unique<SPIRV::IncomingCall>(
186*bdd1243dSDimitry Andric         BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
187*bdd1243dSDimitry Andric 
188*bdd1243dSDimitry Andric   // If the initial look up was unsuccessful and the demangled call takes at
189*bdd1243dSDimitry Andric   // least 1 argument, add a prefix or suffix signifying the type of the first
190*bdd1243dSDimitry Andric   // argument and repeat the search.
191*bdd1243dSDimitry Andric   if (BuiltinArgumentTypes.size() >= 1) {
192*bdd1243dSDimitry Andric     char FirstArgumentType = BuiltinArgumentTypes[0][0];
193*bdd1243dSDimitry Andric     // Prefix to be added to the builtin's name for lookup.
194*bdd1243dSDimitry Andric     // For example, OpenCL "abs" taking an unsigned value has a prefix "u_".
195*bdd1243dSDimitry Andric     std::string Prefix;
196*bdd1243dSDimitry Andric 
197*bdd1243dSDimitry Andric     switch (FirstArgumentType) {
198*bdd1243dSDimitry Andric     // Unsigned:
199*bdd1243dSDimitry Andric     case 'u':
200*bdd1243dSDimitry Andric       if (Set == SPIRV::InstructionSet::OpenCL_std)
201*bdd1243dSDimitry Andric         Prefix = "u_";
202*bdd1243dSDimitry Andric       else if (Set == SPIRV::InstructionSet::GLSL_std_450)
203*bdd1243dSDimitry Andric         Prefix = "u";
204*bdd1243dSDimitry Andric       break;
205*bdd1243dSDimitry Andric     // Signed:
206*bdd1243dSDimitry Andric     case 'c':
207*bdd1243dSDimitry Andric     case 's':
208*bdd1243dSDimitry Andric     case 'i':
209*bdd1243dSDimitry Andric     case 'l':
210*bdd1243dSDimitry Andric       if (Set == SPIRV::InstructionSet::OpenCL_std)
211*bdd1243dSDimitry Andric         Prefix = "s_";
212*bdd1243dSDimitry Andric       else if (Set == SPIRV::InstructionSet::GLSL_std_450)
213*bdd1243dSDimitry Andric         Prefix = "s";
214*bdd1243dSDimitry Andric       break;
215*bdd1243dSDimitry Andric     // Floating-point:
216*bdd1243dSDimitry Andric     case 'f':
217*bdd1243dSDimitry Andric     case 'd':
218*bdd1243dSDimitry Andric     case 'h':
219*bdd1243dSDimitry Andric       if (Set == SPIRV::InstructionSet::OpenCL_std ||
220*bdd1243dSDimitry Andric           Set == SPIRV::InstructionSet::GLSL_std_450)
221*bdd1243dSDimitry Andric         Prefix = "f";
222*bdd1243dSDimitry Andric       break;
223*bdd1243dSDimitry Andric     }
224*bdd1243dSDimitry Andric 
225*bdd1243dSDimitry Andric     // If argument-type name prefix was added, look up the builtin again.
226*bdd1243dSDimitry Andric     if (!Prefix.empty() &&
227*bdd1243dSDimitry Andric         (Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set)))
228*bdd1243dSDimitry Andric       return std::make_unique<SPIRV::IncomingCall>(
229*bdd1243dSDimitry Andric           BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
230*bdd1243dSDimitry Andric 
231*bdd1243dSDimitry Andric     // If lookup with a prefix failed, find a suffix to be added to the
232*bdd1243dSDimitry Andric     // builtin's name for lookup. For example, OpenCL "group_reduce_max" taking
233*bdd1243dSDimitry Andric     // an unsigned value has a suffix "u".
234*bdd1243dSDimitry Andric     std::string Suffix;
235*bdd1243dSDimitry Andric 
236*bdd1243dSDimitry Andric     switch (FirstArgumentType) {
237*bdd1243dSDimitry Andric     // Unsigned:
238*bdd1243dSDimitry Andric     case 'u':
239*bdd1243dSDimitry Andric       Suffix = "u";
240*bdd1243dSDimitry Andric       break;
241*bdd1243dSDimitry Andric     // Signed:
242*bdd1243dSDimitry Andric     case 'c':
243*bdd1243dSDimitry Andric     case 's':
244*bdd1243dSDimitry Andric     case 'i':
245*bdd1243dSDimitry Andric     case 'l':
246*bdd1243dSDimitry Andric       Suffix = "s";
247*bdd1243dSDimitry Andric       break;
248*bdd1243dSDimitry Andric     // Floating-point:
249*bdd1243dSDimitry Andric     case 'f':
250*bdd1243dSDimitry Andric     case 'd':
251*bdd1243dSDimitry Andric     case 'h':
252*bdd1243dSDimitry Andric       Suffix = "f";
253*bdd1243dSDimitry Andric       break;
254*bdd1243dSDimitry Andric     }
255*bdd1243dSDimitry Andric 
256*bdd1243dSDimitry Andric     // If argument-type name suffix was added, look up the builtin again.
257*bdd1243dSDimitry Andric     if (!Suffix.empty() &&
258*bdd1243dSDimitry Andric         (Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set)))
259*bdd1243dSDimitry Andric       return std::make_unique<SPIRV::IncomingCall>(
260*bdd1243dSDimitry Andric           BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
261*bdd1243dSDimitry Andric   }
262*bdd1243dSDimitry Andric 
263*bdd1243dSDimitry Andric   // No builtin with such name was found in the set.
264*bdd1243dSDimitry Andric   return nullptr;
265*bdd1243dSDimitry Andric }
266*bdd1243dSDimitry Andric 
267*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
268*bdd1243dSDimitry Andric // Helper functions for building misc instructions
269*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
270*bdd1243dSDimitry Andric 
271*bdd1243dSDimitry Andric /// Helper function building either a resulting scalar or vector bool register
272*bdd1243dSDimitry Andric /// depending on the expected \p ResultType.
273*bdd1243dSDimitry Andric ///
274*bdd1243dSDimitry Andric /// \returns Tuple of the resulting register and its type.
275*bdd1243dSDimitry Andric static std::tuple<Register, SPIRVType *>
276*bdd1243dSDimitry Andric buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType,
277*bdd1243dSDimitry Andric                   SPIRVGlobalRegistry *GR) {
278*bdd1243dSDimitry Andric   LLT Type;
279*bdd1243dSDimitry Andric   SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
280*bdd1243dSDimitry Andric 
281*bdd1243dSDimitry Andric   if (ResultType->getOpcode() == SPIRV::OpTypeVector) {
282*bdd1243dSDimitry Andric     unsigned VectorElements = ResultType->getOperand(2).getImm();
283*bdd1243dSDimitry Andric     BoolType =
284*bdd1243dSDimitry Andric         GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder);
285*bdd1243dSDimitry Andric     const FixedVectorType *LLVMVectorType =
286*bdd1243dSDimitry Andric         cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType));
287*bdd1243dSDimitry Andric     Type = LLT::vector(LLVMVectorType->getElementCount(), 1);
288*bdd1243dSDimitry Andric   } else {
289*bdd1243dSDimitry Andric     Type = LLT::scalar(1);
290*bdd1243dSDimitry Andric   }
291*bdd1243dSDimitry Andric 
292*bdd1243dSDimitry Andric   Register ResultRegister =
293*bdd1243dSDimitry Andric       MIRBuilder.getMRI()->createGenericVirtualRegister(Type);
294*bdd1243dSDimitry Andric   GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF());
295*bdd1243dSDimitry Andric   return std::make_tuple(ResultRegister, BoolType);
296*bdd1243dSDimitry Andric }
297*bdd1243dSDimitry Andric 
298*bdd1243dSDimitry Andric /// Helper function for building either a vector or scalar select instruction
299*bdd1243dSDimitry Andric /// depending on the expected \p ResultType.
300*bdd1243dSDimitry Andric static bool buildSelectInst(MachineIRBuilder &MIRBuilder,
301*bdd1243dSDimitry Andric                             Register ReturnRegister, Register SourceRegister,
302*bdd1243dSDimitry Andric                             const SPIRVType *ReturnType,
303*bdd1243dSDimitry Andric                             SPIRVGlobalRegistry *GR) {
304*bdd1243dSDimitry Andric   Register TrueConst, FalseConst;
305*bdd1243dSDimitry Andric 
306*bdd1243dSDimitry Andric   if (ReturnType->getOpcode() == SPIRV::OpTypeVector) {
307*bdd1243dSDimitry Andric     unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType);
308*bdd1243dSDimitry Andric     uint64_t AllOnes = APInt::getAllOnesValue(Bits).getZExtValue();
309*bdd1243dSDimitry Andric     TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType);
310*bdd1243dSDimitry Andric     FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType);
311*bdd1243dSDimitry Andric   } else {
312*bdd1243dSDimitry Andric     TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType);
313*bdd1243dSDimitry Andric     FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType);
314*bdd1243dSDimitry Andric   }
315*bdd1243dSDimitry Andric   return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst,
316*bdd1243dSDimitry Andric                                 FalseConst);
317*bdd1243dSDimitry Andric }
318*bdd1243dSDimitry Andric 
319*bdd1243dSDimitry Andric /// Helper function for building a load instruction loading into the
320*bdd1243dSDimitry Andric /// \p DestinationReg.
321*bdd1243dSDimitry Andric static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister,
322*bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
323*bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR, LLT LowLevelType,
324*bdd1243dSDimitry Andric                               Register DestinationReg = Register(0)) {
325*bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
326*bdd1243dSDimitry Andric   if (!DestinationReg.isValid()) {
327*bdd1243dSDimitry Andric     DestinationReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
328*bdd1243dSDimitry Andric     MRI->setType(DestinationReg, LLT::scalar(32));
329*bdd1243dSDimitry Andric     GR->assignSPIRVTypeToVReg(BaseType, DestinationReg, MIRBuilder.getMF());
330*bdd1243dSDimitry Andric   }
331*bdd1243dSDimitry Andric   // TODO: consider using correct address space and alignment (p0 is canonical
332*bdd1243dSDimitry Andric   // type for selection though).
333*bdd1243dSDimitry Andric   MachinePointerInfo PtrInfo = MachinePointerInfo();
334*bdd1243dSDimitry Andric   MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align());
335*bdd1243dSDimitry Andric   return DestinationReg;
336*bdd1243dSDimitry Andric }
337*bdd1243dSDimitry Andric 
338*bdd1243dSDimitry Andric /// Helper function for building a load instruction for loading a builtin global
339*bdd1243dSDimitry Andric /// variable of \p BuiltinValue value.
340*bdd1243dSDimitry Andric static Register buildBuiltinVariableLoad(MachineIRBuilder &MIRBuilder,
341*bdd1243dSDimitry Andric                                          SPIRVType *VariableType,
342*bdd1243dSDimitry Andric                                          SPIRVGlobalRegistry *GR,
343*bdd1243dSDimitry Andric                                          SPIRV::BuiltIn::BuiltIn BuiltinValue,
344*bdd1243dSDimitry Andric                                          LLT LLType,
345*bdd1243dSDimitry Andric                                          Register Reg = Register(0)) {
346*bdd1243dSDimitry Andric   Register NewRegister =
347*bdd1243dSDimitry Andric       MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass);
348*bdd1243dSDimitry Andric   MIRBuilder.getMRI()->setType(NewRegister,
349*bdd1243dSDimitry Andric                                LLT::pointer(0, GR->getPointerSize()));
350*bdd1243dSDimitry Andric   SPIRVType *PtrType = GR->getOrCreateSPIRVPointerType(
351*bdd1243dSDimitry Andric       VariableType, MIRBuilder, SPIRV::StorageClass::Input);
352*bdd1243dSDimitry Andric   GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
353*bdd1243dSDimitry Andric 
354*bdd1243dSDimitry Andric   // Set up the global OpVariable with the necessary builtin decorations.
355*bdd1243dSDimitry Andric   Register Variable = GR->buildGlobalVariable(
356*bdd1243dSDimitry Andric       NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
357*bdd1243dSDimitry Andric       SPIRV::StorageClass::Input, nullptr, true, true,
358*bdd1243dSDimitry Andric       SPIRV::LinkageType::Import, MIRBuilder, false);
359*bdd1243dSDimitry Andric 
360*bdd1243dSDimitry Andric   // Load the value from the global variable.
361*bdd1243dSDimitry Andric   Register LoadedRegister =
362*bdd1243dSDimitry Andric       buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg);
363*bdd1243dSDimitry Andric   MIRBuilder.getMRI()->setType(LoadedRegister, LLType);
364*bdd1243dSDimitry Andric   return LoadedRegister;
365*bdd1243dSDimitry Andric }
366*bdd1243dSDimitry Andric 
367*bdd1243dSDimitry Andric /// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg
368*bdd1243dSDimitry Andric /// and its definition, set the new register as a destination of the definition,
369*bdd1243dSDimitry Andric /// assign SPIRVType to both registers. If SpirvTy is provided, use it as
370*bdd1243dSDimitry Andric /// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in
371*bdd1243dSDimitry Andric /// SPIRVPreLegalizer.cpp.
372*bdd1243dSDimitry Andric extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
373*bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR,
374*bdd1243dSDimitry Andric                                   MachineIRBuilder &MIB,
375*bdd1243dSDimitry Andric                                   MachineRegisterInfo &MRI);
376*bdd1243dSDimitry Andric 
377*bdd1243dSDimitry Andric // TODO: Move to TableGen.
378*bdd1243dSDimitry Andric static SPIRV::MemorySemantics::MemorySemantics
379*bdd1243dSDimitry Andric getSPIRVMemSemantics(std::memory_order MemOrder) {
380*bdd1243dSDimitry Andric   switch (MemOrder) {
381*bdd1243dSDimitry Andric   case std::memory_order::memory_order_relaxed:
382*bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::None;
383*bdd1243dSDimitry Andric   case std::memory_order::memory_order_acquire:
384*bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::Acquire;
385*bdd1243dSDimitry Andric   case std::memory_order::memory_order_release:
386*bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::Release;
387*bdd1243dSDimitry Andric   case std::memory_order::memory_order_acq_rel:
388*bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::AcquireRelease;
389*bdd1243dSDimitry Andric   case std::memory_order::memory_order_seq_cst:
390*bdd1243dSDimitry Andric     return SPIRV::MemorySemantics::SequentiallyConsistent;
391*bdd1243dSDimitry Andric   default:
392*bdd1243dSDimitry Andric     llvm_unreachable("Unknown CL memory scope");
393*bdd1243dSDimitry Andric   }
394*bdd1243dSDimitry Andric }
395*bdd1243dSDimitry Andric 
396*bdd1243dSDimitry Andric static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) {
397*bdd1243dSDimitry Andric   switch (ClScope) {
398*bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_work_item:
399*bdd1243dSDimitry Andric     return SPIRV::Scope::Invocation;
400*bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_work_group:
401*bdd1243dSDimitry Andric     return SPIRV::Scope::Workgroup;
402*bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_device:
403*bdd1243dSDimitry Andric     return SPIRV::Scope::Device;
404*bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_all_svm_devices:
405*bdd1243dSDimitry Andric     return SPIRV::Scope::CrossDevice;
406*bdd1243dSDimitry Andric   case SPIRV::CLMemoryScope::memory_scope_sub_group:
407*bdd1243dSDimitry Andric     return SPIRV::Scope::Subgroup;
408*bdd1243dSDimitry Andric   }
409*bdd1243dSDimitry Andric   llvm_unreachable("Unknown CL memory scope");
410*bdd1243dSDimitry Andric }
411*bdd1243dSDimitry Andric 
412*bdd1243dSDimitry Andric static Register buildConstantIntReg(uint64_t Val, MachineIRBuilder &MIRBuilder,
413*bdd1243dSDimitry Andric                                     SPIRVGlobalRegistry *GR,
414*bdd1243dSDimitry Andric                                     unsigned BitWidth = 32) {
415*bdd1243dSDimitry Andric   SPIRVType *IntType = GR->getOrCreateSPIRVIntegerType(BitWidth, MIRBuilder);
416*bdd1243dSDimitry Andric   return GR->buildConstantInt(Val, MIRBuilder, IntType);
417*bdd1243dSDimitry Andric }
418*bdd1243dSDimitry Andric 
419*bdd1243dSDimitry Andric static Register buildScopeReg(Register CLScopeRegister,
420*bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
421*bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR,
422*bdd1243dSDimitry Andric                               const MachineRegisterInfo *MRI) {
423*bdd1243dSDimitry Andric   auto CLScope =
424*bdd1243dSDimitry Andric       static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI));
425*bdd1243dSDimitry Andric   SPIRV::Scope::Scope Scope = getSPIRVScope(CLScope);
426*bdd1243dSDimitry Andric 
427*bdd1243dSDimitry Andric   if (CLScope == static_cast<unsigned>(Scope))
428*bdd1243dSDimitry Andric     return CLScopeRegister;
429*bdd1243dSDimitry Andric 
430*bdd1243dSDimitry Andric   return buildConstantIntReg(Scope, MIRBuilder, GR);
431*bdd1243dSDimitry Andric }
432*bdd1243dSDimitry Andric 
433*bdd1243dSDimitry Andric static Register buildMemSemanticsReg(Register SemanticsRegister,
434*bdd1243dSDimitry Andric                                      Register PtrRegister,
435*bdd1243dSDimitry Andric                                      const MachineRegisterInfo *MRI,
436*bdd1243dSDimitry Andric                                      SPIRVGlobalRegistry *GR) {
437*bdd1243dSDimitry Andric   std::memory_order Order =
438*bdd1243dSDimitry Andric       static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI));
439*bdd1243dSDimitry Andric   unsigned Semantics =
440*bdd1243dSDimitry Andric       getSPIRVMemSemantics(Order) |
441*bdd1243dSDimitry Andric       getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
442*bdd1243dSDimitry Andric 
443*bdd1243dSDimitry Andric   if (Order == Semantics)
444*bdd1243dSDimitry Andric     return SemanticsRegister;
445*bdd1243dSDimitry Andric 
446*bdd1243dSDimitry Andric   return Register();
447*bdd1243dSDimitry Andric }
448*bdd1243dSDimitry Andric 
449*bdd1243dSDimitry Andric /// Helper function for translating atomic init to OpStore.
450*bdd1243dSDimitry Andric static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call,
451*bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder) {
452*bdd1243dSDimitry Andric   assert(Call->Arguments.size() == 2 &&
453*bdd1243dSDimitry Andric          "Need 2 arguments for atomic init translation");
454*bdd1243dSDimitry Andric 
455*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpStore)
456*bdd1243dSDimitry Andric       .addUse(Call->Arguments[0])
457*bdd1243dSDimitry Andric       .addUse(Call->Arguments[1]);
458*bdd1243dSDimitry Andric   return true;
459*bdd1243dSDimitry Andric }
460*bdd1243dSDimitry Andric 
461*bdd1243dSDimitry Andric /// Helper function for building an atomic load instruction.
462*bdd1243dSDimitry Andric static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call,
463*bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
464*bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
465*bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
466*bdd1243dSDimitry Andric   // TODO: if true insert call to __translate_ocl_memory_sccope before
467*bdd1243dSDimitry Andric   // OpAtomicLoad and the function implementation. We can use Translator's
468*bdd1243dSDimitry Andric   // output for transcoding/atomic_explicit_arguments.cl as an example.
469*bdd1243dSDimitry Andric   Register ScopeRegister;
470*bdd1243dSDimitry Andric   if (Call->Arguments.size() > 1)
471*bdd1243dSDimitry Andric     ScopeRegister = Call->Arguments[1];
472*bdd1243dSDimitry Andric   else
473*bdd1243dSDimitry Andric     ScopeRegister = buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);
474*bdd1243dSDimitry Andric 
475*bdd1243dSDimitry Andric   Register MemSemanticsReg;
476*bdd1243dSDimitry Andric   if (Call->Arguments.size() > 2) {
477*bdd1243dSDimitry Andric     // TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad.
478*bdd1243dSDimitry Andric     MemSemanticsReg = Call->Arguments[2];
479*bdd1243dSDimitry Andric   } else {
480*bdd1243dSDimitry Andric     int Semantics =
481*bdd1243dSDimitry Andric         SPIRV::MemorySemantics::SequentiallyConsistent |
482*bdd1243dSDimitry Andric         getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
483*bdd1243dSDimitry Andric     MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
484*bdd1243dSDimitry Andric   }
485*bdd1243dSDimitry Andric 
486*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
487*bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
488*bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
489*bdd1243dSDimitry Andric       .addUse(PtrRegister)
490*bdd1243dSDimitry Andric       .addUse(ScopeRegister)
491*bdd1243dSDimitry Andric       .addUse(MemSemanticsReg);
492*bdd1243dSDimitry Andric   return true;
493*bdd1243dSDimitry Andric }
494*bdd1243dSDimitry Andric 
495*bdd1243dSDimitry Andric /// Helper function for building an atomic store instruction.
496*bdd1243dSDimitry Andric static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call,
497*bdd1243dSDimitry Andric                                  MachineIRBuilder &MIRBuilder,
498*bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
499*bdd1243dSDimitry Andric   Register ScopeRegister =
500*bdd1243dSDimitry Andric       buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);
501*bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
502*bdd1243dSDimitry Andric   int Semantics =
503*bdd1243dSDimitry Andric       SPIRV::MemorySemantics::SequentiallyConsistent |
504*bdd1243dSDimitry Andric       getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
505*bdd1243dSDimitry Andric   Register MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
506*bdd1243dSDimitry Andric 
507*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
508*bdd1243dSDimitry Andric       .addUse(PtrRegister)
509*bdd1243dSDimitry Andric       .addUse(ScopeRegister)
510*bdd1243dSDimitry Andric       .addUse(MemSemanticsReg)
511*bdd1243dSDimitry Andric       .addUse(Call->Arguments[1]);
512*bdd1243dSDimitry Andric   return true;
513*bdd1243dSDimitry Andric }
514*bdd1243dSDimitry Andric 
515*bdd1243dSDimitry Andric /// Helper function for building an atomic compare-exchange instruction.
516*bdd1243dSDimitry Andric static bool buildAtomicCompareExchangeInst(const SPIRV::IncomingCall *Call,
517*bdd1243dSDimitry Andric                                            MachineIRBuilder &MIRBuilder,
518*bdd1243dSDimitry Andric                                            SPIRVGlobalRegistry *GR) {
519*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
520*bdd1243dSDimitry Andric   unsigned Opcode =
521*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
522*bdd1243dSDimitry Andric   bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg");
523*bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
524*bdd1243dSDimitry Andric 
525*bdd1243dSDimitry Andric   Register ObjectPtr = Call->Arguments[0];   // Pointer (volatile A *object.)
526*bdd1243dSDimitry Andric   Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).
527*bdd1243dSDimitry Andric   Register Desired = Call->Arguments[2];     // Value (C Desired).
528*bdd1243dSDimitry Andric   SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
529*bdd1243dSDimitry Andric   LLT DesiredLLT = MRI->getType(Desired);
530*bdd1243dSDimitry Andric 
531*bdd1243dSDimitry Andric   assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() ==
532*bdd1243dSDimitry Andric          SPIRV::OpTypePointer);
533*bdd1243dSDimitry Andric   unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode();
534*bdd1243dSDimitry Andric   assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt
535*bdd1243dSDimitry Andric                    : ExpectedType == SPIRV::OpTypePointer);
536*bdd1243dSDimitry Andric   assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
537*bdd1243dSDimitry Andric 
538*bdd1243dSDimitry Andric   SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
539*bdd1243dSDimitry Andric   assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected");
540*bdd1243dSDimitry Andric   auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
541*bdd1243dSDimitry Andric       SpvObjectPtrTy->getOperand(1).getImm());
542*bdd1243dSDimitry Andric   auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
543*bdd1243dSDimitry Andric 
544*bdd1243dSDimitry Andric   Register MemSemEqualReg;
545*bdd1243dSDimitry Andric   Register MemSemUnequalReg;
546*bdd1243dSDimitry Andric   uint64_t MemSemEqual =
547*bdd1243dSDimitry Andric       IsCmpxchg
548*bdd1243dSDimitry Andric           ? SPIRV::MemorySemantics::None
549*bdd1243dSDimitry Andric           : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
550*bdd1243dSDimitry Andric   uint64_t MemSemUnequal =
551*bdd1243dSDimitry Andric       IsCmpxchg
552*bdd1243dSDimitry Andric           ? SPIRV::MemorySemantics::None
553*bdd1243dSDimitry Andric           : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
554*bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 4) {
555*bdd1243dSDimitry Andric     assert(Call->Arguments.size() >= 5 &&
556*bdd1243dSDimitry Andric            "Need 5+ args for explicit atomic cmpxchg");
557*bdd1243dSDimitry Andric     auto MemOrdEq =
558*bdd1243dSDimitry Andric         static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));
559*bdd1243dSDimitry Andric     auto MemOrdNeq =
560*bdd1243dSDimitry Andric         static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));
561*bdd1243dSDimitry Andric     MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;
562*bdd1243dSDimitry Andric     MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;
563*bdd1243dSDimitry Andric     if (MemOrdEq == MemSemEqual)
564*bdd1243dSDimitry Andric       MemSemEqualReg = Call->Arguments[3];
565*bdd1243dSDimitry Andric     if (MemOrdNeq == MemSemEqual)
566*bdd1243dSDimitry Andric       MemSemUnequalReg = Call->Arguments[4];
567*bdd1243dSDimitry Andric   }
568*bdd1243dSDimitry Andric   if (!MemSemEqualReg.isValid())
569*bdd1243dSDimitry Andric     MemSemEqualReg = buildConstantIntReg(MemSemEqual, MIRBuilder, GR);
570*bdd1243dSDimitry Andric   if (!MemSemUnequalReg.isValid())
571*bdd1243dSDimitry Andric     MemSemUnequalReg = buildConstantIntReg(MemSemUnequal, MIRBuilder, GR);
572*bdd1243dSDimitry Andric 
573*bdd1243dSDimitry Andric   Register ScopeReg;
574*bdd1243dSDimitry Andric   auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;
575*bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 6) {
576*bdd1243dSDimitry Andric     assert(Call->Arguments.size() == 6 &&
577*bdd1243dSDimitry Andric            "Extra args for explicit atomic cmpxchg");
578*bdd1243dSDimitry Andric     auto ClScope = static_cast<SPIRV::CLMemoryScope>(
579*bdd1243dSDimitry Andric         getIConstVal(Call->Arguments[5], MRI));
580*bdd1243dSDimitry Andric     Scope = getSPIRVScope(ClScope);
581*bdd1243dSDimitry Andric     if (ClScope == static_cast<unsigned>(Scope))
582*bdd1243dSDimitry Andric       ScopeReg = Call->Arguments[5];
583*bdd1243dSDimitry Andric   }
584*bdd1243dSDimitry Andric   if (!ScopeReg.isValid())
585*bdd1243dSDimitry Andric     ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);
586*bdd1243dSDimitry Andric 
587*bdd1243dSDimitry Andric   Register Expected = IsCmpxchg
588*bdd1243dSDimitry Andric                           ? ExpectedArg
589*bdd1243dSDimitry Andric                           : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder,
590*bdd1243dSDimitry Andric                                           GR, LLT::scalar(32));
591*bdd1243dSDimitry Andric   MRI->setType(Expected, DesiredLLT);
592*bdd1243dSDimitry Andric   Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)
593*bdd1243dSDimitry Andric                             : Call->ReturnRegister;
594*bdd1243dSDimitry Andric   GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());
595*bdd1243dSDimitry Andric 
596*bdd1243dSDimitry Andric   SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
597*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
598*bdd1243dSDimitry Andric       .addDef(Tmp)
599*bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(IntTy))
600*bdd1243dSDimitry Andric       .addUse(ObjectPtr)
601*bdd1243dSDimitry Andric       .addUse(ScopeReg)
602*bdd1243dSDimitry Andric       .addUse(MemSemEqualReg)
603*bdd1243dSDimitry Andric       .addUse(MemSemUnequalReg)
604*bdd1243dSDimitry Andric       .addUse(Desired)
605*bdd1243dSDimitry Andric       .addUse(Expected);
606*bdd1243dSDimitry Andric   if (!IsCmpxchg) {
607*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);
608*bdd1243dSDimitry Andric     MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);
609*bdd1243dSDimitry Andric   }
610*bdd1243dSDimitry Andric   return true;
611*bdd1243dSDimitry Andric }
612*bdd1243dSDimitry Andric 
613*bdd1243dSDimitry Andric /// Helper function for building an atomic load instruction.
614*bdd1243dSDimitry Andric static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
615*bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
616*bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
617*bdd1243dSDimitry Andric   const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
618*bdd1243dSDimitry Andric   SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
619*bdd1243dSDimitry Andric   Register ScopeRegister;
620*bdd1243dSDimitry Andric 
621*bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 4) {
622*bdd1243dSDimitry Andric     assert(Call->Arguments.size() == 4 &&
623*bdd1243dSDimitry Andric            "Too many args for explicit atomic RMW");
624*bdd1243dSDimitry Andric     ScopeRegister = buildScopeReg(Call->Arguments[3], MIRBuilder, GR, MRI);
625*bdd1243dSDimitry Andric   }
626*bdd1243dSDimitry Andric 
627*bdd1243dSDimitry Andric   if (!ScopeRegister.isValid())
628*bdd1243dSDimitry Andric     ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR);
629*bdd1243dSDimitry Andric 
630*bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
631*bdd1243dSDimitry Andric   unsigned Semantics = SPIRV::MemorySemantics::None;
632*bdd1243dSDimitry Andric   Register MemSemanticsReg;
633*bdd1243dSDimitry Andric 
634*bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 3)
635*bdd1243dSDimitry Andric     MemSemanticsReg =
636*bdd1243dSDimitry Andric         buildMemSemanticsReg(Call->Arguments[2], PtrRegister, MRI, GR);
637*bdd1243dSDimitry Andric 
638*bdd1243dSDimitry Andric   if (!MemSemanticsReg.isValid())
639*bdd1243dSDimitry Andric     MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
640*bdd1243dSDimitry Andric 
641*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
642*bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
643*bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
644*bdd1243dSDimitry Andric       .addUse(PtrRegister)
645*bdd1243dSDimitry Andric       .addUse(ScopeRegister)
646*bdd1243dSDimitry Andric       .addUse(MemSemanticsReg)
647*bdd1243dSDimitry Andric       .addUse(Call->Arguments[1]);
648*bdd1243dSDimitry Andric   return true;
649*bdd1243dSDimitry Andric }
650*bdd1243dSDimitry Andric 
651*bdd1243dSDimitry Andric /// Helper function for building atomic flag instructions (e.g.
652*bdd1243dSDimitry Andric /// OpAtomicFlagTestAndSet).
653*bdd1243dSDimitry Andric static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,
654*bdd1243dSDimitry Andric                                 unsigned Opcode, MachineIRBuilder &MIRBuilder,
655*bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
656*bdd1243dSDimitry Andric   const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
657*bdd1243dSDimitry Andric 
658*bdd1243dSDimitry Andric   Register PtrRegister = Call->Arguments[0];
659*bdd1243dSDimitry Andric   unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent;
660*bdd1243dSDimitry Andric   Register MemSemanticsReg;
661*bdd1243dSDimitry Andric 
662*bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 2)
663*bdd1243dSDimitry Andric     MemSemanticsReg =
664*bdd1243dSDimitry Andric         buildMemSemanticsReg(Call->Arguments[1], PtrRegister, MRI, GR);
665*bdd1243dSDimitry Andric 
666*bdd1243dSDimitry Andric   if (!MemSemanticsReg.isValid())
667*bdd1243dSDimitry Andric     MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
668*bdd1243dSDimitry Andric 
669*bdd1243dSDimitry Andric   assert((Opcode != SPIRV::OpAtomicFlagClear ||
670*bdd1243dSDimitry Andric           (Semantics != SPIRV::MemorySemantics::Acquire &&
671*bdd1243dSDimitry Andric            Semantics != SPIRV::MemorySemantics::AcquireRelease)) &&
672*bdd1243dSDimitry Andric          "Invalid memory order argument!");
673*bdd1243dSDimitry Andric 
674*bdd1243dSDimitry Andric   SPIRV::Scope::Scope Scope = SPIRV::Scope::Device;
675*bdd1243dSDimitry Andric   Register ScopeRegister;
676*bdd1243dSDimitry Andric 
677*bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 3)
678*bdd1243dSDimitry Andric     ScopeRegister = buildScopeReg(Call->Arguments[2], MIRBuilder, GR, MRI);
679*bdd1243dSDimitry Andric 
680*bdd1243dSDimitry Andric   if (!ScopeRegister.isValid())
681*bdd1243dSDimitry Andric     ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR);
682*bdd1243dSDimitry Andric 
683*bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode);
684*bdd1243dSDimitry Andric   if (Opcode == SPIRV::OpAtomicFlagTestAndSet)
685*bdd1243dSDimitry Andric     MIB.addDef(Call->ReturnRegister)
686*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType));
687*bdd1243dSDimitry Andric 
688*bdd1243dSDimitry Andric   MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);
689*bdd1243dSDimitry Andric   return true;
690*bdd1243dSDimitry Andric }
691*bdd1243dSDimitry Andric 
692*bdd1243dSDimitry Andric /// Helper function for building barriers, i.e., memory/control ordering
693*bdd1243dSDimitry Andric /// operations.
694*bdd1243dSDimitry Andric static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
695*bdd1243dSDimitry Andric                              MachineIRBuilder &MIRBuilder,
696*bdd1243dSDimitry Andric                              SPIRVGlobalRegistry *GR) {
697*bdd1243dSDimitry Andric   const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
698*bdd1243dSDimitry Andric   unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);
699*bdd1243dSDimitry Andric   unsigned MemSemantics = SPIRV::MemorySemantics::None;
700*bdd1243dSDimitry Andric 
701*bdd1243dSDimitry Andric   if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
702*bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
703*bdd1243dSDimitry Andric 
704*bdd1243dSDimitry Andric   if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)
705*bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;
706*bdd1243dSDimitry Andric 
707*bdd1243dSDimitry Andric   if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)
708*bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::ImageMemory;
709*bdd1243dSDimitry Andric 
710*bdd1243dSDimitry Andric   if (Opcode == SPIRV::OpMemoryBarrier) {
711*bdd1243dSDimitry Andric     std::memory_order MemOrder =
712*bdd1243dSDimitry Andric         static_cast<std::memory_order>(getIConstVal(Call->Arguments[1], MRI));
713*bdd1243dSDimitry Andric     MemSemantics = getSPIRVMemSemantics(MemOrder) | MemSemantics;
714*bdd1243dSDimitry Andric   } else {
715*bdd1243dSDimitry Andric     MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;
716*bdd1243dSDimitry Andric   }
717*bdd1243dSDimitry Andric 
718*bdd1243dSDimitry Andric   Register MemSemanticsReg;
719*bdd1243dSDimitry Andric   if (MemFlags == MemSemantics)
720*bdd1243dSDimitry Andric     MemSemanticsReg = Call->Arguments[0];
721*bdd1243dSDimitry Andric   else
722*bdd1243dSDimitry Andric     MemSemanticsReg = buildConstantIntReg(MemSemantics, MIRBuilder, GR);
723*bdd1243dSDimitry Andric 
724*bdd1243dSDimitry Andric   Register ScopeReg;
725*bdd1243dSDimitry Andric   SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
726*bdd1243dSDimitry Andric   SPIRV::Scope::Scope MemScope = Scope;
727*bdd1243dSDimitry Andric   if (Call->Arguments.size() >= 2) {
728*bdd1243dSDimitry Andric     assert(
729*bdd1243dSDimitry Andric         ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||
730*bdd1243dSDimitry Andric          (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&
731*bdd1243dSDimitry Andric         "Extra args for explicitly scoped barrier");
732*bdd1243dSDimitry Andric     Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]
733*bdd1243dSDimitry Andric                                                            : Call->Arguments[1];
734*bdd1243dSDimitry Andric     SPIRV::CLMemoryScope CLScope =
735*bdd1243dSDimitry Andric         static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
736*bdd1243dSDimitry Andric     MemScope = getSPIRVScope(CLScope);
737*bdd1243dSDimitry Andric     if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
738*bdd1243dSDimitry Andric         (Opcode == SPIRV::OpMemoryBarrier))
739*bdd1243dSDimitry Andric       Scope = MemScope;
740*bdd1243dSDimitry Andric 
741*bdd1243dSDimitry Andric     if (CLScope == static_cast<unsigned>(Scope))
742*bdd1243dSDimitry Andric       ScopeReg = Call->Arguments[1];
743*bdd1243dSDimitry Andric   }
744*bdd1243dSDimitry Andric 
745*bdd1243dSDimitry Andric   if (!ScopeReg.isValid())
746*bdd1243dSDimitry Andric     ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);
747*bdd1243dSDimitry Andric 
748*bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);
749*bdd1243dSDimitry Andric   if (Opcode != SPIRV::OpMemoryBarrier)
750*bdd1243dSDimitry Andric     MIB.addUse(buildConstantIntReg(MemScope, MIRBuilder, GR));
751*bdd1243dSDimitry Andric   MIB.addUse(MemSemanticsReg);
752*bdd1243dSDimitry Andric   return true;
753*bdd1243dSDimitry Andric }
754*bdd1243dSDimitry Andric 
755*bdd1243dSDimitry Andric static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
756*bdd1243dSDimitry Andric   switch (dim) {
757*bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_1D:
758*bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Buffer:
759*bdd1243dSDimitry Andric     return 1;
760*bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_2D:
761*bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Cube:
762*bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Rect:
763*bdd1243dSDimitry Andric     return 2;
764*bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_3D:
765*bdd1243dSDimitry Andric     return 3;
766*bdd1243dSDimitry Andric   default:
767*bdd1243dSDimitry Andric     llvm_unreachable("Cannot get num components for given Dim");
768*bdd1243dSDimitry Andric   }
769*bdd1243dSDimitry Andric }
770*bdd1243dSDimitry Andric 
771*bdd1243dSDimitry Andric /// Helper function for obtaining the number of size components.
772*bdd1243dSDimitry Andric static unsigned getNumSizeComponents(SPIRVType *imgType) {
773*bdd1243dSDimitry Andric   assert(imgType->getOpcode() == SPIRV::OpTypeImage);
774*bdd1243dSDimitry Andric   auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());
775*bdd1243dSDimitry Andric   unsigned numComps = getNumComponentsForDim(dim);
776*bdd1243dSDimitry Andric   bool arrayed = imgType->getOperand(4).getImm() == 1;
777*bdd1243dSDimitry Andric   return arrayed ? numComps + 1 : numComps;
778*bdd1243dSDimitry Andric }
779*bdd1243dSDimitry Andric 
780*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
781*bdd1243dSDimitry Andric // Implementation functions for each builtin group
782*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
783*bdd1243dSDimitry Andric 
784*bdd1243dSDimitry Andric static bool generateExtInst(const SPIRV::IncomingCall *Call,
785*bdd1243dSDimitry Andric                             MachineIRBuilder &MIRBuilder,
786*bdd1243dSDimitry Andric                             SPIRVGlobalRegistry *GR) {
787*bdd1243dSDimitry Andric   // Lookup the extended instruction number in the TableGen records.
788*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
789*bdd1243dSDimitry Andric   uint32_t Number =
790*bdd1243dSDimitry Andric       SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number;
791*bdd1243dSDimitry Andric 
792*bdd1243dSDimitry Andric   // Build extended instruction.
793*bdd1243dSDimitry Andric   auto MIB =
794*bdd1243dSDimitry Andric       MIRBuilder.buildInstr(SPIRV::OpExtInst)
795*bdd1243dSDimitry Andric           .addDef(Call->ReturnRegister)
796*bdd1243dSDimitry Andric           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
797*bdd1243dSDimitry Andric           .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
798*bdd1243dSDimitry Andric           .addImm(Number);
799*bdd1243dSDimitry Andric 
800*bdd1243dSDimitry Andric   for (auto Argument : Call->Arguments)
801*bdd1243dSDimitry Andric     MIB.addUse(Argument);
802*bdd1243dSDimitry Andric   return true;
803*bdd1243dSDimitry Andric }
804*bdd1243dSDimitry Andric 
805*bdd1243dSDimitry Andric static bool generateRelationalInst(const SPIRV::IncomingCall *Call,
806*bdd1243dSDimitry Andric                                    MachineIRBuilder &MIRBuilder,
807*bdd1243dSDimitry Andric                                    SPIRVGlobalRegistry *GR) {
808*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
809*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
810*bdd1243dSDimitry Andric   unsigned Opcode =
811*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
812*bdd1243dSDimitry Andric 
813*bdd1243dSDimitry Andric   Register CompareRegister;
814*bdd1243dSDimitry Andric   SPIRVType *RelationType;
815*bdd1243dSDimitry Andric   std::tie(CompareRegister, RelationType) =
816*bdd1243dSDimitry Andric       buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
817*bdd1243dSDimitry Andric 
818*bdd1243dSDimitry Andric   // Build relational instruction.
819*bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode)
820*bdd1243dSDimitry Andric                  .addDef(CompareRegister)
821*bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(RelationType));
822*bdd1243dSDimitry Andric 
823*bdd1243dSDimitry Andric   for (auto Argument : Call->Arguments)
824*bdd1243dSDimitry Andric     MIB.addUse(Argument);
825*bdd1243dSDimitry Andric 
826*bdd1243dSDimitry Andric   // Build select instruction.
827*bdd1243dSDimitry Andric   return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,
828*bdd1243dSDimitry Andric                          Call->ReturnType, GR);
829*bdd1243dSDimitry Andric }
830*bdd1243dSDimitry Andric 
831*bdd1243dSDimitry Andric static bool generateGroupInst(const SPIRV::IncomingCall *Call,
832*bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
833*bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR) {
834*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
835*bdd1243dSDimitry Andric   const SPIRV::GroupBuiltin *GroupBuiltin =
836*bdd1243dSDimitry Andric       SPIRV::lookupGroupBuiltin(Builtin->Name);
837*bdd1243dSDimitry Andric   const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
838*bdd1243dSDimitry Andric   Register Arg0;
839*bdd1243dSDimitry Andric   if (GroupBuiltin->HasBoolArg) {
840*bdd1243dSDimitry Andric     Register ConstRegister = Call->Arguments[0];
841*bdd1243dSDimitry Andric     auto ArgInstruction = getDefInstrMaybeConstant(ConstRegister, MRI);
842*bdd1243dSDimitry Andric     // TODO: support non-constant bool values.
843*bdd1243dSDimitry Andric     assert(ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT &&
844*bdd1243dSDimitry Andric            "Only constant bool value args are supported");
845*bdd1243dSDimitry Andric     if (GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode() !=
846*bdd1243dSDimitry Andric         SPIRV::OpTypeBool)
847*bdd1243dSDimitry Andric       Arg0 = GR->buildConstantInt(getIConstVal(ConstRegister, MRI), MIRBuilder,
848*bdd1243dSDimitry Andric                                   GR->getOrCreateSPIRVBoolType(MIRBuilder));
849*bdd1243dSDimitry Andric   }
850*bdd1243dSDimitry Andric 
851*bdd1243dSDimitry Andric   Register GroupResultRegister = Call->ReturnRegister;
852*bdd1243dSDimitry Andric   SPIRVType *GroupResultType = Call->ReturnType;
853*bdd1243dSDimitry Andric 
854*bdd1243dSDimitry Andric   // TODO: maybe we need to check whether the result type is already boolean
855*bdd1243dSDimitry Andric   // and in this case do not insert select instruction.
856*bdd1243dSDimitry Andric   const bool HasBoolReturnTy =
857*bdd1243dSDimitry Andric       GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||
858*bdd1243dSDimitry Andric       GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||
859*bdd1243dSDimitry Andric       GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;
860*bdd1243dSDimitry Andric 
861*bdd1243dSDimitry Andric   if (HasBoolReturnTy)
862*bdd1243dSDimitry Andric     std::tie(GroupResultRegister, GroupResultType) =
863*bdd1243dSDimitry Andric         buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
864*bdd1243dSDimitry Andric 
865*bdd1243dSDimitry Andric   auto Scope = Builtin->Name.startswith("sub_group") ? SPIRV::Scope::Subgroup
866*bdd1243dSDimitry Andric                                                      : SPIRV::Scope::Workgroup;
867*bdd1243dSDimitry Andric   Register ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR);
868*bdd1243dSDimitry Andric 
869*bdd1243dSDimitry Andric   // Build work/sub group instruction.
870*bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
871*bdd1243dSDimitry Andric                  .addDef(GroupResultRegister)
872*bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(GroupResultType))
873*bdd1243dSDimitry Andric                  .addUse(ScopeRegister);
874*bdd1243dSDimitry Andric 
875*bdd1243dSDimitry Andric   if (!GroupBuiltin->NoGroupOperation)
876*bdd1243dSDimitry Andric     MIB.addImm(GroupBuiltin->GroupOperation);
877*bdd1243dSDimitry Andric   if (Call->Arguments.size() > 0) {
878*bdd1243dSDimitry Andric     MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);
879*bdd1243dSDimitry Andric     for (unsigned i = 1; i < Call->Arguments.size(); i++)
880*bdd1243dSDimitry Andric       MIB.addUse(Call->Arguments[i]);
881*bdd1243dSDimitry Andric   }
882*bdd1243dSDimitry Andric 
883*bdd1243dSDimitry Andric   // Build select instruction.
884*bdd1243dSDimitry Andric   if (HasBoolReturnTy)
885*bdd1243dSDimitry Andric     buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,
886*bdd1243dSDimitry Andric                     Call->ReturnType, GR);
887*bdd1243dSDimitry Andric   return true;
888*bdd1243dSDimitry Andric }
889*bdd1243dSDimitry Andric 
890*bdd1243dSDimitry Andric // These queries ask for a single size_t result for a given dimension index, e.g
891*bdd1243dSDimitry Andric // size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to
892*bdd1243dSDimitry Andric // these values are all vec3 types, so we need to extract the correct index or
893*bdd1243dSDimitry Andric // return defaultVal (0 or 1 depending on the query). We also handle extending
894*bdd1243dSDimitry Andric // or tuncating in case size_t does not match the expected result type's
895*bdd1243dSDimitry Andric // bitwidth.
896*bdd1243dSDimitry Andric //
897*bdd1243dSDimitry Andric // For a constant index >= 3 we generate:
898*bdd1243dSDimitry Andric //  %res = OpConstant %SizeT 0
899*bdd1243dSDimitry Andric //
900*bdd1243dSDimitry Andric // For other indices we generate:
901*bdd1243dSDimitry Andric //  %g = OpVariable %ptr_V3_SizeT Input
902*bdd1243dSDimitry Andric //  OpDecorate %g BuiltIn XXX
903*bdd1243dSDimitry Andric //  OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"
904*bdd1243dSDimitry Andric //  OpDecorate %g Constant
905*bdd1243dSDimitry Andric //  %loadedVec = OpLoad %V3_SizeT %g
906*bdd1243dSDimitry Andric //
907*bdd1243dSDimitry Andric //  Then, if the index is constant < 3, we generate:
908*bdd1243dSDimitry Andric //    %res = OpCompositeExtract %SizeT %loadedVec idx
909*bdd1243dSDimitry Andric //  If the index is dynamic, we generate:
910*bdd1243dSDimitry Andric //    %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx
911*bdd1243dSDimitry Andric //    %cmp = OpULessThan %bool %idx %const_3
912*bdd1243dSDimitry Andric //    %res = OpSelect %SizeT %cmp %tmp %const_0
913*bdd1243dSDimitry Andric //
914*bdd1243dSDimitry Andric //  If the bitwidth of %res does not match the expected return type, we add an
915*bdd1243dSDimitry Andric //  extend or truncate.
916*bdd1243dSDimitry Andric static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call,
917*bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
918*bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR,
919*bdd1243dSDimitry Andric                               SPIRV::BuiltIn::BuiltIn BuiltinValue,
920*bdd1243dSDimitry Andric                               uint64_t DefaultValue) {
921*bdd1243dSDimitry Andric   Register IndexRegister = Call->Arguments[0];
922*bdd1243dSDimitry Andric   const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
923*bdd1243dSDimitry Andric   const unsigned PointerSize = GR->getPointerSize();
924*bdd1243dSDimitry Andric   const SPIRVType *PointerSizeType =
925*bdd1243dSDimitry Andric       GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);
926*bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
927*bdd1243dSDimitry Andric   auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);
928*bdd1243dSDimitry Andric 
929*bdd1243dSDimitry Andric   // Set up the final register to do truncation or extension on at the end.
930*bdd1243dSDimitry Andric   Register ToTruncate = Call->ReturnRegister;
931*bdd1243dSDimitry Andric 
932*bdd1243dSDimitry Andric   // If the index is constant, we can statically determine if it is in range.
933*bdd1243dSDimitry Andric   bool IsConstantIndex =
934*bdd1243dSDimitry Andric       IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;
935*bdd1243dSDimitry Andric 
936*bdd1243dSDimitry Andric   // If it's out of range (max dimension is 3), we can just return the constant
937*bdd1243dSDimitry Andric   // default value (0 or 1 depending on which query function).
938*bdd1243dSDimitry Andric   if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {
939*bdd1243dSDimitry Andric     Register defaultReg = Call->ReturnRegister;
940*bdd1243dSDimitry Andric     if (PointerSize != ResultWidth) {
941*bdd1243dSDimitry Andric       defaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
942*bdd1243dSDimitry Andric       GR->assignSPIRVTypeToVReg(PointerSizeType, defaultReg,
943*bdd1243dSDimitry Andric                                 MIRBuilder.getMF());
944*bdd1243dSDimitry Andric       ToTruncate = defaultReg;
945*bdd1243dSDimitry Andric     }
946*bdd1243dSDimitry Andric     auto NewRegister =
947*bdd1243dSDimitry Andric         GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
948*bdd1243dSDimitry Andric     MIRBuilder.buildCopy(defaultReg, NewRegister);
949*bdd1243dSDimitry Andric   } else { // If it could be in range, we need to load from the given builtin.
950*bdd1243dSDimitry Andric     auto Vec3Ty =
951*bdd1243dSDimitry Andric         GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder);
952*bdd1243dSDimitry Andric     Register LoadedVector =
953*bdd1243dSDimitry Andric         buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,
954*bdd1243dSDimitry Andric                                  LLT::fixed_vector(3, PointerSize));
955*bdd1243dSDimitry Andric     // Set up the vreg to extract the result to (possibly a new temporary one).
956*bdd1243dSDimitry Andric     Register Extracted = Call->ReturnRegister;
957*bdd1243dSDimitry Andric     if (!IsConstantIndex || PointerSize != ResultWidth) {
958*bdd1243dSDimitry Andric       Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
959*bdd1243dSDimitry Andric       GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());
960*bdd1243dSDimitry Andric     }
961*bdd1243dSDimitry Andric     // Use Intrinsic::spv_extractelt so dynamic vs static extraction is
962*bdd1243dSDimitry Andric     // handled later: extr = spv_extractelt LoadedVector, IndexRegister.
963*bdd1243dSDimitry Andric     MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(
964*bdd1243dSDimitry Andric         Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true);
965*bdd1243dSDimitry Andric     ExtractInst.addUse(LoadedVector).addUse(IndexRegister);
966*bdd1243dSDimitry Andric 
967*bdd1243dSDimitry Andric     // If the index is dynamic, need check if it's < 3, and then use a select.
968*bdd1243dSDimitry Andric     if (!IsConstantIndex) {
969*bdd1243dSDimitry Andric       insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder,
970*bdd1243dSDimitry Andric                         *MRI);
971*bdd1243dSDimitry Andric 
972*bdd1243dSDimitry Andric       auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);
973*bdd1243dSDimitry Andric       auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
974*bdd1243dSDimitry Andric 
975*bdd1243dSDimitry Andric       Register CompareRegister =
976*bdd1243dSDimitry Andric           MRI->createGenericVirtualRegister(LLT::scalar(1));
977*bdd1243dSDimitry Andric       GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());
978*bdd1243dSDimitry Andric 
979*bdd1243dSDimitry Andric       // Use G_ICMP to check if idxVReg < 3.
980*bdd1243dSDimitry Andric       MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister,
981*bdd1243dSDimitry Andric                            GR->buildConstantInt(3, MIRBuilder, IndexType));
982*bdd1243dSDimitry Andric 
983*bdd1243dSDimitry Andric       // Get constant for the default value (0 or 1 depending on which
984*bdd1243dSDimitry Andric       // function).
985*bdd1243dSDimitry Andric       Register DefaultRegister =
986*bdd1243dSDimitry Andric           GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
987*bdd1243dSDimitry Andric 
988*bdd1243dSDimitry Andric       // Get a register for the selection result (possibly a new temporary one).
989*bdd1243dSDimitry Andric       Register SelectionResult = Call->ReturnRegister;
990*bdd1243dSDimitry Andric       if (PointerSize != ResultWidth) {
991*bdd1243dSDimitry Andric         SelectionResult =
992*bdd1243dSDimitry Andric             MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
993*bdd1243dSDimitry Andric         GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,
994*bdd1243dSDimitry Andric                                   MIRBuilder.getMF());
995*bdd1243dSDimitry Andric       }
996*bdd1243dSDimitry Andric       // Create the final G_SELECT to return the extracted value or the default.
997*bdd1243dSDimitry Andric       MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,
998*bdd1243dSDimitry Andric                              DefaultRegister);
999*bdd1243dSDimitry Andric       ToTruncate = SelectionResult;
1000*bdd1243dSDimitry Andric     } else {
1001*bdd1243dSDimitry Andric       ToTruncate = Extracted;
1002*bdd1243dSDimitry Andric     }
1003*bdd1243dSDimitry Andric   }
1004*bdd1243dSDimitry Andric   // Alter the result's bitwidth if it does not match the SizeT value extracted.
1005*bdd1243dSDimitry Andric   if (PointerSize != ResultWidth)
1006*bdd1243dSDimitry Andric     MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);
1007*bdd1243dSDimitry Andric   return true;
1008*bdd1243dSDimitry Andric }
1009*bdd1243dSDimitry Andric 
1010*bdd1243dSDimitry Andric static bool generateBuiltinVar(const SPIRV::IncomingCall *Call,
1011*bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
1012*bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
1013*bdd1243dSDimitry Andric   // Lookup the builtin variable record.
1014*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1015*bdd1243dSDimitry Andric   SPIRV::BuiltIn::BuiltIn Value =
1016*bdd1243dSDimitry Andric       SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1017*bdd1243dSDimitry Andric 
1018*bdd1243dSDimitry Andric   if (Value == SPIRV::BuiltIn::GlobalInvocationId)
1019*bdd1243dSDimitry Andric     return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);
1020*bdd1243dSDimitry Andric 
1021*bdd1243dSDimitry Andric   // Build a load instruction for the builtin variable.
1022*bdd1243dSDimitry Andric   unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);
1023*bdd1243dSDimitry Andric   LLT LLType;
1024*bdd1243dSDimitry Andric   if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector)
1025*bdd1243dSDimitry Andric     LLType =
1026*bdd1243dSDimitry Andric         LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth);
1027*bdd1243dSDimitry Andric   else
1028*bdd1243dSDimitry Andric     LLType = LLT::scalar(BitWidth);
1029*bdd1243dSDimitry Andric 
1030*bdd1243dSDimitry Andric   return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,
1031*bdd1243dSDimitry Andric                                   LLType, Call->ReturnRegister);
1032*bdd1243dSDimitry Andric }
1033*bdd1243dSDimitry Andric 
1034*bdd1243dSDimitry Andric static bool generateAtomicInst(const SPIRV::IncomingCall *Call,
1035*bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
1036*bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
1037*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1038*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1039*bdd1243dSDimitry Andric   unsigned Opcode =
1040*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1041*bdd1243dSDimitry Andric 
1042*bdd1243dSDimitry Andric   switch (Opcode) {
1043*bdd1243dSDimitry Andric   case SPIRV::OpStore:
1044*bdd1243dSDimitry Andric     return buildAtomicInitInst(Call, MIRBuilder);
1045*bdd1243dSDimitry Andric   case SPIRV::OpAtomicLoad:
1046*bdd1243dSDimitry Andric     return buildAtomicLoadInst(Call, MIRBuilder, GR);
1047*bdd1243dSDimitry Andric   case SPIRV::OpAtomicStore:
1048*bdd1243dSDimitry Andric     return buildAtomicStoreInst(Call, MIRBuilder, GR);
1049*bdd1243dSDimitry Andric   case SPIRV::OpAtomicCompareExchange:
1050*bdd1243dSDimitry Andric   case SPIRV::OpAtomicCompareExchangeWeak:
1051*bdd1243dSDimitry Andric     return buildAtomicCompareExchangeInst(Call, MIRBuilder, GR);
1052*bdd1243dSDimitry Andric   case SPIRV::OpAtomicIAdd:
1053*bdd1243dSDimitry Andric   case SPIRV::OpAtomicISub:
1054*bdd1243dSDimitry Andric   case SPIRV::OpAtomicOr:
1055*bdd1243dSDimitry Andric   case SPIRV::OpAtomicXor:
1056*bdd1243dSDimitry Andric   case SPIRV::OpAtomicAnd:
1057*bdd1243dSDimitry Andric   case SPIRV::OpAtomicExchange:
1058*bdd1243dSDimitry Andric     return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
1059*bdd1243dSDimitry Andric   case SPIRV::OpMemoryBarrier:
1060*bdd1243dSDimitry Andric     return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
1061*bdd1243dSDimitry Andric   case SPIRV::OpAtomicFlagTestAndSet:
1062*bdd1243dSDimitry Andric   case SPIRV::OpAtomicFlagClear:
1063*bdd1243dSDimitry Andric     return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);
1064*bdd1243dSDimitry Andric   default:
1065*bdd1243dSDimitry Andric     return false;
1066*bdd1243dSDimitry Andric   }
1067*bdd1243dSDimitry Andric }
1068*bdd1243dSDimitry Andric 
1069*bdd1243dSDimitry Andric static bool generateBarrierInst(const SPIRV::IncomingCall *Call,
1070*bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
1071*bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
1072*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1073*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1074*bdd1243dSDimitry Andric   unsigned Opcode =
1075*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1076*bdd1243dSDimitry Andric 
1077*bdd1243dSDimitry Andric   return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
1078*bdd1243dSDimitry Andric }
1079*bdd1243dSDimitry Andric 
1080*bdd1243dSDimitry Andric static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call,
1081*bdd1243dSDimitry Andric                                   MachineIRBuilder &MIRBuilder,
1082*bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR) {
1083*bdd1243dSDimitry Andric   unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode();
1084*bdd1243dSDimitry Andric   bool IsVec = Opcode == SPIRV::OpTypeVector;
1085*bdd1243dSDimitry Andric   // Use OpDot only in case of vector args and OpFMul in case of scalar args.
1086*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS)
1087*bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
1088*bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1089*bdd1243dSDimitry Andric       .addUse(Call->Arguments[0])
1090*bdd1243dSDimitry Andric       .addUse(Call->Arguments[1]);
1091*bdd1243dSDimitry Andric   return true;
1092*bdd1243dSDimitry Andric }
1093*bdd1243dSDimitry Andric 
1094*bdd1243dSDimitry Andric static bool generateGetQueryInst(const SPIRV::IncomingCall *Call,
1095*bdd1243dSDimitry Andric                                  MachineIRBuilder &MIRBuilder,
1096*bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
1097*bdd1243dSDimitry Andric   // Lookup the builtin record.
1098*bdd1243dSDimitry Andric   SPIRV::BuiltIn::BuiltIn Value =
1099*bdd1243dSDimitry Andric       SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value;
1100*bdd1243dSDimitry Andric   uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize ||
1101*bdd1243dSDimitry Andric                         Value == SPIRV::BuiltIn::WorkgroupSize ||
1102*bdd1243dSDimitry Andric                         Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);
1103*bdd1243dSDimitry Andric   return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0);
1104*bdd1243dSDimitry Andric }
1105*bdd1243dSDimitry Andric 
1106*bdd1243dSDimitry Andric static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call,
1107*bdd1243dSDimitry Andric                                        MachineIRBuilder &MIRBuilder,
1108*bdd1243dSDimitry Andric                                        SPIRVGlobalRegistry *GR) {
1109*bdd1243dSDimitry Andric   // Lookup the image size query component number in the TableGen records.
1110*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1111*bdd1243dSDimitry Andric   uint32_t Component =
1112*bdd1243dSDimitry Andric       SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component;
1113*bdd1243dSDimitry Andric   // Query result may either be a vector or a scalar. If return type is not a
1114*bdd1243dSDimitry Andric   // vector, expect only a single size component. Otherwise get the number of
1115*bdd1243dSDimitry Andric   // expected components.
1116*bdd1243dSDimitry Andric   SPIRVType *RetTy = Call->ReturnType;
1117*bdd1243dSDimitry Andric   unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector
1118*bdd1243dSDimitry Andric                                           ? RetTy->getOperand(2).getImm()
1119*bdd1243dSDimitry Andric                                           : 1;
1120*bdd1243dSDimitry Andric   // Get the actual number of query result/size components.
1121*bdd1243dSDimitry Andric   SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1122*bdd1243dSDimitry Andric   unsigned NumActualRetComponents = getNumSizeComponents(ImgType);
1123*bdd1243dSDimitry Andric   Register QueryResult = Call->ReturnRegister;
1124*bdd1243dSDimitry Andric   SPIRVType *QueryResultType = Call->ReturnType;
1125*bdd1243dSDimitry Andric   if (NumExpectedRetComponents != NumActualRetComponents) {
1126*bdd1243dSDimitry Andric     QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(
1127*bdd1243dSDimitry Andric         LLT::fixed_vector(NumActualRetComponents, 32));
1128*bdd1243dSDimitry Andric     SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
1129*bdd1243dSDimitry Andric     QueryResultType = GR->getOrCreateSPIRVVectorType(
1130*bdd1243dSDimitry Andric         IntTy, NumActualRetComponents, MIRBuilder);
1131*bdd1243dSDimitry Andric     GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());
1132*bdd1243dSDimitry Andric   }
1133*bdd1243dSDimitry Andric   bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;
1134*bdd1243dSDimitry Andric   unsigned Opcode =
1135*bdd1243dSDimitry Andric       IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;
1136*bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode)
1137*bdd1243dSDimitry Andric                  .addDef(QueryResult)
1138*bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(QueryResultType))
1139*bdd1243dSDimitry Andric                  .addUse(Call->Arguments[0]);
1140*bdd1243dSDimitry Andric   if (!IsDimBuf)
1141*bdd1243dSDimitry Andric     MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Lod id.
1142*bdd1243dSDimitry Andric   if (NumExpectedRetComponents == NumActualRetComponents)
1143*bdd1243dSDimitry Andric     return true;
1144*bdd1243dSDimitry Andric   if (NumExpectedRetComponents == 1) {
1145*bdd1243dSDimitry Andric     // Only 1 component is expected, build OpCompositeExtract instruction.
1146*bdd1243dSDimitry Andric     unsigned ExtractedComposite =
1147*bdd1243dSDimitry Andric         Component == 3 ? NumActualRetComponents - 1 : Component;
1148*bdd1243dSDimitry Andric     assert(ExtractedComposite < NumActualRetComponents &&
1149*bdd1243dSDimitry Andric            "Invalid composite index!");
1150*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1151*bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1152*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1153*bdd1243dSDimitry Andric         .addUse(QueryResult)
1154*bdd1243dSDimitry Andric         .addImm(ExtractedComposite);
1155*bdd1243dSDimitry Andric   } else {
1156*bdd1243dSDimitry Andric     // More than 1 component is expected, fill a new vector.
1157*bdd1243dSDimitry Andric     auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)
1158*bdd1243dSDimitry Andric                    .addDef(Call->ReturnRegister)
1159*bdd1243dSDimitry Andric                    .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1160*bdd1243dSDimitry Andric                    .addUse(QueryResult)
1161*bdd1243dSDimitry Andric                    .addUse(QueryResult);
1162*bdd1243dSDimitry Andric     for (unsigned i = 0; i < NumExpectedRetComponents; ++i)
1163*bdd1243dSDimitry Andric       MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);
1164*bdd1243dSDimitry Andric   }
1165*bdd1243dSDimitry Andric   return true;
1166*bdd1243dSDimitry Andric }
1167*bdd1243dSDimitry Andric 
1168*bdd1243dSDimitry Andric static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call,
1169*bdd1243dSDimitry Andric                                        MachineIRBuilder &MIRBuilder,
1170*bdd1243dSDimitry Andric                                        SPIRVGlobalRegistry *GR) {
1171*bdd1243dSDimitry Andric   assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&
1172*bdd1243dSDimitry Andric          "Image samples query result must be of int type!");
1173*bdd1243dSDimitry Andric 
1174*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1175*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1176*bdd1243dSDimitry Andric   unsigned Opcode =
1177*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1178*bdd1243dSDimitry Andric 
1179*bdd1243dSDimitry Andric   Register Image = Call->Arguments[0];
1180*bdd1243dSDimitry Andric   SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(
1181*bdd1243dSDimitry Andric       GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());
1182*bdd1243dSDimitry Andric 
1183*bdd1243dSDimitry Andric   switch (Opcode) {
1184*bdd1243dSDimitry Andric   case SPIRV::OpImageQuerySamples:
1185*bdd1243dSDimitry Andric     assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&
1186*bdd1243dSDimitry Andric            "Image must be of 2D dimensionality");
1187*bdd1243dSDimitry Andric     break;
1188*bdd1243dSDimitry Andric   case SPIRV::OpImageQueryLevels:
1189*bdd1243dSDimitry Andric     assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||
1190*bdd1243dSDimitry Andric             ImageDimensionality == SPIRV::Dim::DIM_2D ||
1191*bdd1243dSDimitry Andric             ImageDimensionality == SPIRV::Dim::DIM_3D ||
1192*bdd1243dSDimitry Andric             ImageDimensionality == SPIRV::Dim::DIM_Cube) &&
1193*bdd1243dSDimitry Andric            "Image must be of 1D/2D/3D/Cube dimensionality");
1194*bdd1243dSDimitry Andric     break;
1195*bdd1243dSDimitry Andric   }
1196*bdd1243dSDimitry Andric 
1197*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
1198*bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
1199*bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1200*bdd1243dSDimitry Andric       .addUse(Image);
1201*bdd1243dSDimitry Andric   return true;
1202*bdd1243dSDimitry Andric }
1203*bdd1243dSDimitry Andric 
1204*bdd1243dSDimitry Andric // TODO: Move to TableGen.
1205*bdd1243dSDimitry Andric static SPIRV::SamplerAddressingMode::SamplerAddressingMode
1206*bdd1243dSDimitry Andric getSamplerAddressingModeFromBitmask(unsigned Bitmask) {
1207*bdd1243dSDimitry Andric   switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {
1208*bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_CLAMP:
1209*bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::Clamp;
1210*bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:
1211*bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::ClampToEdge;
1212*bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_REPEAT:
1213*bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::Repeat;
1214*bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:
1215*bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::RepeatMirrored;
1216*bdd1243dSDimitry Andric   case SPIRV::CLK_ADDRESS_NONE:
1217*bdd1243dSDimitry Andric     return SPIRV::SamplerAddressingMode::None;
1218*bdd1243dSDimitry Andric   default:
1219*bdd1243dSDimitry Andric     llvm_unreachable("Unknown CL address mode");
1220*bdd1243dSDimitry Andric   }
1221*bdd1243dSDimitry Andric }
1222*bdd1243dSDimitry Andric 
1223*bdd1243dSDimitry Andric static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {
1224*bdd1243dSDimitry Andric   return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;
1225*bdd1243dSDimitry Andric }
1226*bdd1243dSDimitry Andric 
1227*bdd1243dSDimitry Andric static SPIRV::SamplerFilterMode::SamplerFilterMode
1228*bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(unsigned Bitmask) {
1229*bdd1243dSDimitry Andric   if (Bitmask & SPIRV::CLK_FILTER_LINEAR)
1230*bdd1243dSDimitry Andric     return SPIRV::SamplerFilterMode::Linear;
1231*bdd1243dSDimitry Andric   if (Bitmask & SPIRV::CLK_FILTER_NEAREST)
1232*bdd1243dSDimitry Andric     return SPIRV::SamplerFilterMode::Nearest;
1233*bdd1243dSDimitry Andric   return SPIRV::SamplerFilterMode::Nearest;
1234*bdd1243dSDimitry Andric }
1235*bdd1243dSDimitry Andric 
1236*bdd1243dSDimitry Andric static bool generateReadImageInst(const StringRef DemangledCall,
1237*bdd1243dSDimitry Andric                                   const SPIRV::IncomingCall *Call,
1238*bdd1243dSDimitry Andric                                   MachineIRBuilder &MIRBuilder,
1239*bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR) {
1240*bdd1243dSDimitry Andric   Register Image = Call->Arguments[0];
1241*bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1242*bdd1243dSDimitry Andric 
1243*bdd1243dSDimitry Andric   if (DemangledCall.contains_insensitive("ocl_sampler")) {
1244*bdd1243dSDimitry Andric     Register Sampler = Call->Arguments[1];
1245*bdd1243dSDimitry Andric 
1246*bdd1243dSDimitry Andric     if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&
1247*bdd1243dSDimitry Andric         getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {
1248*bdd1243dSDimitry Andric       uint64_t SamplerMask = getIConstVal(Sampler, MRI);
1249*bdd1243dSDimitry Andric       Sampler = GR->buildConstantSampler(
1250*bdd1243dSDimitry Andric           Register(), getSamplerAddressingModeFromBitmask(SamplerMask),
1251*bdd1243dSDimitry Andric           getSamplerParamFromBitmask(SamplerMask),
1252*bdd1243dSDimitry Andric           getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder,
1253*bdd1243dSDimitry Andric           GR->getSPIRVTypeForVReg(Sampler));
1254*bdd1243dSDimitry Andric     }
1255*bdd1243dSDimitry Andric     SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1256*bdd1243dSDimitry Andric     SPIRVType *SampledImageType =
1257*bdd1243dSDimitry Andric         GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1258*bdd1243dSDimitry Andric     Register SampledImage = MRI->createVirtualRegister(&SPIRV::IDRegClass);
1259*bdd1243dSDimitry Andric 
1260*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1261*bdd1243dSDimitry Andric         .addDef(SampledImage)
1262*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(SampledImageType))
1263*bdd1243dSDimitry Andric         .addUse(Image)
1264*bdd1243dSDimitry Andric         .addUse(Sampler);
1265*bdd1243dSDimitry Andric 
1266*bdd1243dSDimitry Andric     Register Lod = GR->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()),
1267*bdd1243dSDimitry Andric                                        MIRBuilder);
1268*bdd1243dSDimitry Andric     SPIRVType *TempType = Call->ReturnType;
1269*bdd1243dSDimitry Andric     bool NeedsExtraction = false;
1270*bdd1243dSDimitry Andric     if (TempType->getOpcode() != SPIRV::OpTypeVector) {
1271*bdd1243dSDimitry Andric       TempType =
1272*bdd1243dSDimitry Andric           GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder);
1273*bdd1243dSDimitry Andric       NeedsExtraction = true;
1274*bdd1243dSDimitry Andric     }
1275*bdd1243dSDimitry Andric     LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(TempType));
1276*bdd1243dSDimitry Andric     Register TempRegister = MRI->createGenericVirtualRegister(LLType);
1277*bdd1243dSDimitry Andric     GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());
1278*bdd1243dSDimitry Andric 
1279*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1280*bdd1243dSDimitry Andric         .addDef(NeedsExtraction ? TempRegister : Call->ReturnRegister)
1281*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(TempType))
1282*bdd1243dSDimitry Andric         .addUse(SampledImage)
1283*bdd1243dSDimitry Andric         .addUse(Call->Arguments[2]) // Coordinate.
1284*bdd1243dSDimitry Andric         .addImm(SPIRV::ImageOperand::Lod)
1285*bdd1243dSDimitry Andric         .addUse(Lod);
1286*bdd1243dSDimitry Andric 
1287*bdd1243dSDimitry Andric     if (NeedsExtraction)
1288*bdd1243dSDimitry Andric       MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1289*bdd1243dSDimitry Andric           .addDef(Call->ReturnRegister)
1290*bdd1243dSDimitry Andric           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1291*bdd1243dSDimitry Andric           .addUse(TempRegister)
1292*bdd1243dSDimitry Andric           .addImm(0);
1293*bdd1243dSDimitry Andric   } else if (DemangledCall.contains_insensitive("msaa")) {
1294*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageRead)
1295*bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1296*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1297*bdd1243dSDimitry Andric         .addUse(Image)
1298*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]) // Coordinate.
1299*bdd1243dSDimitry Andric         .addImm(SPIRV::ImageOperand::Sample)
1300*bdd1243dSDimitry Andric         .addUse(Call->Arguments[2]);
1301*bdd1243dSDimitry Andric   } else {
1302*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageRead)
1303*bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1304*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1305*bdd1243dSDimitry Andric         .addUse(Image)
1306*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]); // Coordinate.
1307*bdd1243dSDimitry Andric   }
1308*bdd1243dSDimitry Andric   return true;
1309*bdd1243dSDimitry Andric }
1310*bdd1243dSDimitry Andric 
1311*bdd1243dSDimitry Andric static bool generateWriteImageInst(const SPIRV::IncomingCall *Call,
1312*bdd1243dSDimitry Andric                                    MachineIRBuilder &MIRBuilder,
1313*bdd1243dSDimitry Andric                                    SPIRVGlobalRegistry *GR) {
1314*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(SPIRV::OpImageWrite)
1315*bdd1243dSDimitry Andric       .addUse(Call->Arguments[0])  // Image.
1316*bdd1243dSDimitry Andric       .addUse(Call->Arguments[1])  // Coordinate.
1317*bdd1243dSDimitry Andric       .addUse(Call->Arguments[2]); // Texel.
1318*bdd1243dSDimitry Andric   return true;
1319*bdd1243dSDimitry Andric }
1320*bdd1243dSDimitry Andric 
1321*bdd1243dSDimitry Andric static bool generateSampleImageInst(const StringRef DemangledCall,
1322*bdd1243dSDimitry Andric                                     const SPIRV::IncomingCall *Call,
1323*bdd1243dSDimitry Andric                                     MachineIRBuilder &MIRBuilder,
1324*bdd1243dSDimitry Andric                                     SPIRVGlobalRegistry *GR) {
1325*bdd1243dSDimitry Andric   if (Call->Builtin->Name.contains_insensitive(
1326*bdd1243dSDimitry Andric           "__translate_sampler_initializer")) {
1327*bdd1243dSDimitry Andric     // Build sampler literal.
1328*bdd1243dSDimitry Andric     uint64_t Bitmask = getIConstVal(Call->Arguments[0], MIRBuilder.getMRI());
1329*bdd1243dSDimitry Andric     Register Sampler = GR->buildConstantSampler(
1330*bdd1243dSDimitry Andric         Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),
1331*bdd1243dSDimitry Andric         getSamplerParamFromBitmask(Bitmask),
1332*bdd1243dSDimitry Andric         getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType);
1333*bdd1243dSDimitry Andric     return Sampler.isValid();
1334*bdd1243dSDimitry Andric   } else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) {
1335*bdd1243dSDimitry Andric     // Create OpSampledImage.
1336*bdd1243dSDimitry Andric     Register Image = Call->Arguments[0];
1337*bdd1243dSDimitry Andric     SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1338*bdd1243dSDimitry Andric     SPIRVType *SampledImageType =
1339*bdd1243dSDimitry Andric         GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1340*bdd1243dSDimitry Andric     Register SampledImage =
1341*bdd1243dSDimitry Andric         Call->ReturnRegister.isValid()
1342*bdd1243dSDimitry Andric             ? Call->ReturnRegister
1343*bdd1243dSDimitry Andric             : MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass);
1344*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1345*bdd1243dSDimitry Andric         .addDef(SampledImage)
1346*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(SampledImageType))
1347*bdd1243dSDimitry Andric         .addUse(Image)
1348*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]); // Sampler.
1349*bdd1243dSDimitry Andric     return true;
1350*bdd1243dSDimitry Andric   } else if (Call->Builtin->Name.contains_insensitive(
1351*bdd1243dSDimitry Andric                  "__spirv_ImageSampleExplicitLod")) {
1352*bdd1243dSDimitry Andric     // Sample an image using an explicit level of detail.
1353*bdd1243dSDimitry Andric     std::string ReturnType = DemangledCall.str();
1354*bdd1243dSDimitry Andric     if (DemangledCall.contains("_R")) {
1355*bdd1243dSDimitry Andric       ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);
1356*bdd1243dSDimitry Andric       ReturnType = ReturnType.substr(0, ReturnType.find('('));
1357*bdd1243dSDimitry Andric     }
1358*bdd1243dSDimitry Andric     SPIRVType *Type = GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder);
1359*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1360*bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1361*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Type))
1362*bdd1243dSDimitry Andric         .addUse(Call->Arguments[0]) // Image.
1363*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]) // Coordinate.
1364*bdd1243dSDimitry Andric         .addImm(SPIRV::ImageOperand::Lod)
1365*bdd1243dSDimitry Andric         .addUse(Call->Arguments[3]);
1366*bdd1243dSDimitry Andric     return true;
1367*bdd1243dSDimitry Andric   }
1368*bdd1243dSDimitry Andric   return false;
1369*bdd1243dSDimitry Andric }
1370*bdd1243dSDimitry Andric 
1371*bdd1243dSDimitry Andric static bool generateSelectInst(const SPIRV::IncomingCall *Call,
1372*bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder) {
1373*bdd1243dSDimitry Andric   MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],
1374*bdd1243dSDimitry Andric                          Call->Arguments[1], Call->Arguments[2]);
1375*bdd1243dSDimitry Andric   return true;
1376*bdd1243dSDimitry Andric }
1377*bdd1243dSDimitry Andric 
1378*bdd1243dSDimitry Andric static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call,
1379*bdd1243dSDimitry Andric                                      MachineIRBuilder &MIRBuilder,
1380*bdd1243dSDimitry Andric                                      SPIRVGlobalRegistry *GR) {
1381*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1382*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1383*bdd1243dSDimitry Andric   unsigned Opcode =
1384*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1385*bdd1243dSDimitry Andric   const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1386*bdd1243dSDimitry Andric 
1387*bdd1243dSDimitry Andric   switch (Opcode) {
1388*bdd1243dSDimitry Andric   case SPIRV::OpSpecConstant: {
1389*bdd1243dSDimitry Andric     // Build the SpecID decoration.
1390*bdd1243dSDimitry Andric     unsigned SpecId =
1391*bdd1243dSDimitry Andric         static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));
1392*bdd1243dSDimitry Andric     buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,
1393*bdd1243dSDimitry Andric                     {SpecId});
1394*bdd1243dSDimitry Andric     // Determine the constant MI.
1395*bdd1243dSDimitry Andric     Register ConstRegister = Call->Arguments[1];
1396*bdd1243dSDimitry Andric     const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);
1397*bdd1243dSDimitry Andric     assert(Const &&
1398*bdd1243dSDimitry Andric            (Const->getOpcode() == TargetOpcode::G_CONSTANT ||
1399*bdd1243dSDimitry Andric             Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&
1400*bdd1243dSDimitry Andric            "Argument should be either an int or floating-point constant");
1401*bdd1243dSDimitry Andric     // Determine the opcode and built the OpSpec MI.
1402*bdd1243dSDimitry Andric     const MachineOperand &ConstOperand = Const->getOperand(1);
1403*bdd1243dSDimitry Andric     if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {
1404*bdd1243dSDimitry Andric       assert(ConstOperand.isCImm() && "Int constant operand is expected");
1405*bdd1243dSDimitry Andric       Opcode = ConstOperand.getCImm()->getValue().getZExtValue()
1406*bdd1243dSDimitry Andric                    ? SPIRV::OpSpecConstantTrue
1407*bdd1243dSDimitry Andric                    : SPIRV::OpSpecConstantFalse;
1408*bdd1243dSDimitry Andric     }
1409*bdd1243dSDimitry Andric     auto MIB = MIRBuilder.buildInstr(Opcode)
1410*bdd1243dSDimitry Andric                    .addDef(Call->ReturnRegister)
1411*bdd1243dSDimitry Andric                    .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1412*bdd1243dSDimitry Andric 
1413*bdd1243dSDimitry Andric     if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {
1414*bdd1243dSDimitry Andric       if (Const->getOpcode() == TargetOpcode::G_CONSTANT)
1415*bdd1243dSDimitry Andric         addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1416*bdd1243dSDimitry Andric       else
1417*bdd1243dSDimitry Andric         addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);
1418*bdd1243dSDimitry Andric     }
1419*bdd1243dSDimitry Andric     return true;
1420*bdd1243dSDimitry Andric   }
1421*bdd1243dSDimitry Andric   case SPIRV::OpSpecConstantComposite: {
1422*bdd1243dSDimitry Andric     auto MIB = MIRBuilder.buildInstr(Opcode)
1423*bdd1243dSDimitry Andric                    .addDef(Call->ReturnRegister)
1424*bdd1243dSDimitry Andric                    .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1425*bdd1243dSDimitry Andric     for (unsigned i = 0; i < Call->Arguments.size(); i++)
1426*bdd1243dSDimitry Andric       MIB.addUse(Call->Arguments[i]);
1427*bdd1243dSDimitry Andric     return true;
1428*bdd1243dSDimitry Andric   }
1429*bdd1243dSDimitry Andric   default:
1430*bdd1243dSDimitry Andric     return false;
1431*bdd1243dSDimitry Andric   }
1432*bdd1243dSDimitry Andric }
1433*bdd1243dSDimitry Andric 
1434*bdd1243dSDimitry Andric static MachineInstr *getBlockStructInstr(Register ParamReg,
1435*bdd1243dSDimitry Andric                                          MachineRegisterInfo *MRI) {
1436*bdd1243dSDimitry Andric   // We expect the following sequence of instructions:
1437*bdd1243dSDimitry Andric   //   %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)
1438*bdd1243dSDimitry Andric   //   or       = G_GLOBAL_VALUE @block_literal_global
1439*bdd1243dSDimitry Andric   //   %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0
1440*bdd1243dSDimitry Andric   //   %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)
1441*bdd1243dSDimitry Andric   MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
1442*bdd1243dSDimitry Andric   assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
1443*bdd1243dSDimitry Andric          MI->getOperand(1).isReg());
1444*bdd1243dSDimitry Andric   Register BitcastReg = MI->getOperand(1).getReg();
1445*bdd1243dSDimitry Andric   MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
1446*bdd1243dSDimitry Andric   assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&
1447*bdd1243dSDimitry Andric          BitcastMI->getOperand(2).isReg());
1448*bdd1243dSDimitry Andric   Register ValueReg = BitcastMI->getOperand(2).getReg();
1449*bdd1243dSDimitry Andric   MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);
1450*bdd1243dSDimitry Andric   return ValueMI;
1451*bdd1243dSDimitry Andric }
1452*bdd1243dSDimitry Andric 
1453*bdd1243dSDimitry Andric // Return an integer constant corresponding to the given register and
1454*bdd1243dSDimitry Andric // defined in spv_track_constant.
1455*bdd1243dSDimitry Andric // TODO: maybe unify with prelegalizer pass.
1456*bdd1243dSDimitry Andric static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) {
1457*bdd1243dSDimitry Andric   MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);
1458*bdd1243dSDimitry Andric   assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&
1459*bdd1243dSDimitry Andric          DefMI->getOperand(2).isReg());
1460*bdd1243dSDimitry Andric   MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());
1461*bdd1243dSDimitry Andric   assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&
1462*bdd1243dSDimitry Andric          DefMI2->getOperand(1).isCImm());
1463*bdd1243dSDimitry Andric   return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();
1464*bdd1243dSDimitry Andric }
1465*bdd1243dSDimitry Andric 
1466*bdd1243dSDimitry Andric // Return type of the instruction result from spv_assign_type intrinsic.
1467*bdd1243dSDimitry Andric // TODO: maybe unify with prelegalizer pass.
1468*bdd1243dSDimitry Andric static const Type *getMachineInstrType(MachineInstr *MI) {
1469*bdd1243dSDimitry Andric   MachineInstr *NextMI = MI->getNextNode();
1470*bdd1243dSDimitry Andric   if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
1471*bdd1243dSDimitry Andric     NextMI = NextMI->getNextNode();
1472*bdd1243dSDimitry Andric   Register ValueReg = MI->getOperand(0).getReg();
1473*bdd1243dSDimitry Andric   if (!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) ||
1474*bdd1243dSDimitry Andric       NextMI->getOperand(1).getReg() != ValueReg)
1475*bdd1243dSDimitry Andric     return nullptr;
1476*bdd1243dSDimitry Andric   Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
1477*bdd1243dSDimitry Andric   assert(Ty && "Type is expected");
1478*bdd1243dSDimitry Andric   return getTypedPtrEltType(Ty);
1479*bdd1243dSDimitry Andric }
1480*bdd1243dSDimitry Andric 
1481*bdd1243dSDimitry Andric static const Type *getBlockStructType(Register ParamReg,
1482*bdd1243dSDimitry Andric                                       MachineRegisterInfo *MRI) {
1483*bdd1243dSDimitry Andric   // In principle, this information should be passed to us from Clang via
1484*bdd1243dSDimitry Andric   // an elementtype attribute. However, said attribute requires that
1485*bdd1243dSDimitry Andric   // the function call be an intrinsic, which is not. Instead, we rely on being
1486*bdd1243dSDimitry Andric   // able to trace this to the declaration of a variable: OpenCL C specification
1487*bdd1243dSDimitry Andric   // section 6.12.5 should guarantee that we can do this.
1488*bdd1243dSDimitry Andric   MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);
1489*bdd1243dSDimitry Andric   if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
1490*bdd1243dSDimitry Andric     return getTypedPtrEltType(MI->getOperand(1).getGlobal()->getType());
1491*bdd1243dSDimitry Andric   assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
1492*bdd1243dSDimitry Andric          "Blocks in OpenCL C must be traceable to allocation site");
1493*bdd1243dSDimitry Andric   return getMachineInstrType(MI);
1494*bdd1243dSDimitry Andric }
1495*bdd1243dSDimitry Andric 
1496*bdd1243dSDimitry Andric // TODO: maybe move to the global register.
1497*bdd1243dSDimitry Andric static SPIRVType *
1498*bdd1243dSDimitry Andric getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder,
1499*bdd1243dSDimitry Andric                                    SPIRVGlobalRegistry *GR) {
1500*bdd1243dSDimitry Andric   LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
1501*bdd1243dSDimitry Andric   Type *OpaqueType = StructType::getTypeByName(Context, "spirv.DeviceEvent");
1502*bdd1243dSDimitry Andric   if (!OpaqueType)
1503*bdd1243dSDimitry Andric     OpaqueType = StructType::getTypeByName(Context, "opencl.clk_event_t");
1504*bdd1243dSDimitry Andric   if (!OpaqueType)
1505*bdd1243dSDimitry Andric     OpaqueType = StructType::create(Context, "spirv.DeviceEvent");
1506*bdd1243dSDimitry Andric   unsigned SC0 = storageClassToAddressSpace(SPIRV::StorageClass::Function);
1507*bdd1243dSDimitry Andric   unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
1508*bdd1243dSDimitry Andric   Type *PtrType = PointerType::get(PointerType::get(OpaqueType, SC0), SC1);
1509*bdd1243dSDimitry Andric   return GR->getOrCreateSPIRVType(PtrType, MIRBuilder);
1510*bdd1243dSDimitry Andric }
1511*bdd1243dSDimitry Andric 
1512*bdd1243dSDimitry Andric static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call,
1513*bdd1243dSDimitry Andric                                MachineIRBuilder &MIRBuilder,
1514*bdd1243dSDimitry Andric                                SPIRVGlobalRegistry *GR) {
1515*bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1516*bdd1243dSDimitry Andric   const DataLayout &DL = MIRBuilder.getDataLayout();
1517*bdd1243dSDimitry Andric   bool HasEvents = Call->Builtin->Name.find("events") != StringRef::npos;
1518*bdd1243dSDimitry Andric   const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
1519*bdd1243dSDimitry Andric 
1520*bdd1243dSDimitry Andric   // Make vararg instructions before OpEnqueueKernel.
1521*bdd1243dSDimitry Andric   // Local sizes arguments: Sizes of block invoke arguments. Clang generates
1522*bdd1243dSDimitry Andric   // local size operands as an array, so we need to unpack them.
1523*bdd1243dSDimitry Andric   SmallVector<Register, 16> LocalSizes;
1524*bdd1243dSDimitry Andric   if (Call->Builtin->Name.find("_varargs") != StringRef::npos) {
1525*bdd1243dSDimitry Andric     const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6;
1526*bdd1243dSDimitry Andric     Register GepReg = Call->Arguments[LocalSizeArrayIdx];
1527*bdd1243dSDimitry Andric     MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg);
1528*bdd1243dSDimitry Andric     assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) &&
1529*bdd1243dSDimitry Andric            GepMI->getOperand(3).isReg());
1530*bdd1243dSDimitry Andric     Register ArrayReg = GepMI->getOperand(3).getReg();
1531*bdd1243dSDimitry Andric     MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg);
1532*bdd1243dSDimitry Andric     const Type *LocalSizeTy = getMachineInstrType(ArrayMI);
1533*bdd1243dSDimitry Andric     assert(LocalSizeTy && "Local size type is expected");
1534*bdd1243dSDimitry Andric     const uint64_t LocalSizeNum =
1535*bdd1243dSDimitry Andric         cast<ArrayType>(LocalSizeTy)->getNumElements();
1536*bdd1243dSDimitry Andric     unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
1537*bdd1243dSDimitry Andric     const LLT LLType = LLT::pointer(SC, GR->getPointerSize());
1538*bdd1243dSDimitry Andric     const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType(
1539*bdd1243dSDimitry Andric         Int32Ty, MIRBuilder, SPIRV::StorageClass::Function);
1540*bdd1243dSDimitry Andric     for (unsigned I = 0; I < LocalSizeNum; ++I) {
1541*bdd1243dSDimitry Andric       Register Reg =
1542*bdd1243dSDimitry Andric           MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass);
1543*bdd1243dSDimitry Andric       MIRBuilder.getMRI()->setType(Reg, LLType);
1544*bdd1243dSDimitry Andric       GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF());
1545*bdd1243dSDimitry Andric       auto GEPInst = MIRBuilder.buildIntrinsic(Intrinsic::spv_gep,
1546*bdd1243dSDimitry Andric                                                ArrayRef<Register>{Reg}, true);
1547*bdd1243dSDimitry Andric       GEPInst
1548*bdd1243dSDimitry Andric           .addImm(GepMI->getOperand(2).getImm())          // In bound.
1549*bdd1243dSDimitry Andric           .addUse(ArrayMI->getOperand(0).getReg())        // Alloca.
1550*bdd1243dSDimitry Andric           .addUse(buildConstantIntReg(0, MIRBuilder, GR)) // Indices.
1551*bdd1243dSDimitry Andric           .addUse(buildConstantIntReg(I, MIRBuilder, GR));
1552*bdd1243dSDimitry Andric       LocalSizes.push_back(Reg);
1553*bdd1243dSDimitry Andric     }
1554*bdd1243dSDimitry Andric   }
1555*bdd1243dSDimitry Andric 
1556*bdd1243dSDimitry Andric   // SPIRV OpEnqueueKernel instruction has 10+ arguments.
1557*bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)
1558*bdd1243dSDimitry Andric                  .addDef(Call->ReturnRegister)
1559*bdd1243dSDimitry Andric                  .addUse(GR->getSPIRVTypeID(Int32Ty));
1560*bdd1243dSDimitry Andric 
1561*bdd1243dSDimitry Andric   // Copy all arguments before block invoke function pointer.
1562*bdd1243dSDimitry Andric   const unsigned BlockFIdx = HasEvents ? 6 : 3;
1563*bdd1243dSDimitry Andric   for (unsigned i = 0; i < BlockFIdx; i++)
1564*bdd1243dSDimitry Andric     MIB.addUse(Call->Arguments[i]);
1565*bdd1243dSDimitry Andric 
1566*bdd1243dSDimitry Andric   // If there are no event arguments in the original call, add dummy ones.
1567*bdd1243dSDimitry Andric   if (!HasEvents) {
1568*bdd1243dSDimitry Andric     MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Dummy num events.
1569*bdd1243dSDimitry Andric     Register NullPtr = GR->getOrCreateConstNullPtr(
1570*bdd1243dSDimitry Andric         MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR));
1571*bdd1243dSDimitry Andric     MIB.addUse(NullPtr); // Dummy wait events.
1572*bdd1243dSDimitry Andric     MIB.addUse(NullPtr); // Dummy ret event.
1573*bdd1243dSDimitry Andric   }
1574*bdd1243dSDimitry Andric 
1575*bdd1243dSDimitry Andric   MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI);
1576*bdd1243dSDimitry Andric   assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
1577*bdd1243dSDimitry Andric   // Invoke: Pointer to invoke function.
1578*bdd1243dSDimitry Andric   MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal());
1579*bdd1243dSDimitry Andric 
1580*bdd1243dSDimitry Andric   Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1];
1581*bdd1243dSDimitry Andric   // Param: Pointer to block literal.
1582*bdd1243dSDimitry Andric   MIB.addUse(BlockLiteralReg);
1583*bdd1243dSDimitry Andric 
1584*bdd1243dSDimitry Andric   Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
1585*bdd1243dSDimitry Andric   // TODO: these numbers should be obtained from block literal structure.
1586*bdd1243dSDimitry Andric   // Param Size: Size of block literal structure.
1587*bdd1243dSDimitry Andric   MIB.addUse(buildConstantIntReg(DL.getTypeStoreSize(PType), MIRBuilder, GR));
1588*bdd1243dSDimitry Andric   // Param Aligment: Aligment of block literal structure.
1589*bdd1243dSDimitry Andric   MIB.addUse(
1590*bdd1243dSDimitry Andric       buildConstantIntReg(DL.getPrefTypeAlignment(PType), MIRBuilder, GR));
1591*bdd1243dSDimitry Andric 
1592*bdd1243dSDimitry Andric   for (unsigned i = 0; i < LocalSizes.size(); i++)
1593*bdd1243dSDimitry Andric     MIB.addUse(LocalSizes[i]);
1594*bdd1243dSDimitry Andric   return true;
1595*bdd1243dSDimitry Andric }
1596*bdd1243dSDimitry Andric 
1597*bdd1243dSDimitry Andric static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,
1598*bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
1599*bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
1600*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1601*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1602*bdd1243dSDimitry Andric   unsigned Opcode =
1603*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1604*bdd1243dSDimitry Andric 
1605*bdd1243dSDimitry Andric   switch (Opcode) {
1606*bdd1243dSDimitry Andric   case SPIRV::OpRetainEvent:
1607*bdd1243dSDimitry Andric   case SPIRV::OpReleaseEvent:
1608*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);
1609*bdd1243dSDimitry Andric   case SPIRV::OpCreateUserEvent:
1610*bdd1243dSDimitry Andric   case SPIRV::OpGetDefaultQueue:
1611*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
1612*bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1613*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1614*bdd1243dSDimitry Andric   case SPIRV::OpIsValidEvent:
1615*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
1616*bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1617*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1618*bdd1243dSDimitry Andric         .addUse(Call->Arguments[0]);
1619*bdd1243dSDimitry Andric   case SPIRV::OpSetUserEventStatus:
1620*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
1621*bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
1622*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]);
1623*bdd1243dSDimitry Andric   case SPIRV::OpCaptureEventProfilingInfo:
1624*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
1625*bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
1626*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1])
1627*bdd1243dSDimitry Andric         .addUse(Call->Arguments[2]);
1628*bdd1243dSDimitry Andric   case SPIRV::OpBuildNDRange: {
1629*bdd1243dSDimitry Andric     MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1630*bdd1243dSDimitry Andric     SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1631*bdd1243dSDimitry Andric     assert(PtrType->getOpcode() == SPIRV::OpTypePointer &&
1632*bdd1243dSDimitry Andric            PtrType->getOperand(2).isReg());
1633*bdd1243dSDimitry Andric     Register TypeReg = PtrType->getOperand(2).getReg();
1634*bdd1243dSDimitry Andric     SPIRVType *StructType = GR->getSPIRVTypeForVReg(TypeReg);
1635*bdd1243dSDimitry Andric     Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
1636*bdd1243dSDimitry Andric     GR->assignSPIRVTypeToVReg(StructType, TmpReg, MIRBuilder.getMF());
1637*bdd1243dSDimitry Andric     // Skip the first arg, it's the destination pointer. OpBuildNDRange takes
1638*bdd1243dSDimitry Andric     // three other arguments, so pass zero constant on absence.
1639*bdd1243dSDimitry Andric     unsigned NumArgs = Call->Arguments.size();
1640*bdd1243dSDimitry Andric     assert(NumArgs >= 2);
1641*bdd1243dSDimitry Andric     Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2];
1642*bdd1243dSDimitry Andric     Register LocalWorkSize =
1643*bdd1243dSDimitry Andric         NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3];
1644*bdd1243dSDimitry Andric     Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1];
1645*bdd1243dSDimitry Andric     if (NumArgs < 4) {
1646*bdd1243dSDimitry Andric       Register Const;
1647*bdd1243dSDimitry Andric       SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize);
1648*bdd1243dSDimitry Andric       if (SpvTy->getOpcode() == SPIRV::OpTypePointer) {
1649*bdd1243dSDimitry Andric         MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize);
1650*bdd1243dSDimitry Andric         assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) &&
1651*bdd1243dSDimitry Andric                DefInstr->getOperand(3).isReg());
1652*bdd1243dSDimitry Andric         Register GWSPtr = DefInstr->getOperand(3).getReg();
1653*bdd1243dSDimitry Andric         // TODO: Maybe simplify generation of the type of the fields.
1654*bdd1243dSDimitry Andric         unsigned Size = Call->Builtin->Name.equals("ndrange_3D") ? 3 : 2;
1655*bdd1243dSDimitry Andric         unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32;
1656*bdd1243dSDimitry Andric         Type *BaseTy = IntegerType::get(
1657*bdd1243dSDimitry Andric             MIRBuilder.getMF().getFunction().getContext(), BitWidth);
1658*bdd1243dSDimitry Andric         Type *FieldTy = ArrayType::get(BaseTy, Size);
1659*bdd1243dSDimitry Andric         SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder);
1660*bdd1243dSDimitry Andric         GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::IDRegClass);
1661*bdd1243dSDimitry Andric         GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize,
1662*bdd1243dSDimitry Andric                                   MIRBuilder.getMF());
1663*bdd1243dSDimitry Andric         MIRBuilder.buildInstr(SPIRV::OpLoad)
1664*bdd1243dSDimitry Andric             .addDef(GlobalWorkSize)
1665*bdd1243dSDimitry Andric             .addUse(GR->getSPIRVTypeID(SpvFieldTy))
1666*bdd1243dSDimitry Andric             .addUse(GWSPtr);
1667*bdd1243dSDimitry Andric         Const = GR->getOrCreateConsIntArray(0, MIRBuilder, SpvFieldTy);
1668*bdd1243dSDimitry Andric       } else {
1669*bdd1243dSDimitry Andric         Const = GR->buildConstantInt(0, MIRBuilder, SpvTy);
1670*bdd1243dSDimitry Andric       }
1671*bdd1243dSDimitry Andric       if (!LocalWorkSize.isValid())
1672*bdd1243dSDimitry Andric         LocalWorkSize = Const;
1673*bdd1243dSDimitry Andric       if (!GlobalWorkOffset.isValid())
1674*bdd1243dSDimitry Andric         GlobalWorkOffset = Const;
1675*bdd1243dSDimitry Andric     }
1676*bdd1243dSDimitry Andric     MIRBuilder.buildInstr(Opcode)
1677*bdd1243dSDimitry Andric         .addDef(TmpReg)
1678*bdd1243dSDimitry Andric         .addUse(TypeReg)
1679*bdd1243dSDimitry Andric         .addUse(GlobalWorkSize)
1680*bdd1243dSDimitry Andric         .addUse(LocalWorkSize)
1681*bdd1243dSDimitry Andric         .addUse(GlobalWorkOffset);
1682*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(SPIRV::OpStore)
1683*bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
1684*bdd1243dSDimitry Andric         .addUse(TmpReg);
1685*bdd1243dSDimitry Andric   }
1686*bdd1243dSDimitry Andric   case SPIRV::OpEnqueueKernel:
1687*bdd1243dSDimitry Andric     return buildEnqueueKernel(Call, MIRBuilder, GR);
1688*bdd1243dSDimitry Andric   default:
1689*bdd1243dSDimitry Andric     return false;
1690*bdd1243dSDimitry Andric   }
1691*bdd1243dSDimitry Andric }
1692*bdd1243dSDimitry Andric 
1693*bdd1243dSDimitry Andric static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,
1694*bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
1695*bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR) {
1696*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1697*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1698*bdd1243dSDimitry Andric   unsigned Opcode =
1699*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1700*bdd1243dSDimitry Andric   auto Scope = buildConstantIntReg(SPIRV::Scope::Workgroup, MIRBuilder, GR);
1701*bdd1243dSDimitry Andric 
1702*bdd1243dSDimitry Andric   switch (Opcode) {
1703*bdd1243dSDimitry Andric   case SPIRV::OpGroupAsyncCopy:
1704*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
1705*bdd1243dSDimitry Andric         .addDef(Call->ReturnRegister)
1706*bdd1243dSDimitry Andric         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1707*bdd1243dSDimitry Andric         .addUse(Scope)
1708*bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
1709*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1])
1710*bdd1243dSDimitry Andric         .addUse(Call->Arguments[2])
1711*bdd1243dSDimitry Andric         .addUse(buildConstantIntReg(1, MIRBuilder, GR))
1712*bdd1243dSDimitry Andric         .addUse(Call->Arguments[3]);
1713*bdd1243dSDimitry Andric   case SPIRV::OpGroupWaitEvents:
1714*bdd1243dSDimitry Andric     return MIRBuilder.buildInstr(Opcode)
1715*bdd1243dSDimitry Andric         .addUse(Scope)
1716*bdd1243dSDimitry Andric         .addUse(Call->Arguments[0])
1717*bdd1243dSDimitry Andric         .addUse(Call->Arguments[1]);
1718*bdd1243dSDimitry Andric   default:
1719*bdd1243dSDimitry Andric     return false;
1720*bdd1243dSDimitry Andric   }
1721*bdd1243dSDimitry Andric }
1722*bdd1243dSDimitry Andric 
1723*bdd1243dSDimitry Andric static bool generateConvertInst(const StringRef DemangledCall,
1724*bdd1243dSDimitry Andric                                 const SPIRV::IncomingCall *Call,
1725*bdd1243dSDimitry Andric                                 MachineIRBuilder &MIRBuilder,
1726*bdd1243dSDimitry Andric                                 SPIRVGlobalRegistry *GR) {
1727*bdd1243dSDimitry Andric   // Lookup the conversion builtin in the TableGen records.
1728*bdd1243dSDimitry Andric   const SPIRV::ConvertBuiltin *Builtin =
1729*bdd1243dSDimitry Andric       SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set);
1730*bdd1243dSDimitry Andric 
1731*bdd1243dSDimitry Andric   if (Builtin->IsSaturated)
1732*bdd1243dSDimitry Andric     buildOpDecorate(Call->ReturnRegister, MIRBuilder,
1733*bdd1243dSDimitry Andric                     SPIRV::Decoration::SaturatedConversion, {});
1734*bdd1243dSDimitry Andric   if (Builtin->IsRounded)
1735*bdd1243dSDimitry Andric     buildOpDecorate(Call->ReturnRegister, MIRBuilder,
1736*bdd1243dSDimitry Andric                     SPIRV::Decoration::FPRoundingMode, {Builtin->RoundingMode});
1737*bdd1243dSDimitry Andric 
1738*bdd1243dSDimitry Andric   unsigned Opcode = SPIRV::OpNop;
1739*bdd1243dSDimitry Andric   if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {
1740*bdd1243dSDimitry Andric     // Int -> ...
1741*bdd1243dSDimitry Andric     if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
1742*bdd1243dSDimitry Andric       // Int -> Int
1743*bdd1243dSDimitry Andric       if (Builtin->IsSaturated)
1744*bdd1243dSDimitry Andric         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS
1745*bdd1243dSDimitry Andric                                               : SPIRV::OpSatConvertSToU;
1746*bdd1243dSDimitry Andric       else
1747*bdd1243dSDimitry Andric         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert
1748*bdd1243dSDimitry Andric                                               : SPIRV::OpSConvert;
1749*bdd1243dSDimitry Andric     } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
1750*bdd1243dSDimitry Andric                                           SPIRV::OpTypeFloat)) {
1751*bdd1243dSDimitry Andric       // Int -> Float
1752*bdd1243dSDimitry Andric       bool IsSourceSigned =
1753*bdd1243dSDimitry Andric           DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';
1754*bdd1243dSDimitry Andric       Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;
1755*bdd1243dSDimitry Andric     }
1756*bdd1243dSDimitry Andric   } else if (GR->isScalarOrVectorOfType(Call->Arguments[0],
1757*bdd1243dSDimitry Andric                                         SPIRV::OpTypeFloat)) {
1758*bdd1243dSDimitry Andric     // Float -> ...
1759*bdd1243dSDimitry Andric     if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt))
1760*bdd1243dSDimitry Andric       // Float -> Int
1761*bdd1243dSDimitry Andric       Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS
1762*bdd1243dSDimitry Andric                                             : SPIRV::OpConvertFToU;
1763*bdd1243dSDimitry Andric     else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
1764*bdd1243dSDimitry Andric                                         SPIRV::OpTypeFloat))
1765*bdd1243dSDimitry Andric       // Float -> Float
1766*bdd1243dSDimitry Andric       Opcode = SPIRV::OpFConvert;
1767*bdd1243dSDimitry Andric   }
1768*bdd1243dSDimitry Andric 
1769*bdd1243dSDimitry Andric   assert(Opcode != SPIRV::OpNop &&
1770*bdd1243dSDimitry Andric          "Conversion between the types not implemented!");
1771*bdd1243dSDimitry Andric 
1772*bdd1243dSDimitry Andric   MIRBuilder.buildInstr(Opcode)
1773*bdd1243dSDimitry Andric       .addDef(Call->ReturnRegister)
1774*bdd1243dSDimitry Andric       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1775*bdd1243dSDimitry Andric       .addUse(Call->Arguments[0]);
1776*bdd1243dSDimitry Andric   return true;
1777*bdd1243dSDimitry Andric }
1778*bdd1243dSDimitry Andric 
1779*bdd1243dSDimitry Andric static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call,
1780*bdd1243dSDimitry Andric                                         MachineIRBuilder &MIRBuilder,
1781*bdd1243dSDimitry Andric                                         SPIRVGlobalRegistry *GR) {
1782*bdd1243dSDimitry Andric   // Lookup the vector load/store builtin in the TableGen records.
1783*bdd1243dSDimitry Andric   const SPIRV::VectorLoadStoreBuiltin *Builtin =
1784*bdd1243dSDimitry Andric       SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
1785*bdd1243dSDimitry Andric                                           Call->Builtin->Set);
1786*bdd1243dSDimitry Andric   // Build extended instruction.
1787*bdd1243dSDimitry Andric   auto MIB =
1788*bdd1243dSDimitry Andric       MIRBuilder.buildInstr(SPIRV::OpExtInst)
1789*bdd1243dSDimitry Andric           .addDef(Call->ReturnRegister)
1790*bdd1243dSDimitry Andric           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1791*bdd1243dSDimitry Andric           .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1792*bdd1243dSDimitry Andric           .addImm(Builtin->Number);
1793*bdd1243dSDimitry Andric   for (auto Argument : Call->Arguments)
1794*bdd1243dSDimitry Andric     MIB.addUse(Argument);
1795*bdd1243dSDimitry Andric 
1796*bdd1243dSDimitry Andric   // Rounding mode should be passed as a last argument in the MI for builtins
1797*bdd1243dSDimitry Andric   // like "vstorea_halfn_r".
1798*bdd1243dSDimitry Andric   if (Builtin->IsRounded)
1799*bdd1243dSDimitry Andric     MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));
1800*bdd1243dSDimitry Andric   return true;
1801*bdd1243dSDimitry Andric }
1802*bdd1243dSDimitry Andric 
1803*bdd1243dSDimitry Andric static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call,
1804*bdd1243dSDimitry Andric                                   MachineIRBuilder &MIRBuilder,
1805*bdd1243dSDimitry Andric                                   SPIRVGlobalRegistry *GR) {
1806*bdd1243dSDimitry Andric   // Lookup the instruction opcode in the TableGen records.
1807*bdd1243dSDimitry Andric   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1808*bdd1243dSDimitry Andric   unsigned Opcode =
1809*bdd1243dSDimitry Andric       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1810*bdd1243dSDimitry Andric   bool IsLoad = Opcode == SPIRV::OpLoad;
1811*bdd1243dSDimitry Andric   // Build the instruction.
1812*bdd1243dSDimitry Andric   auto MIB = MIRBuilder.buildInstr(Opcode);
1813*bdd1243dSDimitry Andric   if (IsLoad) {
1814*bdd1243dSDimitry Andric     MIB.addDef(Call->ReturnRegister);
1815*bdd1243dSDimitry Andric     MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
1816*bdd1243dSDimitry Andric   }
1817*bdd1243dSDimitry Andric   // Add a pointer to the value to load/store.
1818*bdd1243dSDimitry Andric   MIB.addUse(Call->Arguments[0]);
1819*bdd1243dSDimitry Andric   // Add a value to store.
1820*bdd1243dSDimitry Andric   if (!IsLoad)
1821*bdd1243dSDimitry Andric     MIB.addUse(Call->Arguments[1]);
1822*bdd1243dSDimitry Andric   // Add optional memory attributes and an alignment.
1823*bdd1243dSDimitry Andric   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1824*bdd1243dSDimitry Andric   unsigned NumArgs = Call->Arguments.size();
1825*bdd1243dSDimitry Andric   if ((IsLoad && NumArgs >= 2) || NumArgs >= 3)
1826*bdd1243dSDimitry Andric     MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI));
1827*bdd1243dSDimitry Andric   if ((IsLoad && NumArgs >= 3) || NumArgs >= 4)
1828*bdd1243dSDimitry Andric     MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI));
1829*bdd1243dSDimitry Andric   return true;
1830*bdd1243dSDimitry Andric }
1831*bdd1243dSDimitry Andric 
1832*bdd1243dSDimitry Andric /// Lowers a builtin funtion call using the provided \p DemangledCall skeleton
1833*bdd1243dSDimitry Andric /// and external instruction \p Set.
1834*bdd1243dSDimitry Andric namespace SPIRV {
1835*bdd1243dSDimitry Andric std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
1836*bdd1243dSDimitry Andric                                  SPIRV::InstructionSet::InstructionSet Set,
1837*bdd1243dSDimitry Andric                                  MachineIRBuilder &MIRBuilder,
1838*bdd1243dSDimitry Andric                                  const Register OrigRet, const Type *OrigRetTy,
1839*bdd1243dSDimitry Andric                                  const SmallVectorImpl<Register> &Args,
1840*bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
1841*bdd1243dSDimitry Andric   LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");
1842*bdd1243dSDimitry Andric 
1843*bdd1243dSDimitry Andric   // SPIR-V type and return register.
1844*bdd1243dSDimitry Andric   Register ReturnRegister = OrigRet;
1845*bdd1243dSDimitry Andric   SPIRVType *ReturnType = nullptr;
1846*bdd1243dSDimitry Andric   if (OrigRetTy && !OrigRetTy->isVoidTy()) {
1847*bdd1243dSDimitry Andric     ReturnType = GR->assignTypeToVReg(OrigRetTy, OrigRet, MIRBuilder);
1848*bdd1243dSDimitry Andric   } else if (OrigRetTy && OrigRetTy->isVoidTy()) {
1849*bdd1243dSDimitry Andric     ReturnRegister = MIRBuilder.getMRI()->createVirtualRegister(&IDRegClass);
1850*bdd1243dSDimitry Andric     MIRBuilder.getMRI()->setType(ReturnRegister, LLT::scalar(32));
1851*bdd1243dSDimitry Andric     ReturnType = GR->assignTypeToVReg(OrigRetTy, ReturnRegister, MIRBuilder);
1852*bdd1243dSDimitry Andric   }
1853*bdd1243dSDimitry Andric 
1854*bdd1243dSDimitry Andric   // Lookup the builtin in the TableGen records.
1855*bdd1243dSDimitry Andric   std::unique_ptr<const IncomingCall> Call =
1856*bdd1243dSDimitry Andric       lookupBuiltin(DemangledCall, Set, ReturnRegister, ReturnType, Args);
1857*bdd1243dSDimitry Andric 
1858*bdd1243dSDimitry Andric   if (!Call) {
1859*bdd1243dSDimitry Andric     LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
1860*bdd1243dSDimitry Andric     return std::nullopt;
1861*bdd1243dSDimitry Andric   }
1862*bdd1243dSDimitry Andric 
1863*bdd1243dSDimitry Andric   // TODO: check if the provided args meet the builtin requirments.
1864*bdd1243dSDimitry Andric   assert(Args.size() >= Call->Builtin->MinNumArgs &&
1865*bdd1243dSDimitry Andric          "Too few arguments to generate the builtin");
1866*bdd1243dSDimitry Andric   if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs)
1867*bdd1243dSDimitry Andric     LLVM_DEBUG(dbgs() << "More arguments provided than required!\n");
1868*bdd1243dSDimitry Andric 
1869*bdd1243dSDimitry Andric   // Match the builtin with implementation based on the grouping.
1870*bdd1243dSDimitry Andric   switch (Call->Builtin->Group) {
1871*bdd1243dSDimitry Andric   case SPIRV::Extended:
1872*bdd1243dSDimitry Andric     return generateExtInst(Call.get(), MIRBuilder, GR);
1873*bdd1243dSDimitry Andric   case SPIRV::Relational:
1874*bdd1243dSDimitry Andric     return generateRelationalInst(Call.get(), MIRBuilder, GR);
1875*bdd1243dSDimitry Andric   case SPIRV::Group:
1876*bdd1243dSDimitry Andric     return generateGroupInst(Call.get(), MIRBuilder, GR);
1877*bdd1243dSDimitry Andric   case SPIRV::Variable:
1878*bdd1243dSDimitry Andric     return generateBuiltinVar(Call.get(), MIRBuilder, GR);
1879*bdd1243dSDimitry Andric   case SPIRV::Atomic:
1880*bdd1243dSDimitry Andric     return generateAtomicInst(Call.get(), MIRBuilder, GR);
1881*bdd1243dSDimitry Andric   case SPIRV::Barrier:
1882*bdd1243dSDimitry Andric     return generateBarrierInst(Call.get(), MIRBuilder, GR);
1883*bdd1243dSDimitry Andric   case SPIRV::Dot:
1884*bdd1243dSDimitry Andric     return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);
1885*bdd1243dSDimitry Andric   case SPIRV::GetQuery:
1886*bdd1243dSDimitry Andric     return generateGetQueryInst(Call.get(), MIRBuilder, GR);
1887*bdd1243dSDimitry Andric   case SPIRV::ImageSizeQuery:
1888*bdd1243dSDimitry Andric     return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);
1889*bdd1243dSDimitry Andric   case SPIRV::ImageMiscQuery:
1890*bdd1243dSDimitry Andric     return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);
1891*bdd1243dSDimitry Andric   case SPIRV::ReadImage:
1892*bdd1243dSDimitry Andric     return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
1893*bdd1243dSDimitry Andric   case SPIRV::WriteImage:
1894*bdd1243dSDimitry Andric     return generateWriteImageInst(Call.get(), MIRBuilder, GR);
1895*bdd1243dSDimitry Andric   case SPIRV::SampleImage:
1896*bdd1243dSDimitry Andric     return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
1897*bdd1243dSDimitry Andric   case SPIRV::Select:
1898*bdd1243dSDimitry Andric     return generateSelectInst(Call.get(), MIRBuilder);
1899*bdd1243dSDimitry Andric   case SPIRV::SpecConstant:
1900*bdd1243dSDimitry Andric     return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
1901*bdd1243dSDimitry Andric   case SPIRV::Enqueue:
1902*bdd1243dSDimitry Andric     return generateEnqueueInst(Call.get(), MIRBuilder, GR);
1903*bdd1243dSDimitry Andric   case SPIRV::AsyncCopy:
1904*bdd1243dSDimitry Andric     return generateAsyncCopy(Call.get(), MIRBuilder, GR);
1905*bdd1243dSDimitry Andric   case SPIRV::Convert:
1906*bdd1243dSDimitry Andric     return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
1907*bdd1243dSDimitry Andric   case SPIRV::VectorLoadStore:
1908*bdd1243dSDimitry Andric     return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);
1909*bdd1243dSDimitry Andric   case SPIRV::LoadStore:
1910*bdd1243dSDimitry Andric     return generateLoadStoreInst(Call.get(), MIRBuilder, GR);
1911*bdd1243dSDimitry Andric   }
1912*bdd1243dSDimitry Andric   return false;
1913*bdd1243dSDimitry Andric }
1914*bdd1243dSDimitry Andric 
1915*bdd1243dSDimitry Andric struct DemangledType {
1916*bdd1243dSDimitry Andric   StringRef Name;
1917*bdd1243dSDimitry Andric   uint32_t Opcode;
1918*bdd1243dSDimitry Andric };
1919*bdd1243dSDimitry Andric 
1920*bdd1243dSDimitry Andric #define GET_DemangledTypes_DECL
1921*bdd1243dSDimitry Andric #define GET_DemangledTypes_IMPL
1922*bdd1243dSDimitry Andric 
1923*bdd1243dSDimitry Andric struct ImageType {
1924*bdd1243dSDimitry Andric   StringRef Name;
1925*bdd1243dSDimitry Andric   StringRef SampledType;
1926*bdd1243dSDimitry Andric   AccessQualifier::AccessQualifier Qualifier;
1927*bdd1243dSDimitry Andric   Dim::Dim Dimensionality;
1928*bdd1243dSDimitry Andric   bool Arrayed;
1929*bdd1243dSDimitry Andric   bool Depth;
1930*bdd1243dSDimitry Andric   bool Multisampled;
1931*bdd1243dSDimitry Andric   bool Sampled;
1932*bdd1243dSDimitry Andric   ImageFormat::ImageFormat Format;
1933*bdd1243dSDimitry Andric };
1934*bdd1243dSDimitry Andric 
1935*bdd1243dSDimitry Andric struct PipeType {
1936*bdd1243dSDimitry Andric   StringRef Name;
1937*bdd1243dSDimitry Andric   AccessQualifier::AccessQualifier Qualifier;
1938*bdd1243dSDimitry Andric };
1939*bdd1243dSDimitry Andric 
1940*bdd1243dSDimitry Andric using namespace AccessQualifier;
1941*bdd1243dSDimitry Andric using namespace Dim;
1942*bdd1243dSDimitry Andric using namespace ImageFormat;
1943*bdd1243dSDimitry Andric #define GET_ImageTypes_DECL
1944*bdd1243dSDimitry Andric #define GET_ImageTypes_IMPL
1945*bdd1243dSDimitry Andric #define GET_PipeTypes_DECL
1946*bdd1243dSDimitry Andric #define GET_PipeTypes_IMPL
1947*bdd1243dSDimitry Andric #include "SPIRVGenTables.inc"
1948*bdd1243dSDimitry Andric } // namespace SPIRV
1949*bdd1243dSDimitry Andric 
1950*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
1951*bdd1243dSDimitry Andric // Misc functions for parsing builtin types and looking up implementation
1952*bdd1243dSDimitry Andric // details in TableGenerated tables.
1953*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
1954*bdd1243dSDimitry Andric 
1955*bdd1243dSDimitry Andric static const SPIRV::DemangledType *findBuiltinType(StringRef Name) {
1956*bdd1243dSDimitry Andric   if (Name.startswith("opencl."))
1957*bdd1243dSDimitry Andric     return SPIRV::lookupBuiltinType(Name);
1958*bdd1243dSDimitry Andric   if (!Name.startswith("spirv."))
1959*bdd1243dSDimitry Andric     return nullptr;
1960*bdd1243dSDimitry Andric   // Some SPIR-V builtin types have a complex list of parameters as part of
1961*bdd1243dSDimitry Andric   // their name (e.g. spirv.Image._void_1_0_0_0_0_0_0). Those parameters often
1962*bdd1243dSDimitry Andric   // are numeric literals which cannot be easily represented by TableGen
1963*bdd1243dSDimitry Andric   // records and should be parsed instead.
1964*bdd1243dSDimitry Andric   unsigned BaseTypeNameLength =
1965*bdd1243dSDimitry Andric       Name.contains('_') ? Name.find('_') - 1 : Name.size();
1966*bdd1243dSDimitry Andric   return SPIRV::lookupBuiltinType(Name.substr(0, BaseTypeNameLength).str());
1967*bdd1243dSDimitry Andric }
1968*bdd1243dSDimitry Andric 
1969*bdd1243dSDimitry Andric static std::unique_ptr<const SPIRV::ImageType>
1970*bdd1243dSDimitry Andric lookupOrParseBuiltinImageType(StringRef Name) {
1971*bdd1243dSDimitry Andric   if (Name.startswith("opencl.")) {
1972*bdd1243dSDimitry Andric     // Lookup OpenCL builtin image type lowering details in TableGen records.
1973*bdd1243dSDimitry Andric     const SPIRV::ImageType *Record = SPIRV::lookupImageType(Name);
1974*bdd1243dSDimitry Andric     return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType(*Record));
1975*bdd1243dSDimitry Andric   }
1976*bdd1243dSDimitry Andric   if (!Name.startswith("spirv."))
1977*bdd1243dSDimitry Andric     llvm_unreachable("Unknown builtin image type name/literal");
1978*bdd1243dSDimitry Andric   // Parse the literals of SPIR-V image builtin parameters. The name should
1979*bdd1243dSDimitry Andric   // have the following format:
1980*bdd1243dSDimitry Andric   // spirv.Image._Type_Dim_Depth_Arrayed_MS_Sampled_ImageFormat_AccessQualifier
1981*bdd1243dSDimitry Andric   // e.g. %spirv.Image._void_1_0_0_0_0_0_0
1982*bdd1243dSDimitry Andric   StringRef TypeParametersString = Name.substr(strlen("spirv.Image."));
1983*bdd1243dSDimitry Andric   SmallVector<StringRef> TypeParameters;
1984*bdd1243dSDimitry Andric   SplitString(TypeParametersString, TypeParameters, "_");
1985*bdd1243dSDimitry Andric   assert(TypeParameters.size() == 8 &&
1986*bdd1243dSDimitry Andric          "Wrong number of literals in SPIR-V builtin image type");
1987*bdd1243dSDimitry Andric 
1988*bdd1243dSDimitry Andric   StringRef SampledType = TypeParameters[0];
1989*bdd1243dSDimitry Andric   unsigned Dim, Depth, Arrayed, Multisampled, Sampled, Format, AccessQual;
1990*bdd1243dSDimitry Andric   bool AreParameterLiteralsValid =
1991*bdd1243dSDimitry Andric       !(TypeParameters[1].getAsInteger(10, Dim) ||
1992*bdd1243dSDimitry Andric         TypeParameters[2].getAsInteger(10, Depth) ||
1993*bdd1243dSDimitry Andric         TypeParameters[3].getAsInteger(10, Arrayed) ||
1994*bdd1243dSDimitry Andric         TypeParameters[4].getAsInteger(10, Multisampled) ||
1995*bdd1243dSDimitry Andric         TypeParameters[5].getAsInteger(10, Sampled) ||
1996*bdd1243dSDimitry Andric         TypeParameters[6].getAsInteger(10, Format) ||
1997*bdd1243dSDimitry Andric         TypeParameters[7].getAsInteger(10, AccessQual));
1998*bdd1243dSDimitry Andric   assert(AreParameterLiteralsValid &&
1999*bdd1243dSDimitry Andric          "Invalid format of SPIR-V image type parameter literals.");
2000*bdd1243dSDimitry Andric 
2001*bdd1243dSDimitry Andric   return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType{
2002*bdd1243dSDimitry Andric       Name, SampledType, SPIRV::AccessQualifier::AccessQualifier(AccessQual),
2003*bdd1243dSDimitry Andric       SPIRV::Dim::Dim(Dim), static_cast<bool>(Arrayed),
2004*bdd1243dSDimitry Andric       static_cast<bool>(Depth), static_cast<bool>(Multisampled),
2005*bdd1243dSDimitry Andric       static_cast<bool>(Sampled), SPIRV::ImageFormat::ImageFormat(Format)});
2006*bdd1243dSDimitry Andric }
2007*bdd1243dSDimitry Andric 
2008*bdd1243dSDimitry Andric static std::unique_ptr<const SPIRV::PipeType>
2009*bdd1243dSDimitry Andric lookupOrParseBuiltinPipeType(StringRef Name) {
2010*bdd1243dSDimitry Andric   if (Name.startswith("opencl.")) {
2011*bdd1243dSDimitry Andric     // Lookup OpenCL builtin pipe type lowering details in TableGen records.
2012*bdd1243dSDimitry Andric     const SPIRV::PipeType *Record = SPIRV::lookupPipeType(Name);
2013*bdd1243dSDimitry Andric     return std::unique_ptr<SPIRV::PipeType>(new SPIRV::PipeType(*Record));
2014*bdd1243dSDimitry Andric   }
2015*bdd1243dSDimitry Andric   if (!Name.startswith("spirv."))
2016*bdd1243dSDimitry Andric     llvm_unreachable("Unknown builtin pipe type name/literal");
2017*bdd1243dSDimitry Andric   // Parse the access qualifier literal in the name of the SPIR-V pipe type.
2018*bdd1243dSDimitry Andric   // The name should have the following format:
2019*bdd1243dSDimitry Andric   // spirv.Pipe._AccessQualifier
2020*bdd1243dSDimitry Andric   // e.g. %spirv.Pipe._1
2021*bdd1243dSDimitry Andric   if (Name.endswith("_0"))
2022*bdd1243dSDimitry Andric     return std::unique_ptr<SPIRV::PipeType>(
2023*bdd1243dSDimitry Andric         new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadOnly});
2024*bdd1243dSDimitry Andric   if (Name.endswith("_1"))
2025*bdd1243dSDimitry Andric     return std::unique_ptr<SPIRV::PipeType>(
2026*bdd1243dSDimitry Andric         new SPIRV::PipeType{Name, SPIRV::AccessQualifier::WriteOnly});
2027*bdd1243dSDimitry Andric   if (Name.endswith("_2"))
2028*bdd1243dSDimitry Andric     return std::unique_ptr<SPIRV::PipeType>(
2029*bdd1243dSDimitry Andric         new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadWrite});
2030*bdd1243dSDimitry Andric   llvm_unreachable("Unknown pipe type access qualifier literal");
2031*bdd1243dSDimitry Andric }
2032*bdd1243dSDimitry Andric 
2033*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
2034*bdd1243dSDimitry Andric // Implementation functions for builtin types.
2035*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
2036*bdd1243dSDimitry Andric 
2037*bdd1243dSDimitry Andric static SPIRVType *getNonParametrizedType(const StructType *OpaqueType,
2038*bdd1243dSDimitry Andric                                          const SPIRV::DemangledType *TypeRecord,
2039*bdd1243dSDimitry Andric                                          MachineIRBuilder &MIRBuilder,
2040*bdd1243dSDimitry Andric                                          SPIRVGlobalRegistry *GR) {
2041*bdd1243dSDimitry Andric   unsigned Opcode = TypeRecord->Opcode;
2042*bdd1243dSDimitry Andric   // Create or get an existing type from GlobalRegistry.
2043*bdd1243dSDimitry Andric   return GR->getOrCreateOpTypeByOpcode(OpaqueType, MIRBuilder, Opcode);
2044*bdd1243dSDimitry Andric }
2045*bdd1243dSDimitry Andric 
2046*bdd1243dSDimitry Andric static SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder,
2047*bdd1243dSDimitry Andric                                  SPIRVGlobalRegistry *GR) {
2048*bdd1243dSDimitry Andric   // Create or get an existing type from GlobalRegistry.
2049*bdd1243dSDimitry Andric   return GR->getOrCreateOpTypeSampler(MIRBuilder);
2050*bdd1243dSDimitry Andric }
2051*bdd1243dSDimitry Andric 
2052*bdd1243dSDimitry Andric static SPIRVType *getPipeType(const StructType *OpaqueType,
2053*bdd1243dSDimitry Andric                               MachineIRBuilder &MIRBuilder,
2054*bdd1243dSDimitry Andric                               SPIRVGlobalRegistry *GR) {
2055*bdd1243dSDimitry Andric   // Lookup pipe type lowering details in TableGen records or parse the
2056*bdd1243dSDimitry Andric   // name/literal for details.
2057*bdd1243dSDimitry Andric   std::unique_ptr<const SPIRV::PipeType> Record =
2058*bdd1243dSDimitry Andric       lookupOrParseBuiltinPipeType(OpaqueType->getName());
2059*bdd1243dSDimitry Andric   // Create or get an existing type from GlobalRegistry.
2060*bdd1243dSDimitry Andric   return GR->getOrCreateOpTypePipe(MIRBuilder, Record.get()->Qualifier);
2061*bdd1243dSDimitry Andric }
2062*bdd1243dSDimitry Andric 
2063*bdd1243dSDimitry Andric static SPIRVType *
2064*bdd1243dSDimitry Andric getImageType(const StructType *OpaqueType,
2065*bdd1243dSDimitry Andric              SPIRV::AccessQualifier::AccessQualifier AccessQual,
2066*bdd1243dSDimitry Andric              MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
2067*bdd1243dSDimitry Andric   // Lookup image type lowering details in TableGen records or parse the
2068*bdd1243dSDimitry Andric   // name/literal for details.
2069*bdd1243dSDimitry Andric   std::unique_ptr<const SPIRV::ImageType> Record =
2070*bdd1243dSDimitry Andric       lookupOrParseBuiltinImageType(OpaqueType->getName());
2071*bdd1243dSDimitry Andric 
2072*bdd1243dSDimitry Andric   SPIRVType *SampledType =
2073*bdd1243dSDimitry Andric       GR->getOrCreateSPIRVTypeByName(Record.get()->SampledType, MIRBuilder);
2074*bdd1243dSDimitry Andric   return GR->getOrCreateOpTypeImage(
2075*bdd1243dSDimitry Andric       MIRBuilder, SampledType, Record.get()->Dimensionality,
2076*bdd1243dSDimitry Andric       Record.get()->Depth, Record.get()->Arrayed, Record.get()->Multisampled,
2077*bdd1243dSDimitry Andric       Record.get()->Sampled, Record.get()->Format,
2078*bdd1243dSDimitry Andric       AccessQual == SPIRV::AccessQualifier::WriteOnly
2079*bdd1243dSDimitry Andric           ? SPIRV::AccessQualifier::WriteOnly
2080*bdd1243dSDimitry Andric           : Record.get()->Qualifier);
2081*bdd1243dSDimitry Andric }
2082*bdd1243dSDimitry Andric 
2083*bdd1243dSDimitry Andric static SPIRVType *getSampledImageType(const StructType *OpaqueType,
2084*bdd1243dSDimitry Andric                                       MachineIRBuilder &MIRBuilder,
2085*bdd1243dSDimitry Andric                                       SPIRVGlobalRegistry *GR) {
2086*bdd1243dSDimitry Andric   StringRef TypeParametersString =
2087*bdd1243dSDimitry Andric       OpaqueType->getName().substr(strlen("spirv.SampledImage."));
2088*bdd1243dSDimitry Andric   LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
2089*bdd1243dSDimitry Andric   Type *ImageOpaqueType = StructType::getTypeByName(
2090*bdd1243dSDimitry Andric       Context, "spirv.Image." + TypeParametersString.str());
2091*bdd1243dSDimitry Andric   SPIRVType *TargetImageType =
2092*bdd1243dSDimitry Andric       GR->getOrCreateSPIRVType(ImageOpaqueType, MIRBuilder);
2093*bdd1243dSDimitry Andric   return GR->getOrCreateOpTypeSampledImage(TargetImageType, MIRBuilder);
2094*bdd1243dSDimitry Andric }
2095*bdd1243dSDimitry Andric 
2096*bdd1243dSDimitry Andric namespace SPIRV {
2097*bdd1243dSDimitry Andric SPIRVType *lowerBuiltinType(const StructType *OpaqueType,
2098*bdd1243dSDimitry Andric                             SPIRV::AccessQualifier::AccessQualifier AccessQual,
2099*bdd1243dSDimitry Andric                             MachineIRBuilder &MIRBuilder,
2100*bdd1243dSDimitry Andric                             SPIRVGlobalRegistry *GR) {
2101*bdd1243dSDimitry Andric   assert(OpaqueType->hasName() &&
2102*bdd1243dSDimitry Andric          "Structs representing builtin types must have a parsable name");
2103*bdd1243dSDimitry Andric   unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
2104*bdd1243dSDimitry Andric 
2105*bdd1243dSDimitry Andric   const StringRef Name = OpaqueType->getName();
2106*bdd1243dSDimitry Andric   LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
2107*bdd1243dSDimitry Andric 
2108*bdd1243dSDimitry Andric   // Lookup the demangled builtin type in the TableGen records.
2109*bdd1243dSDimitry Andric   const SPIRV::DemangledType *TypeRecord = findBuiltinType(Name);
2110*bdd1243dSDimitry Andric   if (!TypeRecord)
2111*bdd1243dSDimitry Andric     report_fatal_error("Missing TableGen record for builtin type: " + Name);
2112*bdd1243dSDimitry Andric 
2113*bdd1243dSDimitry Andric   // "Lower" the BuiltinType into TargetType. The following get<...>Type methods
2114*bdd1243dSDimitry Andric   // use the implementation details from TableGen records to either create a new
2115*bdd1243dSDimitry Andric   // OpType<...> machine instruction or get an existing equivalent SPIRVType
2116*bdd1243dSDimitry Andric   // from GlobalRegistry.
2117*bdd1243dSDimitry Andric   SPIRVType *TargetType;
2118*bdd1243dSDimitry Andric   switch (TypeRecord->Opcode) {
2119*bdd1243dSDimitry Andric   case SPIRV::OpTypeImage:
2120*bdd1243dSDimitry Andric     TargetType = getImageType(OpaqueType, AccessQual, MIRBuilder, GR);
2121*bdd1243dSDimitry Andric     break;
2122*bdd1243dSDimitry Andric   case SPIRV::OpTypePipe:
2123*bdd1243dSDimitry Andric     TargetType = getPipeType(OpaqueType, MIRBuilder, GR);
2124*bdd1243dSDimitry Andric     break;
2125*bdd1243dSDimitry Andric   case SPIRV::OpTypeDeviceEvent:
2126*bdd1243dSDimitry Andric     TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
2127*bdd1243dSDimitry Andric     break;
2128*bdd1243dSDimitry Andric   case SPIRV::OpTypeSampler:
2129*bdd1243dSDimitry Andric     TargetType = getSamplerType(MIRBuilder, GR);
2130*bdd1243dSDimitry Andric     break;
2131*bdd1243dSDimitry Andric   case SPIRV::OpTypeSampledImage:
2132*bdd1243dSDimitry Andric     TargetType = getSampledImageType(OpaqueType, MIRBuilder, GR);
2133*bdd1243dSDimitry Andric     break;
2134*bdd1243dSDimitry Andric   default:
2135*bdd1243dSDimitry Andric     TargetType = getNonParametrizedType(OpaqueType, TypeRecord, MIRBuilder, GR);
2136*bdd1243dSDimitry Andric     break;
2137*bdd1243dSDimitry Andric   }
2138*bdd1243dSDimitry Andric 
2139*bdd1243dSDimitry Andric   // Emit OpName instruction if a new OpType<...> instruction was added
2140*bdd1243dSDimitry Andric   // (equivalent type was not found in GlobalRegistry).
2141*bdd1243dSDimitry Andric   if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
2142*bdd1243dSDimitry Andric     buildOpName(GR->getSPIRVTypeID(TargetType), OpaqueType->getName(),
2143*bdd1243dSDimitry Andric                 MIRBuilder);
2144*bdd1243dSDimitry Andric 
2145*bdd1243dSDimitry Andric   return TargetType;
2146*bdd1243dSDimitry Andric }
2147*bdd1243dSDimitry Andric } // namespace SPIRV
2148*bdd1243dSDimitry Andric } // namespace llvm
2149