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