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