1//===- SPIRVInstructionSelector.cpp ------------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the targeting of the InstructionSelector class for
10// SPIRV.
11// TODO: This should be generated by TableGen.
12//
13//===----------------------------------------------------------------------===//
14
15#include "MCTargetDesc/SPIRVBaseInfo.h"
16#include "MCTargetDesc/SPIRVMCTargetDesc.h"
17#include "SPIRV.h"
18#include "SPIRVGlobalRegistry.h"
19#include "SPIRVInstrInfo.h"
20#include "SPIRVRegisterInfo.h"
21#include "SPIRVTargetMachine.h"
22#include "SPIRVTypeInst.h"
23#include "SPIRVUtils.h"
24#include "llvm/ADT/APFloat.h"
25#include "llvm/ADT/STLExtras.h"
26#include "llvm/ADT/STLFunctionalExtras.h"
27#include "llvm/ADT/SmallSet.h"
28#include "llvm/ADT/StringExtras.h"
29#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
30#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
31#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
32#include "llvm/CodeGen/MachineInstrBuilder.h"
33#include "llvm/CodeGen/MachineRegisterInfo.h"
34#include "llvm/CodeGen/Register.h"
35#include "llvm/CodeGen/TargetOpcodes.h"
36#include "llvm/IR/DiagnosticInfo.h"
37#include "llvm/IR/IntrinsicsSPIRV.h"
38#include "llvm/Support/Debug.h"
39#include "llvm/Support/ErrorHandling.h"
40#include <functional>
41#include <optional>
42
43#define DEBUG_TYPE "spirv-isel"
44
45using namespace llvm;
46namespace CL = SPIRV::OpenCLExtInst;
47namespace GL = SPIRV::GLSLExtInst;
48
49using ExtInstList =
50 std::vector<std::pair<SPIRV::InstructionSet::InstructionSet, uint32_t>>;
51
52namespace {
53
54struct ImageOperands {
55 std::optional<Register> Bias;
56 std::optional<Register> Offset;
57 std::optional<Register> MinLod;
58 std::optional<Register> GradX;
59 std::optional<Register> GradY;
60 std::optional<Register> Lod;
61 std::optional<Register> Compare;
62};
63
64llvm::SPIRV::SelectionControl::SelectionControl
65getSelectionOperandForImm(int Imm) {
66 if (Imm == 2)
67 return SPIRV::SelectionControl::Flatten;
68 if (Imm == 1)
69 return SPIRV::SelectionControl::DontFlatten;
70 if (Imm == 0)
71 return SPIRV::SelectionControl::None;
72 llvm_unreachable("Invalid immediate");
73}
74
75#define GET_GLOBALISEL_PREDICATE_BITSET
76#include "SPIRVGenGlobalISel.inc"
77#undef GET_GLOBALISEL_PREDICATE_BITSET
78
79class SPIRVInstructionSelector : public InstructionSelector {
80 const SPIRVSubtarget &STI;
81 const SPIRVInstrInfo &TII;
82 const SPIRVRegisterInfo &TRI;
83 const RegisterBankInfo &RBI;
84 SPIRVGlobalRegistry &GR;
85 MachineRegisterInfo *MRI;
86 MachineFunction *HasVRegsReset = nullptr;
87
88 /// We need to keep track of the number we give to anonymous global values to
89 /// generate the same name every time when this is needed.
90 mutable DenseMap<const GlobalValue *, unsigned> UnnamedGlobalIDs;
91 SmallPtrSet<MachineInstr *, 8> DeadMIs;
92
93public:
94 SPIRVInstructionSelector(const SPIRVTargetMachine &TM,
95 const SPIRVSubtarget &ST,
96 const RegisterBankInfo &RBI);
97 void setupMF(MachineFunction &MF, GISelValueTracking *VT,
98 CodeGenCoverage *CoverageInfo, ProfileSummaryInfo *PSI,
99 BlockFrequencyInfo *BFI) override;
100 // Common selection code. Instruction-specific selection occurs in spvSelect.
101 bool select(MachineInstr &I) override;
102 static const char *getName() { return DEBUG_TYPE; }
103
104#define GET_GLOBALISEL_PREDICATES_DECL
105#include "SPIRVGenGlobalISel.inc"
106#undef GET_GLOBALISEL_PREDICATES_DECL
107
108#define GET_GLOBALISEL_TEMPORARIES_DECL
109#include "SPIRVGenGlobalISel.inc"
110#undef GET_GLOBALISEL_TEMPORARIES_DECL
111
112private:
113 void resetVRegsType(MachineFunction &MF);
114 void removeDeadInstruction(MachineInstr &MI) const;
115 void removeOpNamesForDeadMI(MachineInstr &MI) const;
116
117 // tblgen-erated 'select' implementation, used as the initial selector for
118 // the patterns that don't require complex C++.
119 bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
120
121 // All instruction-specific selection that didn't happen in "select()".
122 // Is basically a large Switch/Case delegating to all other select method.
123 bool spvSelect(Register ResVReg, SPIRVTypeInst ResType,
124 MachineInstr &I) const;
125
126 bool selectFirstBitHigh(Register ResVReg, SPIRVTypeInst ResType,
127 MachineInstr &I, bool IsSigned) const;
128
129 bool selectFirstBitLow(Register ResVReg, SPIRVTypeInst ResType,
130 MachineInstr &I) const;
131
132 bool selectFirstBitSet16(Register ResVReg, SPIRVTypeInst ResType,
133 MachineInstr &I, unsigned ExtendOpcode,
134 unsigned BitSetOpcode) const;
135
136 bool selectFirstBitSet32(Register ResVReg, SPIRVTypeInst ResType,
137 MachineInstr &I, Register SrcReg,
138 unsigned BitSetOpcode) const;
139
140 bool selectFirstBitSet64(Register ResVReg, SPIRVTypeInst ResType,
141 MachineInstr &I, Register SrcReg,
142 unsigned BitSetOpcode, bool SwapPrimarySide) const;
143
144 bool selectGlobalValue(Register ResVReg, MachineInstr &I,
145 const MachineInstr *Init = nullptr) const;
146
147 bool selectOpWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
148 MachineInstr &I, ArrayRef<Register> SrcRegs,
149 unsigned Opcode) const;
150
151 bool selectUnOp(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
152 unsigned Opcode) const;
153
154 bool selectBitcast(Register ResVReg, SPIRVTypeInst ResType,
155 MachineInstr &I) const;
156
157 bool selectLoad(Register ResVReg, SPIRVTypeInst ResType,
158 MachineInstr &I) const;
159 bool selectAtomicLoad(Register ResVReg, SPIRVTypeInst ResType,
160 MachineInstr &I) const;
161 bool selectStore(MachineInstr &I) const;
162 bool selectAtomicStore(MachineInstr &I) const;
163
164 bool selectStackSave(Register ResVReg, SPIRVTypeInst ResType,
165 MachineInstr &I) const;
166 bool selectStackRestore(MachineInstr &I) const;
167
168 bool selectMemOperation(Register ResVReg, MachineInstr &I) const;
169 Register getOrCreateMemSetGlobal(MachineInstr &I) const;
170 bool selectCopyMemory(MachineInstr &I, Register SrcReg) const;
171 bool selectCopyMemorySized(MachineInstr &I, Register SrcReg) const;
172
173 bool selectAtomicRMW(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
174 unsigned NewOpcode, unsigned NegateOpcode = 0) const;
175
176 // Creates an integer-typed register with bitwidth equal to pointer size.
177 Register createPtrSizedIntReg(MachineIRBuilder &MIRBuilder) const;
178 // Emit an OpConvertPtrToU that converts the pointer value in \p PtrVal into
179 // an integer of equal bitwidth, returning the register holding the result.
180 Register convertPtrToInt(Register PtrVal, MachineIRBuilder &MIRBuilder) const;
181 // Emit an OpBitcast that reinterprets the pointer \p Ptr as a pointer to an
182 // integer of pointer size in storage class \p SC, returning the result.
183 Register castPtrToPtrToInt(Register Ptr, SPIRV::StorageClass::StorageClass SC,
184 MachineIRBuilder &MIRBuilder) const;
185 // Handle atomic loads, stores and exchanges of pointer types by casting
186 // to/from integer types as needed.
187 bool selectAtomicPtrValue(
188 Register ResVReg, SPIRVTypeInst ResType, MachineIRBuilder &MIRBuilder,
189 function_ref<Register(SPIRVTypeInst IntType)> EmitAtomic) const;
190
191 bool selectAtomicCmpXchg(Register ResVReg, SPIRVTypeInst ResType,
192 MachineInstr &I) const;
193
194 bool selectFence(MachineInstr &I) const;
195
196 bool selectAddrSpaceCast(Register ResVReg, SPIRVTypeInst ResType,
197 MachineInstr &I) const;
198
199 bool selectPtrMask(Register ResVReg, SPIRVTypeInst ResType,
200 MachineInstr &I) const;
201
202 bool selectAnyOrAll(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
203 unsigned OpType) const;
204
205 bool selectAll(Register ResVReg, SPIRVTypeInst ResType,
206 MachineInstr &I) const;
207
208 bool selectAny(Register ResVReg, SPIRVTypeInst ResType,
209 MachineInstr &I) const;
210
211 bool selectBitreverse(Register ResVReg, SPIRVTypeInst ResType,
212 MachineInstr &I) const;
213
214 bool selectBitreverseViaI32(Register ResVReg, SPIRVTypeInst ResType,
215 MachineInstr &I, Register Op) const;
216
217 bool selectBitreverse64(Register ResVReg, SPIRVTypeInst ResType,
218 MachineInstr &I, Register SrcReg) const;
219
220 bool selectBitreverseNative(Register ResVReg, SPIRVTypeInst ResType,
221 MachineInstr &I, Register Op) const;
222
223 bool selectBuildVector(Register ResVReg, SPIRVTypeInst ResType,
224 MachineInstr &I) const;
225 bool selectSplatVector(Register ResVReg, SPIRVTypeInst ResType,
226 MachineInstr &I) const;
227 bool selectConcatVectors(Register ResVReg, SPIRVTypeInst ResType,
228 MachineInstr &I) const;
229
230 bool selectCmp(Register ResVReg, SPIRVTypeInst ResType,
231 unsigned comparisonOpcode, MachineInstr &I) const;
232 bool selectDiscard(Register ResVReg, SPIRVTypeInst ResType,
233 MachineInstr &I) const;
234
235 bool selectICmp(Register ResVReg, SPIRVTypeInst ResType,
236 MachineInstr &I) const;
237 bool selectFCmp(Register ResVReg, SPIRVTypeInst ResType,
238 MachineInstr &I) const;
239
240 bool selectSign(Register ResVReg, SPIRVTypeInst ResType,
241 MachineInstr &I) const;
242
243 bool selectFloatDot(Register ResVReg, SPIRVTypeInst ResType,
244 MachineInstr &I) const;
245
246 bool selectOverflowArith(Register ResVReg, SPIRVTypeInst ResType,
247 MachineInstr &I, unsigned Opcode) const;
248 bool selectDebugTrap(Register ResVReg, SPIRVTypeInst ResType,
249 MachineInstr &I) const;
250 bool selectPrefetch(MachineInstr &I) const;
251
252 bool selectIntegerDot(Register ResVReg, SPIRVTypeInst ResType,
253 MachineInstr &I, bool Signed) const;
254
255 bool selectIntegerDotExpansion(Register ResVReg, SPIRVTypeInst ResType,
256 MachineInstr &I) const;
257
258 bool selectOpIsInf(Register ResVReg, SPIRVTypeInst ResType,
259 MachineInstr &I) const;
260
261 bool selectOpIsNan(Register ResVReg, SPIRVTypeInst ResType,
262 MachineInstr &I) const;
263
264 bool selectOpIsFinite(Register ResVReg, SPIRVTypeInst ResType,
265 MachineInstr &I) const;
266
267 bool selectOpIsNormal(Register ResVReg, SPIRVTypeInst ResType,
268 MachineInstr &I) const;
269
270 bool selectPopCount(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
271 unsigned Opcode) const;
272
273 bool selectPopCount16(Register ResVReg, SPIRVTypeInst ResType,
274 MachineInstr &I, unsigned ExtOpcode,
275 unsigned Opcode) const;
276
277 bool selectPopCount32(Register ResVReg, SPIRVTypeInst ResType,
278 MachineInstr &I, Register SrcReg,
279 unsigned Opcode) const;
280
281 bool selectPopCount64(Register ResVReg, SPIRVTypeInst ResType,
282 MachineInstr &I, Register SrcReg,
283 unsigned Opcode) const;
284
285 template <bool Signed>
286 bool selectDot4AddPacked(Register ResVReg, SPIRVTypeInst ResType,
287 MachineInstr &I) const;
288 template <bool Signed>
289 bool selectDot4AddPackedExpansion(Register ResVReg, SPIRVTypeInst ResType,
290 MachineInstr &I) const;
291
292 bool selectWavePrefixBitCount(Register ResVReg, SPIRVTypeInst ResType,
293 MachineInstr &I) const;
294
295 template <typename PickOpcodeFn>
296 bool selectWaveReduce(Register ResVReg, SPIRVTypeInst ResType,
297 MachineInstr &I, bool IsUnsigned,
298 PickOpcodeFn &&PickOpcode) const;
299
300 bool selectWaveReduceOp(Register ResVReg, SPIRVTypeInst ResType,
301 MachineInstr &I, unsigned Opcode) const;
302
303 bool selectWaveReduceMax(Register ResVReg, SPIRVTypeInst ResType,
304 MachineInstr &I, bool IsUnsigned) const;
305
306 bool selectWaveReduceMin(Register ResVReg, SPIRVTypeInst ResType,
307 MachineInstr &I, bool IsUnsigned) const;
308
309 bool selectWaveReduceSum(Register ResVReg, SPIRVTypeInst ResType,
310 MachineInstr &I) const;
311
312 bool selectWaveReduceProduct(Register ResVReg, const SPIRVTypeInst ResType,
313 MachineInstr &I) const;
314
315 template <typename PickOpcodeFn>
316 bool selectWaveExclusiveScan(Register ResVReg, SPIRVTypeInst ResType,
317 MachineInstr &I, bool IsUnsigned,
318 PickOpcodeFn &&PickOpcode) const;
319
320 bool selectWaveExclusiveScanSum(Register ResVReg, SPIRVTypeInst ResType,
321 MachineInstr &I) const;
322
323 bool selectWaveExclusiveScanProduct(Register ResVReg, SPIRVTypeInst ResType,
324 MachineInstr &I) const;
325
326 bool selectQuadSwap(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
327 unsigned Direction) const;
328
329 bool selectConst(Register ResVReg, SPIRVTypeInst ResType,
330 MachineInstr &I) const;
331
332 bool selectSelect(Register ResVReg, SPIRVTypeInst ResType,
333 MachineInstr &I) const;
334 bool selectBoolToInt(Register ResVReg, SPIRVTypeInst ResType,
335 Register BooleanVReg, MachineInstr &InsertAt,
336 bool IsSigned) const;
337 bool selectIToF(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
338 bool IsSigned, unsigned Opcode) const;
339 bool selectExt(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
340 bool IsSigned) const;
341
342 bool selectTrunc(Register ResVReg, SPIRVTypeInst ResType,
343 MachineInstr &I) const;
344
345 bool selectSUCmp(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
346 bool IsSigned) const;
347
348 bool selectIntToBool(Register IntReg, Register ResVReg, MachineInstr &I,
349 SPIRVTypeInst intTy, SPIRVTypeInst boolTy) const;
350
351 bool selectOpUndef(Register ResVReg, SPIRVTypeInst ResType,
352 MachineInstr &I) const;
353 bool selectFreeze(Register ResVReg, SPIRVTypeInst ResType,
354 MachineInstr &I) const;
355 bool selectIntrinsic(Register ResVReg, SPIRVTypeInst ResType,
356 MachineInstr &I) const;
357 bool selectExtractVal(Register ResVReg, SPIRVTypeInst ResType,
358 MachineInstr &I) const;
359 bool selectInsertVal(Register ResVReg, SPIRVTypeInst ResType,
360 MachineInstr &I) const;
361 bool selectExtractElt(Register ResVReg, SPIRVTypeInst ResType,
362 MachineInstr &I) const;
363 bool selectInsertElt(Register ResVReg, SPIRVTypeInst ResType,
364 MachineInstr &I) const;
365 bool selectGEP(Register ResVReg, SPIRVTypeInst ResType,
366 MachineInstr &I) const;
367
368 bool selectMaskedGather(Register ResVReg, SPIRVTypeInst ResType,
369 MachineInstr &I) const;
370 bool selectMaskedScatter(MachineInstr &I) const;
371
372 bool diagnoseUnsupported(const MachineInstr &I, const Twine &Msg) const;
373
374 bool selectAbort(MachineInstr &I) const;
375 bool selectTrap(MachineInstr &I) const;
376 bool selectFrameIndex(Register ResVReg, SPIRVTypeInst ResType,
377 MachineInstr &I) const;
378 bool selectAllocaArray(Register ResVReg, SPIRVTypeInst ResType,
379 MachineInstr &I) const;
380
381 bool selectBranch(MachineInstr &I) const;
382 bool selectBranchCond(MachineInstr &I) const;
383
384 bool selectPhi(Register ResVReg, MachineInstr &I) const;
385
386 bool selectExtInst(Register ResVReg, SPIRVTypeInst RestType, MachineInstr &I,
387 GL::GLSLExtInst GLInst, bool setMIFlags = true,
388 bool useMISrc = true,
389 ArrayRef<Register> SrcRegs = {}) const;
390 bool selectExtInst(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
391 CL::OpenCLExtInst CLInst, bool setMIFlags = true,
392 bool useMISrc = true,
393 ArrayRef<Register> SrcRegs = {}) const;
394 bool selectExtInst(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
395 CL::OpenCLExtInst CLInst, GL::GLSLExtInst GLInst,
396 bool setMIFlags = true, bool useMISrc = true,
397 ArrayRef<Register> SrcRegs = {}) const;
398 bool selectExtInst(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
399 const ExtInstList &ExtInsts, bool setMIFlags = true,
400 bool useMISrc = true,
401 ArrayRef<Register> SrcRegs = {}) const;
402
403 bool selectLog10(Register ResVReg, SPIRVTypeInst ResType,
404 MachineInstr &I) const;
405
406 bool selectFpowi(Register ResVReg, SPIRVTypeInst ResType,
407 MachineInstr &I) const;
408
409 bool selectSaturate(Register ResVReg, SPIRVTypeInst ResType,
410 MachineInstr &I) const;
411
412 bool selectWaveOpInst(Register ResVReg, SPIRVTypeInst ResType,
413 MachineInstr &I, unsigned Opcode) const;
414
415 bool selectBarrierInst(MachineInstr &I, unsigned Scope, unsigned MemSem,
416 bool WithGroupSync) const;
417
418 bool selectWaveActiveCountBits(Register ResVReg, SPIRVTypeInst ResType,
419 MachineInstr &I) const;
420
421 bool selectWaveActiveAllEqual(Register ResVReg, SPIRVTypeInst ResType,
422 MachineInstr &I) const;
423
424 bool selectUnmergeValues(MachineInstr &I) const;
425
426 bool selectHandleFromBinding(Register &ResVReg, SPIRVTypeInst ResType,
427 MachineInstr &I) const;
428
429 bool selectCounterHandleFromBinding(Register &ResVReg, SPIRVTypeInst ResType,
430 MachineInstr &I) const;
431
432 bool selectReadImageIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
433 MachineInstr &I) const;
434 bool selectGetDimensionsIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
435 MachineInstr &I) const;
436 bool selectGetDimensionsLevelsIntrinsic(Register &ResVReg,
437 SPIRVTypeInst ResType,
438 MachineInstr &I) const;
439 bool selectGetDimensionsMSIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
440 MachineInstr &I) const;
441 bool
442 selectImageQuerySize(Register ImageReg, Register &ResVReg, MachineInstr &I,
443 std::optional<Register> LodReg = std::nullopt) const;
444 bool selectSampleBasicIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
445 MachineInstr &I) const;
446 bool selectCalculateLodIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
447 MachineInstr &I) const;
448 bool selectSampleBiasIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
449 MachineInstr &I) const;
450 bool selectSampleGradIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
451 MachineInstr &I) const;
452 bool selectSampleLevelIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
453 MachineInstr &I) const;
454 bool selectLoadLevelIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
455 MachineInstr &I) const;
456 bool selectSampleCmpIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
457 MachineInstr &I) const;
458 bool selectSampleCmpLevelZeroIntrinsic(Register &ResVReg,
459 SPIRVTypeInst ResType,
460 MachineInstr &I) const;
461 bool selectGatherIntrinsic(Register &ResVReg, SPIRVTypeInst ResType,
462 MachineInstr &I) const;
463 bool selectImageWriteIntrinsic(MachineInstr &I) const;
464 bool selectResourceGetPointer(Register &ResVReg, SPIRVTypeInst ResType,
465 MachineInstr &I) const;
466 bool selectPushConstantGetPointer(Register &ResVReg, SPIRVTypeInst ResType,
467 MachineInstr &I) const;
468 bool selectResourceNonUniformIndex(Register &ResVReg, SPIRVTypeInst ResType,
469 MachineInstr &I) const;
470 bool selectModf(Register ResVReg, SPIRVTypeInst ResType,
471 MachineInstr &I) const;
472 bool selectUpdateCounter(Register &ResVReg, SPIRVTypeInst ResType,
473 MachineInstr &I) const;
474 bool selectFrexp(Register ResVReg, SPIRVTypeInst ResType,
475 MachineInstr &I) const;
476
477 bool selectCopySign(Register ResVReg, SPIRVTypeInst ResType,
478 MachineInstr &I) const;
479
480 bool selectLdexp(Register ResVReg, SPIRVTypeInst ResType,
481 MachineInstr &I) const;
482 bool selectSincos(Register ResVReg, SPIRVTypeInst ResType,
483 MachineInstr &I) const;
484 bool selectExp10(Register ResVReg, SPIRVTypeInst ResType,
485 MachineInstr &I) const;
486 bool selectDerivativeInst(Register ResVReg, SPIRVTypeInst ResType,
487 MachineInstr &I, const unsigned DPdOpCode) const;
488 // Utilities
489 Register buildI32Constant(uint32_t Val, MachineInstr &I,
490 SPIRVTypeInst ResType = nullptr) const;
491 Register buildI32ConstantInEntryBlock(uint32_t Val, MachineInstr &I,
492 SPIRVTypeInst ResType = nullptr) const;
493
494 Register buildZerosVal(SPIRVTypeInst ResType, MachineInstr &I) const;
495 bool isScalarOrVectorIntConstantZero(Register Reg) const;
496 Register buildZerosValF(SPIRVTypeInst ResType, MachineInstr &I) const;
497 Register buildOnesVal(bool AllOnes, SPIRVTypeInst ResType,
498 MachineInstr &I) const;
499 Register buildOnesValF(SPIRVTypeInst ResType, MachineInstr &I) const;
500
501 bool wrapIntoSpecConstantOp(MachineInstr &I,
502 SmallVector<Register> &CompositeArgs) const;
503
504 Register getUcharPtrTypeReg(MachineInstr &I,
505 SPIRV::StorageClass::StorageClass SC) const;
506 MachineInstrBuilder buildSpecConstantOp(MachineInstr &I, Register Dest,
507 Register Src, Register DestType,
508 uint32_t Opcode) const;
509 MachineInstrBuilder buildConstGenericPtr(MachineInstr &I, Register SrcPtr,
510 SPIRVTypeInst SrcPtrTy) const;
511 Register buildPointerToResource(SPIRVTypeInst ResType,
512 SPIRV::StorageClass::StorageClass SC,
513 uint32_t Set, uint32_t Binding,
514 uint32_t ArraySize, Register IndexReg,
515 StringRef Name,
516 MachineIRBuilder MIRBuilder) const;
517 SPIRVTypeInst widenTypeToVec4(SPIRVTypeInst Type, MachineInstr &I) const;
518 bool extractSubvector(Register &ResVReg, SPIRVTypeInst ResType,
519 Register &ReadReg, MachineInstr &InsertionPoint) const;
520 bool generateImageReadOrFetch(Register &ResVReg, SPIRVTypeInst ResType,
521 Register ImageReg, Register IdxReg,
522 DebugLoc Loc, MachineInstr &Pos,
523 const ImageOperands *ImOps = nullptr) const;
524 bool generateSampleImage(Register ResVReg, SPIRVTypeInst ResType,
525 Register ImageReg, Register SamplerReg,
526 Register CoordinateReg, const ImageOperands &ImOps,
527 DebugLoc Loc, MachineInstr &I) const;
528 bool BuildCOPY(Register DestReg, Register SrcReg, MachineInstr &I) const;
529 bool loadVec3BuiltinInputID(SPIRV::BuiltIn::BuiltIn BuiltInValue,
530 Register ResVReg, SPIRVTypeInst ResType,
531 MachineInstr &I) const;
532 bool loadBuiltinInputID(SPIRV::BuiltIn::BuiltIn BuiltInValue,
533 Register ResVReg, SPIRVTypeInst ResType,
534 MachineInstr &I) const;
535 bool loadHandleBeforePosition(Register &HandleReg, SPIRVTypeInst ResType,
536 GIntrinsic &HandleDef, MachineInstr &Pos) const;
537 void decorateUsesAsNonUniform(Register &NonUniformReg) const;
538 bool errorIfInstrOutsideShader(MachineInstr &I) const;
539
540 bool
541 handle64BitOverflow(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
542 Register SrcReg, unsigned int Opcode,
543 std::function<bool(Register, SPIRVTypeInst,
544 MachineInstr &, Register, unsigned)>
545 CallbackFunction) const;
546};
547
548bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) {
549 const TargetExtType *TET = cast<TargetExtType>(Val: HandleType);
550 if (TET->getTargetExtName() == "spirv.Image") {
551 return false;
552 }
553 assert(TET->getTargetExtName() == "spirv.SignedImage");
554 return TET->getTypeParameter(i: 0)->isIntegerTy();
555}
556} // end anonymous namespace
557
558#define GET_GLOBALISEL_IMPL
559#include "SPIRVGenGlobalISel.inc"
560#undef GET_GLOBALISEL_IMPL
561
562SPIRVInstructionSelector::SPIRVInstructionSelector(const SPIRVTargetMachine &TM,
563 const SPIRVSubtarget &ST,
564 const RegisterBankInfo &RBI)
565 : InstructionSelector(), STI(ST), TII(*ST.getInstrInfo()),
566 TRI(*ST.getRegisterInfo()), RBI(RBI), GR(*ST.getSPIRVGlobalRegistry()),
567 MRI(nullptr),
568#define GET_GLOBALISEL_PREDICATES_INIT
569#include "SPIRVGenGlobalISel.inc"
570#undef GET_GLOBALISEL_PREDICATES_INIT
571#define GET_GLOBALISEL_TEMPORARIES_INIT
572#include "SPIRVGenGlobalISel.inc"
573#undef GET_GLOBALISEL_TEMPORARIES_INIT
574{
575}
576
577void SPIRVInstructionSelector::setupMF(MachineFunction &MF,
578 GISelValueTracking *VT,
579 CodeGenCoverage *CoverageInfo,
580 ProfileSummaryInfo *PSI,
581 BlockFrequencyInfo *BFI) {
582 MRI = &MF.getRegInfo();
583 GR.setCurrentFunc(MF);
584 InstructionSelector::setupMF(mf&: MF, vt: VT, covinfo: CoverageInfo, psi: PSI, bfi: BFI);
585}
586
587// Ensure that register classes correspond to pattern matching rules.
588void SPIRVInstructionSelector::resetVRegsType(MachineFunction &MF) {
589 if (HasVRegsReset == &MF)
590 return;
591 HasVRegsReset = &MF;
592
593 MachineRegisterInfo &MRI = MF.getRegInfo();
594 for (unsigned I = 0, E = MRI.getNumVirtRegs(); I != E; ++I) {
595 Register Reg = Register::index2VirtReg(Index: I);
596 LLT RegType = MRI.getType(Reg);
597 if (RegType.isScalar())
598 MRI.setType(VReg: Reg, Ty: LLT::scalar(SizeInBits: 64));
599 else if (RegType.isPointer())
600 MRI.setType(VReg: Reg, Ty: LLT::pointer(AddressSpace: 0, SizeInBits: 64));
601 else if (RegType.isVector())
602 MRI.setType(VReg: Reg, Ty: LLT::fixed_vector(NumElements: 2, ScalarTy: LLT::scalar(SizeInBits: 64)));
603 }
604 for (const auto &MBB : MF) {
605 for (const auto &MI : MBB) {
606 if (isPreISelGenericOpcode(Opcode: MI.getOpcode()))
607 GR.erase(MI: &MI);
608 if (MI.getOpcode() != SPIRV::ASSIGN_TYPE)
609 continue;
610
611 Register DstReg = MI.getOperand(i: 0).getReg();
612 LLT DstType = MRI.getType(Reg: DstReg);
613 Register SrcReg = MI.getOperand(i: 1).getReg();
614 LLT SrcType = MRI.getType(Reg: SrcReg);
615 if (DstType != SrcType)
616 MRI.setType(VReg: DstReg, Ty: MRI.getType(Reg: SrcReg));
617
618 const TargetRegisterClass *DstRC = MRI.getRegClassOrNull(Reg: DstReg);
619 const TargetRegisterClass *SrcRC = MRI.getRegClassOrNull(Reg: SrcReg);
620 if (DstRC != SrcRC && SrcRC)
621 MRI.setRegClass(Reg: DstReg, RC: SrcRC);
622 }
623 }
624}
625
626// Return true if the MachineInstr represents a constant register
627static bool isConstReg(MachineRegisterInfo *MRI, MachineInstr *OpDef) {
628
629 SmallVector<MachineInstr *> Stack = {OpDef};
630 SmallPtrSet<MachineInstr *, 4> Visited;
631
632 while (!Stack.empty()) {
633 MachineInstr *MI = Stack.pop_back_val();
634 MI = passCopy(Def: MI, MRI);
635 if (!Visited.insert(Ptr: MI).second)
636 continue;
637 switch (MI->getOpcode()) {
638 case TargetOpcode::G_INTRINSIC:
639 case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
640 case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: {
641 GIntrinsic *GIntr = cast<GIntrinsic>(Val: MI);
642 unsigned IntrID = GIntr->getIntrinsicID();
643 if (IntrID != Intrinsic::spv_const_composite &&
644 IntrID != Intrinsic::spv_undef && IntrID != Intrinsic::spv_poison)
645 return false;
646 continue;
647 }
648 case TargetOpcode::G_BUILD_VECTOR:
649 case TargetOpcode::G_SPLAT_VECTOR:
650 for (unsigned i = OpDef->getNumExplicitDefs();
651 i < OpDef->getNumOperands(); i++) {
652 if (!OpDef->getOperand(i).isReg())
653 continue;
654 MachineInstr *OpNestedDef =
655 MRI->getVRegDef(Reg: OpDef->getOperand(i).getReg());
656 Stack.push_back(Elt: OpNestedDef);
657 }
658 continue;
659 case TargetOpcode::G_CONSTANT:
660 case TargetOpcode::G_FCONSTANT:
661 case TargetOpcode::G_IMPLICIT_DEF:
662 case SPIRV::OpConstantTrue:
663 case SPIRV::OpConstantFalse:
664 case SPIRV::OpConstantI:
665 case SPIRV::OpConstantF:
666 case SPIRV::OpConstantComposite:
667 case SPIRV::OpConstantCompositeContinuedINTEL:
668 case SPIRV::OpConstantSampler:
669 case SPIRV::OpConstantNull:
670 case SPIRV::OpUndef:
671 case SPIRV::OpPoisonKHR:
672 case SPIRV::OpConstantFunctionPointerINTEL:
673 continue;
674 default:
675 return false;
676 }
677 }
678 return true;
679}
680
681// Return true if the virtual register represents a constant
682static bool isConstReg(MachineRegisterInfo *MRI, Register OpReg) {
683 if (MachineInstr *OpDef = MRI->getVRegDef(Reg: OpReg))
684 return isConstReg(MRI, OpDef);
685 return false;
686}
687
688// TODO(168736): We should make this either a flag in tabelgen
689// or reduce our dependence on the global registry, so we can remove this
690// function. It can easily be missed when new intrinsics are added.
691
692// Most SPIR-V intrinsics are considered to have side-effects in their tablegen
693// definition because they are referenced in the global registry. This is a list
694// of intrinsics that have no side effects other than their references in the
695// global registry.
696static bool intrinsicHasSideEffects(Intrinsic::ID ID) {
697 switch (ID) {
698 // This is not an exhaustive list and may need to be updated.
699 case Intrinsic::spv_all:
700 case Intrinsic::spv_alloca:
701 case Intrinsic::spv_any:
702 case Intrinsic::spv_bitcast:
703 case Intrinsic::spv_const_composite:
704 case Intrinsic::spv_degrees:
705 case Intrinsic::spv_distance:
706 case Intrinsic::spv_extractelt:
707 case Intrinsic::spv_extractv:
708 case Intrinsic::spv_faceforward:
709 case Intrinsic::spv_fdot:
710 case Intrinsic::spv_firstbitlow:
711 case Intrinsic::spv_firstbitshigh:
712 case Intrinsic::spv_firstbituhigh:
713 case Intrinsic::spv_frac:
714 case Intrinsic::spv_gep:
715 case Intrinsic::spv_global_offset:
716 case Intrinsic::spv_global_size:
717 case Intrinsic::spv_group_id:
718 case Intrinsic::spv_insertelt:
719 case Intrinsic::spv_insertv:
720 case Intrinsic::spv_isinf:
721 case Intrinsic::spv_isnan:
722 case Intrinsic::spv_isfinite:
723 case Intrinsic::spv_isnormal:
724 case Intrinsic::spv_lerp:
725 case Intrinsic::spv_length:
726 case Intrinsic::spv_normalize:
727 case Intrinsic::spv_num_subgroups:
728 case Intrinsic::spv_num_workgroups:
729 case Intrinsic::spv_ptrcast:
730 case Intrinsic::spv_radians:
731 case Intrinsic::spv_reflect:
732 case Intrinsic::spv_refract:
733 case Intrinsic::spv_resource_getbasepointer:
734 case Intrinsic::spv_resource_getpointer:
735 case Intrinsic::spv_resource_handlefrombinding:
736 case Intrinsic::spv_resource_handlefromimplicitbinding:
737 case Intrinsic::spv_resource_nonuniformindex:
738 case Intrinsic::spv_resource_sample:
739 case Intrinsic::spv_rsqrt:
740 case Intrinsic::spv_saturate:
741 case Intrinsic::spv_sdot:
742 case Intrinsic::spv_sign:
743 case Intrinsic::spv_smoothstep:
744 case Intrinsic::spv_subgroup_id:
745 case Intrinsic::spv_subgroup_local_invocation_id:
746 case Intrinsic::spv_subgroup_max_size:
747 case Intrinsic::spv_subgroup_size:
748 case Intrinsic::spv_thread_id:
749 case Intrinsic::spv_thread_id_in_group:
750 case Intrinsic::spv_udot:
751 case Intrinsic::spv_undef:
752 case Intrinsic::spv_value_md:
753 case Intrinsic::spv_workgroup_size:
754 return false;
755 default:
756 return true;
757 }
758}
759
760// TODO(168736): We should make this either a flag in tabelgen
761// or reduce our dependence on the global registry, so we can remove this
762// function. It can easily be missed when new intrinsics are added.
763static bool isOpcodeWithNoSideEffects(unsigned Opcode) {
764 switch (Opcode) {
765 case SPIRV::OpTypeVoid:
766 case SPIRV::OpTypeBool:
767 case SPIRV::OpTypeInt:
768 case SPIRV::OpTypeFloat:
769 case SPIRV::OpTypeVector:
770 case SPIRV::OpTypeVectorIdEXT:
771 case SPIRV::OpTypeMatrix:
772 case SPIRV::OpTypeImage:
773 case SPIRV::OpTypeSampler:
774 case SPIRV::OpTypeSampledImage:
775 case SPIRV::OpTypeArray:
776 case SPIRV::OpTypeRuntimeArray:
777 case SPIRV::OpTypeStruct:
778 case SPIRV::OpTypeOpaque:
779 case SPIRV::OpTypePointer:
780 case SPIRV::OpTypeFunction:
781 case SPIRV::OpTypeEvent:
782 case SPIRV::OpTypeDeviceEvent:
783 case SPIRV::OpTypeReserveId:
784 case SPIRV::OpTypeQueue:
785 case SPIRV::OpTypePipe:
786 case SPIRV::OpTypeForwardPointer:
787 case SPIRV::OpTypePipeStorage:
788 case SPIRV::OpTypeNamedBarrier:
789 case SPIRV::OpTypeAccelerationStructureNV:
790 case SPIRV::OpTypeCooperativeMatrixNV:
791 case SPIRV::OpTypeCooperativeMatrixKHR:
792 return true;
793 default:
794 return false;
795 }
796}
797
798bool isDead(const MachineInstr &MI, const MachineRegisterInfo &MRI) {
799 // If there are no definitions, then assume there is some other
800 // side-effect that makes this instruction live.
801 if (MI.getNumDefs() == 0)
802 return false;
803
804 for (const auto &MO : MI.all_defs()) {
805 Register Reg = MO.getReg();
806 if (Reg.isPhysical()) {
807 LLVM_DEBUG(dbgs() << "Not dead: def of physical register " << Reg);
808 return false;
809 }
810 for (const auto &UseMI : MRI.use_nodbg_instructions(Reg)) {
811 if (UseMI.getOpcode() != SPIRV::OpName) {
812 LLVM_DEBUG(dbgs() << "Not dead: def " << MO << " has use in " << UseMI);
813 return false;
814 }
815 }
816 }
817
818 if (MI.getOpcode() == TargetOpcode::LOCAL_ESCAPE || MI.isFakeUse() ||
819 MI.isLifetimeMarker()) {
820 LLVM_DEBUG(
821 dbgs()
822 << "Not dead: Opcode is LOCAL_ESCAPE, fake use, or lifetime marker.\n");
823 return false;
824 }
825 if (MI.isPHI()) {
826 LLVM_DEBUG(dbgs() << "Dead: Phi instruction with no uses.\n");
827 return true;
828 }
829
830 // It is possible that the only side effect is that the instruction is
831 // referenced in the global registry. If that is the only side effect, the
832 // intrinsic is dead.
833 if (MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS ||
834 MI.getOpcode() == TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS) {
835 const auto &Intr = cast<GIntrinsic>(Val: MI);
836 if (!intrinsicHasSideEffects(ID: Intr.getIntrinsicID())) {
837 LLVM_DEBUG(dbgs() << "Dead: Intrinsic with no real side effects.\n");
838 return true;
839 }
840 }
841
842 if (MI.mayStore() || MI.isCall() ||
843 (MI.mayLoad() && MI.hasOrderedMemoryRef()) || MI.isPosition() ||
844 MI.isDebugInstr() || MI.isTerminator() || MI.isJumpTableDebugInfo()) {
845 LLVM_DEBUG(dbgs() << "Not dead: instruction has side effects.\n");
846 return false;
847 }
848
849 if (isPreISelGenericOpcode(Opcode: MI.getOpcode())) {
850 // TODO: Is there a generic way to check if the opcode has side effects?
851 LLVM_DEBUG(dbgs() << "Dead: Generic opcode with no uses.\n");
852 return true;
853 }
854
855 if (isOpcodeWithNoSideEffects(Opcode: MI.getOpcode())) {
856 LLVM_DEBUG(dbgs() << "Dead: known opcode with no side effects\n");
857 return true;
858 }
859
860 return false;
861}
862
863void SPIRVInstructionSelector::removeOpNamesForDeadMI(MachineInstr &MI) const {
864 // Delete the OpName that uses the result if there is one.
865 for (const auto &MO : MI.all_defs()) {
866 Register Reg = MO.getReg();
867 if (Reg.isPhysical())
868 continue;
869 SmallVector<MachineInstr *, 4> UselessOpNames;
870 for (MachineInstr &UseMI : MRI->use_nodbg_instructions(Reg)) {
871 assert(UseMI.getOpcode() == SPIRV::OpName &&
872 "There is still a use of the dead function.");
873 UselessOpNames.push_back(Elt: &UseMI);
874 }
875 for (MachineInstr *OpNameMI : UselessOpNames) {
876 GR.invalidateMachineInstr(MI: OpNameMI);
877 OpNameMI->eraseFromParent();
878 }
879 }
880}
881
882void SPIRVInstructionSelector::removeDeadInstruction(MachineInstr &MI) const {
883 salvageDebugInfo(MRI: *MRI, MI);
884 GR.invalidateMachineInstr(MI: &MI);
885 removeOpNamesForDeadMI(MI);
886 MI.eraseFromParent();
887}
888
889bool SPIRVInstructionSelector::select(MachineInstr &I) {
890 resetVRegsType(MF&: *I.getParent()->getParent());
891
892 assert(I.getParent() && "Instruction should be in a basic block!");
893 assert(I.getParent()->getParent() && "Instruction should be in a function!");
894
895 LLVM_DEBUG(dbgs() << "Checking if instruction is dead: " << I;);
896 if (isDead(MI: I, MRI: *MRI)) {
897 LLVM_DEBUG(dbgs() << "Instruction is dead.\n");
898 removeDeadInstruction(MI&: I);
899 return true;
900 }
901
902 Register Opcode = I.getOpcode();
903 // If it's not a GMIR instruction, we've selected it already.
904 if (!isPreISelGenericOpcode(Opcode)) {
905 if (Opcode == SPIRV::ASSIGN_TYPE) { // These pseudos aren't needed any more.
906 Register DstReg = I.getOperand(i: 0).getReg();
907 Register SrcReg = I.getOperand(i: 1).getReg();
908 auto *Def = MRI->getVRegDef(Reg: SrcReg);
909 if (isTypeFoldingSupported(Opcode: Def->getOpcode()) &&
910 Def->getOpcode() != TargetOpcode::G_CONSTANT &&
911 Def->getOpcode() != TargetOpcode::G_FCONSTANT) {
912 if (Def->getOpcode() == TargetOpcode::G_SELECT) {
913 Register SelectDstReg = Def->getOperand(i: 0).getReg();
914 bool SuccessToSelectSelect [[maybe_unused]] = selectSelect(
915 ResVReg: SelectDstReg, ResType: GR.getSPIRVTypeForVReg(VReg: SelectDstReg), I&: *Def);
916 assert(SuccessToSelectSelect);
917 GR.invalidateMachineInstr(MI: Def);
918 Def->eraseFromParent();
919 MRI->replaceRegWith(FromReg: DstReg, ToReg: SelectDstReg);
920 GR.invalidateMachineInstr(MI: &I);
921 I.eraseFromParent();
922 return true;
923 }
924
925 bool Res = selectImpl(I, CoverageInfo&: *CoverageInfo);
926 LLVM_DEBUG({
927 if (!Res && Def->getOpcode() != TargetOpcode::G_CONSTANT) {
928 dbgs() << "Unexpected pattern in ASSIGN_TYPE.\nInstruction: ";
929 I.print(dbgs());
930 }
931 });
932 assert(Res || Def->getOpcode() == TargetOpcode::G_CONSTANT);
933 if (Res) {
934 if (!isTriviallyDead(MI: *Def, MRI: *MRI) && isDead(MI: *Def, MRI: *MRI))
935 DeadMIs.insert(Ptr: Def);
936 return Res;
937 }
938 }
939 MRI->setRegClass(Reg: SrcReg, RC: MRI->getRegClass(Reg: DstReg));
940 MRI->replaceRegWith(FromReg: SrcReg, ToReg: DstReg);
941 GR.invalidateMachineInstr(MI: &I);
942 I.eraseFromParent();
943 return true;
944 } else if (I.getNumDefs() == 1) {
945 // Make all vregs 64 bits (for SPIR-V IDs).
946 MRI->setType(VReg: I.getOperand(i: 0).getReg(), Ty: LLT::scalar(SizeInBits: 64));
947 }
948 constrainSelectedInstRegOperands(I, TII, TRI, RBI);
949 return true;
950 }
951
952 if (DeadMIs.contains(Ptr: &I)) {
953 // if the instruction has been already made dead by folding it away
954 // erase it
955 LLVM_DEBUG(dbgs() << "Instruction is folded and dead.\n");
956 removeDeadInstruction(MI&: I);
957 DeadMIs.erase(Ptr: &I);
958 return true;
959 }
960
961 if (I.getNumOperands() != I.getNumExplicitOperands()) {
962 LLVM_DEBUG(errs() << "Generic instr has unexpected implicit operands\n");
963 return false;
964 }
965
966 // Common code for getting return reg+type, and removing selected instr
967 // from parent occurs here. Instr-specific selection happens in spvSelect().
968 bool HasDefs = I.getNumDefs() > 0;
969 Register ResVReg = HasDefs ? I.getOperand(i: 0).getReg() : Register(0);
970 SPIRVTypeInst ResType = HasDefs ? GR.getSPIRVTypeForVReg(VReg: ResVReg) : nullptr;
971 assert(!HasDefs || ResType || I.getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
972 I.getOpcode() == TargetOpcode::G_IMPLICIT_DEF);
973 if (spvSelect(ResVReg, ResType, I)) {
974 if (HasDefs) // Make all vregs 64 bits (for SPIR-V IDs).
975 for (unsigned i = 0; i < I.getNumDefs(); ++i)
976 MRI->setType(VReg: I.getOperand(i).getReg(), Ty: LLT::scalar(SizeInBits: 64));
977 GR.invalidateMachineInstr(MI: &I);
978 I.eraseFromParent();
979 return true;
980 }
981 return false;
982}
983
984static bool mayApplyGenericSelection(unsigned Opcode) {
985 switch (Opcode) {
986 case TargetOpcode::G_CONSTANT:
987 case TargetOpcode::G_FCONSTANT:
988 return false;
989 }
990 return isTypeFoldingSupported(Opcode);
991}
992
993bool SPIRVInstructionSelector::BuildCOPY(Register DestReg, Register SrcReg,
994 MachineInstr &I) const {
995 const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Reg: DestReg);
996 const TargetRegisterClass *SrcRC = MRI->getRegClassOrNull(Reg: SrcReg);
997 if (DstRC != SrcRC && SrcRC)
998 MRI->setRegClass(Reg: DestReg, RC: SrcRC);
999 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: TargetOpcode::COPY))
1000 .addDef(RegNo: DestReg)
1001 .addUse(RegNo: SrcReg)
1002 .constrainAllUses(TII, TRI, RBI);
1003 return true;
1004}
1005
1006bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
1007 SPIRVTypeInst ResType,
1008 MachineInstr &I) const {
1009 const unsigned Opcode = I.getOpcode();
1010 if (mayApplyGenericSelection(Opcode))
1011 return selectImpl(I, CoverageInfo&: *CoverageInfo);
1012 switch (Opcode) {
1013 case TargetOpcode::G_CONSTANT:
1014 case TargetOpcode::G_FCONSTANT:
1015 return selectConst(ResVReg, ResType, I);
1016 case TargetOpcode::G_GLOBAL_VALUE:
1017 return selectGlobalValue(ResVReg, I);
1018 case TargetOpcode::G_IMPLICIT_DEF:
1019 return selectOpUndef(ResVReg, ResType, I);
1020 case TargetOpcode::G_FREEZE:
1021 return selectFreeze(ResVReg, ResType, I);
1022
1023 case TargetOpcode::G_INTRINSIC:
1024 case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
1025 case TargetOpcode::G_INTRINSIC_CONVERGENT:
1026 case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS:
1027 return selectIntrinsic(ResVReg, ResType, I);
1028 case TargetOpcode::G_BITREVERSE:
1029 return selectBitreverse(ResVReg, ResType, I);
1030
1031 case TargetOpcode::G_BUILD_VECTOR:
1032 return selectBuildVector(ResVReg, ResType, I);
1033 case TargetOpcode::G_SPLAT_VECTOR:
1034 return selectSplatVector(ResVReg, ResType, I);
1035 case TargetOpcode::G_CONCAT_VECTORS:
1036 return selectConcatVectors(ResVReg, ResType, I);
1037
1038 case TargetOpcode::G_SHUFFLE_VECTOR: {
1039 MachineBasicBlock &BB = *I.getParent();
1040 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
1041 .addDef(RegNo: ResVReg)
1042 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1043 .addUse(RegNo: I.getOperand(i: 1).getReg())
1044 .addUse(RegNo: I.getOperand(i: 2).getReg());
1045 for (auto V : I.getOperand(i: 3).getShuffleMask())
1046 MIB.addImm(Val: V);
1047 MIB.constrainAllUses(TII, TRI, RBI);
1048 return true;
1049 }
1050 case TargetOpcode::G_MEMMOVE:
1051 case TargetOpcode::G_MEMCPY:
1052 case TargetOpcode::G_MEMCPY_INLINE:
1053 case TargetOpcode::G_MEMSET:
1054 case TargetOpcode::G_MEMSET_INLINE:
1055 return selectMemOperation(ResVReg, I);
1056
1057 case TargetOpcode::G_ICMP:
1058 return selectICmp(ResVReg, ResType, I);
1059 case TargetOpcode::G_FCMP:
1060 return selectFCmp(ResVReg, ResType, I);
1061
1062 case TargetOpcode::G_FRAME_INDEX:
1063 return selectFrameIndex(ResVReg, ResType, I);
1064
1065 case TargetOpcode::G_LOAD:
1066 return selectLoad(ResVReg, ResType, I);
1067 case TargetOpcode::G_STORE:
1068 return selectStore(I);
1069
1070 case TargetOpcode::G_BR:
1071 return selectBranch(I);
1072 case TargetOpcode::G_BRCOND:
1073 return selectBranchCond(I);
1074
1075 case TargetOpcode::G_PHI:
1076 return selectPhi(ResVReg, I);
1077
1078 case TargetOpcode::G_FPTOSI:
1079 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpConvertFToS);
1080 case TargetOpcode::G_FPTOUI:
1081 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpConvertFToU);
1082
1083 case TargetOpcode::G_FPTOSI_SAT:
1084 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpConvertFToS);
1085 case TargetOpcode::G_FPTOUI_SAT:
1086 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpConvertFToU);
1087
1088 case TargetOpcode::G_SITOFP:
1089 return selectIToF(ResVReg, ResType, I, IsSigned: true, Opcode: SPIRV::OpConvertSToF);
1090 case TargetOpcode::G_UITOFP:
1091 return selectIToF(ResVReg, ResType, I, IsSigned: false, Opcode: SPIRV::OpConvertUToF);
1092
1093 case TargetOpcode::G_CTPOP:
1094 return selectPopCount(ResVReg, ResType, I, Opcode: SPIRV::OpBitCount);
1095 case TargetOpcode::G_SMIN:
1096 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_min, GLInst: GL::SMin);
1097 case TargetOpcode::G_UMIN:
1098 return selectExtInst(ResVReg, ResType, I, CLInst: CL::u_min, GLInst: GL::UMin);
1099
1100 case TargetOpcode::G_SMAX:
1101 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_max, GLInst: GL::SMax);
1102 case TargetOpcode::G_UMAX:
1103 return selectExtInst(ResVReg, ResType, I, CLInst: CL::u_max, GLInst: GL::UMax);
1104
1105 case TargetOpcode::G_SCMP:
1106 return selectSUCmp(ResVReg, ResType, I, IsSigned: true);
1107 case TargetOpcode::G_UCMP:
1108 return selectSUCmp(ResVReg, ResType, I, IsSigned: false);
1109 case TargetOpcode::G_LROUND:
1110 case TargetOpcode::G_LLROUND: {
1111 Register regForLround =
1112 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ResVReg), Name: "lround");
1113 MRI->setRegClass(Reg: regForLround, RC: &SPIRV::iIDRegClass);
1114 GR.assignSPIRVTypeToVReg(Type: GR.getSPIRVTypeForVReg(VReg: I.getOperand(i: 1).getReg()),
1115 VReg: regForLround, MF: *(I.getParent()->getParent()));
1116 selectExtInst(ResVReg: regForLround, ResType: GR.getSPIRVTypeForVReg(VReg: regForLround), I,
1117 CLInst: CL::round, GLInst: GL::Round, /* setMIFlags */ false);
1118 MachineBasicBlock &BB = *I.getParent();
1119 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConvertFToS))
1120 .addDef(RegNo: ResVReg)
1121 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1122 .addUse(RegNo: regForLround);
1123 MIB.constrainAllUses(TII, TRI, RBI);
1124 return true;
1125 }
1126 case TargetOpcode::G_STRICT_FMA:
1127 case TargetOpcode::G_FMA: {
1128 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_fma)) {
1129 MachineBasicBlock &BB = *I.getParent();
1130 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFmaKHR))
1131 .addDef(RegNo: ResVReg)
1132 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1133 .addUse(RegNo: I.getOperand(i: 1).getReg())
1134 .addUse(RegNo: I.getOperand(i: 2).getReg())
1135 .addUse(RegNo: I.getOperand(i: 3).getReg())
1136 .setMIFlags(I.getFlags());
1137 MIB.constrainAllUses(TII, TRI, RBI);
1138 return true;
1139 }
1140 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fma, GLInst: GL::Fma);
1141 }
1142
1143 case TargetOpcode::G_FLDEXP:
1144 case TargetOpcode::G_STRICT_FLDEXP:
1145 return selectLdexp(ResVReg, ResType, I);
1146
1147 case TargetOpcode::G_FPOW:
1148 return selectExtInst(ResVReg, ResType, I, CLInst: CL::pow, GLInst: GL::Pow);
1149 case TargetOpcode::G_FPOWI:
1150 return selectFpowi(ResVReg, ResType, I);
1151
1152 case TargetOpcode::G_FEXP:
1153 return selectExtInst(ResVReg, ResType, I, CLInst: CL::exp, GLInst: GL::Exp);
1154 case TargetOpcode::G_FEXP2:
1155 return selectExtInst(ResVReg, ResType, I, CLInst: CL::exp2, GLInst: GL::Exp2);
1156 case TargetOpcode::G_FEXP10:
1157 return selectExp10(ResVReg, ResType, I);
1158
1159 case TargetOpcode::G_FMODF:
1160 return selectModf(ResVReg, ResType, I);
1161 case TargetOpcode::G_FSINCOS:
1162 return selectSincos(ResVReg, ResType, I);
1163
1164 case TargetOpcode::G_FLOG:
1165 return selectExtInst(ResVReg, ResType, I, CLInst: CL::log, GLInst: GL::Log);
1166 case TargetOpcode::G_FLOG2:
1167 return selectExtInst(ResVReg, ResType, I, CLInst: CL::log2, GLInst: GL::Log2);
1168 case TargetOpcode::G_FLOG10:
1169 return selectLog10(ResVReg, ResType, I);
1170
1171 case TargetOpcode::G_FABS:
1172 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fabs, GLInst: GL::FAbs);
1173 case TargetOpcode::G_ABS:
1174 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_abs, GLInst: GL::SAbs);
1175
1176 case TargetOpcode::G_FMINNUM:
1177 case TargetOpcode::G_FMINIMUM:
1178 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fmin, GLInst: GL::NMin);
1179 case TargetOpcode::G_FMAXNUM:
1180 case TargetOpcode::G_FMAXIMUM:
1181 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fmax, GLInst: GL::NMax);
1182
1183 case TargetOpcode::G_FCOPYSIGN:
1184 return selectCopySign(ResVReg, ResType, I);
1185
1186 case TargetOpcode::G_FCEIL:
1187 return selectExtInst(ResVReg, ResType, I, CLInst: CL::ceil, GLInst: GL::Ceil);
1188 case TargetOpcode::G_FFLOOR:
1189 return selectExtInst(ResVReg, ResType, I, CLInst: CL::floor, GLInst: GL::Floor);
1190
1191 case TargetOpcode::G_FCOS:
1192 return selectExtInst(ResVReg, ResType, I, CLInst: CL::cos, GLInst: GL::Cos);
1193 case TargetOpcode::G_FSIN:
1194 return selectExtInst(ResVReg, ResType, I, CLInst: CL::sin, GLInst: GL::Sin);
1195 case TargetOpcode::G_FTAN:
1196 return selectExtInst(ResVReg, ResType, I, CLInst: CL::tan, GLInst: GL::Tan);
1197 case TargetOpcode::G_FACOS:
1198 return selectExtInst(ResVReg, ResType, I, CLInst: CL::acos, GLInst: GL::Acos);
1199 case TargetOpcode::G_FASIN:
1200 return selectExtInst(ResVReg, ResType, I, CLInst: CL::asin, GLInst: GL::Asin);
1201 case TargetOpcode::G_FATAN:
1202 return selectExtInst(ResVReg, ResType, I, CLInst: CL::atan, GLInst: GL::Atan);
1203 case TargetOpcode::G_FATAN2:
1204 return selectExtInst(ResVReg, ResType, I, CLInst: CL::atan2, GLInst: GL::Atan2);
1205 case TargetOpcode::G_FCOSH:
1206 return selectExtInst(ResVReg, ResType, I, CLInst: CL::cosh, GLInst: GL::Cosh);
1207 case TargetOpcode::G_FSINH:
1208 return selectExtInst(ResVReg, ResType, I, CLInst: CL::sinh, GLInst: GL::Sinh);
1209 case TargetOpcode::G_FTANH:
1210 return selectExtInst(ResVReg, ResType, I, CLInst: CL::tanh, GLInst: GL::Tanh);
1211
1212 case TargetOpcode::G_STRICT_FSQRT:
1213 case TargetOpcode::G_FSQRT:
1214 return selectExtInst(ResVReg, ResType, I, CLInst: CL::sqrt, GLInst: GL::Sqrt);
1215
1216 case TargetOpcode::G_CTTZ:
1217 case TargetOpcode::G_CTTZ_ZERO_POISON:
1218 return selectExtInst(ResVReg, ResType, I, CLInst: CL::ctz);
1219 case TargetOpcode::G_CTLZ:
1220 case TargetOpcode::G_CTLZ_ZERO_POISON:
1221 return selectExtInst(ResVReg, ResType, I, CLInst: CL::clz);
1222
1223 case TargetOpcode::G_INTRINSIC_ROUND:
1224 return selectExtInst(ResVReg, ResType, I, CLInst: CL::round, GLInst: GL::Round);
1225 case TargetOpcode::G_INTRINSIC_ROUNDEVEN:
1226 return selectExtInst(ResVReg, ResType, I, CLInst: CL::rint, GLInst: GL::RoundEven);
1227 case TargetOpcode::G_INTRINSIC_TRUNC:
1228 return selectExtInst(ResVReg, ResType, I, CLInst: CL::trunc, GLInst: GL::Trunc);
1229 case TargetOpcode::G_FRINT:
1230 case TargetOpcode::G_FNEARBYINT:
1231 return selectExtInst(ResVReg, ResType, I, CLInst: CL::rint, GLInst: GL::RoundEven);
1232
1233 case TargetOpcode::G_SMULH:
1234 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_mul_hi);
1235 case TargetOpcode::G_UMULH:
1236 return selectExtInst(ResVReg, ResType, I, CLInst: CL::u_mul_hi);
1237
1238 case TargetOpcode::G_SADDSAT:
1239 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_add_sat);
1240 case TargetOpcode::G_UADDSAT:
1241 return selectExtInst(ResVReg, ResType, I, CLInst: CL::u_add_sat);
1242 case TargetOpcode::G_SSUBSAT:
1243 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_sub_sat);
1244 case TargetOpcode::G_USUBSAT:
1245 return selectExtInst(ResVReg, ResType, I, CLInst: CL::u_sub_sat);
1246
1247 case TargetOpcode::G_FFREXP:
1248 return selectFrexp(ResVReg, ResType, I);
1249
1250 case TargetOpcode::G_UADDO:
1251 return selectOverflowArith(ResVReg, ResType, I,
1252 Opcode: isVectorType(SPVTy: ResType) ? SPIRV::OpIAddCarryV
1253 : SPIRV::OpIAddCarryS);
1254 case TargetOpcode::G_USUBO:
1255 return selectOverflowArith(ResVReg, ResType, I,
1256 Opcode: isVectorType(SPVTy: ResType) ? SPIRV::OpISubBorrowV
1257 : SPIRV::OpISubBorrowS);
1258 case TargetOpcode::G_UMULO:
1259 return selectOverflowArith(ResVReg, ResType, I, Opcode: SPIRV::OpUMulExtended);
1260 case TargetOpcode::G_SMULO:
1261 return selectOverflowArith(ResVReg, ResType, I, Opcode: SPIRV::OpSMulExtended);
1262
1263 case TargetOpcode::G_SEXT:
1264 return selectExt(ResVReg, ResType, I, IsSigned: true);
1265 case TargetOpcode::G_ANYEXT:
1266 case TargetOpcode::G_ZEXT:
1267 return selectExt(ResVReg, ResType, I, IsSigned: false);
1268 case TargetOpcode::G_TRUNC:
1269 return selectTrunc(ResVReg, ResType, I);
1270 case TargetOpcode::G_FPTRUNC:
1271 case TargetOpcode::G_FPEXT:
1272 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpFConvert);
1273
1274 case TargetOpcode::G_PTRTOINT:
1275 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpConvertPtrToU);
1276 case TargetOpcode::G_INTTOPTR:
1277 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpConvertUToPtr);
1278 case TargetOpcode::G_BITCAST:
1279 return selectBitcast(ResVReg, ResType, I);
1280 case TargetOpcode::G_ADDRSPACE_CAST:
1281 return selectAddrSpaceCast(ResVReg, ResType, I);
1282 case TargetOpcode::G_PTRMASK:
1283 return selectPtrMask(ResVReg, ResType, I);
1284 case TargetOpcode::G_PTR_ADD: {
1285 // Currently, we get G_PTR_ADD only applied to global variables.
1286 assert(I.getOperand(1).isReg() && I.getOperand(2).isReg());
1287 Register GV = I.getOperand(i: 1).getReg();
1288 MachineRegisterInfo::def_instr_iterator II = MRI->def_instr_begin(RegNo: GV);
1289 (void)II;
1290 assert(((*II).getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
1291 (*II).getOpcode() == TargetOpcode::COPY ||
1292 (*II).getOpcode() == SPIRV::OpVariable ||
1293 (*II).getOpcode() == SPIRV::OpUntypedVariableKHR) &&
1294 getImm(I.getOperand(2), MRI));
1295 // It may be the initialization of a global variable.
1296 bool IsGVInit = false;
1297 for (MachineRegisterInfo::use_instr_iterator
1298 UseIt = MRI->use_instr_begin(RegNo: I.getOperand(i: 0).getReg()),
1299 UseEnd = MRI->use_instr_end();
1300 UseIt != UseEnd; UseIt = std::next(x: UseIt)) {
1301 if ((*UseIt).getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
1302 (*UseIt).getOpcode() == SPIRV::OpSpecConstantOp ||
1303 (*UseIt).getOpcode() == SPIRV::OpVariable ||
1304 (*UseIt).getOpcode() == SPIRV::OpUntypedVariableKHR) {
1305 IsGVInit = true;
1306 break;
1307 }
1308 }
1309 MachineBasicBlock &BB = *I.getParent();
1310 // An untyped result needs OpUntypedInBoundsPtrAccessChainKHR, which spells
1311 // out the Base Type. The offset of G_PTR_ADD is a byte count, so the Base
1312 // Type is i8 and the offset is the Element index as is. The opcode is
1313 // picked by the result type alone because an untyped access chain accepts
1314 // a typed base while a typed access chain rejects an untyped result.
1315 SPIRVTypeInst GVType = GR.getSPIRVTypeForVReg(VReg: GV);
1316 const bool UseUntypedPointers =
1317 ResType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
1318 if (UseUntypedPointers) {
1319 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
1320 .addDef(RegNo: ResVReg)
1321 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1322 .addImm(Val: static_cast<uint32_t>(
1323 SPIRV::Opcode::UntypedInBoundsPtrAccessChainKHR))
1324 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: GR.getOrCreateSPIRVIntegerType(BitWidth: 8, I, TII)))
1325 .addUse(RegNo: GV)
1326 .addUse(RegNo: I.getOperand(i: 2).getReg())
1327 .constrainAllUses(TII, TRI, RBI);
1328 return true;
1329 }
1330 if (!IsGVInit) {
1331 SPIRVTypeInst GVPointeeType = GR.getPointeeType(PtrType: GVType);
1332 SPIRVTypeInst ResPointeeType = GR.getPointeeType(PtrType: ResType);
1333 if (GVPointeeType && ResPointeeType && GVPointeeType != ResPointeeType) {
1334 // Build a new virtual register that is associated with the required
1335 // data type.
1336 Register NewVReg = MRI->createGenericVirtualRegister(Ty: MRI->getType(Reg: GV));
1337 MRI->setRegClass(Reg: NewVReg, RC: MRI->getRegClass(Reg: GV));
1338 // Having a correctly typed base we are ready to build the actually
1339 // required GEP. It may not be a constant though, because all Operands
1340 // of OpSpecConstantOp is to originate from other const instructions,
1341 // and only the AccessChain named opcodes accept a global OpVariable
1342 // instruction. We can't use an AccessChain opcode because of the type
1343 // mismatch between result and base types.
1344 if (!GR.isBitcastCompatible(Type1: ResType, Type2: GVType))
1345 return diagnoseUnsupported(
1346 I, Msg: "incompatible result and operand types in a bitcast");
1347 Register ResTypeReg = GR.getSPIRVTypeID(SpirvType: ResType);
1348 MachineInstrBuilder MIB =
1349 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBitcast))
1350 .addDef(RegNo: NewVReg)
1351 .addUse(RegNo: ResTypeReg)
1352 .addUse(RegNo: GV);
1353 MIB.constrainAllUses(TII, TRI, RBI);
1354 BuildMI(BB, I, MIMD: I.getDebugLoc(),
1355 MCID: TII.get(Opcode: STI.isLogicalSPIRV() ? SPIRV::OpInBoundsAccessChain
1356 : SPIRV::OpInBoundsPtrAccessChain))
1357 .addDef(RegNo: ResVReg)
1358 .addUse(RegNo: ResTypeReg)
1359 .addUse(RegNo: NewVReg)
1360 .addUse(RegNo: I.getOperand(i: 2).getReg())
1361 .constrainAllUses(TII, TRI, RBI);
1362 } else {
1363 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
1364 .addDef(RegNo: ResVReg)
1365 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1366 .addImm(
1367 Val: static_cast<uint32_t>(SPIRV::Opcode::InBoundsPtrAccessChain))
1368 .addUse(RegNo: GV)
1369 .addUse(RegNo: I.getOperand(i: 2).getReg())
1370 .constrainAllUses(TII, TRI, RBI);
1371 }
1372 return true;
1373 }
1374 // It's possible to translate G_PTR_ADD to OpSpecConstantOp: either to
1375 // initialize a global variable with a constant expression (e.g., the test
1376 // case opencl/basic/progvar_prog_scope_init.ll), or for another use case
1377 Register Idx = buildZerosVal(ResType: GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII), I);
1378 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
1379 .addDef(RegNo: ResVReg)
1380 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1381 .addImm(Val: static_cast<uint32_t>(
1382 SPIRV::Opcode::InBoundsPtrAccessChain))
1383 .addUse(RegNo: GV)
1384 .addUse(RegNo: Idx)
1385 .addUse(RegNo: I.getOperand(i: 2).getReg());
1386 MIB.constrainAllUses(TII, TRI, RBI);
1387 return true;
1388 }
1389
1390 case TargetOpcode::G_ATOMICRMW_OR:
1391 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicOr);
1392 case TargetOpcode::G_ATOMICRMW_ADD:
1393 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicIAdd);
1394 case TargetOpcode::G_ATOMICRMW_AND:
1395 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicAnd);
1396 case TargetOpcode::G_ATOMICRMW_MAX:
1397 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicSMax);
1398 case TargetOpcode::G_ATOMICRMW_MIN:
1399 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicSMin);
1400 case TargetOpcode::G_ATOMICRMW_SUB:
1401 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicISub);
1402 case TargetOpcode::G_ATOMICRMW_XOR:
1403 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicXor);
1404 case TargetOpcode::G_ATOMICRMW_UMAX:
1405 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicUMax);
1406 case TargetOpcode::G_ATOMICRMW_UMIN:
1407 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicUMin);
1408 case TargetOpcode::G_ATOMICRMW_XCHG:
1409 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicExchange);
1410
1411 case TargetOpcode::G_ATOMICRMW_FADD:
1412 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicFAddEXT);
1413 case TargetOpcode::G_ATOMICRMW_FSUB:
1414 // Translate G_ATOMICRMW_FSUB to OpAtomicFAddEXT with negative value operand
1415 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicFAddEXT,
1416 NegateOpcode: isVectorType(SPVTy: ResType) ? SPIRV::OpFNegateV
1417 : SPIRV::OpFNegate);
1418 case TargetOpcode::G_ATOMICRMW_FMIN:
1419 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicFMinEXT);
1420 case TargetOpcode::G_ATOMICRMW_FMAX:
1421 return selectAtomicRMW(ResVReg, ResType, I, NewOpcode: SPIRV::OpAtomicFMaxEXT);
1422
1423 case TargetOpcode::G_FENCE:
1424 return selectFence(I);
1425
1426 case TargetOpcode::G_STACKSAVE:
1427 return selectStackSave(ResVReg, ResType, I);
1428 case TargetOpcode::G_STACKRESTORE:
1429 return selectStackRestore(I);
1430
1431 case TargetOpcode::G_UNMERGE_VALUES:
1432 return selectUnmergeValues(I);
1433
1434 case TargetOpcode::G_TRAP:
1435 case TargetOpcode::G_UBSANTRAP:
1436 return selectTrap(I);
1437
1438 // Discard gen opcodes for intrinsics which we do not expect to actually
1439 // represent code after lowering or intrinsics which are not implemented but
1440 // should not crash when found in a customer's LLVM IR input.
1441 case TargetOpcode::DBG_LABEL:
1442 return true;
1443 case TargetOpcode::G_DEBUGTRAP:
1444 return selectDebugTrap(ResVReg, ResType, I);
1445 case TargetOpcode::G_PREFETCH:
1446 return selectPrefetch(I);
1447
1448 default:
1449 return false;
1450 }
1451}
1452
1453bool SPIRVInstructionSelector::selectDebugTrap(Register ResVReg,
1454 SPIRVTypeInst ResType,
1455 MachineInstr &I) const {
1456 unsigned Opcode = SPIRV::OpNop;
1457 MachineBasicBlock &BB = *I.getParent();
1458 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
1459 .constrainAllUses(TII, TRI, RBI);
1460 return true;
1461}
1462
1463bool SPIRVInstructionSelector::selectPrefetch(MachineInstr &I) const {
1464 // llvm.prefetch is missing info about how many bytes to prefetch that
1465 // OpUntypedPrefetchKHR instruction requires, so just drop the instruction
1466 // in case untyped pointers are enabled since OCL prefetch cannot take
1467 // untyped pointers.
1468 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_untyped_pointers))
1469 return true;
1470
1471 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
1472 MachineIRBuilder MIRBuilder(I);
1473 const unsigned PointerSize = GR.getPointerSize();
1474 const SPIRVTypeInst PointerSizeType =
1475 GR.getOrCreateSPIRVIntegerType(BitWidth: PointerSize, MIRBuilder);
1476 bool ZeroAsNull = !STI.isShader();
1477
1478 // Assume that a single element needs prefetch.
1479 Register ConstIntOne =
1480 GR.getOrCreateConstInt(Val: 1, I, SpvType: PointerSizeType, TII, ZeroAsNull);
1481
1482 Register AddrVal = I.getOperand(i: 0).getReg();
1483 Register ExtReg = MRI->createVirtualRegister(
1484 RegClass: GR.getRegClass(SpvType: GR.getOpTypeVoid(MIRBuilder)));
1485 return selectExtInst(ResVReg: ExtReg, ResType: GR.getOpTypeVoid(MIRBuilder), I, CLInst: CL::prefetch,
1486 /*setMIFlags=*/false, /*useMSSrc=*/useMISrc: false,
1487 SrcRegs: {AddrVal, ConstIntOne});
1488 }
1489 return true;
1490}
1491
1492bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
1493 SPIRVTypeInst ResType,
1494 MachineInstr &I,
1495 GL::GLSLExtInst GLInst,
1496 bool setMIFlags, bool useMISrc,
1497 ArrayRef<Register> SrcRegs) const {
1498 if (!STI.canUseExtInstSet(
1499 E: SPIRV::InstructionSet::InstructionSet::GLSL_std_450))
1500 return diagnoseUnsupported(
1501 I,
1502 Msg: "this instruction is only supported with the GLSL extended instruction "
1503 "set.");
1504 return selectExtInst(ResVReg, ResType, I,
1505 ExtInsts: {{SPIRV::InstructionSet::GLSL_std_450, GLInst}},
1506 setMIFlags, useMISrc, SrcRegs);
1507}
1508
1509bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
1510 SPIRVTypeInst ResType,
1511 MachineInstr &I,
1512 CL::OpenCLExtInst CLInst,
1513 bool setMIFlags, bool useMISrc,
1514 ArrayRef<Register> SrcRegs) const {
1515 return selectExtInst(ResVReg, ResType, I,
1516 ExtInsts: {{SPIRV::InstructionSet::OpenCL_std, CLInst}},
1517 setMIFlags, useMISrc, SrcRegs);
1518}
1519
1520bool SPIRVInstructionSelector::selectExtInst(
1521 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
1522 CL::OpenCLExtInst CLInst, GL::GLSLExtInst GLInst, bool setMIFlags,
1523 bool useMISrc, ArrayRef<Register> SrcRegs) const {
1524 ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst},
1525 {SPIRV::InstructionSet::GLSL_std_450, GLInst}};
1526 return selectExtInst(ResVReg, ResType, I, ExtInsts, setMIFlags, useMISrc,
1527 SrcRegs);
1528}
1529
1530bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
1531 SPIRVTypeInst ResType,
1532 MachineInstr &I,
1533 const ExtInstList &Insts,
1534 bool setMIFlags, bool useMISrc,
1535 ArrayRef<Register> SrcRegs) const {
1536
1537 for (const auto &[InstructionSet, Opcode] : Insts) {
1538 if (!STI.canUseExtInstSet(E: InstructionSet))
1539 continue;
1540 MachineBasicBlock &BB = *I.getParent();
1541 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
1542 .addDef(RegNo: ResVReg)
1543 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1544 .addImm(Val: static_cast<uint32_t>(InstructionSet))
1545 .addImm(Val: Opcode);
1546 if (setMIFlags)
1547 MIB.setMIFlags(I.getFlags());
1548 if (useMISrc) {
1549 const unsigned NumOps = I.getNumOperands();
1550 unsigned Index = 1;
1551 if (Index < NumOps &&
1552 I.getOperand(i: Index).getType() ==
1553 MachineOperand::MachineOperandType::MO_IntrinsicID)
1554 Index = 2;
1555 for (; Index < NumOps; ++Index)
1556 MIB.add(MO: I.getOperand(i: Index));
1557 } else {
1558 for (Register SReg : SrcRegs) {
1559 MIB.addUse(RegNo: SReg);
1560 }
1561 }
1562 MIB.constrainAllUses(TII, TRI, RBI);
1563 return true;
1564 }
1565 return false;
1566}
1567
1568bool SPIRVInstructionSelector::selectCopySign(Register ResVReg,
1569 SPIRVTypeInst ResType,
1570 MachineInstr &I) const {
1571 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std))
1572 return selectExtInst(ResVReg, ResType, I, CLInst: CL::copysign);
1573
1574 // There is no copysign instruction in the GLSL Extended Instruction set, so
1575 // it is implemented with bit manipulation:
1576 // bitcast((bitcast(magnitude) & ~signBit) | (bitcast(sign) & signBit))
1577 Register MagnitudeReg = I.getOperand(i: 1).getReg();
1578 Register SignReg = I.getOperand(i: 2).getReg();
1579
1580 const unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
1581 const unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: ResType);
1582 SPIRVTypeInst IntType = GR.getOrCreateSPIRVIntegerType(BitWidth, I, TII);
1583 const APInt SignMaskVal = APInt::getSignMask(BitWidth);
1584
1585 Register SignMask, NotSignMask;
1586 unsigned AndOpcode, OrOpcode;
1587 if (ComponentCount > 1) {
1588 IntType = GR.getOrCreateSPIRVVectorType(BaseType: IntType, NumElements: ComponentCount, I, TII);
1589 SignMask = GR.getOrCreateConstVector(Val: SignMaskVal, I, SpvType: IntType, TII);
1590 NotSignMask = GR.getOrCreateConstVector(Val: ~SignMaskVal, I, SpvType: IntType, TII);
1591 AndOpcode = SPIRV::OpBitwiseAndV;
1592 OrOpcode = SPIRV::OpBitwiseOrV;
1593 } else {
1594 SignMask = GR.getOrCreateConstInt(Val: SignMaskVal, I, SpvType: IntType, TII);
1595 NotSignMask = GR.getOrCreateConstInt(Val: ~SignMaskVal, I, SpvType: IntType, TII);
1596 AndOpcode = SPIRV::OpBitwiseAndS;
1597 OrOpcode = SPIRV::OpBitwiseOrS;
1598 }
1599
1600 auto EmitBitOp = [&](Register &ResReg, ArrayRef<Register> SrcRegs,
1601 unsigned Opcode) {
1602 ResReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: IntType));
1603 return selectOpWithSrcs(ResVReg: ResReg, ResType: IntType, I, SrcRegs, Opcode);
1604 };
1605
1606 Register MagnitudeInt, SignInt, MagnitudeBits, SignBits, CombinedInt;
1607 if (!EmitBitOp(MagnitudeInt, {MagnitudeReg}, SPIRV::OpBitcast) ||
1608 !EmitBitOp(SignInt, {SignReg}, SPIRV::OpBitcast) ||
1609 !EmitBitOp(MagnitudeBits, {MagnitudeInt, NotSignMask}, AndOpcode) ||
1610 !EmitBitOp(SignBits, {SignInt, SignMask}, AndOpcode) ||
1611 !EmitBitOp(CombinedInt, {MagnitudeBits, SignBits}, OrOpcode))
1612 return false;
1613
1614 return selectOpWithSrcs(ResVReg, ResType, I, SrcRegs: {CombinedInt}, Opcode: SPIRV::OpBitcast);
1615}
1616
1617bool SPIRVInstructionSelector::selectFrexp(Register ResVReg,
1618 SPIRVTypeInst ResType,
1619 MachineInstr &I) const {
1620 ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CL::frexp},
1621 {SPIRV::InstructionSet::GLSL_std_450, GL::Frexp}};
1622 for (const auto &Ex : ExtInsts) {
1623 SPIRV::InstructionSet::InstructionSet Set = Ex.first;
1624 uint32_t Opcode = Ex.second;
1625 if (!STI.canUseExtInstSet(E: Set))
1626 continue;
1627
1628 MachineIRBuilder MIRBuilder(I);
1629 SPIRVTypeInst PointeeTy = GR.getSPIRVTypeForVReg(VReg: I.getOperand(i: 1).getReg());
1630 const SPIRVTypeInst PointerType = GR.getOrCreateSPIRVPointerType(
1631 BaseType: PointeeTy, MIRBuilder, SC: SPIRV::StorageClass::Function);
1632 Register PointerVReg =
1633 createVirtualRegister(SpvType: PointerType, GR: &GR, MRI, MF: MRI->getMF());
1634
1635 auto It = getOpVariableMBBIt(MF&: *I.getMF());
1636 // An untyped pointer result type is only legal on OpUntypedVariableKHR,
1637 // but not on OpVariable.
1638 const bool IsUntyped =
1639 PointerType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
1640 auto VarMIB =
1641 BuildMI(BB&: *It->getParent(), I: It, MIMD: It->getDebugLoc(),
1642 MCID: TII.get(Opcode: IsUntyped ? SPIRV::OpUntypedVariableKHR
1643 : SPIRV::OpVariable))
1644 .addDef(RegNo: PointerVReg)
1645 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PointerType))
1646 .addImm(Val: static_cast<uint32_t>(SPIRV::StorageClass::Function));
1647 if (IsUntyped)
1648 VarMIB.addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PointeeTy)); // Data Type
1649 VarMIB.constrainAllUses(TII, TRI, RBI);
1650
1651 SPIRVTypeInst MantissaTy = GR.getSPIRVTypeForVReg(VReg: I.getOperand(i: 2).getReg());
1652 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
1653 .addDef(RegNo: ResVReg)
1654 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MantissaTy))
1655 .addImm(Val: static_cast<uint32_t>(Ex.first))
1656 .addImm(Val: Opcode)
1657 .add(MO: I.getOperand(i: 2))
1658 .addUse(RegNo: PointerVReg)
1659 .constrainAllUses(TII, TRI, RBI);
1660
1661 Register ExpResReg = I.getOperand(i: 1).getReg();
1662 if (!MRI->use_nodbg_empty(RegNo: ExpResReg))
1663 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
1664 .addDef(RegNo: ExpResReg)
1665 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PointeeTy))
1666 .addUse(RegNo: PointerVReg)
1667 .constrainAllUses(TII, TRI, RBI);
1668 return true;
1669 }
1670 return false;
1671}
1672
1673bool SPIRVInstructionSelector::selectLdexp(Register ResVReg,
1674 SPIRVTypeInst ResType,
1675 MachineInstr &I) const {
1676 Register XReg = I.getOperand(i: 1).getReg();
1677 Register ExpReg = I.getOperand(i: 2).getReg();
1678
1679 // Both OpenCL.std ldexp and GLSL.std.450 Ldexp require the exponent to have
1680 // the same number of components as the result, but G_FLDEXP's exponent is
1681 // scalar even for vector operands. Splat it when the result is a vector.
1682 SPIRVTypeInst ExpType = GR.getSPIRVTypeForVReg(VReg: ExpReg);
1683 if (ResType->getOpcode() == SPIRV::OpTypeVector &&
1684 ExpType->getOpcode() != SPIRV::OpTypeVector) {
1685 unsigned NumElts = ResType->getOperand(i: 2).getImm();
1686 SPIRVTypeInst ExpVecType =
1687 GR.getOrCreateSPIRVVectorType(BaseType: ExpType, NumElements: NumElts, I, TII);
1688 Register SplatReg =
1689 createVirtualRegister(SpvType: ExpVecType, GR: &GR, MRI, MF: MRI->getMF());
1690 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
1691 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
1692 .addDef(RegNo: SplatReg)
1693 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ExpVecType));
1694 for (unsigned J = 0; J < NumElts; ++J)
1695 MIB.addUse(RegNo: ExpReg);
1696 MIB.constrainAllUses(TII, TRI, RBI);
1697 ExpReg = SplatReg;
1698 }
1699
1700 return selectExtInst(ResVReg, ResType, I, CLInst: CL::ldexp, GLInst: GL::Ldexp,
1701 /*setMIFlags=*/true, /*useMISrc=*/false, SrcRegs: {XReg, ExpReg});
1702}
1703
1704bool SPIRVInstructionSelector::selectSincos(Register ResVReg,
1705 SPIRVTypeInst ResType,
1706 MachineInstr &I) const {
1707 Register CosResVReg = I.getOperand(i: 1).getReg();
1708 unsigned SrcIdx = I.getNumExplicitDefs();
1709 Register ResTypeReg = GR.getSPIRVTypeID(SpirvType: ResType);
1710
1711 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
1712 // OpenCL.std sincos(x, cosval*) -> returns sin(x), writes cos(x) to ptr.
1713 MachineIRBuilder MIRBuilder(I);
1714 const SPIRVTypeInst PointerType = GR.getOrCreateSPIRVPointerType(
1715 BaseType: ResType, MIRBuilder, SC: SPIRV::StorageClass::Function);
1716 Register PointerVReg =
1717 createVirtualRegister(SpvType: PointerType, GR: &GR, MRI, MF: MRI->getMF());
1718
1719 auto It = getOpVariableMBBIt(MF&: *I.getMF());
1720 // An untyped pointer result type is only legal on OpUntypedVariableKHR,
1721 // but not on OpVariable.
1722 const bool IsUntyped =
1723 PointerType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
1724 auto VarMIB =
1725 BuildMI(BB&: *It->getParent(), I: It, MIMD: It->getDebugLoc(),
1726 MCID: TII.get(Opcode: IsUntyped ? SPIRV::OpUntypedVariableKHR
1727 : SPIRV::OpVariable))
1728 .addDef(RegNo: PointerVReg)
1729 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PointerType))
1730 .addImm(Val: static_cast<uint32_t>(SPIRV::StorageClass::Function));
1731 if (IsUntyped)
1732 VarMIB.addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType)); // Data Type
1733 VarMIB.constrainAllUses(TII, TRI, RBI);
1734 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
1735 .addDef(RegNo: ResVReg)
1736 .addUse(RegNo: ResTypeReg)
1737 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1738 .addImm(Val: CL::sincos)
1739 .add(MO: I.getOperand(i: SrcIdx))
1740 .addUse(RegNo: PointerVReg)
1741 .constrainAllUses(TII, TRI, RBI);
1742 if (!MRI->use_nodbg_empty(RegNo: CosResVReg))
1743 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
1744 .addDef(RegNo: CosResVReg)
1745 .addUse(RegNo: ResTypeReg)
1746 .addUse(RegNo: PointerVReg)
1747 .constrainAllUses(TII, TRI, RBI);
1748 return true;
1749 } else if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::GLSL_std_450)) {
1750 // GLSL.std.450 has no combined sincos; emit separate Sin and Cos.
1751 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
1752 .addDef(RegNo: ResVReg)
1753 .addUse(RegNo: ResTypeReg)
1754 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
1755 .addImm(Val: GL::Sin)
1756 .add(MO: I.getOperand(i: SrcIdx))
1757 .constrainAllUses(TII, TRI, RBI);
1758 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
1759 .addDef(RegNo: CosResVReg)
1760 .addUse(RegNo: ResTypeReg)
1761 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
1762 .addImm(Val: GL::Cos)
1763 .add(MO: I.getOperand(i: SrcIdx))
1764 .constrainAllUses(TII, TRI, RBI);
1765 return true;
1766 }
1767 return false;
1768}
1769
1770bool SPIRVInstructionSelector::selectOpWithSrcs(Register ResVReg,
1771 SPIRVTypeInst ResType,
1772 MachineInstr &I,
1773 ArrayRef<Register> Srcs,
1774 unsigned Opcode) const {
1775 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
1776 .addDef(RegNo: ResVReg)
1777 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
1778 for (Register SReg : Srcs) {
1779 MIB.addUse(RegNo: SReg);
1780 }
1781 MIB.constrainAllUses(TII, TRI, RBI);
1782 return true;
1783}
1784
1785bool SPIRVInstructionSelector::selectPopCount16(Register ResVReg,
1786 SPIRVTypeInst ResType,
1787 MachineInstr &I,
1788 unsigned ExtOpcode,
1789 unsigned Opcode) const {
1790 MachineIRBuilder MIRBuilder(I);
1791
1792 Register OpReg = I.getOperand(i: 1).getReg();
1793 SPIRVTypeInst SrcType = GR.getSPIRVTypeForVReg(VReg: OpReg);
1794 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: SrcType);
1795 bool IsScalar = !isVectorType(SPVTy: SrcType);
1796 SPIRVTypeInst I32Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, MIRBuilder);
1797
1798 SPIRVTypeInst ExtType =
1799 IsScalar ? I32Type
1800 : GR.getOrCreateSPIRVVectorType(BaseType: I32Type, NumElements: ComponentCount,
1801 MIRBuilder, /*IsSigned=*/EmitIR: false);
1802
1803 Register ExtReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ExtType));
1804 // Always use OpUConvert to always use a 0 extend
1805 if (!selectOpWithSrcs(ResVReg: ExtReg, ResType: ExtType, I, Srcs: {OpReg}, Opcode: SPIRV::OpUConvert))
1806 return false;
1807
1808 Register PopCountReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ExtType));
1809 if (!selectPopCount32(ResVReg: PopCountReg, ResType: ExtType, I, SrcReg: ExtReg, Opcode))
1810 return false;
1811
1812 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {PopCountReg}, Opcode: ExtOpcode);
1813}
1814
1815bool SPIRVInstructionSelector::selectPopCount32(Register ResVReg,
1816 SPIRVTypeInst ResType,
1817 MachineInstr &I,
1818 Register SrcReg,
1819 unsigned Opcode) const {
1820 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {SrcReg}, Opcode);
1821}
1822
1823bool SPIRVInstructionSelector::selectPopCount64(Register ResVReg,
1824 SPIRVTypeInst ResType,
1825 MachineInstr &I,
1826 Register SrcReg,
1827 unsigned Opcode) const {
1828 MachineIRBuilder MIRBuilder(I);
1829
1830 SPIRVTypeInst SrcType = GR.getSPIRVTypeForVReg(VReg: SrcReg);
1831 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: SrcType);
1832 bool IsScalar = !isVectorType(SPVTy: SrcType);
1833 SPIRVTypeInst I32Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, MIRBuilder);
1834
1835 // We need to work with a type matching the shape of the input but using 32
1836 // bit int instead of 64.
1837 SPIRVTypeInst WorkingType =
1838 IsScalar ? I32Type
1839 : GR.getOrCreateSPIRVVectorType(BaseType: I32Type, NumElements: ComponentCount,
1840 MIRBuilder, /*IsSigned=*/EmitIR: false);
1841
1842 // Truncate and count the low bits.
1843 Register Trunc = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: WorkingType));
1844 if (!selectOpWithSrcs(ResVReg: Trunc, ResType: WorkingType, I, Srcs: {SrcReg}, Opcode: SPIRV::OpUConvert))
1845 return false;
1846
1847 Register LowCount = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: WorkingType));
1848 if (!selectOpWithSrcs(ResVReg: LowCount, ResType: WorkingType, I, Srcs: {Trunc}, Opcode: SPIRV::OpBitCount))
1849 return false;
1850
1851 // Shift the high bits over and count them too.
1852 Register ShiftAmount = IsScalar
1853 ? GR.getOrCreateConstInt(Val: 32, I, SpvType: SrcType, TII)
1854 : GR.getOrCreateConstVector(Val: 32, I, SpvType: SrcType, TII);
1855 unsigned ShiftOp =
1856 IsScalar ? SPIRV::OpShiftRightLogicalS : SPIRV::OpShiftRightLogicalV;
1857 Register Shift = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: SrcType));
1858 if (!selectOpWithSrcs(ResVReg: Shift, ResType: SrcType, I, Srcs: {SrcReg, ShiftAmount}, Opcode: ShiftOp))
1859 return false;
1860
1861 Trunc = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: WorkingType));
1862 if (!selectOpWithSrcs(ResVReg: Trunc, ResType: WorkingType, I, Srcs: {Shift}, Opcode: SPIRV::OpUConvert))
1863 return false;
1864
1865 Register HighCount = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: WorkingType));
1866 if (!selectOpWithSrcs(ResVReg: HighCount, ResType: WorkingType, I, Srcs: {Trunc}, Opcode: SPIRV::OpBitCount))
1867 return false;
1868
1869 // Add them up and zext or sext back to 64 bit values.
1870 Register Sum = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: WorkingType));
1871 if (!selectOpWithSrcs(ResVReg: Sum, ResType: WorkingType, I, Srcs: {HighCount, LowCount},
1872 Opcode: IsScalar ? SPIRV::OpIAddS : SPIRV::OpIAddV))
1873 return false;
1874
1875 bool IsSigned = GR.isScalarOrVectorSigned(Type: ResType);
1876 unsigned ConvOp = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
1877 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {Sum}, Opcode: ConvOp);
1878}
1879
1880bool SPIRVInstructionSelector::selectPopCount(Register ResVReg,
1881 SPIRVTypeInst ResType,
1882 MachineInstr &I,
1883 unsigned Opcode) const {
1884 // Vulkan restricts OpBitCount to 32-bit integers or vectors of 32-bit
1885 // integers unless VK_KHR_maintenance9 is enabled. Until VK_KHR_maintenance9
1886 // is core we will not generate OpBitCount with any other types when
1887 // targeting Vulkan.
1888 if (!STI.getTargetTriple().isVulkanOS())
1889 return selectUnOp(ResVReg, ResType, I, Opcode);
1890
1891 Register OpReg = I.getOperand(i: 1).getReg();
1892 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
1893 unsigned ExtOpcode = GR.isScalarOrVectorSigned(Type: ResType) ? SPIRV::OpSConvert
1894 : SPIRV::OpUConvert;
1895 switch (GR.getScalarOrVectorBitWidth(Type: OpType)) {
1896 case 8:
1897 case 16:
1898 return selectPopCount16(ResVReg, ResType, I, ExtOpcode, Opcode);
1899 case 32:
1900 return selectPopCount32(ResVReg, ResType, I, SrcReg: OpReg, Opcode);
1901 case 64:
1902 return selectPopCount64(ResVReg, ResType, I, SrcReg: OpReg, Opcode);
1903 default:
1904 return diagnoseUnsupported(I, Msg: "unsupported operand bit width for popcount");
1905 }
1906}
1907
1908bool SPIRVInstructionSelector::selectUnOp(Register ResVReg,
1909 SPIRVTypeInst ResType,
1910 MachineInstr &I,
1911 unsigned Opcode) const {
1912 if (STI.isPhysicalSPIRV() && I.getOperand(i: 1).isReg()) {
1913 Register SrcReg = I.getOperand(i: 1).getReg();
1914 bool IsGV = false;
1915 for (MachineRegisterInfo::def_instr_iterator DefIt =
1916 MRI->def_instr_begin(RegNo: SrcReg);
1917 DefIt != MRI->def_instr_end(); DefIt = std::next(x: DefIt)) {
1918 unsigned DefOpCode = DefIt->getOpcode();
1919 if (DefOpCode == SPIRV::ASSIGN_TYPE || DefOpCode == TargetOpcode::COPY) {
1920 // We need special handling to look through the type assignment or the
1921 // COPY pseudo-op and see if this is a constant or a global.
1922 if (auto *VRD = getVRegDef(MRI&: *MRI, Reg: DefIt->getOperand(i: 1).getReg()))
1923 DefOpCode = VRD->getOpcode();
1924 }
1925 if (DefOpCode == TargetOpcode::G_GLOBAL_VALUE ||
1926 DefOpCode == TargetOpcode::G_CONSTANT ||
1927 DefOpCode == SPIRV::OpVariable ||
1928 DefOpCode == SPIRV::OpUntypedVariableKHR ||
1929 DefOpCode == SPIRV::OpConstantI) {
1930 IsGV = true;
1931 break;
1932 }
1933 }
1934 if (IsGV) {
1935 uint32_t SpecOpcode = 0;
1936 switch (Opcode) {
1937 case SPIRV::OpConvertPtrToU:
1938 SpecOpcode = static_cast<uint32_t>(SPIRV::Opcode::ConvertPtrToU);
1939 break;
1940 case SPIRV::OpConvertUToPtr:
1941 SpecOpcode = static_cast<uint32_t>(SPIRV::Opcode::ConvertUToPtr);
1942 break;
1943 }
1944 if (SpecOpcode) {
1945 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
1946 MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
1947 .addDef(RegNo: ResVReg)
1948 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
1949 .addImm(Val: SpecOpcode)
1950 .addUse(RegNo: SrcReg)
1951 .constrainAllUses(TII, TRI, RBI);
1952 return true;
1953 }
1954 }
1955 }
1956 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {I.getOperand(i: 1).getReg()},
1957 Opcode);
1958}
1959
1960bool SPIRVInstructionSelector::selectBitcast(Register ResVReg,
1961 SPIRVTypeInst ResType,
1962 MachineInstr &I) const {
1963 Register OpReg = I.getOperand(i: 1).getReg();
1964 SPIRVTypeInst OpType =
1965 OpReg.isValid() ? GR.getSPIRVTypeForVReg(VReg: OpReg) : nullptr;
1966 if (!GR.isBitcastCompatible(Type1: ResType, Type2: OpType))
1967 return diagnoseUnsupported(
1968 I, Msg: "incompatible result and operand types in a bitcast");
1969 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpBitcast);
1970}
1971
1972static void addMemoryOperands(MachineMemOperand *MemOp,
1973 MachineInstrBuilder &MIB,
1974 MachineIRBuilder &MIRBuilder,
1975 SPIRVGlobalRegistry &GR) {
1976 const SPIRVSubtarget *ST =
1977 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
1978 uint32_t SpvMemOp = static_cast<uint32_t>(SPIRV::MemoryOperand::None);
1979 if (MemOp->isVolatile())
1980 SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Volatile);
1981 if (MemOp->isNonTemporal())
1982 SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Nontemporal);
1983 // Aligned memory operand requires the Kernel capability.
1984 if (!ST->isShader() && MemOp->getAlign().value())
1985 SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Aligned);
1986
1987 [[maybe_unused]] MachineInstr *AliasList = nullptr;
1988 [[maybe_unused]] MachineInstr *NoAliasList = nullptr;
1989 if (ST->canUseExtension(E: SPIRV::Extension::SPV_INTEL_memory_access_aliasing)) {
1990 if (auto *MD = MemOp->getAAInfo().Scope) {
1991 AliasList = GR.getOrAddMemAliasingINTELInst(MIRBuilder, AliasingListMD: MD);
1992 if (AliasList)
1993 SpvMemOp |=
1994 static_cast<uint32_t>(SPIRV::MemoryOperand::AliasScopeINTELMask);
1995 }
1996 if (auto *MD = MemOp->getAAInfo().NoAlias) {
1997 NoAliasList = GR.getOrAddMemAliasingINTELInst(MIRBuilder, AliasingListMD: MD);
1998 if (NoAliasList)
1999 SpvMemOp |=
2000 static_cast<uint32_t>(SPIRV::MemoryOperand::NoAliasINTELMask);
2001 }
2002 }
2003
2004 if (SpvMemOp != static_cast<uint32_t>(SPIRV::MemoryOperand::None)) {
2005 MIB.addImm(Val: SpvMemOp);
2006 if (SpvMemOp & static_cast<uint32_t>(SPIRV::MemoryOperand::Aligned))
2007 MIB.addImm(Val: MemOp->getAlign().value());
2008 if (AliasList)
2009 MIB.addUse(RegNo: AliasList->getOperand(i: 0).getReg());
2010 if (NoAliasList)
2011 MIB.addUse(RegNo: NoAliasList->getOperand(i: 0).getReg());
2012 }
2013}
2014
2015static void addMemoryOperands(uint64_t Flags, MachineInstrBuilder &MIB) {
2016 uint32_t SpvMemOp = static_cast<uint32_t>(SPIRV::MemoryOperand::None);
2017 if (Flags & MachineMemOperand::Flags::MOVolatile)
2018 SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Volatile);
2019 if (Flags & MachineMemOperand::Flags::MONonTemporal)
2020 SpvMemOp |= static_cast<uint32_t>(SPIRV::MemoryOperand::Nontemporal);
2021
2022 if (SpvMemOp != static_cast<uint32_t>(SPIRV::MemoryOperand::None))
2023 MIB.addImm(Val: SpvMemOp);
2024}
2025
2026bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
2027 SPIRVTypeInst ResType,
2028 MachineInstr &I) const {
2029 unsigned OpOffset = isa<GIntrinsic>(Val: I) ? 1 : 0;
2030 Register Ptr = I.getOperand(i: 1 + OpOffset).getReg();
2031
2032 auto *PtrDef = getVRegDef(MRI&: *MRI, Reg: Ptr);
2033 auto *IntPtrDef = dyn_cast<GIntrinsic>(Val: PtrDef);
2034 if (IntPtrDef &&
2035 (IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getbasepointer ||
2036 IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer)) {
2037
2038 Register HandleReg = IntPtrDef->getOperand(i: 2).getReg();
2039 SPIRVTypeInst HandleType = GR.getSPIRVTypeForVReg(VReg: HandleReg);
2040 if (HandleType->getOpcode() == SPIRV::OpTypeImage) {
2041 Register NewHandleReg =
2042 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: HandleReg));
2043 auto *HandleDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: HandleReg));
2044 if (!loadHandleBeforePosition(HandleReg&: NewHandleReg, ResType: HandleType, HandleDef&: *HandleDef, Pos&: I)) {
2045 return false;
2046 }
2047
2048 Register IdxReg = IntPtrDef->getOperand(i: 3).getReg();
2049 return generateImageReadOrFetch(ResVReg, ResType, ImageReg: NewHandleReg, IdxReg,
2050 Loc: I.getDebugLoc(), Pos&: I);
2051 }
2052 }
2053
2054 MachineIRBuilder MIRBuilder(I);
2055
2056 if (I.getNumMemOperands()) {
2057 const MachineMemOperand *MemOp = *I.memoperands_begin();
2058 if (MemOp->isAtomic())
2059 return selectAtomicLoad(ResVReg, ResType, I);
2060 }
2061
2062 auto MIB = MIRBuilder.buildInstr(Opcode: SPIRV::OpLoad)
2063 .addDef(RegNo: ResVReg)
2064 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2065 .addUse(RegNo: Ptr);
2066 if (!I.getNumMemOperands()) {
2067 assert(I.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS ||
2068 I.getOpcode() ==
2069 TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS);
2070 addMemoryOperands(Flags: I.getOperand(i: 2 + OpOffset).getImm(), MIB);
2071 } else {
2072 addMemoryOperands(MemOp: *I.memoperands_begin(), MIB, MIRBuilder, GR);
2073 }
2074 MIB.constrainAllUses(TII, TRI, RBI);
2075 return true;
2076}
2077
2078Register SPIRVInstructionSelector::createPtrSizedIntReg(
2079 MachineIRBuilder &MIRBuilder) const {
2080 SPIRVTypeInst IntType =
2081 GR.getOrCreateSPIRVIntegerType(BitWidth: GR.getPointerSize(), MIRBuilder);
2082 Register Reg =
2083 MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: GR.getPointerSize()));
2084 MRI->setRegClass(Reg, RC: GR.getRegClass(SpvType: IntType));
2085 GR.assignSPIRVTypeToVReg(Type: IntType, VReg: Reg, MF: MIRBuilder.getMF());
2086 return Reg;
2087}
2088
2089Register
2090SPIRVInstructionSelector::convertPtrToInt(Register PtrVal,
2091 MachineIRBuilder &MIRBuilder) const {
2092 SPIRVTypeInst IntType =
2093 GR.getOrCreateSPIRVIntegerType(BitWidth: GR.getPointerSize(), MIRBuilder);
2094 Register IntReg = createPtrSizedIntReg(MIRBuilder);
2095 MIRBuilder.buildInstr(Opcode: SPIRV::OpConvertPtrToU)
2096 .addDef(RegNo: IntReg)
2097 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: IntType)) // Result type
2098 .addUse(RegNo: PtrVal) // Pointer operand
2099 .constrainAllUses(TII, TRI, RBI);
2100 return IntReg;
2101}
2102
2103Register SPIRVInstructionSelector::castPtrToPtrToInt(
2104 Register Ptr, SPIRV::StorageClass::StorageClass SC,
2105 MachineIRBuilder &MIRBuilder) const {
2106 SPIRVTypeInst IntType =
2107 GR.getOrCreateSPIRVIntegerType(BitWidth: GR.getPointerSize(), MIRBuilder);
2108 SPIRVTypeInst PtrType =
2109 GR.getOrCreateSPIRVPointerType(BaseType: IntType, MIRBuilder, SC);
2110 Register CastedPtr =
2111 MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: GR.getPointerSize()));
2112 MRI->setRegClass(Reg: CastedPtr, RC: GR.getRegClass(SpvType: PtrType));
2113 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: CastedPtr, MF: MIRBuilder.getMF());
2114 MIRBuilder.buildInstr(Opcode: SPIRV::OpBitcast)
2115 .addDef(RegNo: CastedPtr)
2116 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrType))
2117 .addUse(RegNo: Ptr)
2118 .constrainAllUses(TII, TRI, RBI);
2119 return CastedPtr;
2120}
2121
2122bool SPIRVInstructionSelector::selectAtomicPtrValue(
2123 Register ResVReg, SPIRVTypeInst ResType, MachineIRBuilder &MIRBuilder,
2124 function_ref<Register(SPIRVTypeInst IntType)> EmitAtomic) const {
2125 // Pointer-typed atomics are lowered by bitcasting the Ptr operand to a
2126 // pointer to an integer of the same size as the pointer, so that the actual
2127 // atomic instruction operates on integers as required by the spec. Value
2128 // operands and results are converted with OpConvertPtrToU/OpConvertUToPtr.
2129 unsigned PtrSize = GR.getPointerSize();
2130 SPIRVTypeInst IntType = GR.getOrCreateSPIRVIntegerType(BitWidth: PtrSize, MIRBuilder);
2131
2132 Register IntResult = EmitAtomic(IntType);
2133 if (IntResult.isValid())
2134 MIRBuilder.buildInstr(Opcode: SPIRV::OpConvertUToPtr)
2135 .addDef(RegNo: ResVReg)
2136 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2137 .addUse(RegNo: IntResult)
2138 .constrainAllUses(TII, TRI, RBI);
2139 return true;
2140}
2141
2142bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
2143 SPIRVTypeInst ResType,
2144 MachineInstr &I) const {
2145 LLVMContext &Context = I.getMF()->getFunction().getContext();
2146
2147 unsigned OpOffset = isa<GIntrinsic>(Val: I) ? 1 : 0;
2148 Register Ptr = I.getOperand(i: 1 + OpOffset).getReg();
2149
2150 if (!ResType.isTypeIntOrFloat() && !ResType.isTypePtr())
2151 return diagnoseUnsupported(
2152 I, Msg: "Lowering to SPIR-V of atomic load is only "
2153 "allowed for integer, floating point or pointer types");
2154
2155 assert(I.getNumMemOperands());
2156 const MachineMemOperand &MemOp = **I.memoperands_begin();
2157 assert(MemOp.isAtomic());
2158
2159 uint32_t Scope = static_cast<uint32_t>(
2160 getMemScope(TT: STI.getTargetTriple(), Ctx&: Context, Id: MemOp.getSyncScopeID()));
2161 Register ScopeReg = buildI32Constant(Val: Scope, I);
2162
2163 AtomicOrdering AO = MemOp.getSuccessOrdering();
2164 uint32_t StorageClass = static_cast<uint32_t>(getMemSemanticsForStorageClass(
2165 SC: addressSpaceToStorageClass(AddrSpace: MemOp.getAddrSpace(), STI)));
2166 uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(Ord: AO));
2167 if (MemOp.isVolatile() && STI.getTargetTriple().isVulkanOS())
2168 MemSem |= static_cast<uint32_t>(SPIRV::MemorySemantics::Volatile);
2169 uint32_t Sem = getMemSemanticsWithStorageClass(TT: STI.getTargetTriple(), OrderSem: MemSem,
2170 StorageClassSem: StorageClass);
2171 Register MemSemReg = buildI32Constant(Val: Sem, I);
2172
2173 MachineIRBuilder MIRBuilder(I);
2174
2175 if (ResType.isTypePtr()) {
2176 if (!STI.isPhysicalSPIRV())
2177 return diagnoseUnsupported(
2178 I, Msg: "Lowering to SPIR-V of atomic load is only "
2179 "allowed for pointer types for physical addressing model");
2180 // If data to load is a pointer type we bitcast the Ptr parameter to pointer
2181 // to an integer type of the same size as the pointer size and then generate
2182 // OpAtomicLoad the return value of that OpAtomicLoad is an integer that is
2183 // converted back to a pointer type using OpConvertUToPtr.
2184 SPIRV::StorageClass::StorageClass SC =
2185 addressSpaceToStorageClass(AddrSpace: MemOp.getAddrSpace(), STI);
2186 return selectAtomicPtrValue(
2187 ResVReg, ResType, MIRBuilder, EmitAtomic: [&](SPIRVTypeInst IntType) {
2188 Register CastedPtr = castPtrToPtrToInt(Ptr, SC, MIRBuilder);
2189 Register IntResult = createPtrSizedIntReg(MIRBuilder);
2190 MIRBuilder.buildInstr(Opcode: SPIRV::OpAtomicLoad)
2191 .addDef(RegNo: IntResult)
2192 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: IntType))
2193 .addUse(RegNo: CastedPtr)
2194 .addUse(RegNo: ScopeReg)
2195 .addUse(RegNo: MemSemReg)
2196 .constrainAllUses(TII, TRI, RBI);
2197 return IntResult;
2198 });
2199 }
2200 auto AtomicLoad = MIRBuilder.buildInstr(Opcode: SPIRV::OpAtomicLoad)
2201 .addDef(RegNo: ResVReg)
2202 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2203 .addUse(RegNo: Ptr)
2204 .addUse(RegNo: ScopeReg)
2205 .addUse(RegNo: MemSemReg);
2206 AtomicLoad.constrainAllUses(TII, TRI, RBI);
2207
2208 return true;
2209}
2210
2211bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
2212 unsigned OpOffset = isa<GIntrinsic>(Val: I) ? 1 : 0;
2213 Register StoreVal = I.getOperand(i: 0 + OpOffset).getReg();
2214 Register Ptr = I.getOperand(i: 1 + OpOffset).getReg();
2215
2216 auto *PtrDef = getVRegDef(MRI&: *MRI, Reg: Ptr);
2217 auto *IntPtrDef = dyn_cast<GIntrinsic>(Val: PtrDef);
2218 if (IntPtrDef &&
2219 (IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getbasepointer ||
2220 IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer)) {
2221
2222 Register HandleReg = IntPtrDef->getOperand(i: 2).getReg();
2223 Register NewHandleReg =
2224 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: HandleReg));
2225 auto *HandleDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: HandleReg));
2226 SPIRVTypeInst HandleType = GR.getSPIRVTypeForVReg(VReg: HandleReg);
2227 if (!loadHandleBeforePosition(HandleReg&: NewHandleReg, ResType: HandleType, HandleDef&: *HandleDef, Pos&: I)) {
2228 return false;
2229 }
2230
2231 Register IdxReg = IntPtrDef->getOperand(i: 3).getReg();
2232 if (HandleType->getOpcode() == SPIRV::OpTypeImage) {
2233 SPIRVTypeInst SampledType =
2234 GR.getSPIRVTypeForVReg(VReg: HandleType->getOperand(i: 1).getReg());
2235 SPIRVTypeInst StoreValCompType =
2236 GR.getScalarOrVectorComponentType(Type: GR.getSPIRVTypeForVReg(VReg: StoreVal));
2237 if (StoreValCompType && StoreValCompType != SampledType) {
2238 // A wide element (e.g. int64_t2) is emulated with a narrower packed
2239 // image. This bitcasts the value to match the format.
2240 SPIRVTypeInst PackedType = widenTypeToVec4(Type: SampledType, I);
2241 Register PackedReg =
2242 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PackedType));
2243 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBitcast))
2244 .addDef(RegNo: PackedReg)
2245 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PackedType))
2246 .addUse(RegNo: StoreVal)
2247 .constrainAllUses(TII, TRI, RBI);
2248 StoreVal = PackedReg;
2249 }
2250
2251 auto BMI = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
2252 MCID: TII.get(Opcode: SPIRV::OpImageWrite))
2253 .addUse(RegNo: NewHandleReg)
2254 .addUse(RegNo: IdxReg)
2255 .addUse(RegNo: StoreVal);
2256
2257 const llvm::Type *LLVMHandleType = GR.getTypeForSPIRVType(Ty: HandleType);
2258 if (sampledTypeIsSignedInteger(HandleType: LLVMHandleType))
2259 BMI.addImm(Val: 0x1000); // SignExtend
2260
2261 BMI.constrainAllUses(TII, TRI, RBI);
2262 return true;
2263 }
2264 }
2265
2266 SPIRVTypeInst PointeeTy = GR.getPointeeType(PtrType: GR.getSPIRVTypeForVReg(VReg: Ptr));
2267 SPIRVTypeInst StoreTy = GR.getSPIRVTypeForVReg(VReg: StoreVal);
2268 if (PointeeTy && PointeeTy->getOpcode() == SPIRV::OpTypeVectorIdEXT &&
2269 StoreTy->getOpcode() != SPIRV::OpTypeVectorIdEXT &&
2270 GR.getScalarOrVectorComponentCount(Type: PointeeTy) == 1) {
2271 MachineInstr *StoreValDef = getVRegDef(MRI&: *MRI, Reg: StoreVal);
2272 Register Reg = StoreValDef->getOperand(i: 0).getReg();
2273 if (auto FC = GFConstant::getConstant(Const: Reg, MRI: *MRI))
2274 StoreVal =
2275 GR.getOrCreateConstVector(Val: FC->getScalarValue(), I, SpvType: PointeeTy, TII);
2276 else if (auto IC = GIConstant::getConstant(Const: Reg, MRI: *MRI))
2277 StoreVal =
2278 GR.getOrCreateConstVector(Val: IC->getScalarValue(), I, SpvType: PointeeTy, TII);
2279 else
2280 llvm_unreachable("Unexpected <1 x T> Store type!");
2281 }
2282
2283 if (I.getNumMemOperands()) {
2284 const MachineMemOperand *MemOp = *I.memoperands_begin();
2285 if (MemOp->isAtomic())
2286 return selectAtomicStore(I);
2287 }
2288
2289 // Stores into a read-only storage class produce invalid SPIR-V. Reject such
2290 // input with a diagnostic rather than silently emitting an OpStore that
2291 // validation rejects.
2292 SPIRV::StorageClass::StorageClass PtrSC = GR.getPointerStorageClass(VReg: Ptr);
2293 if (PtrSC == SPIRV::StorageClass::UniformConstant ||
2294 PtrSC == SPIRV::StorageClass::Input ||
2295 PtrSC == SPIRV::StorageClass::PushConstant)
2296 return diagnoseUnsupported(
2297 I, Msg: "store into a read-only SPIR-V storage class is not allowed");
2298
2299 MachineIRBuilder MIRBuilder(I);
2300 auto MIB = MIRBuilder.buildInstr(Opcode: SPIRV::OpStore).addUse(RegNo: Ptr).addUse(RegNo: StoreVal);
2301 if (!I.getNumMemOperands()) {
2302 assert(I.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS ||
2303 I.getOpcode() ==
2304 TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS);
2305 addMemoryOperands(Flags: I.getOperand(i: 2 + OpOffset).getImm(), MIB);
2306 } else {
2307 addMemoryOperands(MemOp: *I.memoperands_begin(), MIB, MIRBuilder, GR);
2308 }
2309 MIB.constrainAllUses(TII, TRI, RBI);
2310 return true;
2311}
2312
2313bool SPIRVInstructionSelector::selectAtomicStore(MachineInstr &I) const {
2314 LLVMContext &Context = I.getMF()->getFunction().getContext();
2315
2316 unsigned OpOffset = isa<GIntrinsic>(Val: I) ? 1 : 0;
2317 Register StoreVal = I.getOperand(i: 0 + OpOffset).getReg();
2318 Register Ptr = I.getOperand(i: 1 + OpOffset).getReg();
2319
2320 SPIRVTypeInst PtrType = GR.getSPIRVTypeForVReg(VReg: Ptr);
2321 SPIRVTypeInst PointeeType = GR.getPointeeType(PtrType);
2322 // For an untyped pointer the data type is the stored value's type.
2323 if (!PointeeType && PtrType &&
2324 PtrType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR)
2325 PointeeType = GR.getSPIRVTypeForVReg(VReg: StoreVal);
2326 if (!PointeeType)
2327 return diagnoseUnsupported(I,
2328 Msg: "Lowering to SPIR-V of atomic store is only "
2329 "allowed for integer or floating point types");
2330
2331 assert(I.getNumMemOperands());
2332 const MachineMemOperand &MemOp = **I.memoperands_begin();
2333 assert(MemOp.isAtomic());
2334
2335 uint32_t Scope = static_cast<uint32_t>(
2336 getMemScope(TT: STI.getTargetTriple(), Ctx&: Context, Id: MemOp.getSyncScopeID()));
2337 Register ScopeReg = buildI32Constant(Val: Scope, I);
2338
2339 AtomicOrdering AO = MemOp.getSuccessOrdering();
2340 uint32_t StorageClass = static_cast<uint32_t>(getMemSemanticsForStorageClass(
2341 SC: addressSpaceToStorageClass(AddrSpace: MemOp.getAddrSpace(), STI)));
2342 uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(Ord: AO));
2343 if (MemOp.isVolatile() && STI.getTargetTriple().isVulkanOS())
2344 MemSem |= static_cast<uint32_t>(SPIRV::MemorySemantics::Volatile);
2345 uint32_t Sem = getMemSemanticsWithStorageClass(TT: STI.getTargetTriple(), OrderSem: MemSem,
2346 StorageClassSem: StorageClass);
2347 Register MemSemReg = buildI32Constant(Val: Sem, I);
2348 MachineIRBuilder MIRBuilder(I);
2349
2350 if (PointeeType.isTypePtr()) {
2351 if (!STI.isPhysicalSPIRV())
2352 return diagnoseUnsupported(
2353 I, Msg: "Lowering to SPIR-V of atomic store is only "
2354 "allowed for pointer types for physical addressing model");
2355 // If data to store is a pointer type we cast it to an integer type of the
2356 // same size as the pointer size using OpConvertPtrToU, bitcast Ptr
2357 // parameter to pointer to integer type and then generate OpAtomicStore
2358 // with casted values as required by spec.
2359 SPIRV::StorageClass::StorageClass SC =
2360 addressSpaceToStorageClass(AddrSpace: MemOp.getAddrSpace(), STI);
2361 return selectAtomicPtrValue(
2362 ResVReg: Register(), ResType: SPIRVTypeInst(), MIRBuilder, EmitAtomic: [&](SPIRVTypeInst IntType) {
2363 Register ValueAsInt = convertPtrToInt(PtrVal: StoreVal, MIRBuilder);
2364 Register CastedPtr = castPtrToPtrToInt(Ptr, SC, MIRBuilder);
2365 MIRBuilder.buildInstr(Opcode: SPIRV::OpAtomicStore)
2366 .addUse(RegNo: CastedPtr)
2367 .addUse(RegNo: ScopeReg)
2368 .addUse(RegNo: MemSemReg)
2369 .addUse(RegNo: ValueAsInt)
2370 .constrainAllUses(TII, TRI, RBI);
2371 // Stores produce no result, so no OpConvertUToPtr is needed.
2372 return Register();
2373 });
2374 }
2375
2376 if (!PointeeType.isTypeIntOrFloat())
2377 return diagnoseUnsupported(I,
2378 Msg: "Lowering to SPIR-V of atomic store is only "
2379 "allowed for integer or floating point types");
2380
2381 auto AtomicStore = MIRBuilder.buildInstr(Opcode: SPIRV::OpAtomicStore)
2382 .addUse(RegNo: Ptr)
2383 .addUse(RegNo: ScopeReg)
2384 .addUse(RegNo: MemSemReg)
2385 .addUse(RegNo: StoreVal);
2386 AtomicStore.constrainAllUses(TII, TRI, RBI);
2387
2388 return true;
2389}
2390
2391bool SPIRVInstructionSelector::selectMaskedGather(Register ResVReg,
2392 SPIRVTypeInst ResType,
2393 MachineInstr &I) const {
2394 assert(I.getNumExplicitDefs() == 1 && "Expected single def for gather");
2395 // Operand indices:
2396 // 0: result (def)
2397 // 1: intrinsic ID
2398 // 2: vector of pointers
2399 // 3: alignment (i32 immediate)
2400 // 4: mask (vector of i1)
2401 // 5: passthru/fill value
2402 const Register PtrsReg = I.getOperand(i: 2).getReg();
2403 const uint32_t Alignment = I.getOperand(i: 3).getImm();
2404 const Register MaskReg = I.getOperand(i: 4).getReg();
2405 const Register PassthruReg = I.getOperand(i: 5).getReg();
2406 const Register AlignmentReg = buildI32Constant(Val: Alignment, I);
2407
2408 MachineBasicBlock &BB = *I.getParent();
2409 auto MIB =
2410 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpMaskedGatherINTEL))
2411 .addDef(RegNo: ResVReg)
2412 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2413 .addUse(RegNo: PtrsReg)
2414 .addUse(RegNo: AlignmentReg)
2415 .addUse(RegNo: MaskReg)
2416 .addUse(RegNo: PassthruReg);
2417 MIB.constrainAllUses(TII, TRI, RBI);
2418 return true;
2419}
2420
2421bool SPIRVInstructionSelector::selectMaskedScatter(MachineInstr &I) const {
2422 assert(I.getNumExplicitDefs() == 0 && "Expected no defs for scatter");
2423 // Operand indices (no explicit defs):
2424 // 0: intrinsic ID
2425 // 1: value vector
2426 // 2: vector of pointers
2427 // 3: alignment (i32 immediate)
2428 // 4: mask (vector of i1)
2429 const Register ValuesReg = I.getOperand(i: 1).getReg();
2430 const Register PtrsReg = I.getOperand(i: 2).getReg();
2431 const uint32_t Alignment = I.getOperand(i: 3).getImm();
2432 const Register MaskReg = I.getOperand(i: 4).getReg();
2433 const Register AlignmentReg = buildI32Constant(Val: Alignment, I);
2434 MachineBasicBlock &BB = *I.getParent();
2435
2436 auto MIB =
2437 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpMaskedScatterINTEL))
2438 .addUse(RegNo: PtrsReg)
2439 .addUse(RegNo: AlignmentReg)
2440 .addUse(RegNo: MaskReg)
2441 .addUse(RegNo: ValuesReg);
2442 MIB.constrainAllUses(TII, TRI, RBI);
2443 return true;
2444}
2445
2446bool SPIRVInstructionSelector::diagnoseUnsupported(const MachineInstr &I,
2447 const Twine &Msg) const {
2448 const Function &F = I.getMF()->getFunction();
2449 F.getContext().diagnose(
2450 DI: DiagnosticInfoUnsupported(F, Msg, I.getDebugLoc(), DS_Error));
2451 return false;
2452}
2453
2454bool SPIRVInstructionSelector::selectStackSave(Register ResVReg,
2455 SPIRVTypeInst ResType,
2456 MachineInstr &I) const {
2457 if (!STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_variable_length_array))
2458 return diagnoseUnsupported(
2459 I, Msg: "llvm.stacksave intrinsic: this instruction requires the following "
2460 "SPIR-V extension: SPV_INTEL_variable_length_array");
2461 MachineBasicBlock &BB = *I.getParent();
2462 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSaveMemoryINTEL))
2463 .addDef(RegNo: ResVReg)
2464 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2465 .constrainAllUses(TII, TRI, RBI);
2466 return true;
2467}
2468
2469bool SPIRVInstructionSelector::selectStackRestore(MachineInstr &I) const {
2470 if (!STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_variable_length_array))
2471 return diagnoseUnsupported(
2472 I,
2473 Msg: "llvm.stackrestore intrinsic: this instruction requires the following "
2474 "SPIR-V extension: SPV_INTEL_variable_length_array");
2475 if (!I.getOperand(i: 0).isReg())
2476 return false;
2477 MachineBasicBlock &BB = *I.getParent();
2478 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpRestoreMemoryINTEL))
2479 .addUse(RegNo: I.getOperand(i: 0).getReg())
2480 .constrainAllUses(TII, TRI, RBI);
2481 return true;
2482}
2483
2484Register
2485SPIRVInstructionSelector::getOrCreateMemSetGlobal(MachineInstr &I) const {
2486 MachineIRBuilder MIRBuilder(I);
2487 assert(I.getOperand(1).isReg() && I.getOperand(2).isReg());
2488
2489 // TODO: check if we have such GV, add init, use buildGlobalVariable.
2490 unsigned Num = getIConstVal(ConstReg: I.getOperand(i: 2).getReg(), MRI);
2491 Function &CurFunction = GR.CurMF->getFunction();
2492 Type *LLVMArrTy =
2493 ArrayType::get(ElementType: IntegerType::get(C&: CurFunction.getContext(), NumBits: 8), NumElements: Num);
2494 GlobalVariable *GV = new GlobalVariable(*CurFunction.getParent(), LLVMArrTy,
2495 true, GlobalValue::InternalLinkage,
2496 Constant::getNullValue(Ty: LLVMArrTy));
2497
2498 Type *ValTy = Type::getInt8Ty(C&: I.getMF()->getFunction().getContext());
2499 Type *ArrTy = ArrayType::get(ElementType: ValTy, NumElements: Num);
2500 SPIRVTypeInst VarTy = GR.getOrCreateSPIRVPointerType(
2501 BaseType: ArrTy, MIRBuilder, SC: SPIRV::StorageClass::UniformConstant);
2502
2503 SPIRVTypeInst SpvArrTy = GR.getOrCreateSPIRVType(
2504 Type: ArrTy, MIRBuilder, AQ: SPIRV::AccessQualifier::None, EmitIR: false);
2505
2506 unsigned Val = getIConstVal(ConstReg: I.getOperand(i: 1).getReg(), MRI);
2507 Register Const = GR.getOrCreateConstIntArray(Val, Num, I, SpvType: SpvArrTy, TII);
2508
2509 Register VarReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2510 // With SPV_KHR_untyped_pointers enabled, getOrCreateSPIRVPointerType returns
2511 // an untyped pointer type. An untyped pointer result type is only legal on
2512 // OpUntypedVariableKHR, not on OpVariable.
2513 // Pick the matching opcode/operands so the synthesized constant global is
2514 // valid SPIR-V.
2515 const bool IsUntyped = VarTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
2516 auto MIBVar = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
2517 MCID: TII.get(Opcode: IsUntyped ? SPIRV::OpUntypedVariableKHR
2518 : SPIRV::OpVariable))
2519 .addDef(RegNo: VarReg)
2520 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: VarTy))
2521 .addImm(Val: SPIRV::StorageClass::UniformConstant);
2522 if (IsUntyped)
2523 MIBVar.addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvArrTy)); // Data Type
2524 MIBVar.addUse(RegNo: Const); // Initializer
2525 MIBVar.constrainAllUses(TII, TRI, RBI);
2526
2527 GR.add(V: GV, MI: MIBVar);
2528 GR.addGlobalObject(V: GV, MF: GR.CurMF, R: VarReg);
2529
2530 buildOpDecorate(Reg: VarReg, I, TII, Dec: SPIRV::Decoration::Constant, DecArgs: {});
2531 return VarReg;
2532}
2533
2534bool SPIRVInstructionSelector::selectCopyMemory(MachineInstr &I,
2535 Register SrcReg) const {
2536 MachineBasicBlock &BB = *I.getParent();
2537 Register DstReg = I.getOperand(i: 0).getReg();
2538 SPIRVTypeInst DstTy = GR.getSPIRVTypeForVReg(VReg: DstReg);
2539 SPIRVTypeInst SrcTy = GR.getSPIRVTypeForVReg(VReg: SrcReg);
2540 if (GR.getPointeeType(PtrType: DstTy) != GR.getPointeeType(PtrType: SrcTy))
2541 return diagnoseUnsupported(
2542 I, Msg: "OpCopyMemory requires operands to have the same type");
2543 uint64_t CopySize = getIConstVal(ConstReg: I.getOperand(i: 2).getReg(), MRI);
2544 SPIRVTypeInst PointeeTy = GR.getPointeeType(PtrType: DstTy);
2545 const Type *LLVMPointeeTy = GR.getTypeForSPIRVType(Ty: PointeeTy);
2546 if (!LLVMPointeeTy)
2547 return diagnoseUnsupported(
2548 I, Msg: "Unable to determine pointee type size for OpCopyMemory");
2549 const DataLayout &DL = I.getMF()->getFunction().getDataLayout();
2550 if (CopySize != DL.getTypeStoreSize(Ty: const_cast<Type *>(LLVMPointeeTy)))
2551 return diagnoseUnsupported(
2552 I, Msg: "OpCopyMemory requires the size to match the pointee type size");
2553 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCopyMemory))
2554 .addUse(RegNo: DstReg)
2555 .addUse(RegNo: SrcReg);
2556 if (I.getNumMemOperands()) {
2557 MachineIRBuilder MIRBuilder(I);
2558 addMemoryOperands(MemOp: *I.memoperands_begin(), MIB, MIRBuilder, GR);
2559 }
2560 MIB.constrainAllUses(TII, TRI, RBI);
2561 return true;
2562}
2563
2564bool SPIRVInstructionSelector::selectCopyMemorySized(MachineInstr &I,
2565 Register SrcReg) const {
2566 MachineBasicBlock &BB = *I.getParent();
2567 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCopyMemorySized))
2568 .addUse(RegNo: I.getOperand(i: 0).getReg())
2569 .addUse(RegNo: SrcReg)
2570 .addUse(RegNo: I.getOperand(i: 2).getReg());
2571 if (I.getNumMemOperands()) {
2572 MachineIRBuilder MIRBuilder(I);
2573 addMemoryOperands(MemOp: *I.memoperands_begin(), MIB, MIRBuilder, GR);
2574 }
2575 MIB.constrainAllUses(TII, TRI, RBI);
2576 return true;
2577}
2578
2579bool SPIRVInstructionSelector::selectMemOperation(Register ResVReg,
2580 MachineInstr &I) const {
2581 // Zero-sized memcpy/memmove/memset are no-ops.
2582 Register SizeReg = I.getOperand(i: 2).getReg();
2583 if (MachineInstr *SizeDef = getDefInstrMaybeConstant(ConstReg&: SizeReg, MRI);
2584 SizeDef && SizeDef->getOpcode() == TargetOpcode::G_CONSTANT &&
2585 getIConstVal(ConstReg: SizeReg, MRI) == 0)
2586 return true;
2587
2588 Register SrcReg = I.getOperand(i: 1).getReg();
2589 if (I.getOpcode() == TargetOpcode::G_MEMSET ||
2590 I.getOpcode() == TargetOpcode::G_MEMSET_INLINE) {
2591 Register VarReg = getOrCreateMemSetGlobal(I);
2592 if (!VarReg.isValid())
2593 return false;
2594 Type *ValTy = Type::getInt8Ty(C&: I.getMF()->getFunction().getContext());
2595 SPIRVTypeInst SourceTy = GR.getOrCreateSPIRVPointerType(
2596 BaseType: ValTy, I, SC: SPIRV::StorageClass::UniformConstant);
2597 SrcReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2598 if (!selectOpWithSrcs(ResVReg: SrcReg, ResType: SourceTy, I, Srcs: {VarReg}, Opcode: SPIRV::OpBitcast))
2599 return false;
2600 }
2601 if (STI.isLogicalSPIRV()) {
2602 if (!selectCopyMemory(I, SrcReg))
2603 return false;
2604 } else {
2605 if (!selectCopyMemorySized(I, SrcReg))
2606 return false;
2607 }
2608 if (ResVReg.isValid() && ResVReg != I.getOperand(i: 0).getReg())
2609 if (!BuildCOPY(DestReg: ResVReg, SrcReg: I.getOperand(i: 0).getReg(), I))
2610 return false;
2611 return true;
2612}
2613
2614bool SPIRVInstructionSelector::selectAtomicRMW(Register ResVReg,
2615 SPIRVTypeInst ResType,
2616 MachineInstr &I,
2617 unsigned NewOpcode,
2618 unsigned NegateOpcode) const {
2619 assert(I.hasOneMemOperand());
2620 const MachineMemOperand *MemOp = *I.memoperands_begin();
2621 uint32_t Scope = static_cast<uint32_t>(
2622 getMemScope(TT: STI.getTargetTriple(), Ctx&: GR.CurMF->getFunction().getContext(),
2623 Id: MemOp->getSyncScopeID()));
2624 Register ScopeReg = buildI32Constant(Val: Scope, I);
2625
2626 Register Ptr = I.getOperand(i: 1).getReg();
2627 uint32_t ScSem = static_cast<uint32_t>(
2628 getMemSemanticsForStorageClass(SC: GR.getPointerStorageClass(VReg: Ptr)));
2629 AtomicOrdering AO = MemOp->getSuccessOrdering();
2630 uint32_t OrderSem = static_cast<uint32_t>(getMemSemantics(Ord: AO));
2631 Register MemSemReg = buildI32Constant(
2632 Val: getMemSemanticsWithStorageClass(TT: STI.getTargetTriple(), OrderSem, StorageClassSem: ScSem),
2633 I);
2634
2635 Register ValueReg = I.getOperand(i: 2).getReg();
2636 if (NegateOpcode != 0) {
2637 // Translation with negative value operand is requested
2638 Register TmpReg = createVirtualRegister(SpvType: ResType, GR: &GR, MRI, MF: MRI->getMF());
2639 if (!selectOpWithSrcs(ResVReg: TmpReg, ResType, I, Srcs: {ValueReg}, Opcode: NegateOpcode))
2640 return false;
2641 ValueReg = TmpReg;
2642 }
2643
2644 if (ResType.isTypePtr()) {
2645 if (NewOpcode != SPIRV::OpAtomicExchange)
2646 return diagnoseUnsupported(
2647 I, Msg: "Lowering to SPIR-V of this atomic operation is not "
2648 "allowed for pointer types");
2649 if (!STI.isPhysicalSPIRV())
2650 return diagnoseUnsupported(
2651 I, Msg: "Lowering to SPIR-V of atomic exchange is only "
2652 "allowed for pointer types for physical addressing model");
2653 // If the exchanged value is a pointer type we convert the value operand to
2654 // an integer type of the same size as the pointer size using
2655 // OpConvertPtrToU, bitcast the Ptr parameter to pointer to integer type and
2656 // then generate OpAtomicExchange on integers. The integer result is
2657 // converted back to a pointer type using OpConvertUToPtr, similar to atomic
2658 // load and store.
2659 MachineIRBuilder MIRBuilder(I);
2660 SPIRV::StorageClass::StorageClass SC = GR.getPointerStorageClass(VReg: Ptr);
2661 return selectAtomicPtrValue(
2662 ResVReg, ResType, MIRBuilder, EmitAtomic: [&](SPIRVTypeInst IntType) {
2663 Register ValueAsInt = convertPtrToInt(PtrVal: ValueReg, MIRBuilder);
2664 Register CastedPtr = castPtrToPtrToInt(Ptr, SC, MIRBuilder);
2665 Register ExchangeResReg = createPtrSizedIntReg(MIRBuilder);
2666 MIRBuilder.buildInstr(Opcode: SPIRV::OpAtomicExchange)
2667 .addDef(RegNo: ExchangeResReg)
2668 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: IntType))
2669 .addUse(RegNo: CastedPtr)
2670 .addUse(RegNo: ScopeReg)
2671 .addUse(RegNo: MemSemReg)
2672 .addUse(RegNo: ValueAsInt)
2673 .constrainAllUses(TII, TRI, RBI);
2674 return ExchangeResReg;
2675 });
2676 }
2677
2678 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: NewOpcode))
2679 .addDef(RegNo: ResVReg)
2680 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2681 .addUse(RegNo: Ptr)
2682 .addUse(RegNo: ScopeReg)
2683 .addUse(RegNo: MemSemReg)
2684 .addUse(RegNo: ValueReg)
2685 .constrainAllUses(TII, TRI, RBI);
2686 return true;
2687}
2688
2689bool SPIRVInstructionSelector::selectUnmergeValues(MachineInstr &I) const {
2690 unsigned ArgI = I.getNumOperands() - 1;
2691 Register SrcReg =
2692 I.getOperand(i: ArgI).isReg() ? I.getOperand(i: ArgI).getReg() : Register(0);
2693 SPIRVTypeInst SrcType =
2694 SrcReg.isValid() ? GR.getSPIRVTypeForVReg(VReg: SrcReg) : nullptr;
2695 if (!SrcType || !isVectorType(SPVTy: SrcType))
2696 report_fatal_error(
2697 reason: "cannot select G_UNMERGE_VALUES with a non-vector argument");
2698
2699 SPIRVTypeInst ScalarType = GR.getScalarOrVectorComponentType(Type: SrcType);
2700 MachineBasicBlock &BB = *I.getParent();
2701 unsigned CurrentIndex = 0;
2702 for (unsigned i = 0; i < I.getNumDefs(); ++i) {
2703 Register ResVReg = I.getOperand(i).getReg();
2704 SPIRVTypeInst ResType = GR.getSPIRVTypeForVReg(VReg: ResVReg);
2705 if (!ResType) {
2706 LLT ResLLT = MRI->getType(Reg: ResVReg);
2707 assert(ResLLT.isValid());
2708 if (ResLLT.isVector()) {
2709 ResType = GR.getOrCreateSPIRVVectorType(
2710 BaseType: ScalarType, NumElements: ResLLT.getNumElements(), I, TII);
2711 } else {
2712 ResType = ScalarType;
2713 }
2714 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
2715 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: ResVReg, MF: *GR.CurMF);
2716 }
2717
2718 if (isVectorType(SPVTy: ResType)) {
2719 Register UndefReg = GR.getOrCreateUndef(I, SpvType: SrcType, TII);
2720 auto MIB =
2721 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
2722 .addDef(RegNo: ResVReg)
2723 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2724 .addUse(RegNo: SrcReg)
2725 .addUse(RegNo: UndefReg);
2726 unsigned NumElements = GR.getScalarOrVectorComponentCount(Type: ResType);
2727 for (unsigned j = 0; j < NumElements; ++j) {
2728 MIB.addImm(Val: CurrentIndex + j);
2729 }
2730 CurrentIndex += NumElements;
2731 MIB.constrainAllUses(TII, TRI, RBI);
2732 } else {
2733 auto MIB =
2734 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
2735 .addDef(RegNo: ResVReg)
2736 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2737 .addUse(RegNo: SrcReg)
2738 .addImm(Val: CurrentIndex);
2739 CurrentIndex++;
2740 MIB.constrainAllUses(TII, TRI, RBI);
2741 }
2742 }
2743 return true;
2744}
2745
2746bool SPIRVInstructionSelector::selectFence(MachineInstr &I) const {
2747 AtomicOrdering AO = AtomicOrdering(I.getOperand(i: 0).getImm());
2748 uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(Ord: AO));
2749 Register MemSemReg = buildI32ConstantInEntryBlock(Val: MemSem, I);
2750 SyncScope::ID Ord = SyncScope::ID(I.getOperand(i: 1).getImm());
2751 uint32_t Scope = static_cast<uint32_t>(getMemScope(
2752 TT: STI.getTargetTriple(), Ctx&: GR.CurMF->getFunction().getContext(), Id: Ord));
2753 Register ScopeReg = buildI32ConstantInEntryBlock(Val: Scope, I);
2754 MachineBasicBlock &BB = *I.getParent();
2755 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpMemoryBarrier))
2756 .addUse(RegNo: ScopeReg)
2757 .addUse(RegNo: MemSemReg)
2758 .constrainAllUses(TII, TRI, RBI);
2759 return true;
2760}
2761
2762bool SPIRVInstructionSelector::selectOverflowArith(Register ResVReg,
2763 SPIRVTypeInst ResType,
2764 MachineInstr &I,
2765 unsigned Opcode) const {
2766 Type *ResTy = nullptr;
2767 StringRef ResName;
2768 if (!GR.findValueAttrs(Key: &I, Ty&: ResTy, Name&: ResName))
2769 return diagnoseUnsupported(
2770 I,
2771 Msg: "Not enough info to select the arithmetic with overflow instruction");
2772 if (!ResTy || !ResTy->isStructTy())
2773 return diagnoseUnsupported(I,
2774 Msg: "Expect struct type result for the arithmetic "
2775 "with overflow instruction");
2776 // "Result Type must be from OpTypeStruct. The struct must have two members,
2777 // and the two members must be the same type."
2778 Type *ResElemTy = cast<StructType>(Val: ResTy)->getElementType(N: 0);
2779 ResTy = StructType::get(elt1: ResElemTy, elts: ResElemTy);
2780 // Build SPIR-V types and constant(s) if needed.
2781 MachineIRBuilder MIRBuilder(I);
2782 SPIRVTypeInst StructType = GR.getOrCreateSPIRVType(
2783 Type: ResTy, MIRBuilder, AQ: SPIRV::AccessQualifier::ReadWrite, EmitIR: false);
2784 assert(I.getNumDefs() > 1 && "Not enought operands");
2785 SPIRVTypeInst BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
2786 unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
2787 if (N > 1 || (isVectorType(SPVTy: ResType) &&
2788 STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector)))
2789 BoolType = GR.getOrCreateSPIRVVectorType(BaseType: BoolType, NumElements: N, I, TII);
2790 Register BoolTypeReg = GR.getSPIRVTypeID(SpirvType: BoolType);
2791 Register ZeroReg = buildZerosVal(ResType, I);
2792 // A new virtual register to store the result struct.
2793 Register StructVReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2794 MRI->setRegClass(Reg: StructVReg, RC: &SPIRV::IDRegClass);
2795 // Build the result name if needed.
2796 if (ResName.size() > 0)
2797 buildOpName(Target: StructVReg, Name: ResName, MIRBuilder);
2798 // Build the arithmetic with overflow instruction.
2799 MachineBasicBlock &BB = *I.getParent();
2800 auto MIB =
2801 BuildMI(BB, I: MIRBuilder.getInsertPt(), MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
2802 .addDef(RegNo: StructVReg)
2803 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: StructType));
2804 for (unsigned i = I.getNumDefs(); i < I.getNumOperands(); ++i)
2805 MIB.addUse(RegNo: I.getOperand(i).getReg());
2806 MIB.constrainAllUses(TII, TRI, RBI);
2807 // Build instructions to extract fields of the instruction's result.
2808 // A new virtual register to store the higher part of the result struct.
2809 Register HigherVReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2810 MRI->setRegClass(Reg: HigherVReg, RC: &SPIRV::iIDRegClass);
2811 for (unsigned i = 0; i < I.getNumDefs(); ++i) {
2812 auto MIB =
2813 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
2814 .addDef(RegNo: i == 1 ? HigherVReg : I.getOperand(i).getReg())
2815 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2816 .addUse(RegNo: StructVReg)
2817 .addImm(Val: i);
2818 MIB.constrainAllUses(TII, TRI, RBI);
2819 }
2820 // Build boolean value from the higher part.
2821 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpINotEqual))
2822 .addDef(RegNo: I.getOperand(i: 1).getReg())
2823 .addUse(RegNo: BoolTypeReg)
2824 .addUse(RegNo: HigherVReg)
2825 .addUse(RegNo: ZeroReg)
2826 .constrainAllUses(TII, TRI, RBI);
2827 return true;
2828}
2829
2830bool SPIRVInstructionSelector::selectAtomicCmpXchg(Register ResVReg,
2831 SPIRVTypeInst ResType,
2832 MachineInstr &I) const {
2833 assert(isa<GIntrinsic>(I) &&
2834 "selectAtomicCmpXchg only handles the spv_cmpxchg intrinsic");
2835 Register Ptr = I.getOperand(i: 2).getReg();
2836 Register ScopeReg = I.getOperand(i: 5).getReg();
2837 Register MemSemEqReg = I.getOperand(i: 6).getReg();
2838 Register MemSemNeqReg = I.getOperand(i: 7).getReg();
2839 Register Cmp = I.getOperand(i: 3).getReg();
2840 Register Val = I.getOperand(i: 4).getReg();
2841 SPIRVTypeInst SpvValTy = GR.getSPIRVTypeForVReg(VReg: Val);
2842 Register ACmpRes = createVirtualRegister(SpvType: SpvValTy, GR: &GR, MRI, MF: *I.getMF());
2843 const DebugLoc &DL = I.getDebugLoc();
2844 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpAtomicCompareExchange))
2845 .addDef(RegNo: ACmpRes)
2846 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvValTy))
2847 .addUse(RegNo: Ptr)
2848 .addUse(RegNo: ScopeReg)
2849 .addUse(RegNo: MemSemEqReg)
2850 .addUse(RegNo: MemSemNeqReg)
2851 .addUse(RegNo: Val)
2852 .addUse(RegNo: Cmp)
2853 .constrainAllUses(TII, TRI, RBI);
2854 SPIRVTypeInst BoolTy = GR.getOrCreateSPIRVBoolType(I, TII);
2855 Register CmpSuccReg = createVirtualRegister(SpvType: BoolTy, GR: &GR, MRI, MF: *I.getMF());
2856 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpIEqual))
2857 .addDef(RegNo: CmpSuccReg)
2858 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: BoolTy))
2859 .addUse(RegNo: ACmpRes)
2860 .addUse(RegNo: Cmp)
2861 .constrainAllUses(TII, TRI, RBI);
2862 Register TmpReg = createVirtualRegister(SpvType: ResType, GR: &GR, MRI, MF: *I.getMF());
2863 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeInsert))
2864 .addDef(RegNo: TmpReg)
2865 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2866 .addUse(RegNo: ACmpRes)
2867 .addUse(RegNo: GR.getOrCreateUndef(I, SpvType: ResType, TII))
2868 .addImm(Val: 0)
2869 .constrainAllUses(TII, TRI, RBI);
2870 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeInsert))
2871 .addDef(RegNo: ResVReg)
2872 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2873 .addUse(RegNo: CmpSuccReg)
2874 .addUse(RegNo: TmpReg)
2875 .addImm(Val: 1)
2876 .constrainAllUses(TII, TRI, RBI);
2877 return true;
2878}
2879
2880static bool isUSMStorageClass(SPIRV::StorageClass::StorageClass SC) {
2881 switch (SC) {
2882 case SPIRV::StorageClass::DeviceOnlyINTEL:
2883 case SPIRV::StorageClass::HostOnlyINTEL:
2884 return true;
2885 default:
2886 return false;
2887 }
2888}
2889
2890// Returns true ResVReg is referred only from global vars and OpName's.
2891static bool isASCastInGVar(MachineRegisterInfo *MRI, Register ResVReg) {
2892 bool IsGRef = false;
2893 bool IsAllowedRefs =
2894 llvm::all_of(Range: MRI->use_instructions(Reg: ResVReg), P: [&IsGRef](auto const &It) {
2895 unsigned Opcode = It.getOpcode();
2896 if (Opcode == SPIRV::OpConstantComposite ||
2897 Opcode == SPIRV::OpSpecConstantComposite ||
2898 Opcode == SPIRV::OpVariable ||
2899 Opcode == SPIRV::OpUntypedVariableKHR ||
2900 isSpvIntrinsic(It, Intrinsic::spv_init_global))
2901 return IsGRef = true;
2902 return Opcode == SPIRV::OpName;
2903 });
2904 return IsAllowedRefs && IsGRef;
2905}
2906
2907Register SPIRVInstructionSelector::getUcharPtrTypeReg(
2908 MachineInstr &I, SPIRV::StorageClass::StorageClass SC) const {
2909 return GR.getSPIRVTypeID(SpirvType: GR.getOrCreateSPIRVPointerType(
2910 BaseType: Type::getInt8Ty(C&: I.getMF()->getFunction().getContext()), I, SC));
2911}
2912
2913MachineInstrBuilder
2914SPIRVInstructionSelector::buildSpecConstantOp(MachineInstr &I, Register Dest,
2915 Register Src, Register DestType,
2916 uint32_t Opcode) const {
2917 return BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
2918 MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
2919 .addDef(RegNo: Dest)
2920 .addUse(RegNo: DestType)
2921 .addImm(Val: Opcode)
2922 .addUse(RegNo: Src);
2923}
2924
2925MachineInstrBuilder
2926SPIRVInstructionSelector::buildConstGenericPtr(MachineInstr &I, Register SrcPtr,
2927 SPIRVTypeInst SrcPtrTy) const {
2928 SPIRVTypeInst GenericPtrTy =
2929 GR.changePointerStorageClass(PtrType: SrcPtrTy, SC: SPIRV::StorageClass::Generic, I);
2930 Register Tmp = MRI->createVirtualRegister(RegClass: &SPIRV::pIDRegClass);
2931 MRI->setType(VReg: Tmp, Ty: LLT::pointer(AddressSpace: storageClassToAddressSpace(
2932 SC: SPIRV::StorageClass::Generic),
2933 SizeInBits: GR.getPointerSize()));
2934 MachineFunction *MF = I.getParent()->getParent();
2935 GR.assignSPIRVTypeToVReg(Type: GenericPtrTy, VReg: Tmp, MF: *MF);
2936 MachineInstrBuilder MIB = buildSpecConstantOp(
2937 I, Dest: Tmp, Src: SrcPtr, DestType: GR.getSPIRVTypeID(SpirvType: GenericPtrTy),
2938 Opcode: static_cast<uint32_t>(SPIRV::Opcode::PtrCastToGeneric));
2939 GR.add(Obj: MIB.getInstr(), MI: MIB);
2940 return MIB;
2941}
2942
2943// In SPIR-V address space casting can only happen to and from the Generic
2944// storage class. We can also only cast Workgroup, CrossWorkgroup, or Function
2945// pointers to and from Generic pointers. As such, we can convert e.g. from
2946// Workgroup to Function by going via a Generic pointer as an intermediary. All
2947// other combinations can only be done by a bitcast, and are probably not safe.
2948bool SPIRVInstructionSelector::selectAddrSpaceCast(Register ResVReg,
2949 SPIRVTypeInst ResType,
2950 MachineInstr &I) const {
2951 MachineBasicBlock &BB = *I.getParent();
2952 const DebugLoc &DL = I.getDebugLoc();
2953
2954 Register SrcPtr = I.getOperand(i: 1).getReg();
2955 SPIRVTypeInst SrcPtrTy = GR.getSPIRVTypeForVReg(VReg: SrcPtr);
2956
2957 // don't generate a cast for a null that may be represented by OpTypeInt
2958 if (!SrcPtrTy || !SrcPtrTy.isPointer() || !ResType || !ResType.isPointer())
2959 return BuildCOPY(DestReg: ResVReg, SrcReg: SrcPtr, I);
2960
2961 SPIRV::StorageClass::StorageClass SrcSC = GR.getPointerStorageClass(Type: SrcPtrTy);
2962 SPIRV::StorageClass::StorageClass DstSC = GR.getPointerStorageClass(Type: ResType);
2963
2964 if (isASCastInGVar(MRI, ResVReg)) {
2965 // AddrSpaceCast uses within OpVariable and OpConstantComposite instructions
2966 // are expressed by OpSpecConstantOp with an Opcode.
2967 // TODO: maybe insert a check whether the Kernel capability was declared and
2968 // so PtrCastToGeneric/GenericCastToPtr are available.
2969 unsigned SpecOpcode = [&]() -> unsigned {
2970 if (SrcSC == SPIRV::StorageClass::CodeSectionINTEL)
2971 return static_cast<uint32_t>(SPIRV::Opcode::Bitcast);
2972 if (DstSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SC: SrcSC))
2973 return static_cast<uint32_t>(SPIRV::Opcode::PtrCastToGeneric);
2974 if (SrcSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SC: DstSC))
2975 return static_cast<uint32_t>(SPIRV::Opcode::GenericCastToPtr);
2976 return 0u;
2977 }();
2978 // TODO: OpConstantComposite expects i8*, so we are forced to forget a
2979 // correct value of ResType and use general i8* instead. Maybe this should
2980 // be addressed in the emit-intrinsic step to infer a correct
2981 // OpConstantComposite type.
2982 if (SpecOpcode) {
2983 buildSpecConstantOp(I, Dest: ResVReg, Src: SrcPtr, DestType: getUcharPtrTypeReg(I, SC: DstSC),
2984 Opcode: SpecOpcode)
2985 .constrainAllUses(TII, TRI, RBI);
2986 } else if (isGenericCastablePtr(SC: SrcSC) && isGenericCastablePtr(SC: DstSC)) {
2987 MachineInstrBuilder MIB = buildConstGenericPtr(I, SrcPtr, SrcPtrTy);
2988 MIB.constrainAllUses(TII, TRI, RBI);
2989 buildSpecConstantOp(
2990 I, Dest: ResVReg, Src: MIB->getOperand(i: 0).getReg(), DestType: getUcharPtrTypeReg(I, SC: DstSC),
2991 Opcode: static_cast<uint32_t>(SPIRV::Opcode::GenericCastToPtr))
2992 .constrainAllUses(TII, TRI, RBI);
2993 }
2994 return true;
2995 }
2996
2997 // don't generate a cast between identical storage classes
2998 if (SrcSC == DstSC)
2999 return BuildCOPY(DestReg: ResVReg, SrcReg: SrcPtr, I);
3000
3001 if ((SrcSC == SPIRV::StorageClass::Function &&
3002 DstSC == SPIRV::StorageClass::Private) ||
3003 (DstSC == SPIRV::StorageClass::Function &&
3004 SrcSC == SPIRV::StorageClass::Private))
3005 return BuildCOPY(DestReg: ResVReg, SrcReg: SrcPtr, I);
3006
3007 // Casting from an eligible pointer to Generic.
3008 if (DstSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SC: SrcSC))
3009 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpPtrCastToGeneric);
3010 // Casting from Generic to an eligible pointer.
3011 if (SrcSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SC: DstSC))
3012 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpGenericCastToPtr);
3013 // Casting between 2 eligible pointers using Generic as an intermediary.
3014 if (isGenericCastablePtr(SC: SrcSC) && isGenericCastablePtr(SC: DstSC)) {
3015 SPIRVTypeInst GenericPtrTy =
3016 GR.changePointerStorageClass(PtrType: SrcPtrTy, SC: SPIRV::StorageClass::Generic, I);
3017 Register Tmp = createVirtualRegister(SpvType: GenericPtrTy, GR: &GR, MRI, MF: MRI->getMF());
3018 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpPtrCastToGeneric))
3019 .addDef(RegNo: Tmp)
3020 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: GenericPtrTy))
3021 .addUse(RegNo: SrcPtr)
3022 .constrainAllUses(TII, TRI, RBI);
3023 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGenericCastToPtr))
3024 .addDef(RegNo: ResVReg)
3025 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3026 .addUse(RegNo: Tmp)
3027 .constrainAllUses(TII, TRI, RBI);
3028 return true;
3029 }
3030
3031 // Check if instructions from the SPV_INTEL_usm_storage_classes extension may
3032 // be applied
3033 if (isUSMStorageClass(SC: SrcSC) && DstSC == SPIRV::StorageClass::CrossWorkgroup)
3034 return selectUnOp(ResVReg, ResType, I,
3035 Opcode: SPIRV::OpPtrCastToCrossWorkgroupINTEL);
3036 if (SrcSC == SPIRV::StorageClass::CrossWorkgroup && isUSMStorageClass(SC: DstSC))
3037 return selectUnOp(ResVReg, ResType, I,
3038 Opcode: SPIRV::OpCrossWorkgroupCastToPtrINTEL);
3039 if (isUSMStorageClass(SC: SrcSC) && DstSC == SPIRV::StorageClass::Generic)
3040 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpPtrCastToGeneric);
3041 if (SrcSC == SPIRV::StorageClass::Generic && isUSMStorageClass(SC: DstSC))
3042 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpGenericCastToPtr);
3043
3044 // Bitcast for pointers requires that the address spaces must match
3045 return false;
3046}
3047
3048// G_PTRMASK - Apply a bitmask to a pointer value.
3049// Result = Ptr & Mask
3050// We need to convert the pointer to an integer, perform the AND operation,
3051// and convert back to a pointer.
3052bool SPIRVInstructionSelector::selectPtrMask(Register ResVReg,
3053 SPIRVTypeInst ResType,
3054 MachineInstr &I) const {
3055 if (STI.isLogicalSPIRV())
3056 return diagnoseUnsupported(
3057 I, Msg: "G_PTRMASK is not supported with logical SPIR-V");
3058 MachineBasicBlock &BB = *I.getParent();
3059 MachineFunction &MF = *BB.getParent();
3060 const DebugLoc &DL = I.getDebugLoc();
3061
3062 Register PtrReg = I.getOperand(i: 1).getReg();
3063 Register MaskReg = I.getOperand(i: 2).getReg();
3064
3065 SPIRVTypeInst MaskType = GR.getSPIRVTypeForVReg(VReg: MaskReg);
3066
3067 // Convert pointer to integer.
3068 Register PtrAsInt = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: MaskType));
3069 GR.assignSPIRVTypeToVReg(Type: MaskType, VReg: PtrAsInt, MF);
3070
3071 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpConvertPtrToU))
3072 .addDef(RegNo: PtrAsInt)
3073 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MaskType))
3074 .addUse(RegNo: PtrReg)
3075 .constrainAllUses(TII, TRI, RBI);
3076
3077 // Perform bitwise AND.
3078 Register MaskedInt = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: MaskType));
3079 GR.assignSPIRVTypeToVReg(Type: MaskType, VReg: MaskedInt, MF);
3080
3081 unsigned AndOpcode = GR.getScalarOrVectorComponentCount(Type: MaskType) > 1
3082 ? SPIRV::OpBitwiseAndV
3083 : SPIRV::OpBitwiseAndS;
3084
3085 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: AndOpcode))
3086 .addDef(RegNo: MaskedInt)
3087 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MaskType))
3088 .addUse(RegNo: PtrAsInt)
3089 .addUse(RegNo: MaskReg)
3090 .constrainAllUses(TII, TRI, RBI);
3091
3092 // Convert integer back to pointer.
3093 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpConvertUToPtr))
3094 .addDef(RegNo: ResVReg)
3095 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3096 .addUse(RegNo: MaskedInt)
3097 .constrainAllUses(TII, TRI, RBI);
3098
3099 return true;
3100}
3101
3102static unsigned getFCmpOpcode(unsigned PredNum) {
3103 auto Pred = static_cast<CmpInst::Predicate>(PredNum);
3104 switch (Pred) {
3105 case CmpInst::FCMP_OEQ:
3106 return SPIRV::OpFOrdEqual;
3107 case CmpInst::FCMP_OGE:
3108 return SPIRV::OpFOrdGreaterThanEqual;
3109 case CmpInst::FCMP_OGT:
3110 return SPIRV::OpFOrdGreaterThan;
3111 case CmpInst::FCMP_OLE:
3112 return SPIRV::OpFOrdLessThanEqual;
3113 case CmpInst::FCMP_OLT:
3114 return SPIRV::OpFOrdLessThan;
3115 case CmpInst::FCMP_ONE:
3116 return SPIRV::OpFOrdNotEqual;
3117 case CmpInst::FCMP_ORD:
3118 return SPIRV::OpOrdered;
3119 case CmpInst::FCMP_UEQ:
3120 return SPIRV::OpFUnordEqual;
3121 case CmpInst::FCMP_UGE:
3122 return SPIRV::OpFUnordGreaterThanEqual;
3123 case CmpInst::FCMP_UGT:
3124 return SPIRV::OpFUnordGreaterThan;
3125 case CmpInst::FCMP_ULE:
3126 return SPIRV::OpFUnordLessThanEqual;
3127 case CmpInst::FCMP_ULT:
3128 return SPIRV::OpFUnordLessThan;
3129 case CmpInst::FCMP_UNE:
3130 return SPIRV::OpFUnordNotEqual;
3131 case CmpInst::FCMP_UNO:
3132 return SPIRV::OpUnordered;
3133 default:
3134 llvm_unreachable("Unknown predicate type for FCmp");
3135 }
3136}
3137
3138static unsigned getICmpOpcode(unsigned PredNum) {
3139 auto Pred = static_cast<CmpInst::Predicate>(PredNum);
3140 switch (Pred) {
3141 case CmpInst::ICMP_EQ:
3142 return SPIRV::OpIEqual;
3143 case CmpInst::ICMP_NE:
3144 return SPIRV::OpINotEqual;
3145 case CmpInst::ICMP_SGE:
3146 return SPIRV::OpSGreaterThanEqual;
3147 case CmpInst::ICMP_SGT:
3148 return SPIRV::OpSGreaterThan;
3149 case CmpInst::ICMP_SLE:
3150 return SPIRV::OpSLessThanEqual;
3151 case CmpInst::ICMP_SLT:
3152 return SPIRV::OpSLessThan;
3153 case CmpInst::ICMP_UGE:
3154 return SPIRV::OpUGreaterThanEqual;
3155 case CmpInst::ICMP_UGT:
3156 return SPIRV::OpUGreaterThan;
3157 case CmpInst::ICMP_ULE:
3158 return SPIRV::OpULessThanEqual;
3159 case CmpInst::ICMP_ULT:
3160 return SPIRV::OpULessThan;
3161 default:
3162 llvm_unreachable("Unknown predicate type for ICmp");
3163 }
3164}
3165
3166static unsigned getPtrCmpOpcode(unsigned Pred) {
3167 switch (static_cast<CmpInst::Predicate>(Pred)) {
3168 case CmpInst::ICMP_EQ:
3169 return SPIRV::OpPtrEqual;
3170 case CmpInst::ICMP_NE:
3171 return SPIRV::OpPtrNotEqual;
3172 default:
3173 llvm_unreachable("Unknown predicate type for pointer comparison");
3174 }
3175}
3176
3177// Return the logical operation, or abort if none exists.
3178static unsigned getBoolCmpOpcode(unsigned PredNum) {
3179 auto Pred = static_cast<CmpInst::Predicate>(PredNum);
3180 switch (Pred) {
3181 case CmpInst::ICMP_EQ:
3182 return SPIRV::OpLogicalEqual;
3183 case CmpInst::ICMP_NE:
3184 return SPIRV::OpLogicalNotEqual;
3185 default:
3186 llvm_unreachable("Unknown predicate type for Bool comparison");
3187 }
3188}
3189
3190static APFloat getZeroFP(const Type *LLVMFloatTy) {
3191 if (!LLVMFloatTy)
3192 return APFloat::getZero(Sem: APFloat::IEEEsingle());
3193 switch (LLVMFloatTy->getScalarType()->getTypeID()) {
3194 case Type::HalfTyID:
3195 return APFloat::getZero(Sem: APFloat::IEEEhalf());
3196 case Type::BFloatTyID:
3197 return APFloat::getZero(Sem: APFloat::BFloat());
3198 default:
3199 case Type::FloatTyID:
3200 return APFloat::getZero(Sem: APFloat::IEEEsingle());
3201 case Type::DoubleTyID:
3202 return APFloat::getZero(Sem: APFloat::IEEEdouble());
3203 }
3204}
3205
3206static APFloat getOneFP(const Type *LLVMFloatTy) {
3207 if (!LLVMFloatTy)
3208 return APFloat::getOne(Sem: APFloat::IEEEsingle());
3209 switch (LLVMFloatTy->getScalarType()->getTypeID()) {
3210 case Type::HalfTyID:
3211 return APFloat::getOne(Sem: APFloat::IEEEhalf());
3212 case Type::BFloatTyID:
3213 return APFloat::getOne(Sem: APFloat::BFloat());
3214 default:
3215 case Type::FloatTyID:
3216 return APFloat::getOne(Sem: APFloat::IEEEsingle());
3217 case Type::DoubleTyID:
3218 return APFloat::getOne(Sem: APFloat::IEEEdouble());
3219 }
3220}
3221
3222bool SPIRVInstructionSelector::selectAnyOrAll(Register ResVReg,
3223 SPIRVTypeInst ResType,
3224 MachineInstr &I,
3225 unsigned OpAnyOrAll) const {
3226 assert(I.getNumOperands() == 3);
3227 assert(I.getOperand(2).isReg());
3228 MachineBasicBlock &BB = *I.getParent();
3229 Register InputRegister = I.getOperand(i: 2).getReg();
3230 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3231
3232 assert(InputType && "VReg has no type assigned");
3233
3234 bool IsBoolTy = GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeBool);
3235 if (IsBoolTy && !isVectorType(SPVTy: InputType)) {
3236 assert(ResVReg == I.getOperand(0).getReg());
3237 return BuildCOPY(DestReg: ResVReg, SrcReg: InputRegister, I);
3238 }
3239
3240 bool IsFloatTy = GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3241 unsigned SpirvNotEqualId =
3242 IsFloatTy ? SPIRV::OpFOrdNotEqual : SPIRV::OpINotEqual;
3243 SPIRVTypeInst SpvBoolScalarTy = GR.getOrCreateSPIRVBoolType(I, TII);
3244 SPIRVTypeInst SpvBoolTy = SpvBoolScalarTy;
3245 Register NotEqualReg = ResVReg;
3246
3247 if (isVectorType(SPVTy: InputType)) {
3248 NotEqualReg =
3249 IsBoolTy ? InputRegister
3250 : createVirtualRegister(SpvType: SpvBoolTy, GR: &GR, MRI, MF: MRI->getMF());
3251 const unsigned NumElts = GR.getScalarOrVectorComponentCount(Type: InputType);
3252 SpvBoolTy = GR.getOrCreateSPIRVVectorType(BaseType: SpvBoolTy, NumElements: NumElts, I, TII);
3253 }
3254
3255 if (!IsBoolTy) {
3256 Register ConstZeroReg =
3257 IsFloatTy ? buildZerosValF(ResType: InputType, I) : buildZerosVal(ResType: InputType, I);
3258
3259 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SpirvNotEqualId))
3260 .addDef(RegNo: NotEqualReg)
3261 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvBoolTy))
3262 .addUse(RegNo: InputRegister)
3263 .addUse(RegNo: ConstZeroReg)
3264 .constrainAllUses(TII, TRI, RBI);
3265 }
3266
3267 if (isVectorType(SPVTy: InputType))
3268 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: OpAnyOrAll))
3269 .addDef(RegNo: ResVReg)
3270 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvBoolScalarTy))
3271 .addUse(RegNo: NotEqualReg)
3272 .constrainAllUses(TII, TRI, RBI);
3273 return true;
3274}
3275
3276bool SPIRVInstructionSelector::selectAll(Register ResVReg,
3277 SPIRVTypeInst ResType,
3278 MachineInstr &I) const {
3279 return selectAnyOrAll(ResVReg, ResType, I, OpAnyOrAll: SPIRV::OpAll);
3280}
3281
3282bool SPIRVInstructionSelector::selectAny(Register ResVReg,
3283 SPIRVTypeInst ResType,
3284 MachineInstr &I) const {
3285 return selectAnyOrAll(ResVReg, ResType, I, OpAnyOrAll: SPIRV::OpAny);
3286}
3287
3288// Select the OpDot instruction for the given float dot
3289bool SPIRVInstructionSelector::selectFloatDot(Register ResVReg,
3290 SPIRVTypeInst ResType,
3291 MachineInstr &I) const {
3292 assert(I.getNumOperands() == 4);
3293 assert(I.getOperand(2).isReg());
3294 assert(I.getOperand(3).isReg());
3295
3296 [[maybe_unused]] SPIRVTypeInst VecType =
3297 GR.getSPIRVTypeForVReg(VReg: I.getOperand(i: 2).getReg());
3298
3299 assert(((VecType->getOpcode() == SPIRV::OpTypeVector &&
3300 GR.getScalarOrVectorComponentCount(VecType) > 1) ||
3301 VecType->getOpcode() == SPIRV::OpTypeVectorIdEXT) &&
3302 "dot product requires either a vector of at least 2 components or"
3303 " the SPV_EXT_long vector extension.");
3304
3305 [[maybe_unused]] SPIRVTypeInst EltType =
3306 GR.getScalarOrVectorComponentType(Type: VecType);
3307
3308 assert(EltType->getOpcode() == SPIRV::OpTypeFloat);
3309
3310 MachineBasicBlock &BB = *I.getParent();
3311 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpDot))
3312 .addDef(RegNo: ResVReg)
3313 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3314 .addUse(RegNo: I.getOperand(i: 2).getReg())
3315 .addUse(RegNo: I.getOperand(i: 3).getReg())
3316 .constrainAllUses(TII, TRI, RBI);
3317 return true;
3318}
3319
3320bool SPIRVInstructionSelector::selectIntegerDot(Register ResVReg,
3321 SPIRVTypeInst ResType,
3322 MachineInstr &I,
3323 bool Signed) const {
3324 assert(I.getNumOperands() == 4);
3325 assert(I.getOperand(2).isReg());
3326 assert(I.getOperand(3).isReg());
3327 MachineBasicBlock &BB = *I.getParent();
3328
3329 auto DotOp = Signed ? SPIRV::OpSDot : SPIRV::OpUDot;
3330 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DotOp))
3331 .addDef(RegNo: ResVReg)
3332 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3333 .addUse(RegNo: I.getOperand(i: 2).getReg())
3334 .addUse(RegNo: I.getOperand(i: 3).getReg())
3335 .constrainAllUses(TII, TRI, RBI);
3336 return true;
3337}
3338
3339// Since pre-1.6 SPIRV has no integer dot implementation,
3340// expand by piecewise multiplying and adding the results
3341bool SPIRVInstructionSelector::selectIntegerDotExpansion(
3342 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3343 assert(I.getNumOperands() == 4);
3344 assert(I.getOperand(2).isReg());
3345 assert(I.getOperand(3).isReg());
3346 MachineBasicBlock &BB = *I.getParent();
3347
3348 // Multiply the vectors, then sum the results
3349 Register Vec0 = I.getOperand(i: 2).getReg();
3350 Register Vec1 = I.getOperand(i: 3).getReg();
3351 Register TmpVec = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3352 SPIRVTypeInst VecType = GR.getSPIRVTypeForVReg(VReg: Vec0);
3353
3354 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIMulV))
3355 .addDef(RegNo: TmpVec)
3356 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: VecType))
3357 .addUse(RegNo: Vec0)
3358 .addUse(RegNo: Vec1)
3359 .constrainAllUses(TII, TRI, RBI);
3360
3361 assert(((VecType->getOpcode() == SPIRV::OpTypeVector &&
3362 GR.getScalarOrVectorComponentCount(VecType) > 1) ||
3363 VecType->getOpcode() == SPIRV::OpTypeVectorIdEXT) &&
3364 "dot product requires either a vector of at least 2 components "
3365 "or the SPV_EXT_long_vector extension.");
3366
3367 Register Res = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3368 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
3369 .addDef(RegNo: Res)
3370 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3371 .addUse(RegNo: TmpVec)
3372 .addImm(Val: 0)
3373 .constrainAllUses(TII, TRI, RBI);
3374
3375 for (unsigned i = 1; i < GR.getScalarOrVectorComponentCount(Type: VecType); i++) {
3376 Register Elt = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3377
3378 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
3379 .addDef(RegNo: Elt)
3380 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3381 .addUse(RegNo: TmpVec)
3382 .addImm(Val: i)
3383 .constrainAllUses(TII, TRI, RBI);
3384
3385 Register Sum = i < GR.getScalarOrVectorComponentCount(Type: VecType) - 1
3386 ? MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType))
3387 : ResVReg;
3388
3389 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
3390 .addDef(RegNo: Sum)
3391 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3392 .addUse(RegNo: Res)
3393 .addUse(RegNo: Elt)
3394 .constrainAllUses(TII, TRI, RBI);
3395 Res = Sum;
3396 }
3397
3398 return true;
3399}
3400
3401bool SPIRVInstructionSelector::selectOpIsInf(Register ResVReg,
3402 SPIRVTypeInst ResType,
3403 MachineInstr &I) const {
3404 MachineBasicBlock &BB = *I.getParent();
3405 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsInf))
3406 .addDef(RegNo: ResVReg)
3407 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3408 .addUse(RegNo: I.getOperand(i: 2).getReg())
3409 .constrainAllUses(TII, TRI, RBI);
3410 return true;
3411}
3412
3413bool SPIRVInstructionSelector::selectOpIsNan(Register ResVReg,
3414 SPIRVTypeInst ResType,
3415 MachineInstr &I) const {
3416 MachineBasicBlock &BB = *I.getParent();
3417 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsNan))
3418 .addDef(RegNo: ResVReg)
3419 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3420 .addUse(RegNo: I.getOperand(i: 2).getReg())
3421 .constrainAllUses(TII, TRI, RBI);
3422 return true;
3423}
3424
3425bool SPIRVInstructionSelector::selectOpIsFinite(Register ResVReg,
3426 SPIRVTypeInst ResType,
3427 MachineInstr &I) const {
3428 MachineBasicBlock &BB = *I.getParent();
3429 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsFinite))
3430 .addDef(RegNo: ResVReg)
3431 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3432 .addUse(RegNo: I.getOperand(i: 2).getReg())
3433 .constrainAllUses(TII, TRI, RBI);
3434 return true;
3435}
3436
3437bool SPIRVInstructionSelector::selectOpIsNormal(Register ResVReg,
3438 SPIRVTypeInst ResType,
3439 MachineInstr &I) const {
3440 MachineBasicBlock &BB = *I.getParent();
3441 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsNormal))
3442 .addDef(RegNo: ResVReg)
3443 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3444 .addUse(RegNo: I.getOperand(i: 2).getReg())
3445 .constrainAllUses(TII, TRI, RBI);
3446 return true;
3447}
3448
3449template <bool Signed>
3450bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
3451 SPIRVTypeInst ResType,
3452 MachineInstr &I) const {
3453 assert(I.getNumOperands() == 5);
3454 assert(I.getOperand(2).isReg());
3455 assert(I.getOperand(3).isReg());
3456 assert(I.getOperand(4).isReg());
3457 MachineBasicBlock &BB = *I.getParent();
3458
3459 Register Acc = I.getOperand(i: 2).getReg();
3460 Register X = I.getOperand(i: 3).getReg();
3461 Register Y = I.getOperand(i: 4).getReg();
3462
3463 auto DotOp = Signed ? SPIRV::OpSDot : SPIRV::OpUDot;
3464 Register Dot = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3465 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DotOp))
3466 .addDef(RegNo: Dot)
3467 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3468 .addUse(RegNo: X)
3469 .addUse(RegNo: Y);
3470 MIB.addImm(Val: SPIRV::BuiltIn::PackedVectorFormat4x8Bit);
3471 MIB.constrainAllUses(TII, TRI, RBI);
3472
3473 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
3474 .addDef(RegNo: ResVReg)
3475 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3476 .addUse(RegNo: Dot)
3477 .addUse(RegNo: Acc)
3478 .constrainAllUses(TII, TRI, RBI);
3479 return true;
3480}
3481
3482// Since pre-1.6 SPIRV has no DotProductInput4x8BitPacked implementation,
3483// extract the elements of the packed inputs, multiply them and add the result
3484// to the accumulator.
3485template <bool Signed>
3486bool SPIRVInstructionSelector::selectDot4AddPackedExpansion(
3487 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3488 assert(I.getNumOperands() == 5);
3489 assert(I.getOperand(2).isReg());
3490 assert(I.getOperand(3).isReg());
3491 assert(I.getOperand(4).isReg());
3492 MachineBasicBlock &BB = *I.getParent();
3493
3494 Register Acc = I.getOperand(i: 2).getReg();
3495 Register X = I.getOperand(i: 3).getReg();
3496 Register Y = I.getOperand(i: 4).getReg();
3497
3498 SPIRVTypeInst EltType = GR.getOrCreateSPIRVIntegerType(BitWidth: 8, I, TII);
3499 auto ExtractOp =
3500 Signed ? SPIRV::OpBitFieldSExtract : SPIRV::OpBitFieldUExtract;
3501
3502 bool ZeroAsNull = !STI.isShader();
3503 // Extract the i8 element, multiply and add it to the accumulator
3504 for (unsigned i = 0; i < 4; i++) {
3505 // A[i]
3506 Register AElt = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3507 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: ExtractOp))
3508 .addDef(RegNo: AElt)
3509 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3510 .addUse(RegNo: X)
3511 .addUse(RegNo: GR.getOrCreateConstInt(Val: i * 8, I, SpvType: EltType, TII, ZeroAsNull))
3512 .addUse(RegNo: GR.getOrCreateConstInt(Val: 8, I, SpvType: EltType, TII, ZeroAsNull))
3513 .constrainAllUses(TII, TRI, RBI);
3514
3515 // B[i]
3516 Register BElt = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3517 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: ExtractOp))
3518 .addDef(RegNo: BElt)
3519 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3520 .addUse(RegNo: Y)
3521 .addUse(RegNo: GR.getOrCreateConstInt(Val: i * 8, I, SpvType: EltType, TII, ZeroAsNull))
3522 .addUse(RegNo: GR.getOrCreateConstInt(Val: 8, I, SpvType: EltType, TII, ZeroAsNull))
3523 .constrainAllUses(TII, TRI, RBI);
3524
3525 // A[i] * B[i]
3526 Register Mul = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3527 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIMulS))
3528 .addDef(RegNo: Mul)
3529 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3530 .addUse(RegNo: AElt)
3531 .addUse(RegNo: BElt)
3532 .constrainAllUses(TII, TRI, RBI);
3533
3534 // Discard 24 highest-bits so that stored i32 register is i8 equivalent
3535 Register MaskMul = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3536 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: ExtractOp))
3537 .addDef(RegNo: MaskMul)
3538 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3539 .addUse(RegNo: Mul)
3540 .addUse(RegNo: GR.getOrCreateConstInt(Val: 0, I, SpvType: EltType, TII, ZeroAsNull))
3541 .addUse(RegNo: GR.getOrCreateConstInt(Val: 8, I, SpvType: EltType, TII, ZeroAsNull))
3542 .constrainAllUses(TII, TRI, RBI);
3543
3544 // Acc = Acc + A[i] * B[i]
3545 Register Sum =
3546 i < 3 ? MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass) : ResVReg;
3547 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
3548 .addDef(RegNo: Sum)
3549 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3550 .addUse(RegNo: Acc)
3551 .addUse(RegNo: MaskMul)
3552 .constrainAllUses(TII, TRI, RBI);
3553
3554 Acc = Sum;
3555 }
3556
3557 return true;
3558}
3559
3560/// Transform saturate(x) to clamp(x, 0.0f, 1.0f) as SPIRV
3561/// does not have a saturate builtin.
3562bool SPIRVInstructionSelector::selectSaturate(Register ResVReg,
3563 SPIRVTypeInst ResType,
3564 MachineInstr &I) const {
3565 assert(I.getNumOperands() == 3);
3566 assert(I.getOperand(2).isReg());
3567 MachineBasicBlock &BB = *I.getParent();
3568 Register VZero = buildZerosValF(ResType, I);
3569 Register VOne = buildOnesValF(ResType, I);
3570
3571 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
3572 .addDef(RegNo: ResVReg)
3573 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3574 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
3575 .addImm(Val: GL::FClamp)
3576 .addUse(RegNo: I.getOperand(i: 2).getReg())
3577 .addUse(RegNo: VZero)
3578 .addUse(RegNo: VOne)
3579 .constrainAllUses(TII, TRI, RBI);
3580 return true;
3581}
3582
3583bool SPIRVInstructionSelector::selectSign(Register ResVReg,
3584 SPIRVTypeInst ResType,
3585 MachineInstr &I) const {
3586 assert(I.getNumOperands() == 3);
3587 assert(I.getOperand(2).isReg());
3588 MachineBasicBlock &BB = *I.getParent();
3589 Register InputRegister = I.getOperand(i: 2).getReg();
3590 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3591 auto &DL = I.getDebugLoc();
3592
3593 if (!InputType)
3594 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3595
3596 bool IsFloatTy = GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3597
3598 unsigned SignBitWidth = GR.getScalarOrVectorBitWidth(Type: InputType);
3599 unsigned ResBitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
3600
3601 bool NeedsConversion = IsFloatTy || SignBitWidth != ResBitWidth;
3602
3603 auto SignOpcode = IsFloatTy ? GL::FSign : GL::SSign;
3604 Register SignReg = NeedsConversion
3605 ? MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass)
3606 : ResVReg;
3607
3608 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpExtInst))
3609 .addDef(RegNo: SignReg)
3610 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: InputType))
3611 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
3612 .addImm(Val: SignOpcode)
3613 .addUse(RegNo: InputRegister)
3614 .constrainAllUses(TII, TRI, RBI);
3615
3616 if (NeedsConversion) {
3617 auto ConvertOpcode = IsFloatTy ? SPIRV::OpConvertFToS : SPIRV::OpSConvert;
3618 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: ConvertOpcode))
3619 .addDef(RegNo: ResVReg)
3620 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3621 .addUse(RegNo: SignReg)
3622 .constrainAllUses(TII, TRI, RBI);
3623 }
3624
3625 return true;
3626}
3627
3628bool SPIRVInstructionSelector::selectWaveOpInst(Register ResVReg,
3629 SPIRVTypeInst ResType,
3630 MachineInstr &I,
3631 unsigned Opcode) const {
3632 MachineBasicBlock &BB = *I.getParent();
3633 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3634
3635 auto BMI = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
3636 .addDef(RegNo: ResVReg)
3637 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3638 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I,
3639 SpvType: IntTy, TII, ZeroAsNull: !STI.isShader()));
3640
3641 for (unsigned J = 2; J < I.getNumOperands(); J++) {
3642 BMI.addUse(RegNo: I.getOperand(i: J).getReg());
3643 }
3644
3645 BMI.constrainAllUses(TII, TRI, RBI);
3646 return true;
3647}
3648
3649bool SPIRVInstructionSelector::selectBarrierInst(MachineInstr &I,
3650 unsigned Scope,
3651 unsigned MemSem,
3652 bool WithGroupSync) const {
3653 auto BarrierType =
3654 WithGroupSync ? SPIRV::OpControlBarrier : SPIRV::OpMemoryBarrier;
3655
3656 MemSem |= SPIRV::MemorySemantics::AcquireRelease;
3657
3658 assert(((Scope != SPIRV::Scope::Workgroup) ||
3659 ((MemSem & SPIRV::MemorySemantics::WorkgroupMemory) > 0)) &&
3660 "Workgroup Scope must set WorkGroupMemory semantic "
3661 "in Barrier instruction");
3662
3663 assert(((Scope != SPIRV::Scope::Device) ||
3664 ((MemSem & SPIRV::MemorySemantics::UniformMemory) > 0 &&
3665 (MemSem & SPIRV::MemorySemantics::ImageMemory) > 0)) &&
3666 "Device Scope must set UniformMemory and ImageMemory semantic "
3667 "in Barrier instruction");
3668
3669 MachineBasicBlock &BB = *I.getParent();
3670 auto MI = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: BarrierType));
3671
3672 // OpControlBarrier needs to also set Execution Scope
3673 if (WithGroupSync) {
3674 Register ExecReg = buildI32Constant(Val: SPIRV::Scope::Workgroup, I);
3675 MI.addUse(RegNo: ExecReg);
3676 }
3677
3678 Register ScopeReg = buildI32Constant(Val: Scope, I);
3679 Register MemSemReg = buildI32Constant(Val: MemSem, I);
3680
3681 MI.addUse(RegNo: ScopeReg).addUse(RegNo: MemSemReg).constrainAllUses(TII, TRI, RBI);
3682 return true;
3683}
3684
3685bool SPIRVInstructionSelector::selectWaveActiveCountBits(
3686 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3687
3688 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3689 SPIRVTypeInst BallotType = GR.getOrCreateSPIRVVectorType(BaseType: IntTy, NumElements: 4, I, TII);
3690 Register BallotReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BallotType));
3691 if (!selectWaveOpInst(ResVReg: BallotReg, ResType: BallotType, I,
3692 Opcode: SPIRV::OpGroupNonUniformBallot))
3693 return false;
3694
3695 MachineBasicBlock &BB = *I.getParent();
3696 BuildMI(BB, I, MIMD: I.getDebugLoc(),
3697 MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformBallotBitCount))
3698 .addDef(RegNo: ResVReg)
3699 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3700 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3701 ZeroAsNull: !STI.isShader()))
3702 .addImm(Val: SPIRV::GroupOperation::Reduce)
3703 .addUse(RegNo: BallotReg)
3704 .constrainAllUses(TII, TRI, RBI);
3705
3706 return true;
3707}
3708
3709bool SPIRVInstructionSelector::selectWaveActiveAllEqual(Register ResVReg,
3710 SPIRVTypeInst ResType,
3711 MachineInstr &I) const {
3712 MachineBasicBlock &BB = *I.getParent();
3713 const DebugLoc &DL = I.getDebugLoc();
3714
3715 // Input to the intrinsic
3716 Register InputReg = I.getOperand(i: 2).getReg();
3717 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputReg);
3718
3719 // Determine if input is vector
3720 unsigned NumElems = GR.getScalarOrVectorComponentCount(Type: InputType);
3721 bool IsVector = NumElems > 1 ||
3722 (InputType->getOpcode() == SPIRV::OpTypeVectorIdEXT &&
3723 STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector));
3724
3725 // Determine element types
3726 SPIRVTypeInst ElemInputType = GR.getScalarOrVectorComponentType(Type: InputType);
3727 SPIRVTypeInst ElemBoolType = GR.getScalarOrVectorComponentType(Type: ResType);
3728
3729 // Subgroup scope constant
3730 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3731 Register ScopeConst = GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy,
3732 TII, ZeroAsNull: !STI.isShader());
3733
3734 // Scalar case
3735 if (!IsVector) {
3736 return selectWaveOpInst(ResVReg, ResType: ElemBoolType, I,
3737 Opcode: SPIRV::OpGroupNonUniformAllEqual);
3738 }
3739
3740 // Vector case
3741 SmallVector<Register, 4> ElementResults;
3742 ElementResults.reserve(N: NumElems);
3743
3744 for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
3745 // Extract element
3746 Register ElemInput = InputReg;
3747 Register Extracted =
3748 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ElemInputType));
3749
3750 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
3751 .addDef(RegNo: Extracted)
3752 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ElemInputType))
3753 .addUse(RegNo: InputReg)
3754 .addImm(Val: Idx)
3755 .constrainAllUses(TII, TRI, RBI);
3756
3757 ElemInput = Extracted;
3758
3759 // Emit per-element AllEqual
3760 Register ElemResult =
3761 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ElemBoolType));
3762
3763 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformAllEqual))
3764 .addDef(RegNo: ElemResult)
3765 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ElemBoolType))
3766 .addUse(RegNo: ScopeConst)
3767 .addUse(RegNo: ElemInput)
3768 .constrainAllUses(TII, TRI, RBI);
3769
3770 ElementResults.push_back(Elt: ElemResult);
3771 }
3772
3773 // Reconstruct vector<bool>
3774 auto MIB = BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
3775 .addDef(RegNo: ResVReg)
3776 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
3777 for (Register R : ElementResults)
3778 MIB.addUse(RegNo: R);
3779
3780 MIB.constrainAllUses(TII, TRI, RBI);
3781
3782 return true;
3783}
3784
3785bool SPIRVInstructionSelector::selectWavePrefixBitCount(Register ResVReg,
3786 SPIRVTypeInst ResType,
3787 MachineInstr &I) const {
3788
3789 assert(I.getNumOperands() == 3);
3790
3791 auto Op = I.getOperand(i: 2);
3792 assert(Op.isReg());
3793
3794 MachineBasicBlock &BB = *I.getParent();
3795 DebugLoc DL = I.getDebugLoc();
3796
3797 Register InputRegister = Op.getReg();
3798 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3799
3800 if (!InputType)
3801 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3802
3803 if (InputType->getOpcode() != SPIRV::OpTypeBool)
3804 return diagnoseUnsupported(I, Msg: "WavePrefixBitCount requires boolean input");
3805
3806 // Types
3807 SPIRVTypeInst Int32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3808
3809 // Ballot result type: vector<uint32>
3810 // Match DXC: %v4uint for Subgroup size
3811 SPIRVTypeInst BallotTy = GR.getOrCreateSPIRVVectorType(BaseType: Int32Ty, NumElements: 4, I, TII);
3812
3813 // Create a vreg for the ballot result
3814 Register BallotVReg = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3815
3816 // 1. OpGroupNonUniformBallot
3817 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformBallot))
3818 .addDef(RegNo: BallotVReg)
3819 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: BallotTy))
3820 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: Int32Ty, TII))
3821 .addUse(RegNo: InputRegister)
3822 .constrainAllUses(TII, TRI, RBI);
3823
3824 // 2. OpGroupNonUniformBallotBitCount
3825 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformBallotBitCount))
3826 .addDef(RegNo: ResVReg)
3827 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3828 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: Int32Ty, TII))
3829 .addImm(Val: SPIRV::GroupOperation::ExclusiveScan)
3830 .addUse(RegNo: BallotVReg)
3831 .constrainAllUses(TII, TRI, RBI);
3832
3833 return true;
3834}
3835
3836bool SPIRVInstructionSelector::selectWaveReduceMax(Register ResVReg,
3837 SPIRVTypeInst ResType,
3838 MachineInstr &I,
3839 bool IsUnsigned) const {
3840 return selectWaveReduce(
3841 ResVReg, ResType, I, IsUnsigned,
3842 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3843 const bool IsFloatTy =
3844 GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3845 const auto IntOp = IsUnsigned ? SPIRV::OpGroupNonUniformUMax
3846 : SPIRV::OpGroupNonUniformSMax;
3847 return IsFloatTy ? SPIRV::OpGroupNonUniformFMax : IntOp;
3848 });
3849}
3850
3851bool SPIRVInstructionSelector::selectWaveReduceMin(Register ResVReg,
3852 SPIRVTypeInst ResType,
3853 MachineInstr &I,
3854 bool IsUnsigned) const {
3855 return selectWaveReduce(
3856 ResVReg, ResType, I, IsUnsigned,
3857 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3858 const bool IsFloatTy =
3859 GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3860 const auto IntOp = IsUnsigned ? SPIRV::OpGroupNonUniformUMin
3861 : SPIRV::OpGroupNonUniformSMin;
3862 return IsFloatTy ? SPIRV::OpGroupNonUniformFMin : IntOp;
3863 });
3864}
3865
3866bool SPIRVInstructionSelector::selectWaveReduceSum(Register ResVReg,
3867 SPIRVTypeInst ResType,
3868 MachineInstr &I) const {
3869 return selectWaveReduce(ResVReg, ResType, I, /*IsUnsigned*/ false,
3870 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3871 bool IsFloatTy = GR.isScalarOrVectorOfType(
3872 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3873 return IsFloatTy ? SPIRV::OpGroupNonUniformFAdd
3874 : SPIRV::OpGroupNonUniformIAdd;
3875 });
3876}
3877
3878bool SPIRVInstructionSelector::selectWaveReduceProduct(Register ResVReg,
3879 SPIRVTypeInst ResType,
3880 MachineInstr &I) const {
3881 return selectWaveReduce(ResVReg, ResType, I, /*IsUnsigned*/ false,
3882 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3883 bool IsFloatTy = GR.isScalarOrVectorOfType(
3884 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3885 return IsFloatTy ? SPIRV::OpGroupNonUniformFMul
3886 : SPIRV::OpGroupNonUniformIMul;
3887 });
3888}
3889
3890template <typename PickOpcodeFn>
3891bool SPIRVInstructionSelector::selectWaveReduce(
3892 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, bool IsUnsigned,
3893 PickOpcodeFn &&PickOpcode) const {
3894 assert(I.getNumOperands() == 3);
3895 assert(I.getOperand(2).isReg());
3896 MachineBasicBlock &BB = *I.getParent();
3897 Register InputRegister = I.getOperand(i: 2).getReg();
3898 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3899
3900 if (!InputType)
3901 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3902
3903 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3904 const unsigned Opcode = PickOpcode(InputRegister, IsUnsigned);
3905 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
3906 .addDef(RegNo: ResVReg)
3907 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3908 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3909 ZeroAsNull: !STI.isShader()))
3910 .addImm(Val: SPIRV::GroupOperation::Reduce)
3911 .addUse(RegNo: I.getOperand(i: 2).getReg())
3912 .constrainAllUses(TII, TRI, RBI);
3913 return true;
3914}
3915
3916bool SPIRVInstructionSelector::selectWaveReduceOp(Register ResVReg,
3917 SPIRVTypeInst ResType,
3918 MachineInstr &I,
3919 unsigned Opcode) const {
3920 return selectWaveReduce(
3921 ResVReg, ResType, I, IsUnsigned: false,
3922 PickOpcode: [&](Register InputRegister, bool IsUnsigned) { return Opcode; });
3923}
3924
3925bool SPIRVInstructionSelector::selectWaveExclusiveScanSum(
3926 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3927 return selectWaveExclusiveScan(ResVReg, ResType, I, /*IsUnsigned*/ false,
3928 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3929 bool IsFloatTy = GR.isScalarOrVectorOfType(
3930 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3931 return IsFloatTy
3932 ? SPIRV::OpGroupNonUniformFAdd
3933 : SPIRV::OpGroupNonUniformIAdd;
3934 });
3935}
3936
3937bool SPIRVInstructionSelector::selectWaveExclusiveScanProduct(
3938 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3939 return selectWaveExclusiveScan(ResVReg, ResType, I, /*IsUnsigned*/ false,
3940 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3941 bool IsFloatTy = GR.isScalarOrVectorOfType(
3942 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3943 return IsFloatTy
3944 ? SPIRV::OpGroupNonUniformFMul
3945 : SPIRV::OpGroupNonUniformIMul;
3946 });
3947}
3948
3949template <typename PickOpcodeFn>
3950bool SPIRVInstructionSelector::selectWaveExclusiveScan(
3951 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, bool IsUnsigned,
3952 PickOpcodeFn &&PickOpcode) const {
3953 assert(I.getNumOperands() == 3);
3954 assert(I.getOperand(2).isReg());
3955 MachineBasicBlock &BB = *I.getParent();
3956 Register InputRegister = I.getOperand(i: 2).getReg();
3957 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3958
3959 if (!InputType)
3960 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3961
3962 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3963 const unsigned Opcode = PickOpcode(InputRegister, IsUnsigned);
3964 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
3965 .addDef(RegNo: ResVReg)
3966 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3967 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3968 ZeroAsNull: !STI.isShader()))
3969 .addImm(Val: SPIRV::GroupOperation::ExclusiveScan)
3970 .addUse(RegNo: I.getOperand(i: 2).getReg())
3971 .constrainAllUses(TII, TRI, RBI);
3972 return true;
3973}
3974
3975bool SPIRVInstructionSelector::selectQuadSwap(Register ResVReg,
3976 SPIRVTypeInst ResType,
3977 MachineInstr &I,
3978 unsigned Direction) const {
3979 assert(I.getNumOperands() == 3);
3980 assert(I.getOperand(2).isReg());
3981 MachineBasicBlock &BB = *I.getParent();
3982 Register InputRegister = I.getOperand(i: 2).getReg();
3983
3984 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3985 bool ZeroAsNull = !STI.isShader();
3986 Register DirectionReg =
3987 GR.getOrCreateConstInt(Val: Direction, I, SpvType: IntTy, TII, ZeroAsNull);
3988 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformQuadSwap))
3989 .addDef(RegNo: ResVReg)
3990 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3991 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3992 ZeroAsNull))
3993 .addUse(RegNo: InputRegister)
3994 .addUse(RegNo: DirectionReg)
3995 .constrainAllUses(TII, TRI, RBI);
3996 return true;
3997}
3998
3999bool SPIRVInstructionSelector::selectBitreverseViaI32(Register ResVReg,
4000 SPIRVTypeInst ResType,
4001 MachineInstr &I,
4002 Register Op) const {
4003 SPIRVTypeInst Int32Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
4004 const unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
4005 Register ShiftConst =
4006 GR.getOrCreateConstInt(Val: 32 - BitWidth, I, SpvType: Int32Type, TII);
4007 unsigned ShiftOp = SPIRV::OpShiftRightLogicalS;
4008
4009 const unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
4010 const unsigned ExtendOpcode = GR.isScalarOrVectorSigned(Type: ResType)
4011 ? SPIRV::OpSConvert
4012 : SPIRV::OpUConvert;
4013
4014 if (N > 1 || (ResType->getOpcode() == SPIRV::OpTypeVectorIdEXT &&
4015 STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector))) {
4016 Int32Type = GR.getOrCreateSPIRVVectorType(BaseType: Int32Type, NumElements: N, I, TII);
4017 ShiftOp = SPIRV::OpShiftRightLogicalV;
4018
4019 // Vector shifts require a composite constant
4020 const Register CompositeReg =
4021 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
4022 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4023 MCID: TII.get(Opcode: SPIRV::OpConstantComposite))
4024 .addDef(RegNo: CompositeReg)
4025 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Int32Type));
4026 for (unsigned It = 0; It < N; ++It)
4027 MIB.addUse(RegNo: ShiftConst);
4028 MIB.constrainAllUses(TII, TRI, RBI);
4029
4030 ShiftConst = CompositeReg;
4031 }
4032
4033 // Converts the input to i32 (or vector of i32)
4034 Register ExtReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
4035 if (!selectOpWithSrcs(ResVReg: ExtReg, ResType: Int32Type, I, Srcs: {Op}, Opcode: ExtendOpcode))
4036 return false;
4037
4038 // Perform bitreverse on the i32 value
4039 Register BitrevReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
4040 if (!selectBitreverseNative(ResVReg: BitrevReg, ResType: Int32Type, I, Op: ExtReg))
4041 return false;
4042
4043 // Shift the bit-reversed value to get the final result.
4044 Register ShiftReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
4045 if (!selectOpWithSrcs(ResVReg: ShiftReg, ResType: Int32Type, I, Srcs: {BitrevReg, ShiftConst},
4046 Opcode: ShiftOp))
4047 return false;
4048
4049 // Finally, convert the result back.
4050 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {ShiftReg}, Opcode: ExtendOpcode);
4051}
4052
4053bool SPIRVInstructionSelector::handle64BitOverflow(
4054 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, Register SrcReg,
4055 unsigned int Opcode,
4056 std::function<bool(Register, SPIRVTypeInst, MachineInstr &, Register,
4057 unsigned)>
4058 CallbackFunction) const {
4059
4060 SPIRVTypeInst BaseType = GR.retrieveScalarOrVectorIntType(Type: ResType);
4061 assert(BaseType->getOpcode() == SPIRV::OpTypeInt &&
4062 "handle64BitOverflow should only be used for integer types");
4063 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: ResType);
4064 assert(ComponentCount < 5 && "Vec 5+ will generate invalid SPIR-V ops");
4065
4066 MachineIRBuilder MIRBuilder(I);
4067 SPIRVTypeInst I64Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 64, MIRBuilder);
4068 SPIRVTypeInst I64x2Type =
4069 GR.getOrCreateSPIRVVectorType(BaseType: I64Type, NumElements: 2, MIRBuilder, EmitIR: false);
4070 SPIRVTypeInst Vec2ResType =
4071 GR.getOrCreateSPIRVVectorType(BaseType, NumElements: 2, MIRBuilder, EmitIR: false);
4072
4073 std::vector<Register> PartialRegs;
4074
4075 unsigned CurrentComponent = 0;
4076 for (; CurrentComponent + 1 < ComponentCount; CurrentComponent += 2) {
4077 Register PopCountResult =
4078 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: I64x2Type));
4079
4080 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4081 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
4082 .addDef(RegNo: PopCountResult)
4083 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: I64x2Type))
4084 .addUse(RegNo: SrcReg)
4085 .addUse(RegNo: SrcReg)
4086 .addImm(Val: CurrentComponent)
4087 .addImm(Val: CurrentComponent + 1);
4088
4089 MIB.constrainAllUses(TII, TRI, RBI);
4090
4091 Register SubVecReg =
4092 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Vec2ResType));
4093
4094 if (!CallbackFunction(SubVecReg, Vec2ResType, I, PopCountResult, Opcode))
4095 return false;
4096
4097 PartialRegs.push_back(x: SubVecReg);
4098 }
4099 // On odd component counts we need to handle one more component
4100 if (CurrentComponent != ComponentCount) {
4101 bool ZeroAsNull = !STI.isShader();
4102 Register FinalElemReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: I64Type));
4103 Register ConstIntLastIdx = GR.getOrCreateConstInt(
4104 Val: ComponentCount - 1, I, SpvType: BaseType, TII, ZeroAsNull);
4105
4106 if (!selectOpWithSrcs(ResVReg: FinalElemReg, ResType: I64Type, I, Srcs: {SrcReg, ConstIntLastIdx},
4107 Opcode: SPIRV::OpVectorExtractDynamic))
4108 return false;
4109
4110 Register FinalElemResReg =
4111 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BaseType));
4112
4113 if (!CallbackFunction(FinalElemResReg, BaseType, I, FinalElemReg, Opcode))
4114 return false;
4115
4116 PartialRegs.push_back(x: FinalElemResReg);
4117 }
4118 // Join all the resulting registers back into the return type in order
4119 // (ie i32x2, i32x2, i32x1 -> i32x5)
4120 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: PartialRegs,
4121 Opcode: SPIRV::OpCompositeConstruct);
4122}
4123
4124bool SPIRVInstructionSelector::selectBitreverse64(Register ResVReg,
4125 SPIRVTypeInst ResType,
4126 MachineInstr &I,
4127 Register SrcReg) const {
4128 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: ResType);
4129 if (ComponentCount > 2)
4130 return handle64BitOverflow(
4131 ResVReg, ResType, I, SrcReg, Opcode: SPIRV::OpBitReverse,
4132 CallbackFunction: [this](Register R, SPIRVTypeInst T, MachineInstr &I, Register S,
4133 unsigned O) { return this->selectBitreverse64(ResVReg: R, ResType: T, I, SrcReg: S); });
4134
4135 MachineIRBuilder MIRBuilder(I);
4136
4137 SPIRVTypeInst I32Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, MIRBuilder);
4138 SPIRVTypeInst VecI32Type = GR.getOrCreateSPIRVVectorType(
4139 BaseType: I32Type, NumElements: 2 * ComponentCount, MIRBuilder, /*IsSigned=*/EmitIR: false);
4140
4141 // Converts 64 bit into and array of 32 bit, containing 2 elements.
4142 Register Vec32 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: VecI32Type));
4143 if (!selectOpWithSrcs(ResVReg: Vec32, ResType: VecI32Type, I, Srcs: {SrcReg}, Opcode: SPIRV::OpBitcast))
4144 return false;
4145
4146 // Apply bitreverse on each 32 bit lane
4147 Register Reverse32 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: VecI32Type));
4148 if (!selectBitreverseNative(ResVReg: Reverse32, ResType: VecI32Type, I, Op: Vec32))
4149 return false;
4150
4151 // Reversing a 64-bit value = reverse each 32-bit half AND swap them,
4152 // so the old High word becomes lane 0 (low) and old Low becomes lane 1
4153 // (high).
4154 Register SwappedVec = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: VecI32Type));
4155 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4156 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
4157 .addDef(RegNo: SwappedVec)
4158 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: VecI32Type))
4159 .addUse(RegNo: Reverse32)
4160 .addUse(RegNo: Reverse32);
4161 for (unsigned J = 0; J < ComponentCount; ++J) {
4162 MIB.addImm(Val: 2 * J + 1);
4163 MIB.addImm(Val: 2 * J);
4164 }
4165 MIB.constrainAllUses(TII, TRI, RBI);
4166
4167 // Groups 32 bit vector back to 64 bit scalar.
4168 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {SwappedVec}, Opcode: SPIRV::OpBitcast);
4169}
4170
4171bool SPIRVInstructionSelector::selectBitreverseNative(Register ResVReg,
4172 SPIRVTypeInst ResType,
4173 MachineInstr &I,
4174 Register Op) const {
4175 MachineBasicBlock &BB = *I.getParent();
4176 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBitReverse))
4177 .addDef(RegNo: ResVReg)
4178 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4179 .addUse(RegNo: Op)
4180 .constrainAllUses(TII, TRI, RBI);
4181 return true;
4182}
4183
4184bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg,
4185 SPIRVTypeInst ResType,
4186 MachineInstr &I) const {
4187 Register OpReg = I.getOperand(i: 1).getReg();
4188
4189 // TODO: Fix shader behavior in case of VK_KHR_maintenance9 extension is set
4190 if (STI.isShader()) {
4191 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
4192 switch (GR.getScalarOrVectorBitWidth(Type: OpType)) {
4193 case 8:
4194 case 16:
4195 case 24:
4196 return selectBitreverseViaI32(ResVReg, ResType, I, Op: OpReg);
4197 case 32:
4198 return selectBitreverseNative(ResVReg, ResType, I, Op: OpReg);
4199 case 64:
4200 return selectBitreverse64(ResVReg, ResType, I, SrcReg: OpReg);
4201 }
4202 return SPIRVInstructionSelector::diagnoseUnsupported(
4203 I, Msg: "G_BITREVERSE only support 16,32,64 bits.");
4204 }
4205
4206 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_bit_instructions))
4207 return selectBitreverseNative(ResVReg, ResType, I, Op: OpReg);
4208
4209 // Expansion bitreverse using bit manipulation operations
4210 // Algo: https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
4211 const unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
4212 // TODO: add support for any bit width and bitwidth more than 64.
4213 if (BitWidth > 64 || !isPowerOf2_32(Value: BitWidth))
4214 return false;
4215
4216 const unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
4217
4218 unsigned AndOp = SPIRV::OpBitwiseAndS;
4219 unsigned OrOp = SPIRV::OpBitwiseOrS;
4220 unsigned ShlOp = SPIRV::OpShiftLeftLogicalS;
4221 unsigned ShrOp = SPIRV::OpShiftRightLogicalS;
4222 if (N > 1 || (ResType->getOpcode() == SPIRV::OpTypeVectorIdEXT &&
4223 STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector))) {
4224 AndOp = SPIRV::OpBitwiseAndV;
4225 OrOp = SPIRV::OpBitwiseOrV;
4226 ShlOp = SPIRV::OpShiftLeftLogicalV;
4227 ShrOp = SPIRV::OpShiftRightLogicalV;
4228 }
4229
4230 // Helper, one swap per step: ((input>>shift)&mask)|((input&mask)<<shift),
4231 // RPN: input shift >> mask & input mask & shift << |
4232 auto SwapBits = [&](const Register Input, const uint64_t Mask,
4233 const unsigned Shift) -> Register {
4234 auto CreateConst = [&](const uint64_t Value) -> Register {
4235 if (N == 1 &&
4236 (ResType->getOpcode() != SPIRV::OpTypeVectorIdEXT ||
4237 !STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector)))
4238 return GR.getOrCreateConstInt(
4239 Val: Value, I, SpvType: GR.retrieveScalarOrVectorIntType(Type: ResType), TII);
4240 return GR.getOrCreateConstVector(Val: Value, I, SpvType: ResType, TII);
4241 };
4242
4243 Register MaskReg = CreateConst(Mask);
4244 Register ShiftReg = CreateConst(Shift);
4245 Register T1 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4246 Register T2 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4247 Register T3 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4248 Register T4 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4249 Register Result = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4250
4251 if (!selectOpWithSrcs(ResVReg: T1, ResType, I, Srcs: {Input, ShiftReg}, Opcode: ShrOp) ||
4252 !selectOpWithSrcs(ResVReg: T2, ResType, I, Srcs: {T1, MaskReg}, Opcode: AndOp) ||
4253 !selectOpWithSrcs(ResVReg: T3, ResType, I, Srcs: {Input, MaskReg}, Opcode: AndOp) ||
4254 !selectOpWithSrcs(ResVReg: T4, ResType, I, Srcs: {T3, ShiftReg}, Opcode: ShlOp) ||
4255 !selectOpWithSrcs(ResVReg: Result, ResType, I, Srcs: {T2, T4}, Opcode: OrOp))
4256 return Register();
4257
4258 return Result;
4259 };
4260
4261 unsigned Shift = BitWidth;
4262 Register Result = OpReg;
4263 uint64_t Mask = ~0ull;
4264 while ((Shift >>= 1) > 0) {
4265 Mask ^= (Mask << Shift);
4266 Result = SwapBits(Result, Mask, Shift);
4267 if (!Result.isValid())
4268 return false;
4269 }
4270
4271 return BuildCOPY(DestReg: ResVReg, SrcReg: Result, I);
4272}
4273
4274bool SPIRVInstructionSelector::selectFreeze(Register ResVReg,
4275 SPIRVTypeInst ResType,
4276 MachineInstr &I) const {
4277 assert(I.getOperand(0).isReg() && I.getOperand(1).isReg() &&
4278 "G_FREEZE must define and use a register");
4279 Register OpReg = I.getOperand(i: 1).getReg();
4280
4281 // With SPV_KHR_poison_freeze, lower `freeze` to OpFreezeKHR.
4282 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_poison_freeze)) {
4283 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFreezeKHR))
4284 .addDef(RegNo: ResVReg)
4285 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4286 .addUse(RegNo: OpReg)
4287 .constrainAllUses(TII, TRI, RBI);
4288 return true;
4289 }
4290
4291 // There is no way to implement `freeze` correctly without support on SPIR-V
4292 // standard side, but we may at least address a simple (static) case when
4293 // undef/poison value presence is obvious. The main benefit of even
4294 // incomplete `freeze` support is preventing of translation from crashing due
4295 // to lack of support on legalization and instruction selection steps.
4296 if (MachineInstr *Def = MRI->getVRegDef(Reg: OpReg)) {
4297 if (Def->getOpcode() == TargetOpcode::COPY)
4298 Def = MRI->getVRegDef(Reg: Def->getOperand(i: 1).getReg());
4299 Register Reg;
4300 switch (Def->getOpcode()) {
4301 case SPIRV::ASSIGN_TYPE:
4302 if (MachineInstr *AssignToDef =
4303 MRI->getVRegDef(Reg: Def->getOperand(i: 1).getReg())) {
4304 if (AssignToDef->getOpcode() == TargetOpcode::G_IMPLICIT_DEF)
4305 Reg = Def->getOperand(i: 2).getReg();
4306 }
4307 break;
4308 case SPIRV::OpUndef:
4309 Reg = Def->getOperand(i: 1).getReg();
4310 break;
4311 }
4312 unsigned DestOpCode;
4313 if (Reg.isValid()) {
4314 DestOpCode = SPIRV::OpConstantNull;
4315 LLVM_DEBUG(dbgs() << "SPV_KHR_poison_freeze is not enabled. freeze of a "
4316 "static undef/poison lowered to OpConstantNull\n");
4317 } else {
4318 DestOpCode = TargetOpcode::COPY;
4319 Reg = OpReg;
4320 LLVM_DEBUG(dbgs() << "SPV_KHR_poison_freeze is not enabled. freeze "
4321 "skipped, lowered as a copy of the operand\n");
4322 }
4323 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DestOpCode))
4324 .addDef(RegNo: I.getOperand(i: 0).getReg())
4325 .addUse(RegNo: Reg)
4326 .constrainAllUses(TII, TRI, RBI);
4327 return true;
4328 }
4329 return false;
4330}
4331
4332bool SPIRVInstructionSelector::selectBuildVector(Register ResVReg,
4333 SPIRVTypeInst ResType,
4334 MachineInstr &I) const {
4335 unsigned N = 0;
4336 if (isVectorType(SPVTy: ResType))
4337 N = GR.getScalarOrVectorComponentCount(Type: ResType);
4338 else if (ResType->getOpcode() == SPIRV::OpTypeArray)
4339 N = getArrayComponentCount(MRI, ResType);
4340 else
4341 report_fatal_error(reason: "Cannot select G_BUILD_VECTOR with a non-vector result");
4342 if (I.getNumExplicitOperands() - I.getNumExplicitDefs() != N)
4343 report_fatal_error(reason: "G_BUILD_VECTOR and the result type are inconsistent");
4344
4345 // check if we may construct a constant vector
4346 bool IsConst = true;
4347 for (unsigned i = I.getNumExplicitDefs();
4348 i < I.getNumExplicitOperands() && IsConst; ++i)
4349 if (!isConstReg(MRI, OpReg: I.getOperand(i).getReg()))
4350 IsConst = false;
4351
4352 if (!IsConst && (N < 2 && ResType->getOpcode() != SPIRV::OpTypeVectorIdEXT))
4353 return diagnoseUnsupported(
4354 I, Msg: "There must be at least two constituent operands in a vector");
4355
4356 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
4357
4358 bool IsNullVector = IsConst && !STI.isShader();
4359 for (unsigned i = I.getNumExplicitDefs();
4360 i < I.getNumExplicitOperands() && IsNullVector; ++i) {
4361 MachineInstr *Def = getDef(MO: I.getOperand(i), MRI);
4362 IsNullVector = Def && isNullOrNullSplat(MI: *Def, MRI: *MRI);
4363 }
4364
4365 if (IsNullVector) {
4366 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
4367 .addDef(RegNo: ResVReg)
4368 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4369 .constrainAllUses(TII, TRI, RBI);
4370 return true;
4371 }
4372
4373 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4374 MCID: TII.get(Opcode: IsConst ? SPIRV::OpConstantComposite
4375 : SPIRV::OpCompositeConstruct))
4376 .addDef(RegNo: ResVReg)
4377 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
4378 for (unsigned i = I.getNumExplicitDefs(); i < I.getNumExplicitOperands(); ++i)
4379 MIB.addUse(RegNo: I.getOperand(i).getReg());
4380 MIB.constrainAllUses(TII, TRI, RBI);
4381 return true;
4382}
4383
4384bool SPIRVInstructionSelector::selectSplatVector(Register ResVReg,
4385 SPIRVTypeInst ResType,
4386 MachineInstr &I) const {
4387 unsigned N = 0;
4388 if (isVectorType(SPVTy: ResType))
4389 N = GR.getScalarOrVectorComponentCount(Type: ResType);
4390 else if (ResType->getOpcode() == SPIRV::OpTypeArray)
4391 N = getArrayComponentCount(MRI, ResType);
4392 else
4393 report_fatal_error(reason: "Cannot select G_SPLAT_VECTOR with a non-vector result");
4394
4395 unsigned OpIdx = I.getNumExplicitDefs();
4396 if (!I.getOperand(i: OpIdx).isReg())
4397 report_fatal_error(reason: "Unexpected argument in G_SPLAT_VECTOR");
4398
4399 // check if we may construct a constant vector
4400 Register OpReg = I.getOperand(i: OpIdx).getReg();
4401 bool IsConst = isConstReg(MRI, OpReg);
4402
4403 if (!IsConst && (N < 2 && ResType->getOpcode() != SPIRV::OpTypeVectorIdEXT))
4404 return diagnoseUnsupported(
4405 I, Msg: "There must be at least two constituent operands in a vector");
4406
4407 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
4408 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4409 MCID: TII.get(Opcode: IsConst ? SPIRV::OpConstantComposite
4410 : SPIRV::OpCompositeConstruct))
4411 .addDef(RegNo: ResVReg)
4412 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
4413 for (unsigned i = 0; i < N; ++i)
4414 MIB.addUse(RegNo: OpReg);
4415 MIB.constrainAllUses(TII, TRI, RBI);
4416 return true;
4417}
4418
4419bool SPIRVInstructionSelector::selectConcatVectors(Register ResVReg,
4420 SPIRVTypeInst ResType,
4421 MachineInstr &I) const {
4422 // Implement G_CONCAT_VECTORS using OpCompositeConstruct, which allows vector
4423 // constituents that share the result's component type to be
4424 // concatenated in operand order.
4425 if (!isVectorType(SPVTy: ResType))
4426 report_fatal_error(
4427 reason: "Cannot select G_CONCAT_VECTORS with a non-vector result");
4428
4429 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4430 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
4431 .addDef(RegNo: ResVReg)
4432 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
4433 for (unsigned OpIdx = I.getNumExplicitDefs();
4434 OpIdx < I.getNumExplicitOperands(); ++OpIdx)
4435 MIB.addUse(RegNo: I.getOperand(i: OpIdx).getReg());
4436 MIB.constrainAllUses(TII, TRI, RBI);
4437 return true;
4438}
4439
4440bool SPIRVInstructionSelector::selectDiscard(Register ResVReg,
4441 SPIRVTypeInst ResType,
4442 MachineInstr &I) const {
4443
4444 unsigned Opcode;
4445 MachineBasicBlock &BB = *I.getParent();
4446
4447 if (STI.canUseExtension(
4448 E: SPIRV::Extension::SPV_EXT_demote_to_helper_invocation) ||
4449 STI.isAtLeastSPIRVVer(VerToCompareTo: llvm::VersionTuple(1, 6))) {
4450 Opcode = SPIRV::OpDemoteToHelperInvocation;
4451 } else {
4452 Opcode = SPIRV::OpKill;
4453 // OpKill must be the last operation of any basic block.
4454 for (MachineInstr &ToErase : make_early_inc_range(
4455 Range: make_range(x: std::next(x: I.getIterator()), y: BB.instr_end()))) {
4456 GR.invalidateMachineInstr(MI: &ToErase);
4457 ToErase.eraseFromParent();
4458 }
4459 }
4460
4461 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
4462 .constrainAllUses(TII, TRI, RBI);
4463 return true;
4464}
4465
4466bool SPIRVInstructionSelector::selectCmp(Register ResVReg,
4467 SPIRVTypeInst ResType, unsigned CmpOpc,
4468 MachineInstr &I) const {
4469 Register Cmp0 = I.getOperand(i: 2).getReg();
4470 Register Cmp1 = I.getOperand(i: 3).getReg();
4471 assert(GR.getSPIRVTypeForVReg(Cmp0)->getOpcode() ==
4472 GR.getSPIRVTypeForVReg(Cmp1)->getOpcode() &&
4473 "CMP operands should have the same type");
4474 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: CmpOpc))
4475 .addDef(RegNo: ResVReg)
4476 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4477 .addUse(RegNo: Cmp0)
4478 .addUse(RegNo: Cmp1)
4479 .setMIFlags(I.getFlags())
4480 .constrainAllUses(TII, TRI, RBI);
4481 return true;
4482}
4483
4484bool SPIRVInstructionSelector::selectICmp(Register ResVReg,
4485 SPIRVTypeInst ResType,
4486 MachineInstr &I) const {
4487 auto Pred = I.getOperand(i: 1).getPredicate();
4488 unsigned CmpOpc;
4489
4490 Register CmpOperand = I.getOperand(i: 2).getReg();
4491 SPIRVTypeInst CmpOperandType = GR.getSPIRVTypeForVReg(VReg: CmpOperand);
4492 bool IsPtrCmp = CmpOperandType && CmpOperandType.isPointer();
4493 if (IsPtrCmp) {
4494 CmpOpc = getPtrCmpOpcode(Pred);
4495 // OpPtrEqual/OpPtrNotEqual require both operands to share an identical
4496 // pointer type. If they are not OpBitcast is inserted.
4497 Register Op1 = I.getOperand(i: 3).getReg();
4498 SPIRVTypeInst Ty0 = GR.getSPIRVTypeForVReg(VReg: CmpOperand);
4499 if (Ty0 != GR.getSPIRVTypeForVReg(VReg: Op1)) {
4500 Register NewOp1 = createVirtualRegister(SpvType: Ty0, GR: &GR, MRI, MF: MRI->getMF());
4501 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBitcast))
4502 .addDef(RegNo: NewOp1)
4503 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Ty0))
4504 .addUse(RegNo: Op1)
4505 .constrainAllUses(TII, TRI, RBI);
4506 I.getOperand(i: 3).setReg(NewOp1);
4507 }
4508 } else if (GR.isScalarOrVectorOfType(VReg: CmpOperand, TypeOpcode: SPIRV::OpTypeBool))
4509 CmpOpc = getBoolCmpOpcode(PredNum: Pred);
4510 else
4511 CmpOpc = getICmpOpcode(PredNum: Pred);
4512 return selectCmp(ResVReg, ResType, CmpOpc, I);
4513}
4514
4515Register
4516SPIRVInstructionSelector::buildI32Constant(uint32_t Val, MachineInstr &I,
4517 SPIRVTypeInst ResType) const {
4518 Type *LLVMTy = IntegerType::get(C&: GR.CurMF->getFunction().getContext(), NumBits: 32);
4519 SPIRVTypeInst SpvI32Ty =
4520 ResType ? ResType : GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
4521 // Find a constant in DT or build a new one.
4522 auto ConstInt = ConstantInt::get(Ty: LLVMTy, V: Val);
4523 Register NewReg = GR.find(V: ConstInt, MF: GR.CurMF);
4524 if (!NewReg.isValid()) {
4525 NewReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
4526 MachineBasicBlock &BB = *I.getParent();
4527 MachineInstr *MI =
4528 Val == 0
4529 ? BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
4530 .addDef(RegNo: NewReg)
4531 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvI32Ty))
4532 : BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantI))
4533 .addDef(RegNo: NewReg)
4534 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvI32Ty))
4535 .addImm(Val: APInt(32, Val).getZExtValue());
4536 constrainSelectedInstRegOperands(I&: *MI, TII, TRI, RBI);
4537 GR.add(V: ConstInt, MI);
4538 }
4539 return NewReg;
4540}
4541
4542// Like buildI32Constant, but always inserts the constant definition in the
4543// entry block so it dominates all uses regardless of block ordering.
4544Register SPIRVInstructionSelector::buildI32ConstantInEntryBlock(
4545 uint32_t Val, MachineInstr &I, SPIRVTypeInst ResType) const {
4546 Type *LLVMTy = IntegerType::get(C&: GR.CurMF->getFunction().getContext(), NumBits: 32);
4547 SPIRVTypeInst SpvI32Ty =
4548 ResType ? ResType : GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
4549 auto *ConstInt = ConstantInt::get(Ty: LLVMTy, V: Val);
4550 Register NewReg = GR.find(V: ConstInt, MF: GR.CurMF);
4551 if (!NewReg.isValid()) {
4552 NewReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
4553 auto InsertIt = getOpVariableMBBIt(MF&: *I.getMF());
4554 MachineBasicBlock &EntryBB = *InsertIt->getParent();
4555 MachineInstr *MI = nullptr;
4556 Register TypeReg = GR.getSPIRVTypeID(SpirvType: SpvI32Ty);
4557 DebugLoc DbgLoc = I.getDebugLoc();
4558 if (Val == 0) {
4559 MI = BuildMI(BB&: EntryBB, I: InsertIt, MIMD: DbgLoc, MCID: TII.get(Opcode: SPIRV::OpConstantNull))
4560 .addDef(RegNo: NewReg)
4561 .addUse(RegNo: TypeReg);
4562 } else {
4563 uint64_t ImmVal = APInt(32, Val).getZExtValue();
4564 MI = BuildMI(BB&: EntryBB, I: InsertIt, MIMD: DbgLoc, MCID: TII.get(Opcode: SPIRV::OpConstantI))
4565 .addDef(RegNo: NewReg)
4566 .addUse(RegNo: TypeReg)
4567 .addImm(Val: ImmVal);
4568 }
4569 constrainSelectedInstRegOperands(I&: *MI, TII, TRI, RBI);
4570 GR.add(V: ConstInt, MI);
4571 }
4572 return NewReg;
4573}
4574
4575bool SPIRVInstructionSelector::selectFCmp(Register ResVReg,
4576 SPIRVTypeInst ResType,
4577 MachineInstr &I) const {
4578 unsigned CmpOp = getFCmpOpcode(PredNum: I.getOperand(i: 1).getPredicate());
4579 return selectCmp(ResVReg, ResType, CmpOpc: CmpOp, I);
4580}
4581
4582bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
4583 SPIRVTypeInst ResType,
4584 MachineInstr &I) const {
4585 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
4586 return selectExtInst(ResVReg, ResType, I, CLInst: CL::exp10);
4587 }
4588
4589 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::GLSL_std_450)) {
4590 /// There is no exp10 in GLSL. Use exp10(x) = exp2(x * log2(10)) instead
4591 /// log2(10) ~= 3.3219280948874l
4592
4593 if (!isVectorType(SPVTy: ResType) && !ResType.isAnyTypeFloat())
4594 return false;
4595
4596 MachineIRBuilder MIRBuilder(I);
4597
4598 SPIRVTypeInst SpirvScalarType = GR.getScalarOrVectorComponentType(Type: ResType);
4599
4600 // Match the literal precision to the scalar type so the OpConstant
4601 // literal does not contain non-zero high-order bits that would fail
4602 // SPIR-V validation when the type is narrower than 32 bits (e.g. half).
4603 APFloat ConstVal(3.3219280948873623);
4604 bool LosesInfo;
4605 ConstVal.convert(
4606 ToSemantics: getZeroFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: SpirvScalarType)).getSemantics(),
4607 RM: APFloat::rmNearestTiesToEven, losesInfo: &LosesInfo);
4608 Register ConstReg =
4609 GR.buildConstantFP(Val: ConstVal, MIRBuilder, SpvType: SpirvScalarType);
4610 Register ArgReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4611 auto Opcode =
4612 isVectorType(SPVTy: ResType) ? SPIRV::OpVectorTimesScalar : SPIRV::OpFMulS;
4613
4614 if (!selectOpWithSrcs(ResVReg: ArgReg, ResType, I,
4615 Srcs: {I.getOperand(i: 1).getReg(), ConstReg}, Opcode))
4616 return false;
4617 if (!selectExtInst(ResVReg, ResType, I,
4618 Insts: {{SPIRV::InstructionSet::GLSL_std_450, GL::Exp2}}, setMIFlags: false,
4619 useMISrc: false, SrcRegs: {ArgReg}))
4620 return false;
4621
4622 return true;
4623 }
4624
4625 return false;
4626}
4627
4628Register SPIRVInstructionSelector::buildZerosVal(SPIRVTypeInst ResType,
4629 MachineInstr &I) const {
4630 // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
4631 bool ZeroAsNull = !STI.isShader();
4632 if (isVectorType(SPVTy: ResType))
4633 return GR.getOrCreateConstVector(Val: 0UL, I, SpvType: ResType, TII, ZeroAsNull);
4634 return GR.getOrCreateConstInt(Val: 0, I, SpvType: ResType, TII, ZeroAsNull);
4635}
4636
4637bool SPIRVInstructionSelector::isScalarOrVectorIntConstantZero(
4638 Register Reg) const {
4639 SPIRVTypeInst Type = GR.getSPIRVTypeForVReg(VReg: Reg);
4640 if (!Type)
4641 return false;
4642 SPIRVTypeInst CompType = GR.getScalarOrVectorComponentType(Type);
4643 if (!CompType || CompType->getOpcode() != SPIRV::OpTypeInt)
4644 return false;
4645
4646 auto IsZero = [this](Register Reg) {
4647 MachineInstr *Def = getDefInstrMaybeConstant(ConstReg&: Reg, MRI);
4648 if (!Def)
4649 return false;
4650
4651 if (Def->getOpcode() == SPIRV::OpConstantNull)
4652 return true;
4653
4654 if (Def->getOpcode() == TargetOpcode::G_CONSTANT ||
4655 Def->getOpcode() == SPIRV::OpConstantI)
4656 return getIConstVal(ConstReg: Reg, MRI) == 0;
4657
4658 return false;
4659 };
4660
4661 if (IsZero(Reg))
4662 return true;
4663
4664 MachineInstr *Def = MRI->getVRegDef(Reg);
4665 if (!Def)
4666 return false;
4667
4668 if (Def->getOpcode() == TargetOpcode::G_BUILD_VECTOR ||
4669 (Def->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
4670 cast<GIntrinsic>(Val: Def)->getIntrinsicID() ==
4671 Intrinsic::spv_const_composite)) {
4672 unsigned StartOp = Def->getOpcode() == TargetOpcode::G_BUILD_VECTOR ? 1 : 2;
4673 for (unsigned i = StartOp; i < Def->getNumOperands(); ++i) {
4674 if (!IsZero(Def->getOperand(i).getReg()))
4675 return false;
4676 }
4677 return true;
4678 }
4679
4680 return false;
4681}
4682
4683Register SPIRVInstructionSelector::buildZerosValF(SPIRVTypeInst ResType,
4684 MachineInstr &I) const {
4685 // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
4686 bool ZeroAsNull = !STI.isShader();
4687 APFloat VZero = getZeroFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: ResType));
4688 if (isVectorType(SPVTy: ResType))
4689 return GR.getOrCreateConstVector(Val: VZero, I, SpvType: ResType, TII, ZeroAsNull);
4690 return GR.getOrCreateConstFP(Val: VZero, I, SpvType: ResType, TII, ZeroAsNull);
4691}
4692
4693Register SPIRVInstructionSelector::buildOnesValF(SPIRVTypeInst ResType,
4694 MachineInstr &I) const {
4695 // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
4696 bool ZeroAsNull = !STI.isShader();
4697 APFloat VOne = getOneFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: ResType));
4698 if (isVectorType(SPVTy: ResType))
4699 return GR.getOrCreateConstVector(Val: VOne, I, SpvType: ResType, TII, ZeroAsNull);
4700 return GR.getOrCreateConstFP(Val: VOne, I, SpvType: ResType, TII, ZeroAsNull);
4701}
4702
4703Register SPIRVInstructionSelector::buildOnesVal(bool AllOnes,
4704 SPIRVTypeInst ResType,
4705 MachineInstr &I) const {
4706 unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
4707 APInt One =
4708 AllOnes ? APInt::getAllOnes(numBits: BitWidth) : APInt::getOneBitSet(numBits: BitWidth, BitNo: 0);
4709 if (isVectorType(SPVTy: ResType))
4710 return GR.getOrCreateConstVector(Val: One, I, SpvType: ResType, TII);
4711 return GR.getOrCreateConstInt(Val: One, I, SpvType: ResType, TII);
4712}
4713
4714bool SPIRVInstructionSelector::selectSelect(Register ResVReg,
4715 SPIRVTypeInst ResType,
4716 MachineInstr &I) const {
4717 Register SelectFirstArg = I.getOperand(i: 2).getReg();
4718 Register SelectSecondArg = I.getOperand(i: 3).getReg();
4719 assert(ResType == GR.getSPIRVTypeForVReg(SelectFirstArg) &&
4720 ResType == GR.getSPIRVTypeForVReg(SelectSecondArg));
4721
4722 bool IsFloatTy =
4723 GR.isScalarOrVectorOfType(VReg: SelectFirstArg, TypeOpcode: SPIRV::OpTypeFloat);
4724 bool IsPtrTy =
4725 GR.isScalarOrVectorOfType(VReg: SelectFirstArg, TypeOpcode: SPIRV::OpTypePointer);
4726
4727 bool IsScalarBool =
4728 GR.isScalarOfType(VReg: I.getOperand(i: 1).getReg(), TypeOpcode: SPIRV::OpTypeBool);
4729 unsigned Opcode;
4730 if (isVectorType(SPVTy: GR.getSPIRVTypeForVReg(VReg: SelectFirstArg))) {
4731 if (IsFloatTy) {
4732 Opcode = IsScalarBool ? SPIRV::OpSelectVFSCond : SPIRV::OpSelectVFVCond;
4733 } else if (IsPtrTy) {
4734 Opcode = IsScalarBool ? SPIRV::OpSelectVPSCond : SPIRV::OpSelectVPVCond;
4735 } else {
4736 Opcode = IsScalarBool ? SPIRV::OpSelectVISCond : SPIRV::OpSelectVIVCond;
4737 }
4738 } else {
4739 assert(IsScalarBool && "OpSelect with a scalar result requires a scalar "
4740 "boolean condition");
4741 if (IsFloatTy) {
4742 Opcode = SPIRV::OpSelectSFSCond;
4743 } else if (IsPtrTy) {
4744 Opcode = SPIRV::OpSelectSPSCond;
4745 } else {
4746 Opcode = SPIRV::OpSelectSISCond;
4747 }
4748 }
4749 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
4750 .addDef(RegNo: ResVReg)
4751 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4752 .addUse(RegNo: I.getOperand(i: 1).getReg())
4753 .addUse(RegNo: SelectFirstArg)
4754 .addUse(RegNo: SelectSecondArg)
4755 .constrainAllUses(TII, TRI, RBI);
4756 return true;
4757}
4758
4759// This function is used to extend a bool or a vector of bools into an integer
4760// or vector of integers.
4761bool SPIRVInstructionSelector::selectBoolToInt(Register ResVReg,
4762 SPIRVTypeInst ResType,
4763 Register BooleanVReg,
4764 MachineInstr &InsertAt,
4765 bool IsSigned) const {
4766 // To extend a bool, we need to use OpSelect between constants.
4767 Register ZeroReg = buildZerosVal(ResType, I&: InsertAt);
4768 Register OneReg = buildOnesVal(AllOnes: IsSigned, ResType, I&: InsertAt);
4769 bool IsScalarBool = GR.isScalarOfType(VReg: BooleanVReg, TypeOpcode: SPIRV::OpTypeBool);
4770 unsigned Opcode =
4771 IsScalarBool ? SPIRV::OpSelectSISCond : SPIRV::OpSelectVIVCond;
4772 BuildMI(BB&: *InsertAt.getParent(), I&: InsertAt, MIMD: InsertAt.getDebugLoc(),
4773 MCID: TII.get(Opcode))
4774 .addDef(RegNo: ResVReg)
4775 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4776 .addUse(RegNo: BooleanVReg)
4777 .addUse(RegNo: OneReg)
4778 .addUse(RegNo: ZeroReg)
4779 .constrainAllUses(TII, TRI, RBI);
4780 return true;
4781}
4782
4783bool SPIRVInstructionSelector::selectIToF(Register ResVReg,
4784 SPIRVTypeInst ResType,
4785 MachineInstr &I, bool IsSigned,
4786 unsigned Opcode) const {
4787 Register SrcReg = I.getOperand(i: 1).getReg();
4788 // We can convert bool value directly to float type without OpConvert*ToF,
4789 // however the translator generates OpSelect+OpConvert*ToF, so we do the same.
4790 if (GR.isScalarOrVectorOfType(VReg: I.getOperand(i: 1).getReg(), TypeOpcode: SPIRV::OpTypeBool)) {
4791 unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
4792 SPIRVTypeInst TmpType = GR.getOrCreateSPIRVIntegerType(BitWidth, I, TII);
4793 if (isVectorType(SPVTy: ResType)) {
4794 const unsigned NumElts = GR.getScalarOrVectorComponentCount(Type: ResType);
4795 TmpType = GR.getOrCreateSPIRVVectorType(BaseType: TmpType, NumElements: NumElts, I, TII);
4796 }
4797 SrcReg = createVirtualRegister(SpvType: TmpType, GR: &GR, MRI, MF: MRI->getMF());
4798 selectBoolToInt(ResVReg: SrcReg, ResType: TmpType, BooleanVReg: I.getOperand(i: 1).getReg(), InsertAt&: I, IsSigned);
4799 }
4800 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {SrcReg}, Opcode);
4801}
4802
4803bool SPIRVInstructionSelector::selectExt(Register ResVReg,
4804 SPIRVTypeInst ResType, MachineInstr &I,
4805 bool IsSigned) const {
4806 Register SrcReg = I.getOperand(i: 1).getReg();
4807 if (GR.isScalarOrVectorOfType(VReg: SrcReg, TypeOpcode: SPIRV::OpTypeBool))
4808 return selectBoolToInt(ResVReg, ResType, BooleanVReg: I.getOperand(i: 1).getReg(), InsertAt&: I,
4809 IsSigned);
4810
4811 SPIRVTypeInst SrcType = GR.getSPIRVTypeForVReg(VReg: SrcReg);
4812 if (ResType == SrcType)
4813 return BuildCOPY(DestReg: ResVReg, SrcReg, I);
4814
4815 unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
4816 return selectUnOp(ResVReg, ResType, I, Opcode);
4817}
4818
4819bool SPIRVInstructionSelector::selectSUCmp(Register ResVReg,
4820 SPIRVTypeInst ResType,
4821 MachineInstr &I,
4822 bool IsSigned) const {
4823 MachineIRBuilder MIRBuilder(I);
4824 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
4825 MachineBasicBlock &BB = *I.getParent();
4826 // Ensure we have bool.
4827 SPIRVTypeInst BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
4828 unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
4829 if (N > 1 || (ResType->getOpcode() == SPIRV::OpTypeVectorIdEXT &&
4830 STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector)))
4831 BoolType = GR.getOrCreateSPIRVVectorType(BaseType: BoolType, NumElements: N, I, TII);
4832 Register BoolTypeReg = GR.getSPIRVTypeID(SpirvType: BoolType);
4833 // Build less-than-equal and less-than.
4834 Register IsLessEqReg =
4835 createVirtualRegister(SpvType: BoolType, GR: &GR, MRI, MF: MIRBuilder.getMF());
4836 BuildMI(BB, I, MIMD: I.getDebugLoc(),
4837 MCID: TII.get(Opcode: IsSigned ? SPIRV::OpSLessThanEqual : SPIRV::OpULessThanEqual))
4838 .addDef(RegNo: IsLessEqReg)
4839 .addUse(RegNo: BoolTypeReg)
4840 .addUse(RegNo: I.getOperand(i: 1).getReg())
4841 .addUse(RegNo: I.getOperand(i: 2).getReg())
4842 .constrainAllUses(TII, TRI, RBI);
4843 Register IsLessReg =
4844 createVirtualRegister(SpvType: BoolType, GR: &GR, MRI, MF: MIRBuilder.getMF());
4845 BuildMI(BB, I, MIMD: I.getDebugLoc(),
4846 MCID: TII.get(Opcode: IsSigned ? SPIRV::OpSLessThan : SPIRV::OpULessThan))
4847 .addDef(RegNo: IsLessReg)
4848 .addUse(RegNo: BoolTypeReg)
4849 .addUse(RegNo: I.getOperand(i: 1).getReg())
4850 .addUse(RegNo: I.getOperand(i: 2).getReg())
4851 .constrainAllUses(TII, TRI, RBI);
4852 // Build selects.
4853 Register ResTypeReg = GR.getSPIRVTypeID(SpirvType: ResType);
4854 Register NegOneOrZeroReg =
4855 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4856 MRI->setType(VReg: NegOneOrZeroReg, Ty: LLT::scalar(SizeInBits: 64));
4857 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: NegOneOrZeroReg, MF: MIRBuilder.getMF());
4858 unsigned SelectOpcode =
4859 (N > 1 || (ResType->getOpcode() == SPIRV::OpTypeVectorIdEXT &&
4860 STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector)))
4861 ? SPIRV::OpSelectVIVCond
4862 : SPIRV::OpSelectSISCond;
4863 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SelectOpcode))
4864 .addDef(RegNo: NegOneOrZeroReg)
4865 .addUse(RegNo: ResTypeReg)
4866 .addUse(RegNo: IsLessReg)
4867 .addUse(RegNo: buildOnesVal(AllOnes: true, ResType, I)) // -1
4868 .addUse(RegNo: buildZerosVal(ResType, I))
4869 .constrainAllUses(TII, TRI, RBI);
4870 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SelectOpcode))
4871 .addDef(RegNo: ResVReg)
4872 .addUse(RegNo: ResTypeReg)
4873 .addUse(RegNo: IsLessEqReg)
4874 .addUse(RegNo: NegOneOrZeroReg) // -1 or 0
4875 .addUse(RegNo: buildOnesVal(AllOnes: false, ResType, I))
4876 .constrainAllUses(TII, TRI, RBI);
4877 return true;
4878}
4879
4880bool SPIRVInstructionSelector::selectIntToBool(Register IntReg,
4881 Register ResVReg,
4882 MachineInstr &I,
4883 SPIRVTypeInst IntTy,
4884 SPIRVTypeInst BoolTy) const {
4885 // To truncate to a bool, we use OpBitwiseAnd 1 and OpINotEqual to zero.
4886 Register BitIntReg = createVirtualRegister(SpvType: IntTy, GR: &GR, MRI, MF: MRI->getMF());
4887 unsigned Opcode =
4888 isVectorType(SPVTy: IntTy) ? SPIRV::OpBitwiseAndV : SPIRV::OpBitwiseAndS;
4889 Register Zero = buildZerosVal(ResType: IntTy, I);
4890 Register One = buildOnesVal(AllOnes: false, ResType: IntTy, I);
4891 MachineBasicBlock &BB = *I.getParent();
4892 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
4893 .addDef(RegNo: BitIntReg)
4894 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: IntTy))
4895 .addUse(RegNo: IntReg)
4896 .addUse(RegNo: One)
4897 .constrainAllUses(TII, TRI, RBI);
4898 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpINotEqual))
4899 .addDef(RegNo: ResVReg)
4900 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: BoolTy))
4901 .addUse(RegNo: BitIntReg)
4902 .addUse(RegNo: Zero)
4903 .constrainAllUses(TII, TRI, RBI);
4904 return true;
4905}
4906
4907bool SPIRVInstructionSelector::selectTrunc(Register ResVReg,
4908 SPIRVTypeInst ResType,
4909 MachineInstr &I) const {
4910 Register IntReg = I.getOperand(i: 1).getReg();
4911 const SPIRVTypeInst ArgType = GR.getSPIRVTypeForVReg(VReg: IntReg);
4912 if (GR.isScalarOrVectorOfType(VReg: ResVReg, TypeOpcode: SPIRV::OpTypeBool))
4913 return selectIntToBool(IntReg, ResVReg, I, IntTy: ArgType, BoolTy: ResType);
4914 if (ArgType == ResType)
4915 return BuildCOPY(DestReg: ResVReg, SrcReg: IntReg, I);
4916 bool IsSigned = GR.isScalarOrVectorSigned(Type: ResType);
4917 unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
4918 return selectUnOp(ResVReg, ResType, I, Opcode);
4919}
4920
4921bool SPIRVInstructionSelector::selectConst(Register ResVReg,
4922 SPIRVTypeInst ResType,
4923 MachineInstr &I) const {
4924 unsigned Opcode = I.getOpcode();
4925 unsigned TpOpcode = ResType->getOpcode();
4926 Register Reg;
4927 if (ResType.isPointer() || TpOpcode == SPIRV::OpTypeEvent) {
4928 assert(Opcode == TargetOpcode::G_CONSTANT &&
4929 I.getOperand(1).getCImm()->isZero());
4930 MachineBasicBlock &DepMBB = I.getMF()->front();
4931 MachineIRBuilder MIRBuilder(DepMBB, DepMBB.getFirstNonPHI());
4932 Reg = GR.getOrCreateConstNullPtr(MIRBuilder, SpvType: ResType);
4933 } else if (TpOpcode == SPIRV::OpTypeVectorIdEXT) {
4934 // We ended up here coming from a splat on a <1 x T> type, which
4935 // IRTranslator translated into a scalar, so we have to restore the
4936 // vectorness.
4937 assert(GR.getScalarOrVectorComponentCount(ResType) == 1 &&
4938 "Expected <1 x T> Vector!");
4939 if (Opcode == TargetOpcode::G_FCONSTANT)
4940 Reg = GR.getOrCreateConstVector(Val: I.getOperand(i: 1).getFPImm()->getValue(), I,
4941 SpvType: ResType, TII);
4942 else // We handle vector of pointer here as well.
4943 Reg = GR.getOrCreateConstVector(Val: I.getOperand(i: 1).getCImm()->getValue(), I,
4944 SpvType: ResType, TII);
4945 } else if (Opcode == TargetOpcode::G_FCONSTANT) {
4946 Reg = GR.getOrCreateConstFP(Val: I.getOperand(i: 1).getFPImm()->getValue(), I,
4947 SpvType: ResType, TII, ZeroAsNull: !STI.isShader());
4948
4949 } else {
4950 Reg = GR.getOrCreateConstInt(Val: I.getOperand(i: 1).getCImm()->getValue(), I,
4951 SpvType: ResType, TII, ZeroAsNull: !STI.isShader());
4952 }
4953 return Reg == ResVReg ? true : BuildCOPY(DestReg: ResVReg, SrcReg: Reg, I);
4954}
4955
4956bool SPIRVInstructionSelector::selectOpUndef(Register ResVReg,
4957 SPIRVTypeInst ResType,
4958 MachineInstr &I) const {
4959 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
4960 .addDef(RegNo: ResVReg)
4961 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4962 .constrainAllUses(TII, TRI, RBI);
4963 return true;
4964}
4965
4966bool SPIRVInstructionSelector::selectInsertVal(Register ResVReg,
4967 SPIRVTypeInst ResType,
4968 MachineInstr &I) const {
4969 MachineBasicBlock &BB = *I.getParent();
4970 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeInsert))
4971 .addDef(RegNo: ResVReg)
4972 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4973 // object to insert
4974 .addUse(RegNo: I.getOperand(i: 3).getReg())
4975 // composite to insert into
4976 .addUse(RegNo: I.getOperand(i: 2).getReg());
4977 for (unsigned i = 4; i < I.getNumOperands(); i++)
4978 MIB.addImm(Val: foldImm(MO: I.getOperand(i), MRI));
4979 MIB.constrainAllUses(TII, TRI, RBI);
4980 return true;
4981}
4982
4983bool SPIRVInstructionSelector::selectExtractVal(Register ResVReg,
4984 SPIRVTypeInst ResType,
4985 MachineInstr &I) const {
4986 Type *MaybeResTy = nullptr;
4987 StringRef ResName;
4988 if (GR.findValueAttrs(Key: &I, Ty&: MaybeResTy, Name&: ResName) &&
4989 MaybeResTy != GR.getTypeForSPIRVType(Ty: ResType)) {
4990 assert((!MaybeResTy || MaybeResTy->isAggregateType()) &&
4991 "Expected aggregate type for extractv instruction");
4992 ResType = GR.getOrCreateSPIRVType(Type: MaybeResTy, I,
4993 AQ: SPIRV::AccessQualifier::ReadWrite, EmitIR: false);
4994 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: ResVReg, MF: *I.getMF());
4995 }
4996 MachineBasicBlock &BB = *I.getParent();
4997 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
4998 .addDef(RegNo: ResVReg)
4999 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5000 .addUse(RegNo: I.getOperand(i: 2).getReg());
5001 for (unsigned i = 3; i < I.getNumOperands(); i++)
5002 MIB.addImm(Val: foldImm(MO: I.getOperand(i), MRI));
5003 MIB.constrainAllUses(TII, TRI, RBI);
5004 return true;
5005}
5006
5007bool SPIRVInstructionSelector::selectInsertElt(Register ResVReg,
5008 SPIRVTypeInst ResType,
5009 MachineInstr &I) const {
5010 if (getImm(MO: I.getOperand(i: 4), MRI))
5011 return selectInsertVal(ResVReg, ResType, I);
5012 MachineBasicBlock &BB = *I.getParent();
5013 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVectorInsertDynamic))
5014 .addDef(RegNo: ResVReg)
5015 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5016 .addUse(RegNo: I.getOperand(i: 2).getReg())
5017 .addUse(RegNo: I.getOperand(i: 3).getReg())
5018 .addUse(RegNo: I.getOperand(i: 4).getReg())
5019 .constrainAllUses(TII, TRI, RBI);
5020 return true;
5021}
5022
5023bool SPIRVInstructionSelector::selectExtractElt(Register ResVReg,
5024 SPIRVTypeInst ResType,
5025 MachineInstr &I) const {
5026 if (getImm(MO: I.getOperand(i: 3), MRI))
5027 return selectExtractVal(ResVReg, ResType, I);
5028 MachineBasicBlock &BB = *I.getParent();
5029 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVectorExtractDynamic))
5030 .addDef(RegNo: ResVReg)
5031 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5032 .addUse(RegNo: I.getOperand(i: 2).getReg())
5033 .addUse(RegNo: I.getOperand(i: 3).getReg())
5034 .constrainAllUses(TII, TRI, RBI);
5035 return true;
5036}
5037
5038bool SPIRVInstructionSelector::selectGEP(Register ResVReg,
5039 SPIRVTypeInst ResType,
5040 MachineInstr &I) const {
5041 const bool IsGEPInBounds = I.getOperand(i: 2).getImm();
5042 // Pointers to opaque types stay typed even with the extension on, so emit the
5043 // untyped variant only when the result is actually an untyped pointer.
5044 const bool UseUntypedPointers =
5045 ResType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
5046
5047 // Determine the opcode based on pointer type and bounds checking.
5048 // When using untyped pointers, use OpUntyped*AccessChainKHR variants.
5049 unsigned Opcode;
5050 if (UseUntypedPointers) {
5051 if (STI.isLogicalSPIRV()) {
5052 Opcode = IsGEPInBounds ? SPIRV::OpUntypedInBoundsAccessChainKHR
5053 : SPIRV::OpUntypedAccessChainKHR;
5054 } else {
5055 Opcode = IsGEPInBounds ? SPIRV::OpUntypedInBoundsPtrAccessChainKHR
5056 : SPIRV::OpUntypedPtrAccessChainKHR;
5057 }
5058 } else {
5059 // OpAccessChain could be used for OpenCL, but the SPIRV-LLVM Translator
5060 // only relies on PtrAccessChain, so we'll try not to deviate. For Vulkan
5061 // however, we have to use Op[InBounds]AccessChain.
5062 // FIXME: fix llvm-spirv.
5063 if (STI.isLogicalSPIRV()) {
5064 Opcode =
5065 IsGEPInBounds ? SPIRV::OpInBoundsAccessChain : SPIRV::OpAccessChain;
5066 } else {
5067 Opcode = IsGEPInBounds ? SPIRV::OpInBoundsPtrAccessChain
5068 : SPIRV::OpPtrAccessChain;
5069 }
5070 }
5071
5072 Register BaseReg = I.getOperand(i: 3).getReg();
5073 auto Res = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
5074 .addDef(RegNo: ResVReg)
5075 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5076
5077 // For untyped access chains, we need to add the base type operand.
5078 if (UseUntypedPointers) {
5079 // Get the element type from the base pointer register.
5080 // For untyped pointers, this was stored when processing
5081 // spv_assign_ptr_type.
5082 SPIRVTypeInst BaseType = GR.getUntypedPtrElementType(Reg: BaseReg);
5083 if (!BaseType) {
5084 // Otherwise try the pointee type for mixed typed-pointer usage.
5085 SPIRVTypeInst BasePtrType = GR.getSPIRVTypeForVReg(VReg: BaseReg);
5086 BaseType = BasePtrType ? GR.getPointeeType(PtrType: BasePtrType) : nullptr;
5087 }
5088 if (!BaseType) {
5089 // The base may be a not-yet-selected global. Read its value type from
5090 // the defining G_GLOBAL_VALUE, following copies.
5091 Register DefReg = BaseReg;
5092 MachineInstr *Def = MRI->getVRegDef(Reg: DefReg);
5093 while (Def && Def->getOpcode() == TargetOpcode::COPY &&
5094 Def->getOperand(i: 1).isReg())
5095 Def = MRI->getVRegDef(Reg: Def->getOperand(i: 1).getReg());
5096 if (Def && Def->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
5097 if (const auto *GVar =
5098 dyn_cast<GlobalVariable>(Val: Def->getOperand(i: 1).getGlobal()))
5099 BaseType = GR.getOrCreateSPIRVType(Type: GVar->getValueType(), I,
5100 AQ: SPIRV::AccessQualifier::ReadWrite,
5101 /*EmitIR=*/false);
5102 }
5103 if (!BaseType)
5104 return diagnoseUnsupported(
5105 I, Msg: "could not deduce the base type of an untyped access chain");
5106 Res.addUse(RegNo: GR.getSPIRVTypeID(SpirvType: BaseType));
5107 }
5108
5109 // Object to get a pointer to.
5110 Res.addUse(RegNo: BaseReg);
5111
5112 const bool IsAccessChainOpcode =
5113 (Opcode == SPIRV::OpAccessChain ||
5114 Opcode == SPIRV::OpInBoundsAccessChain ||
5115 Opcode == SPIRV::OpUntypedAccessChainKHR ||
5116 Opcode == SPIRV::OpUntypedInBoundsAccessChainKHR);
5117
5118 assert((!IsAccessChainOpcode || (getImm(I.getOperand(4), MRI) &&
5119 foldImm(I.getOperand(4), MRI) == 0)) &&
5120 "Cannot translate GEP to OpAccessChain.");
5121
5122 // Adding indices.
5123 const unsigned StartingIndex = IsAccessChainOpcode ? 5 : 4;
5124 for (unsigned i = StartingIndex; i < I.getNumExplicitOperands(); ++i)
5125 Res.addUse(RegNo: I.getOperand(i).getReg());
5126 Res.constrainAllUses(TII, TRI, RBI);
5127
5128 // IRTranslator doesn't like <1 x T> vectors, and treats them as scalars. This
5129 // creates an information loss issue, and also broken code where a
5130 // OpCompositeExtract is applied to the scalar return of an OpPtrAccessChain,
5131 // when it was meant to be applied to the OpCompositeInsert created <1 x T>.
5132 // TODO: should we also re-create the index <1 x T>?
5133 if (MRI->hasOneUse(RegNo: ResVReg)) {
5134 MachineInstr &Extract = *MRI->use_instr_begin(RegNo: ResVReg);
5135 if (Extract.getOpcode() == SPIRV::OpCompositeExtract) {
5136 SPIRVTypeInst V = GR.getOrCreateSPIRVVectorType(BaseType: ResType, NumElements: 1, I&: Extract, TII);
5137 // We cannot use GlobalRegistry::getOrCreateUndef directly here because it
5138 // tries to use UndefVal::get, which does not work for TypedPointerType,
5139 // which we can get here if we're dealing with <1 x T*>.
5140 Register Tmp = createVirtualRegister(SpvType: V, GR: &GR, MRI, MF: *I.getMF());
5141 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
5142 .addDef(RegNo: Tmp)
5143 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: V))
5144 .constrainAllUses(TII, TRI, RBI);
5145 Register InsertReg = createVirtualRegister(SpvType: V, GR: &GR, MRI, MF: *I.getMF());
5146 BuildMI(BB&: *I.getParent(), I&: Extract, MIMD: Extract.getDebugLoc(),
5147 MCID: TII.get(Opcode: SPIRV::OpCompositeInsert))
5148 .addDef(RegNo: InsertReg)
5149 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: V))
5150 .addUse(RegNo: ResVReg)
5151 .addUse(RegNo: Tmp)
5152 .addImm(Val: 0)
5153 .constrainAllUses(TII, TRI, RBI);
5154 Extract.substituteRegister(FromReg: ResVReg, ToReg: InsertReg, SubIdx: 0, RegInfo: TRI);
5155 }
5156 }
5157 return true;
5158}
5159
5160// Maybe wrap a value into OpSpecConstantOp
5161bool SPIRVInstructionSelector::wrapIntoSpecConstantOp(
5162 MachineInstr &I, SmallVector<Register> &CompositeArgs) const {
5163 unsigned Lim = I.getNumExplicitOperands();
5164 for (unsigned i = I.getNumExplicitDefs() + 1; i < Lim; ++i) {
5165 Register OpReg = I.getOperand(i).getReg();
5166 MachineInstr *OpDefine = MRI->getVRegDef(Reg: OpReg);
5167 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
5168 if (!OpDefine || !OpType || isConstReg(MRI, OpDef: OpDefine) ||
5169 OpDefine->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST ||
5170 OpDefine->getOpcode() == TargetOpcode::G_INTTOPTR ||
5171 GR.isAggregateType(Type: OpType)) {
5172 // The case of G_ADDRSPACE_CAST inside spv_const_composite() is processed
5173 // by selectAddrSpaceCast(), and G_INTTOPTR is processed by selectUnOp()
5174 CompositeArgs.push_back(Elt: OpReg);
5175 continue;
5176 }
5177 MachineFunction *MF = I.getMF();
5178 Register WrapReg = GR.find(MI: OpDefine, MF);
5179 if (WrapReg.isValid()) {
5180 CompositeArgs.push_back(Elt: WrapReg);
5181 continue;
5182 }
5183 SPIRVTypeInst WrapType = OpType;
5184 if (OpType->getOpcode() == SPIRV::OpTypePointer &&
5185 GR.getPointerStorageClass(Type: OpType) ==
5186 SPIRV::StorageClass::CodeSectionINTEL) {
5187 WrapType = GR.changePointerStorageClass(PtrType: OpType,
5188 SC: SPIRV::StorageClass::Function, I);
5189 }
5190 WrapReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: WrapType));
5191 CompositeArgs.push_back(Elt: WrapReg);
5192 // Decorate the wrapper register and generate a new instruction
5193 MRI->setType(VReg: WrapReg, Ty: LLT::pointer(AddressSpace: 0, SizeInBits: 64));
5194 GR.assignSPIRVTypeToVReg(Type: WrapType, VReg: WrapReg, MF: *MF);
5195 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
5196 MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
5197 .addDef(RegNo: WrapReg)
5198 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: WrapType))
5199 .addImm(Val: static_cast<uint32_t>(SPIRV::Opcode::Bitcast))
5200 .addUse(RegNo: OpReg);
5201 GR.add(Obj: OpDefine, MI: MIB);
5202 MIB.constrainAllUses(TII, TRI, RBI);
5203 }
5204 return true;
5205}
5206
5207bool SPIRVInstructionSelector::selectDerivativeInst(
5208 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
5209 const unsigned DPdOpCode) const {
5210 // TODO: This should check specifically for Fragment Execution Model, but STI
5211 // doesn't provide that information yet. See #167562
5212 if (!errorIfInstrOutsideShader(I))
5213 return false;
5214
5215 // If the arg/result types are half then we need to wrap the instr in
5216 // conversions to float
5217 // This case occurs because a half arg/result is legal in HLSL but not spirv.
5218 Register SrcReg = I.getOperand(i: 2).getReg();
5219 SPIRVTypeInst SrcType = GR.getSPIRVTypeForVReg(VReg: SrcReg);
5220 unsigned BitWidth = std::min(a: GR.getScalarOrVectorBitWidth(Type: SrcType),
5221 b: GR.getScalarOrVectorBitWidth(Type: ResType));
5222 if (BitWidth == 32)
5223 return BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DPdOpCode))
5224 .addDef(RegNo: ResVReg)
5225 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5226 .addUse(RegNo: I.getOperand(i: 2).getReg());
5227
5228 MachineIRBuilder MIRBuilder(I);
5229 unsigned componentCount = GR.getScalarOrVectorComponentCount(Type: SrcType);
5230 SPIRVTypeInst F32ConvertTy = GR.getOrCreateSPIRVFloatType(BitWidth: 32, I, TII);
5231 if (componentCount != 1)
5232 F32ConvertTy = GR.getOrCreateSPIRVVectorType(BaseType: F32ConvertTy, NumElements: componentCount,
5233 MIRBuilder, EmitIR: false);
5234
5235 const TargetRegisterClass *RegClass = GR.getRegClass(SpvType: SrcType);
5236 Register ConvertToVReg = MRI->createVirtualRegister(RegClass);
5237 Register DpdOpVReg = MRI->createVirtualRegister(RegClass);
5238
5239 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFConvert))
5240 .addDef(RegNo: ConvertToVReg)
5241 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: F32ConvertTy))
5242 .addUse(RegNo: SrcReg)
5243 .constrainAllUses(TII, TRI, RBI);
5244 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DPdOpCode))
5245 .addDef(RegNo: DpdOpVReg)
5246 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: F32ConvertTy))
5247 .addUse(RegNo: ConvertToVReg)
5248 .constrainAllUses(TII, TRI, RBI);
5249 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFConvert))
5250 .addDef(RegNo: ResVReg)
5251 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5252 .addUse(RegNo: DpdOpVReg)
5253 .constrainAllUses(TII, TRI, RBI);
5254 return true;
5255}
5256
5257bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
5258 SPIRVTypeInst ResType,
5259 MachineInstr &I) const {
5260 MachineBasicBlock &BB = *I.getParent();
5261 Intrinsic::ID IID = cast<GIntrinsic>(Val&: I).getIntrinsicID();
5262 switch (IID) {
5263 case Intrinsic::spv_load:
5264 return selectLoad(ResVReg, ResType, I);
5265 case Intrinsic::spv_atomic_load:
5266 return selectAtomicLoad(ResVReg, ResType, I);
5267 case Intrinsic::spv_store:
5268 return selectStore(I);
5269 case Intrinsic::spv_atomic_store:
5270 return selectAtomicStore(I);
5271 case Intrinsic::spv_extractv:
5272 return selectExtractVal(ResVReg, ResType, I);
5273 case Intrinsic::spv_insertv:
5274 return selectInsertVal(ResVReg, ResType, I);
5275 case Intrinsic::spv_extractelt:
5276 return selectExtractElt(ResVReg, ResType, I);
5277 case Intrinsic::spv_insertelt:
5278 return selectInsertElt(ResVReg, ResType, I);
5279 case Intrinsic::spv_gep:
5280 return selectGEP(ResVReg, ResType, I);
5281 case Intrinsic::spv_bitcast: {
5282 Register OpReg = I.getOperand(i: 2).getReg();
5283 SPIRVTypeInst OpType =
5284 OpReg.isValid() ? GR.getSPIRVTypeForVReg(VReg: OpReg) : nullptr;
5285 if (!GR.isBitcastCompatible(Type1: ResType, Type2: OpType))
5286 report_fatal_error(reason: "incompatible result and operand types in a bitcast");
5287 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {OpReg}, Opcode: SPIRV::OpBitcast);
5288 }
5289 case Intrinsic::spv_unref_global:
5290 case Intrinsic::spv_init_global: {
5291 MachineInstr *MI = MRI->getVRegDef(Reg: I.getOperand(i: 1).getReg());
5292 MachineInstr *Init = I.getNumExplicitOperands() > 2
5293 ? MRI->getVRegDef(Reg: I.getOperand(i: 2).getReg())
5294 : nullptr;
5295 assert(MI);
5296 Register GVarVReg = MI->getOperand(i: 0).getReg();
5297 if (!selectGlobalValue(ResVReg: GVarVReg, I&: *MI, Init))
5298 return false;
5299 // We violate SSA form by inserting OpVariable and still having a gMIR
5300 // instruction %vreg = G_GLOBAL_VALUE @gvar. We need to fix this by erasing
5301 // the duplicated definition.
5302 if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
5303 GR.invalidateMachineInstr(MI);
5304 MI->eraseFromParent();
5305 }
5306 return true;
5307 }
5308 case Intrinsic::spv_undef: {
5309 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
5310 .addDef(RegNo: ResVReg)
5311 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5312 MIB.constrainAllUses(TII, TRI, RBI);
5313 return true;
5314 }
5315 case Intrinsic::spv_poison:
5316 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpPoisonKHR))
5317 .addDef(RegNo: ResVReg)
5318 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5319 .constrainAllUses(TII, TRI, RBI);
5320 return true;
5321 case Intrinsic::spv_freeze:
5322 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFreezeKHR))
5323 .addDef(RegNo: ResVReg)
5324 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5325 .addUse(RegNo: I.getOperand(i: 2).getReg())
5326 .constrainAllUses(TII, TRI, RBI);
5327 return true;
5328 case Intrinsic::spv_named_boolean_spec_constant: {
5329 auto Opcode = I.getOperand(i: 3).getImm() ? SPIRV::OpSpecConstantTrue
5330 : SPIRV::OpSpecConstantFalse;
5331
5332 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
5333 .addDef(RegNo: I.getOperand(i: 0).getReg())
5334 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5335 MIB.constrainAllUses(TII, TRI, RBI);
5336 unsigned SpecId = I.getOperand(i: 2).getImm();
5337 buildOpDecorate(Reg: I.getOperand(i: 0).getReg(), I&: *++MIB->getIterator(), TII,
5338 Dec: SPIRV::Decoration::SpecId, DecArgs: {SpecId});
5339
5340 return true;
5341 }
5342 case Intrinsic::spv_const_composite: {
5343 // If no values are attached, the composite is null constant.
5344 bool IsNull = I.getNumExplicitDefs() + 1 == I.getNumExplicitOperands();
5345 SmallVector<Register> CompositeArgs;
5346 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
5347
5348 // skip type MD node we already used when generated assign.type for this
5349 if (!IsNull) {
5350 if (!wrapIntoSpecConstantOp(I, CompositeArgs))
5351 return false;
5352 std::function<bool(Register)> HasSpecConstOperand =
5353 [&](Register Reg) -> bool {
5354 MachineInstr *Def = MRI->getVRegDef(Reg);
5355 if (!Def)
5356 return false;
5357 if (!isConstReg(MRI, OpDef: Def))
5358 return true;
5359 // Recurse into not-yet-selected spv_const_composite intrinsics
5360 // to detect transitive spec constant operands.
5361 if (isSpvIntrinsic(MI: *Def, IntrinsicID: Intrinsic::spv_const_composite)) {
5362 for (unsigned J = Def->getNumExplicitDefs() + 1;
5363 J < Def->getNumExplicitOperands(); ++J) {
5364 if (Def->getOperand(i: J).isReg() &&
5365 HasSpecConstOperand(Def->getOperand(i: J).getReg()))
5366 return true;
5367 }
5368 }
5369 return false;
5370 };
5371 bool HasSpecConst = llvm::any_of(Range&: CompositeArgs, P: HasSpecConstOperand);
5372 unsigned CompositeOpc = HasSpecConst ? SPIRV::OpSpecConstantComposite
5373 : SPIRV::OpConstantComposite;
5374 unsigned ContinuedOpc = HasSpecConst
5375 ? SPIRV::OpSpecConstantCompositeContinuedINTEL
5376 : SPIRV::OpConstantCompositeContinuedINTEL;
5377 MachineIRBuilder MIR(I);
5378 SmallVector<MachineInstr *, 4> Instructions = createContinuedInstructions(
5379 MIRBuilder&: MIR, Opcode: CompositeOpc, MinWC: 3, ContinuedOpcode: ContinuedOpc, Args: CompositeArgs, ReturnRegister: ResVReg,
5380 TypeID: GR.getSPIRVTypeID(SpirvType: ResType));
5381 for (auto *Instr : Instructions) {
5382 Instr->setDebugLoc(I.getDebugLoc());
5383 constrainSelectedInstRegOperands(I&: *Instr, TII, TRI, RBI);
5384 }
5385 return true;
5386 } else {
5387 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
5388 .addDef(RegNo: ResVReg)
5389 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5390 MIB.constrainAllUses(TII, TRI, RBI);
5391 return true;
5392 }
5393 }
5394 case Intrinsic::spv_assign_name: {
5395 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpName));
5396 MIB.addUse(RegNo: I.getOperand(i: I.getNumExplicitDefs() + 1).getReg());
5397 for (unsigned i = I.getNumExplicitDefs() + 2;
5398 i < I.getNumExplicitOperands(); ++i) {
5399 MIB.addImm(Val: I.getOperand(i).getImm());
5400 }
5401 MIB.constrainAllUses(TII, TRI, RBI);
5402 return true;
5403 }
5404 case Intrinsic::spv_switch: {
5405 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSwitch));
5406 for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
5407 if (I.getOperand(i).isReg())
5408 MIB.addReg(RegNo: I.getOperand(i).getReg());
5409 else if (I.getOperand(i).isCImm())
5410 addNumImm(Imm: I.getOperand(i).getCImm()->getValue(), MIB);
5411 else if (I.getOperand(i).isMBB())
5412 MIB.addMBB(MBB: I.getOperand(i).getMBB());
5413 else
5414 llvm_unreachable("Unexpected OpSwitch operand");
5415 }
5416 MIB.constrainAllUses(TII, TRI, RBI);
5417 return true;
5418 }
5419 case Intrinsic::spv_loop_merge: {
5420 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoopMerge));
5421 for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
5422 if (I.getOperand(i).isMBB())
5423 MIB.addMBB(MBB: I.getOperand(i).getMBB());
5424 else
5425 MIB.addImm(Val: foldImm(MO: I.getOperand(i), MRI));
5426 }
5427 MIB.constrainAllUses(TII, TRI, RBI);
5428 return true;
5429 }
5430 case Intrinsic::spv_loop_control_intel: {
5431 auto MIB =
5432 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoopControlINTEL));
5433 for (unsigned J = 1; J < I.getNumExplicitOperands(); ++J)
5434 MIB.addImm(Val: foldImm(MO: I.getOperand(i: J), MRI));
5435 MIB.constrainAllUses(TII, TRI, RBI);
5436 return true;
5437 }
5438 case Intrinsic::spv_selection_merge: {
5439 auto MIB =
5440 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSelectionMerge));
5441 assert(I.getOperand(1).isMBB() &&
5442 "operand 1 to spv_selection_merge must be a basic block");
5443 MIB.addMBB(MBB: I.getOperand(i: 1).getMBB());
5444 MIB.addImm(Val: getSelectionOperandForImm(Imm: I.getOperand(i: 2).getImm()));
5445 MIB.constrainAllUses(TII, TRI, RBI);
5446 return true;
5447 }
5448 case Intrinsic::spv_cmpxchg:
5449 return selectAtomicCmpXchg(ResVReg, ResType, I);
5450 case Intrinsic::spv_unreachable:
5451 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUnreachable))
5452 .constrainAllUses(TII, TRI, RBI);
5453 return true;
5454 case Intrinsic::spv_abort:
5455 return selectAbort(I);
5456 case Intrinsic::spv_alloca:
5457 return selectFrameIndex(ResVReg, ResType, I);
5458 case Intrinsic::spv_alloca_array:
5459 return selectAllocaArray(ResVReg, ResType, I);
5460 case Intrinsic::spv_assume:
5461 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_expect_assume)) {
5462 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAssumeTrueKHR))
5463 .addUse(RegNo: I.getOperand(i: 1).getReg())
5464 .constrainAllUses(TII, TRI, RBI);
5465 return true;
5466 }
5467 break;
5468 case Intrinsic::spv_expect:
5469 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_expect_assume)) {
5470 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExpectKHR))
5471 .addDef(RegNo: ResVReg)
5472 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5473 .addUse(RegNo: I.getOperand(i: 2).getReg())
5474 .addUse(RegNo: I.getOperand(i: 3).getReg())
5475 .constrainAllUses(TII, TRI, RBI);
5476 return true;
5477 }
5478 break;
5479 case Intrinsic::arithmetic_fence:
5480 if (STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_arithmetic_fence)) {
5481 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpArithmeticFenceEXT))
5482 .addDef(RegNo: ResVReg)
5483 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5484 .addUse(RegNo: I.getOperand(i: 2).getReg())
5485 .constrainAllUses(TII, TRI, RBI);
5486 return true;
5487 } else
5488 return BuildCOPY(DestReg: ResVReg, SrcReg: I.getOperand(i: 2).getReg(), I);
5489 break;
5490 case Intrinsic::spv_thread_id:
5491 // The HLSL SV_DispatchThreadID semantic is lowered to llvm.spv.thread.id
5492 // intrinsic in LLVM IR for SPIR-V backend.
5493 //
5494 // In SPIR-V backend, llvm.spv.thread.id is now correctly translated to a
5495 // `GlobalInvocationId` builtin variable
5496 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::GlobalInvocationId, ResVReg,
5497 ResType, I);
5498 case Intrinsic::spv_thread_id_in_group:
5499 // The HLSL SV_GroupThreadId semantic is lowered to
5500 // llvm.spv.thread.id.in.group intrinsic in LLVM IR for SPIR-V backend.
5501 //
5502 // In SPIR-V backend, llvm.spv.thread.id.in.group is now correctly
5503 // translated to a `LocalInvocationId` builtin variable
5504 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::LocalInvocationId, ResVReg,
5505 ResType, I);
5506 case Intrinsic::spv_group_id:
5507 // The HLSL SV_GroupId semantic is lowered to
5508 // llvm.spv.group.id intrinsic in LLVM IR for SPIR-V backend.
5509 //
5510 // In SPIR-V backend, llvm.spv.group.id is now translated to a `WorkgroupId`
5511 // builtin variable
5512 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::WorkgroupId, ResVReg, ResType,
5513 I);
5514 case Intrinsic::spv_flattened_thread_id_in_group:
5515 // The HLSL SV_GroupIndex semantic is lowered to
5516 // llvm.spv.flattened.thread.id.in.group() intrinsic in LLVM IR for SPIR-V
5517 // backend.
5518 //
5519 // In SPIR-V backend, llvm.spv.flattened.thread.id.in.group is translated to
5520 // a `LocalInvocationIndex` builtin variable
5521 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::LocalInvocationIndex, ResVReg,
5522 ResType, I);
5523 case Intrinsic::spv_workgroup_size:
5524 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::WorkgroupSize, ResVReg,
5525 ResType, I);
5526 case Intrinsic::spv_global_size:
5527 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::GlobalSize, ResVReg, ResType,
5528 I);
5529 case Intrinsic::spv_global_offset:
5530 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::GlobalOffset, ResVReg,
5531 ResType, I);
5532 case Intrinsic::spv_num_workgroups:
5533 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::NumWorkgroups, ResVReg,
5534 ResType, I);
5535 case Intrinsic::spv_subgroup_size:
5536 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupSize, ResVReg, ResType,
5537 I);
5538 case Intrinsic::spv_num_subgroups:
5539 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::NumSubgroups, ResVReg, ResType,
5540 I);
5541 case Intrinsic::spv_subgroup_id:
5542 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupId, ResVReg, ResType, I);
5543 case Intrinsic::spv_subgroup_local_invocation_id:
5544 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupLocalInvocationId,
5545 ResVReg, ResType, I);
5546 case Intrinsic::spv_subgroup_max_size:
5547 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupMaxSize, ResVReg, ResType,
5548 I);
5549 case Intrinsic::spv_fdot:
5550 return selectFloatDot(ResVReg, ResType, I);
5551 case Intrinsic::spv_udot:
5552 case Intrinsic::spv_sdot:
5553 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_integer_dot_product) ||
5554 STI.isAtLeastSPIRVVer(VerToCompareTo: VersionTuple(1, 6)))
5555 return selectIntegerDot(ResVReg, ResType, I,
5556 /*Signed=*/IID == Intrinsic::spv_sdot);
5557 return selectIntegerDotExpansion(ResVReg, ResType, I);
5558 case Intrinsic::spv_dot4add_i8packed:
5559 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_integer_dot_product) ||
5560 STI.isAtLeastSPIRVVer(VerToCompareTo: VersionTuple(1, 6)))
5561 return selectDot4AddPacked<true>(ResVReg, ResType, I);
5562 return selectDot4AddPackedExpansion<true>(ResVReg, ResType, I);
5563 case Intrinsic::spv_dot4add_u8packed:
5564 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_integer_dot_product) ||
5565 STI.isAtLeastSPIRVVer(VerToCompareTo: VersionTuple(1, 6)))
5566 return selectDot4AddPacked<false>(ResVReg, ResType, I);
5567 return selectDot4AddPackedExpansion<false>(ResVReg, ResType, I);
5568 case Intrinsic::spv_all:
5569 return selectAll(ResVReg, ResType, I);
5570 case Intrinsic::spv_any:
5571 return selectAny(ResVReg, ResType, I);
5572 case Intrinsic::spv_distance:
5573 return selectExtInst(ResVReg, ResType, I, CLInst: CL::distance, GLInst: GL::Distance);
5574 case Intrinsic::spv_lerp:
5575 return selectExtInst(ResVReg, ResType, I, CLInst: CL::mix, GLInst: GL::FMix);
5576 case Intrinsic::spv_length:
5577 return selectExtInst(ResVReg, ResType, I, CLInst: CL::length, GLInst: GL::Length);
5578 case Intrinsic::spv_degrees:
5579 return selectExtInst(ResVReg, ResType, I, CLInst: CL::degrees, GLInst: GL::Degrees);
5580 case Intrinsic::spv_faceforward:
5581 return selectExtInst(ResVReg, ResType, I, GLInst: GL::FaceForward);
5582 case Intrinsic::spv_frac:
5583 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fract, GLInst: GL::Fract);
5584 case Intrinsic::spv_isinf:
5585 return selectOpIsInf(ResVReg, ResType, I);
5586 case Intrinsic::spv_isnan:
5587 return selectOpIsNan(ResVReg, ResType, I);
5588 case Intrinsic::spv_isfinite:
5589 return selectOpIsFinite(ResVReg, ResType, I);
5590 case Intrinsic::spv_isnormal:
5591 return selectOpIsNormal(ResVReg, ResType, I);
5592 case Intrinsic::spv_normalize:
5593 return selectExtInst(ResVReg, ResType, I, CLInst: CL::normalize, GLInst: GL::Normalize);
5594 case Intrinsic::spv_refract:
5595 return selectExtInst(ResVReg, ResType, I, GLInst: GL::Refract);
5596 case Intrinsic::spv_reflect:
5597 return selectExtInst(ResVReg, ResType, I, GLInst: GL::Reflect);
5598 case Intrinsic::spv_rsqrt:
5599 return selectExtInst(ResVReg, ResType, I, CLInst: CL::rsqrt, GLInst: GL::InverseSqrt);
5600 case Intrinsic::spv_sign:
5601 return selectSign(ResVReg, ResType, I);
5602 case Intrinsic::spv_smoothstep:
5603 return selectExtInst(ResVReg, ResType, I, CLInst: CL::smoothstep, GLInst: GL::SmoothStep);
5604 case Intrinsic::spv_firstbituhigh: // There is no CL equivalent of FindUMsb
5605 return selectFirstBitHigh(ResVReg, ResType, I, /*IsSigned=*/false);
5606 case Intrinsic::spv_firstbitshigh: // There is no CL equivalent of FindSMsb
5607 return selectFirstBitHigh(ResVReg, ResType, I, /*IsSigned=*/true);
5608 case Intrinsic::spv_firstbitlow: // There is no CL equivlent of FindILsb
5609 return selectFirstBitLow(ResVReg, ResType, I);
5610 case Intrinsic::spv_all_memory_barrier:
5611 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5612 MemSem: SPIRV::MemorySemantics::UniformMemory |
5613 SPIRV::MemorySemantics::ImageMemory |
5614 SPIRV::MemorySemantics::WorkgroupMemory,
5615 /*WithGroupSync*/ false);
5616 case Intrinsic::spv_all_memory_barrier_with_group_sync:
5617 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5618 MemSem: SPIRV::MemorySemantics::UniformMemory |
5619 SPIRV::MemorySemantics::ImageMemory |
5620 SPIRV::MemorySemantics::WorkgroupMemory,
5621 /*WithGroupSync*/ true);
5622 case Intrinsic::spv_device_memory_barrier:
5623 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5624 MemSem: SPIRV::MemorySemantics::UniformMemory |
5625 SPIRV::MemorySemantics::ImageMemory,
5626 /*WithGroupSync*/ false);
5627 case Intrinsic::spv_device_memory_barrier_with_group_sync:
5628 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5629 MemSem: SPIRV::MemorySemantics::UniformMemory |
5630 SPIRV::MemorySemantics::ImageMemory,
5631 /*WithGroupSync*/ true);
5632 case Intrinsic::spv_group_memory_barrier:
5633 return selectBarrierInst(I, Scope: SPIRV::Scope::Workgroup,
5634 MemSem: SPIRV::MemorySemantics::WorkgroupMemory,
5635 /*WithGroupSync*/ false);
5636 case Intrinsic::spv_group_memory_barrier_with_group_sync:
5637 return selectBarrierInst(I, Scope: SPIRV::Scope::Workgroup,
5638 MemSem: SPIRV::MemorySemantics::WorkgroupMemory,
5639 /*WithGroupSync*/ true);
5640 case Intrinsic::spv_generic_cast_to_ptr_explicit: {
5641 Register PtrReg = I.getOperand(i: I.getNumExplicitDefs() + 1).getReg();
5642 SPIRV::StorageClass::StorageClass ResSC =
5643 GR.getPointerStorageClass(Type: ResType);
5644 if (!isGenericCastablePtr(SC: ResSC))
5645 return diagnoseUnsupported(I, Msg: "The target storage class is not castable "
5646 "from the Generic storage class");
5647 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpGenericCastToPtrExplicit))
5648 .addDef(RegNo: ResVReg)
5649 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5650 .addUse(RegNo: PtrReg)
5651 .addImm(Val: ResSC)
5652 .constrainAllUses(TII, TRI, RBI);
5653 return true;
5654 }
5655 case Intrinsic::spv_lifetime_start:
5656 case Intrinsic::spv_lifetime_end: {
5657 unsigned Op = IID == Intrinsic::spv_lifetime_start ? SPIRV::OpLifetimeStart
5658 : SPIRV::OpLifetimeStop;
5659 int64_t Size = I.getOperand(i: I.getNumExplicitDefs() + 1).getImm();
5660 Register PtrReg = I.getOperand(i: I.getNumExplicitDefs() + 2).getReg();
5661 if (Size == -1)
5662 Size = 0;
5663 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: Op))
5664 .addUse(RegNo: PtrReg)
5665 .addImm(Val: Size)
5666 .constrainAllUses(TII, TRI, RBI);
5667 return true;
5668 }
5669 case Intrinsic::spv_saturate:
5670 return selectSaturate(ResVReg, ResType, I);
5671 case Intrinsic::spv_nclamp:
5672 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fclamp, GLInst: GL::NClamp);
5673 case Intrinsic::spv_uclamp:
5674 return selectExtInst(ResVReg, ResType, I, CLInst: CL::u_clamp, GLInst: GL::UClamp);
5675 case Intrinsic::spv_sclamp:
5676 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_clamp, GLInst: GL::SClamp);
5677 case Intrinsic::spv_subgroup_prefix_bit_count:
5678 return selectWavePrefixBitCount(ResVReg, ResType, I);
5679 case Intrinsic::spv_wave_active_countbits:
5680 return selectWaveActiveCountBits(ResVReg, ResType, I);
5681 case Intrinsic::spv_wave_all_equal:
5682 return selectWaveActiveAllEqual(ResVReg, ResType, I);
5683 case Intrinsic::spv_wave_all:
5684 return selectWaveOpInst(ResVReg, ResType, I, Opcode: SPIRV::OpGroupNonUniformAll);
5685 case Intrinsic::spv_wave_any:
5686 return selectWaveOpInst(ResVReg, ResType, I, Opcode: SPIRV::OpGroupNonUniformAny);
5687 case Intrinsic::spv_subgroup_ballot:
5688 return selectWaveOpInst(ResVReg, ResType, I,
5689 Opcode: SPIRV::OpGroupNonUniformBallot);
5690 case Intrinsic::spv_wave_is_first_lane:
5691 return selectWaveOpInst(ResVReg, ResType, I, Opcode: SPIRV::OpGroupNonUniformElect);
5692 case Intrinsic::spv_wave_reduce_or:
5693 return selectWaveReduceOp(ResVReg, ResType, I,
5694 Opcode: SPIRV::OpGroupNonUniformBitwiseOr);
5695 case Intrinsic::spv_wave_reduce_xor:
5696 return selectWaveReduceOp(ResVReg, ResType, I,
5697 Opcode: SPIRV::OpGroupNonUniformBitwiseXor);
5698 case Intrinsic::spv_wave_reduce_and:
5699 return selectWaveReduceOp(ResVReg, ResType, I,
5700 Opcode: SPIRV::OpGroupNonUniformBitwiseAnd);
5701 case Intrinsic::spv_wave_reduce_umax:
5702 return selectWaveReduceMax(ResVReg, ResType, I, /*IsUnsigned*/ true);
5703 case Intrinsic::spv_wave_reduce_max:
5704 return selectWaveReduceMax(ResVReg, ResType, I, /*IsUnsigned*/ false);
5705 case Intrinsic::spv_wave_reduce_umin:
5706 return selectWaveReduceMin(ResVReg, ResType, I, /*IsUnsigned*/ true);
5707 case Intrinsic::spv_wave_reduce_min:
5708 return selectWaveReduceMin(ResVReg, ResType, I, /*IsUnsigned*/ false);
5709 case Intrinsic::spv_wave_reduce_sum:
5710 return selectWaveReduceSum(ResVReg, ResType, I);
5711 case Intrinsic::spv_wave_product:
5712 return selectWaveReduceProduct(ResVReg, ResType, I);
5713 case Intrinsic::spv_wave_readlane:
5714 return selectWaveOpInst(ResVReg, ResType, I,
5715 Opcode: SPIRV::OpGroupNonUniformShuffle);
5716 case Intrinsic::spv_wave_prefix_sum:
5717 return selectWaveExclusiveScanSum(ResVReg, ResType, I);
5718 case Intrinsic::spv_wave_prefix_product:
5719 return selectWaveExclusiveScanProduct(ResVReg, ResType, I);
5720 case Intrinsic::spv_quad_read_across_x: {
5721 return selectQuadSwap(ResVReg, ResType, I, /*Direction*/ 0);
5722 }
5723 case Intrinsic::spv_quad_read_across_y: {
5724 return selectQuadSwap(ResVReg, ResType, I, /*Direction*/ 1);
5725 }
5726 case Intrinsic::spv_quad_read_across_diagonal: {
5727 return selectQuadSwap(ResVReg, ResType, I, /*Direction*/ 2);
5728 }
5729 case Intrinsic::spv_radians:
5730 return selectExtInst(ResVReg, ResType, I, CLInst: CL::radians, GLInst: GL::Radians);
5731 // Discard intrinsics which we do not expect to actually represent code after
5732 // lowering or intrinsics which are not implemented but should not crash when
5733 // found in a customer's LLVM IR input.
5734 case Intrinsic::instrprof_increment:
5735 case Intrinsic::instrprof_increment_step:
5736 case Intrinsic::instrprof_value_profile:
5737 break;
5738 // Discard internal intrinsics.
5739 case Intrinsic::spv_value_md:
5740 break;
5741 case Intrinsic::spv_resource_handlefrombinding: {
5742 return selectHandleFromBinding(ResVReg, ResType, I);
5743 }
5744 case Intrinsic::spv_resource_counterhandlefrombinding:
5745 return selectCounterHandleFromBinding(ResVReg, ResType, I);
5746 case Intrinsic::spv_resource_updatecounter:
5747 return selectUpdateCounter(ResVReg, ResType, I);
5748 case Intrinsic::spv_resource_store_typedbuffer: {
5749 return selectImageWriteIntrinsic(I);
5750 }
5751 case Intrinsic::spv_resource_load_typedbuffer: {
5752 return selectReadImageIntrinsic(ResVReg, ResType, I);
5753 }
5754 case Intrinsic::spv_resource_load_level: {
5755 return selectLoadLevelIntrinsic(ResVReg, ResType, I);
5756 }
5757 case Intrinsic::spv_resource_getdimensions_x:
5758 case Intrinsic::spv_resource_getdimensions_xy:
5759 case Intrinsic::spv_resource_getdimensions_xyz: {
5760 return selectGetDimensionsIntrinsic(ResVReg, ResType, I);
5761 }
5762 case Intrinsic::spv_resource_getdimensions_levels_x:
5763 case Intrinsic::spv_resource_getdimensions_levels_xy:
5764 case Intrinsic::spv_resource_getdimensions_levels_xyz: {
5765 return selectGetDimensionsLevelsIntrinsic(ResVReg, ResType, I);
5766 }
5767 case Intrinsic::spv_resource_getdimensions_ms_xy:
5768 case Intrinsic::spv_resource_getdimensions_ms_xyz: {
5769 return selectGetDimensionsMSIntrinsic(ResVReg, ResType, I);
5770 }
5771 case Intrinsic::spv_resource_calculate_lod:
5772 case Intrinsic::spv_resource_calculate_lod_unclamped:
5773 return selectCalculateLodIntrinsic(ResVReg, ResType, I);
5774 case Intrinsic::spv_resource_sample:
5775 case Intrinsic::spv_resource_sample_clamp:
5776 return selectSampleBasicIntrinsic(ResVReg, ResType, I);
5777 case Intrinsic::spv_resource_samplebias:
5778 case Intrinsic::spv_resource_samplebias_clamp:
5779 return selectSampleBiasIntrinsic(ResVReg, ResType, I);
5780 case Intrinsic::spv_resource_samplegrad:
5781 case Intrinsic::spv_resource_samplegrad_clamp:
5782 return selectSampleGradIntrinsic(ResVReg, ResType, I);
5783 case Intrinsic::spv_resource_samplelevel:
5784 return selectSampleLevelIntrinsic(ResVReg, ResType, I);
5785 case Intrinsic::spv_resource_samplecmp:
5786 case Intrinsic::spv_resource_samplecmp_clamp:
5787 return selectSampleCmpIntrinsic(ResVReg, ResType, I);
5788 case Intrinsic::spv_resource_samplecmplevelzero:
5789 return selectSampleCmpLevelZeroIntrinsic(ResVReg, ResType, I);
5790 case Intrinsic::spv_resource_gather:
5791 case Intrinsic::spv_resource_gather_cmp:
5792 return selectGatherIntrinsic(ResVReg, ResType, I);
5793 case Intrinsic::spv_resource_getbasepointer:
5794 case Intrinsic::spv_resource_getpointer: {
5795 return selectResourceGetPointer(ResVReg, ResType, I);
5796 }
5797 case Intrinsic::spv_pushconstant_getpointer: {
5798 return selectPushConstantGetPointer(ResVReg, ResType, I);
5799 }
5800 case Intrinsic::spv_discard: {
5801 return selectDiscard(ResVReg, ResType, I);
5802 }
5803 case Intrinsic::spv_resource_nonuniformindex: {
5804 return selectResourceNonUniformIndex(ResVReg, ResType, I);
5805 }
5806 case Intrinsic::spv_unpackhalf2x16: {
5807 return selectExtInst(ResVReg, ResType, I, GLInst: GL::UnpackHalf2x16);
5808 }
5809 case Intrinsic::spv_packhalf2x16: {
5810 return selectExtInst(ResVReg, ResType, I, GLInst: GL::PackHalf2x16);
5811 }
5812 case Intrinsic::spv_ddx:
5813 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdx);
5814 case Intrinsic::spv_ddy:
5815 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdy);
5816 case Intrinsic::spv_ddx_coarse:
5817 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdxCoarse);
5818 case Intrinsic::spv_ddy_coarse:
5819 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdyCoarse);
5820 case Intrinsic::spv_ddx_fine:
5821 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdxFine);
5822 case Intrinsic::spv_ddy_fine:
5823 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdyFine);
5824 case Intrinsic::spv_fwidth:
5825 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpFwidth);
5826 case Intrinsic::spv_masked_gather:
5827 if (STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_masked_gather_scatter))
5828 return selectMaskedGather(ResVReg, ResType, I);
5829 return diagnoseUnsupported(
5830 I, Msg: "llvm.masked.gather requires SPV_INTEL_masked_gather_scatter");
5831 case Intrinsic::spv_masked_scatter:
5832 if (STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_masked_gather_scatter))
5833 return selectMaskedScatter(I);
5834 return diagnoseUnsupported(
5835 I, Msg: "llvm.masked.scatter requires SPV_INTEL_masked_gather_scatter");
5836 case Intrinsic::returnaddress:
5837 case Intrinsic::frameaddress: {
5838 // SPIR-V does not have a stack or return address. Lower to null.
5839 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
5840 .addDef(RegNo: ResVReg)
5841 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5842 MIB.constrainAllUses(TII, TRI, RBI);
5843 return true;
5844 }
5845 default:
5846 return diagnoseUnsupported(I, Msg: "intrinsic selection not implemented.");
5847 }
5848 return true;
5849}
5850
5851bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
5852 SPIRVTypeInst ResType,
5853 MachineInstr &I) const {
5854 // The images need to be loaded in the same basic block as their use. We defer
5855 // loading the image to the intrinsic that uses it.
5856 if (ResType->getOpcode() == SPIRV::OpTypeImage)
5857 return true;
5858
5859 return loadHandleBeforePosition(HandleReg&: ResVReg, ResType: GR.getSPIRVTypeForVReg(VReg: ResVReg),
5860 HandleDef&: *cast<GIntrinsic>(Val: &I), Pos&: I);
5861}
5862
5863bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
5864 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
5865 auto &Intr = cast<GIntrinsic>(Val&: I);
5866 assert(Intr.getIntrinsicID() ==
5867 Intrinsic::spv_resource_counterhandlefrombinding);
5868
5869 // Extract information from the intrinsic call.
5870 Register MainHandleReg = Intr.getOperand(i: 2).getReg();
5871 auto *MainHandleDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: MainHandleReg));
5872 assert(MainHandleDef->getIntrinsicID() ==
5873 Intrinsic::spv_resource_handlefrombinding);
5874
5875 uint32_t Set = getIConstVal(ConstReg: Intr.getOperand(i: 3).getReg(), MRI);
5876 uint32_t Binding = getIConstVal(ConstReg: Intr.getOperand(i: 4).getReg(), MRI);
5877 uint32_t ArraySize = getIConstVal(ConstReg: MainHandleDef->getOperand(i: 4).getReg(), MRI);
5878 Register IndexReg = MainHandleDef->getOperand(i: 5).getReg();
5879 std::string CounterName =
5880 getStringValueFromReg(Reg: MainHandleDef->getOperand(i: 6).getReg(), MRI&: *MRI) +
5881 ".counter";
5882
5883 // Create the counter variable.
5884 MachineIRBuilder MIRBuilder(I);
5885 Register CounterVarReg =
5886 buildPointerToResource(ResType: SPIRVTypeInst(GR.getPointeeType(PtrType: ResType)),
5887 SC: GR.getPointerStorageClass(Type: ResType), Set, Binding,
5888 ArraySize, IndexReg, Name: CounterName, MIRBuilder);
5889
5890 return BuildCOPY(DestReg: ResVReg, SrcReg: CounterVarReg, I);
5891}
5892
5893bool SPIRVInstructionSelector::selectUpdateCounter(Register &ResVReg,
5894 SPIRVTypeInst ResType,
5895 MachineInstr &I) const {
5896 auto &Intr = cast<GIntrinsic>(Val&: I);
5897 assert(Intr.getIntrinsicID() == Intrinsic::spv_resource_updatecounter);
5898
5899 Register CounterHandleReg = Intr.getOperand(i: 2).getReg();
5900 Register IncrReg = Intr.getOperand(i: 3).getReg();
5901
5902 // The counter handle is a pointer to the counter variable (which is a struct
5903 // containing an i32). We need to get a pointer to that i32 member to do the
5904 // atomic operation.
5905#ifndef NDEBUG
5906 SPIRVTypeInst CounterVarType = GR.getSPIRVTypeForVReg(CounterHandleReg);
5907 SPIRVTypeInst CounterVarPointeeType = GR.getPointeeType(CounterVarType);
5908 assert(CounterVarPointeeType &&
5909 CounterVarPointeeType->getOpcode() == SPIRV::OpTypeStruct &&
5910 "Counter variable must be a struct");
5911 assert(GR.getPointerStorageClass(CounterVarType) ==
5912 SPIRV::StorageClass::StorageBuffer &&
5913 "Counter variable must be in the storage buffer storage class");
5914 assert(CounterVarPointeeType->getNumOperands() == 2 &&
5915 "Counter variable must have exactly 1 member in the struct");
5916 const SPIRVTypeInst MemberType =
5917 GR.getSPIRVTypeForVReg(CounterVarPointeeType->getOperand(1).getReg());
5918 assert(MemberType->getOpcode() == SPIRV::OpTypeInt &&
5919 "Counter variable struct must have a single i32 member");
5920#endif
5921
5922 // The struct has a single i32 member.
5923 MachineIRBuilder MIRBuilder(I);
5924 const Type *LLVMIntType =
5925 Type::getInt32Ty(C&: I.getMF()->getFunction().getContext());
5926
5927 SPIRVTypeInst IntPtrType = GR.getOrCreateSPIRVPointerType(
5928 BaseType: LLVMIntType, MIRBuilder, SC: SPIRV::StorageClass::StorageBuffer);
5929
5930 Register Zero = buildI32Constant(Val: 0, I);
5931
5932 Register PtrToCounter =
5933 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: IntPtrType));
5934 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAccessChain))
5935 .addDef(RegNo: PtrToCounter)
5936 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: IntPtrType))
5937 .addUse(RegNo: CounterHandleReg)
5938 .addUse(RegNo: Zero)
5939 .constrainAllUses(TII, TRI, RBI);
5940
5941 // For UAV/SSBO counters, the scope is Device. The counter variable is not
5942 // used as a flag. So the memory semantics can be None.
5943 Register Scope = buildI32Constant(Val: SPIRV::Scope::Device, I);
5944 Register Semantics = buildI32Constant(Val: SPIRV::MemorySemantics::None, I);
5945
5946 int64_t IncrVal = getIConstValSext(ConstReg: IncrReg, MRI);
5947 Register Incr = buildI32Constant(Val: static_cast<uint32_t>(IncrVal), I);
5948
5949 Register AtomicRes = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
5950 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAtomicIAdd))
5951 .addDef(RegNo: AtomicRes)
5952 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5953 .addUse(RegNo: PtrToCounter)
5954 .addUse(RegNo: Scope)
5955 .addUse(RegNo: Semantics)
5956 .addUse(RegNo: Incr)
5957 .constrainAllUses(TII, TRI, RBI);
5958 if (IncrVal >= 0) {
5959 return BuildCOPY(DestReg: ResVReg, SrcReg: AtomicRes, I);
5960 }
5961
5962 // In HLSL, IncrementCounter returns the value *before* the increment, while
5963 // DecrementCounter returns the value *after* the decrement. Both are lowered
5964 // to the same atomic intrinsic which returns the value *before* the
5965 // operation. So for decrements (negative IncrVal), we must subtract the
5966 // increment value from the result to get the post-decrement value.
5967 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
5968 .addDef(RegNo: ResVReg)
5969 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5970 .addUse(RegNo: AtomicRes)
5971 .addUse(RegNo: Incr)
5972 .constrainAllUses(TII, TRI, RBI);
5973 return true;
5974}
5975bool SPIRVInstructionSelector::selectReadImageIntrinsic(Register &ResVReg,
5976 SPIRVTypeInst ResType,
5977 MachineInstr &I) const {
5978
5979 // If the load of the image is in a different basic block, then
5980 // this will generate invalid code. A proper solution is to move
5981 // the OpLoad from selectHandleFromBinding here. However, to do
5982 // that we will need to change the return type of the intrinsic.
5983 // We will do that when we can, but for now trying to move forward with other
5984 // issues.
5985 Register ImageReg = I.getOperand(i: 2).getReg();
5986 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
5987 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
5988 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
5989 HandleDef&: *ImageDef, Pos&: I)) {
5990 return false;
5991 }
5992
5993 Register IdxReg = I.getOperand(i: 3).getReg();
5994 DebugLoc Loc = I.getDebugLoc();
5995 MachineInstr &Pos = I;
5996
5997 return generateImageReadOrFetch(ResVReg, ResType, ImageReg: NewImageReg, IdxReg, Loc,
5998 Pos);
5999}
6000
6001bool SPIRVInstructionSelector::generateSampleImage(
6002 Register ResVReg, SPIRVTypeInst ResType, Register ImageReg,
6003 Register SamplerReg, Register CoordinateReg, const ImageOperands &ImOps,
6004 DebugLoc Loc, MachineInstr &Pos) const {
6005 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6006 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6007 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6008 HandleDef&: *ImageDef, Pos)) {
6009 return false;
6010 }
6011
6012 auto *SamplerDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: SamplerReg));
6013 Register NewSamplerReg =
6014 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: SamplerReg));
6015 if (!loadHandleBeforePosition(HandleReg&: NewSamplerReg,
6016 ResType: GR.getSPIRVTypeForVReg(VReg: SamplerReg), HandleDef&: *SamplerDef,
6017 Pos)) {
6018 return false;
6019 }
6020
6021 MachineIRBuilder MIRBuilder(Pos);
6022 SPIRVTypeInst SampledImageType = GR.getOrCreateOpTypeSampledImage(
6023 ImageType: GR.getSPIRVTypeForVReg(VReg: ImageReg), MIRBuilder);
6024 Register SampledImageReg =
6025 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: SampledImageType));
6026
6027 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode: SPIRV::OpSampledImage))
6028 .addDef(RegNo: SampledImageReg)
6029 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SampledImageType))
6030 .addUse(RegNo: NewImageReg)
6031 .addUse(RegNo: NewSamplerReg)
6032 .constrainAllUses(TII, TRI, RBI);
6033
6034 bool IsExplicitLod = ImOps.GradX.has_value() || ImOps.GradY.has_value() ||
6035 ImOps.Lod.has_value();
6036 unsigned Opcode = IsExplicitLod ? SPIRV::OpImageSampleExplicitLod
6037 : SPIRV::OpImageSampleImplicitLod;
6038 if (ImOps.Compare)
6039 Opcode = IsExplicitLod ? SPIRV::OpImageSampleDrefExplicitLod
6040 : SPIRV::OpImageSampleDrefImplicitLod;
6041
6042 auto MIB = BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode))
6043 .addDef(RegNo: ResVReg)
6044 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6045 .addUse(RegNo: SampledImageReg)
6046 .addUse(RegNo: CoordinateReg);
6047
6048 if (ImOps.Compare)
6049 MIB.addUse(RegNo: *ImOps.Compare);
6050
6051 uint32_t ImageOperands = 0;
6052 if (ImOps.Bias)
6053 ImageOperands |= SPIRV::ImageOperand::Bias;
6054 if (ImOps.Lod)
6055 ImageOperands |= SPIRV::ImageOperand::Lod;
6056 if (ImOps.GradX && ImOps.GradY)
6057 ImageOperands |= SPIRV::ImageOperand::Grad;
6058 if (ImOps.Offset && !isScalarOrVectorIntConstantZero(Reg: *ImOps.Offset)) {
6059 if (isConstReg(MRI, OpReg: *ImOps.Offset))
6060 ImageOperands |= SPIRV::ImageOperand::ConstOffset;
6061 else {
6062 Pos.emitGenericError(
6063 ErrMsg: "Non-constant offsets are not supported in sample instructions.");
6064 return false;
6065 }
6066 }
6067 if (ImOps.MinLod)
6068 ImageOperands |= SPIRV::ImageOperand::MinLod;
6069
6070 if (ImageOperands != 0) {
6071 MIB.addImm(Val: ImageOperands);
6072 if (ImageOperands & SPIRV::ImageOperand::Bias)
6073 MIB.addUse(RegNo: *ImOps.Bias);
6074 if (ImageOperands & SPIRV::ImageOperand::Lod)
6075 MIB.addUse(RegNo: *ImOps.Lod);
6076 if (ImageOperands & SPIRV::ImageOperand::Grad) {
6077 MIB.addUse(RegNo: *ImOps.GradX);
6078 MIB.addUse(RegNo: *ImOps.GradY);
6079 }
6080 if (ImageOperands &
6081 (SPIRV::ImageOperand::ConstOffset | SPIRV::ImageOperand::Offset))
6082 MIB.addUse(RegNo: *ImOps.Offset);
6083 if (ImageOperands & SPIRV::ImageOperand::MinLod)
6084 MIB.addUse(RegNo: *ImOps.MinLod);
6085 }
6086
6087 MIB.constrainAllUses(TII, TRI, RBI);
6088 return true;
6089}
6090
6091bool SPIRVInstructionSelector::selectImageQuerySize(
6092 Register ImageReg, Register &ResVReg, MachineInstr &I,
6093 std::optional<Register> LodReg) const {
6094 unsigned Opcode =
6095 LodReg ? SPIRV::OpImageQuerySizeLod : SPIRV::OpImageQuerySize;
6096 SPIRVTypeInst ImageType = GR.getSPIRVTypeForVReg(VReg: ImageReg);
6097 assert(ImageType && ImageType->getOpcode() == SPIRV::OpTypeImage &&
6098 "ImageReg is not an image type.");
6099
6100 auto Dim = static_cast<SPIRV::Dim::Dim>(ImageType->getOperand(i: 2).getImm());
6101 bool IsArray = ImageType->getOperand(i: 4).getImm() != 0;
6102 unsigned NumComponents = 0;
6103 switch (Dim) {
6104 case SPIRV::Dim::DIM_1D:
6105 case SPIRV::Dim::DIM_Buffer:
6106 NumComponents = IsArray ? 2 : 1;
6107 break;
6108 case SPIRV::Dim::DIM_2D:
6109 case SPIRV::Dim::DIM_Cube:
6110 case SPIRV::Dim::DIM_Rect:
6111 NumComponents = IsArray ? 3 : 2;
6112 break;
6113 case SPIRV::Dim::DIM_3D:
6114 NumComponents = 3;
6115 break;
6116 default:
6117 I.emitGenericError(ErrMsg: "Unsupported image dimension for OpImageQuerySize.");
6118 return false;
6119 }
6120
6121 SPIRVTypeInst I32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
6122 SPIRVTypeInst ResType =
6123 NumComponents == 1
6124 ? I32Ty
6125 : GR.getOrCreateSPIRVVectorType(BaseType: I32Ty, NumElements: NumComponents, I, TII);
6126
6127 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
6128 .addDef(RegNo: ResVReg)
6129 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6130 .addUse(RegNo: ImageReg);
6131 if (LodReg)
6132 MIB.addUse(RegNo: *LodReg);
6133 MIB.constrainAllUses(TII, TRI, RBI);
6134 return true;
6135}
6136
6137bool SPIRVInstructionSelector::selectGetDimensionsIntrinsic(
6138 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6139 Register ImageReg = I.getOperand(i: 2).getReg();
6140 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6141 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6142 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6143 HandleDef&: *ImageDef, Pos&: I)) {
6144 return false;
6145 }
6146 return selectImageQuerySize(ImageReg: NewImageReg, ResVReg, I);
6147}
6148
6149bool SPIRVInstructionSelector::selectGetDimensionsLevelsIntrinsic(
6150 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6151 Register ImageReg = I.getOperand(i: 2).getReg();
6152 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6153 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6154 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6155 HandleDef&: *ImageDef, Pos&: I)) {
6156 return false;
6157 }
6158
6159 Register SizeReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
6160 Register LodReg = I.getOperand(i: 3).getReg();
6161
6162 assert(GR.getSPIRVTypeForVReg(NewImageReg)->getOperand(6).getImm() == 1 &&
6163 "OpImageQuerySizeLod and OpImageQueryLevels require a sampled image");
6164
6165 if (!selectImageQuerySize(ImageReg: NewImageReg, ResVReg&: SizeReg, I, LodReg)) {
6166 return false;
6167 }
6168
6169 SPIRVTypeInst I32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
6170 Register LevelsReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
6171 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6172 MCID: TII.get(Opcode: SPIRV::OpImageQueryLevels))
6173 .addDef(RegNo: LevelsReg)
6174 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: I32Ty))
6175 .addUse(RegNo: NewImageReg)
6176 .constrainAllUses(TII, TRI, RBI);
6177
6178 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6179 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
6180 .addDef(RegNo: ResVReg)
6181 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6182 .addUse(RegNo: SizeReg)
6183 .addUse(RegNo: LevelsReg)
6184 .constrainAllUses(TII, TRI, RBI);
6185
6186 return true;
6187}
6188
6189bool SPIRVInstructionSelector::selectGetDimensionsMSIntrinsic(
6190 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6191 Register ImageReg = I.getOperand(i: 2).getReg();
6192 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6193 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6194 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6195 HandleDef&: *ImageDef, Pos&: I)) {
6196 return false;
6197 }
6198
6199 Register SizeReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
6200
6201 assert(GR.getSPIRVTypeForVReg(NewImageReg)->getOperand(5).getImm() == 1 &&
6202 "OpImageQuerySamples requires a multisampled image");
6203
6204 if (!selectImageQuerySize(ImageReg: NewImageReg, ResVReg&: SizeReg, I)) {
6205 return false;
6206 }
6207
6208 Register SamplesReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
6209
6210 SPIRVTypeInst I32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
6211 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6212 MCID: TII.get(Opcode: SPIRV::OpImageQuerySamples))
6213 .addDef(RegNo: SamplesReg)
6214 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: I32Ty))
6215 .addUse(RegNo: NewImageReg)
6216 .constrainAllUses(TII, TRI, RBI);
6217
6218 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6219 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
6220 .addDef(RegNo: ResVReg)
6221 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6222 .addUse(RegNo: SizeReg)
6223 .addUse(RegNo: SamplesReg)
6224 .constrainAllUses(TII, TRI, RBI);
6225
6226 return true;
6227}
6228
6229bool SPIRVInstructionSelector::selectCalculateLodIntrinsic(
6230 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6231 Register ImageReg = I.getOperand(i: 2).getReg();
6232 Register SamplerReg = I.getOperand(i: 3).getReg();
6233 Register CoordinateReg = I.getOperand(i: 4).getReg();
6234
6235 auto *ImageDef = dyn_cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6236 if (!ImageDef)
6237 return false;
6238 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6239 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6240 HandleDef&: *ImageDef, Pos&: I)) {
6241 return false;
6242 }
6243
6244 auto *SamplerDef = dyn_cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: SamplerReg));
6245 if (!SamplerDef)
6246 return false;
6247 Register NewSamplerReg =
6248 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: SamplerReg));
6249 if (!loadHandleBeforePosition(
6250 HandleReg&: NewSamplerReg, ResType: GR.getSPIRVTypeForVReg(VReg: SamplerReg), HandleDef&: *SamplerDef, Pos&: I)) {
6251 return false;
6252 }
6253
6254 MachineIRBuilder MIRBuilder(I);
6255 SPIRVTypeInst SampledImageType = GR.getOrCreateOpTypeSampledImage(
6256 ImageType: GR.getSPIRVTypeForVReg(VReg: ImageReg), MIRBuilder);
6257 Register SampledImageReg =
6258 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: SampledImageType));
6259
6260 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSampledImage))
6261 .addDef(RegNo: SampledImageReg)
6262 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SampledImageType))
6263 .addUse(RegNo: NewImageReg)
6264 .addUse(RegNo: NewSamplerReg)
6265 .constrainAllUses(TII, TRI, RBI);
6266
6267 SPIRVTypeInst Vec2Ty = GR.getOrCreateSPIRVVectorType(BaseType: ResType, NumElements: 2, I, TII);
6268 Register QueryResultReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Vec2Ty));
6269
6270 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpImageQueryLod))
6271 .addDef(RegNo: QueryResultReg)
6272 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Vec2Ty))
6273 .addUse(RegNo: SampledImageReg)
6274 .addUse(RegNo: CoordinateReg)
6275 .constrainAllUses(TII, TRI, RBI);
6276
6277 unsigned ExtractedIndex =
6278 cast<GIntrinsic>(Val&: I).getIntrinsicID() ==
6279 Intrinsic::spv_resource_calculate_lod_unclamped
6280 ? 1
6281 : 0;
6282
6283 MachineInstrBuilder MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6284 MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
6285 .addDef(RegNo: ResVReg)
6286 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6287 .addUse(RegNo: QueryResultReg)
6288 .addImm(Val: ExtractedIndex);
6289
6290 MIB.constrainAllUses(TII, TRI, RBI);
6291 return true;
6292}
6293
6294bool SPIRVInstructionSelector::selectSampleBasicIntrinsic(
6295 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6296 Register ImageReg = I.getOperand(i: 2).getReg();
6297 Register SamplerReg = I.getOperand(i: 3).getReg();
6298 Register CoordinateReg = I.getOperand(i: 4).getReg();
6299 ImageOperands ImOps;
6300 if (I.getNumOperands() > 5)
6301 ImOps.Offset = I.getOperand(i: 5).getReg();
6302 if (I.getNumOperands() > 6)
6303 ImOps.MinLod = I.getOperand(i: 6).getReg();
6304 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6305 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6306}
6307
6308bool SPIRVInstructionSelector::selectSampleBiasIntrinsic(
6309 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6310 Register ImageReg = I.getOperand(i: 2).getReg();
6311 Register SamplerReg = I.getOperand(i: 3).getReg();
6312 Register CoordinateReg = I.getOperand(i: 4).getReg();
6313 ImageOperands ImOps;
6314 ImOps.Bias = I.getOperand(i: 5).getReg();
6315 if (I.getNumOperands() > 6)
6316 ImOps.Offset = I.getOperand(i: 6).getReg();
6317 if (I.getNumOperands() > 7)
6318 ImOps.MinLod = I.getOperand(i: 7).getReg();
6319 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6320 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6321}
6322
6323bool SPIRVInstructionSelector::selectSampleGradIntrinsic(
6324 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6325 Register ImageReg = I.getOperand(i: 2).getReg();
6326 Register SamplerReg = I.getOperand(i: 3).getReg();
6327 Register CoordinateReg = I.getOperand(i: 4).getReg();
6328 ImageOperands ImOps;
6329 ImOps.GradX = I.getOperand(i: 5).getReg();
6330 ImOps.GradY = I.getOperand(i: 6).getReg();
6331 if (I.getNumOperands() > 7)
6332 ImOps.Offset = I.getOperand(i: 7).getReg();
6333 if (I.getNumOperands() > 8)
6334 ImOps.MinLod = I.getOperand(i: 8).getReg();
6335 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6336 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6337}
6338
6339bool SPIRVInstructionSelector::selectSampleLevelIntrinsic(
6340 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6341 Register ImageReg = I.getOperand(i: 2).getReg();
6342 Register SamplerReg = I.getOperand(i: 3).getReg();
6343 Register CoordinateReg = I.getOperand(i: 4).getReg();
6344 ImageOperands ImOps;
6345 ImOps.Lod = I.getOperand(i: 5).getReg();
6346 if (I.getNumOperands() > 6)
6347 ImOps.Offset = I.getOperand(i: 6).getReg();
6348 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6349 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6350}
6351
6352bool SPIRVInstructionSelector::selectSampleCmpIntrinsic(Register &ResVReg,
6353 SPIRVTypeInst ResType,
6354 MachineInstr &I) const {
6355 Register ImageReg = I.getOperand(i: 2).getReg();
6356 Register SamplerReg = I.getOperand(i: 3).getReg();
6357 Register CoordinateReg = I.getOperand(i: 4).getReg();
6358 ImageOperands ImOps;
6359 ImOps.Compare = I.getOperand(i: 5).getReg();
6360 if (I.getNumOperands() > 6)
6361 ImOps.Offset = I.getOperand(i: 6).getReg();
6362 if (I.getNumOperands() > 7)
6363 ImOps.MinLod = I.getOperand(i: 7).getReg();
6364 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6365 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6366}
6367
6368bool SPIRVInstructionSelector::selectLoadLevelIntrinsic(Register &ResVReg,
6369 SPIRVTypeInst ResType,
6370 MachineInstr &I) const {
6371 Register ImageReg = I.getOperand(i: 2).getReg();
6372 Register CoordinateReg = I.getOperand(i: 3).getReg();
6373 Register LodReg = I.getOperand(i: 4).getReg();
6374
6375 ImageOperands ImOps;
6376 ImOps.Lod = LodReg;
6377 if (I.getNumOperands() > 5)
6378 ImOps.Offset = I.getOperand(i: 5).getReg();
6379
6380 auto *ImageDef = dyn_cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6381 if (!ImageDef)
6382 return false;
6383
6384 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6385 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6386 HandleDef&: *ImageDef, Pos&: I)) {
6387 return false;
6388 }
6389
6390 return generateImageReadOrFetch(ResVReg, ResType, ImageReg: NewImageReg, IdxReg: CoordinateReg,
6391 Loc: I.getDebugLoc(), Pos&: I, ImOps: &ImOps);
6392}
6393
6394bool SPIRVInstructionSelector::selectSampleCmpLevelZeroIntrinsic(
6395 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6396 Register ImageReg = I.getOperand(i: 2).getReg();
6397 Register SamplerReg = I.getOperand(i: 3).getReg();
6398 Register CoordinateReg = I.getOperand(i: 4).getReg();
6399 ImageOperands ImOps;
6400 ImOps.Compare = I.getOperand(i: 5).getReg();
6401 if (I.getNumOperands() > 6)
6402 ImOps.Offset = I.getOperand(i: 6).getReg();
6403 SPIRVTypeInst FloatTy = GR.getOrCreateSPIRVFloatType(BitWidth: 32, I, TII);
6404 ImOps.Lod = GR.getOrCreateConstFP(Val: APFloat(0.0f), I, SpvType: FloatTy, TII);
6405 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6406 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6407}
6408
6409bool SPIRVInstructionSelector::selectGatherIntrinsic(Register &ResVReg,
6410 SPIRVTypeInst ResType,
6411 MachineInstr &I) const {
6412 Register ImageReg = I.getOperand(i: 2).getReg();
6413 Register SamplerReg = I.getOperand(i: 3).getReg();
6414 Register CoordinateReg = I.getOperand(i: 4).getReg();
6415 SPIRVTypeInst ImageType = GR.getSPIRVTypeForVReg(VReg: ImageReg);
6416 assert(ImageType && ImageType->getOpcode() == SPIRV::OpTypeImage &&
6417 "ImageReg is not an image type.");
6418
6419 Register ComponentOrCompareReg;
6420 Register OffsetReg;
6421
6422 ComponentOrCompareReg = I.getOperand(i: 5).getReg();
6423 OffsetReg = I.getOperand(i: 6).getReg();
6424 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6425 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6426 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: ImageType, HandleDef&: *ImageDef, Pos&: I)) {
6427 return false;
6428 }
6429
6430 auto Dim = static_cast<SPIRV::Dim::Dim>(ImageType->getOperand(i: 2).getImm());
6431 if (Dim != SPIRV::Dim::DIM_2D && Dim != SPIRV::Dim::DIM_Cube &&
6432 Dim != SPIRV::Dim::DIM_Rect) {
6433 I.emitGenericError(
6434 ErrMsg: "Gather operations are only supported for 2D, Cube, and Rect images.");
6435 return false;
6436 }
6437
6438 auto *SamplerDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: SamplerReg));
6439 Register NewSamplerReg =
6440 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: SamplerReg));
6441 if (!loadHandleBeforePosition(
6442 HandleReg&: NewSamplerReg, ResType: GR.getSPIRVTypeForVReg(VReg: SamplerReg), HandleDef&: *SamplerDef, Pos&: I)) {
6443 return false;
6444 }
6445
6446 MachineIRBuilder MIRBuilder(I);
6447 SPIRVTypeInst SampledImageType =
6448 GR.getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
6449 Register SampledImageReg =
6450 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: SampledImageType));
6451
6452 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSampledImage))
6453 .addDef(RegNo: SampledImageReg)
6454 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SampledImageType))
6455 .addUse(RegNo: NewImageReg)
6456 .addUse(RegNo: NewSamplerReg)
6457 .constrainAllUses(TII, TRI, RBI);
6458
6459 auto IntrId = cast<GIntrinsic>(Val&: I).getIntrinsicID();
6460 bool IsGatherCmp = IntrId == Intrinsic::spv_resource_gather_cmp;
6461 unsigned Opcode =
6462 IsGatherCmp ? SPIRV::OpImageDrefGather : SPIRV::OpImageGather;
6463
6464 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
6465 .addDef(RegNo: ResVReg)
6466 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6467 .addUse(RegNo: SampledImageReg)
6468 .addUse(RegNo: CoordinateReg)
6469 .addUse(RegNo: ComponentOrCompareReg);
6470
6471 uint32_t ImageOperands = 0;
6472 if (OffsetReg && !isScalarOrVectorIntConstantZero(Reg: OffsetReg)) {
6473 if (Dim == SPIRV::Dim::DIM_Cube) {
6474 I.emitGenericError(
6475 ErrMsg: "Gather operations with offset are not supported for Cube images.");
6476 return false;
6477 }
6478 if (isConstReg(MRI, OpReg: OffsetReg))
6479 ImageOperands |= SPIRV::ImageOperand::ConstOffset;
6480 else {
6481 ImageOperands |= SPIRV::ImageOperand::Offset;
6482 }
6483 }
6484
6485 if (ImageOperands != 0) {
6486 MIB.addImm(Val: ImageOperands);
6487 if (ImageOperands &
6488 (SPIRV::ImageOperand::ConstOffset | SPIRV::ImageOperand::Offset))
6489 MIB.addUse(RegNo: OffsetReg);
6490 }
6491
6492 MIB.constrainAllUses(TII, TRI, RBI);
6493 return true;
6494}
6495
6496bool SPIRVInstructionSelector::generateImageReadOrFetch(
6497 Register &ResVReg, SPIRVTypeInst ResType, Register ImageReg,
6498 Register IdxReg, DebugLoc Loc, MachineInstr &Pos,
6499 const ImageOperands *ImOps) const {
6500 SPIRVTypeInst ImageType = GR.getSPIRVTypeForVReg(VReg: ImageReg);
6501 assert(ImageType && ImageType->getOpcode() == SPIRV::OpTypeImage &&
6502 "ImageReg is not an image type.");
6503
6504 bool IsSignedInteger =
6505 sampledTypeIsSignedInteger(HandleType: GR.getTypeForSPIRVType(Ty: ImageType));
6506 // Check if the "sampled" operand of the image type is 1.
6507 // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpImageFetch
6508 auto SampledOp = ImageType->getOperand(i: 6);
6509 bool IsFetch = (SampledOp.getImm() == 1);
6510
6511 auto AddOperands = [&](MachineInstrBuilder &MIB) {
6512 uint32_t ImageOperandsMask = 0;
6513 if (IsSignedInteger)
6514 ImageOperandsMask |= 0x1000; // SignExtend
6515
6516 if (IsFetch && ImOps) {
6517 if (ImOps->Lod)
6518 ImageOperandsMask |= SPIRV::ImageOperand::Lod;
6519 if (ImOps->Offset && !isScalarOrVectorIntConstantZero(Reg: *ImOps->Offset)) {
6520 if (isConstReg(MRI, OpReg: *ImOps->Offset))
6521 ImageOperandsMask |= SPIRV::ImageOperand::ConstOffset;
6522 else
6523 ImageOperandsMask |= SPIRV::ImageOperand::Offset;
6524 }
6525 }
6526
6527 if (ImageOperandsMask != 0) {
6528 MIB.addImm(Val: ImageOperandsMask);
6529 if (IsFetch && ImOps) {
6530 if (ImOps->Lod)
6531 MIB.addUse(RegNo: *ImOps->Lod);
6532 if (ImOps->Offset &&
6533 (ImageOperandsMask &
6534 (SPIRV::ImageOperand::Offset | SPIRV::ImageOperand::ConstOffset)))
6535 MIB.addUse(RegNo: *ImOps->Offset);
6536 }
6537 }
6538 };
6539
6540 uint64_t ResultSize = GR.getScalarOrVectorComponentCount(Type: ResType);
6541
6542 // A wide element (e.g. int64_t2) is emulated with a narrower packed image, so
6543 // its sampled type is different from the result.
6544 SPIRVTypeInst SampledType =
6545 GR.getSPIRVTypeForVReg(VReg: ImageType->getOperand(i: 1).getReg());
6546 bool IsPacked = SampledType != GR.getScalarOrVectorComponentType(Type: ResType);
6547 SPIRVTypeInst ReadType =
6548 widenTypeToVec4(Type: IsPacked ? SampledType : ResType, I&: Pos);
6549 bool ReadTypeMatchesResult = ReadType == ResType;
6550 // Read directly into the result, or into a temporary to bitcast/extract.
6551 Register ReadReg = ReadTypeMatchesResult
6552 ? ResVReg
6553 : MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ReadType));
6554
6555 auto BMI =
6556 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc,
6557 MCID: TII.get(Opcode: IsFetch ? SPIRV::OpImageFetch : SPIRV::OpImageRead))
6558 .addDef(RegNo: ReadReg)
6559 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ReadType))
6560 .addUse(RegNo: ImageReg)
6561 .addUse(RegNo: IdxReg);
6562 AddOperands(BMI);
6563 BMI.constrainAllUses(TII, TRI, RBI);
6564
6565 if (ReadTypeMatchesResult)
6566 return true;
6567
6568 if (IsPacked) {
6569 // Reinterpret the packed vector as the wide result type.
6570 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode: SPIRV::OpBitcast))
6571 .addDef(RegNo: ResVReg)
6572 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6573 .addUse(RegNo: ReadReg)
6574 .constrainAllUses(TII, TRI, RBI);
6575 return true;
6576 }
6577
6578 if (ResultSize == 1) {
6579 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
6580 .addDef(RegNo: ResVReg)
6581 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6582 .addUse(RegNo: ReadReg)
6583 .addImm(Val: 0)
6584 .constrainAllUses(TII, TRI, RBI);
6585 return true;
6586 }
6587 return extractSubvector(ResVReg, ResType, ReadReg, InsertionPoint&: Pos);
6588}
6589
6590bool SPIRVInstructionSelector::selectResourceGetPointer(Register &ResVReg,
6591 SPIRVTypeInst ResType,
6592 MachineInstr &I) const {
6593 Register ResourcePtr = I.getOperand(i: 2).getReg();
6594 SPIRVTypeInst RegType = GR.getSPIRVTypeForVReg(VReg: ResourcePtr, MF: I.getMF());
6595 if (RegType->getOpcode() == SPIRV::OpTypeImage) {
6596 // For texel buffers, the index into the image is part of the OpImageRead or
6597 // OpImageWrite instructions. So we will do nothing in this case. This
6598 // intrinsic will be combined with the load or store when selecting the load
6599 // or store.
6600 return true;
6601 }
6602
6603 assert(ResType->getOpcode() == SPIRV::OpTypePointer);
6604 MachineIRBuilder MIRBuilder(I);
6605
6606 Register ZeroReg =
6607 buildZerosVal(ResType: GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII), I);
6608 auto MIB =
6609 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAccessChain))
6610 .addDef(RegNo: ResVReg)
6611 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6612 .addUse(RegNo: ResourcePtr)
6613 .addUse(RegNo: ZeroReg);
6614
6615 if (I.getNumExplicitOperands() > 3) {
6616 Register IndexReg = I.getOperand(i: 3).getReg();
6617 MIB.addUse(RegNo: IndexReg);
6618 }
6619 MIB.constrainAllUses(TII, TRI, RBI);
6620 return true;
6621}
6622
6623bool SPIRVInstructionSelector::selectPushConstantGetPointer(
6624 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6625 MRI->replaceRegWith(FromReg: ResVReg, ToReg: I.getOperand(i: 2).getReg());
6626 return true;
6627}
6628
6629bool SPIRVInstructionSelector::selectResourceNonUniformIndex(
6630 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6631 Register ObjReg = I.getOperand(i: 2).getReg();
6632 if (!BuildCOPY(DestReg: ResVReg, SrcReg: ObjReg, I))
6633 return false;
6634
6635 buildOpDecorate(Reg: ResVReg, I, TII, Dec: SPIRV::Decoration::NonUniformEXT, DecArgs: {});
6636 // Check for the registers that use the index marked as non-uniform
6637 // and recursively mark them as non-uniform.
6638 // Per the spec, it's necessary that the final argument used for
6639 // load/store/sample/atomic must be decorated, so we need to propagate the
6640 // decoration through access chains and copies.
6641 // https://docs.vulkan.org/samples/latest/samples/extensions/descriptor_indexing/README.html#_when_to_use_non_uniform_indexing_qualifier
6642 decorateUsesAsNonUniform(NonUniformReg&: ResVReg);
6643 return true;
6644}
6645
6646void SPIRVInstructionSelector::decorateUsesAsNonUniform(
6647 Register &NonUniformReg) const {
6648 llvm::SmallVector<std::pair<Register, MachineInstr *>> WorkList = {
6649 {NonUniformReg, nullptr}};
6650 llvm::SmallSet<Register, 8> Visited;
6651 while (WorkList.size() > 0) {
6652 auto [CurrentReg, DefMI] = WorkList.pop_back_val();
6653
6654 if (!Visited.insert(V: CurrentReg).second)
6655 continue;
6656
6657 bool IsDecorated = false;
6658 for (MachineInstr &Use : MRI->use_instructions(Reg: CurrentReg)) {
6659 if (Use.getOpcode() == SPIRV::OpDecorate &&
6660 Use.getOperand(i: 1).getImm() == SPIRV::Decoration::NonUniformEXT) {
6661 IsDecorated = true;
6662 continue;
6663 }
6664 // Check if the instruction has the result register and add it to the
6665 // worklist.
6666 if (Use.getOperand(i: 0).isReg() && Use.getOperand(i: 0).isDef()) {
6667 Register ResultReg = Use.getOperand(i: 0).getReg();
6668 if (ResultReg == CurrentReg)
6669 continue;
6670 WorkList.push_back(Elt: {ResultReg, &Use});
6671 }
6672 }
6673
6674 if (!IsDecorated) {
6675 MachineBasicBlock &MBB = *DefMI->getParent();
6676 MachineInstr &InsertPt =
6677 DefMI->isPHI() ? *MBB.getFirstNonPHI() : *DefMI->getNextNode();
6678 buildOpDecorate(Reg: CurrentReg, I&: InsertPt, TII,
6679 Dec: SPIRV::Decoration::NonUniformEXT, DecArgs: {});
6680 }
6681 }
6682}
6683
6684bool SPIRVInstructionSelector::extractSubvector(
6685 Register &ResVReg, SPIRVTypeInst ResType, Register &ReadReg,
6686 MachineInstr &InsertionPoint) const {
6687 SPIRVTypeInst InputType = GR.getResultType(VReg: ReadReg);
6688 [[maybe_unused]] uint64_t InputSize =
6689 GR.getScalarOrVectorComponentCount(Type: InputType);
6690 uint64_t ResultSize = GR.getScalarOrVectorComponentCount(Type: ResType);
6691 [[maybe_unused]] bool IsLongVectorEXT =
6692 STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_long_vector);
6693 assert((InputSize > 1 || IsLongVectorEXT) && "The input must be a vector.");
6694 assert((ResultSize > 1 || IsLongVectorEXT) && "The result must be a vector.");
6695 assert(ResultSize < InputSize &&
6696 "Cannot extract more element than there are in the input.");
6697 SmallVector<Register> ComponentRegisters;
6698 SPIRVTypeInst ScalarType = GR.getScalarOrVectorComponentType(Type: ResType);
6699 const TargetRegisterClass *ScalarRegClass = GR.getRegClass(SpvType: ScalarType);
6700 for (uint64_t I = 0; I < ResultSize; I++) {
6701 Register ComponentReg = MRI->createVirtualRegister(RegClass: ScalarRegClass);
6702 BuildMI(BB&: *InsertionPoint.getParent(), I&: InsertionPoint,
6703 MIMD: InsertionPoint.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
6704 .addDef(RegNo: ComponentReg)
6705 .addUse(RegNo: ScalarType->getOperand(i: 0).getReg())
6706 .addUse(RegNo: ReadReg)
6707 .addImm(Val: I)
6708 .constrainAllUses(TII, TRI, RBI);
6709 ComponentRegisters.emplace_back(Args&: ComponentReg);
6710 }
6711
6712 MachineInstrBuilder MIB = BuildMI(BB&: *InsertionPoint.getParent(), I&: InsertionPoint,
6713 MIMD: InsertionPoint.getDebugLoc(),
6714 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
6715 .addDef(RegNo: ResVReg)
6716 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
6717
6718 for (Register ComponentReg : ComponentRegisters)
6719 MIB.addUse(RegNo: ComponentReg);
6720 MIB.constrainAllUses(TII, TRI, RBI);
6721 return true;
6722}
6723
6724bool SPIRVInstructionSelector::selectImageWriteIntrinsic(
6725 MachineInstr &I) const {
6726 // If the load of the image is in a different basic block, then
6727 // this will generate invalid code. A proper solution is to move
6728 // the OpLoad from selectHandleFromBinding here. However, to do
6729 // that we will need to change the return type of the intrinsic.
6730 // We will do that when we can, but for now trying to move forward with other
6731 // issues.
6732 Register ImageReg = I.getOperand(i: 1).getReg();
6733 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6734 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6735 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6736 HandleDef&: *ImageDef, Pos&: I)) {
6737 return false;
6738 }
6739
6740 Register CoordinateReg = I.getOperand(i: 2).getReg();
6741 Register DataReg = I.getOperand(i: 3).getReg();
6742 assert(isVectorType(GR.getResultType(DataReg)));
6743 assert(GR.getScalarOrVectorComponentCount(GR.getResultType(DataReg)) == 4);
6744 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpImageWrite))
6745 .addUse(RegNo: NewImageReg)
6746 .addUse(RegNo: CoordinateReg)
6747 .addUse(RegNo: DataReg)
6748 .constrainAllUses(TII, TRI, RBI);
6749 return true;
6750}
6751
6752Register SPIRVInstructionSelector::buildPointerToResource(
6753 SPIRVTypeInst SpirvResType, SPIRV::StorageClass::StorageClass SC,
6754 uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg,
6755 StringRef Name, MachineIRBuilder MIRBuilder) const {
6756 const Type *ResType = GR.getTypeForSPIRVType(Ty: SpirvResType);
6757 if (ArraySize == 1) {
6758 SPIRVTypeInst PtrType =
6759 GR.getOrCreateSPIRVPointerType(BaseType: ResType, MIRBuilder, SC);
6760 assert(GR.getPointeeType(PtrType) == SpirvResType &&
6761 "SpirvResType did not have an explicit layout.");
6762 return GR.getOrCreateGlobalVariableWithBinding(VarType: PtrType, Set, Binding, Name,
6763 MIRBuilder);
6764 }
6765
6766 const Type *VarType = ArrayType::get(ElementType: const_cast<Type *>(ResType), NumElements: ArraySize);
6767 SPIRVTypeInst VarPointerType =
6768 GR.getOrCreateSPIRVPointerType(BaseType: VarType, MIRBuilder, SC);
6769 Register VarReg = GR.getOrCreateGlobalVariableWithBinding(
6770 VarType: VarPointerType, Set, Binding, Name, MIRBuilder);
6771
6772 SPIRVTypeInst ResPointerType =
6773 GR.getOrCreateSPIRVPointerType(BaseType: ResType, MIRBuilder, SC);
6774 Register AcReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResPointerType));
6775
6776 MIRBuilder.buildInstr(Opcode: SPIRV::OpAccessChain)
6777 .addDef(RegNo: AcReg)
6778 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResPointerType))
6779 .addUse(RegNo: VarReg)
6780 .addUse(RegNo: IndexReg);
6781
6782 return AcReg;
6783}
6784
6785bool SPIRVInstructionSelector::selectFirstBitSet16(
6786 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
6787 unsigned ExtendOpcode, unsigned BitSetOpcode) const {
6788 Register ExtReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6789 if (!selectOpWithSrcs(ResVReg: ExtReg, ResType, I, Srcs: {I.getOperand(i: 2).getReg()},
6790 Opcode: ExtendOpcode))
6791 return false;
6792
6793 return selectFirstBitSet32(ResVReg, ResType, I, SrcReg: ExtReg, BitSetOpcode);
6794}
6795
6796bool SPIRVInstructionSelector::selectFirstBitSet32(
6797 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, Register SrcReg,
6798 unsigned BitSetOpcode) const {
6799 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
6800 .addDef(RegNo: ResVReg)
6801 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6802 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
6803 .addImm(Val: BitSetOpcode)
6804 .addUse(RegNo: SrcReg)
6805 .constrainAllUses(TII, TRI, RBI);
6806 return true;
6807}
6808
6809bool SPIRVInstructionSelector::selectFirstBitSet64(
6810 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, Register SrcReg,
6811 unsigned BitSetOpcode, bool SwapPrimarySide) const {
6812 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: ResType);
6813 SPIRVTypeInst BaseType = GR.retrieveScalarOrVectorIntType(Type: ResType);
6814 bool ZeroAsNull = !STI.isShader();
6815 Register ConstIntZero =
6816 GR.getOrCreateConstInt(Val: 0, I, SpvType: BaseType, TII, ZeroAsNull);
6817 Register ConstIntOne =
6818 GR.getOrCreateConstInt(Val: 1, I, SpvType: BaseType, TII, ZeroAsNull);
6819
6820 // SPIRV doesn't support vectors with more than 4 components. Since the
6821 // algoritm below converts i64 -> i32x2 and i64x4 -> i32x8 it can only
6822 // operate on vectors with 2 or less components. When largers vectors are
6823 // seen. Split them, recurse, then recombine them.
6824 // TODO: handle the case where SPV_EXT_long_vector is enabled.
6825 if (ComponentCount > 2) {
6826 auto Func = [this, SwapPrimarySide](Register ResVReg, SPIRVTypeInst ResType,
6827 MachineInstr &I, Register SrcReg,
6828 unsigned Opcode) -> bool {
6829 return this->selectFirstBitSet64(ResVReg, ResType, I, SrcReg, BitSetOpcode: Opcode,
6830 SwapPrimarySide);
6831 };
6832
6833 return handle64BitOverflow(ResVReg, ResType, I, SrcReg, Opcode: BitSetOpcode, CallbackFunction: Func);
6834 }
6835
6836 // 1. Split int64 into 2 pieces using a bitcast
6837 MachineIRBuilder MIRBuilder(I);
6838 SPIRVTypeInst PostCastType = GR.getOrCreateSPIRVVectorType(
6839 BaseType, NumElements: 2 * ComponentCount, MIRBuilder, EmitIR: false);
6840 Register BitcastReg =
6841 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PostCastType));
6842
6843 if (!selectOpWithSrcs(ResVReg: BitcastReg, ResType: PostCastType, I, Srcs: {SrcReg},
6844 Opcode: SPIRV::OpBitcast))
6845 return false;
6846
6847 // 2. Find the first set bit from the primary side for all the pieces in #1
6848 Register FBSReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PostCastType));
6849 if (!selectFirstBitSet32(ResVReg: FBSReg, ResType: PostCastType, I, SrcReg: BitcastReg, BitSetOpcode))
6850 return false;
6851
6852 // 3. Split result vector into high bits and low bits
6853 Register HighReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6854 Register LowReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6855
6856 bool IsScalarRes = !isVectorType(SPVTy: ResType);
6857 if (IsScalarRes) {
6858 // if scalar do a vector extract
6859 if (!selectOpWithSrcs(ResVReg: HighReg, ResType, I, Srcs: {FBSReg, ConstIntOne},
6860 Opcode: SPIRV::OpVectorExtractDynamic))
6861 return false;
6862 if (!selectOpWithSrcs(ResVReg: LowReg, ResType, I, Srcs: {FBSReg, ConstIntZero},
6863 Opcode: SPIRV::OpVectorExtractDynamic))
6864 return false;
6865 } else {
6866 // if vector do a shufflevector
6867 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6868 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
6869 .addDef(RegNo: HighReg)
6870 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6871 .addUse(RegNo: FBSReg)
6872 // Per the spec, repeat the vector if only one vec is needed
6873 .addUse(RegNo: FBSReg);
6874
6875 // high bits are stored in even natural indexes. Extract them from FBSReg
6876 for (unsigned J = 1; J < ComponentCount * 2; J += 2) {
6877 MIB.addImm(Val: J);
6878 }
6879
6880 MIB.constrainAllUses(TII, TRI, RBI);
6881
6882 MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6883 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
6884 .addDef(RegNo: LowReg)
6885 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6886 .addUse(RegNo: FBSReg)
6887 // Per the spec, repeat the vector if only one vec is needed
6888 .addUse(RegNo: FBSReg);
6889
6890 // low bits are stored in odd natural indices. Extract them from FBSReg
6891 for (unsigned J = 0; J < ComponentCount * 2; J += 2) {
6892 MIB.addImm(Val: J);
6893 }
6894 MIB.constrainAllUses(TII, TRI, RBI);
6895 }
6896
6897 // 4. Check the result. When primary bits == -1 use secondary, otherwise use
6898 // primary
6899 SPIRVTypeInst BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
6900 Register NegOneReg;
6901 Register Reg0;
6902 Register Reg32;
6903 unsigned SelectOp;
6904 unsigned AddOp;
6905
6906 if (IsScalarRes) {
6907 NegOneReg =
6908 GR.getOrCreateConstInt(Val: (unsigned)-1, I, SpvType: ResType, TII, ZeroAsNull);
6909 Reg0 = GR.getOrCreateConstInt(Val: 0, I, SpvType: ResType, TII, ZeroAsNull);
6910 Reg32 = GR.getOrCreateConstInt(Val: 32, I, SpvType: ResType, TII, ZeroAsNull);
6911 SelectOp = SPIRV::OpSelectSISCond;
6912 AddOp = SPIRV::OpIAddS;
6913 } else {
6914 BoolType = GR.getOrCreateSPIRVVectorType(BaseType: BoolType, NumElements: ComponentCount,
6915 MIRBuilder, EmitIR: false);
6916 NegOneReg =
6917 GR.getOrCreateConstVector(Val: (unsigned)-1, I, SpvType: ResType, TII, ZeroAsNull);
6918 Reg0 = GR.getOrCreateConstVector(Val: 0, I, SpvType: ResType, TII, ZeroAsNull);
6919 Reg32 = GR.getOrCreateConstVector(Val: 32, I, SpvType: ResType, TII, ZeroAsNull);
6920 SelectOp = SPIRV::OpSelectVIVCond;
6921 AddOp = SPIRV::OpIAddV;
6922 }
6923
6924 Register PrimaryReg = HighReg;
6925 Register SecondaryReg = LowReg;
6926 Register RegPrimaryOffset = Reg32;
6927 Register RegSecondaryOffset = Reg0;
6928
6929 // By default the emitted opcodes check for the set bit from the MSB side.
6930 // Setting SwapPrimarySide checks the set bit from the LSB side
6931 if (SwapPrimarySide) {
6932 PrimaryReg = LowReg;
6933 SecondaryReg = HighReg;
6934 RegPrimaryOffset = Reg0;
6935 RegSecondaryOffset = Reg32;
6936 }
6937
6938 Register RegSecondaryHasVal =
6939 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BoolType));
6940 if (!selectOpWithSrcs(ResVReg: RegSecondaryHasVal, ResType: BoolType, I,
6941 Srcs: {SecondaryReg, NegOneReg}, Opcode: SPIRV::OpINotEqual))
6942 return false;
6943
6944 Register RegPrimaryHasVal =
6945 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BoolType));
6946 if (!selectOpWithSrcs(ResVReg: RegPrimaryHasVal, ResType: BoolType, I, Srcs: {PrimaryReg, NegOneReg},
6947 Opcode: SPIRV::OpINotEqual))
6948 return false;
6949
6950 // Pass 1: seed with secondary (lower-priority fallback)
6951 // ReturnBits = secondaryHasVal ? SecondaryBits : -1
6952 // Add = secondaryHasVal ? SecondaryOffset : 0
6953 Register RegReturnBits = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6954 if (!selectOpWithSrcs(ResVReg: RegReturnBits, ResType, I,
6955 Srcs: {RegSecondaryHasVal, SecondaryReg, NegOneReg},
6956 Opcode: SelectOp))
6957 return false;
6958
6959 Register RegAdd;
6960 if (SwapPrimarySide) {
6961 RegAdd = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6962 if (!selectOpWithSrcs(ResVReg: RegAdd, ResType, I,
6963 Srcs: {RegSecondaryHasVal, RegSecondaryOffset, Reg0},
6964 Opcode: SelectOp))
6965 return false;
6966 } else {
6967 RegAdd = Reg0;
6968 }
6969
6970 // Pass 2: override with primary (higher priority) if it has a valid result
6971 // ReturnBits2 = primaryHasVal ? PrimaryBits : ReturnBits
6972 // Add2 = primaryHasVal ? PrimaryOffset : Add
6973 Register RegReturnBits2 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6974 if (!selectOpWithSrcs(ResVReg: RegReturnBits2, ResType, I,
6975 Srcs: {RegPrimaryHasVal, PrimaryReg, RegReturnBits},
6976 Opcode: SelectOp))
6977 return false;
6978
6979 Register RegAdd2 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6980 if (!selectOpWithSrcs(ResVReg: RegAdd2, ResType, I,
6981 Srcs: {RegPrimaryHasVal, RegPrimaryOffset, RegAdd}, Opcode: SelectOp))
6982 return false;
6983
6984 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {RegReturnBits2, RegAdd2},
6985 Opcode: AddOp);
6986}
6987
6988bool SPIRVInstructionSelector::selectFirstBitHigh(Register ResVReg,
6989 SPIRVTypeInst ResType,
6990 MachineInstr &I,
6991 bool IsSigned) const {
6992 // FindUMsb and FindSMsb intrinsics only support 32 bit integers
6993 Register OpReg = I.getOperand(i: 2).getReg();
6994 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
6995 // zero or sign extend
6996 unsigned ExtendOpcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
6997 unsigned BitSetOpcode = IsSigned ? GL::FindSMsb : GL::FindUMsb;
6998
6999 switch (GR.getScalarOrVectorBitWidth(Type: OpType)) {
7000 case 16:
7001 return selectFirstBitSet16(ResVReg, ResType, I, ExtendOpcode, BitSetOpcode);
7002 case 32:
7003 return selectFirstBitSet32(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode);
7004 case 64:
7005 return selectFirstBitSet64(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode,
7006 /*SwapPrimarySide=*/false);
7007 default:
7008 return diagnoseUnsupported(
7009 I,
7010 Msg: "spv_firstbituhigh and spv_firstbitshigh only support 16,32,64 bits.");
7011 }
7012}
7013
7014bool SPIRVInstructionSelector::selectFirstBitLow(Register ResVReg,
7015 SPIRVTypeInst ResType,
7016 MachineInstr &I) const {
7017 // FindILsb intrinsic only supports 32 bit integers
7018 Register OpReg = I.getOperand(i: 2).getReg();
7019 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
7020 // OpUConvert treats the operand bits as an unsigned i16 and zero extends it
7021 // to an unsigned i32. As this leaves all the least significant bits unchanged
7022 // so the first set bit from the LSB side doesn't change.
7023 unsigned ExtendOpcode = SPIRV::OpUConvert;
7024 unsigned BitSetOpcode = GL::FindILsb;
7025
7026 switch (GR.getScalarOrVectorBitWidth(Type: OpType)) {
7027 case 16:
7028 return selectFirstBitSet16(ResVReg, ResType, I, ExtendOpcode, BitSetOpcode);
7029 case 32:
7030 return selectFirstBitSet32(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode);
7031 case 64:
7032 return selectFirstBitSet64(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode,
7033 /*SwapPrimarySide=*/true);
7034 default:
7035 return diagnoseUnsupported(I,
7036 Msg: "spv_firstbitlow only supports 16,32,64 bits.");
7037 }
7038}
7039
7040bool SPIRVInstructionSelector::selectAllocaArray(Register ResVReg,
7041 SPIRVTypeInst ResType,
7042 MachineInstr &I) const {
7043 // there was an allocation size parameter to the allocation instruction
7044 // that is not 1
7045 MachineBasicBlock &BB = *I.getParent();
7046 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVariableLengthArrayINTEL))
7047 .addDef(RegNo: ResVReg)
7048 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7049 .addUse(RegNo: I.getOperand(i: 2).getReg())
7050 .constrainAllUses(TII, TRI, RBI);
7051 if (!STI.isShader()) {
7052 unsigned Alignment = I.getOperand(i: 3).getImm();
7053 buildOpDecorate(Reg: ResVReg, I, TII, Dec: SPIRV::Decoration::Alignment, DecArgs: {Alignment});
7054 }
7055 return true;
7056}
7057
7058// Returns true iff `Ty` is a concrete SPIR-V type per the SPV_KHR_abort
7059// definition: a numerical scalar (int/float), a (physical) pointer, a vector,
7060// matrix or any aggregate (array/struct) recursively containing only such
7061// types. OpTypeBool, OpTypeVoid, opaque handles and similar abstract
7062// non-concrete types are rejected.
7063static bool isConcreteSPIRVType(SPIRVTypeInst Ty,
7064 const SPIRVGlobalRegistry &GR) {
7065 SmallVector<SPIRVTypeInst, 4> Worklist{Ty};
7066 while (!Worklist.empty()) {
7067 SPIRVTypeInst T = Worklist.pop_back_val();
7068 switch (T->getOpcode()) {
7069 case SPIRV::OpTypeInt:
7070 case SPIRV::OpTypeFloat:
7071 case SPIRV::OpTypePointer:
7072 break;
7073 case SPIRV::OpTypeVector:
7074 case SPIRV::OpTypeVectorIdEXT:
7075 case SPIRV::OpTypeMatrix:
7076 case SPIRV::OpTypeArray: {
7077 Register OperandReg = T->getOperand(i: 1).getReg();
7078 SPIRVTypeInst ElementT = GR.getSPIRVTypeForVReg(VReg: OperandReg);
7079 Worklist.push_back(Elt: ElementT);
7080 } break;
7081 case SPIRV::OpTypeStruct:
7082 for (unsigned Idx = 1, E = T->getNumOperands(); Idx < E; ++Idx) {
7083 Register OperandReg = T->getOperand(i: Idx).getReg();
7084 SPIRVTypeInst ElementT = GR.getSPIRVTypeForVReg(VReg: OperandReg);
7085 Worklist.push_back(Elt: ElementT);
7086 }
7087 break;
7088 default:
7089 return false;
7090 }
7091 }
7092 return true;
7093}
7094
7095bool SPIRVInstructionSelector::selectAbort(MachineInstr &I) const {
7096 assert(I.getNumExplicitOperands() == 2);
7097
7098 Register MsgReg = I.getOperand(i: 1).getReg();
7099 SPIRVTypeInst MsgType = GR.getSPIRVTypeForVReg(VReg: MsgReg);
7100 assert(MsgType && "Message argument of llvm.spv.abort has no SPIR-V type");
7101
7102 if (!isConcreteSPIRVType(Ty: MsgType, GR))
7103 return diagnoseUnsupported(
7104 I,
7105 Msg: "llvm.spv.abort message type must be a concrete SPIR-V type (numerical "
7106 "scalar, pointer, vector, matrix, or aggregate of such types)");
7107
7108 MachineBasicBlock &BB = *I.getParent();
7109 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAbortKHR))
7110 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MsgType))
7111 .addUse(RegNo: MsgReg)
7112 .constrainAllUses(TII, TRI, RBI);
7113 return true;
7114}
7115
7116bool SPIRVInstructionSelector::selectTrap(MachineInstr &I) const {
7117 // When the SPV_KHR_abort extension is disabled, drop the G_TRAP and
7118 // G_UBSANTRAP silently.
7119 if (!STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_abort))
7120 return true;
7121
7122 // Use the 32-bit integer constant for the abort "message" argument:
7123 // - G_UBSANTRAP operand is zero-extended to 32 bits.
7124 // - "All ones" constant is used for G_TRAP.
7125 uint32_t MsgVal = ~0u;
7126 if (I.getOpcode() == TargetOpcode::G_UBSANTRAP)
7127 MsgVal = static_cast<uint32_t>(I.getOperand(i: 0).getImm());
7128
7129 SPIRVTypeInst MsgType = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
7130 Register MsgReg = buildI32ConstantInEntryBlock(Val: MsgVal, I, ResType: MsgType);
7131
7132 MachineBasicBlock &BB = *I.getParent();
7133 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAbortKHR))
7134 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MsgType))
7135 .addUse(RegNo: MsgReg)
7136 .constrainAllUses(TII, TRI, RBI);
7137 return true;
7138}
7139
7140bool SPIRVInstructionSelector::selectFrameIndex(Register ResVReg,
7141 SPIRVTypeInst ResType,
7142 MachineInstr &I) const {
7143 // Change order of instructions if needed: all OpVariable instructions in a
7144 // function must be the first instructions in the first block
7145 auto It = getOpVariableMBBIt(MF&: *I.getMF());
7146
7147 // Pointers to opaque types stay typed even with the extension on, so emit the
7148 // untyped variant only when the result is actually an untyped pointer.
7149 bool UseUntypedPointers =
7150 ResType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
7151 unsigned Opcode =
7152 UseUntypedPointers ? SPIRV::OpUntypedVariableKHR : SPIRV::OpVariable;
7153
7154 auto MIB = BuildMI(BB&: *It->getParent(), I: It, MIMD: It->getDebugLoc(), MCID: TII.get(Opcode))
7155 .addDef(RegNo: ResVReg)
7156 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7157 .addImm(Val: static_cast<uint32_t>(SPIRV::StorageClass::Function));
7158
7159 // OpUntypedVariableKHR takes an extra Data Type operand right after the
7160 // storage class.
7161 if (UseUntypedPointers) {
7162 // Get the element type that was stored when processing spv_assign_ptr_type.
7163 SPIRVTypeInst DataType = GR.getUntypedPtrElementType(Reg: ResVReg);
7164 if (!DataType)
7165 return diagnoseUnsupported(
7166 I, Msg: "could not deduce the data type of an untyped variable");
7167 MIB.addUse(RegNo: GR.getSPIRVTypeID(SpirvType: DataType));
7168 }
7169 MIB.constrainAllUses(TII, TRI, RBI);
7170
7171 if (!STI.isShader()) {
7172 unsigned Alignment = I.getOperand(i: 2).getImm();
7173 buildOpDecorate(Reg: ResVReg, I&: *It, TII, Dec: SPIRV::Decoration::Alignment,
7174 DecArgs: {Alignment});
7175 }
7176 return true;
7177}
7178
7179bool SPIRVInstructionSelector::selectBranch(MachineInstr &I) const {
7180 // InstructionSelector walks backwards through the instructions. We can use
7181 // both a G_BR and a G_BRCOND to create an OpBranchConditional. We hit G_BR
7182 // first, so can generate an OpBranchConditional here. If there is no
7183 // G_BRCOND, we just use OpBranch for a regular unconditional branch.
7184 const MachineInstr *PrevI = I.getPrevNode();
7185 MachineBasicBlock &MBB = *I.getParent();
7186 if (PrevI != nullptr && PrevI->getOpcode() == TargetOpcode::G_BRCOND) {
7187 BuildMI(BB&: MBB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBranchConditional))
7188 .addUse(RegNo: PrevI->getOperand(i: 0).getReg())
7189 .addMBB(MBB: PrevI->getOperand(i: 1).getMBB())
7190 .addMBB(MBB: I.getOperand(i: 0).getMBB())
7191 .constrainAllUses(TII, TRI, RBI);
7192 return true;
7193 }
7194 BuildMI(BB&: MBB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBranch))
7195 .addMBB(MBB: I.getOperand(i: 0).getMBB())
7196 .constrainAllUses(TII, TRI, RBI);
7197 return true;
7198}
7199
7200bool SPIRVInstructionSelector::selectBranchCond(MachineInstr &I) const {
7201 // InstructionSelector walks backwards through the instructions. For an
7202 // explicit conditional branch with no fallthrough, we use both a G_BR and a
7203 // G_BRCOND to create an OpBranchConditional. We should hit G_BR first, and
7204 // generate the OpBranchConditional in selectBranch above.
7205 //
7206 // If an OpBranchConditional has been generated, we simply return, as the work
7207 // is alread done. If there is no OpBranchConditional, LLVM must be relying on
7208 // implicit fallthrough to the next basic block, so we need to create an
7209 // OpBranchConditional with an explicit "false" argument pointing to the next
7210 // basic block that LLVM would fall through to.
7211 const MachineInstr *NextI = I.getNextNode();
7212 // Check if this has already been successfully selected.
7213 if (NextI != nullptr && NextI->getOpcode() == SPIRV::OpBranchConditional)
7214 return true;
7215 // Must be relying on implicit block fallthrough, so generate an
7216 // OpBranchConditional with the "next" basic block as the "false" target.
7217 MachineBasicBlock &MBB = *I.getParent();
7218 unsigned NextMBBNum = MBB.getNextNode()->getNumber();
7219 MachineBasicBlock *NextMBB = I.getMF()->getBlockNumbered(N: NextMBBNum);
7220 BuildMI(BB&: MBB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBranchConditional))
7221 .addUse(RegNo: I.getOperand(i: 0).getReg())
7222 .addMBB(MBB: I.getOperand(i: 1).getMBB())
7223 .addMBB(MBB: NextMBB)
7224 .constrainAllUses(TII, TRI, RBI);
7225 return true;
7226}
7227
7228bool SPIRVInstructionSelector::selectPhi(Register ResVReg,
7229 MachineInstr &I) const {
7230 auto MIB =
7231 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: TargetOpcode::PHI))
7232 .addDef(RegNo: ResVReg);
7233 const unsigned NumOps = I.getNumOperands();
7234 for (unsigned i = 1; i < NumOps; i += 2) {
7235 MIB.addUse(RegNo: I.getOperand(i: i + 0).getReg());
7236 MIB.addMBB(MBB: I.getOperand(i: i + 1).getMBB());
7237 }
7238 MIB.constrainAllUses(TII, TRI, RBI);
7239 return true;
7240}
7241
7242bool SPIRVInstructionSelector::selectGlobalValue(
7243 Register ResVReg, MachineInstr &I, const MachineInstr *Init) const {
7244 // FIXME: don't use MachineIRBuilder here, replace it with BuildMI.
7245 MachineIRBuilder MIRBuilder(I);
7246 const GlobalValue *GV = I.getOperand(i: 1).getGlobal();
7247 Type *GVType = toTypedPointer(Ty: GR.getDeducedGlobalValueType(Global: GV));
7248
7249 std::string GlobalIdent;
7250 if (!GV->hasName()) {
7251 unsigned &ID = UnnamedGlobalIDs[GV];
7252 if (ID == 0)
7253 ID = UnnamedGlobalIDs.size();
7254 GlobalIdent = "__unnamed_" + Twine(ID).str();
7255 } else {
7256 GlobalIdent = GV->getName();
7257 }
7258
7259 // Behaviour of functions as operands depends on availability of the
7260 // corresponding extension (SPV_INTEL_function_pointers):
7261 // - If there is an extension to operate with functions as operands:
7262 // We create a proper constant operand and evaluate a correct type for a
7263 // function pointer.
7264 // - Without the required extension:
7265 // We have functions as operands in tests with blocks of instruction e.g. in
7266 // transcoding/global_block.ll. These operands are not used and should be
7267 // substituted by zero constants. Their type is expected to be always
7268 // OpTypePointer Function %uchar.
7269 if (isa<Function>(Val: GV)) {
7270 const Constant *ConstVal = GV;
7271 MachineBasicBlock &BB = *I.getParent();
7272 Register NewReg = GR.find(V: ConstVal, MF: GR.CurMF);
7273 if (!NewReg.isValid()) {
7274 const Function *GVFun =
7275 STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_function_pointers)
7276 ? dyn_cast<Function>(Val: GV)
7277 : nullptr;
7278 SPIRVTypeInst ResType = GR.getOrCreateSPIRVPointerType(
7279 BaseType: GVType, I,
7280 SC: GVFun ? SPIRV::StorageClass::CodeSectionINTEL
7281 : addressSpaceToStorageClass(AddrSpace: GV->getAddressSpace(), STI));
7282 if (GVFun) {
7283 // References to a function via function pointers generate virtual
7284 // registers without a definition. We will resolve it later, during
7285 // module analysis stage.
7286 Register ResTypeReg = GR.getSPIRVTypeID(SpirvType: ResType);
7287 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
7288 Register FuncVReg =
7289 MRI->createGenericVirtualRegister(Ty: GR.getRegType(SpvType: ResType));
7290 MRI->setRegClass(Reg: FuncVReg, RC: &SPIRV::pIDRegClass);
7291 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: FuncVReg, MF: *GR.CurMF);
7292 MachineInstrBuilder MIB1 =
7293 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
7294 .addDef(RegNo: FuncVReg)
7295 .addUse(RegNo: ResTypeReg);
7296 MachineInstrBuilder MIB2 =
7297 BuildMI(BB, I, MIMD: I.getDebugLoc(),
7298 MCID: TII.get(Opcode: SPIRV::OpConstantFunctionPointerINTEL))
7299 .addDef(RegNo: ResVReg)
7300 .addUse(RegNo: ResTypeReg)
7301 .addUse(RegNo: FuncVReg);
7302 GR.add(V: ConstVal, MI: MIB2);
7303 // mapping the function pointer to the used Function
7304 GR.recordFunctionPointer(MO: &MIB2.getInstr()->getOperand(i: 2), F: GVFun);
7305 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: ResVReg, MF: *GR.CurMF);
7306 MIB1.constrainAllUses(TII, TRI, RBI);
7307 MIB2.constrainAllUses(TII, TRI, RBI);
7308 return true;
7309 }
7310 MachineInstrBuilder MIB3 =
7311 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
7312 .addDef(RegNo: ResVReg)
7313 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
7314 GR.add(V: ConstVal, MI: MIB3);
7315 GR.recordFunctionPointer(MO: &MIB3.getInstr()->getOperand(i: 0),
7316 F: cast<Function>(Val: GV));
7317 MIB3.constrainAllUses(TII, TRI, RBI);
7318 return true;
7319 }
7320 assert(NewReg != ResVReg);
7321 return BuildCOPY(DestReg: ResVReg, SrcReg: NewReg, I);
7322 }
7323 auto GlobalVar = cast<GlobalVariable>(Val: GV);
7324 assert(GlobalVar->getName() != "llvm.global.annotations");
7325
7326 // Skip empty declaration for GVs with initializers till we get the decl with
7327 // passed initializer.
7328 if (hasInitializer(GV: GlobalVar) && !Init)
7329 return true;
7330
7331 const std::optional<SPIRV::LinkageType::LinkageType> LnkType =
7332 getSpirvLinkageTypeFor(ST: STI, GV: *GV);
7333
7334 if (LnkType && *LnkType == SPIRV::LinkageType::Import)
7335 Init = nullptr;
7336
7337 const unsigned AddrSpace = GV->getAddressSpace();
7338 SPIRV::StorageClass::StorageClass StorageClass =
7339 addressSpaceToStorageClass(AddrSpace, STI);
7340 SPIRVTypeInst ResType =
7341 GR.getOrCreateSPIRVPointerType(BaseType: GVType, I, SC: StorageClass);
7342 Register Reg = GR.buildGlobalVariable(
7343 Reg: ResVReg, BaseType: ResType, Name: GlobalIdent, GV, Storage: StorageClass, Init,
7344 IsConst: GlobalVar->isConstant(), LinkageType: LnkType, MIRBuilder, IsInstSelector: true);
7345 // TODO: For AMDGCN, we pipe externally_initialized through via
7346 // HostAccessINTEL, with ReadWrite (3) access, which is we then handle during
7347 // reverse translation. We should remove this once SPIR-V gains the ability to
7348 // express the concept.
7349 if (GlobalVar->isExternallyInitialized() &&
7350 STI.getTargetTriple().getVendor() == Triple::AMD) {
7351 constexpr unsigned ReadWriteINTEL = 3u;
7352 buildOpDecorate(Reg, MIRBuilder, Dec: SPIRV::Decoration::HostAccessINTEL,
7353 DecArgs: {ReadWriteINTEL});
7354 MachineInstrBuilder MIB(*MF, --MIRBuilder.getInsertPt());
7355 addStringImm(Str: GV->getName(), MIB);
7356 }
7357 return Reg.isValid();
7358}
7359
7360bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
7361 SPIRVTypeInst ResType,
7362 MachineInstr &I) const {
7363 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
7364 return selectExtInst(ResVReg, ResType, I, CLInst: CL::log10);
7365 }
7366
7367 // There is no log10 instruction in the GLSL Extended Instruction set, so it
7368 // is implemented as:
7369 // log10(x) = log2(x) * (1 / log2(10))
7370 // = log2(x) * 0.30103
7371
7372 MachineIRBuilder MIRBuilder(I);
7373 MachineBasicBlock &BB = *I.getParent();
7374
7375 // Build log2(x).
7376 Register VarReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
7377 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
7378 .addDef(RegNo: VarReg)
7379 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7380 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
7381 .addImm(Val: GL::Log2)
7382 .add(MO: I.getOperand(i: 1))
7383 .constrainAllUses(TII, TRI, RBI);
7384
7385 // Build 0.30103.
7386 assert(isVectorType(ResType) || ResType.isAnyTypeFloat());
7387 // TODO: Add matrix implementation once supported by the HLSL frontend.
7388 SPIRVTypeInst SpirvScalarType = GR.getScalarOrVectorComponentType(Type: ResType);
7389 // The literal must match the precision of the scalar type, otherwise the
7390 // OpConstant will contain non-zero high-order bits and fail SPIR-V
7391 // validation when the type is narrower than 32 bits (e.g. half).
7392 APFloat ScaleVal(0.30103);
7393 bool LosesInfo;
7394 ScaleVal.convert(
7395 ToSemantics: getZeroFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: SpirvScalarType)).getSemantics(),
7396 RM: APFloat::rmNearestTiesToEven, losesInfo: &LosesInfo);
7397 Register ScaleReg = GR.buildConstantFP(Val: ScaleVal, MIRBuilder, SpvType: SpirvScalarType);
7398
7399 // Multiply log2(x) by 0.30103 to get log10(x) result.
7400 auto Opcode =
7401 isVectorType(SPVTy: ResType) ? SPIRV::OpVectorTimesScalar : SPIRV::OpFMulS;
7402 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
7403 .addDef(RegNo: ResVReg)
7404 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7405 .addUse(RegNo: VarReg)
7406 .addUse(RegNo: ScaleReg)
7407 .constrainAllUses(TII, TRI, RBI);
7408 return true;
7409}
7410
7411bool SPIRVInstructionSelector::selectFpowi(Register ResVReg,
7412 SPIRVTypeInst ResType,
7413 MachineInstr &I) const {
7414 // On OpenCL targets, pown(gentype x, intn n) maps directly.
7415 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std))
7416 return selectExtInst(ResVReg, ResType, I, CLInst: CL::pown);
7417
7418 // On GLSL (Vulkan) targets, there is no integer-exponent power instruction.
7419 // Lower as: Pow(base, OpConvertSToF(exp)).
7420 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::GLSL_std_450)) {
7421 Register BaseReg = I.getOperand(i: 1).getReg();
7422 Register ExpReg = I.getOperand(i: 2).getReg();
7423 Register FloatExpReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
7424 if (!selectOpWithSrcs(ResVReg: FloatExpReg, ResType, I, Srcs: {ExpReg},
7425 Opcode: SPIRV::OpConvertSToF))
7426 return false;
7427 return selectExtInst(ResVReg, ResType, I, GLInst: GL::Pow,
7428 /*setMIFlags=*/true, /*useMISrc=*/false,
7429 SrcRegs: {BaseReg, FloatExpReg});
7430 }
7431 return false;
7432}
7433
7434bool SPIRVInstructionSelector::selectModf(Register ResVReg,
7435 SPIRVTypeInst ResType,
7436 MachineInstr &I) const {
7437 // llvm.modf has a single arg --the number to be decomposed-- and returns a
7438 // struct { restype, restype }, while OpenCLLIB::modf has two args --the
7439 // number to be decomposed and a pointer--, returns the fractional part and
7440 // the integral part is stored in the pointer argument. Therefore, we can't
7441 // use directly the OpenCLLIB::modf intrinsic. However, we can do some
7442 // scaffolding to make it work. The idea is to create an alloca instruction
7443 // to get a ptr, pass this ptr to OpenCL::modf, and then load the value
7444 // from this ptr to place it in the struct. llvm.modf returns the fractional
7445 // part as the first element of the result, and the integral part as the
7446 // second element of the result.
7447
7448 // At this point, the return type is not a struct anymore, but rather two
7449 // independent elements of SPIRVResType. We can get each independent element
7450 // from I.getDefs() or I.getOperands().
7451 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
7452 MachineIRBuilder MIRBuilder(I);
7453 SPIRVTypeInst FloatType =
7454 GR.getSPIRVTypeForVReg(VReg: I.getOperand(i: I.getNumExplicitDefs()).getReg());
7455 // Get pointer type for alloca variable.
7456 const SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
7457 BaseType: FloatType, MIRBuilder, SC: SPIRV::StorageClass::Function);
7458 // Create new register for the pointer type of alloca variable.
7459 Register PtrTyReg =
7460 MIRBuilder.getMRI()->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
7461 MIRBuilder.getMRI()->setType(
7462 VReg: PtrTyReg,
7463 Ty: LLT::pointer(AddressSpace: storageClassToAddressSpace(SC: SPIRV::StorageClass::Function),
7464 SizeInBits: GR.getPointerSize()));
7465
7466 // Assign SPIR-V type of the pointer type of the alloca variable to the
7467 // new register.
7468 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: PtrTyReg, MF: MIRBuilder.getMF());
7469 MachineBasicBlock::iterator VarPos = getOpVariableMBBIt(MF&: *I.getMF());
7470 MachineBasicBlock &EntryBB = I.getMF()->front();
7471 const bool IsUntyped =
7472 PtrType->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
7473 auto AllocaMIB =
7474 BuildMI(BB&: EntryBB, I: VarPos, MIMD: I.getDebugLoc(),
7475 MCID: TII.get(Opcode: IsUntyped ? SPIRV::OpUntypedVariableKHR
7476 : SPIRV::OpVariable))
7477 .addDef(RegNo: PtrTyReg)
7478 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrType))
7479 .addImm(Val: static_cast<uint32_t>(SPIRV::StorageClass::Function));
7480 if (IsUntyped)
7481 AllocaMIB.addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType)); // Data Type
7482 Register Variable = AllocaMIB->getOperand(i: 0).getReg();
7483
7484 MachineBasicBlock &BB = *I.getParent();
7485 // Create the OpenCLLIB::modf instruction.
7486 auto MIB =
7487 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
7488 .addDef(RegNo: ResVReg)
7489 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: FloatType))
7490 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
7491 .addImm(Val: CL::modf)
7492 .setMIFlags(I.getFlags())
7493 .add(MO: I.getOperand(i: I.getNumExplicitDefs())) // Floating point value.
7494 .addUse(RegNo: Variable); // Pointer to integral part.
7495 // Assign the integral part stored in the ptr to the second element of the
7496 // result.
7497 Register IntegralPartReg = I.getOperand(i: 1).getReg();
7498 if (IntegralPartReg.isValid() && !MRI->use_nodbg_empty(RegNo: IntegralPartReg)) {
7499 // Load the value from the pointer to integral part.
7500 auto LoadMIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
7501 .addDef(RegNo: IntegralPartReg)
7502 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: FloatType))
7503 .addUse(RegNo: Variable);
7504 LoadMIB.constrainAllUses(TII, TRI, RBI);
7505 }
7506
7507 MIB.constrainAllUses(TII, TRI, RBI);
7508 return true;
7509 } else if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::GLSL_std_450)) {
7510 assert(false && "GLSL::Modf is deprecated.");
7511 // FIXME: GL::Modf is deprecated, use Modfstruct instead.
7512 return false;
7513 }
7514 return false;
7515}
7516
7517// Generate the instructions to load 3-element vector builtin input
7518// IDs/Indices.
7519// Like: GlobalInvocationId, LocalInvocationId, etc....
7520
7521bool SPIRVInstructionSelector::loadVec3BuiltinInputID(
7522 SPIRV::BuiltIn::BuiltIn BuiltInValue, Register ResVReg,
7523 SPIRVTypeInst ResType, MachineInstr &I) const {
7524 MachineIRBuilder MIRBuilder(I);
7525 const SPIRVTypeInst Vec3Ty =
7526 GR.getOrCreateSPIRVVectorType(BaseType: ResType, NumElements: 3, MIRBuilder, EmitIR: false);
7527 const SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
7528 BaseType: Vec3Ty, MIRBuilder, SC: SPIRV::StorageClass::Input);
7529
7530 // Create new register for the input ID builtin variable.
7531 Register NewRegister =
7532 MIRBuilder.getMRI()->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
7533 MIRBuilder.getMRI()->setType(VReg: NewRegister, Ty: LLT::pointer(AddressSpace: 0, SizeInBits: 64));
7534 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: NewRegister, MF: MIRBuilder.getMF());
7535
7536 // Build global variable with the necessary decorations for the input ID
7537 // builtin variable.
7538 Register Variable = GR.buildGlobalVariable(
7539 Reg: NewRegister, BaseType: PtrType, Name: getLinkStringForBuiltIn(BuiltInValue), GV: nullptr,
7540 Storage: SPIRV::StorageClass::Input, Init: nullptr, IsConst: true, LinkageType: std::nullopt, MIRBuilder,
7541 IsInstSelector: false);
7542
7543 // Create new register for loading value.
7544 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
7545 Register LoadedRegister = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
7546 MIRBuilder.getMRI()->setType(VReg: LoadedRegister, Ty: LLT::pointer(AddressSpace: 0, SizeInBits: 64));
7547 GR.assignSPIRVTypeToVReg(Type: Vec3Ty, VReg: LoadedRegister, MF: MIRBuilder.getMF());
7548
7549 // Load v3uint value from the global variable.
7550 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
7551 .addDef(RegNo: LoadedRegister)
7552 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Vec3Ty))
7553 .addUse(RegNo: Variable);
7554
7555 // Get the input ID index. Expecting operand is a constant immediate value,
7556 // wrapped in a type assignment.
7557 assert(I.getOperand(2).isReg());
7558 const uint32_t ThreadId = foldImm(MO: I.getOperand(i: 2), MRI);
7559
7560 // Extract the input ID from the loaded vector value.
7561 MachineBasicBlock &BB = *I.getParent();
7562 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
7563 .addDef(RegNo: ResVReg)
7564 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7565 .addUse(RegNo: LoadedRegister)
7566 .addImm(Val: ThreadId);
7567 MIB.constrainAllUses(TII, TRI, RBI);
7568 return true;
7569}
7570
7571// Generate the instructions to load 32-bit integer builtin input IDs/Indices.
7572// Like LocalInvocationIndex
7573bool SPIRVInstructionSelector::loadBuiltinInputID(
7574 SPIRV::BuiltIn::BuiltIn BuiltInValue, Register ResVReg,
7575 SPIRVTypeInst ResType, MachineInstr &I) const {
7576 MachineIRBuilder MIRBuilder(I);
7577 const SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
7578 BaseType: ResType, MIRBuilder, SC: SPIRV::StorageClass::Input);
7579
7580 // Create new register for the input ID builtin variable.
7581 Register NewRegister =
7582 MIRBuilder.getMRI()->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PtrType));
7583 MIRBuilder.getMRI()->setType(
7584 VReg: NewRegister,
7585 Ty: LLT::pointer(AddressSpace: storageClassToAddressSpace(SC: SPIRV::StorageClass::Input),
7586 SizeInBits: GR.getPointerSize()));
7587 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: NewRegister, MF: MIRBuilder.getMF());
7588
7589 // Build global variable with the necessary decorations for the input ID
7590 // builtin variable.
7591 Register Variable = GR.buildGlobalVariable(
7592 Reg: NewRegister, BaseType: PtrType, Name: getLinkStringForBuiltIn(BuiltInValue), GV: nullptr,
7593 Storage: SPIRV::StorageClass::Input, Init: nullptr, IsConst: true, LinkageType: std::nullopt, MIRBuilder,
7594 IsInstSelector: false);
7595
7596 // Load uint value from the global variable.
7597 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
7598 .addDef(RegNo: ResVReg)
7599 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7600 .addUse(RegNo: Variable);
7601
7602 MIB.constrainAllUses(TII, TRI, RBI);
7603 return true;
7604}
7605
7606SPIRVTypeInst SPIRVInstructionSelector::widenTypeToVec4(SPIRVTypeInst Type,
7607 MachineInstr &I) const {
7608 MachineIRBuilder MIRBuilder(I);
7609 if (!isVectorType(SPVTy: Type))
7610 return GR.getOrCreateSPIRVVectorType(BaseType: Type, NumElements: 4, MIRBuilder, EmitIR: false);
7611
7612 if (GR.getScalarOrVectorComponentCount(Type) == 4)
7613 return Type;
7614
7615 SPIRVTypeInst ScalarType = GR.getScalarOrVectorComponentType(Type);
7616 return GR.getOrCreateSPIRVVectorType(BaseType: ScalarType, NumElements: 4, MIRBuilder, EmitIR: false);
7617}
7618
7619bool SPIRVInstructionSelector::loadHandleBeforePosition(
7620 Register &HandleReg, SPIRVTypeInst ResType, GIntrinsic &HandleDef,
7621 MachineInstr &Pos) const {
7622
7623 assert(HandleDef.getIntrinsicID() ==
7624 Intrinsic::spv_resource_handlefrombinding);
7625 uint32_t Set = foldImm(MO: HandleDef.getOperand(i: 2), MRI);
7626 uint32_t Binding = foldImm(MO: HandleDef.getOperand(i: 3), MRI);
7627 uint32_t ArraySize = foldImm(MO: HandleDef.getOperand(i: 4), MRI);
7628 Register IndexReg = HandleDef.getOperand(i: 5).getReg();
7629 std::string Name =
7630 getStringValueFromReg(Reg: HandleDef.getOperand(i: 6).getReg(), MRI&: *MRI);
7631
7632 bool IsStructuredBuffer = ResType->getOpcode() == SPIRV::OpTypePointer;
7633 MachineIRBuilder MIRBuilder(HandleDef);
7634 SPIRVTypeInst VarType = ResType;
7635 SPIRV::StorageClass::StorageClass SC = SPIRV::StorageClass::UniformConstant;
7636
7637 if (IsStructuredBuffer) {
7638 VarType = GR.getPointeeType(PtrType: ResType);
7639 SC = GR.getPointerStorageClass(Type: ResType);
7640 }
7641
7642 // ArraySize 0 means an unbounded array and we need to set to required
7643 // capability.
7644 if (ArraySize == 0)
7645 MIRBuilder.buildInstr(Opcode: SPIRV::OpCapability)
7646 .addImm(Val: SPIRV::Capability::RuntimeDescriptorArrayEXT);
7647
7648 Register VarReg =
7649 buildPointerToResource(SpirvResType: SPIRVTypeInst(VarType), SC, Set, Binding,
7650 ArraySize, IndexReg, Name, MIRBuilder);
7651
7652 // The handle for the buffer is the pointer to the resource. For an image, the
7653 // handle is the image object. So images get an extra load.
7654 uint32_t LoadOpcode =
7655 IsStructuredBuffer ? SPIRV::OpCopyObject : SPIRV::OpLoad;
7656 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: HandleReg, MF: *Pos.getMF());
7657 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: HandleDef.getDebugLoc(), MCID: TII.get(Opcode: LoadOpcode))
7658 .addDef(RegNo: HandleReg)
7659 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7660 .addUse(RegNo: VarReg)
7661 .constrainAllUses(TII, TRI, RBI);
7662 return true;
7663}
7664
7665bool SPIRVInstructionSelector::errorIfInstrOutsideShader(
7666 MachineInstr &I) const {
7667 if (!STI.isShader())
7668 return diagnoseUnsupported(
7669 I, Msg: "this instruction is only supported in shaders.");
7670 return true;
7671}
7672
7673namespace llvm {
7674InstructionSelector *
7675createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
7676 const SPIRVSubtarget &Subtarget,
7677 const RegisterBankInfo &RBI) {
7678 return new SPIRVInstructionSelector(TM, Subtarget, RBI);
7679}
7680} // namespace llvm
7681