xref: /llvm-project/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp (revision d459784cbea334d167b2dca48e0c26115c68e5d3)
1f61eb416SIlia Diachkov //===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===//
2f61eb416SIlia Diachkov //
3f61eb416SIlia Diachkov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f61eb416SIlia Diachkov // See https://llvm.org/LICENSE.txt for license information.
5f61eb416SIlia Diachkov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f61eb416SIlia Diachkov //
7f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
8f61eb416SIlia Diachkov //
9f61eb416SIlia Diachkov // This file implements lowering builtin function calls and types using their
10f61eb416SIlia Diachkov // demangled names and TableGen records.
11f61eb416SIlia Diachkov //
12f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
13f61eb416SIlia Diachkov 
14f61eb416SIlia Diachkov #include "SPIRVBuiltins.h"
15f61eb416SIlia Diachkov #include "SPIRV.h"
16b221b973SVyacheslav Levytskyy #include "SPIRVSubtarget.h"
17f61eb416SIlia Diachkov #include "SPIRVUtils.h"
18a48f32d2SFangrui Song #include "llvm/ADT/StringExtras.h"
19748922b3SIlia Diachkov #include "llvm/Analysis/ValueTracking.h"
20f61eb416SIlia Diachkov #include "llvm/IR/IntrinsicsSPIRV.h"
219b43078eSVyacheslav Levytskyy #include <regex>
22f61eb416SIlia Diachkov #include <string>
23f61eb416SIlia Diachkov #include <tuple>
24f61eb416SIlia Diachkov 
25f61eb416SIlia Diachkov #define DEBUG_TYPE "spirv-builtins"
26f61eb416SIlia Diachkov 
27f61eb416SIlia Diachkov namespace llvm {
28f61eb416SIlia Diachkov namespace SPIRV {
29f61eb416SIlia Diachkov #define GET_BuiltinGroup_DECL
30f61eb416SIlia Diachkov #include "SPIRVGenTables.inc"
31f61eb416SIlia Diachkov 
32f61eb416SIlia Diachkov struct DemangledBuiltin {
33f61eb416SIlia Diachkov   StringRef Name;
34f61eb416SIlia Diachkov   InstructionSet::InstructionSet Set;
35f61eb416SIlia Diachkov   BuiltinGroup Group;
36f61eb416SIlia Diachkov   uint8_t MinNumArgs;
37f61eb416SIlia Diachkov   uint8_t MaxNumArgs;
38f61eb416SIlia Diachkov };
39f61eb416SIlia Diachkov 
40f61eb416SIlia Diachkov #define GET_DemangledBuiltins_DECL
41f61eb416SIlia Diachkov #define GET_DemangledBuiltins_IMPL
42f61eb416SIlia Diachkov 
43f61eb416SIlia Diachkov struct IncomingCall {
44f61eb416SIlia Diachkov   const std::string BuiltinName;
45f61eb416SIlia Diachkov   const DemangledBuiltin *Builtin;
46f61eb416SIlia Diachkov 
47f61eb416SIlia Diachkov   const Register ReturnRegister;
48f61eb416SIlia Diachkov   const SPIRVType *ReturnType;
49f61eb416SIlia Diachkov   const SmallVectorImpl<Register> &Arguments;
50f61eb416SIlia Diachkov 
51f61eb416SIlia Diachkov   IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin,
52f61eb416SIlia Diachkov                const Register ReturnRegister, const SPIRVType *ReturnType,
53f61eb416SIlia Diachkov                const SmallVectorImpl<Register> &Arguments)
54f61eb416SIlia Diachkov       : BuiltinName(BuiltinName), Builtin(Builtin),
55f61eb416SIlia Diachkov         ReturnRegister(ReturnRegister), ReturnType(ReturnType),
56f61eb416SIlia Diachkov         Arguments(Arguments) {}
57c2483ed5SVyacheslav Levytskyy 
58c2483ed5SVyacheslav Levytskyy   bool isSpirvOp() const { return BuiltinName.rfind("__spirv_", 0) == 0; }
59f61eb416SIlia Diachkov };
60f61eb416SIlia Diachkov 
61f61eb416SIlia Diachkov struct NativeBuiltin {
62f61eb416SIlia Diachkov   StringRef Name;
63f61eb416SIlia Diachkov   InstructionSet::InstructionSet Set;
64f61eb416SIlia Diachkov   uint32_t Opcode;
65f61eb416SIlia Diachkov };
66f61eb416SIlia Diachkov 
67f61eb416SIlia Diachkov #define GET_NativeBuiltins_DECL
68f61eb416SIlia Diachkov #define GET_NativeBuiltins_IMPL
69f61eb416SIlia Diachkov 
70f61eb416SIlia Diachkov struct GroupBuiltin {
71f61eb416SIlia Diachkov   StringRef Name;
72f61eb416SIlia Diachkov   uint32_t Opcode;
73f61eb416SIlia Diachkov   uint32_t GroupOperation;
74f61eb416SIlia Diachkov   bool IsElect;
75f61eb416SIlia Diachkov   bool IsAllOrAny;
76f61eb416SIlia Diachkov   bool IsAllEqual;
77f61eb416SIlia Diachkov   bool IsBallot;
78f61eb416SIlia Diachkov   bool IsInverseBallot;
79f61eb416SIlia Diachkov   bool IsBallotBitExtract;
80f61eb416SIlia Diachkov   bool IsBallotFindBit;
81f61eb416SIlia Diachkov   bool IsLogical;
82f61eb416SIlia Diachkov   bool NoGroupOperation;
83f61eb416SIlia Diachkov   bool HasBoolArg;
84f61eb416SIlia Diachkov };
85f61eb416SIlia Diachkov 
86f61eb416SIlia Diachkov #define GET_GroupBuiltins_DECL
87f61eb416SIlia Diachkov #define GET_GroupBuiltins_IMPL
88f61eb416SIlia Diachkov 
89b221b973SVyacheslav Levytskyy struct IntelSubgroupsBuiltin {
90b221b973SVyacheslav Levytskyy   StringRef Name;
91b221b973SVyacheslav Levytskyy   uint32_t Opcode;
92b221b973SVyacheslav Levytskyy   bool IsBlock;
93b221b973SVyacheslav Levytskyy   bool IsWrite;
944a6ecd38SViktoria Maximova   bool IsMedia;
95b221b973SVyacheslav Levytskyy };
96b221b973SVyacheslav Levytskyy 
97b221b973SVyacheslav Levytskyy #define GET_IntelSubgroupsBuiltins_DECL
98b221b973SVyacheslav Levytskyy #define GET_IntelSubgroupsBuiltins_IMPL
99b221b973SVyacheslav Levytskyy 
100925768eeSVyacheslav Levytskyy struct AtomicFloatingBuiltin {
101925768eeSVyacheslav Levytskyy   StringRef Name;
102925768eeSVyacheslav Levytskyy   uint32_t Opcode;
103925768eeSVyacheslav Levytskyy };
104925768eeSVyacheslav Levytskyy 
105925768eeSVyacheslav Levytskyy #define GET_AtomicFloatingBuiltins_DECL
106925768eeSVyacheslav Levytskyy #define GET_AtomicFloatingBuiltins_IMPL
10766ebda46SVyacheslav Levytskyy struct GroupUniformBuiltin {
10866ebda46SVyacheslav Levytskyy   StringRef Name;
10966ebda46SVyacheslav Levytskyy   uint32_t Opcode;
11066ebda46SVyacheslav Levytskyy   bool IsLogical;
11166ebda46SVyacheslav Levytskyy };
11266ebda46SVyacheslav Levytskyy 
11366ebda46SVyacheslav Levytskyy #define GET_GroupUniformBuiltins_DECL
11466ebda46SVyacheslav Levytskyy #define GET_GroupUniformBuiltins_IMPL
115925768eeSVyacheslav Levytskyy 
116f61eb416SIlia Diachkov struct GetBuiltin {
117f61eb416SIlia Diachkov   StringRef Name;
118f61eb416SIlia Diachkov   InstructionSet::InstructionSet Set;
119f61eb416SIlia Diachkov   BuiltIn::BuiltIn Value;
120f61eb416SIlia Diachkov };
121f61eb416SIlia Diachkov 
122f61eb416SIlia Diachkov using namespace BuiltIn;
123f61eb416SIlia Diachkov #define GET_GetBuiltins_DECL
124f61eb416SIlia Diachkov #define GET_GetBuiltins_IMPL
125f61eb416SIlia Diachkov 
126f61eb416SIlia Diachkov struct ImageQueryBuiltin {
127f61eb416SIlia Diachkov   StringRef Name;
128f61eb416SIlia Diachkov   InstructionSet::InstructionSet Set;
129f61eb416SIlia Diachkov   uint32_t Component;
130f61eb416SIlia Diachkov };
131f61eb416SIlia Diachkov 
132f61eb416SIlia Diachkov #define GET_ImageQueryBuiltins_DECL
133f61eb416SIlia Diachkov #define GET_ImageQueryBuiltins_IMPL
134f61eb416SIlia Diachkov 
135f61eb416SIlia Diachkov struct ConvertBuiltin {
136f61eb416SIlia Diachkov   StringRef Name;
137f61eb416SIlia Diachkov   InstructionSet::InstructionSet Set;
138f61eb416SIlia Diachkov   bool IsDestinationSigned;
139f61eb416SIlia Diachkov   bool IsSaturated;
140f61eb416SIlia Diachkov   bool IsRounded;
1418f30b623SVyacheslav Levytskyy   bool IsBfloat16;
142f61eb416SIlia Diachkov   FPRoundingMode::FPRoundingMode RoundingMode;
143f61eb416SIlia Diachkov };
144f61eb416SIlia Diachkov 
145f61eb416SIlia Diachkov struct VectorLoadStoreBuiltin {
146f61eb416SIlia Diachkov   StringRef Name;
147f61eb416SIlia Diachkov   InstructionSet::InstructionSet Set;
148f61eb416SIlia Diachkov   uint32_t Number;
14903203b79SMichal Paszkowski   uint32_t ElementCount;
150f61eb416SIlia Diachkov   bool IsRounded;
151f61eb416SIlia Diachkov   FPRoundingMode::FPRoundingMode RoundingMode;
152f61eb416SIlia Diachkov };
153f61eb416SIlia Diachkov 
154f61eb416SIlia Diachkov using namespace FPRoundingMode;
155f61eb416SIlia Diachkov #define GET_ConvertBuiltins_DECL
156f61eb416SIlia Diachkov #define GET_ConvertBuiltins_IMPL
157f61eb416SIlia Diachkov 
158f61eb416SIlia Diachkov using namespace InstructionSet;
159f61eb416SIlia Diachkov #define GET_VectorLoadStoreBuiltins_DECL
160f61eb416SIlia Diachkov #define GET_VectorLoadStoreBuiltins_IMPL
161f61eb416SIlia Diachkov 
162f61eb416SIlia Diachkov #define GET_CLMemoryScope_DECL
163f61eb416SIlia Diachkov #define GET_CLSamplerAddressingMode_DECL
164f61eb416SIlia Diachkov #define GET_CLMemoryFenceFlags_DECL
165f61eb416SIlia Diachkov #define GET_ExtendedBuiltins_DECL
166f61eb416SIlia Diachkov #include "SPIRVGenTables.inc"
167f61eb416SIlia Diachkov } // namespace SPIRV
168f61eb416SIlia Diachkov 
169f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
170f61eb416SIlia Diachkov // Misc functions for looking up builtins and veryfying requirements using
171f61eb416SIlia Diachkov // TableGen records
172f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
173f61eb416SIlia Diachkov 
174dbd00a59SVyacheslav Levytskyy namespace SPIRV {
175dbd00a59SVyacheslav Levytskyy /// Parses the name part of the demangled builtin call.
176978de2d6SVyacheslav Levytskyy std::string lookupBuiltinNameHelper(StringRef DemangledCall,
1773ed2a813SVyacheslav Levytskyy                                     FPDecorationId *DecorationId) {
178bf9e9e5eSVyacheslav Levytskyy   const static std::string PassPrefix = "(anonymous namespace)::";
179bf9e9e5eSVyacheslav Levytskyy   std::string BuiltinName;
180bf9e9e5eSVyacheslav Levytskyy   // Itanium Demangler result may have "(anonymous namespace)::" prefix
181bf9e9e5eSVyacheslav Levytskyy   if (DemangledCall.starts_with(PassPrefix.c_str()))
182bf9e9e5eSVyacheslav Levytskyy     BuiltinName = DemangledCall.substr(PassPrefix.length());
183bf9e9e5eSVyacheslav Levytskyy   else
184bf9e9e5eSVyacheslav Levytskyy     BuiltinName = DemangledCall;
185f61eb416SIlia Diachkov   // Extract the builtin function name and types of arguments from the call
186f61eb416SIlia Diachkov   // skeleton.
187bf9e9e5eSVyacheslav Levytskyy   BuiltinName = BuiltinName.substr(0, BuiltinName.find('('));
188f61eb416SIlia Diachkov 
1891ed1ec9aSVyacheslav Levytskyy   // Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR
1901ed1ec9aSVyacheslav Levytskyy   if (BuiltinName.rfind("__spirv_ocl_", 0) == 0)
1911ed1ec9aSVyacheslav Levytskyy     BuiltinName = BuiltinName.substr(12);
1921ed1ec9aSVyacheslav Levytskyy 
193f61eb416SIlia Diachkov   // Check if the extracted name contains type information between angle
194f61eb416SIlia Diachkov   // brackets. If so, the builtin is an instantiated template - needs to have
195f61eb416SIlia Diachkov   // the information after angle brackets and return type removed.
1968ac46d6bSVyacheslav Levytskyy   std::size_t Pos1 = BuiltinName.rfind('<');
1978ac46d6bSVyacheslav Levytskyy   if (Pos1 != std::string::npos && BuiltinName.back() == '>') {
1988ac46d6bSVyacheslav Levytskyy     std::size_t Pos2 = BuiltinName.rfind(' ', Pos1);
1998ac46d6bSVyacheslav Levytskyy     if (Pos2 == std::string::npos)
2008ac46d6bSVyacheslav Levytskyy       Pos2 = 0;
2018ac46d6bSVyacheslav Levytskyy     else
2028ac46d6bSVyacheslav Levytskyy       ++Pos2;
2038ac46d6bSVyacheslav Levytskyy     BuiltinName = BuiltinName.substr(Pos2, Pos1 - Pos2);
20433b51588Sserge-sans-paille     BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(' ') + 1);
205f61eb416SIlia Diachkov   }
206f61eb416SIlia Diachkov 
2079b43078eSVyacheslav Levytskyy   // Check if the extracted name begins with:
2089b43078eSVyacheslav Levytskyy   // - "__spirv_ImageSampleExplicitLod"
2099b43078eSVyacheslav Levytskyy   // - "__spirv_ImageRead"
2109b43078eSVyacheslav Levytskyy   // - "__spirv_ImageQuerySizeLod"
2119b43078eSVyacheslav Levytskyy   // - "__spirv_UDotKHR"
2129b43078eSVyacheslav Levytskyy   // - "__spirv_SDotKHR"
2139b43078eSVyacheslav Levytskyy   // - "__spirv_SUDotKHR"
2149b43078eSVyacheslav Levytskyy   // - "__spirv_SDotAccSatKHR"
2159b43078eSVyacheslav Levytskyy   // - "__spirv_UDotAccSatKHR"
2169b43078eSVyacheslav Levytskyy   // - "__spirv_SUDotAccSatKHR"
2179b43078eSVyacheslav Levytskyy   // - "__spirv_ReadClockKHR"
2189b43078eSVyacheslav Levytskyy   // - "__spirv_SubgroupBlockReadINTEL"
2199b43078eSVyacheslav Levytskyy   // - "__spirv_SubgroupImageBlockReadINTEL"
2204a6ecd38SViktoria Maximova   // - "__spirv_SubgroupImageMediaBlockReadINTEL"
2214a6ecd38SViktoria Maximova   // - "__spirv_SubgroupImageMediaBlockWriteINTEL"
2229b43078eSVyacheslav Levytskyy   // - "__spirv_Convert"
2239b43078eSVyacheslav Levytskyy   // - "__spirv_UConvert"
2249b43078eSVyacheslav Levytskyy   // - "__spirv_SConvert"
2259b43078eSVyacheslav Levytskyy   // - "__spirv_FConvert"
2269b43078eSVyacheslav Levytskyy   // - "__spirv_SatConvert"
2279b43078eSVyacheslav Levytskyy   // and contains return type information at the end "_R<type>".
2289b43078eSVyacheslav Levytskyy   // If so, extract the plain builtin name without the type information.
2299b43078eSVyacheslav Levytskyy   static const std::regex SpvWithR(
2309b43078eSVyacheslav Levytskyy       "(__spirv_(ImageSampleExplicitLod|ImageRead|ImageQuerySizeLod|UDotKHR|"
2319b43078eSVyacheslav Levytskyy       "SDotKHR|SUDotKHR|SDotAccSatKHR|UDotAccSatKHR|SUDotAccSatKHR|"
2324a6ecd38SViktoria Maximova       "ReadClockKHR|SubgroupBlockReadINTEL|SubgroupImageBlockReadINTEL|"
2334a6ecd38SViktoria Maximova       "SubgroupImageMediaBlockReadINTEL|SubgroupImageMediaBlockWriteINTEL|"
2344a6ecd38SViktoria Maximova       "Convert|"
2353ed2a813SVyacheslav Levytskyy       "UConvert|SConvert|FConvert|SatConvert).*)_R[^_]*_?(\\w+)?.*");
2369b43078eSVyacheslav Levytskyy   std::smatch Match;
2373ed2a813SVyacheslav Levytskyy   if (std::regex_match(BuiltinName, Match, SpvWithR) && Match.size() > 1) {
2383ed2a813SVyacheslav Levytskyy     std::ssub_match SubMatch;
2393ed2a813SVyacheslav Levytskyy     if (DecorationId && Match.size() > 3) {
2403ed2a813SVyacheslav Levytskyy       SubMatch = Match[3];
2413ed2a813SVyacheslav Levytskyy       *DecorationId = demangledPostfixToDecorationId(SubMatch.str());
2423ed2a813SVyacheslav Levytskyy     }
2433ed2a813SVyacheslav Levytskyy     SubMatch = Match[1];
2443ed2a813SVyacheslav Levytskyy     BuiltinName = SubMatch.str();
245978de2d6SVyacheslav Levytskyy   }
246f61eb416SIlia Diachkov 
247dbd00a59SVyacheslav Levytskyy   return BuiltinName;
248dbd00a59SVyacheslav Levytskyy }
249dbd00a59SVyacheslav Levytskyy } // namespace SPIRV
250dbd00a59SVyacheslav Levytskyy 
251dbd00a59SVyacheslav Levytskyy /// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
252dbd00a59SVyacheslav Levytskyy /// the provided \p DemangledCall and specified \p Set.
253dbd00a59SVyacheslav Levytskyy ///
254dbd00a59SVyacheslav Levytskyy /// The lookup follows the following algorithm, returning the first successful
255dbd00a59SVyacheslav Levytskyy /// match:
256dbd00a59SVyacheslav Levytskyy /// 1. Search with the plain demangled name (expecting a 1:1 match).
257dbd00a59SVyacheslav Levytskyy /// 2. Search with the prefix before or suffix after the demangled name
258dbd00a59SVyacheslav Levytskyy /// signyfying the type of the first argument.
259dbd00a59SVyacheslav Levytskyy ///
260dbd00a59SVyacheslav Levytskyy /// \returns Wrapper around the demangled call and found builtin definition.
261dbd00a59SVyacheslav Levytskyy static std::unique_ptr<const SPIRV::IncomingCall>
262dbd00a59SVyacheslav Levytskyy lookupBuiltin(StringRef DemangledCall,
263dbd00a59SVyacheslav Levytskyy               SPIRV::InstructionSet::InstructionSet Set,
264dbd00a59SVyacheslav Levytskyy               Register ReturnRegister, const SPIRVType *ReturnType,
265dbd00a59SVyacheslav Levytskyy               const SmallVectorImpl<Register> &Arguments) {
266dbd00a59SVyacheslav Levytskyy   std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall);
267dbd00a59SVyacheslav Levytskyy 
268f61eb416SIlia Diachkov   SmallVector<StringRef, 10> BuiltinArgumentTypes;
269f61eb416SIlia Diachkov   StringRef BuiltinArgs =
270f61eb416SIlia Diachkov       DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
271f61eb416SIlia Diachkov   BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false);
272f61eb416SIlia Diachkov 
273f61eb416SIlia Diachkov   // Look up the builtin in the defined set. Start with the plain demangled
274f61eb416SIlia Diachkov   // name, expecting a 1:1 match in the defined builtin set.
275f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin;
276f61eb416SIlia Diachkov   if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set)))
277f61eb416SIlia Diachkov     return std::make_unique<SPIRV::IncomingCall>(
278f61eb416SIlia Diachkov         BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
279f61eb416SIlia Diachkov 
280f61eb416SIlia Diachkov   // If the initial look up was unsuccessful and the demangled call takes at
281f61eb416SIlia Diachkov   // least 1 argument, add a prefix or suffix signifying the type of the first
282f61eb416SIlia Diachkov   // argument and repeat the search.
283f61eb416SIlia Diachkov   if (BuiltinArgumentTypes.size() >= 1) {
284f61eb416SIlia Diachkov     char FirstArgumentType = BuiltinArgumentTypes[0][0];
285f61eb416SIlia Diachkov     // Prefix to be added to the builtin's name for lookup.
286f61eb416SIlia Diachkov     // For example, OpenCL "abs" taking an unsigned value has a prefix "u_".
287f61eb416SIlia Diachkov     std::string Prefix;
288f61eb416SIlia Diachkov 
289f61eb416SIlia Diachkov     switch (FirstArgumentType) {
290f61eb416SIlia Diachkov     // Unsigned:
291f61eb416SIlia Diachkov     case 'u':
292f61eb416SIlia Diachkov       if (Set == SPIRV::InstructionSet::OpenCL_std)
293f61eb416SIlia Diachkov         Prefix = "u_";
294f61eb416SIlia Diachkov       else if (Set == SPIRV::InstructionSet::GLSL_std_450)
295f61eb416SIlia Diachkov         Prefix = "u";
296f61eb416SIlia Diachkov       break;
297f61eb416SIlia Diachkov     // Signed:
298f61eb416SIlia Diachkov     case 'c':
299f61eb416SIlia Diachkov     case 's':
300f61eb416SIlia Diachkov     case 'i':
301f61eb416SIlia Diachkov     case 'l':
302f61eb416SIlia Diachkov       if (Set == SPIRV::InstructionSet::OpenCL_std)
303f61eb416SIlia Diachkov         Prefix = "s_";
304f61eb416SIlia Diachkov       else if (Set == SPIRV::InstructionSet::GLSL_std_450)
305f61eb416SIlia Diachkov         Prefix = "s";
306f61eb416SIlia Diachkov       break;
307f61eb416SIlia Diachkov     // Floating-point:
308f61eb416SIlia Diachkov     case 'f':
309f61eb416SIlia Diachkov     case 'd':
310f61eb416SIlia Diachkov     case 'h':
311f61eb416SIlia Diachkov       if (Set == SPIRV::InstructionSet::OpenCL_std ||
312f61eb416SIlia Diachkov           Set == SPIRV::InstructionSet::GLSL_std_450)
313f61eb416SIlia Diachkov         Prefix = "f";
314f61eb416SIlia Diachkov       break;
315f61eb416SIlia Diachkov     }
316f61eb416SIlia Diachkov 
317f61eb416SIlia Diachkov     // If argument-type name prefix was added, look up the builtin again.
318f61eb416SIlia Diachkov     if (!Prefix.empty() &&
319f61eb416SIlia Diachkov         (Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set)))
320f61eb416SIlia Diachkov       return std::make_unique<SPIRV::IncomingCall>(
321f61eb416SIlia Diachkov           BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
322f61eb416SIlia Diachkov 
323f61eb416SIlia Diachkov     // If lookup with a prefix failed, find a suffix to be added to the
324f61eb416SIlia Diachkov     // builtin's name for lookup. For example, OpenCL "group_reduce_max" taking
325f61eb416SIlia Diachkov     // an unsigned value has a suffix "u".
326f61eb416SIlia Diachkov     std::string Suffix;
327f61eb416SIlia Diachkov 
328f61eb416SIlia Diachkov     switch (FirstArgumentType) {
329f61eb416SIlia Diachkov     // Unsigned:
330f61eb416SIlia Diachkov     case 'u':
331f61eb416SIlia Diachkov       Suffix = "u";
332f61eb416SIlia Diachkov       break;
333f61eb416SIlia Diachkov     // Signed:
334f61eb416SIlia Diachkov     case 'c':
335f61eb416SIlia Diachkov     case 's':
336f61eb416SIlia Diachkov     case 'i':
337f61eb416SIlia Diachkov     case 'l':
338f61eb416SIlia Diachkov       Suffix = "s";
339f61eb416SIlia Diachkov       break;
340f61eb416SIlia Diachkov     // Floating-point:
341f61eb416SIlia Diachkov     case 'f':
342f61eb416SIlia Diachkov     case 'd':
343f61eb416SIlia Diachkov     case 'h':
344f61eb416SIlia Diachkov       Suffix = "f";
345f61eb416SIlia Diachkov       break;
346f61eb416SIlia Diachkov     }
347f61eb416SIlia Diachkov 
348f61eb416SIlia Diachkov     // If argument-type name suffix was added, look up the builtin again.
349f61eb416SIlia Diachkov     if (!Suffix.empty() &&
350f61eb416SIlia Diachkov         (Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set)))
351f61eb416SIlia Diachkov       return std::make_unique<SPIRV::IncomingCall>(
352f61eb416SIlia Diachkov           BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
353f61eb416SIlia Diachkov   }
354f61eb416SIlia Diachkov 
355f61eb416SIlia Diachkov   // No builtin with such name was found in the set.
356f61eb416SIlia Diachkov   return nullptr;
357f61eb416SIlia Diachkov }
358f61eb416SIlia Diachkov 
35957520985SVyacheslav Levytskyy static MachineInstr *getBlockStructInstr(Register ParamReg,
36057520985SVyacheslav Levytskyy                                          MachineRegisterInfo *MRI) {
36157520985SVyacheslav Levytskyy   // We expect the following sequence of instructions:
36257520985SVyacheslav Levytskyy   //   %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)
36357520985SVyacheslav Levytskyy   //   or       = G_GLOBAL_VALUE @block_literal_global
36457520985SVyacheslav Levytskyy   //   %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0
36557520985SVyacheslav Levytskyy   //   %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)
36657520985SVyacheslav Levytskyy   MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
36757520985SVyacheslav Levytskyy   assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
36857520985SVyacheslav Levytskyy          MI->getOperand(1).isReg());
36957520985SVyacheslav Levytskyy   Register BitcastReg = MI->getOperand(1).getReg();
37057520985SVyacheslav Levytskyy   MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
37157520985SVyacheslav Levytskyy   assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&
37257520985SVyacheslav Levytskyy          BitcastMI->getOperand(2).isReg());
37357520985SVyacheslav Levytskyy   Register ValueReg = BitcastMI->getOperand(2).getReg();
37457520985SVyacheslav Levytskyy   MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);
37557520985SVyacheslav Levytskyy   return ValueMI;
37657520985SVyacheslav Levytskyy }
37757520985SVyacheslav Levytskyy 
37857520985SVyacheslav Levytskyy // Return an integer constant corresponding to the given register and
37957520985SVyacheslav Levytskyy // defined in spv_track_constant.
38057520985SVyacheslav Levytskyy // TODO: maybe unify with prelegalizer pass.
38157520985SVyacheslav Levytskyy static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) {
38257520985SVyacheslav Levytskyy   MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);
38357520985SVyacheslav Levytskyy   assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&
38457520985SVyacheslav Levytskyy          DefMI->getOperand(2).isReg());
38557520985SVyacheslav Levytskyy   MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());
38657520985SVyacheslav Levytskyy   assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&
38757520985SVyacheslav Levytskyy          DefMI2->getOperand(1).isCImm());
38857520985SVyacheslav Levytskyy   return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();
38957520985SVyacheslav Levytskyy }
39057520985SVyacheslav Levytskyy 
39157520985SVyacheslav Levytskyy // Return type of the instruction result from spv_assign_type intrinsic.
39257520985SVyacheslav Levytskyy // TODO: maybe unify with prelegalizer pass.
39357520985SVyacheslav Levytskyy static const Type *getMachineInstrType(MachineInstr *MI) {
39457520985SVyacheslav Levytskyy   MachineInstr *NextMI = MI->getNextNode();
39557520985SVyacheslav Levytskyy   if (!NextMI)
39657520985SVyacheslav Levytskyy     return nullptr;
39757520985SVyacheslav Levytskyy   if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
39857520985SVyacheslav Levytskyy     if ((NextMI = NextMI->getNextNode()) == nullptr)
39957520985SVyacheslav Levytskyy       return nullptr;
40057520985SVyacheslav Levytskyy   Register ValueReg = MI->getOperand(0).getReg();
40157520985SVyacheslav Levytskyy   if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) &&
40257520985SVyacheslav Levytskyy        !isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) ||
40357520985SVyacheslav Levytskyy       NextMI->getOperand(1).getReg() != ValueReg)
40457520985SVyacheslav Levytskyy     return nullptr;
40557520985SVyacheslav Levytskyy   Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
40657520985SVyacheslav Levytskyy   assert(Ty && "Type is expected");
40757520985SVyacheslav Levytskyy   return Ty;
40857520985SVyacheslav Levytskyy }
40957520985SVyacheslav Levytskyy 
41057520985SVyacheslav Levytskyy static const Type *getBlockStructType(Register ParamReg,
41157520985SVyacheslav Levytskyy                                       MachineRegisterInfo *MRI) {
41257520985SVyacheslav Levytskyy   // In principle, this information should be passed to us from Clang via
41357520985SVyacheslav Levytskyy   // an elementtype attribute. However, said attribute requires that
41457520985SVyacheslav Levytskyy   // the function call be an intrinsic, which is not. Instead, we rely on being
41557520985SVyacheslav Levytskyy   // able to trace this to the declaration of a variable: OpenCL C specification
41657520985SVyacheslav Levytskyy   // section 6.12.5 should guarantee that we can do this.
41757520985SVyacheslav Levytskyy   MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);
41857520985SVyacheslav Levytskyy   if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
41957520985SVyacheslav Levytskyy     return MI->getOperand(1).getGlobal()->getType();
42057520985SVyacheslav Levytskyy   assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
42157520985SVyacheslav Levytskyy          "Blocks in OpenCL C must be traceable to allocation site");
42257520985SVyacheslav Levytskyy   return getMachineInstrType(MI);
42357520985SVyacheslav Levytskyy }
42457520985SVyacheslav Levytskyy 
425f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
426f61eb416SIlia Diachkov // Helper functions for building misc instructions
427f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
428f61eb416SIlia Diachkov 
429f61eb416SIlia Diachkov /// Helper function building either a resulting scalar or vector bool register
430f61eb416SIlia Diachkov /// depending on the expected \p ResultType.
431f61eb416SIlia Diachkov ///
432f61eb416SIlia Diachkov /// \returns Tuple of the resulting register and its type.
433f61eb416SIlia Diachkov static std::tuple<Register, SPIRVType *>
434f61eb416SIlia Diachkov buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType,
435f61eb416SIlia Diachkov                   SPIRVGlobalRegistry *GR) {
436f61eb416SIlia Diachkov   LLT Type;
437f61eb416SIlia Diachkov   SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
438f61eb416SIlia Diachkov 
439f61eb416SIlia Diachkov   if (ResultType->getOpcode() == SPIRV::OpTypeVector) {
440f61eb416SIlia Diachkov     unsigned VectorElements = ResultType->getOperand(2).getImm();
441f61eb416SIlia Diachkov     BoolType =
442f61eb416SIlia Diachkov         GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder);
443f61eb416SIlia Diachkov     const FixedVectorType *LLVMVectorType =
444f61eb416SIlia Diachkov         cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType));
445f61eb416SIlia Diachkov     Type = LLT::vector(LLVMVectorType->getElementCount(), 1);
446f61eb416SIlia Diachkov   } else {
447f61eb416SIlia Diachkov     Type = LLT::scalar(1);
448f61eb416SIlia Diachkov   }
449f61eb416SIlia Diachkov 
450f61eb416SIlia Diachkov   Register ResultRegister =
451f61eb416SIlia Diachkov       MIRBuilder.getMRI()->createGenericVirtualRegister(Type);
45267d3ef74SVyacheslav Levytskyy   MIRBuilder.getMRI()->setRegClass(ResultRegister, GR->getRegClass(ResultType));
453f61eb416SIlia Diachkov   GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF());
454f61eb416SIlia Diachkov   return std::make_tuple(ResultRegister, BoolType);
455f61eb416SIlia Diachkov }
456f61eb416SIlia Diachkov 
457f61eb416SIlia Diachkov /// Helper function for building either a vector or scalar select instruction
458f61eb416SIlia Diachkov /// depending on the expected \p ResultType.
459f61eb416SIlia Diachkov static bool buildSelectInst(MachineIRBuilder &MIRBuilder,
460f61eb416SIlia Diachkov                             Register ReturnRegister, Register SourceRegister,
461f61eb416SIlia Diachkov                             const SPIRVType *ReturnType,
462f61eb416SIlia Diachkov                             SPIRVGlobalRegistry *GR) {
463f61eb416SIlia Diachkov   Register TrueConst, FalseConst;
464f61eb416SIlia Diachkov 
465f61eb416SIlia Diachkov   if (ReturnType->getOpcode() == SPIRV::OpTypeVector) {
466f61eb416SIlia Diachkov     unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType);
467147ff1b4SMichal Paszkowski     uint64_t AllOnes = APInt::getAllOnes(Bits).getZExtValue();
468f61eb416SIlia Diachkov     TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType);
469f61eb416SIlia Diachkov     FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType);
470f61eb416SIlia Diachkov   } else {
471f61eb416SIlia Diachkov     TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType);
472f61eb416SIlia Diachkov     FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType);
473f61eb416SIlia Diachkov   }
47467d3ef74SVyacheslav Levytskyy 
475f61eb416SIlia Diachkov   return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst,
476f61eb416SIlia Diachkov                                 FalseConst);
477f61eb416SIlia Diachkov }
478f61eb416SIlia Diachkov 
479f61eb416SIlia Diachkov /// Helper function for building a load instruction loading into the
480f61eb416SIlia Diachkov /// \p DestinationReg.
481f61eb416SIlia Diachkov static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister,
482f61eb416SIlia Diachkov                               MachineIRBuilder &MIRBuilder,
483f61eb416SIlia Diachkov                               SPIRVGlobalRegistry *GR, LLT LowLevelType,
484f61eb416SIlia Diachkov                               Register DestinationReg = Register(0)) {
485b5132b7dSVyacheslav Levytskyy   if (!DestinationReg.isValid())
486b5132b7dSVyacheslav Levytskyy     DestinationReg = createVirtualRegister(BaseType, GR, MIRBuilder);
487f61eb416SIlia Diachkov   // TODO: consider using correct address space and alignment (p0 is canonical
488f61eb416SIlia Diachkov   // type for selection though).
489f61eb416SIlia Diachkov   MachinePointerInfo PtrInfo = MachinePointerInfo();
490f61eb416SIlia Diachkov   MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align());
491f61eb416SIlia Diachkov   return DestinationReg;
492f61eb416SIlia Diachkov }
493f61eb416SIlia Diachkov 
494f61eb416SIlia Diachkov /// Helper function for building a load instruction for loading a builtin global
495f61eb416SIlia Diachkov /// variable of \p BuiltinValue value.
496f0eb9083SNathan Gauër static Register buildBuiltinVariableLoad(
497f0eb9083SNathan Gauër     MachineIRBuilder &MIRBuilder, SPIRVType *VariableType,
498f0eb9083SNathan Gauër     SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType,
499f0eb9083SNathan Gauër     Register Reg = Register(0), bool isConst = true, bool hasLinkageTy = true) {
500f61eb416SIlia Diachkov   Register NewRegister =
5018ac46d6bSVyacheslav Levytskyy       MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::pIDRegClass);
5028ac46d6bSVyacheslav Levytskyy   MIRBuilder.getMRI()->setType(
5038ac46d6bSVyacheslav Levytskyy       NewRegister,
5048ac46d6bSVyacheslav Levytskyy       LLT::pointer(storageClassToAddressSpace(SPIRV::StorageClass::Function),
5058ac46d6bSVyacheslav Levytskyy                    GR->getPointerSize()));
506f61eb416SIlia Diachkov   SPIRVType *PtrType = GR->getOrCreateSPIRVPointerType(
507f61eb416SIlia Diachkov       VariableType, MIRBuilder, SPIRV::StorageClass::Input);
508f61eb416SIlia Diachkov   GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
509f61eb416SIlia Diachkov 
510f61eb416SIlia Diachkov   // Set up the global OpVariable with the necessary builtin decorations.
511f61eb416SIlia Diachkov   Register Variable = GR->buildGlobalVariable(
512f61eb416SIlia Diachkov       NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
513f0eb9083SNathan Gauër       SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst,
514f0eb9083SNathan Gauër       /* HasLinkageTy */ hasLinkageTy, SPIRV::LinkageType::Import, MIRBuilder,
515f0eb9083SNathan Gauër       false);
516f61eb416SIlia Diachkov 
517f61eb416SIlia Diachkov   // Load the value from the global variable.
518f61eb416SIlia Diachkov   Register LoadedRegister =
519f61eb416SIlia Diachkov       buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg);
520f61eb416SIlia Diachkov   MIRBuilder.getMRI()->setType(LoadedRegister, LLType);
521f61eb416SIlia Diachkov   return LoadedRegister;
522f61eb416SIlia Diachkov }
523f61eb416SIlia Diachkov 
524f61eb416SIlia Diachkov /// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg
525f61eb416SIlia Diachkov /// and its definition, set the new register as a destination of the definition,
526f61eb416SIlia Diachkov /// assign SPIRVType to both registers. If SpirvTy is provided, use it as
527f61eb416SIlia Diachkov /// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in
528f61eb416SIlia Diachkov /// SPIRVPreLegalizer.cpp.
529f61eb416SIlia Diachkov extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
530f61eb416SIlia Diachkov                                   SPIRVGlobalRegistry *GR,
531f61eb416SIlia Diachkov                                   MachineIRBuilder &MIB,
532f61eb416SIlia Diachkov                                   MachineRegisterInfo &MRI);
533f61eb416SIlia Diachkov 
534f61eb416SIlia Diachkov // TODO: Move to TableGen.
535f61eb416SIlia Diachkov static SPIRV::MemorySemantics::MemorySemantics
536f61eb416SIlia Diachkov getSPIRVMemSemantics(std::memory_order MemOrder) {
537f61eb416SIlia Diachkov   switch (MemOrder) {
538fe7cb156SVyacheslav Levytskyy   case std::memory_order_relaxed:
539f61eb416SIlia Diachkov     return SPIRV::MemorySemantics::None;
540fe7cb156SVyacheslav Levytskyy   case std::memory_order_acquire:
541f61eb416SIlia Diachkov     return SPIRV::MemorySemantics::Acquire;
542fe7cb156SVyacheslav Levytskyy   case std::memory_order_release:
543f61eb416SIlia Diachkov     return SPIRV::MemorySemantics::Release;
544fe7cb156SVyacheslav Levytskyy   case std::memory_order_acq_rel:
545f61eb416SIlia Diachkov     return SPIRV::MemorySemantics::AcquireRelease;
546fe7cb156SVyacheslav Levytskyy   case std::memory_order_seq_cst:
547f61eb416SIlia Diachkov     return SPIRV::MemorySemantics::SequentiallyConsistent;
548f61eb416SIlia Diachkov   default:
549925768eeSVyacheslav Levytskyy     report_fatal_error("Unknown CL memory scope");
550f61eb416SIlia Diachkov   }
551f61eb416SIlia Diachkov }
552f61eb416SIlia Diachkov 
553f61eb416SIlia Diachkov static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) {
554f61eb416SIlia Diachkov   switch (ClScope) {
555f61eb416SIlia Diachkov   case SPIRV::CLMemoryScope::memory_scope_work_item:
556f61eb416SIlia Diachkov     return SPIRV::Scope::Invocation;
557f61eb416SIlia Diachkov   case SPIRV::CLMemoryScope::memory_scope_work_group:
558f61eb416SIlia Diachkov     return SPIRV::Scope::Workgroup;
559f61eb416SIlia Diachkov   case SPIRV::CLMemoryScope::memory_scope_device:
560f61eb416SIlia Diachkov     return SPIRV::Scope::Device;
561f61eb416SIlia Diachkov   case SPIRV::CLMemoryScope::memory_scope_all_svm_devices:
562f61eb416SIlia Diachkov     return SPIRV::Scope::CrossDevice;
563f61eb416SIlia Diachkov   case SPIRV::CLMemoryScope::memory_scope_sub_group:
564f61eb416SIlia Diachkov     return SPIRV::Scope::Subgroup;
565f61eb416SIlia Diachkov   }
566925768eeSVyacheslav Levytskyy   report_fatal_error("Unknown CL memory scope");
567f61eb416SIlia Diachkov }
568f61eb416SIlia Diachkov 
56967d3ef74SVyacheslav Levytskyy static Register buildConstantIntReg32(uint64_t Val,
57067d3ef74SVyacheslav Levytskyy                                       MachineIRBuilder &MIRBuilder,
57167d3ef74SVyacheslav Levytskyy                                       SPIRVGlobalRegistry *GR) {
57267d3ef74SVyacheslav Levytskyy   return GR->buildConstantInt(Val, MIRBuilder,
57367d3ef74SVyacheslav Levytskyy                               GR->getOrCreateSPIRVIntegerType(32, MIRBuilder));
574f61eb416SIlia Diachkov }
575f61eb416SIlia Diachkov 
57687080898SMichal Paszkowski static Register buildScopeReg(Register CLScopeRegister,
57774c66710SIlia Diachkov                               SPIRV::Scope::Scope Scope,
57887080898SMichal Paszkowski                               MachineIRBuilder &MIRBuilder,
57987080898SMichal Paszkowski                               SPIRVGlobalRegistry *GR,
58074c66710SIlia Diachkov                               MachineRegisterInfo *MRI) {
58174c66710SIlia Diachkov   if (CLScopeRegister.isValid()) {
58287080898SMichal Paszkowski     auto CLScope =
58387080898SMichal Paszkowski         static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI));
58474c66710SIlia Diachkov     Scope = getSPIRVScope(CLScope);
58587080898SMichal Paszkowski 
58674c66710SIlia Diachkov     if (CLScope == static_cast<unsigned>(Scope)) {
587f9c98068SVyacheslav Levytskyy       MRI->setRegClass(CLScopeRegister, &SPIRV::iIDRegClass);
58887080898SMichal Paszkowski       return CLScopeRegister;
58974c66710SIlia Diachkov     }
59074c66710SIlia Diachkov   }
59167d3ef74SVyacheslav Levytskyy   return buildConstantIntReg32(Scope, MIRBuilder, GR);
59287080898SMichal Paszkowski }
59387080898SMichal Paszkowski 
594978de2d6SVyacheslav Levytskyy static void setRegClassIfNull(Register Reg, MachineRegisterInfo *MRI,
595978de2d6SVyacheslav Levytskyy                               SPIRVGlobalRegistry *GR) {
596978de2d6SVyacheslav Levytskyy   if (MRI->getRegClassOrNull(Reg))
597978de2d6SVyacheslav Levytskyy     return;
598978de2d6SVyacheslav Levytskyy   SPIRVType *SpvType = GR->getSPIRVTypeForVReg(Reg);
599978de2d6SVyacheslav Levytskyy   MRI->setRegClass(Reg,
600978de2d6SVyacheslav Levytskyy                    SpvType ? GR->getRegClass(SpvType) : &SPIRV::iIDRegClass);
601978de2d6SVyacheslav Levytskyy }
602978de2d6SVyacheslav Levytskyy 
60387080898SMichal Paszkowski static Register buildMemSemanticsReg(Register SemanticsRegister,
60474c66710SIlia Diachkov                                      Register PtrRegister, unsigned &Semantics,
60574c66710SIlia Diachkov                                      MachineIRBuilder &MIRBuilder,
60687080898SMichal Paszkowski                                      SPIRVGlobalRegistry *GR) {
60774c66710SIlia Diachkov   if (SemanticsRegister.isValid()) {
60874c66710SIlia Diachkov     MachineRegisterInfo *MRI = MIRBuilder.getMRI();
60987080898SMichal Paszkowski     std::memory_order Order =
61087080898SMichal Paszkowski         static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI));
61174c66710SIlia Diachkov     Semantics =
61287080898SMichal Paszkowski         getSPIRVMemSemantics(Order) |
61387080898SMichal Paszkowski         getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
614b45072d9SMichal Paszkowski     if (static_cast<unsigned>(Order) == Semantics) {
615f9c98068SVyacheslav Levytskyy       MRI->setRegClass(SemanticsRegister, &SPIRV::iIDRegClass);
61687080898SMichal Paszkowski       return SemanticsRegister;
61774c66710SIlia Diachkov     }
61874c66710SIlia Diachkov   }
61967d3ef74SVyacheslav Levytskyy   return buildConstantIntReg32(Semantics, MIRBuilder, GR);
62087080898SMichal Paszkowski }
62187080898SMichal Paszkowski 
622c2483ed5SVyacheslav Levytskyy static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode,
623c2483ed5SVyacheslav Levytskyy                                const SPIRV::IncomingCall *Call,
62457f79371SVyacheslav Levytskyy                                Register TypeReg,
62557f79371SVyacheslav Levytskyy                                ArrayRef<uint32_t> ImmArgs = {}) {
626c2483ed5SVyacheslav Levytskyy   auto MIB = MIRBuilder.buildInstr(Opcode);
627c2483ed5SVyacheslav Levytskyy   if (TypeReg.isValid())
628c2483ed5SVyacheslav Levytskyy     MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
62957f79371SVyacheslav Levytskyy   unsigned Sz = Call->Arguments.size() - ImmArgs.size();
63067d3ef74SVyacheslav Levytskyy   for (unsigned i = 0; i < Sz; ++i)
63167d3ef74SVyacheslav Levytskyy     MIB.addUse(Call->Arguments[i]);
63257f79371SVyacheslav Levytskyy   for (uint32_t ImmArg : ImmArgs)
63357f79371SVyacheslav Levytskyy     MIB.addImm(ImmArg);
634c2483ed5SVyacheslav Levytskyy   return true;
635c2483ed5SVyacheslav Levytskyy }
636c2483ed5SVyacheslav Levytskyy 
6375fb4a051SMichal Paszkowski /// Helper function for translating atomic init to OpStore.
6385fb4a051SMichal Paszkowski static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call,
6395fb4a051SMichal Paszkowski                                 MachineIRBuilder &MIRBuilder) {
640c2483ed5SVyacheslav Levytskyy   if (Call->isSpirvOp())
64157f79371SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0));
642c2483ed5SVyacheslav Levytskyy 
6435fb4a051SMichal Paszkowski   assert(Call->Arguments.size() == 2 &&
6445fb4a051SMichal Paszkowski          "Need 2 arguments for atomic init translation");
6455fb4a051SMichal Paszkowski   MIRBuilder.buildInstr(SPIRV::OpStore)
6465fb4a051SMichal Paszkowski       .addUse(Call->Arguments[0])
6475fb4a051SMichal Paszkowski       .addUse(Call->Arguments[1]);
6485fb4a051SMichal Paszkowski   return true;
6495fb4a051SMichal Paszkowski }
6505fb4a051SMichal Paszkowski 
651f61eb416SIlia Diachkov /// Helper function for building an atomic load instruction.
652f61eb416SIlia Diachkov static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call,
653f61eb416SIlia Diachkov                                 MachineIRBuilder &MIRBuilder,
654f61eb416SIlia Diachkov                                 SPIRVGlobalRegistry *GR) {
655c2483ed5SVyacheslav Levytskyy   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
656c2483ed5SVyacheslav Levytskyy   if (Call->isSpirvOp())
657c2483ed5SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg);
658c2483ed5SVyacheslav Levytskyy 
659f61eb416SIlia Diachkov   Register PtrRegister = Call->Arguments[0];
660f61eb416SIlia Diachkov   // TODO: if true insert call to __translate_ocl_memory_sccope before
661f61eb416SIlia Diachkov   // OpAtomicLoad and the function implementation. We can use Translator's
662f61eb416SIlia Diachkov   // output for transcoding/atomic_explicit_arguments.cl as an example.
66367d3ef74SVyacheslav Levytskyy   Register ScopeRegister =
66467d3ef74SVyacheslav Levytskyy       Call->Arguments.size() > 1
66567d3ef74SVyacheslav Levytskyy           ? Call->Arguments[1]
66667d3ef74SVyacheslav Levytskyy           : buildConstantIntReg32(SPIRV::Scope::Device, MIRBuilder, GR);
667f61eb416SIlia Diachkov   Register MemSemanticsReg;
668f61eb416SIlia Diachkov   if (Call->Arguments.size() > 2) {
669f61eb416SIlia Diachkov     // TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad.
670f61eb416SIlia Diachkov     MemSemanticsReg = Call->Arguments[2];
671f61eb416SIlia Diachkov   } else {
672f61eb416SIlia Diachkov     int Semantics =
673f61eb416SIlia Diachkov         SPIRV::MemorySemantics::SequentiallyConsistent |
674f61eb416SIlia Diachkov         getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
67567d3ef74SVyacheslav Levytskyy     MemSemanticsReg = buildConstantIntReg32(Semantics, MIRBuilder, GR);
676f61eb416SIlia Diachkov   }
677f61eb416SIlia Diachkov 
678f61eb416SIlia Diachkov   MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
679f61eb416SIlia Diachkov       .addDef(Call->ReturnRegister)
680c2483ed5SVyacheslav Levytskyy       .addUse(TypeReg)
681f61eb416SIlia Diachkov       .addUse(PtrRegister)
682f61eb416SIlia Diachkov       .addUse(ScopeRegister)
683f61eb416SIlia Diachkov       .addUse(MemSemanticsReg);
684f61eb416SIlia Diachkov   return true;
685f61eb416SIlia Diachkov }
686f61eb416SIlia Diachkov 
687f61eb416SIlia Diachkov /// Helper function for building an atomic store instruction.
688f61eb416SIlia Diachkov static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call,
689f61eb416SIlia Diachkov                                  MachineIRBuilder &MIRBuilder,
690f61eb416SIlia Diachkov                                  SPIRVGlobalRegistry *GR) {
691c2483ed5SVyacheslav Levytskyy   if (Call->isSpirvOp())
69257f79371SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call, Register(0));
693c2483ed5SVyacheslav Levytskyy 
694f61eb416SIlia Diachkov   Register ScopeRegister =
69567d3ef74SVyacheslav Levytskyy       buildConstantIntReg32(SPIRV::Scope::Device, MIRBuilder, GR);
696f61eb416SIlia Diachkov   Register PtrRegister = Call->Arguments[0];
697f61eb416SIlia Diachkov   int Semantics =
698f61eb416SIlia Diachkov       SPIRV::MemorySemantics::SequentiallyConsistent |
699f61eb416SIlia Diachkov       getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
70067d3ef74SVyacheslav Levytskyy   Register MemSemanticsReg = buildConstantIntReg32(Semantics, MIRBuilder, GR);
701f61eb416SIlia Diachkov   MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
702f61eb416SIlia Diachkov       .addUse(PtrRegister)
703f61eb416SIlia Diachkov       .addUse(ScopeRegister)
704f61eb416SIlia Diachkov       .addUse(MemSemanticsReg)
705f61eb416SIlia Diachkov       .addUse(Call->Arguments[1]);
706f61eb416SIlia Diachkov   return true;
707f61eb416SIlia Diachkov }
708f61eb416SIlia Diachkov 
709f61eb416SIlia Diachkov /// Helper function for building an atomic compare-exchange instruction.
710c2483ed5SVyacheslav Levytskyy static bool buildAtomicCompareExchangeInst(
711c2483ed5SVyacheslav Levytskyy     const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin,
712c2483ed5SVyacheslav Levytskyy     unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
713c2483ed5SVyacheslav Levytskyy   if (Call->isSpirvOp())
714c2483ed5SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
715c2483ed5SVyacheslav Levytskyy                               GR->getSPIRVTypeID(Call->ReturnType));
716c2483ed5SVyacheslav Levytskyy 
717f61eb416SIlia Diachkov   bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg");
718f61eb416SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
719f61eb416SIlia Diachkov 
720f61eb416SIlia Diachkov   Register ObjectPtr = Call->Arguments[0];   // Pointer (volatile A *object.)
721f61eb416SIlia Diachkov   Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).
722f61eb416SIlia Diachkov   Register Desired = Call->Arguments[2];     // Value (C Desired).
723f61eb416SIlia Diachkov   SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
724f61eb416SIlia Diachkov   LLT DesiredLLT = MRI->getType(Desired);
725f61eb416SIlia Diachkov 
726f61eb416SIlia Diachkov   assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() ==
727f61eb416SIlia Diachkov          SPIRV::OpTypePointer);
728f61eb416SIlia Diachkov   unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode();
729b221b973SVyacheslav Levytskyy   (void)ExpectedType;
730f61eb416SIlia Diachkov   assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt
731f61eb416SIlia Diachkov                    : ExpectedType == SPIRV::OpTypePointer);
732f61eb416SIlia Diachkov   assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
733f61eb416SIlia Diachkov 
734f61eb416SIlia Diachkov   SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
735f61eb416SIlia Diachkov   assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected");
736f61eb416SIlia Diachkov   auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
737f61eb416SIlia Diachkov       SpvObjectPtrTy->getOperand(1).getImm());
738f61eb416SIlia Diachkov   auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
739f61eb416SIlia Diachkov 
740f61eb416SIlia Diachkov   Register MemSemEqualReg;
741f61eb416SIlia Diachkov   Register MemSemUnequalReg;
742f61eb416SIlia Diachkov   uint64_t MemSemEqual =
743f61eb416SIlia Diachkov       IsCmpxchg
744f61eb416SIlia Diachkov           ? SPIRV::MemorySemantics::None
745f61eb416SIlia Diachkov           : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
746f61eb416SIlia Diachkov   uint64_t MemSemUnequal =
747f61eb416SIlia Diachkov       IsCmpxchg
748f61eb416SIlia Diachkov           ? SPIRV::MemorySemantics::None
749f61eb416SIlia Diachkov           : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
750f61eb416SIlia Diachkov   if (Call->Arguments.size() >= 4) {
751f61eb416SIlia Diachkov     assert(Call->Arguments.size() >= 5 &&
752f61eb416SIlia Diachkov            "Need 5+ args for explicit atomic cmpxchg");
753f61eb416SIlia Diachkov     auto MemOrdEq =
754f61eb416SIlia Diachkov         static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));
755f61eb416SIlia Diachkov     auto MemOrdNeq =
756f61eb416SIlia Diachkov         static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));
757f61eb416SIlia Diachkov     MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;
758f61eb416SIlia Diachkov     MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;
759b45072d9SMichal Paszkowski     if (static_cast<unsigned>(MemOrdEq) == MemSemEqual)
760f61eb416SIlia Diachkov       MemSemEqualReg = Call->Arguments[3];
761b45072d9SMichal Paszkowski     if (static_cast<unsigned>(MemOrdNeq) == MemSemEqual)
762f61eb416SIlia Diachkov       MemSemUnequalReg = Call->Arguments[4];
763f61eb416SIlia Diachkov   }
764f61eb416SIlia Diachkov   if (!MemSemEqualReg.isValid())
76567d3ef74SVyacheslav Levytskyy     MemSemEqualReg = buildConstantIntReg32(MemSemEqual, MIRBuilder, GR);
766f61eb416SIlia Diachkov   if (!MemSemUnequalReg.isValid())
76767d3ef74SVyacheslav Levytskyy     MemSemUnequalReg = buildConstantIntReg32(MemSemUnequal, MIRBuilder, GR);
768f61eb416SIlia Diachkov 
769f61eb416SIlia Diachkov   Register ScopeReg;
770f61eb416SIlia Diachkov   auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;
771f61eb416SIlia Diachkov   if (Call->Arguments.size() >= 6) {
772f61eb416SIlia Diachkov     assert(Call->Arguments.size() == 6 &&
773f61eb416SIlia Diachkov            "Extra args for explicit atomic cmpxchg");
774f61eb416SIlia Diachkov     auto ClScope = static_cast<SPIRV::CLMemoryScope>(
775f61eb416SIlia Diachkov         getIConstVal(Call->Arguments[5], MRI));
776f61eb416SIlia Diachkov     Scope = getSPIRVScope(ClScope);
777f61eb416SIlia Diachkov     if (ClScope == static_cast<unsigned>(Scope))
778f61eb416SIlia Diachkov       ScopeReg = Call->Arguments[5];
779f61eb416SIlia Diachkov   }
780f61eb416SIlia Diachkov   if (!ScopeReg.isValid())
78167d3ef74SVyacheslav Levytskyy     ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
782f61eb416SIlia Diachkov 
783f61eb416SIlia Diachkov   Register Expected = IsCmpxchg
784f61eb416SIlia Diachkov                           ? ExpectedArg
785f61eb416SIlia Diachkov                           : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder,
78667d3ef74SVyacheslav Levytskyy                                           GR, LLT::scalar(64));
787f61eb416SIlia Diachkov   MRI->setType(Expected, DesiredLLT);
788f61eb416SIlia Diachkov   Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)
789f61eb416SIlia Diachkov                             : Call->ReturnRegister;
79074c66710SIlia Diachkov   if (!MRI->getRegClassOrNull(Tmp))
79167d3ef74SVyacheslav Levytskyy     MRI->setRegClass(Tmp, GR->getRegClass(SpvDesiredTy));
792f61eb416SIlia Diachkov   GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());
793f61eb416SIlia Diachkov 
794f61eb416SIlia Diachkov   SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
795f61eb416SIlia Diachkov   MIRBuilder.buildInstr(Opcode)
796f61eb416SIlia Diachkov       .addDef(Tmp)
797f61eb416SIlia Diachkov       .addUse(GR->getSPIRVTypeID(IntTy))
798f61eb416SIlia Diachkov       .addUse(ObjectPtr)
799f61eb416SIlia Diachkov       .addUse(ScopeReg)
800f61eb416SIlia Diachkov       .addUse(MemSemEqualReg)
801f61eb416SIlia Diachkov       .addUse(MemSemUnequalReg)
802f61eb416SIlia Diachkov       .addUse(Desired)
803f61eb416SIlia Diachkov       .addUse(Expected);
804f61eb416SIlia Diachkov   if (!IsCmpxchg) {
805f61eb416SIlia Diachkov     MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);
806f61eb416SIlia Diachkov     MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);
807f61eb416SIlia Diachkov   }
808f61eb416SIlia Diachkov   return true;
809f61eb416SIlia Diachkov }
810f61eb416SIlia Diachkov 
811378630b4SVyacheslav Levytskyy /// Helper function for building atomic instructions.
812f61eb416SIlia Diachkov static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
813f61eb416SIlia Diachkov                                MachineIRBuilder &MIRBuilder,
814f61eb416SIlia Diachkov                                SPIRVGlobalRegistry *GR) {
815c2483ed5SVyacheslav Levytskyy   if (Call->isSpirvOp())
816c2483ed5SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
817c2483ed5SVyacheslav Levytskyy                               GR->getSPIRVTypeID(Call->ReturnType));
818c2483ed5SVyacheslav Levytskyy 
81974c66710SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
82074c66710SIlia Diachkov   Register ScopeRegister =
82174c66710SIlia Diachkov       Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register();
82287080898SMichal Paszkowski 
82374c66710SIlia Diachkov   assert(Call->Arguments.size() <= 4 &&
82487080898SMichal Paszkowski          "Too many args for explicit atomic RMW");
82574c66710SIlia Diachkov   ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup,
82674c66710SIlia Diachkov                                 MIRBuilder, GR, MRI);
827f61eb416SIlia Diachkov 
828f61eb416SIlia Diachkov   Register PtrRegister = Call->Arguments[0];
829f61eb416SIlia Diachkov   unsigned Semantics = SPIRV::MemorySemantics::None;
83074c66710SIlia Diachkov   Register MemSemanticsReg =
83174c66710SIlia Diachkov       Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
83274c66710SIlia Diachkov   MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
83374c66710SIlia Diachkov                                          Semantics, MIRBuilder, GR);
834378630b4SVyacheslav Levytskyy   Register ValueReg = Call->Arguments[1];
835378630b4SVyacheslav Levytskyy   Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);
836378630b4SVyacheslav Levytskyy   // support cl_ext_float_atomics
837378630b4SVyacheslav Levytskyy   if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {
838378630b4SVyacheslav Levytskyy     if (Opcode == SPIRV::OpAtomicIAdd) {
839378630b4SVyacheslav Levytskyy       Opcode = SPIRV::OpAtomicFAddEXT;
840378630b4SVyacheslav Levytskyy     } else if (Opcode == SPIRV::OpAtomicISub) {
841378630b4SVyacheslav Levytskyy       // Translate OpAtomicISub applied to a floating type argument to
842378630b4SVyacheslav Levytskyy       // OpAtomicFAddEXT with the negative value operand
843378630b4SVyacheslav Levytskyy       Opcode = SPIRV::OpAtomicFAddEXT;
844378630b4SVyacheslav Levytskyy       Register NegValueReg =
845378630b4SVyacheslav Levytskyy           MRI->createGenericVirtualRegister(MRI->getType(ValueReg));
84667d3ef74SVyacheslav Levytskyy       MRI->setRegClass(NegValueReg, GR->getRegClass(Call->ReturnType));
847378630b4SVyacheslav Levytskyy       GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg,
848378630b4SVyacheslav Levytskyy                                 MIRBuilder.getMF());
849378630b4SVyacheslav Levytskyy       MIRBuilder.buildInstr(TargetOpcode::G_FNEG)
850378630b4SVyacheslav Levytskyy           .addDef(NegValueReg)
851378630b4SVyacheslav Levytskyy           .addUse(ValueReg);
852378630b4SVyacheslav Levytskyy       insertAssignInstr(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder,
853378630b4SVyacheslav Levytskyy                         MIRBuilder.getMF().getRegInfo());
854378630b4SVyacheslav Levytskyy       ValueReg = NegValueReg;
855378630b4SVyacheslav Levytskyy     }
856378630b4SVyacheslav Levytskyy   }
857f61eb416SIlia Diachkov   MIRBuilder.buildInstr(Opcode)
858f61eb416SIlia Diachkov       .addDef(Call->ReturnRegister)
859378630b4SVyacheslav Levytskyy       .addUse(ValueTypeReg)
860f61eb416SIlia Diachkov       .addUse(PtrRegister)
861f61eb416SIlia Diachkov       .addUse(ScopeRegister)
862f61eb416SIlia Diachkov       .addUse(MemSemanticsReg)
863378630b4SVyacheslav Levytskyy       .addUse(ValueReg);
864f61eb416SIlia Diachkov   return true;
865f61eb416SIlia Diachkov }
866f61eb416SIlia Diachkov 
867925768eeSVyacheslav Levytskyy /// Helper function for building an atomic floating-type instruction.
868925768eeSVyacheslav Levytskyy static bool buildAtomicFloatingRMWInst(const SPIRV::IncomingCall *Call,
869925768eeSVyacheslav Levytskyy                                        unsigned Opcode,
870925768eeSVyacheslav Levytskyy                                        MachineIRBuilder &MIRBuilder,
871925768eeSVyacheslav Levytskyy                                        SPIRVGlobalRegistry *GR) {
872925768eeSVyacheslav Levytskyy   assert(Call->Arguments.size() == 4 &&
873925768eeSVyacheslav Levytskyy          "Wrong number of atomic floating-type builtin");
874925768eeSVyacheslav Levytskyy   Register PtrReg = Call->Arguments[0];
875925768eeSVyacheslav Levytskyy   Register ScopeReg = Call->Arguments[1];
876925768eeSVyacheslav Levytskyy   Register MemSemanticsReg = Call->Arguments[2];
877925768eeSVyacheslav Levytskyy   Register ValueReg = Call->Arguments[3];
878925768eeSVyacheslav Levytskyy   MIRBuilder.buildInstr(Opcode)
879925768eeSVyacheslav Levytskyy       .addDef(Call->ReturnRegister)
880925768eeSVyacheslav Levytskyy       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
881925768eeSVyacheslav Levytskyy       .addUse(PtrReg)
882925768eeSVyacheslav Levytskyy       .addUse(ScopeReg)
883925768eeSVyacheslav Levytskyy       .addUse(MemSemanticsReg)
884925768eeSVyacheslav Levytskyy       .addUse(ValueReg);
885925768eeSVyacheslav Levytskyy   return true;
886925768eeSVyacheslav Levytskyy }
887925768eeSVyacheslav Levytskyy 
88887080898SMichal Paszkowski /// Helper function for building atomic flag instructions (e.g.
88987080898SMichal Paszkowski /// OpAtomicFlagTestAndSet).
89087080898SMichal Paszkowski static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,
89187080898SMichal Paszkowski                                 unsigned Opcode, MachineIRBuilder &MIRBuilder,
89287080898SMichal Paszkowski                                 SPIRVGlobalRegistry *GR) {
893c2483ed5SVyacheslav Levytskyy   bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet;
894c2483ed5SVyacheslav Levytskyy   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
895c2483ed5SVyacheslav Levytskyy   if (Call->isSpirvOp())
896c2483ed5SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
897c2483ed5SVyacheslav Levytskyy                               IsSet ? TypeReg : Register(0));
898c2483ed5SVyacheslav Levytskyy 
89974c66710SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
90087080898SMichal Paszkowski   Register PtrRegister = Call->Arguments[0];
90187080898SMichal Paszkowski   unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent;
90274c66710SIlia Diachkov   Register MemSemanticsReg =
90374c66710SIlia Diachkov       Call->Arguments.size() >= 2 ? Call->Arguments[1] : Register();
90474c66710SIlia Diachkov   MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
90574c66710SIlia Diachkov                                          Semantics, MIRBuilder, GR);
90687080898SMichal Paszkowski 
90787080898SMichal Paszkowski   assert((Opcode != SPIRV::OpAtomicFlagClear ||
90887080898SMichal Paszkowski           (Semantics != SPIRV::MemorySemantics::Acquire &&
90987080898SMichal Paszkowski            Semantics != SPIRV::MemorySemantics::AcquireRelease)) &&
91087080898SMichal Paszkowski          "Invalid memory order argument!");
91187080898SMichal Paszkowski 
91274c66710SIlia Diachkov   Register ScopeRegister =
91374c66710SIlia Diachkov       Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
91474c66710SIlia Diachkov   ScopeRegister =
91574c66710SIlia Diachkov       buildScopeReg(ScopeRegister, SPIRV::Scope::Device, MIRBuilder, GR, MRI);
91687080898SMichal Paszkowski 
91787080898SMichal Paszkowski   auto MIB = MIRBuilder.buildInstr(Opcode);
918c2483ed5SVyacheslav Levytskyy   if (IsSet)
919c2483ed5SVyacheslav Levytskyy     MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
92087080898SMichal Paszkowski 
92187080898SMichal Paszkowski   MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);
92287080898SMichal Paszkowski   return true;
92387080898SMichal Paszkowski }
92487080898SMichal Paszkowski 
925f61eb416SIlia Diachkov /// Helper function for building barriers, i.e., memory/control ordering
926f61eb416SIlia Diachkov /// operations.
927f61eb416SIlia Diachkov static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
928f61eb416SIlia Diachkov                              MachineIRBuilder &MIRBuilder,
929f61eb416SIlia Diachkov                              SPIRVGlobalRegistry *GR) {
930bfe84f70SVyacheslav Levytskyy   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
931bfe84f70SVyacheslav Levytskyy   const auto *ST =
932bfe84f70SVyacheslav Levytskyy       static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
933bfe84f70SVyacheslav Levytskyy   if ((Opcode == SPIRV::OpControlBarrierArriveINTEL ||
934bfe84f70SVyacheslav Levytskyy        Opcode == SPIRV::OpControlBarrierWaitINTEL) &&
935bfe84f70SVyacheslav Levytskyy       !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_split_barrier)) {
936bfe84f70SVyacheslav Levytskyy     std::string DiagMsg = std::string(Builtin->Name) +
937bfe84f70SVyacheslav Levytskyy                           ": the builtin requires the following SPIR-V "
938bfe84f70SVyacheslav Levytskyy                           "extension: SPV_INTEL_split_barrier";
939bfe84f70SVyacheslav Levytskyy     report_fatal_error(DiagMsg.c_str(), false);
940bfe84f70SVyacheslav Levytskyy   }
941bfe84f70SVyacheslav Levytskyy 
942c2483ed5SVyacheslav Levytskyy   if (Call->isSpirvOp())
94357f79371SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
944c2483ed5SVyacheslav Levytskyy 
94574c66710SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
946f61eb416SIlia Diachkov   unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);
947f61eb416SIlia Diachkov   unsigned MemSemantics = SPIRV::MemorySemantics::None;
948f61eb416SIlia Diachkov 
949f61eb416SIlia Diachkov   if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
950f61eb416SIlia Diachkov     MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
951f61eb416SIlia Diachkov 
952f61eb416SIlia Diachkov   if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)
953f61eb416SIlia Diachkov     MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;
954f61eb416SIlia Diachkov 
955f61eb416SIlia Diachkov   if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)
956f61eb416SIlia Diachkov     MemSemantics |= SPIRV::MemorySemantics::ImageMemory;
957f61eb416SIlia Diachkov 
958bfe84f70SVyacheslav Levytskyy   if (Opcode == SPIRV::OpMemoryBarrier)
959bfe84f70SVyacheslav Levytskyy     MemSemantics = getSPIRVMemSemantics(static_cast<std::memory_order>(
960bfe84f70SVyacheslav Levytskyy                        getIConstVal(Call->Arguments[1], MRI))) |
961bfe84f70SVyacheslav Levytskyy                    MemSemantics;
962bfe84f70SVyacheslav Levytskyy   else if (Opcode == SPIRV::OpControlBarrierArriveINTEL)
963bfe84f70SVyacheslav Levytskyy     MemSemantics |= SPIRV::MemorySemantics::Release;
964bfe84f70SVyacheslav Levytskyy   else if (Opcode == SPIRV::OpControlBarrierWaitINTEL)
965bfe84f70SVyacheslav Levytskyy     MemSemantics |= SPIRV::MemorySemantics::Acquire;
966bfe84f70SVyacheslav Levytskyy   else
967f61eb416SIlia Diachkov     MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;
968f61eb416SIlia Diachkov 
96967d3ef74SVyacheslav Levytskyy   Register MemSemanticsReg =
97067d3ef74SVyacheslav Levytskyy       MemFlags == MemSemantics
97167d3ef74SVyacheslav Levytskyy           ? Call->Arguments[0]
97267d3ef74SVyacheslav Levytskyy           : buildConstantIntReg32(MemSemantics, MIRBuilder, GR);
973f61eb416SIlia Diachkov   Register ScopeReg;
974f61eb416SIlia Diachkov   SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
975f61eb416SIlia Diachkov   SPIRV::Scope::Scope MemScope = Scope;
976f61eb416SIlia Diachkov   if (Call->Arguments.size() >= 2) {
977f61eb416SIlia Diachkov     assert(
978f61eb416SIlia Diachkov         ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||
979f61eb416SIlia Diachkov          (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&
980f61eb416SIlia Diachkov         "Extra args for explicitly scoped barrier");
981f61eb416SIlia Diachkov     Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]
982f61eb416SIlia Diachkov                                                            : Call->Arguments[1];
983f61eb416SIlia Diachkov     SPIRV::CLMemoryScope CLScope =
984f61eb416SIlia Diachkov         static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
985f61eb416SIlia Diachkov     MemScope = getSPIRVScope(CLScope);
986f61eb416SIlia Diachkov     if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
987f61eb416SIlia Diachkov         (Opcode == SPIRV::OpMemoryBarrier))
988f61eb416SIlia Diachkov       Scope = MemScope;
98967d3ef74SVyacheslav Levytskyy     if (CLScope == static_cast<unsigned>(Scope))
990f61eb416SIlia Diachkov       ScopeReg = Call->Arguments[1];
991f61eb416SIlia Diachkov   }
992f61eb416SIlia Diachkov 
993f61eb416SIlia Diachkov   if (!ScopeReg.isValid())
99467d3ef74SVyacheslav Levytskyy     ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
995f61eb416SIlia Diachkov 
996f61eb416SIlia Diachkov   auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);
997f61eb416SIlia Diachkov   if (Opcode != SPIRV::OpMemoryBarrier)
99867d3ef74SVyacheslav Levytskyy     MIB.addUse(buildConstantIntReg32(MemScope, MIRBuilder, GR));
999f61eb416SIlia Diachkov   MIB.addUse(MemSemanticsReg);
1000f61eb416SIlia Diachkov   return true;
1001f61eb416SIlia Diachkov }
1002f61eb416SIlia Diachkov 
1003f61eb416SIlia Diachkov static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
1004f61eb416SIlia Diachkov   switch (dim) {
1005f61eb416SIlia Diachkov   case SPIRV::Dim::DIM_1D:
1006f61eb416SIlia Diachkov   case SPIRV::Dim::DIM_Buffer:
1007f61eb416SIlia Diachkov     return 1;
1008f61eb416SIlia Diachkov   case SPIRV::Dim::DIM_2D:
1009f61eb416SIlia Diachkov   case SPIRV::Dim::DIM_Cube:
1010f61eb416SIlia Diachkov   case SPIRV::Dim::DIM_Rect:
1011f61eb416SIlia Diachkov     return 2;
1012f61eb416SIlia Diachkov   case SPIRV::Dim::DIM_3D:
1013f61eb416SIlia Diachkov     return 3;
1014f61eb416SIlia Diachkov   default:
1015925768eeSVyacheslav Levytskyy     report_fatal_error("Cannot get num components for given Dim");
1016f61eb416SIlia Diachkov   }
1017f61eb416SIlia Diachkov }
1018f61eb416SIlia Diachkov 
1019f61eb416SIlia Diachkov /// Helper function for obtaining the number of size components.
1020f61eb416SIlia Diachkov static unsigned getNumSizeComponents(SPIRVType *imgType) {
1021f61eb416SIlia Diachkov   assert(imgType->getOpcode() == SPIRV::OpTypeImage);
1022f61eb416SIlia Diachkov   auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());
1023f61eb416SIlia Diachkov   unsigned numComps = getNumComponentsForDim(dim);
1024f61eb416SIlia Diachkov   bool arrayed = imgType->getOperand(4).getImm() == 1;
1025f61eb416SIlia Diachkov   return arrayed ? numComps + 1 : numComps;
1026f61eb416SIlia Diachkov }
1027f61eb416SIlia Diachkov 
1028f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
1029f61eb416SIlia Diachkov // Implementation functions for each builtin group
1030f61eb416SIlia Diachkov //===----------------------------------------------------------------------===//
1031f61eb416SIlia Diachkov 
1032f61eb416SIlia Diachkov static bool generateExtInst(const SPIRV::IncomingCall *Call,
1033f61eb416SIlia Diachkov                             MachineIRBuilder &MIRBuilder,
1034f61eb416SIlia Diachkov                             SPIRVGlobalRegistry *GR) {
1035f61eb416SIlia Diachkov   // Lookup the extended instruction number in the TableGen records.
1036f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1037f61eb416SIlia Diachkov   uint32_t Number =
1038f61eb416SIlia Diachkov       SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number;
1039f61eb416SIlia Diachkov 
1040f61eb416SIlia Diachkov   // Build extended instruction.
1041f61eb416SIlia Diachkov   auto MIB =
1042f61eb416SIlia Diachkov       MIRBuilder.buildInstr(SPIRV::OpExtInst)
1043f61eb416SIlia Diachkov           .addDef(Call->ReturnRegister)
1044f61eb416SIlia Diachkov           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1045f61eb416SIlia Diachkov           .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1046f61eb416SIlia Diachkov           .addImm(Number);
1047f61eb416SIlia Diachkov 
1048f61eb416SIlia Diachkov   for (auto Argument : Call->Arguments)
1049f61eb416SIlia Diachkov     MIB.addUse(Argument);
1050f61eb416SIlia Diachkov   return true;
1051f61eb416SIlia Diachkov }
1052f61eb416SIlia Diachkov 
1053f61eb416SIlia Diachkov static bool generateRelationalInst(const SPIRV::IncomingCall *Call,
1054f61eb416SIlia Diachkov                                    MachineIRBuilder &MIRBuilder,
1055f61eb416SIlia Diachkov                                    SPIRVGlobalRegistry *GR) {
1056f61eb416SIlia Diachkov   // Lookup the instruction opcode in the TableGen records.
1057f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1058f61eb416SIlia Diachkov   unsigned Opcode =
1059f61eb416SIlia Diachkov       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1060f61eb416SIlia Diachkov 
1061f61eb416SIlia Diachkov   Register CompareRegister;
1062f61eb416SIlia Diachkov   SPIRVType *RelationType;
1063f61eb416SIlia Diachkov   std::tie(CompareRegister, RelationType) =
1064f61eb416SIlia Diachkov       buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1065f61eb416SIlia Diachkov 
1066f61eb416SIlia Diachkov   // Build relational instruction.
1067f61eb416SIlia Diachkov   auto MIB = MIRBuilder.buildInstr(Opcode)
1068f61eb416SIlia Diachkov                  .addDef(CompareRegister)
1069f61eb416SIlia Diachkov                  .addUse(GR->getSPIRVTypeID(RelationType));
1070f61eb416SIlia Diachkov 
1071f61eb416SIlia Diachkov   for (auto Argument : Call->Arguments)
1072f61eb416SIlia Diachkov     MIB.addUse(Argument);
1073f61eb416SIlia Diachkov 
1074f61eb416SIlia Diachkov   // Build select instruction.
1075f61eb416SIlia Diachkov   return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,
1076f61eb416SIlia Diachkov                          Call->ReturnType, GR);
1077f61eb416SIlia Diachkov }
1078f61eb416SIlia Diachkov 
1079f61eb416SIlia Diachkov static bool generateGroupInst(const SPIRV::IncomingCall *Call,
1080f61eb416SIlia Diachkov                               MachineIRBuilder &MIRBuilder,
1081f61eb416SIlia Diachkov                               SPIRVGlobalRegistry *GR) {
1082f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1083f61eb416SIlia Diachkov   const SPIRV::GroupBuiltin *GroupBuiltin =
1084f61eb416SIlia Diachkov       SPIRV::lookupGroupBuiltin(Builtin->Name);
10854baea8b3SVyacheslav Levytskyy 
108674c66710SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
10874baea8b3SVyacheslav Levytskyy   if (Call->isSpirvOp()) {
10884baea8b3SVyacheslav Levytskyy     if (GroupBuiltin->NoGroupOperation)
10894baea8b3SVyacheslav Levytskyy       return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call,
10904baea8b3SVyacheslav Levytskyy                                 GR->getSPIRVTypeID(Call->ReturnType));
10914baea8b3SVyacheslav Levytskyy 
10924baea8b3SVyacheslav Levytskyy     // Group Operation is a literal
10934baea8b3SVyacheslav Levytskyy     Register GroupOpReg = Call->Arguments[1];
10944baea8b3SVyacheslav Levytskyy     const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI);
10954baea8b3SVyacheslav Levytskyy     if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT)
10964baea8b3SVyacheslav Levytskyy       report_fatal_error(
10974baea8b3SVyacheslav Levytskyy           "Group Operation parameter must be an integer constant");
10984baea8b3SVyacheslav Levytskyy     uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue();
10994baea8b3SVyacheslav Levytskyy     Register ScopeReg = Call->Arguments[0];
110024cee1c4SVyacheslav Levytskyy     auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
11014baea8b3SVyacheslav Levytskyy                    .addDef(Call->ReturnRegister)
11024baea8b3SVyacheslav Levytskyy                    .addUse(GR->getSPIRVTypeID(Call->ReturnType))
11034baea8b3SVyacheslav Levytskyy                    .addUse(ScopeReg)
110424cee1c4SVyacheslav Levytskyy                    .addImm(GrpOp);
110567d3ef74SVyacheslav Levytskyy     for (unsigned i = 2; i < Call->Arguments.size(); ++i)
110667d3ef74SVyacheslav Levytskyy       MIB.addUse(Call->Arguments[i]);
11074baea8b3SVyacheslav Levytskyy     return true;
11084baea8b3SVyacheslav Levytskyy   }
11094baea8b3SVyacheslav Levytskyy 
1110f61eb416SIlia Diachkov   Register Arg0;
1111f61eb416SIlia Diachkov   if (GroupBuiltin->HasBoolArg) {
111223c72e93SVyacheslav Levytskyy     SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
111323c72e93SVyacheslav Levytskyy     Register BoolReg = Call->Arguments[0];
111423c72e93SVyacheslav Levytskyy     SPIRVType *BoolRegType = GR->getSPIRVTypeForVReg(BoolReg);
111523c72e93SVyacheslav Levytskyy     if (!BoolRegType)
111623c72e93SVyacheslav Levytskyy       report_fatal_error("Can't find a register's type definition");
111723c72e93SVyacheslav Levytskyy     MachineInstr *ArgInstruction = getDefInstrMaybeConstant(BoolReg, MRI);
111823c72e93SVyacheslav Levytskyy     if (ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT) {
111923c72e93SVyacheslav Levytskyy       if (BoolRegType->getOpcode() != SPIRV::OpTypeBool)
112023c72e93SVyacheslav Levytskyy         Arg0 = GR->buildConstantInt(getIConstVal(BoolReg, MRI), MIRBuilder,
112123c72e93SVyacheslav Levytskyy                                     BoolType);
112223c72e93SVyacheslav Levytskyy     } else {
112323c72e93SVyacheslav Levytskyy       if (BoolRegType->getOpcode() == SPIRV::OpTypeInt) {
112423c72e93SVyacheslav Levytskyy         Arg0 = MRI->createGenericVirtualRegister(LLT::scalar(1));
112567d3ef74SVyacheslav Levytskyy         MRI->setRegClass(Arg0, &SPIRV::iIDRegClass);
112623c72e93SVyacheslav Levytskyy         GR->assignSPIRVTypeToVReg(BoolType, Arg0, MIRBuilder.getMF());
112723c72e93SVyacheslav Levytskyy         MIRBuilder.buildICmp(CmpInst::ICMP_NE, Arg0, BoolReg,
112823c72e93SVyacheslav Levytskyy                              GR->buildConstantInt(0, MIRBuilder, BoolRegType));
112923c72e93SVyacheslav Levytskyy         insertAssignInstr(Arg0, nullptr, BoolType, GR, MIRBuilder,
113023c72e93SVyacheslav Levytskyy                           MIRBuilder.getMF().getRegInfo());
113123c72e93SVyacheslav Levytskyy       } else if (BoolRegType->getOpcode() != SPIRV::OpTypeBool) {
113223c72e93SVyacheslav Levytskyy         report_fatal_error("Expect a boolean argument");
113323c72e93SVyacheslav Levytskyy       }
113423c72e93SVyacheslav Levytskyy       // if BoolReg is a boolean register, we don't need to do anything
113523c72e93SVyacheslav Levytskyy     }
1136f61eb416SIlia Diachkov   }
1137f61eb416SIlia Diachkov 
1138f61eb416SIlia Diachkov   Register GroupResultRegister = Call->ReturnRegister;
1139f61eb416SIlia Diachkov   SPIRVType *GroupResultType = Call->ReturnType;
1140f61eb416SIlia Diachkov 
1141f61eb416SIlia Diachkov   // TODO: maybe we need to check whether the result type is already boolean
1142f61eb416SIlia Diachkov   // and in this case do not insert select instruction.
1143f61eb416SIlia Diachkov   const bool HasBoolReturnTy =
1144f61eb416SIlia Diachkov       GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||
1145f61eb416SIlia Diachkov       GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||
1146f61eb416SIlia Diachkov       GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;
1147f61eb416SIlia Diachkov 
1148f61eb416SIlia Diachkov   if (HasBoolReturnTy)
1149f61eb416SIlia Diachkov     std::tie(GroupResultRegister, GroupResultType) =
1150f61eb416SIlia Diachkov         buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1151f61eb416SIlia Diachkov 
1152395f9ce3SKazu Hirata   auto Scope = Builtin->Name.starts_with("sub_group") ? SPIRV::Scope::Subgroup
1153f61eb416SIlia Diachkov                                                       : SPIRV::Scope::Workgroup;
115467d3ef74SVyacheslav Levytskyy   Register ScopeRegister = buildConstantIntReg32(Scope, MIRBuilder, GR);
1155f61eb416SIlia Diachkov 
11562fc7a727SVyacheslav Levytskyy   Register VecReg;
11572fc7a727SVyacheslav Levytskyy   if (GroupBuiltin->Opcode == SPIRV::OpGroupBroadcast &&
11582fc7a727SVyacheslav Levytskyy       Call->Arguments.size() > 2) {
11592fc7a727SVyacheslav Levytskyy     // For OpGroupBroadcast "LocalId must be an integer datatype. It must be a
11602fc7a727SVyacheslav Levytskyy     // scalar, a vector with 2 components, or a vector with 3 components.",
11612fc7a727SVyacheslav Levytskyy     // meaning that we must create a vector from the function arguments if
11622fc7a727SVyacheslav Levytskyy     // it's a work_group_broadcast(val, local_id_x, local_id_y) or
11632fc7a727SVyacheslav Levytskyy     // work_group_broadcast(val, local_id_x, local_id_y, local_id_z) call.
11642fc7a727SVyacheslav Levytskyy     Register ElemReg = Call->Arguments[1];
11652fc7a727SVyacheslav Levytskyy     SPIRVType *ElemType = GR->getSPIRVTypeForVReg(ElemReg);
11662fc7a727SVyacheslav Levytskyy     if (!ElemType || ElemType->getOpcode() != SPIRV::OpTypeInt)
11672fc7a727SVyacheslav Levytskyy       report_fatal_error("Expect an integer <LocalId> argument");
11682fc7a727SVyacheslav Levytskyy     unsigned VecLen = Call->Arguments.size() - 1;
11692fc7a727SVyacheslav Levytskyy     VecReg = MRI->createGenericVirtualRegister(
11702fc7a727SVyacheslav Levytskyy         LLT::fixed_vector(VecLen, MRI->getType(ElemReg)));
11712fc7a727SVyacheslav Levytskyy     MRI->setRegClass(VecReg, &SPIRV::vIDRegClass);
11722fc7a727SVyacheslav Levytskyy     SPIRVType *VecType =
11732fc7a727SVyacheslav Levytskyy         GR->getOrCreateSPIRVVectorType(ElemType, VecLen, MIRBuilder);
11742fc7a727SVyacheslav Levytskyy     GR->assignSPIRVTypeToVReg(VecType, VecReg, MIRBuilder.getMF());
11752fc7a727SVyacheslav Levytskyy     auto MIB =
11762fc7a727SVyacheslav Levytskyy         MIRBuilder.buildInstr(TargetOpcode::G_BUILD_VECTOR).addDef(VecReg);
11772fc7a727SVyacheslav Levytskyy     for (unsigned i = 1; i < Call->Arguments.size(); i++) {
11782fc7a727SVyacheslav Levytskyy       MIB.addUse(Call->Arguments[i]);
1179978de2d6SVyacheslav Levytskyy       setRegClassIfNull(Call->Arguments[i], MRI, GR);
11802fc7a727SVyacheslav Levytskyy     }
11812fc7a727SVyacheslav Levytskyy     insertAssignInstr(VecReg, nullptr, VecType, GR, MIRBuilder,
11822fc7a727SVyacheslav Levytskyy                       MIRBuilder.getMF().getRegInfo());
11832fc7a727SVyacheslav Levytskyy   }
11842fc7a727SVyacheslav Levytskyy 
1185f61eb416SIlia Diachkov   // Build work/sub group instruction.
1186f61eb416SIlia Diachkov   auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1187f61eb416SIlia Diachkov                  .addDef(GroupResultRegister)
1188f61eb416SIlia Diachkov                  .addUse(GR->getSPIRVTypeID(GroupResultType))
1189f61eb416SIlia Diachkov                  .addUse(ScopeRegister);
1190f61eb416SIlia Diachkov 
1191f61eb416SIlia Diachkov   if (!GroupBuiltin->NoGroupOperation)
1192f61eb416SIlia Diachkov     MIB.addImm(GroupBuiltin->GroupOperation);
1193f61eb416SIlia Diachkov   if (Call->Arguments.size() > 0) {
1194f61eb416SIlia Diachkov     MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);
1195978de2d6SVyacheslav Levytskyy     setRegClassIfNull(Call->Arguments[0], MRI, GR);
11962fc7a727SVyacheslav Levytskyy     if (VecReg.isValid())
11972fc7a727SVyacheslav Levytskyy       MIB.addUse(VecReg);
11982fc7a727SVyacheslav Levytskyy     else
119967d3ef74SVyacheslav Levytskyy       for (unsigned i = 1; i < Call->Arguments.size(); i++)
1200f61eb416SIlia Diachkov         MIB.addUse(Call->Arguments[i]);
1201f61eb416SIlia Diachkov   }
1202f61eb416SIlia Diachkov 
1203f61eb416SIlia Diachkov   // Build select instruction.
1204f61eb416SIlia Diachkov   if (HasBoolReturnTy)
1205f61eb416SIlia Diachkov     buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,
1206f61eb416SIlia Diachkov                     Call->ReturnType, GR);
1207f61eb416SIlia Diachkov   return true;
1208f61eb416SIlia Diachkov }
1209f61eb416SIlia Diachkov 
1210b221b973SVyacheslav Levytskyy static bool generateIntelSubgroupsInst(const SPIRV::IncomingCall *Call,
1211b221b973SVyacheslav Levytskyy                                        MachineIRBuilder &MIRBuilder,
1212b221b973SVyacheslav Levytskyy                                        SPIRVGlobalRegistry *GR) {
1213b221b973SVyacheslav Levytskyy   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1214b221b973SVyacheslav Levytskyy   MachineFunction &MF = MIRBuilder.getMF();
1215b221b973SVyacheslav Levytskyy   const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
12164a6ecd38SViktoria Maximova   const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups =
12174a6ecd38SViktoria Maximova       SPIRV::lookupIntelSubgroupsBuiltin(Builtin->Name);
12184a6ecd38SViktoria Maximova 
12194a6ecd38SViktoria Maximova   if (IntelSubgroups->IsMedia &&
12204a6ecd38SViktoria Maximova       !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_media_block_io)) {
12214a6ecd38SViktoria Maximova     std::string DiagMsg = std::string(Builtin->Name) +
12224a6ecd38SViktoria Maximova                           ": the builtin requires the following SPIR-V "
12234a6ecd38SViktoria Maximova                           "extension: SPV_INTEL_media_block_io";
12244a6ecd38SViktoria Maximova     report_fatal_error(DiagMsg.c_str(), false);
12254a6ecd38SViktoria Maximova   } else if (!IntelSubgroups->IsMedia &&
12264a6ecd38SViktoria Maximova              !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1227b221b973SVyacheslav Levytskyy     std::string DiagMsg = std::string(Builtin->Name) +
1228b221b973SVyacheslav Levytskyy                           ": the builtin requires the following SPIR-V "
1229b221b973SVyacheslav Levytskyy                           "extension: SPV_INTEL_subgroups";
1230b221b973SVyacheslav Levytskyy     report_fatal_error(DiagMsg.c_str(), false);
1231b221b973SVyacheslav Levytskyy   }
1232b221b973SVyacheslav Levytskyy 
1233b221b973SVyacheslav Levytskyy   uint32_t OpCode = IntelSubgroups->Opcode;
123437cf0473SVyacheslav Levytskyy   if (Call->isSpirvOp()) {
123537cf0473SVyacheslav Levytskyy     bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL &&
12364a6ecd38SViktoria Maximova                  OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL &&
12374a6ecd38SViktoria Maximova                  OpCode != SPIRV::OpSubgroupImageMediaBlockWriteINTEL;
123837cf0473SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, OpCode, Call,
123937cf0473SVyacheslav Levytskyy                               IsSet ? GR->getSPIRVTypeID(Call->ReturnType)
124037cf0473SVyacheslav Levytskyy                                     : Register(0));
124137cf0473SVyacheslav Levytskyy   }
124237cf0473SVyacheslav Levytskyy 
1243b221b973SVyacheslav Levytskyy   if (IntelSubgroups->IsBlock) {
1244b221b973SVyacheslav Levytskyy     // Minimal number or arguments set in TableGen records is 1
1245b221b973SVyacheslav Levytskyy     if (SPIRVType *Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) {
1246b221b973SVyacheslav Levytskyy       if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) {
1247b221b973SVyacheslav Levytskyy         // TODO: add required validation from the specification:
1248b221b973SVyacheslav Levytskyy         // "'Image' must be an object whose type is OpTypeImage with a 'Sampled'
1249b221b973SVyacheslav Levytskyy         // operand of 0 or 2. If the 'Sampled' operand is 2, then some
1250b221b973SVyacheslav Levytskyy         // dimensions require a capability."
1251b221b973SVyacheslav Levytskyy         switch (OpCode) {
1252b221b973SVyacheslav Levytskyy         case SPIRV::OpSubgroupBlockReadINTEL:
1253b221b973SVyacheslav Levytskyy           OpCode = SPIRV::OpSubgroupImageBlockReadINTEL;
1254b221b973SVyacheslav Levytskyy           break;
1255b221b973SVyacheslav Levytskyy         case SPIRV::OpSubgroupBlockWriteINTEL:
1256b221b973SVyacheslav Levytskyy           OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL;
1257b221b973SVyacheslav Levytskyy           break;
1258b221b973SVyacheslav Levytskyy         }
1259b221b973SVyacheslav Levytskyy       }
1260b221b973SVyacheslav Levytskyy     }
1261b221b973SVyacheslav Levytskyy   }
1262b221b973SVyacheslav Levytskyy 
1263b221b973SVyacheslav Levytskyy   // TODO: opaque pointers types should be eventually resolved in such a way
1264b221b973SVyacheslav Levytskyy   // that validation of block read is enabled with respect to the following
1265b221b973SVyacheslav Levytskyy   // specification requirement:
1266b221b973SVyacheslav Levytskyy   // "'Result Type' may be a scalar or vector type, and its component type must
1267b221b973SVyacheslav Levytskyy   // be equal to the type pointed to by 'Ptr'."
1268b221b973SVyacheslav Levytskyy   // For example, function parameter type should not be default i8 pointer, but
1269b221b973SVyacheslav Levytskyy   // depend on the result type of the instruction where it is used as a pointer
1270b221b973SVyacheslav Levytskyy   // argument of OpSubgroupBlockReadINTEL
1271b221b973SVyacheslav Levytskyy 
1272b221b973SVyacheslav Levytskyy   // Build Intel subgroups instruction
1273b221b973SVyacheslav Levytskyy   MachineInstrBuilder MIB =
1274b221b973SVyacheslav Levytskyy       IntelSubgroups->IsWrite
1275b221b973SVyacheslav Levytskyy           ? MIRBuilder.buildInstr(OpCode)
1276b221b973SVyacheslav Levytskyy           : MIRBuilder.buildInstr(OpCode)
1277b221b973SVyacheslav Levytskyy                 .addDef(Call->ReturnRegister)
1278b221b973SVyacheslav Levytskyy                 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
127967d3ef74SVyacheslav Levytskyy   for (size_t i = 0; i < Call->Arguments.size(); ++i)
1280b221b973SVyacheslav Levytskyy     MIB.addUse(Call->Arguments[i]);
1281b221b973SVyacheslav Levytskyy   return true;
1282b221b973SVyacheslav Levytskyy }
1283b221b973SVyacheslav Levytskyy 
128466ebda46SVyacheslav Levytskyy static bool generateGroupUniformInst(const SPIRV::IncomingCall *Call,
128566ebda46SVyacheslav Levytskyy                                      MachineIRBuilder &MIRBuilder,
128666ebda46SVyacheslav Levytskyy                                      SPIRVGlobalRegistry *GR) {
128766ebda46SVyacheslav Levytskyy   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
128866ebda46SVyacheslav Levytskyy   MachineFunction &MF = MIRBuilder.getMF();
128966ebda46SVyacheslav Levytskyy   const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
129066ebda46SVyacheslav Levytskyy   if (!ST->canUseExtension(
129166ebda46SVyacheslav Levytskyy           SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {
129266ebda46SVyacheslav Levytskyy     std::string DiagMsg = std::string(Builtin->Name) +
129366ebda46SVyacheslav Levytskyy                           ": the builtin requires the following SPIR-V "
129466ebda46SVyacheslav Levytskyy                           "extension: SPV_KHR_uniform_group_instructions";
129566ebda46SVyacheslav Levytskyy     report_fatal_error(DiagMsg.c_str(), false);
129666ebda46SVyacheslav Levytskyy   }
129766ebda46SVyacheslav Levytskyy   const SPIRV::GroupUniformBuiltin *GroupUniform =
129866ebda46SVyacheslav Levytskyy       SPIRV::lookupGroupUniformBuiltin(Builtin->Name);
129966ebda46SVyacheslav Levytskyy   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
130066ebda46SVyacheslav Levytskyy 
130166ebda46SVyacheslav Levytskyy   Register GroupResultReg = Call->ReturnRegister;
130266ebda46SVyacheslav Levytskyy   Register ScopeReg = Call->Arguments[0];
130367d3ef74SVyacheslav Levytskyy   Register ValueReg = Call->Arguments[2];
130466ebda46SVyacheslav Levytskyy 
130566ebda46SVyacheslav Levytskyy   // Group Operation
130666ebda46SVyacheslav Levytskyy   Register ConstGroupOpReg = Call->Arguments[1];
130766ebda46SVyacheslav Levytskyy   const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI);
130866ebda46SVyacheslav Levytskyy   if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT)
130966ebda46SVyacheslav Levytskyy     report_fatal_error(
131066ebda46SVyacheslav Levytskyy         "expect a constant group operation for a uniform group instruction",
131166ebda46SVyacheslav Levytskyy         false);
131266ebda46SVyacheslav Levytskyy   const MachineOperand &ConstOperand = Const->getOperand(1);
131366ebda46SVyacheslav Levytskyy   if (!ConstOperand.isCImm())
131466ebda46SVyacheslav Levytskyy     report_fatal_error("uniform group instructions: group operation must be an "
131566ebda46SVyacheslav Levytskyy                        "integer constant",
131666ebda46SVyacheslav Levytskyy                        false);
131766ebda46SVyacheslav Levytskyy 
131866ebda46SVyacheslav Levytskyy   auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode)
131966ebda46SVyacheslav Levytskyy                  .addDef(GroupResultReg)
132066ebda46SVyacheslav Levytskyy                  .addUse(GR->getSPIRVTypeID(Call->ReturnType))
132166ebda46SVyacheslav Levytskyy                  .addUse(ScopeReg);
132266ebda46SVyacheslav Levytskyy   addNumImm(ConstOperand.getCImm()->getValue(), MIB);
132366ebda46SVyacheslav Levytskyy   MIB.addUse(ValueReg);
132466ebda46SVyacheslav Levytskyy 
132566ebda46SVyacheslav Levytskyy   return true;
132666ebda46SVyacheslav Levytskyy }
132766ebda46SVyacheslav Levytskyy 
132889c23f76SSven van Haastregt static bool generateKernelClockInst(const SPIRV::IncomingCall *Call,
132989c23f76SSven van Haastregt                                     MachineIRBuilder &MIRBuilder,
133089c23f76SSven van Haastregt                                     SPIRVGlobalRegistry *GR) {
133189c23f76SSven van Haastregt   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
133289c23f76SSven van Haastregt   MachineFunction &MF = MIRBuilder.getMF();
133389c23f76SSven van Haastregt   const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
133489c23f76SSven van Haastregt   if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) {
133589c23f76SSven van Haastregt     std::string DiagMsg = std::string(Builtin->Name) +
133689c23f76SSven van Haastregt                           ": the builtin requires the following SPIR-V "
133789c23f76SSven van Haastregt                           "extension: SPV_KHR_shader_clock";
133889c23f76SSven van Haastregt     report_fatal_error(DiagMsg.c_str(), false);
133989c23f76SSven van Haastregt   }
134089c23f76SSven van Haastregt 
134189c23f76SSven van Haastregt   Register ResultReg = Call->ReturnRegister;
134289c23f76SSven van Haastregt 
134389c23f76SSven van Haastregt   // Deduce the `Scope` operand from the builtin function name.
134489c23f76SSven van Haastregt   SPIRV::Scope::Scope ScopeArg =
134589c23f76SSven van Haastregt       StringSwitch<SPIRV::Scope::Scope>(Builtin->Name)
134689c23f76SSven van Haastregt           .EndsWith("device", SPIRV::Scope::Scope::Device)
134789c23f76SSven van Haastregt           .EndsWith("work_group", SPIRV::Scope::Scope::Workgroup)
134889c23f76SSven van Haastregt           .EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup);
134967d3ef74SVyacheslav Levytskyy   Register ScopeReg = buildConstantIntReg32(ScopeArg, MIRBuilder, GR);
135089c23f76SSven van Haastregt 
135189c23f76SSven van Haastregt   MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
135289c23f76SSven van Haastregt       .addDef(ResultReg)
135389c23f76SSven van Haastregt       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
135489c23f76SSven van Haastregt       .addUse(ScopeReg);
135589c23f76SSven van Haastregt 
135689c23f76SSven van Haastregt   return true;
135789c23f76SSven van Haastregt }
135889c23f76SSven van Haastregt 
1359f61eb416SIlia Diachkov // These queries ask for a single size_t result for a given dimension index, e.g
13603544d200SIlia Diachkov // size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to
1361f61eb416SIlia Diachkov // these values are all vec3 types, so we need to extract the correct index or
1362f61eb416SIlia Diachkov // return defaultVal (0 or 1 depending on the query). We also handle extending
1363f61eb416SIlia Diachkov // or tuncating in case size_t does not match the expected result type's
1364f61eb416SIlia Diachkov // bitwidth.
1365f61eb416SIlia Diachkov //
1366f61eb416SIlia Diachkov // For a constant index >= 3 we generate:
1367f61eb416SIlia Diachkov //  %res = OpConstant %SizeT 0
1368f61eb416SIlia Diachkov //
1369f61eb416SIlia Diachkov // For other indices we generate:
1370f61eb416SIlia Diachkov //  %g = OpVariable %ptr_V3_SizeT Input
1371f61eb416SIlia Diachkov //  OpDecorate %g BuiltIn XXX
1372f61eb416SIlia Diachkov //  OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"
1373f61eb416SIlia Diachkov //  OpDecorate %g Constant
1374f61eb416SIlia Diachkov //  %loadedVec = OpLoad %V3_SizeT %g
1375f61eb416SIlia Diachkov //
1376f61eb416SIlia Diachkov //  Then, if the index is constant < 3, we generate:
1377f61eb416SIlia Diachkov //    %res = OpCompositeExtract %SizeT %loadedVec idx
1378f61eb416SIlia Diachkov //  If the index is dynamic, we generate:
1379f61eb416SIlia Diachkov //    %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx
1380f61eb416SIlia Diachkov //    %cmp = OpULessThan %bool %idx %const_3
1381f61eb416SIlia Diachkov //    %res = OpSelect %SizeT %cmp %tmp %const_0
1382f61eb416SIlia Diachkov //
1383f61eb416SIlia Diachkov //  If the bitwidth of %res does not match the expected return type, we add an
1384f61eb416SIlia Diachkov //  extend or truncate.
1385f61eb416SIlia Diachkov static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call,
1386f61eb416SIlia Diachkov                               MachineIRBuilder &MIRBuilder,
1387f61eb416SIlia Diachkov                               SPIRVGlobalRegistry *GR,
1388f61eb416SIlia Diachkov                               SPIRV::BuiltIn::BuiltIn BuiltinValue,
1389f61eb416SIlia Diachkov                               uint64_t DefaultValue) {
1390f61eb416SIlia Diachkov   Register IndexRegister = Call->Arguments[0];
1391f61eb416SIlia Diachkov   const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
1392f61eb416SIlia Diachkov   const unsigned PointerSize = GR->getPointerSize();
1393f61eb416SIlia Diachkov   const SPIRVType *PointerSizeType =
1394f61eb416SIlia Diachkov       GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);
1395f61eb416SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1396f61eb416SIlia Diachkov   auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);
1397f61eb416SIlia Diachkov 
1398f61eb416SIlia Diachkov   // Set up the final register to do truncation or extension on at the end.
1399f61eb416SIlia Diachkov   Register ToTruncate = Call->ReturnRegister;
1400f61eb416SIlia Diachkov 
1401f61eb416SIlia Diachkov   // If the index is constant, we can statically determine if it is in range.
1402f61eb416SIlia Diachkov   bool IsConstantIndex =
1403f61eb416SIlia Diachkov       IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;
1404f61eb416SIlia Diachkov 
1405f61eb416SIlia Diachkov   // If it's out of range (max dimension is 3), we can just return the constant
1406f61eb416SIlia Diachkov   // default value (0 or 1 depending on which query function).
1407f61eb416SIlia Diachkov   if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {
140874c66710SIlia Diachkov     Register DefaultReg = Call->ReturnRegister;
1409f61eb416SIlia Diachkov     if (PointerSize != ResultWidth) {
141074c66710SIlia Diachkov       DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1411f9c98068SVyacheslav Levytskyy       MRI->setRegClass(DefaultReg, &SPIRV::iIDRegClass);
141274c66710SIlia Diachkov       GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg,
1413f61eb416SIlia Diachkov                                 MIRBuilder.getMF());
141474c66710SIlia Diachkov       ToTruncate = DefaultReg;
1415f61eb416SIlia Diachkov     }
1416f61eb416SIlia Diachkov     auto NewRegister =
1417f61eb416SIlia Diachkov         GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
141874c66710SIlia Diachkov     MIRBuilder.buildCopy(DefaultReg, NewRegister);
1419f61eb416SIlia Diachkov   } else { // If it could be in range, we need to load from the given builtin.
1420f61eb416SIlia Diachkov     auto Vec3Ty =
1421f61eb416SIlia Diachkov         GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder);
1422f61eb416SIlia Diachkov     Register LoadedVector =
1423f61eb416SIlia Diachkov         buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,
1424f61eb416SIlia Diachkov                                  LLT::fixed_vector(3, PointerSize));
1425f61eb416SIlia Diachkov     // Set up the vreg to extract the result to (possibly a new temporary one).
1426f61eb416SIlia Diachkov     Register Extracted = Call->ReturnRegister;
1427f61eb416SIlia Diachkov     if (!IsConstantIndex || PointerSize != ResultWidth) {
1428f61eb416SIlia Diachkov       Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1429f9c98068SVyacheslav Levytskyy       MRI->setRegClass(Extracted, &SPIRV::iIDRegClass);
1430f61eb416SIlia Diachkov       GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());
1431f61eb416SIlia Diachkov     }
1432f61eb416SIlia Diachkov     // Use Intrinsic::spv_extractelt so dynamic vs static extraction is
1433f61eb416SIlia Diachkov     // handled later: extr = spv_extractelt LoadedVector, IndexRegister.
1434f61eb416SIlia Diachkov     MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(
1435d9847cdeSSameer Sahasrabuddhe         Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false);
1436f61eb416SIlia Diachkov     ExtractInst.addUse(LoadedVector).addUse(IndexRegister);
1437f61eb416SIlia Diachkov 
1438f61eb416SIlia Diachkov     // If the index is dynamic, need check if it's < 3, and then use a select.
1439f61eb416SIlia Diachkov     if (!IsConstantIndex) {
1440f61eb416SIlia Diachkov       insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder,
1441f61eb416SIlia Diachkov                         *MRI);
1442f61eb416SIlia Diachkov 
1443f61eb416SIlia Diachkov       auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);
1444f61eb416SIlia Diachkov       auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
1445f61eb416SIlia Diachkov 
1446f61eb416SIlia Diachkov       Register CompareRegister =
1447f61eb416SIlia Diachkov           MRI->createGenericVirtualRegister(LLT::scalar(1));
1448f9c98068SVyacheslav Levytskyy       MRI->setRegClass(CompareRegister, &SPIRV::iIDRegClass);
1449f61eb416SIlia Diachkov       GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());
1450f61eb416SIlia Diachkov 
1451f61eb416SIlia Diachkov       // Use G_ICMP to check if idxVReg < 3.
1452f61eb416SIlia Diachkov       MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister,
1453f61eb416SIlia Diachkov                            GR->buildConstantInt(3, MIRBuilder, IndexType));
1454f61eb416SIlia Diachkov 
1455f61eb416SIlia Diachkov       // Get constant for the default value (0 or 1 depending on which
1456f61eb416SIlia Diachkov       // function).
1457f61eb416SIlia Diachkov       Register DefaultRegister =
1458f61eb416SIlia Diachkov           GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
1459f61eb416SIlia Diachkov 
1460f61eb416SIlia Diachkov       // Get a register for the selection result (possibly a new temporary one).
1461f61eb416SIlia Diachkov       Register SelectionResult = Call->ReturnRegister;
1462f61eb416SIlia Diachkov       if (PointerSize != ResultWidth) {
1463f61eb416SIlia Diachkov         SelectionResult =
1464f61eb416SIlia Diachkov             MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1465f9c98068SVyacheslav Levytskyy         MRI->setRegClass(SelectionResult, &SPIRV::iIDRegClass);
1466f61eb416SIlia Diachkov         GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,
1467f61eb416SIlia Diachkov                                   MIRBuilder.getMF());
1468f61eb416SIlia Diachkov       }
1469f61eb416SIlia Diachkov       // Create the final G_SELECT to return the extracted value or the default.
1470f61eb416SIlia Diachkov       MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,
1471f61eb416SIlia Diachkov                              DefaultRegister);
1472f61eb416SIlia Diachkov       ToTruncate = SelectionResult;
1473f61eb416SIlia Diachkov     } else {
1474f61eb416SIlia Diachkov       ToTruncate = Extracted;
1475f61eb416SIlia Diachkov     }
1476f61eb416SIlia Diachkov   }
1477f61eb416SIlia Diachkov   // Alter the result's bitwidth if it does not match the SizeT value extracted.
1478f61eb416SIlia Diachkov   if (PointerSize != ResultWidth)
1479f61eb416SIlia Diachkov     MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);
1480f61eb416SIlia Diachkov   return true;
1481f61eb416SIlia Diachkov }
1482f61eb416SIlia Diachkov 
1483f61eb416SIlia Diachkov static bool generateBuiltinVar(const SPIRV::IncomingCall *Call,
1484f61eb416SIlia Diachkov                                MachineIRBuilder &MIRBuilder,
1485f61eb416SIlia Diachkov                                SPIRVGlobalRegistry *GR) {
1486f61eb416SIlia Diachkov   // Lookup the builtin variable record.
1487f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1488f61eb416SIlia Diachkov   SPIRV::BuiltIn::BuiltIn Value =
1489f61eb416SIlia Diachkov       SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1490f61eb416SIlia Diachkov 
1491f61eb416SIlia Diachkov   if (Value == SPIRV::BuiltIn::GlobalInvocationId)
1492f61eb416SIlia Diachkov     return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);
1493f61eb416SIlia Diachkov 
1494f61eb416SIlia Diachkov   // Build a load instruction for the builtin variable.
1495f61eb416SIlia Diachkov   unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);
1496f61eb416SIlia Diachkov   LLT LLType;
1497f61eb416SIlia Diachkov   if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector)
1498f61eb416SIlia Diachkov     LLType =
1499f61eb416SIlia Diachkov         LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth);
1500f61eb416SIlia Diachkov   else
1501f61eb416SIlia Diachkov     LLType = LLT::scalar(BitWidth);
1502f61eb416SIlia Diachkov 
1503f61eb416SIlia Diachkov   return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,
1504f61eb416SIlia Diachkov                                   LLType, Call->ReturnRegister);
1505f61eb416SIlia Diachkov }
1506f61eb416SIlia Diachkov 
1507f61eb416SIlia Diachkov static bool generateAtomicInst(const SPIRV::IncomingCall *Call,
1508f61eb416SIlia Diachkov                                MachineIRBuilder &MIRBuilder,
1509f61eb416SIlia Diachkov                                SPIRVGlobalRegistry *GR) {
1510f61eb416SIlia Diachkov   // Lookup the instruction opcode in the TableGen records.
1511f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1512f61eb416SIlia Diachkov   unsigned Opcode =
1513f61eb416SIlia Diachkov       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1514f61eb416SIlia Diachkov 
1515f61eb416SIlia Diachkov   switch (Opcode) {
15165fb4a051SMichal Paszkowski   case SPIRV::OpStore:
15175fb4a051SMichal Paszkowski     return buildAtomicInitInst(Call, MIRBuilder);
1518f61eb416SIlia Diachkov   case SPIRV::OpAtomicLoad:
1519f61eb416SIlia Diachkov     return buildAtomicLoadInst(Call, MIRBuilder, GR);
1520f61eb416SIlia Diachkov   case SPIRV::OpAtomicStore:
1521f61eb416SIlia Diachkov     return buildAtomicStoreInst(Call, MIRBuilder, GR);
1522f61eb416SIlia Diachkov   case SPIRV::OpAtomicCompareExchange:
1523f61eb416SIlia Diachkov   case SPIRV::OpAtomicCompareExchangeWeak:
1524c2483ed5SVyacheslav Levytskyy     return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder,
1525c2483ed5SVyacheslav Levytskyy                                           GR);
1526f61eb416SIlia Diachkov   case SPIRV::OpAtomicIAdd:
1527f61eb416SIlia Diachkov   case SPIRV::OpAtomicISub:
1528f61eb416SIlia Diachkov   case SPIRV::OpAtomicOr:
1529f61eb416SIlia Diachkov   case SPIRV::OpAtomicXor:
1530f61eb416SIlia Diachkov   case SPIRV::OpAtomicAnd:
15315fb4a051SMichal Paszkowski   case SPIRV::OpAtomicExchange:
1532f61eb416SIlia Diachkov     return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
1533f61eb416SIlia Diachkov   case SPIRV::OpMemoryBarrier:
1534f61eb416SIlia Diachkov     return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
153587080898SMichal Paszkowski   case SPIRV::OpAtomicFlagTestAndSet:
153687080898SMichal Paszkowski   case SPIRV::OpAtomicFlagClear:
153787080898SMichal Paszkowski     return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);
1538f61eb416SIlia Diachkov   default:
153924cee1c4SVyacheslav Levytskyy     if (Call->isSpirvOp())
154024cee1c4SVyacheslav Levytskyy       return buildOpFromWrapper(MIRBuilder, Opcode, Call,
154124cee1c4SVyacheslav Levytskyy                                 GR->getSPIRVTypeID(Call->ReturnType));
1542f61eb416SIlia Diachkov     return false;
1543f61eb416SIlia Diachkov   }
1544f61eb416SIlia Diachkov }
1545f61eb416SIlia Diachkov 
1546925768eeSVyacheslav Levytskyy static bool generateAtomicFloatingInst(const SPIRV::IncomingCall *Call,
1547925768eeSVyacheslav Levytskyy                                        MachineIRBuilder &MIRBuilder,
1548925768eeSVyacheslav Levytskyy                                        SPIRVGlobalRegistry *GR) {
1549925768eeSVyacheslav Levytskyy   // Lookup the instruction opcode in the TableGen records.
1550925768eeSVyacheslav Levytskyy   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1551925768eeSVyacheslav Levytskyy   unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->Name)->Opcode;
1552925768eeSVyacheslav Levytskyy 
1553925768eeSVyacheslav Levytskyy   switch (Opcode) {
1554925768eeSVyacheslav Levytskyy   case SPIRV::OpAtomicFAddEXT:
1555925768eeSVyacheslav Levytskyy   case SPIRV::OpAtomicFMinEXT:
1556925768eeSVyacheslav Levytskyy   case SPIRV::OpAtomicFMaxEXT:
1557925768eeSVyacheslav Levytskyy     return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR);
1558925768eeSVyacheslav Levytskyy   default:
1559925768eeSVyacheslav Levytskyy     return false;
1560925768eeSVyacheslav Levytskyy   }
1561925768eeSVyacheslav Levytskyy }
1562925768eeSVyacheslav Levytskyy 
1563f61eb416SIlia Diachkov static bool generateBarrierInst(const SPIRV::IncomingCall *Call,
1564f61eb416SIlia Diachkov                                 MachineIRBuilder &MIRBuilder,
1565f61eb416SIlia Diachkov                                 SPIRVGlobalRegistry *GR) {
1566f61eb416SIlia Diachkov   // Lookup the instruction opcode in the TableGen records.
1567f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1568f61eb416SIlia Diachkov   unsigned Opcode =
1569f61eb416SIlia Diachkov       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1570f61eb416SIlia Diachkov 
1571f61eb416SIlia Diachkov   return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
1572f61eb416SIlia Diachkov }
1573f61eb416SIlia Diachkov 
157457520985SVyacheslav Levytskyy static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call,
157557520985SVyacheslav Levytskyy                                   MachineIRBuilder &MIRBuilder) {
157657520985SVyacheslav Levytskyy   MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)
157757520985SVyacheslav Levytskyy       .addDef(Call->ReturnRegister)
157857520985SVyacheslav Levytskyy       .addUse(Call->Arguments[0]);
157957520985SVyacheslav Levytskyy   return true;
158057520985SVyacheslav Levytskyy }
158157520985SVyacheslav Levytskyy 
1582f61eb416SIlia Diachkov static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call,
1583f61eb416SIlia Diachkov                                   MachineIRBuilder &MIRBuilder,
1584f61eb416SIlia Diachkov                                   SPIRVGlobalRegistry *GR) {
158524cee1c4SVyacheslav Levytskyy   if (Call->isSpirvOp())
158624cee1c4SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call,
158724cee1c4SVyacheslav Levytskyy                               GR->getSPIRVTypeID(Call->ReturnType));
1588f61eb416SIlia Diachkov   unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode();
1589f61eb416SIlia Diachkov   bool IsVec = Opcode == SPIRV::OpTypeVector;
1590f61eb416SIlia Diachkov   // Use OpDot only in case of vector args and OpFMul in case of scalar args.
1591f61eb416SIlia Diachkov   MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS)
1592f61eb416SIlia Diachkov       .addDef(Call->ReturnRegister)
1593f61eb416SIlia Diachkov       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1594f61eb416SIlia Diachkov       .addUse(Call->Arguments[0])
1595f61eb416SIlia Diachkov       .addUse(Call->Arguments[1]);
1596f61eb416SIlia Diachkov   return true;
1597f61eb416SIlia Diachkov }
1598f61eb416SIlia Diachkov 
1599f0eb9083SNathan Gauër static bool generateWaveInst(const SPIRV::IncomingCall *Call,
1600f0eb9083SNathan Gauër                              MachineIRBuilder &MIRBuilder,
1601f0eb9083SNathan Gauër                              SPIRVGlobalRegistry *GR) {
1602f0eb9083SNathan Gauër   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1603f0eb9083SNathan Gauër   SPIRV::BuiltIn::BuiltIn Value =
1604f0eb9083SNathan Gauër       SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1605f0eb9083SNathan Gauër 
1606f0eb9083SNathan Gauër   // For now, we only support a single Wave intrinsic with a single return type.
1607f0eb9083SNathan Gauër   assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt);
1608f0eb9083SNathan Gauër   LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType));
1609f0eb9083SNathan Gauër 
1610f0eb9083SNathan Gauër   return buildBuiltinVariableLoad(
1611f0eb9083SNathan Gauër       MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister,
1612f0eb9083SNathan Gauër       /* isConst= */ false, /* hasLinkageTy= */ false);
1613f0eb9083SNathan Gauër }
1614f0eb9083SNathan Gauër 
16158ac46d6bSVyacheslav Levytskyy // We expect a builtin
16168ac46d6bSVyacheslav Levytskyy //     Name(ptr sret([RetType]) %result, Type %operand1, Type %operand1)
16178ac46d6bSVyacheslav Levytskyy // where %result is a pointer to where the result of the builtin execution
16188ac46d6bSVyacheslav Levytskyy // is to be stored, and generate the following instructions:
16198ac46d6bSVyacheslav Levytskyy //     Res = Opcode RetType Operand1 Operand1
16208ac46d6bSVyacheslav Levytskyy //     OpStore RetVariable Res
16218ac46d6bSVyacheslav Levytskyy static bool generateICarryBorrowInst(const SPIRV::IncomingCall *Call,
16228ac46d6bSVyacheslav Levytskyy                                      MachineIRBuilder &MIRBuilder,
16238ac46d6bSVyacheslav Levytskyy                                      SPIRVGlobalRegistry *GR) {
16248ac46d6bSVyacheslav Levytskyy   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
16258ac46d6bSVyacheslav Levytskyy   unsigned Opcode =
16268ac46d6bSVyacheslav Levytskyy       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
16278ac46d6bSVyacheslav Levytskyy 
16288ac46d6bSVyacheslav Levytskyy   Register SRetReg = Call->Arguments[0];
16298ac46d6bSVyacheslav Levytskyy   SPIRVType *PtrRetType = GR->getSPIRVTypeForVReg(SRetReg);
16308ac46d6bSVyacheslav Levytskyy   SPIRVType *RetType = GR->getPointeeType(PtrRetType);
16318ac46d6bSVyacheslav Levytskyy   if (!RetType)
16328ac46d6bSVyacheslav Levytskyy     report_fatal_error("The first parameter must be a pointer");
16338ac46d6bSVyacheslav Levytskyy   if (RetType->getOpcode() != SPIRV::OpTypeStruct)
16348ac46d6bSVyacheslav Levytskyy     report_fatal_error("Expected struct type result for the arithmetic with "
16358ac46d6bSVyacheslav Levytskyy                        "overflow builtins");
16368ac46d6bSVyacheslav Levytskyy 
16378ac46d6bSVyacheslav Levytskyy   SPIRVType *OpType1 = GR->getSPIRVTypeForVReg(Call->Arguments[1]);
16388ac46d6bSVyacheslav Levytskyy   SPIRVType *OpType2 = GR->getSPIRVTypeForVReg(Call->Arguments[2]);
16398ac46d6bSVyacheslav Levytskyy   if (!OpType1 || !OpType2 || OpType1 != OpType2)
16408ac46d6bSVyacheslav Levytskyy     report_fatal_error("Operands must have the same type");
16418ac46d6bSVyacheslav Levytskyy   if (OpType1->getOpcode() == SPIRV::OpTypeVector)
16428ac46d6bSVyacheslav Levytskyy     switch (Opcode) {
16438ac46d6bSVyacheslav Levytskyy     case SPIRV::OpIAddCarryS:
16448ac46d6bSVyacheslav Levytskyy       Opcode = SPIRV::OpIAddCarryV;
16458ac46d6bSVyacheslav Levytskyy       break;
16468ac46d6bSVyacheslav Levytskyy     case SPIRV::OpISubBorrowS:
16478ac46d6bSVyacheslav Levytskyy       Opcode = SPIRV::OpISubBorrowV;
16488ac46d6bSVyacheslav Levytskyy       break;
16498ac46d6bSVyacheslav Levytskyy     }
16508ac46d6bSVyacheslav Levytskyy 
16518ac46d6bSVyacheslav Levytskyy   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
165242633cf2SVyacheslav Levytskyy   Register ResReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
165342633cf2SVyacheslav Levytskyy   if (const TargetRegisterClass *DstRC =
165442633cf2SVyacheslav Levytskyy           MRI->getRegClassOrNull(Call->Arguments[1])) {
165542633cf2SVyacheslav Levytskyy     MRI->setRegClass(ResReg, DstRC);
165642633cf2SVyacheslav Levytskyy     MRI->setType(ResReg, MRI->getType(Call->Arguments[1]));
165742633cf2SVyacheslav Levytskyy   } else {
165842633cf2SVyacheslav Levytskyy     MRI->setType(ResReg, LLT::scalar(64));
165942633cf2SVyacheslav Levytskyy   }
16608ac46d6bSVyacheslav Levytskyy   GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
16618ac46d6bSVyacheslav Levytskyy   MIRBuilder.buildInstr(Opcode)
16628ac46d6bSVyacheslav Levytskyy       .addDef(ResReg)
16638ac46d6bSVyacheslav Levytskyy       .addUse(GR->getSPIRVTypeID(RetType))
16648ac46d6bSVyacheslav Levytskyy       .addUse(Call->Arguments[1])
16658ac46d6bSVyacheslav Levytskyy       .addUse(Call->Arguments[2]);
16668ac46d6bSVyacheslav Levytskyy   MIRBuilder.buildInstr(SPIRV::OpStore).addUse(SRetReg).addUse(ResReg);
16678ac46d6bSVyacheslav Levytskyy   return true;
16688ac46d6bSVyacheslav Levytskyy }
16698ac46d6bSVyacheslav Levytskyy 
1670f61eb416SIlia Diachkov static bool generateGetQueryInst(const SPIRV::IncomingCall *Call,
1671f61eb416SIlia Diachkov                                  MachineIRBuilder &MIRBuilder,
1672f61eb416SIlia Diachkov                                  SPIRVGlobalRegistry *GR) {
1673f61eb416SIlia Diachkov   // Lookup the builtin record.
1674f61eb416SIlia Diachkov   SPIRV::BuiltIn::BuiltIn Value =
1675f61eb416SIlia Diachkov       SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value;
1676f61eb416SIlia Diachkov   uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize ||
1677f61eb416SIlia Diachkov                         Value == SPIRV::BuiltIn::WorkgroupSize ||
1678f61eb416SIlia Diachkov                         Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);
1679f61eb416SIlia Diachkov   return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0);
1680f61eb416SIlia Diachkov }
1681f61eb416SIlia Diachkov 
1682f61eb416SIlia Diachkov static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call,
1683f61eb416SIlia Diachkov                                        MachineIRBuilder &MIRBuilder,
1684f61eb416SIlia Diachkov                                        SPIRVGlobalRegistry *GR) {
1685f61eb416SIlia Diachkov   // Lookup the image size query component number in the TableGen records.
1686f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1687f61eb416SIlia Diachkov   uint32_t Component =
1688f61eb416SIlia Diachkov       SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component;
1689f61eb416SIlia Diachkov   // Query result may either be a vector or a scalar. If return type is not a
1690f61eb416SIlia Diachkov   // vector, expect only a single size component. Otherwise get the number of
1691f61eb416SIlia Diachkov   // expected components.
1692f61eb416SIlia Diachkov   SPIRVType *RetTy = Call->ReturnType;
1693f61eb416SIlia Diachkov   unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector
1694f61eb416SIlia Diachkov                                           ? RetTy->getOperand(2).getImm()
1695f61eb416SIlia Diachkov                                           : 1;
1696f61eb416SIlia Diachkov   // Get the actual number of query result/size components.
1697f61eb416SIlia Diachkov   SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1698f61eb416SIlia Diachkov   unsigned NumActualRetComponents = getNumSizeComponents(ImgType);
1699f61eb416SIlia Diachkov   Register QueryResult = Call->ReturnRegister;
1700f61eb416SIlia Diachkov   SPIRVType *QueryResultType = Call->ReturnType;
1701f61eb416SIlia Diachkov   if (NumExpectedRetComponents != NumActualRetComponents) {
1702f61eb416SIlia Diachkov     QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(
1703f61eb416SIlia Diachkov         LLT::fixed_vector(NumActualRetComponents, 32));
170467d3ef74SVyacheslav Levytskyy     MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::vIDRegClass);
1705f61eb416SIlia Diachkov     SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
1706f61eb416SIlia Diachkov     QueryResultType = GR->getOrCreateSPIRVVectorType(
1707f61eb416SIlia Diachkov         IntTy, NumActualRetComponents, MIRBuilder);
1708f61eb416SIlia Diachkov     GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());
1709f61eb416SIlia Diachkov   }
1710f61eb416SIlia Diachkov   bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;
1711f61eb416SIlia Diachkov   unsigned Opcode =
1712f61eb416SIlia Diachkov       IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;
1713f61eb416SIlia Diachkov   auto MIB = MIRBuilder.buildInstr(Opcode)
1714f61eb416SIlia Diachkov                  .addDef(QueryResult)
1715f61eb416SIlia Diachkov                  .addUse(GR->getSPIRVTypeID(QueryResultType))
1716f61eb416SIlia Diachkov                  .addUse(Call->Arguments[0]);
1717f61eb416SIlia Diachkov   if (!IsDimBuf)
171867d3ef74SVyacheslav Levytskyy     MIB.addUse(buildConstantIntReg32(0, MIRBuilder, GR)); // Lod id.
1719f61eb416SIlia Diachkov   if (NumExpectedRetComponents == NumActualRetComponents)
1720f61eb416SIlia Diachkov     return true;
1721f61eb416SIlia Diachkov   if (NumExpectedRetComponents == 1) {
1722f61eb416SIlia Diachkov     // Only 1 component is expected, build OpCompositeExtract instruction.
1723f61eb416SIlia Diachkov     unsigned ExtractedComposite =
1724f61eb416SIlia Diachkov         Component == 3 ? NumActualRetComponents - 1 : Component;
1725f61eb416SIlia Diachkov     assert(ExtractedComposite < NumActualRetComponents &&
1726f61eb416SIlia Diachkov            "Invalid composite index!");
1727505cd125SVyacheslav Levytskyy     Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
1728505cd125SVyacheslav Levytskyy     SPIRVType *NewType = nullptr;
1729505cd125SVyacheslav Levytskyy     if (QueryResultType->getOpcode() == SPIRV::OpTypeVector) {
1730505cd125SVyacheslav Levytskyy       Register NewTypeReg = QueryResultType->getOperand(1).getReg();
1731505cd125SVyacheslav Levytskyy       if (TypeReg != NewTypeReg &&
1732505cd125SVyacheslav Levytskyy           (NewType = GR->getSPIRVTypeForVReg(NewTypeReg)) != nullptr)
1733505cd125SVyacheslav Levytskyy         TypeReg = NewTypeReg;
1734505cd125SVyacheslav Levytskyy     }
1735f61eb416SIlia Diachkov     MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1736f61eb416SIlia Diachkov         .addDef(Call->ReturnRegister)
1737505cd125SVyacheslav Levytskyy         .addUse(TypeReg)
1738f61eb416SIlia Diachkov         .addUse(QueryResult)
1739f61eb416SIlia Diachkov         .addImm(ExtractedComposite);
1740505cd125SVyacheslav Levytskyy     if (NewType != nullptr)
1741505cd125SVyacheslav Levytskyy       insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
1742505cd125SVyacheslav Levytskyy                         MIRBuilder.getMF().getRegInfo());
1743f61eb416SIlia Diachkov   } else {
1744f61eb416SIlia Diachkov     // More than 1 component is expected, fill a new vector.
1745f61eb416SIlia Diachkov     auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)
1746f61eb416SIlia Diachkov                    .addDef(Call->ReturnRegister)
1747f61eb416SIlia Diachkov                    .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1748f61eb416SIlia Diachkov                    .addUse(QueryResult)
1749f61eb416SIlia Diachkov                    .addUse(QueryResult);
1750f61eb416SIlia Diachkov     for (unsigned i = 0; i < NumExpectedRetComponents; ++i)
1751f61eb416SIlia Diachkov       MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);
1752f61eb416SIlia Diachkov   }
1753f61eb416SIlia Diachkov   return true;
1754f61eb416SIlia Diachkov }
1755f61eb416SIlia Diachkov 
1756f61eb416SIlia Diachkov static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call,
1757f61eb416SIlia Diachkov                                        MachineIRBuilder &MIRBuilder,
1758f61eb416SIlia Diachkov                                        SPIRVGlobalRegistry *GR) {
1759f61eb416SIlia Diachkov   assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&
1760f61eb416SIlia Diachkov          "Image samples query result must be of int type!");
17616beac40fSMichal Paszkowski 
17626beac40fSMichal Paszkowski   // Lookup the instruction opcode in the TableGen records.
17636beac40fSMichal Paszkowski   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
17646beac40fSMichal Paszkowski   unsigned Opcode =
17656beac40fSMichal Paszkowski       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
17666beac40fSMichal Paszkowski 
17676beac40fSMichal Paszkowski   Register Image = Call->Arguments[0];
17686beac40fSMichal Paszkowski   SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(
17696beac40fSMichal Paszkowski       GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());
1770b221b973SVyacheslav Levytskyy   (void)ImageDimensionality;
17716beac40fSMichal Paszkowski 
17726beac40fSMichal Paszkowski   switch (Opcode) {
17736beac40fSMichal Paszkowski   case SPIRV::OpImageQuerySamples:
17746beac40fSMichal Paszkowski     assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&
1775f61eb416SIlia Diachkov            "Image must be of 2D dimensionality");
17766beac40fSMichal Paszkowski     break;
17776beac40fSMichal Paszkowski   case SPIRV::OpImageQueryLevels:
17786beac40fSMichal Paszkowski     assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||
17796beac40fSMichal Paszkowski             ImageDimensionality == SPIRV::Dim::DIM_2D ||
17806beac40fSMichal Paszkowski             ImageDimensionality == SPIRV::Dim::DIM_3D ||
17816beac40fSMichal Paszkowski             ImageDimensionality == SPIRV::Dim::DIM_Cube) &&
17826beac40fSMichal Paszkowski            "Image must be of 1D/2D/3D/Cube dimensionality");
17836beac40fSMichal Paszkowski     break;
17846beac40fSMichal Paszkowski   }
17856beac40fSMichal Paszkowski 
17866beac40fSMichal Paszkowski   MIRBuilder.buildInstr(Opcode)
1787f61eb416SIlia Diachkov       .addDef(Call->ReturnRegister)
1788f61eb416SIlia Diachkov       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1789f61eb416SIlia Diachkov       .addUse(Image);
1790f61eb416SIlia Diachkov   return true;
1791f61eb416SIlia Diachkov }
1792f61eb416SIlia Diachkov 
1793f61eb416SIlia Diachkov // TODO: Move to TableGen.
1794f61eb416SIlia Diachkov static SPIRV::SamplerAddressingMode::SamplerAddressingMode
1795f61eb416SIlia Diachkov getSamplerAddressingModeFromBitmask(unsigned Bitmask) {
1796f61eb416SIlia Diachkov   switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {
1797f61eb416SIlia Diachkov   case SPIRV::CLK_ADDRESS_CLAMP:
1798f61eb416SIlia Diachkov     return SPIRV::SamplerAddressingMode::Clamp;
1799f61eb416SIlia Diachkov   case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:
1800f61eb416SIlia Diachkov     return SPIRV::SamplerAddressingMode::ClampToEdge;
1801f61eb416SIlia Diachkov   case SPIRV::CLK_ADDRESS_REPEAT:
1802f61eb416SIlia Diachkov     return SPIRV::SamplerAddressingMode::Repeat;
1803f61eb416SIlia Diachkov   case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:
1804f61eb416SIlia Diachkov     return SPIRV::SamplerAddressingMode::RepeatMirrored;
1805f61eb416SIlia Diachkov   case SPIRV::CLK_ADDRESS_NONE:
1806f61eb416SIlia Diachkov     return SPIRV::SamplerAddressingMode::None;
1807f61eb416SIlia Diachkov   default:
1808925768eeSVyacheslav Levytskyy     report_fatal_error("Unknown CL address mode");
1809f61eb416SIlia Diachkov   }
1810f61eb416SIlia Diachkov }
1811f61eb416SIlia Diachkov 
1812f61eb416SIlia Diachkov static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {
1813f61eb416SIlia Diachkov   return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;
1814f61eb416SIlia Diachkov }
1815f61eb416SIlia Diachkov 
1816f61eb416SIlia Diachkov static SPIRV::SamplerFilterMode::SamplerFilterMode
1817f61eb416SIlia Diachkov getSamplerFilterModeFromBitmask(unsigned Bitmask) {
1818f61eb416SIlia Diachkov   if (Bitmask & SPIRV::CLK_FILTER_LINEAR)
1819f61eb416SIlia Diachkov     return SPIRV::SamplerFilterMode::Linear;
1820f61eb416SIlia Diachkov   if (Bitmask & SPIRV::CLK_FILTER_NEAREST)
1821f61eb416SIlia Diachkov     return SPIRV::SamplerFilterMode::Nearest;
1822f61eb416SIlia Diachkov   return SPIRV::SamplerFilterMode::Nearest;
1823f61eb416SIlia Diachkov }
1824f61eb416SIlia Diachkov 
1825f61eb416SIlia Diachkov static bool generateReadImageInst(const StringRef DemangledCall,
1826f61eb416SIlia Diachkov                                   const SPIRV::IncomingCall *Call,
1827f61eb416SIlia Diachkov                                   MachineIRBuilder &MIRBuilder,
1828f61eb416SIlia Diachkov                                   SPIRVGlobalRegistry *GR) {
1829f61eb416SIlia Diachkov   Register Image = Call->Arguments[0];
1830f61eb416SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
183174c66710SIlia Diachkov   bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler");
183274c66710SIlia Diachkov   bool HasMsaa = DemangledCall.contains_insensitive("msaa");
183374c66710SIlia Diachkov   if (HasOclSampler) {
1834f61eb416SIlia Diachkov     Register Sampler = Call->Arguments[1];
1835f61eb416SIlia Diachkov 
1836f61eb416SIlia Diachkov     if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&
1837f61eb416SIlia Diachkov         getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {
1838f61eb416SIlia Diachkov       uint64_t SamplerMask = getIConstVal(Sampler, MRI);
1839f61eb416SIlia Diachkov       Sampler = GR->buildConstantSampler(
1840f61eb416SIlia Diachkov           Register(), getSamplerAddressingModeFromBitmask(SamplerMask),
1841f61eb416SIlia Diachkov           getSamplerParamFromBitmask(SamplerMask),
1842f61eb416SIlia Diachkov           getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder,
1843f61eb416SIlia Diachkov           GR->getSPIRVTypeForVReg(Sampler));
1844f61eb416SIlia Diachkov     }
1845f61eb416SIlia Diachkov     SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1846f61eb416SIlia Diachkov     SPIRVType *SampledImageType =
1847f61eb416SIlia Diachkov         GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1848f9c98068SVyacheslav Levytskyy     Register SampledImage = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
1849f61eb416SIlia Diachkov 
1850f61eb416SIlia Diachkov     MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1851f61eb416SIlia Diachkov         .addDef(SampledImage)
1852f61eb416SIlia Diachkov         .addUse(GR->getSPIRVTypeID(SampledImageType))
1853f61eb416SIlia Diachkov         .addUse(Image)
1854f61eb416SIlia Diachkov         .addUse(Sampler);
1855f61eb416SIlia Diachkov 
1856f61eb416SIlia Diachkov     Register Lod = GR->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()),
1857f61eb416SIlia Diachkov                                        MIRBuilder);
1858f61eb416SIlia Diachkov 
185967d3ef74SVyacheslav Levytskyy     if (Call->ReturnType->getOpcode() != SPIRV::OpTypeVector) {
186067d3ef74SVyacheslav Levytskyy       SPIRVType *TempType =
186167d3ef74SVyacheslav Levytskyy           GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder);
186267d3ef74SVyacheslav Levytskyy       Register TempRegister =
186367d3ef74SVyacheslav Levytskyy           MRI->createGenericVirtualRegister(GR->getRegType(TempType));
186467d3ef74SVyacheslav Levytskyy       MRI->setRegClass(TempRegister, GR->getRegClass(TempType));
186567d3ef74SVyacheslav Levytskyy       GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());
1866f61eb416SIlia Diachkov       MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
186767d3ef74SVyacheslav Levytskyy           .addDef(TempRegister)
1868f61eb416SIlia Diachkov           .addUse(GR->getSPIRVTypeID(TempType))
1869f61eb416SIlia Diachkov           .addUse(SampledImage)
1870f61eb416SIlia Diachkov           .addUse(Call->Arguments[2]) // Coordinate.
1871f61eb416SIlia Diachkov           .addImm(SPIRV::ImageOperand::Lod)
1872f61eb416SIlia Diachkov           .addUse(Lod);
1873f61eb416SIlia Diachkov       MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1874f61eb416SIlia Diachkov           .addDef(Call->ReturnRegister)
1875f61eb416SIlia Diachkov           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1876f61eb416SIlia Diachkov           .addUse(TempRegister)
1877f61eb416SIlia Diachkov           .addImm(0);
187867d3ef74SVyacheslav Levytskyy     } else {
187967d3ef74SVyacheslav Levytskyy       MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
188067d3ef74SVyacheslav Levytskyy           .addDef(Call->ReturnRegister)
188167d3ef74SVyacheslav Levytskyy           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
188267d3ef74SVyacheslav Levytskyy           .addUse(SampledImage)
188367d3ef74SVyacheslav Levytskyy           .addUse(Call->Arguments[2]) // Coordinate.
188467d3ef74SVyacheslav Levytskyy           .addImm(SPIRV::ImageOperand::Lod)
188567d3ef74SVyacheslav Levytskyy           .addUse(Lod);
188667d3ef74SVyacheslav Levytskyy     }
188774c66710SIlia Diachkov   } else if (HasMsaa) {
1888f61eb416SIlia Diachkov     MIRBuilder.buildInstr(SPIRV::OpImageRead)
1889f61eb416SIlia Diachkov         .addDef(Call->ReturnRegister)
1890f61eb416SIlia Diachkov         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1891f61eb416SIlia Diachkov         .addUse(Image)
1892f61eb416SIlia Diachkov         .addUse(Call->Arguments[1]) // Coordinate.
1893f61eb416SIlia Diachkov         .addImm(SPIRV::ImageOperand::Sample)
1894f61eb416SIlia Diachkov         .addUse(Call->Arguments[2]);
1895f61eb416SIlia Diachkov   } else {
1896f61eb416SIlia Diachkov     MIRBuilder.buildInstr(SPIRV::OpImageRead)
1897f61eb416SIlia Diachkov         .addDef(Call->ReturnRegister)
1898f61eb416SIlia Diachkov         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1899f61eb416SIlia Diachkov         .addUse(Image)
1900f61eb416SIlia Diachkov         .addUse(Call->Arguments[1]); // Coordinate.
1901f61eb416SIlia Diachkov   }
1902f61eb416SIlia Diachkov   return true;
1903f61eb416SIlia Diachkov }
1904f61eb416SIlia Diachkov 
1905f61eb416SIlia Diachkov static bool generateWriteImageInst(const SPIRV::IncomingCall *Call,
1906f61eb416SIlia Diachkov                                    MachineIRBuilder &MIRBuilder,
1907f61eb416SIlia Diachkov                                    SPIRVGlobalRegistry *GR) {
1908f61eb416SIlia Diachkov   MIRBuilder.buildInstr(SPIRV::OpImageWrite)
1909f61eb416SIlia Diachkov       .addUse(Call->Arguments[0])  // Image.
1910f61eb416SIlia Diachkov       .addUse(Call->Arguments[1])  // Coordinate.
1911f61eb416SIlia Diachkov       .addUse(Call->Arguments[2]); // Texel.
1912f61eb416SIlia Diachkov   return true;
1913f61eb416SIlia Diachkov }
1914f61eb416SIlia Diachkov 
1915f61eb416SIlia Diachkov static bool generateSampleImageInst(const StringRef DemangledCall,
1916f61eb416SIlia Diachkov                                     const SPIRV::IncomingCall *Call,
1917f61eb416SIlia Diachkov                                     MachineIRBuilder &MIRBuilder,
1918f61eb416SIlia Diachkov                                     SPIRVGlobalRegistry *GR) {
191974c66710SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1920f61eb416SIlia Diachkov   if (Call->Builtin->Name.contains_insensitive(
1921f61eb416SIlia Diachkov           "__translate_sampler_initializer")) {
1922f61eb416SIlia Diachkov     // Build sampler literal.
192374c66710SIlia Diachkov     uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI);
1924f61eb416SIlia Diachkov     Register Sampler = GR->buildConstantSampler(
1925f61eb416SIlia Diachkov         Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),
1926f61eb416SIlia Diachkov         getSamplerParamFromBitmask(Bitmask),
1927f61eb416SIlia Diachkov         getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType);
1928f61eb416SIlia Diachkov     return Sampler.isValid();
1929f61eb416SIlia Diachkov   } else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) {
1930f61eb416SIlia Diachkov     // Create OpSampledImage.
1931f61eb416SIlia Diachkov     Register Image = Call->Arguments[0];
1932f61eb416SIlia Diachkov     SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1933f61eb416SIlia Diachkov     SPIRVType *SampledImageType =
1934f61eb416SIlia Diachkov         GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1935f61eb416SIlia Diachkov     Register SampledImage =
1936f61eb416SIlia Diachkov         Call->ReturnRegister.isValid()
1937f61eb416SIlia Diachkov             ? Call->ReturnRegister
1938f9c98068SVyacheslav Levytskyy             : MRI->createVirtualRegister(&SPIRV::iIDRegClass);
1939f61eb416SIlia Diachkov     MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1940f61eb416SIlia Diachkov         .addDef(SampledImage)
1941f61eb416SIlia Diachkov         .addUse(GR->getSPIRVTypeID(SampledImageType))
1942f61eb416SIlia Diachkov         .addUse(Image)
1943f61eb416SIlia Diachkov         .addUse(Call->Arguments[1]); // Sampler.
1944f61eb416SIlia Diachkov     return true;
1945f61eb416SIlia Diachkov   } else if (Call->Builtin->Name.contains_insensitive(
1946f61eb416SIlia Diachkov                  "__spirv_ImageSampleExplicitLod")) {
1947f61eb416SIlia Diachkov     // Sample an image using an explicit level of detail.
1948f61eb416SIlia Diachkov     std::string ReturnType = DemangledCall.str();
1949f61eb416SIlia Diachkov     if (DemangledCall.contains("_R")) {
1950f61eb416SIlia Diachkov       ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);
1951f61eb416SIlia Diachkov       ReturnType = ReturnType.substr(0, ReturnType.find('('));
1952f61eb416SIlia Diachkov     }
1953eb989f62SVyacheslav Levytskyy     SPIRVType *Type =
1954eb989f62SVyacheslav Levytskyy         Call->ReturnType
1955eb989f62SVyacheslav Levytskyy             ? Call->ReturnType
1956eb989f62SVyacheslav Levytskyy             : GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder);
19575a07774fSVyacheslav Levytskyy     if (!Type) {
19585a07774fSVyacheslav Levytskyy       std::string DiagMsg =
19595a07774fSVyacheslav Levytskyy           "Unable to recognize SPIRV type name: " + ReturnType;
19605a07774fSVyacheslav Levytskyy       report_fatal_error(DiagMsg.c_str());
19615a07774fSVyacheslav Levytskyy     }
1962f61eb416SIlia Diachkov     MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1963f61eb416SIlia Diachkov         .addDef(Call->ReturnRegister)
1964f61eb416SIlia Diachkov         .addUse(GR->getSPIRVTypeID(Type))
1965f61eb416SIlia Diachkov         .addUse(Call->Arguments[0]) // Image.
1966f61eb416SIlia Diachkov         .addUse(Call->Arguments[1]) // Coordinate.
1967f61eb416SIlia Diachkov         .addImm(SPIRV::ImageOperand::Lod)
1968f61eb416SIlia Diachkov         .addUse(Call->Arguments[3]);
1969f61eb416SIlia Diachkov     return true;
1970f61eb416SIlia Diachkov   }
1971f61eb416SIlia Diachkov   return false;
1972f61eb416SIlia Diachkov }
1973f61eb416SIlia Diachkov 
1974f61eb416SIlia Diachkov static bool generateSelectInst(const SPIRV::IncomingCall *Call,
1975f61eb416SIlia Diachkov                                MachineIRBuilder &MIRBuilder) {
1976f61eb416SIlia Diachkov   MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],
1977f61eb416SIlia Diachkov                          Call->Arguments[1], Call->Arguments[2]);
1978f61eb416SIlia Diachkov   return true;
1979f61eb416SIlia Diachkov }
1980f61eb416SIlia Diachkov 
198157f79371SVyacheslav Levytskyy static bool generateConstructInst(const SPIRV::IncomingCall *Call,
198257f79371SVyacheslav Levytskyy                                   MachineIRBuilder &MIRBuilder,
198357f79371SVyacheslav Levytskyy                                   SPIRVGlobalRegistry *GR) {
198457f79371SVyacheslav Levytskyy   return buildOpFromWrapper(MIRBuilder, SPIRV::OpCompositeConstruct, Call,
198557f79371SVyacheslav Levytskyy                             GR->getSPIRVTypeID(Call->ReturnType));
198657f79371SVyacheslav Levytskyy }
198757f79371SVyacheslav Levytskyy 
198857f79371SVyacheslav Levytskyy static bool generateCoopMatrInst(const SPIRV::IncomingCall *Call,
198957f79371SVyacheslav Levytskyy                                  MachineIRBuilder &MIRBuilder,
199057f79371SVyacheslav Levytskyy                                  SPIRVGlobalRegistry *GR) {
199157f79371SVyacheslav Levytskyy   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
199257f79371SVyacheslav Levytskyy   unsigned Opcode =
199357f79371SVyacheslav Levytskyy       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1994d057b53aSDmitry Sidorov   bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR &&
1995d057b53aSDmitry Sidorov                Opcode != SPIRV::OpCooperativeMatrixStoreCheckedINTEL &&
1996d057b53aSDmitry Sidorov                Opcode != SPIRV::OpCooperativeMatrixPrefetchINTEL;
199757f79371SVyacheslav Levytskyy   unsigned ArgSz = Call->Arguments.size();
199857f79371SVyacheslav Levytskyy   unsigned LiteralIdx = 0;
1999d057b53aSDmitry Sidorov   switch (Opcode) {
2000d057b53aSDmitry Sidorov   // Memory operand is optional and is literal.
2001d057b53aSDmitry Sidorov   case SPIRV::OpCooperativeMatrixLoadKHR:
2002d057b53aSDmitry Sidorov     LiteralIdx = ArgSz > 3 ? 3 : 0;
2003d057b53aSDmitry Sidorov     break;
2004d057b53aSDmitry Sidorov   case SPIRV::OpCooperativeMatrixStoreKHR:
2005d057b53aSDmitry Sidorov     LiteralIdx = ArgSz > 4 ? 4 : 0;
2006d057b53aSDmitry Sidorov     break;
2007d057b53aSDmitry Sidorov   case SPIRV::OpCooperativeMatrixLoadCheckedINTEL:
2008d057b53aSDmitry Sidorov     LiteralIdx = ArgSz > 7 ? 7 : 0;
2009d057b53aSDmitry Sidorov     break;
2010d057b53aSDmitry Sidorov   case SPIRV::OpCooperativeMatrixStoreCheckedINTEL:
2011d057b53aSDmitry Sidorov     LiteralIdx = ArgSz > 8 ? 8 : 0;
2012d057b53aSDmitry Sidorov     break;
2013d057b53aSDmitry Sidorov   // Cooperative Matrix Operands operand is optional and is literal.
2014d057b53aSDmitry Sidorov   case SPIRV::OpCooperativeMatrixMulAddKHR:
2015d057b53aSDmitry Sidorov     LiteralIdx = ArgSz > 3 ? 3 : 0;
2016d057b53aSDmitry Sidorov     break;
2017d057b53aSDmitry Sidorov   };
2018d057b53aSDmitry Sidorov 
201957f79371SVyacheslav Levytskyy   SmallVector<uint32_t, 1> ImmArgs;
202057f79371SVyacheslav Levytskyy   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2021d057b53aSDmitry Sidorov   if (Opcode == SPIRV::OpCooperativeMatrixPrefetchINTEL) {
2022d057b53aSDmitry Sidorov     const uint32_t CacheLevel = getConstFromIntrinsic(Call->Arguments[3], MRI);
2023d057b53aSDmitry Sidorov     auto MIB = MIRBuilder.buildInstr(SPIRV::OpCooperativeMatrixPrefetchINTEL)
2024d057b53aSDmitry Sidorov                    .addUse(Call->Arguments[0])  // pointer
2025d057b53aSDmitry Sidorov                    .addUse(Call->Arguments[1])  // rows
2026d057b53aSDmitry Sidorov                    .addUse(Call->Arguments[2])  // columns
2027d057b53aSDmitry Sidorov                    .addImm(CacheLevel)          // cache level
2028d057b53aSDmitry Sidorov                    .addUse(Call->Arguments[4]); // memory layout
2029d057b53aSDmitry Sidorov     if (ArgSz > 5)
2030d057b53aSDmitry Sidorov       MIB.addUse(Call->Arguments[5]); // stride
2031d057b53aSDmitry Sidorov     if (ArgSz > 6) {
2032d057b53aSDmitry Sidorov       const uint32_t MemOp = getConstFromIntrinsic(Call->Arguments[6], MRI);
2033d057b53aSDmitry Sidorov       MIB.addImm(MemOp); // memory operand
2034d057b53aSDmitry Sidorov     }
2035d057b53aSDmitry Sidorov     return true;
2036d057b53aSDmitry Sidorov   }
203757f79371SVyacheslav Levytskyy   if (LiteralIdx > 0)
203857f79371SVyacheslav Levytskyy     ImmArgs.push_back(getConstFromIntrinsic(Call->Arguments[LiteralIdx], MRI));
203957f79371SVyacheslav Levytskyy   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
204057f79371SVyacheslav Levytskyy   if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) {
204157f79371SVyacheslav Levytskyy     SPIRVType *CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
204257f79371SVyacheslav Levytskyy     if (!CoopMatrType)
204357f79371SVyacheslav Levytskyy       report_fatal_error("Can't find a register's type definition");
204457f79371SVyacheslav Levytskyy     MIRBuilder.buildInstr(Opcode)
204557f79371SVyacheslav Levytskyy         .addDef(Call->ReturnRegister)
204657f79371SVyacheslav Levytskyy         .addUse(TypeReg)
204757f79371SVyacheslav Levytskyy         .addUse(CoopMatrType->getOperand(0).getReg());
204857f79371SVyacheslav Levytskyy     return true;
204957f79371SVyacheslav Levytskyy   }
205057f79371SVyacheslav Levytskyy   return buildOpFromWrapper(MIRBuilder, Opcode, Call,
205157f79371SVyacheslav Levytskyy                             IsSet ? TypeReg : Register(0), ImmArgs);
205257f79371SVyacheslav Levytskyy }
205357f79371SVyacheslav Levytskyy 
2054f61eb416SIlia Diachkov static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call,
2055f61eb416SIlia Diachkov                                      MachineIRBuilder &MIRBuilder,
2056f61eb416SIlia Diachkov                                      SPIRVGlobalRegistry *GR) {
2057f61eb416SIlia Diachkov   // Lookup the instruction opcode in the TableGen records.
2058f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2059f61eb416SIlia Diachkov   unsigned Opcode =
2060f61eb416SIlia Diachkov       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2061f61eb416SIlia Diachkov   const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2062f61eb416SIlia Diachkov 
2063f61eb416SIlia Diachkov   switch (Opcode) {
2064f61eb416SIlia Diachkov   case SPIRV::OpSpecConstant: {
2065f61eb416SIlia Diachkov     // Build the SpecID decoration.
2066f61eb416SIlia Diachkov     unsigned SpecId =
2067f61eb416SIlia Diachkov         static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));
2068f61eb416SIlia Diachkov     buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,
2069f61eb416SIlia Diachkov                     {SpecId});
2070f61eb416SIlia Diachkov     // Determine the constant MI.
2071f61eb416SIlia Diachkov     Register ConstRegister = Call->Arguments[1];
2072f61eb416SIlia Diachkov     const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);
2073f61eb416SIlia Diachkov     assert(Const &&
2074f61eb416SIlia Diachkov            (Const->getOpcode() == TargetOpcode::G_CONSTANT ||
2075f61eb416SIlia Diachkov             Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&
2076f61eb416SIlia Diachkov            "Argument should be either an int or floating-point constant");
2077f61eb416SIlia Diachkov     // Determine the opcode and built the OpSpec MI.
2078f61eb416SIlia Diachkov     const MachineOperand &ConstOperand = Const->getOperand(1);
2079f61eb416SIlia Diachkov     if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {
2080f61eb416SIlia Diachkov       assert(ConstOperand.isCImm() && "Int constant operand is expected");
2081f61eb416SIlia Diachkov       Opcode = ConstOperand.getCImm()->getValue().getZExtValue()
2082f61eb416SIlia Diachkov                    ? SPIRV::OpSpecConstantTrue
2083f61eb416SIlia Diachkov                    : SPIRV::OpSpecConstantFalse;
2084f61eb416SIlia Diachkov     }
2085f61eb416SIlia Diachkov     auto MIB = MIRBuilder.buildInstr(Opcode)
2086f61eb416SIlia Diachkov                    .addDef(Call->ReturnRegister)
2087f61eb416SIlia Diachkov                    .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2088f61eb416SIlia Diachkov 
2089f61eb416SIlia Diachkov     if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {
2090f61eb416SIlia Diachkov       if (Const->getOpcode() == TargetOpcode::G_CONSTANT)
2091f61eb416SIlia Diachkov         addNumImm(ConstOperand.getCImm()->getValue(), MIB);
2092f61eb416SIlia Diachkov       else
2093f61eb416SIlia Diachkov         addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);
2094f61eb416SIlia Diachkov     }
2095f61eb416SIlia Diachkov     return true;
2096f61eb416SIlia Diachkov   }
2097f61eb416SIlia Diachkov   case SPIRV::OpSpecConstantComposite: {
2098f61eb416SIlia Diachkov     auto MIB = MIRBuilder.buildInstr(Opcode)
2099f61eb416SIlia Diachkov                    .addDef(Call->ReturnRegister)
2100f61eb416SIlia Diachkov                    .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2101f61eb416SIlia Diachkov     for (unsigned i = 0; i < Call->Arguments.size(); i++)
2102f61eb416SIlia Diachkov       MIB.addUse(Call->Arguments[i]);
2103f61eb416SIlia Diachkov     return true;
2104f61eb416SIlia Diachkov   }
2105f61eb416SIlia Diachkov   default:
2106f61eb416SIlia Diachkov     return false;
2107f61eb416SIlia Diachkov   }
2108f61eb416SIlia Diachkov }
2109f61eb416SIlia Diachkov 
211074c66710SIlia Diachkov static bool buildNDRange(const SPIRV::IncomingCall *Call,
211174c66710SIlia Diachkov                          MachineIRBuilder &MIRBuilder,
211274c66710SIlia Diachkov                          SPIRVGlobalRegistry *GR) {
211374c66710SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
211474c66710SIlia Diachkov   SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
211574c66710SIlia Diachkov   assert(PtrType->getOpcode() == SPIRV::OpTypePointer &&
211674c66710SIlia Diachkov          PtrType->getOperand(2).isReg());
211774c66710SIlia Diachkov   Register TypeReg = PtrType->getOperand(2).getReg();
211874c66710SIlia Diachkov   SPIRVType *StructType = GR->getSPIRVTypeForVReg(TypeReg);
211974c66710SIlia Diachkov   MachineFunction &MF = MIRBuilder.getMF();
2120f9c98068SVyacheslav Levytskyy   Register TmpReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
212174c66710SIlia Diachkov   GR->assignSPIRVTypeToVReg(StructType, TmpReg, MF);
212274c66710SIlia Diachkov   // Skip the first arg, it's the destination pointer. OpBuildNDRange takes
212374c66710SIlia Diachkov   // three other arguments, so pass zero constant on absence.
212474c66710SIlia Diachkov   unsigned NumArgs = Call->Arguments.size();
212574c66710SIlia Diachkov   assert(NumArgs >= 2);
212674c66710SIlia Diachkov   Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2];
212774c66710SIlia Diachkov   Register LocalWorkSize =
212874c66710SIlia Diachkov       NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3];
212974c66710SIlia Diachkov   Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1];
213074c66710SIlia Diachkov   if (NumArgs < 4) {
213174c66710SIlia Diachkov     Register Const;
213274c66710SIlia Diachkov     SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize);
213374c66710SIlia Diachkov     if (SpvTy->getOpcode() == SPIRV::OpTypePointer) {
213474c66710SIlia Diachkov       MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize);
213574c66710SIlia Diachkov       assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) &&
213674c66710SIlia Diachkov              DefInstr->getOperand(3).isReg());
213774c66710SIlia Diachkov       Register GWSPtr = DefInstr->getOperand(3).getReg();
213874c66710SIlia Diachkov       // TODO: Maybe simplify generation of the type of the fields.
2139c18bcd0aSKazu Hirata       unsigned Size = Call->Builtin->Name == "ndrange_3D" ? 3 : 2;
214074c66710SIlia Diachkov       unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32;
214174c66710SIlia Diachkov       Type *BaseTy = IntegerType::get(MF.getFunction().getContext(), BitWidth);
214274c66710SIlia Diachkov       Type *FieldTy = ArrayType::get(BaseTy, Size);
214374c66710SIlia Diachkov       SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder);
2144f9c98068SVyacheslav Levytskyy       GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
214574c66710SIlia Diachkov       GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize, MF);
214674c66710SIlia Diachkov       MIRBuilder.buildInstr(SPIRV::OpLoad)
214774c66710SIlia Diachkov           .addDef(GlobalWorkSize)
214874c66710SIlia Diachkov           .addUse(GR->getSPIRVTypeID(SpvFieldTy))
214974c66710SIlia Diachkov           .addUse(GWSPtr);
2150f6aa5087SVyacheslav Levytskyy       const SPIRVSubtarget &ST =
2151f6aa5087SVyacheslav Levytskyy           cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
2152f6aa5087SVyacheslav Levytskyy       Const = GR->getOrCreateConstIntArray(0, Size, *MIRBuilder.getInsertPt(),
2153f6aa5087SVyacheslav Levytskyy                                            SpvFieldTy, *ST.getInstrInfo());
215474c66710SIlia Diachkov     } else {
215574c66710SIlia Diachkov       Const = GR->buildConstantInt(0, MIRBuilder, SpvTy);
215674c66710SIlia Diachkov     }
215774c66710SIlia Diachkov     if (!LocalWorkSize.isValid())
215874c66710SIlia Diachkov       LocalWorkSize = Const;
215974c66710SIlia Diachkov     if (!GlobalWorkOffset.isValid())
216074c66710SIlia Diachkov       GlobalWorkOffset = Const;
216174c66710SIlia Diachkov   }
216274c66710SIlia Diachkov   assert(LocalWorkSize.isValid() && GlobalWorkOffset.isValid());
216374c66710SIlia Diachkov   MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
216474c66710SIlia Diachkov       .addDef(TmpReg)
216574c66710SIlia Diachkov       .addUse(TypeReg)
216674c66710SIlia Diachkov       .addUse(GlobalWorkSize)
216774c66710SIlia Diachkov       .addUse(LocalWorkSize)
216874c66710SIlia Diachkov       .addUse(GlobalWorkOffset);
216974c66710SIlia Diachkov   return MIRBuilder.buildInstr(SPIRV::OpStore)
217074c66710SIlia Diachkov       .addUse(Call->Arguments[0])
217174c66710SIlia Diachkov       .addUse(TmpReg);
217274c66710SIlia Diachkov }
217374c66710SIlia Diachkov 
2174748922b3SIlia Diachkov // TODO: maybe move to the global register.
2175748922b3SIlia Diachkov static SPIRVType *
2176748922b3SIlia Diachkov getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder,
2177748922b3SIlia Diachkov                                    SPIRVGlobalRegistry *GR) {
2178748922b3SIlia Diachkov   LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
2179748922b3SIlia Diachkov   unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2180*d459784cSMats Jun Larsen   Type *PtrType = PointerType::get(Context, SC1);
2181748922b3SIlia Diachkov   return GR->getOrCreateSPIRVType(PtrType, MIRBuilder);
2182748922b3SIlia Diachkov }
2183748922b3SIlia Diachkov 
2184748922b3SIlia Diachkov static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call,
2185748922b3SIlia Diachkov                                MachineIRBuilder &MIRBuilder,
2186748922b3SIlia Diachkov                                SPIRVGlobalRegistry *GR) {
2187748922b3SIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2188748922b3SIlia Diachkov   const DataLayout &DL = MIRBuilder.getDataLayout();
2189c2483ed5SVyacheslav Levytskyy   bool IsSpirvOp = Call->isSpirvOp();
2190c2483ed5SVyacheslav Levytskyy   bool HasEvents = Call->Builtin->Name.contains("events") || IsSpirvOp;
2191748922b3SIlia Diachkov   const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
2192748922b3SIlia Diachkov 
2193748922b3SIlia Diachkov   // Make vararg instructions before OpEnqueueKernel.
2194748922b3SIlia Diachkov   // Local sizes arguments: Sizes of block invoke arguments. Clang generates
2195748922b3SIlia Diachkov   // local size operands as an array, so we need to unpack them.
2196748922b3SIlia Diachkov   SmallVector<Register, 16> LocalSizes;
219789d09373SKazu Hirata   if (Call->Builtin->Name.contains("_varargs") || IsSpirvOp) {
2198748922b3SIlia Diachkov     const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6;
2199748922b3SIlia Diachkov     Register GepReg = Call->Arguments[LocalSizeArrayIdx];
2200748922b3SIlia Diachkov     MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg);
2201748922b3SIlia Diachkov     assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) &&
2202748922b3SIlia Diachkov            GepMI->getOperand(3).isReg());
2203748922b3SIlia Diachkov     Register ArrayReg = GepMI->getOperand(3).getReg();
2204748922b3SIlia Diachkov     MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg);
2205748922b3SIlia Diachkov     const Type *LocalSizeTy = getMachineInstrType(ArrayMI);
2206748922b3SIlia Diachkov     assert(LocalSizeTy && "Local size type is expected");
2207748922b3SIlia Diachkov     const uint64_t LocalSizeNum =
2208748922b3SIlia Diachkov         cast<ArrayType>(LocalSizeTy)->getNumElements();
2209748922b3SIlia Diachkov     unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2210748922b3SIlia Diachkov     const LLT LLType = LLT::pointer(SC, GR->getPointerSize());
2211748922b3SIlia Diachkov     const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType(
2212748922b3SIlia Diachkov         Int32Ty, MIRBuilder, SPIRV::StorageClass::Function);
2213748922b3SIlia Diachkov     for (unsigned I = 0; I < LocalSizeNum; ++I) {
2214b5132b7dSVyacheslav Levytskyy       Register Reg = MRI->createVirtualRegister(&SPIRV::pIDRegClass);
221574c66710SIlia Diachkov       MRI->setType(Reg, LLType);
2216748922b3SIlia Diachkov       GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF());
2217d9847cdeSSameer Sahasrabuddhe       auto GEPInst = MIRBuilder.buildIntrinsic(
2218d9847cdeSSameer Sahasrabuddhe           Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false);
2219748922b3SIlia Diachkov       GEPInst
2220748922b3SIlia Diachkov           .addImm(GepMI->getOperand(2).getImm())            // In bound.
2221748922b3SIlia Diachkov           .addUse(ArrayMI->getOperand(0).getReg())          // Alloca.
222267d3ef74SVyacheslav Levytskyy           .addUse(buildConstantIntReg32(0, MIRBuilder, GR)) // Indices.
222367d3ef74SVyacheslav Levytskyy           .addUse(buildConstantIntReg32(I, MIRBuilder, GR));
2224748922b3SIlia Diachkov       LocalSizes.push_back(Reg);
2225748922b3SIlia Diachkov     }
2226748922b3SIlia Diachkov   }
2227748922b3SIlia Diachkov 
2228748922b3SIlia Diachkov   // SPIRV OpEnqueueKernel instruction has 10+ arguments.
2229748922b3SIlia Diachkov   auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)
2230748922b3SIlia Diachkov                  .addDef(Call->ReturnRegister)
2231748922b3SIlia Diachkov                  .addUse(GR->getSPIRVTypeID(Int32Ty));
2232748922b3SIlia Diachkov 
2233748922b3SIlia Diachkov   // Copy all arguments before block invoke function pointer.
2234748922b3SIlia Diachkov   const unsigned BlockFIdx = HasEvents ? 6 : 3;
2235748922b3SIlia Diachkov   for (unsigned i = 0; i < BlockFIdx; i++)
2236748922b3SIlia Diachkov     MIB.addUse(Call->Arguments[i]);
2237748922b3SIlia Diachkov 
2238748922b3SIlia Diachkov   // If there are no event arguments in the original call, add dummy ones.
2239748922b3SIlia Diachkov   if (!HasEvents) {
224067d3ef74SVyacheslav Levytskyy     MIB.addUse(buildConstantIntReg32(0, MIRBuilder, GR)); // Dummy num events.
2241748922b3SIlia Diachkov     Register NullPtr = GR->getOrCreateConstNullPtr(
2242748922b3SIlia Diachkov         MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR));
2243748922b3SIlia Diachkov     MIB.addUse(NullPtr); // Dummy wait events.
2244748922b3SIlia Diachkov     MIB.addUse(NullPtr); // Dummy ret event.
2245748922b3SIlia Diachkov   }
2246748922b3SIlia Diachkov 
2247748922b3SIlia Diachkov   MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI);
2248748922b3SIlia Diachkov   assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
2249748922b3SIlia Diachkov   // Invoke: Pointer to invoke function.
2250748922b3SIlia Diachkov   MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal());
2251748922b3SIlia Diachkov 
2252748922b3SIlia Diachkov   Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1];
2253748922b3SIlia Diachkov   // Param: Pointer to block literal.
2254748922b3SIlia Diachkov   MIB.addUse(BlockLiteralReg);
2255748922b3SIlia Diachkov 
2256748922b3SIlia Diachkov   Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
2257748922b3SIlia Diachkov   // TODO: these numbers should be obtained from block literal structure.
2258748922b3SIlia Diachkov   // Param Size: Size of block literal structure.
225967d3ef74SVyacheslav Levytskyy   MIB.addUse(buildConstantIntReg32(DL.getTypeStoreSize(PType), MIRBuilder, GR));
2260748922b3SIlia Diachkov   // Param Aligment: Aligment of block literal structure.
226167d3ef74SVyacheslav Levytskyy   MIB.addUse(buildConstantIntReg32(DL.getPrefTypeAlign(PType).value(),
226267d3ef74SVyacheslav Levytskyy                                    MIRBuilder, GR));
2263748922b3SIlia Diachkov 
2264748922b3SIlia Diachkov   for (unsigned i = 0; i < LocalSizes.size(); i++)
2265748922b3SIlia Diachkov     MIB.addUse(LocalSizes[i]);
2266748922b3SIlia Diachkov   return true;
2267748922b3SIlia Diachkov }
2268748922b3SIlia Diachkov 
2269f61eb416SIlia Diachkov static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,
2270f61eb416SIlia Diachkov                                 MachineIRBuilder &MIRBuilder,
2271f61eb416SIlia Diachkov                                 SPIRVGlobalRegistry *GR) {
2272f61eb416SIlia Diachkov   // Lookup the instruction opcode in the TableGen records.
2273f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2274f61eb416SIlia Diachkov   unsigned Opcode =
2275f61eb416SIlia Diachkov       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2276f61eb416SIlia Diachkov 
2277f61eb416SIlia Diachkov   switch (Opcode) {
2278f61eb416SIlia Diachkov   case SPIRV::OpRetainEvent:
2279f61eb416SIlia Diachkov   case SPIRV::OpReleaseEvent:
2280f61eb416SIlia Diachkov     return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);
2281f61eb416SIlia Diachkov   case SPIRV::OpCreateUserEvent:
2282f61eb416SIlia Diachkov   case SPIRV::OpGetDefaultQueue:
2283f61eb416SIlia Diachkov     return MIRBuilder.buildInstr(Opcode)
2284f61eb416SIlia Diachkov         .addDef(Call->ReturnRegister)
2285f61eb416SIlia Diachkov         .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2286f61eb416SIlia Diachkov   case SPIRV::OpIsValidEvent:
2287f61eb416SIlia Diachkov     return MIRBuilder.buildInstr(Opcode)
2288f61eb416SIlia Diachkov         .addDef(Call->ReturnRegister)
2289f61eb416SIlia Diachkov         .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2290f61eb416SIlia Diachkov         .addUse(Call->Arguments[0]);
2291f61eb416SIlia Diachkov   case SPIRV::OpSetUserEventStatus:
2292f61eb416SIlia Diachkov     return MIRBuilder.buildInstr(Opcode)
2293f61eb416SIlia Diachkov         .addUse(Call->Arguments[0])
2294f61eb416SIlia Diachkov         .addUse(Call->Arguments[1]);
2295f61eb416SIlia Diachkov   case SPIRV::OpCaptureEventProfilingInfo:
2296f61eb416SIlia Diachkov     return MIRBuilder.buildInstr(Opcode)
2297f61eb416SIlia Diachkov         .addUse(Call->Arguments[0])
2298f61eb416SIlia Diachkov         .addUse(Call->Arguments[1])
2299f61eb416SIlia Diachkov         .addUse(Call->Arguments[2]);
230074c66710SIlia Diachkov   case SPIRV::OpBuildNDRange:
230174c66710SIlia Diachkov     return buildNDRange(Call, MIRBuilder, GR);
2302748922b3SIlia Diachkov   case SPIRV::OpEnqueueKernel:
2303748922b3SIlia Diachkov     return buildEnqueueKernel(Call, MIRBuilder, GR);
2304f61eb416SIlia Diachkov   default:
2305f61eb416SIlia Diachkov     return false;
2306f61eb416SIlia Diachkov   }
2307f61eb416SIlia Diachkov }
2308f61eb416SIlia Diachkov 
2309f61eb416SIlia Diachkov static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,
2310f61eb416SIlia Diachkov                               MachineIRBuilder &MIRBuilder,
2311f61eb416SIlia Diachkov                               SPIRVGlobalRegistry *GR) {
2312f61eb416SIlia Diachkov   // Lookup the instruction opcode in the TableGen records.
2313f61eb416SIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2314f61eb416SIlia Diachkov   unsigned Opcode =
2315f61eb416SIlia Diachkov       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
23161ed1ec9aSVyacheslav Levytskyy 
23171ed1ec9aSVyacheslav Levytskyy   bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;
23181ed1ec9aSVyacheslav Levytskyy   Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
23191ed1ec9aSVyacheslav Levytskyy   if (Call->isSpirvOp())
23201ed1ec9aSVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
23211ed1ec9aSVyacheslav Levytskyy                               IsSet ? TypeReg : Register(0));
23221ed1ec9aSVyacheslav Levytskyy 
232367d3ef74SVyacheslav Levytskyy   auto Scope = buildConstantIntReg32(SPIRV::Scope::Workgroup, MIRBuilder, GR);
2324f61eb416SIlia Diachkov 
2325f61eb416SIlia Diachkov   switch (Opcode) {
2326505cd125SVyacheslav Levytskyy   case SPIRV::OpGroupAsyncCopy: {
2327505cd125SVyacheslav Levytskyy     SPIRVType *NewType =
2328505cd125SVyacheslav Levytskyy         Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent
2329505cd125SVyacheslav Levytskyy             ? nullptr
2330505cd125SVyacheslav Levytskyy             : GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder);
2331505cd125SVyacheslav Levytskyy     Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);
2332505cd125SVyacheslav Levytskyy     unsigned NumArgs = Call->Arguments.size();
2333505cd125SVyacheslav Levytskyy     Register EventReg = Call->Arguments[NumArgs - 1];
2334505cd125SVyacheslav Levytskyy     bool Res = MIRBuilder.buildInstr(Opcode)
2335f61eb416SIlia Diachkov                    .addDef(Call->ReturnRegister)
2336505cd125SVyacheslav Levytskyy                    .addUse(TypeReg)
2337f61eb416SIlia Diachkov                    .addUse(Scope)
2338f61eb416SIlia Diachkov                    .addUse(Call->Arguments[0])
2339f61eb416SIlia Diachkov                    .addUse(Call->Arguments[1])
2340f61eb416SIlia Diachkov                    .addUse(Call->Arguments[2])
2341505cd125SVyacheslav Levytskyy                    .addUse(Call->Arguments.size() > 4
2342505cd125SVyacheslav Levytskyy                                ? Call->Arguments[3]
234367d3ef74SVyacheslav Levytskyy                                : buildConstantIntReg32(1, MIRBuilder, GR))
2344505cd125SVyacheslav Levytskyy                    .addUse(EventReg);
2345505cd125SVyacheslav Levytskyy     if (NewType != nullptr)
2346505cd125SVyacheslav Levytskyy       insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
2347505cd125SVyacheslav Levytskyy                         MIRBuilder.getMF().getRegInfo());
2348505cd125SVyacheslav Levytskyy     return Res;
2349505cd125SVyacheslav Levytskyy   }
2350f61eb416SIlia Diachkov   case SPIRV::OpGroupWaitEvents:
2351f61eb416SIlia Diachkov     return MIRBuilder.buildInstr(Opcode)
2352f61eb416SIlia Diachkov         .addUse(Scope)
2353f61eb416SIlia Diachkov         .addUse(Call->Arguments[0])
2354f61eb416SIlia Diachkov         .addUse(Call->Arguments[1]);
2355f61eb416SIlia Diachkov   default:
2356f61eb416SIlia Diachkov     return false;
2357f61eb416SIlia Diachkov   }
2358f61eb416SIlia Diachkov }
2359f61eb416SIlia Diachkov 
2360f61eb416SIlia Diachkov static bool generateConvertInst(const StringRef DemangledCall,
2361f61eb416SIlia Diachkov                                 const SPIRV::IncomingCall *Call,
2362f61eb416SIlia Diachkov                                 MachineIRBuilder &MIRBuilder,
2363f61eb416SIlia Diachkov                                 SPIRVGlobalRegistry *GR) {
2364f61eb416SIlia Diachkov   // Lookup the conversion builtin in the TableGen records.
2365f61eb416SIlia Diachkov   const SPIRV::ConvertBuiltin *Builtin =
2366f61eb416SIlia Diachkov       SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set);
2367f61eb416SIlia Diachkov 
236824cee1c4SVyacheslav Levytskyy   if (!Builtin && Call->isSpirvOp()) {
236924cee1c4SVyacheslav Levytskyy     const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
237024cee1c4SVyacheslav Levytskyy     unsigned Opcode =
237124cee1c4SVyacheslav Levytskyy         SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
237224cee1c4SVyacheslav Levytskyy     return buildOpFromWrapper(MIRBuilder, Opcode, Call,
237324cee1c4SVyacheslav Levytskyy                               GR->getSPIRVTypeID(Call->ReturnType));
237424cee1c4SVyacheslav Levytskyy   }
237524cee1c4SVyacheslav Levytskyy 
2376f61eb416SIlia Diachkov   if (Builtin->IsSaturated)
2377f61eb416SIlia Diachkov     buildOpDecorate(Call->ReturnRegister, MIRBuilder,
2378f61eb416SIlia Diachkov                     SPIRV::Decoration::SaturatedConversion, {});
2379f61eb416SIlia Diachkov   if (Builtin->IsRounded)
2380f61eb416SIlia Diachkov     buildOpDecorate(Call->ReturnRegister, MIRBuilder,
2381d7259bb7SIlia Diachkov                     SPIRV::Decoration::FPRoundingMode,
2382d7259bb7SIlia Diachkov                     {(unsigned)Builtin->RoundingMode});
2383f61eb416SIlia Diachkov 
23848f30b623SVyacheslav Levytskyy   std::string NeedExtMsg;              // no errors if empty
23858f30b623SVyacheslav Levytskyy   bool IsRightComponentsNumber = true; // check if input/output accepts vectors
2386f61eb416SIlia Diachkov   unsigned Opcode = SPIRV::OpNop;
2387f61eb416SIlia Diachkov   if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {
2388f61eb416SIlia Diachkov     // Int -> ...
2389f61eb416SIlia Diachkov     if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2390f61eb416SIlia Diachkov       // Int -> Int
2391f61eb416SIlia Diachkov       if (Builtin->IsSaturated)
2392f61eb416SIlia Diachkov         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS
2393f61eb416SIlia Diachkov                                               : SPIRV::OpSatConvertSToU;
2394f61eb416SIlia Diachkov       else
2395f61eb416SIlia Diachkov         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert
2396f61eb416SIlia Diachkov                                               : SPIRV::OpSConvert;
2397f61eb416SIlia Diachkov     } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
2398f61eb416SIlia Diachkov                                           SPIRV::OpTypeFloat)) {
2399f61eb416SIlia Diachkov       // Int -> Float
24008f30b623SVyacheslav Levytskyy       if (Builtin->IsBfloat16) {
24018f30b623SVyacheslav Levytskyy         const auto *ST = static_cast<const SPIRVSubtarget *>(
24028f30b623SVyacheslav Levytskyy             &MIRBuilder.getMF().getSubtarget());
24038f30b623SVyacheslav Levytskyy         if (!ST->canUseExtension(
24048f30b623SVyacheslav Levytskyy                 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
24058f30b623SVyacheslav Levytskyy           NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
24068f30b623SVyacheslav Levytskyy         IsRightComponentsNumber =
24078f30b623SVyacheslav Levytskyy             GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
24088f30b623SVyacheslav Levytskyy             GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
24098f30b623SVyacheslav Levytskyy         Opcode = SPIRV::OpConvertBF16ToFINTEL;
24108f30b623SVyacheslav Levytskyy       } else {
2411f61eb416SIlia Diachkov         bool IsSourceSigned =
2412f61eb416SIlia Diachkov             DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';
2413f61eb416SIlia Diachkov         Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;
2414f61eb416SIlia Diachkov       }
24158f30b623SVyacheslav Levytskyy     }
2416f61eb416SIlia Diachkov   } else if (GR->isScalarOrVectorOfType(Call->Arguments[0],
2417f61eb416SIlia Diachkov                                         SPIRV::OpTypeFloat)) {
2418f61eb416SIlia Diachkov     // Float -> ...
24198f30b623SVyacheslav Levytskyy     if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2420f61eb416SIlia Diachkov       // Float -> Int
24218f30b623SVyacheslav Levytskyy       if (Builtin->IsBfloat16) {
24228f30b623SVyacheslav Levytskyy         const auto *ST = static_cast<const SPIRVSubtarget *>(
24238f30b623SVyacheslav Levytskyy             &MIRBuilder.getMF().getSubtarget());
24248f30b623SVyacheslav Levytskyy         if (!ST->canUseExtension(
24258f30b623SVyacheslav Levytskyy                 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
24268f30b623SVyacheslav Levytskyy           NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
24278f30b623SVyacheslav Levytskyy         IsRightComponentsNumber =
24288f30b623SVyacheslav Levytskyy             GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
24298f30b623SVyacheslav Levytskyy             GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
24308f30b623SVyacheslav Levytskyy         Opcode = SPIRV::OpConvertFToBF16INTEL;
24318f30b623SVyacheslav Levytskyy       } else {
2432f61eb416SIlia Diachkov         Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS
2433f61eb416SIlia Diachkov                                               : SPIRV::OpConvertFToU;
24348f30b623SVyacheslav Levytskyy       }
24358f30b623SVyacheslav Levytskyy     } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
24368f30b623SVyacheslav Levytskyy                                           SPIRV::OpTypeFloat)) {
2437f61eb416SIlia Diachkov       // Float -> Float
2438f61eb416SIlia Diachkov       Opcode = SPIRV::OpFConvert;
2439f61eb416SIlia Diachkov     }
24408f30b623SVyacheslav Levytskyy   }
2441f61eb416SIlia Diachkov 
24428f30b623SVyacheslav Levytskyy   if (!NeedExtMsg.empty()) {
24438f30b623SVyacheslav Levytskyy     std::string DiagMsg = std::string(Builtin->Name) +
24448f30b623SVyacheslav Levytskyy                           ": the builtin requires the following SPIR-V "
24458f30b623SVyacheslav Levytskyy                           "extension: " +
24468f30b623SVyacheslav Levytskyy                           NeedExtMsg;
24478f30b623SVyacheslav Levytskyy     report_fatal_error(DiagMsg.c_str(), false);
24488f30b623SVyacheslav Levytskyy   }
24498f30b623SVyacheslav Levytskyy   if (!IsRightComponentsNumber) {
24508f30b623SVyacheslav Levytskyy     std::string DiagMsg =
24518f30b623SVyacheslav Levytskyy         std::string(Builtin->Name) +
24528f30b623SVyacheslav Levytskyy         ": result and argument must have the same number of components";
24538f30b623SVyacheslav Levytskyy     report_fatal_error(DiagMsg.c_str(), false);
24548f30b623SVyacheslav Levytskyy   }
2455f61eb416SIlia Diachkov   assert(Opcode != SPIRV::OpNop &&
2456f61eb416SIlia Diachkov          "Conversion between the types not implemented!");
2457f61eb416SIlia Diachkov 
2458f61eb416SIlia Diachkov   MIRBuilder.buildInstr(Opcode)
2459f61eb416SIlia Diachkov       .addDef(Call->ReturnRegister)
2460f61eb416SIlia Diachkov       .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2461f61eb416SIlia Diachkov       .addUse(Call->Arguments[0]);
2462f61eb416SIlia Diachkov   return true;
2463f61eb416SIlia Diachkov }
2464f61eb416SIlia Diachkov 
2465f61eb416SIlia Diachkov static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call,
2466f61eb416SIlia Diachkov                                         MachineIRBuilder &MIRBuilder,
2467f61eb416SIlia Diachkov                                         SPIRVGlobalRegistry *GR) {
2468f61eb416SIlia Diachkov   // Lookup the vector load/store builtin in the TableGen records.
2469f61eb416SIlia Diachkov   const SPIRV::VectorLoadStoreBuiltin *Builtin =
2470f61eb416SIlia Diachkov       SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2471f61eb416SIlia Diachkov                                           Call->Builtin->Set);
2472f61eb416SIlia Diachkov   // Build extended instruction.
2473f61eb416SIlia Diachkov   auto MIB =
2474f61eb416SIlia Diachkov       MIRBuilder.buildInstr(SPIRV::OpExtInst)
2475f61eb416SIlia Diachkov           .addDef(Call->ReturnRegister)
2476f61eb416SIlia Diachkov           .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2477f61eb416SIlia Diachkov           .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
2478f61eb416SIlia Diachkov           .addImm(Builtin->Number);
2479f61eb416SIlia Diachkov   for (auto Argument : Call->Arguments)
2480f61eb416SIlia Diachkov     MIB.addUse(Argument);
248143222bd3SMichal Paszkowski   if (Builtin->Name.contains("load") && Builtin->ElementCount > 1)
248203203b79SMichal Paszkowski     MIB.addImm(Builtin->ElementCount);
2483f61eb416SIlia Diachkov 
2484f61eb416SIlia Diachkov   // Rounding mode should be passed as a last argument in the MI for builtins
2485f61eb416SIlia Diachkov   // like "vstorea_halfn_r".
2486f61eb416SIlia Diachkov   if (Builtin->IsRounded)
2487f61eb416SIlia Diachkov     MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));
2488f61eb416SIlia Diachkov   return true;
2489f61eb416SIlia Diachkov }
2490f61eb416SIlia Diachkov 
24910b0224bcSIlia Diachkov static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call,
24920b0224bcSIlia Diachkov                                   MachineIRBuilder &MIRBuilder,
24930b0224bcSIlia Diachkov                                   SPIRVGlobalRegistry *GR) {
24940b0224bcSIlia Diachkov   // Lookup the instruction opcode in the TableGen records.
24950b0224bcSIlia Diachkov   const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
24960b0224bcSIlia Diachkov   unsigned Opcode =
24970b0224bcSIlia Diachkov       SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
24980b0224bcSIlia Diachkov   bool IsLoad = Opcode == SPIRV::OpLoad;
24990b0224bcSIlia Diachkov   // Build the instruction.
25000b0224bcSIlia Diachkov   auto MIB = MIRBuilder.buildInstr(Opcode);
25010b0224bcSIlia Diachkov   if (IsLoad) {
25020b0224bcSIlia Diachkov     MIB.addDef(Call->ReturnRegister);
25030b0224bcSIlia Diachkov     MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
25040b0224bcSIlia Diachkov   }
25050b0224bcSIlia Diachkov   // Add a pointer to the value to load/store.
25060b0224bcSIlia Diachkov   MIB.addUse(Call->Arguments[0]);
25070b0224bcSIlia Diachkov   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
250874c66710SIlia Diachkov   // Add a value to store.
250967d3ef74SVyacheslav Levytskyy   if (!IsLoad)
251074c66710SIlia Diachkov     MIB.addUse(Call->Arguments[1]);
251174c66710SIlia Diachkov   // Add optional memory attributes and an alignment.
25120b0224bcSIlia Diachkov   unsigned NumArgs = Call->Arguments.size();
251367d3ef74SVyacheslav Levytskyy   if ((IsLoad && NumArgs >= 2) || NumArgs >= 3)
25140b0224bcSIlia Diachkov     MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI));
251567d3ef74SVyacheslav Levytskyy   if ((IsLoad && NumArgs >= 3) || NumArgs >= 4)
25160b0224bcSIlia Diachkov     MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI));
25170b0224bcSIlia Diachkov   return true;
25180b0224bcSIlia Diachkov }
25190b0224bcSIlia Diachkov 
2520f61eb416SIlia Diachkov namespace SPIRV {
2521bf9e9e5eSVyacheslav Levytskyy // Try to find a builtin function attributes by a demangled function name and
2522bf9e9e5eSVyacheslav Levytskyy // return a tuple <builtin group, op code, ext instruction number>, or a special
2523bf9e9e5eSVyacheslav Levytskyy // tuple value <-1, 0, 0> if the builtin function is not found.
2524bf9e9e5eSVyacheslav Levytskyy // Not all builtin functions are supported, only those with a ready-to-use op
2525bf9e9e5eSVyacheslav Levytskyy // code or instruction number defined in TableGen.
2526bf9e9e5eSVyacheslav Levytskyy // TODO: consider a major rework of mapping demangled calls into a builtin
2527bf9e9e5eSVyacheslav Levytskyy // functions to unify search and decrease number of individual cases.
2528bf9e9e5eSVyacheslav Levytskyy std::tuple<int, unsigned, unsigned>
2529bf9e9e5eSVyacheslav Levytskyy mapBuiltinToOpcode(const StringRef DemangledCall,
2530bf9e9e5eSVyacheslav Levytskyy                    SPIRV::InstructionSet::InstructionSet Set) {
2531bf9e9e5eSVyacheslav Levytskyy   Register Reg;
2532bf9e9e5eSVyacheslav Levytskyy   SmallVector<Register> Args;
2533bf9e9e5eSVyacheslav Levytskyy   std::unique_ptr<const IncomingCall> Call =
2534bf9e9e5eSVyacheslav Levytskyy       lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args);
2535bf9e9e5eSVyacheslav Levytskyy   if (!Call)
2536bf9e9e5eSVyacheslav Levytskyy     return std::make_tuple(-1, 0, 0);
2537bf9e9e5eSVyacheslav Levytskyy 
2538bf9e9e5eSVyacheslav Levytskyy   switch (Call->Builtin->Group) {
2539bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Relational:
2540bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Atomic:
2541bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Barrier:
2542bf9e9e5eSVyacheslav Levytskyy   case SPIRV::CastToPtr:
2543bf9e9e5eSVyacheslav Levytskyy   case SPIRV::ImageMiscQuery:
2544bf9e9e5eSVyacheslav Levytskyy   case SPIRV::SpecConstant:
2545bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Enqueue:
2546bf9e9e5eSVyacheslav Levytskyy   case SPIRV::AsyncCopy:
2547bf9e9e5eSVyacheslav Levytskyy   case SPIRV::LoadStore:
2548bf9e9e5eSVyacheslav Levytskyy   case SPIRV::CoopMatr:
2549bf9e9e5eSVyacheslav Levytskyy     if (const auto *R =
2550bf9e9e5eSVyacheslav Levytskyy             SPIRV::lookupNativeBuiltin(Call->Builtin->Name, Call->Builtin->Set))
2551bf9e9e5eSVyacheslav Levytskyy       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2552bf9e9e5eSVyacheslav Levytskyy     break;
2553bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Extended:
2554bf9e9e5eSVyacheslav Levytskyy     if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->Name,
2555bf9e9e5eSVyacheslav Levytskyy                                                      Call->Builtin->Set))
2556bf9e9e5eSVyacheslav Levytskyy       return std::make_tuple(Call->Builtin->Group, 0, R->Number);
2557bf9e9e5eSVyacheslav Levytskyy     break;
2558bf9e9e5eSVyacheslav Levytskyy   case SPIRV::VectorLoadStore:
2559bf9e9e5eSVyacheslav Levytskyy     if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2560bf9e9e5eSVyacheslav Levytskyy                                                             Call->Builtin->Set))
2561bf9e9e5eSVyacheslav Levytskyy       return std::make_tuple(SPIRV::Extended, 0, R->Number);
2562bf9e9e5eSVyacheslav Levytskyy     break;
2563bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Group:
2564bf9e9e5eSVyacheslav Levytskyy     if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->Name))
2565bf9e9e5eSVyacheslav Levytskyy       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2566bf9e9e5eSVyacheslav Levytskyy     break;
2567bf9e9e5eSVyacheslav Levytskyy   case SPIRV::AtomicFloating:
2568bf9e9e5eSVyacheslav Levytskyy     if (const auto *R = SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->Name))
2569bf9e9e5eSVyacheslav Levytskyy       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2570bf9e9e5eSVyacheslav Levytskyy     break;
2571bf9e9e5eSVyacheslav Levytskyy   case SPIRV::IntelSubgroups:
2572bf9e9e5eSVyacheslav Levytskyy     if (const auto *R = SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->Name))
2573bf9e9e5eSVyacheslav Levytskyy       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2574bf9e9e5eSVyacheslav Levytskyy     break;
2575bf9e9e5eSVyacheslav Levytskyy   case SPIRV::GroupUniform:
2576bf9e9e5eSVyacheslav Levytskyy     if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->Name))
2577bf9e9e5eSVyacheslav Levytskyy       return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2578bf9e9e5eSVyacheslav Levytskyy     break;
2579bf9e9e5eSVyacheslav Levytskyy   case SPIRV::WriteImage:
2580bf9e9e5eSVyacheslav Levytskyy     return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0);
2581bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Select:
2582bf9e9e5eSVyacheslav Levytskyy     return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0);
2583bf9e9e5eSVyacheslav Levytskyy   case SPIRV::Construct:
2584bf9e9e5eSVyacheslav Levytskyy     return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct,
2585bf9e9e5eSVyacheslav Levytskyy                            0);
2586bf9e9e5eSVyacheslav Levytskyy   case SPIRV::KernelClock:
2587bf9e9e5eSVyacheslav Levytskyy     return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0);
2588bf9e9e5eSVyacheslav Levytskyy   default:
2589bf9e9e5eSVyacheslav Levytskyy     return std::make_tuple(-1, 0, 0);
2590bf9e9e5eSVyacheslav Levytskyy   }
2591bf9e9e5eSVyacheslav Levytskyy   return std::make_tuple(-1, 0, 0);
2592bf9e9e5eSVyacheslav Levytskyy }
2593bf9e9e5eSVyacheslav Levytskyy 
2594b0df7040SFangrui Song std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
25951fbc6b26SAleksandr Bezzubikov                                  SPIRV::InstructionSet::InstructionSet Set,
25961fbc6b26SAleksandr Bezzubikov                                  MachineIRBuilder &MIRBuilder,
25971fbc6b26SAleksandr Bezzubikov                                  const Register OrigRet, const Type *OrigRetTy,
25981fbc6b26SAleksandr Bezzubikov                                  const SmallVectorImpl<Register> &Args,
2599f61eb416SIlia Diachkov                                  SPIRVGlobalRegistry *GR) {
2600f61eb416SIlia Diachkov   LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");
2601f61eb416SIlia Diachkov 
2602f61eb416SIlia Diachkov   // Lookup the builtin in the TableGen records.
2603b5132b7dSVyacheslav Levytskyy   SPIRVType *SpvType = GR->getSPIRVTypeForVReg(OrigRet);
2604b5132b7dSVyacheslav Levytskyy   assert(SpvType && "Inconsistent return register: expected valid type info");
2605f61eb416SIlia Diachkov   std::unique_ptr<const IncomingCall> Call =
2606b5132b7dSVyacheslav Levytskyy       lookupBuiltin(DemangledCall, Set, OrigRet, SpvType, Args);
2607f61eb416SIlia Diachkov 
2608f61eb416SIlia Diachkov   if (!Call) {
26095fb4a051SMichal Paszkowski     LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
26101ea9dd32SKazu Hirata     return std::nullopt;
2611f61eb416SIlia Diachkov   }
2612f61eb416SIlia Diachkov 
2613f61eb416SIlia Diachkov   // TODO: check if the provided args meet the builtin requirments.
2614f61eb416SIlia Diachkov   assert(Args.size() >= Call->Builtin->MinNumArgs &&
2615f61eb416SIlia Diachkov          "Too few arguments to generate the builtin");
26165fb4a051SMichal Paszkowski   if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs)
26175fb4a051SMichal Paszkowski     LLVM_DEBUG(dbgs() << "More arguments provided than required!\n");
2618f61eb416SIlia Diachkov 
2619f61eb416SIlia Diachkov   // Match the builtin with implementation based on the grouping.
2620f61eb416SIlia Diachkov   switch (Call->Builtin->Group) {
2621f61eb416SIlia Diachkov   case SPIRV::Extended:
26221fbc6b26SAleksandr Bezzubikov     return generateExtInst(Call.get(), MIRBuilder, GR);
2623f61eb416SIlia Diachkov   case SPIRV::Relational:
26241fbc6b26SAleksandr Bezzubikov     return generateRelationalInst(Call.get(), MIRBuilder, GR);
2625f61eb416SIlia Diachkov   case SPIRV::Group:
26261fbc6b26SAleksandr Bezzubikov     return generateGroupInst(Call.get(), MIRBuilder, GR);
2627f61eb416SIlia Diachkov   case SPIRV::Variable:
26281fbc6b26SAleksandr Bezzubikov     return generateBuiltinVar(Call.get(), MIRBuilder, GR);
2629f61eb416SIlia Diachkov   case SPIRV::Atomic:
26301fbc6b26SAleksandr Bezzubikov     return generateAtomicInst(Call.get(), MIRBuilder, GR);
2631925768eeSVyacheslav Levytskyy   case SPIRV::AtomicFloating:
2632925768eeSVyacheslav Levytskyy     return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);
2633f61eb416SIlia Diachkov   case SPIRV::Barrier:
26341fbc6b26SAleksandr Bezzubikov     return generateBarrierInst(Call.get(), MIRBuilder, GR);
263557520985SVyacheslav Levytskyy   case SPIRV::CastToPtr:
263657520985SVyacheslav Levytskyy     return generateCastToPtrInst(Call.get(), MIRBuilder);
2637f61eb416SIlia Diachkov   case SPIRV::Dot:
26381fbc6b26SAleksandr Bezzubikov     return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);
2639f0eb9083SNathan Gauër   case SPIRV::Wave:
2640f0eb9083SNathan Gauër     return generateWaveInst(Call.get(), MIRBuilder, GR);
26418ac46d6bSVyacheslav Levytskyy   case SPIRV::ICarryBorrow:
26428ac46d6bSVyacheslav Levytskyy     return generateICarryBorrowInst(Call.get(), MIRBuilder, GR);
2643f61eb416SIlia Diachkov   case SPIRV::GetQuery:
26441fbc6b26SAleksandr Bezzubikov     return generateGetQueryInst(Call.get(), MIRBuilder, GR);
2645f61eb416SIlia Diachkov   case SPIRV::ImageSizeQuery:
26461fbc6b26SAleksandr Bezzubikov     return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);
2647f61eb416SIlia Diachkov   case SPIRV::ImageMiscQuery:
26481fbc6b26SAleksandr Bezzubikov     return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);
2649f61eb416SIlia Diachkov   case SPIRV::ReadImage:
26501fbc6b26SAleksandr Bezzubikov     return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2651f61eb416SIlia Diachkov   case SPIRV::WriteImage:
26521fbc6b26SAleksandr Bezzubikov     return generateWriteImageInst(Call.get(), MIRBuilder, GR);
2653f61eb416SIlia Diachkov   case SPIRV::SampleImage:
26541fbc6b26SAleksandr Bezzubikov     return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2655f61eb416SIlia Diachkov   case SPIRV::Select:
26561fbc6b26SAleksandr Bezzubikov     return generateSelectInst(Call.get(), MIRBuilder);
265757f79371SVyacheslav Levytskyy   case SPIRV::Construct:
265857f79371SVyacheslav Levytskyy     return generateConstructInst(Call.get(), MIRBuilder, GR);
2659f61eb416SIlia Diachkov   case SPIRV::SpecConstant:
26601fbc6b26SAleksandr Bezzubikov     return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
2661f61eb416SIlia Diachkov   case SPIRV::Enqueue:
26621fbc6b26SAleksandr Bezzubikov     return generateEnqueueInst(Call.get(), MIRBuilder, GR);
2663f61eb416SIlia Diachkov   case SPIRV::AsyncCopy:
26641fbc6b26SAleksandr Bezzubikov     return generateAsyncCopy(Call.get(), MIRBuilder, GR);
2665f61eb416SIlia Diachkov   case SPIRV::Convert:
26661fbc6b26SAleksandr Bezzubikov     return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
2667f61eb416SIlia Diachkov   case SPIRV::VectorLoadStore:
26681fbc6b26SAleksandr Bezzubikov     return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);
26690b0224bcSIlia Diachkov   case SPIRV::LoadStore:
26700b0224bcSIlia Diachkov     return generateLoadStoreInst(Call.get(), MIRBuilder, GR);
2671b221b973SVyacheslav Levytskyy   case SPIRV::IntelSubgroups:
2672b221b973SVyacheslav Levytskyy     return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR);
267366ebda46SVyacheslav Levytskyy   case SPIRV::GroupUniform:
267466ebda46SVyacheslav Levytskyy     return generateGroupUniformInst(Call.get(), MIRBuilder, GR);
267589c23f76SSven van Haastregt   case SPIRV::KernelClock:
267689c23f76SSven van Haastregt     return generateKernelClockInst(Call.get(), MIRBuilder, GR);
267757f79371SVyacheslav Levytskyy   case SPIRV::CoopMatr:
267857f79371SVyacheslav Levytskyy     return generateCoopMatrInst(Call.get(), MIRBuilder, GR);
2679f61eb416SIlia Diachkov   }
26801fbc6b26SAleksandr Bezzubikov   return false;
2681f61eb416SIlia Diachkov }
2682698c8001SIlia Diachkov 
2683489db653SVyacheslav Levytskyy Type *parseBuiltinCallArgumentType(StringRef TypeStr, LLVMContext &Ctx) {
268443222bd3SMichal Paszkowski   // Parse strings representing OpenCL builtin types.
268543222bd3SMichal Paszkowski   if (hasBuiltinTypePrefix(TypeStr)) {
268643222bd3SMichal Paszkowski     // OpenCL builtin types in demangled call strings have the following format:
268743222bd3SMichal Paszkowski     // e.g. ocl_image2d_ro
2688e83adfe5SChris B     [[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_");
268943222bd3SMichal Paszkowski     assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix");
269043222bd3SMichal Paszkowski 
269143222bd3SMichal Paszkowski     // Check if this is pointer to a builtin type and not just pointer
269243222bd3SMichal Paszkowski     // representing a builtin type. In case it is a pointer to builtin type,
269343222bd3SMichal Paszkowski     // this will require additional handling in the method calling
269443222bd3SMichal Paszkowski     // parseBuiltinCallArgumentBaseType(...) as this function only retrieves the
269543222bd3SMichal Paszkowski     // base types.
269643222bd3SMichal Paszkowski     if (TypeStr.ends_with("*"))
26971ed1ec9aSVyacheslav Levytskyy       TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));
269843222bd3SMichal Paszkowski 
269943222bd3SMichal Paszkowski     return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",
270043222bd3SMichal Paszkowski                                                Ctx);
270143222bd3SMichal Paszkowski   }
270243222bd3SMichal Paszkowski 
270343222bd3SMichal Paszkowski   // Parse type name in either "typeN" or "type vector[N]" format, where
270443222bd3SMichal Paszkowski   // N is the number of elements of the vector.
270543222bd3SMichal Paszkowski   Type *BaseType;
270643222bd3SMichal Paszkowski   unsigned VecElts = 0;
270743222bd3SMichal Paszkowski 
270843222bd3SMichal Paszkowski   BaseType = parseBasicTypeName(TypeStr, Ctx);
270943222bd3SMichal Paszkowski   if (!BaseType)
271043222bd3SMichal Paszkowski     // Unable to recognize SPIRV type name.
271143222bd3SMichal Paszkowski     return nullptr;
271243222bd3SMichal Paszkowski 
271343222bd3SMichal Paszkowski   // Handle "typeN*" or "type vector[N]*".
271443222bd3SMichal Paszkowski   TypeStr.consume_back("*");
271543222bd3SMichal Paszkowski 
271643222bd3SMichal Paszkowski   if (TypeStr.consume_front(" vector["))
271743222bd3SMichal Paszkowski     TypeStr = TypeStr.substr(0, TypeStr.find(']'));
271843222bd3SMichal Paszkowski 
271943222bd3SMichal Paszkowski   TypeStr.getAsInteger(10, VecElts);
272043222bd3SMichal Paszkowski   if (VecElts > 0)
2721dbd00a59SVyacheslav Levytskyy     BaseType = VectorType::get(
2722dbd00a59SVyacheslav Levytskyy         BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);
272343222bd3SMichal Paszkowski 
272443222bd3SMichal Paszkowski   return BaseType;
272543222bd3SMichal Paszkowski }
272643222bd3SMichal Paszkowski 
2727489db653SVyacheslav Levytskyy bool parseBuiltinTypeStr(SmallVector<StringRef, 10> &BuiltinArgsTypeStrs,
2728489db653SVyacheslav Levytskyy                          const StringRef DemangledCall, LLVMContext &Ctx) {
2729489db653SVyacheslav Levytskyy   auto Pos1 = DemangledCall.find('(');
2730489db653SVyacheslav Levytskyy   if (Pos1 == StringRef::npos)
2731489db653SVyacheslav Levytskyy     return false;
2732489db653SVyacheslav Levytskyy   auto Pos2 = DemangledCall.find(')');
2733489db653SVyacheslav Levytskyy   if (Pos2 == StringRef::npos || Pos1 > Pos2)
2734489db653SVyacheslav Levytskyy     return false;
2735489db653SVyacheslav Levytskyy   DemangledCall.slice(Pos1 + 1, Pos2)
2736489db653SVyacheslav Levytskyy       .split(BuiltinArgsTypeStrs, ',', -1, false);
2737489db653SVyacheslav Levytskyy   return true;
2738489db653SVyacheslav Levytskyy }
2739489db653SVyacheslav Levytskyy 
2740489db653SVyacheslav Levytskyy Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall,
2741489db653SVyacheslav Levytskyy                                        unsigned ArgIdx, LLVMContext &Ctx) {
2742489db653SVyacheslav Levytskyy   SmallVector<StringRef, 10> BuiltinArgsTypeStrs;
2743489db653SVyacheslav Levytskyy   parseBuiltinTypeStr(BuiltinArgsTypeStrs, DemangledCall, Ctx);
2744489db653SVyacheslav Levytskyy   if (ArgIdx >= BuiltinArgsTypeStrs.size())
2745489db653SVyacheslav Levytskyy     return nullptr;
2746489db653SVyacheslav Levytskyy   StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim();
2747489db653SVyacheslav Levytskyy   return parseBuiltinCallArgumentType(TypeStr, Ctx);
2748489db653SVyacheslav Levytskyy }
2749489db653SVyacheslav Levytskyy 
27505ac69674SMichal Paszkowski struct BuiltinType {
2751698c8001SIlia Diachkov   StringRef Name;
2752698c8001SIlia Diachkov   uint32_t Opcode;
2753698c8001SIlia Diachkov };
2754698c8001SIlia Diachkov 
27555ac69674SMichal Paszkowski #define GET_BuiltinTypes_DECL
27565ac69674SMichal Paszkowski #define GET_BuiltinTypes_IMPL
2757698c8001SIlia Diachkov 
27585ac69674SMichal Paszkowski struct OpenCLType {
2759698c8001SIlia Diachkov   StringRef Name;
27605ac69674SMichal Paszkowski   StringRef SpirvTypeLiteral;
2761698c8001SIlia Diachkov };
2762698c8001SIlia Diachkov 
27635ac69674SMichal Paszkowski #define GET_OpenCLTypes_DECL
27645ac69674SMichal Paszkowski #define GET_OpenCLTypes_IMPL
2765698c8001SIlia Diachkov 
2766698c8001SIlia Diachkov #include "SPIRVGenTables.inc"
2767698c8001SIlia Diachkov } // namespace SPIRV
2768698c8001SIlia Diachkov 
2769698c8001SIlia Diachkov //===----------------------------------------------------------------------===//
27705ac69674SMichal Paszkowski // Misc functions for parsing builtin types.
2771698c8001SIlia Diachkov //===----------------------------------------------------------------------===//
2772698c8001SIlia Diachkov 
27735ac69674SMichal Paszkowski static Type *parseTypeString(const StringRef Name, LLVMContext &Context) {
2774395f9ce3SKazu Hirata   if (Name.starts_with("void"))
27755ac69674SMichal Paszkowski     return Type::getVoidTy(Context);
2776395f9ce3SKazu Hirata   else if (Name.starts_with("int") || Name.starts_with("uint"))
27775ac69674SMichal Paszkowski     return Type::getInt32Ty(Context);
2778395f9ce3SKazu Hirata   else if (Name.starts_with("float"))
27795ac69674SMichal Paszkowski     return Type::getFloatTy(Context);
2780395f9ce3SKazu Hirata   else if (Name.starts_with("half"))
27815ac69674SMichal Paszkowski     return Type::getHalfTy(Context);
2782925768eeSVyacheslav Levytskyy   report_fatal_error("Unable to recognize type!");
2783698c8001SIlia Diachkov }
2784698c8001SIlia Diachkov 
2785698c8001SIlia Diachkov //===----------------------------------------------------------------------===//
2786698c8001SIlia Diachkov // Implementation functions for builtin types.
2787698c8001SIlia Diachkov //===----------------------------------------------------------------------===//
2788698c8001SIlia Diachkov 
278920f650b2SMichal Paszkowski static SPIRVType *getNonParameterizedType(const TargetExtType *ExtensionType,
27905ac69674SMichal Paszkowski                                           const SPIRV::BuiltinType *TypeRecord,
2791698c8001SIlia Diachkov                                           MachineIRBuilder &MIRBuilder,
2792698c8001SIlia Diachkov                                           SPIRVGlobalRegistry *GR) {
2793698c8001SIlia Diachkov   unsigned Opcode = TypeRecord->Opcode;
2794698c8001SIlia Diachkov   // Create or get an existing type from GlobalRegistry.
27955ac69674SMichal Paszkowski   return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode);
2796698c8001SIlia Diachkov }
2797698c8001SIlia Diachkov 
27983544d200SIlia Diachkov static SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder,
2799698c8001SIlia Diachkov                                  SPIRVGlobalRegistry *GR) {
2800698c8001SIlia Diachkov   // Create or get an existing type from GlobalRegistry.
2801698c8001SIlia Diachkov   return GR->getOrCreateOpTypeSampler(MIRBuilder);
2802698c8001SIlia Diachkov }
2803698c8001SIlia Diachkov 
28045ac69674SMichal Paszkowski static SPIRVType *getPipeType(const TargetExtType *ExtensionType,
28053544d200SIlia Diachkov                               MachineIRBuilder &MIRBuilder,
28063544d200SIlia Diachkov                               SPIRVGlobalRegistry *GR) {
28075ac69674SMichal Paszkowski   assert(ExtensionType->getNumIntParameters() == 1 &&
28085ac69674SMichal Paszkowski          "Invalid number of parameters for SPIR-V pipe builtin!");
2809698c8001SIlia Diachkov   // Create or get an existing type from GlobalRegistry.
28105ac69674SMichal Paszkowski   return GR->getOrCreateOpTypePipe(MIRBuilder,
28115ac69674SMichal Paszkowski                                    SPIRV::AccessQualifier::AccessQualifier(
28125ac69674SMichal Paszkowski                                        ExtensionType->getIntParameter(0)));
2813698c8001SIlia Diachkov }
2814698c8001SIlia Diachkov 
281557f79371SVyacheslav Levytskyy static SPIRVType *getCoopMatrType(const TargetExtType *ExtensionType,
281657f79371SVyacheslav Levytskyy                                   MachineIRBuilder &MIRBuilder,
281757f79371SVyacheslav Levytskyy                                   SPIRVGlobalRegistry *GR) {
281857f79371SVyacheslav Levytskyy   assert(ExtensionType->getNumIntParameters() == 4 &&
281957f79371SVyacheslav Levytskyy          "Invalid number of parameters for SPIR-V coop matrices builtin!");
282057f79371SVyacheslav Levytskyy   assert(ExtensionType->getNumTypeParameters() == 1 &&
282157f79371SVyacheslav Levytskyy          "SPIR-V coop matrices builtin type must have a type parameter!");
282257f79371SVyacheslav Levytskyy   const SPIRVType *ElemType =
282357f79371SVyacheslav Levytskyy       GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
282457f79371SVyacheslav Levytskyy   // Create or get an existing type from GlobalRegistry.
282557f79371SVyacheslav Levytskyy   return GR->getOrCreateOpTypeCoopMatr(
282657f79371SVyacheslav Levytskyy       MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0),
282757f79371SVyacheslav Levytskyy       ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
282857f79371SVyacheslav Levytskyy       ExtensionType->getIntParameter(3));
282957f79371SVyacheslav Levytskyy }
283057f79371SVyacheslav Levytskyy 
28313544d200SIlia Diachkov static SPIRVType *
28325ac69674SMichal Paszkowski getImageType(const TargetExtType *ExtensionType,
28335ac69674SMichal Paszkowski              const SPIRV::AccessQualifier::AccessQualifier Qualifier,
2834698c8001SIlia Diachkov              MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
28355ac69674SMichal Paszkowski   assert(ExtensionType->getNumTypeParameters() == 1 &&
28365ac69674SMichal Paszkowski          "SPIR-V image builtin type must have sampled type parameter!");
28375ac69674SMichal Paszkowski   const SPIRVType *SampledType =
28385ac69674SMichal Paszkowski       GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
28395114758bSSteven Perron   assert((ExtensionType->getNumIntParameters() == 7 ||
28405114758bSSteven Perron           ExtensionType->getNumIntParameters() == 6) &&
28415ac69674SMichal Paszkowski          "Invalid number of parameters for SPIR-V image builtin!");
28425114758bSSteven Perron 
28435114758bSSteven Perron   SPIRV::AccessQualifier::AccessQualifier accessQualifier =
28445114758bSSteven Perron       SPIRV::AccessQualifier::None;
28455114758bSSteven Perron   if (ExtensionType->getNumIntParameters() == 7) {
28465114758bSSteven Perron     accessQualifier = Qualifier == SPIRV::AccessQualifier::WriteOnly
28475114758bSSteven Perron                           ? SPIRV::AccessQualifier::WriteOnly
28485114758bSSteven Perron                           : SPIRV::AccessQualifier::AccessQualifier(
28495114758bSSteven Perron                                 ExtensionType->getIntParameter(6));
28505114758bSSteven Perron   }
28515114758bSSteven Perron 
28525ac69674SMichal Paszkowski   // Create or get an existing type from GlobalRegistry.
2853698c8001SIlia Diachkov   return GR->getOrCreateOpTypeImage(
28545ac69674SMichal Paszkowski       MIRBuilder, SampledType,
28555ac69674SMichal Paszkowski       SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)),
28565ac69674SMichal Paszkowski       ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
28575ac69674SMichal Paszkowski       ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4),
28585ac69674SMichal Paszkowski       SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)),
28595114758bSSteven Perron       accessQualifier);
2860698c8001SIlia Diachkov }
2861698c8001SIlia Diachkov 
28625ac69674SMichal Paszkowski static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType,
2863698c8001SIlia Diachkov                                       MachineIRBuilder &MIRBuilder,
2864698c8001SIlia Diachkov                                       SPIRVGlobalRegistry *GR) {
28655ac69674SMichal Paszkowski   SPIRVType *OpaqueImageType = getImageType(
28665ac69674SMichal Paszkowski       OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder, GR);
28675ac69674SMichal Paszkowski   // Create or get an existing type from GlobalRegistry.
28685ac69674SMichal Paszkowski   return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
2869698c8001SIlia Diachkov }
2870698c8001SIlia Diachkov 
2871698c8001SIlia Diachkov namespace SPIRV {
287243222bd3SMichal Paszkowski TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,
287343222bd3SMichal Paszkowski                                                    LLVMContext &Context) {
287481751905SMichal Paszkowski   StringRef NameWithParameters = TypeName;
287581751905SMichal Paszkowski 
287681751905SMichal Paszkowski   // Pointers-to-opaque-structs representing OpenCL types are first translated
287781751905SMichal Paszkowski   // to equivalent SPIR-V types. OpenCL builtin type names should have the
287881751905SMichal Paszkowski   // following format: e.g. %opencl.event_t
2879395f9ce3SKazu Hirata   if (NameWithParameters.starts_with("opencl.")) {
288081751905SMichal Paszkowski     const SPIRV::OpenCLType *OCLTypeRecord =
288181751905SMichal Paszkowski         SPIRV::lookupOpenCLType(NameWithParameters);
288281751905SMichal Paszkowski     if (!OCLTypeRecord)
288381751905SMichal Paszkowski       report_fatal_error("Missing TableGen record for OpenCL type: " +
288481751905SMichal Paszkowski                          NameWithParameters);
288581751905SMichal Paszkowski     NameWithParameters = OCLTypeRecord->SpirvTypeLiteral;
288681751905SMichal Paszkowski     // Continue with the SPIR-V builtin type...
288781751905SMichal Paszkowski   }
288881751905SMichal Paszkowski 
288981751905SMichal Paszkowski   // Names of the opaque structs representing a SPIR-V builtins without
289081751905SMichal Paszkowski   // parameters should have the following format: e.g. %spirv.Event
2891395f9ce3SKazu Hirata   assert(NameWithParameters.starts_with("spirv.") &&
289281751905SMichal Paszkowski          "Unknown builtin opaque type!");
289381751905SMichal Paszkowski 
289481751905SMichal Paszkowski   // Parameterized SPIR-V builtins names follow this format:
289581751905SMichal Paszkowski   // e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._0
28961daf2994SKazu Hirata   if (!NameWithParameters.contains('_'))
289743222bd3SMichal Paszkowski     return TargetExtType::get(Context, NameWithParameters);
289881751905SMichal Paszkowski 
289981751905SMichal Paszkowski   SmallVector<StringRef> Parameters;
290081751905SMichal Paszkowski   unsigned BaseNameLength = NameWithParameters.find('_') - 1;
290181751905SMichal Paszkowski   SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_");
290281751905SMichal Paszkowski 
290381751905SMichal Paszkowski   SmallVector<Type *, 1> TypeParameters;
290481751905SMichal Paszkowski   bool HasTypeParameter = !isDigit(Parameters[0][0]);
290581751905SMichal Paszkowski   if (HasTypeParameter)
290643222bd3SMichal Paszkowski     TypeParameters.push_back(parseTypeString(Parameters[0], Context));
290781751905SMichal Paszkowski   SmallVector<unsigned> IntParameters;
290881751905SMichal Paszkowski   for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) {
290981751905SMichal Paszkowski     unsigned IntParameter = 0;
291081751905SMichal Paszkowski     bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter);
2911b221b973SVyacheslav Levytskyy     (void)ValidLiteral;
291281751905SMichal Paszkowski     assert(ValidLiteral &&
291381751905SMichal Paszkowski            "Invalid format of SPIR-V builtin parameter literal!");
291481751905SMichal Paszkowski     IntParameters.push_back(IntParameter);
291581751905SMichal Paszkowski   }
291643222bd3SMichal Paszkowski   return TargetExtType::get(Context,
291781751905SMichal Paszkowski                             NameWithParameters.substr(0, BaseNameLength),
291881751905SMichal Paszkowski                             TypeParameters, IntParameters);
291981751905SMichal Paszkowski }
292081751905SMichal Paszkowski 
29215ac69674SMichal Paszkowski SPIRVType *lowerBuiltinType(const Type *OpaqueType,
29224421b24fSIlia Diachkov                             SPIRV::AccessQualifier::AccessQualifier AccessQual,
2923698c8001SIlia Diachkov                             MachineIRBuilder &MIRBuilder,
2924698c8001SIlia Diachkov                             SPIRVGlobalRegistry *GR) {
29255ac69674SMichal Paszkowski   // In LLVM IR, SPIR-V and OpenCL builtin types are represented as either
29265ac69674SMichal Paszkowski   // target(...) target extension types or pointers-to-opaque-structs. The
29275ac69674SMichal Paszkowski   // approach relying on structs is deprecated and works only in the non-opaque
29285ac69674SMichal Paszkowski   // pointer mode (-opaque-pointers=0).
29295ac69674SMichal Paszkowski   // In order to maintain compatibility with LLVM IR generated by older versions
29305ac69674SMichal Paszkowski   // of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are
29315ac69674SMichal Paszkowski   // "translated" to target extension types. This translation is temporary and
29325ac69674SMichal Paszkowski   // will be removed in the future release of LLVM.
29335ac69674SMichal Paszkowski   const TargetExtType *BuiltinType = dyn_cast<TargetExtType>(OpaqueType);
29345ac69674SMichal Paszkowski   if (!BuiltinType)
293581751905SMichal Paszkowski     BuiltinType = parseBuiltinTypeNameToTargetExtType(
293643222bd3SMichal Paszkowski         OpaqueType->getStructName().str(), MIRBuilder.getContext());
29375ac69674SMichal Paszkowski 
2938698c8001SIlia Diachkov   unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
2939698c8001SIlia Diachkov 
29405ac69674SMichal Paszkowski   const StringRef Name = BuiltinType->getName();
2941698c8001SIlia Diachkov   LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
2942698c8001SIlia Diachkov 
2943698c8001SIlia Diachkov   // Lookup the demangled builtin type in the TableGen records.
29445ac69674SMichal Paszkowski   const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
2945698c8001SIlia Diachkov   if (!TypeRecord)
2946698c8001SIlia Diachkov     report_fatal_error("Missing TableGen record for builtin type: " + Name);
2947698c8001SIlia Diachkov 
2948698c8001SIlia Diachkov   // "Lower" the BuiltinType into TargetType. The following get<...>Type methods
29495ac69674SMichal Paszkowski   // use the implementation details from TableGen records or TargetExtType
29505ac69674SMichal Paszkowski   // parameters to either create a new OpType<...> machine instruction or get an
29515ac69674SMichal Paszkowski   // existing equivalent SPIRVType from GlobalRegistry.
2952698c8001SIlia Diachkov   SPIRVType *TargetType;
2953698c8001SIlia Diachkov   switch (TypeRecord->Opcode) {
2954698c8001SIlia Diachkov   case SPIRV::OpTypeImage:
29555ac69674SMichal Paszkowski     TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
2956698c8001SIlia Diachkov     break;
2957698c8001SIlia Diachkov   case SPIRV::OpTypePipe:
29585ac69674SMichal Paszkowski     TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
2959698c8001SIlia Diachkov     break;
2960748922b3SIlia Diachkov   case SPIRV::OpTypeDeviceEvent:
2961748922b3SIlia Diachkov     TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
2962748922b3SIlia Diachkov     break;
2963698c8001SIlia Diachkov   case SPIRV::OpTypeSampler:
2964698c8001SIlia Diachkov     TargetType = getSamplerType(MIRBuilder, GR);
2965698c8001SIlia Diachkov     break;
2966698c8001SIlia Diachkov   case SPIRV::OpTypeSampledImage:
29675ac69674SMichal Paszkowski     TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
2968698c8001SIlia Diachkov     break;
296957f79371SVyacheslav Levytskyy   case SPIRV::OpTypeCooperativeMatrixKHR:
297057f79371SVyacheslav Levytskyy     TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
297157f79371SVyacheslav Levytskyy     break;
2972698c8001SIlia Diachkov   default:
29735ac69674SMichal Paszkowski     TargetType =
297420f650b2SMichal Paszkowski         getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
2975698c8001SIlia Diachkov     break;
2976698c8001SIlia Diachkov   }
2977698c8001SIlia Diachkov 
2978698c8001SIlia Diachkov   // Emit OpName instruction if a new OpType<...> instruction was added
2979698c8001SIlia Diachkov   // (equivalent type was not found in GlobalRegistry).
2980698c8001SIlia Diachkov   if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
29815ac69674SMichal Paszkowski     buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder);
2982698c8001SIlia Diachkov 
2983698c8001SIlia Diachkov   return TargetType;
2984698c8001SIlia Diachkov }
2985f61eb416SIlia Diachkov } // namespace SPIRV
2986f61eb416SIlia Diachkov } // namespace llvm
2987