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