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 selectInterlockedOp(Register ResVReg, SPIRVTypeInst ResType,
181 MachineInstr &I, unsigned Opcode) 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 SPIRVTypeInst SampledType =
2077 GR.getSPIRVTypeForVReg(VReg: HandleType->getOperand(i: 1).getReg());
2078 SPIRVTypeInst StoreValCompType =
2079 GR.getScalarOrVectorComponentType(Type: GR.getSPIRVTypeForVReg(VReg: StoreVal));
2080 if (StoreValCompType && StoreValCompType != SampledType) {
2081 // A wide element (e.g. int64_t2) is emulated with a narrower packed
2082 // image. This bitcasts the value to match the format.
2083 SPIRVTypeInst PackedType = widenTypeToVec4(Type: SampledType, I);
2084 Register PackedReg =
2085 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PackedType));
2086 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBitcast))
2087 .addDef(RegNo: PackedReg)
2088 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PackedType))
2089 .addUse(RegNo: StoreVal)
2090 .constrainAllUses(TII, TRI, RBI);
2091 StoreVal = PackedReg;
2092 }
2093
2094 auto BMI = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
2095 MCID: TII.get(Opcode: SPIRV::OpImageWrite))
2096 .addUse(RegNo: NewHandleReg)
2097 .addUse(RegNo: IdxReg)
2098 .addUse(RegNo: StoreVal);
2099
2100 const llvm::Type *LLVMHandleType = GR.getTypeForSPIRVType(Ty: HandleType);
2101 if (sampledTypeIsSignedInteger(HandleType: LLVMHandleType))
2102 BMI.addImm(Val: 0x1000); // SignExtend
2103
2104 BMI.constrainAllUses(TII, TRI, RBI);
2105 return true;
2106 }
2107 }
2108
2109 if (I.getNumMemOperands()) {
2110 const MachineMemOperand *MemOp = *I.memoperands_begin();
2111 if (MemOp->isAtomic())
2112 return selectAtomicStore(I);
2113 }
2114
2115 // Stores into a read-only storage class produce invalid SPIR-V. Reject such
2116 // input with a diagnostic rather than silently emitting an OpStore that
2117 // validation rejects.
2118 SPIRV::StorageClass::StorageClass PtrSC = GR.getPointerStorageClass(VReg: Ptr);
2119 if (PtrSC == SPIRV::StorageClass::UniformConstant ||
2120 PtrSC == SPIRV::StorageClass::Input ||
2121 PtrSC == SPIRV::StorageClass::PushConstant)
2122 return diagnoseUnsupported(
2123 I, Msg: "store into a read-only SPIR-V storage class is not allowed");
2124
2125 MachineIRBuilder MIRBuilder(I);
2126 auto MIB = MIRBuilder.buildInstr(Opcode: SPIRV::OpStore).addUse(RegNo: Ptr).addUse(RegNo: StoreVal);
2127 if (!I.getNumMemOperands()) {
2128 assert(I.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS ||
2129 I.getOpcode() ==
2130 TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS);
2131 addMemoryOperands(Flags: I.getOperand(i: 2 + OpOffset).getImm(), MIB);
2132 } else {
2133 addMemoryOperands(MemOp: *I.memoperands_begin(), MIB, MIRBuilder, GR);
2134 }
2135 MIB.constrainAllUses(TII, TRI, RBI);
2136 return true;
2137}
2138
2139bool SPIRVInstructionSelector::selectAtomicStore(MachineInstr &I) const {
2140 LLVMContext &Context = I.getMF()->getFunction().getContext();
2141
2142 unsigned OpOffset = isa<GIntrinsic>(Val: I) ? 1 : 0;
2143 Register StoreVal = I.getOperand(i: 0 + OpOffset).getReg();
2144 Register Ptr = I.getOperand(i: 1 + OpOffset).getReg();
2145
2146 SPIRVTypeInst PtrType = GR.getSPIRVTypeForVReg(VReg: Ptr);
2147 SPIRVTypeInst PointeeType = GR.getPointeeType(PtrType);
2148
2149 assert(I.getNumMemOperands());
2150 const MachineMemOperand &MemOp = **I.memoperands_begin();
2151 assert(MemOp.isAtomic());
2152
2153 uint32_t Scope =
2154 static_cast<uint32_t>(getMemScope(Ctx&: Context, Id: MemOp.getSyncScopeID()));
2155 Register ScopeReg = buildI32Constant(Val: Scope, I);
2156
2157 AtomicOrdering AO = MemOp.getSuccessOrdering();
2158 uint32_t StorageClass = static_cast<uint32_t>(getMemSemanticsForStorageClass(
2159 SC: addressSpaceToStorageClass(AddrSpace: MemOp.getAddrSpace(), STI)));
2160 uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(Ord: AO));
2161 if (MemOp.isVolatile() && STI.getTargetTriple().isVulkanOS())
2162 MemSem |= static_cast<uint32_t>(SPIRV::MemorySemantics::Volatile);
2163 Register MemSemReg = buildI32Constant(Val: MemSem | StorageClass, I);
2164 MachineIRBuilder MIRBuilder(I);
2165
2166 if (PointeeType.isTypePtr()) {
2167 if (!STI.isPhysicalSPIRV())
2168 return diagnoseUnsupported(
2169 I, Msg: "Lowering to SPIR-V of atomic store is only "
2170 "allowed for pointer types for physical addressing model");
2171 // If data to store is a pointer type we cast it to an integer type of the
2172 // same size as the pointer size using OpConvertPtrToU, bitcast Ptr
2173 // parameter to pointer to integer type and then generate OpAtomicStore
2174 // with casted values as required by spec.
2175 unsigned PtrSize = GR.getPointerSize();
2176 SPIRVTypeInst PtrAsIntSpirvType =
2177 GR.getOrCreateSPIRVIntegerType(BitWidth: PtrSize, MIRBuilder);
2178
2179 Register PtrToUVal =
2180 MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: PtrSize));
2181 MRI->setRegClass(Reg: PtrToUVal, RC: GR.getRegClass(SpvType: PtrAsIntSpirvType));
2182 GR.assignSPIRVTypeToVReg(Type: PtrAsIntSpirvType, VReg: PtrToUVal, MF: MIRBuilder.getMF());
2183 MIRBuilder.buildInstr(Opcode: SPIRV::OpConvertPtrToU)
2184 .addDef(RegNo: PtrToUVal)
2185 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrAsIntSpirvType)) // Result type
2186 .addUse(RegNo: StoreVal) // Pointer operand
2187 .constrainAllUses(TII, TRI, RBI);
2188
2189 Register PtrCastedToMatchValReg =
2190 MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: PtrSize));
2191 MRI->setRegClass(Reg: PtrCastedToMatchValReg, RC: MRI->getRegClassOrNull(Reg: Ptr));
2192 SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
2193 BaseType: PtrAsIntSpirvType, MIRBuilder,
2194 SC: addressSpaceToStorageClass(AddrSpace: MemOp.getAddrSpace(), STI));
2195 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: PtrCastedToMatchValReg,
2196 MF: MIRBuilder.getMF());
2197
2198 MIRBuilder.buildInstr(Opcode: SPIRV::OpBitcast)
2199 .addDef(RegNo: PtrCastedToMatchValReg)
2200 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrType))
2201 .addUse(RegNo: Ptr)
2202 .constrainAllUses(TII, TRI, RBI);
2203
2204 StoreVal = PtrToUVal;
2205 Ptr = PtrCastedToMatchValReg;
2206 PointeeType = PtrAsIntSpirvType;
2207 }
2208
2209 if (!PointeeType.isTypeIntOrFloat())
2210 return diagnoseUnsupported(I,
2211 Msg: "Lowering to SPIR-V of atomic store is only "
2212 "allowed for integer or floating point types");
2213
2214 auto AtomicStore = MIRBuilder.buildInstr(Opcode: SPIRV::OpAtomicStore)
2215 .addUse(RegNo: Ptr)
2216 .addUse(RegNo: ScopeReg)
2217 .addUse(RegNo: MemSemReg)
2218 .addUse(RegNo: StoreVal);
2219 AtomicStore.constrainAllUses(TII, TRI, RBI);
2220
2221 return true;
2222}
2223
2224bool SPIRVInstructionSelector::selectMaskedGather(Register ResVReg,
2225 SPIRVTypeInst ResType,
2226 MachineInstr &I) const {
2227 assert(I.getNumExplicitDefs() == 1 && "Expected single def for gather");
2228 // Operand indices:
2229 // 0: result (def)
2230 // 1: intrinsic ID
2231 // 2: vector of pointers
2232 // 3: alignment (i32 immediate)
2233 // 4: mask (vector of i1)
2234 // 5: passthru/fill value
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 PassthruReg = I.getOperand(i: 5).getReg();
2239 const Register AlignmentReg = buildI32Constant(Val: Alignment, I);
2240
2241 MachineBasicBlock &BB = *I.getParent();
2242 auto MIB =
2243 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpMaskedGatherINTEL))
2244 .addDef(RegNo: ResVReg)
2245 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2246 .addUse(RegNo: PtrsReg)
2247 .addUse(RegNo: AlignmentReg)
2248 .addUse(RegNo: MaskReg)
2249 .addUse(RegNo: PassthruReg);
2250 MIB.constrainAllUses(TII, TRI, RBI);
2251 return true;
2252}
2253
2254bool SPIRVInstructionSelector::selectMaskedScatter(MachineInstr &I) const {
2255 assert(I.getNumExplicitDefs() == 0 && "Expected no defs for scatter");
2256 // Operand indices (no explicit defs):
2257 // 0: intrinsic ID
2258 // 1: value vector
2259 // 2: vector of pointers
2260 // 3: alignment (i32 immediate)
2261 // 4: mask (vector of i1)
2262 const Register ValuesReg = I.getOperand(i: 1).getReg();
2263 const Register PtrsReg = I.getOperand(i: 2).getReg();
2264 const uint32_t Alignment = I.getOperand(i: 3).getImm();
2265 const Register MaskReg = I.getOperand(i: 4).getReg();
2266 const Register AlignmentReg = buildI32Constant(Val: Alignment, I);
2267 MachineBasicBlock &BB = *I.getParent();
2268
2269 auto MIB =
2270 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpMaskedScatterINTEL))
2271 .addUse(RegNo: PtrsReg)
2272 .addUse(RegNo: AlignmentReg)
2273 .addUse(RegNo: MaskReg)
2274 .addUse(RegNo: ValuesReg);
2275 MIB.constrainAllUses(TII, TRI, RBI);
2276 return true;
2277}
2278
2279bool SPIRVInstructionSelector::diagnoseUnsupported(const MachineInstr &I,
2280 const Twine &Msg) const {
2281 const Function &F = I.getMF()->getFunction();
2282 F.getContext().diagnose(
2283 DI: DiagnosticInfoUnsupported(F, Msg, I.getDebugLoc(), DS_Error));
2284 return false;
2285}
2286
2287bool SPIRVInstructionSelector::selectStackSave(Register ResVReg,
2288 SPIRVTypeInst ResType,
2289 MachineInstr &I) const {
2290 if (!STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_variable_length_array))
2291 return diagnoseUnsupported(
2292 I, Msg: "llvm.stacksave intrinsic: this instruction requires the following "
2293 "SPIR-V extension: SPV_INTEL_variable_length_array");
2294 MachineBasicBlock &BB = *I.getParent();
2295 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSaveMemoryINTEL))
2296 .addDef(RegNo: ResVReg)
2297 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2298 .constrainAllUses(TII, TRI, RBI);
2299 return true;
2300}
2301
2302bool SPIRVInstructionSelector::selectStackRestore(MachineInstr &I) const {
2303 if (!STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_variable_length_array))
2304 return diagnoseUnsupported(
2305 I,
2306 Msg: "llvm.stackrestore intrinsic: this instruction requires the following "
2307 "SPIR-V extension: SPV_INTEL_variable_length_array");
2308 if (!I.getOperand(i: 0).isReg())
2309 return false;
2310 MachineBasicBlock &BB = *I.getParent();
2311 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpRestoreMemoryINTEL))
2312 .addUse(RegNo: I.getOperand(i: 0).getReg())
2313 .constrainAllUses(TII, TRI, RBI);
2314 return true;
2315}
2316
2317Register
2318SPIRVInstructionSelector::getOrCreateMemSetGlobal(MachineInstr &I) const {
2319 MachineIRBuilder MIRBuilder(I);
2320 assert(I.getOperand(1).isReg() && I.getOperand(2).isReg());
2321
2322 // TODO: check if we have such GV, add init, use buildGlobalVariable.
2323 unsigned Num = getIConstVal(ConstReg: I.getOperand(i: 2).getReg(), MRI);
2324 Function &CurFunction = GR.CurMF->getFunction();
2325 Type *LLVMArrTy =
2326 ArrayType::get(ElementType: IntegerType::get(C&: CurFunction.getContext(), NumBits: 8), NumElements: Num);
2327 GlobalVariable *GV = new GlobalVariable(*CurFunction.getParent(), LLVMArrTy,
2328 true, GlobalValue::InternalLinkage,
2329 Constant::getNullValue(Ty: LLVMArrTy));
2330
2331 Type *ValTy = Type::getInt8Ty(C&: I.getMF()->getFunction().getContext());
2332 Type *ArrTy = ArrayType::get(ElementType: ValTy, NumElements: Num);
2333 SPIRVTypeInst VarTy = GR.getOrCreateSPIRVPointerType(
2334 BaseType: ArrTy, MIRBuilder, SC: SPIRV::StorageClass::UniformConstant);
2335
2336 SPIRVTypeInst SpvArrTy = GR.getOrCreateSPIRVType(
2337 Type: ArrTy, MIRBuilder, AQ: SPIRV::AccessQualifier::None, EmitIR: false);
2338
2339 unsigned Val = getIConstVal(ConstReg: I.getOperand(i: 1).getReg(), MRI);
2340 Register Const = GR.getOrCreateConstIntArray(Val, Num, I, SpvType: SpvArrTy, TII);
2341
2342 Register VarReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2343 auto MIBVar =
2344 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVariable))
2345 .addDef(RegNo: VarReg)
2346 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: VarTy))
2347 .addImm(Val: SPIRV::StorageClass::UniformConstant)
2348 .addUse(RegNo: Const);
2349 MIBVar.constrainAllUses(TII, TRI, RBI);
2350
2351 GR.add(V: GV, MI: MIBVar);
2352 GR.addGlobalObject(V: GV, MF: GR.CurMF, R: VarReg);
2353
2354 buildOpDecorate(Reg: VarReg, I, TII, Dec: SPIRV::Decoration::Constant, DecArgs: {});
2355 return VarReg;
2356}
2357
2358bool SPIRVInstructionSelector::selectCopyMemory(MachineInstr &I,
2359 Register SrcReg) const {
2360 MachineBasicBlock &BB = *I.getParent();
2361 Register DstReg = I.getOperand(i: 0).getReg();
2362 SPIRVTypeInst DstTy = GR.getSPIRVTypeForVReg(VReg: DstReg);
2363 SPIRVTypeInst SrcTy = GR.getSPIRVTypeForVReg(VReg: SrcReg);
2364 if (GR.getPointeeType(PtrType: DstTy) != GR.getPointeeType(PtrType: SrcTy))
2365 return diagnoseUnsupported(
2366 I, Msg: "OpCopyMemory requires operands to have the same type");
2367 uint64_t CopySize = getIConstVal(ConstReg: I.getOperand(i: 2).getReg(), MRI);
2368 SPIRVTypeInst PointeeTy = GR.getPointeeType(PtrType: DstTy);
2369 const Type *LLVMPointeeTy = GR.getTypeForSPIRVType(Ty: PointeeTy);
2370 if (!LLVMPointeeTy)
2371 return diagnoseUnsupported(
2372 I, Msg: "Unable to determine pointee type size for OpCopyMemory");
2373 const DataLayout &DL = I.getMF()->getFunction().getDataLayout();
2374 if (CopySize != DL.getTypeStoreSize(Ty: const_cast<Type *>(LLVMPointeeTy)))
2375 return diagnoseUnsupported(
2376 I, Msg: "OpCopyMemory requires the size to match the pointee type size");
2377 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCopyMemory))
2378 .addUse(RegNo: DstReg)
2379 .addUse(RegNo: SrcReg);
2380 if (I.getNumMemOperands()) {
2381 MachineIRBuilder MIRBuilder(I);
2382 addMemoryOperands(MemOp: *I.memoperands_begin(), MIB, MIRBuilder, GR);
2383 }
2384 MIB.constrainAllUses(TII, TRI, RBI);
2385 return true;
2386}
2387
2388bool SPIRVInstructionSelector::selectCopyMemorySized(MachineInstr &I,
2389 Register SrcReg) const {
2390 MachineBasicBlock &BB = *I.getParent();
2391 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCopyMemorySized))
2392 .addUse(RegNo: I.getOperand(i: 0).getReg())
2393 .addUse(RegNo: SrcReg)
2394 .addUse(RegNo: I.getOperand(i: 2).getReg());
2395 if (I.getNumMemOperands()) {
2396 MachineIRBuilder MIRBuilder(I);
2397 addMemoryOperands(MemOp: *I.memoperands_begin(), MIB, MIRBuilder, GR);
2398 }
2399 MIB.constrainAllUses(TII, TRI, RBI);
2400 return true;
2401}
2402
2403bool SPIRVInstructionSelector::selectMemOperation(Register ResVReg,
2404 MachineInstr &I) const {
2405 // Zero-sized memcpy/memmove/memset are no-ops.
2406 Register SizeReg = I.getOperand(i: 2).getReg();
2407 if (MachineInstr *SizeDef = getDefInstrMaybeConstant(ConstReg&: SizeReg, MRI);
2408 SizeDef && SizeDef->getOpcode() == TargetOpcode::G_CONSTANT &&
2409 getIConstVal(ConstReg: SizeReg, MRI) == 0)
2410 return true;
2411
2412 Register SrcReg = I.getOperand(i: 1).getReg();
2413 if (I.getOpcode() == TargetOpcode::G_MEMSET ||
2414 I.getOpcode() == TargetOpcode::G_MEMSET_INLINE) {
2415 Register VarReg = getOrCreateMemSetGlobal(I);
2416 if (!VarReg.isValid())
2417 return false;
2418 Type *ValTy = Type::getInt8Ty(C&: I.getMF()->getFunction().getContext());
2419 SPIRVTypeInst SourceTy = GR.getOrCreateSPIRVPointerType(
2420 BaseType: ValTy, I, SC: SPIRV::StorageClass::UniformConstant);
2421 SrcReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2422 if (!selectOpWithSrcs(ResVReg: SrcReg, ResType: SourceTy, I, Srcs: {VarReg}, Opcode: SPIRV::OpBitcast))
2423 return false;
2424 }
2425 if (STI.isLogicalSPIRV()) {
2426 if (!selectCopyMemory(I, SrcReg))
2427 return false;
2428 } else {
2429 if (!selectCopyMemorySized(I, SrcReg))
2430 return false;
2431 }
2432 if (ResVReg.isValid() && ResVReg != I.getOperand(i: 0).getReg())
2433 if (!BuildCOPY(DestReg: ResVReg, SrcReg: I.getOperand(i: 0).getReg(), I))
2434 return false;
2435 return true;
2436}
2437
2438bool SPIRVInstructionSelector::selectAtomicRMW(Register ResVReg,
2439 SPIRVTypeInst ResType,
2440 MachineInstr &I,
2441 unsigned NewOpcode,
2442 unsigned NegateOpcode) const {
2443 assert(I.hasOneMemOperand());
2444 const MachineMemOperand *MemOp = *I.memoperands_begin();
2445 uint32_t Scope = static_cast<uint32_t>(getMemScope(
2446 Ctx&: GR.CurMF->getFunction().getContext(), Id: MemOp->getSyncScopeID()));
2447 Register ScopeReg = buildI32Constant(Val: Scope, I);
2448
2449 Register Ptr = I.getOperand(i: 1).getReg();
2450 uint32_t ScSem = static_cast<uint32_t>(
2451 getMemSemanticsForStorageClass(SC: GR.getPointerStorageClass(VReg: Ptr)));
2452 AtomicOrdering AO = MemOp->getSuccessOrdering();
2453 uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(Ord: AO)) | ScSem;
2454 Register MemSemReg = buildI32Constant(Val: MemSem, I);
2455
2456 Register ValueReg = I.getOperand(i: 2).getReg();
2457 if (NegateOpcode != 0) {
2458 // Translation with negative value operand is requested
2459 Register TmpReg = createVirtualRegister(SpvType: ResType, GR: &GR, MRI, MF: MRI->getMF());
2460 if (!selectOpWithSrcs(ResVReg: TmpReg, ResType, I, Srcs: {ValueReg}, Opcode: NegateOpcode))
2461 return false;
2462 ValueReg = TmpReg;
2463 }
2464
2465 if (ResType.isTypePtr()) {
2466 if (NewOpcode != SPIRV::OpAtomicExchange)
2467 return diagnoseUnsupported(
2468 I, Msg: "Lowering to SPIR-V of this atomic operation is not "
2469 "allowed for pointer types");
2470 if (!STI.isPhysicalSPIRV())
2471 return diagnoseUnsupported(
2472 I, Msg: "Lowering to SPIR-V of atomic exchange is only "
2473 "allowed for pointer types for physical addressing model");
2474 // If the exchanged value is a pointer type we convert the value operand to
2475 // an integer type of the same size as the pointer size using
2476 // OpConvertPtrToU, bitcast the Ptr parameter to pointer to integer type and
2477 // then generate OpAtomicExchange on integers. The integer result is
2478 // converted back to a pointer type using OpConvertUToPtr, similar to atomic
2479 // load and store.
2480 MachineIRBuilder MIRBuilder(I);
2481 unsigned PtrSize = GR.getPointerSize();
2482 SPIRVTypeInst PtrAsIntSpirvType =
2483 GR.getOrCreateSPIRVIntegerType(BitWidth: PtrSize, MIRBuilder);
2484
2485 Register ValueAsIntReg =
2486 MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: PtrSize));
2487 MRI->setRegClass(Reg: ValueAsIntReg, RC: GR.getRegClass(SpvType: PtrAsIntSpirvType));
2488 GR.assignSPIRVTypeToVReg(Type: PtrAsIntSpirvType, VReg: ValueAsIntReg,
2489 MF: MIRBuilder.getMF());
2490 MIRBuilder.buildInstr(Opcode: SPIRV::OpConvertPtrToU)
2491 .addDef(RegNo: ValueAsIntReg)
2492 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrAsIntSpirvType)) // Result type
2493 .addUse(RegNo: ValueReg) // Pointer operand
2494 .constrainAllUses(TII, TRI, RBI);
2495
2496 SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
2497 BaseType: PtrAsIntSpirvType, MIRBuilder, SC: GR.getPointerStorageClass(VReg: Ptr));
2498 Register PtrCastedToMatchValReg =
2499 MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: PtrSize));
2500 MRI->setRegClass(Reg: PtrCastedToMatchValReg, RC: GR.getRegClass(SpvType: PtrType));
2501 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: PtrCastedToMatchValReg,
2502 MF: MIRBuilder.getMF());
2503 MIRBuilder.buildInstr(Opcode: SPIRV::OpBitcast)
2504 .addDef(RegNo: PtrCastedToMatchValReg)
2505 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrType))
2506 .addUse(RegNo: Ptr)
2507 .constrainAllUses(TII, TRI, RBI);
2508
2509 Register ExchangeResReg =
2510 MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: PtrSize));
2511 MRI->setRegClass(Reg: ExchangeResReg, RC: GR.getRegClass(SpvType: PtrAsIntSpirvType));
2512 GR.assignSPIRVTypeToVReg(Type: PtrAsIntSpirvType, VReg: ExchangeResReg,
2513 MF: MIRBuilder.getMF());
2514 MIRBuilder.buildInstr(Opcode: SPIRV::OpAtomicExchange)
2515 .addDef(RegNo: ExchangeResReg)
2516 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrAsIntSpirvType))
2517 .addUse(RegNo: PtrCastedToMatchValReg)
2518 .addUse(RegNo: ScopeReg)
2519 .addUse(RegNo: MemSemReg)
2520 .addUse(RegNo: ValueAsIntReg)
2521 .constrainAllUses(TII, TRI, RBI);
2522 MIRBuilder.buildInstr(Opcode: SPIRV::OpConvertUToPtr)
2523 .addDef(RegNo: ResVReg)
2524 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2525 .addUse(RegNo: ExchangeResReg)
2526 .constrainAllUses(TII, TRI, RBI);
2527 return true;
2528 }
2529
2530 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: NewOpcode))
2531 .addDef(RegNo: ResVReg)
2532 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2533 .addUse(RegNo: Ptr)
2534 .addUse(RegNo: ScopeReg)
2535 .addUse(RegNo: MemSemReg)
2536 .addUse(RegNo: ValueReg)
2537 .constrainAllUses(TII, TRI, RBI);
2538 return true;
2539}
2540
2541bool SPIRVInstructionSelector::selectInterlockedOp(Register ResVReg,
2542 SPIRVTypeInst ResType,
2543 MachineInstr &I,
2544 unsigned Opcode) const {
2545 Register Ptr = I.getOperand(i: 2).getReg();
2546 Register Value = I.getOperand(i: 3).getReg();
2547
2548 SPIRV::StorageClass::StorageClass SC = GR.getPointerStorageClass(VReg: Ptr);
2549 assert((SC == SPIRV::StorageClass::Workgroup ||
2550 SC == SPIRV::StorageClass::StorageBuffer) &&
2551 "InterlockedAdd requires Workgroup or StorageBuffer storage class");
2552 uint32_t Scope = static_cast<uint32_t>(SC == SPIRV::StorageClass::Workgroup
2553 ? SPIRV::Scope::Workgroup
2554 : SPIRV::Scope::Device);
2555 Register ScopeReg = buildI32Constant(Val: Scope, I);
2556
2557 uint32_t MemSem = static_cast<uint32_t>(getMemSemanticsForStorageClass(SC));
2558 Register MemSemReg = buildI32Constant(Val: MemSem, I);
2559
2560 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
2561 .addDef(RegNo: ResVReg)
2562 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2563 .addUse(RegNo: Ptr)
2564 .addUse(RegNo: ScopeReg)
2565 .addUse(RegNo: MemSemReg)
2566 .addUse(RegNo: Value)
2567 .constrainAllUses(TII, TRI, RBI);
2568 return true;
2569}
2570
2571bool SPIRVInstructionSelector::selectUnmergeValues(MachineInstr &I) const {
2572 unsigned ArgI = I.getNumOperands() - 1;
2573 Register SrcReg =
2574 I.getOperand(i: ArgI).isReg() ? I.getOperand(i: ArgI).getReg() : Register(0);
2575 SPIRVTypeInst SrcType =
2576 SrcReg.isValid() ? GR.getSPIRVTypeForVReg(VReg: SrcReg) : nullptr;
2577 if (!SrcType || SrcType->getOpcode() != SPIRV::OpTypeVector)
2578 report_fatal_error(
2579 reason: "cannot select G_UNMERGE_VALUES with a non-vector argument");
2580
2581 SPIRVTypeInst ScalarType = GR.getScalarOrVectorComponentType(Type: SrcType);
2582 MachineBasicBlock &BB = *I.getParent();
2583 unsigned CurrentIndex = 0;
2584 for (unsigned i = 0; i < I.getNumDefs(); ++i) {
2585 Register ResVReg = I.getOperand(i).getReg();
2586 SPIRVTypeInst ResType = GR.getSPIRVTypeForVReg(VReg: ResVReg);
2587 if (!ResType) {
2588 LLT ResLLT = MRI->getType(Reg: ResVReg);
2589 assert(ResLLT.isValid());
2590 if (ResLLT.isVector()) {
2591 ResType = GR.getOrCreateSPIRVVectorType(
2592 BaseType: ScalarType, NumElements: ResLLT.getNumElements(), I, TII);
2593 } else {
2594 ResType = ScalarType;
2595 }
2596 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
2597 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: ResVReg, MF: *GR.CurMF);
2598 }
2599
2600 if (ResType->getOpcode() == SPIRV::OpTypeVector) {
2601 Register UndefReg = GR.getOrCreateUndef(I, SpvType: SrcType, TII);
2602 auto MIB =
2603 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
2604 .addDef(RegNo: ResVReg)
2605 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2606 .addUse(RegNo: SrcReg)
2607 .addUse(RegNo: UndefReg);
2608 unsigned NumElements = GR.getScalarOrVectorComponentCount(Type: ResType);
2609 for (unsigned j = 0; j < NumElements; ++j) {
2610 MIB.addImm(Val: CurrentIndex + j);
2611 }
2612 CurrentIndex += NumElements;
2613 MIB.constrainAllUses(TII, TRI, RBI);
2614 } else {
2615 auto MIB =
2616 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
2617 .addDef(RegNo: ResVReg)
2618 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2619 .addUse(RegNo: SrcReg)
2620 .addImm(Val: CurrentIndex);
2621 CurrentIndex++;
2622 MIB.constrainAllUses(TII, TRI, RBI);
2623 }
2624 }
2625 return true;
2626}
2627
2628bool SPIRVInstructionSelector::selectFence(MachineInstr &I) const {
2629 AtomicOrdering AO = AtomicOrdering(I.getOperand(i: 0).getImm());
2630 uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(Ord: AO));
2631 Register MemSemReg = buildI32Constant(Val: MemSem, I);
2632 SyncScope::ID Ord = SyncScope::ID(I.getOperand(i: 1).getImm());
2633 uint32_t Scope = static_cast<uint32_t>(
2634 getMemScope(Ctx&: GR.CurMF->getFunction().getContext(), Id: Ord));
2635 Register ScopeReg = buildI32Constant(Val: Scope, I);
2636 MachineBasicBlock &BB = *I.getParent();
2637 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpMemoryBarrier))
2638 .addUse(RegNo: ScopeReg)
2639 .addUse(RegNo: MemSemReg)
2640 .constrainAllUses(TII, TRI, RBI);
2641 return true;
2642}
2643
2644bool SPIRVInstructionSelector::selectOverflowArith(Register ResVReg,
2645 SPIRVTypeInst ResType,
2646 MachineInstr &I,
2647 unsigned Opcode) const {
2648 Type *ResTy = nullptr;
2649 StringRef ResName;
2650 if (!GR.findValueAttrs(Key: &I, Ty&: ResTy, Name&: ResName))
2651 return diagnoseUnsupported(
2652 I,
2653 Msg: "Not enough info to select the arithmetic with overflow instruction");
2654 if (!ResTy || !ResTy->isStructTy())
2655 return diagnoseUnsupported(I,
2656 Msg: "Expect struct type result for the arithmetic "
2657 "with overflow instruction");
2658 // "Result Type must be from OpTypeStruct. The struct must have two members,
2659 // and the two members must be the same type."
2660 Type *ResElemTy = cast<StructType>(Val: ResTy)->getElementType(N: 0);
2661 ResTy = StructType::get(elt1: ResElemTy, elts: ResElemTy);
2662 // Build SPIR-V types and constant(s) if needed.
2663 MachineIRBuilder MIRBuilder(I);
2664 SPIRVTypeInst StructType = GR.getOrCreateSPIRVType(
2665 Type: ResTy, MIRBuilder, AQ: SPIRV::AccessQualifier::ReadWrite, EmitIR: false);
2666 assert(I.getNumDefs() > 1 && "Not enought operands");
2667 SPIRVTypeInst BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
2668 unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
2669 if (N > 1)
2670 BoolType = GR.getOrCreateSPIRVVectorType(BaseType: BoolType, NumElements: N, I, TII);
2671 Register BoolTypeReg = GR.getSPIRVTypeID(SpirvType: BoolType);
2672 Register ZeroReg = buildZerosVal(ResType, I);
2673 // A new virtual register to store the result struct.
2674 Register StructVReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2675 MRI->setRegClass(Reg: StructVReg, RC: &SPIRV::IDRegClass);
2676 // Build the result name if needed.
2677 if (ResName.size() > 0)
2678 buildOpName(Target: StructVReg, Name: ResName, MIRBuilder);
2679 // Build the arithmetic with overflow instruction.
2680 MachineBasicBlock &BB = *I.getParent();
2681 auto MIB =
2682 BuildMI(BB, I: MIRBuilder.getInsertPt(), MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
2683 .addDef(RegNo: StructVReg)
2684 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: StructType));
2685 for (unsigned i = I.getNumDefs(); i < I.getNumOperands(); ++i)
2686 MIB.addUse(RegNo: I.getOperand(i).getReg());
2687 MIB.constrainAllUses(TII, TRI, RBI);
2688 // Build instructions to extract fields of the instruction's result.
2689 // A new virtual register to store the higher part of the result struct.
2690 Register HigherVReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
2691 MRI->setRegClass(Reg: HigherVReg, RC: &SPIRV::iIDRegClass);
2692 for (unsigned i = 0; i < I.getNumDefs(); ++i) {
2693 auto MIB =
2694 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
2695 .addDef(RegNo: i == 1 ? HigherVReg : I.getOperand(i).getReg())
2696 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2697 .addUse(RegNo: StructVReg)
2698 .addImm(Val: i);
2699 MIB.constrainAllUses(TII, TRI, RBI);
2700 }
2701 // Build boolean value from the higher part.
2702 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpINotEqual))
2703 .addDef(RegNo: I.getOperand(i: 1).getReg())
2704 .addUse(RegNo: BoolTypeReg)
2705 .addUse(RegNo: HigherVReg)
2706 .addUse(RegNo: ZeroReg)
2707 .constrainAllUses(TII, TRI, RBI);
2708 return true;
2709}
2710
2711bool SPIRVInstructionSelector::selectAtomicCmpXchg(Register ResVReg,
2712 SPIRVTypeInst ResType,
2713 MachineInstr &I) const {
2714 assert(isa<GIntrinsic>(I) &&
2715 "selectAtomicCmpXchg only handles the spv_cmpxchg intrinsic");
2716 Register Ptr = I.getOperand(i: 2).getReg();
2717 Register ScopeReg = I.getOperand(i: 5).getReg();
2718 Register MemSemEqReg = I.getOperand(i: 6).getReg();
2719 Register MemSemNeqReg = I.getOperand(i: 7).getReg();
2720 Register Cmp = I.getOperand(i: 3).getReg();
2721 Register Val = I.getOperand(i: 4).getReg();
2722 SPIRVTypeInst SpvValTy = GR.getSPIRVTypeForVReg(VReg: Val);
2723 Register ACmpRes = createVirtualRegister(SpvType: SpvValTy, GR: &GR, MRI, MF: *I.getMF());
2724 const DebugLoc &DL = I.getDebugLoc();
2725 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpAtomicCompareExchange))
2726 .addDef(RegNo: ACmpRes)
2727 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvValTy))
2728 .addUse(RegNo: Ptr)
2729 .addUse(RegNo: ScopeReg)
2730 .addUse(RegNo: MemSemEqReg)
2731 .addUse(RegNo: MemSemNeqReg)
2732 .addUse(RegNo: Val)
2733 .addUse(RegNo: Cmp)
2734 .constrainAllUses(TII, TRI, RBI);
2735 SPIRVTypeInst BoolTy = GR.getOrCreateSPIRVBoolType(I, TII);
2736 Register CmpSuccReg = createVirtualRegister(SpvType: BoolTy, GR: &GR, MRI, MF: *I.getMF());
2737 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpIEqual))
2738 .addDef(RegNo: CmpSuccReg)
2739 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: BoolTy))
2740 .addUse(RegNo: ACmpRes)
2741 .addUse(RegNo: Cmp)
2742 .constrainAllUses(TII, TRI, RBI);
2743 Register TmpReg = createVirtualRegister(SpvType: ResType, GR: &GR, MRI, MF: *I.getMF());
2744 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeInsert))
2745 .addDef(RegNo: TmpReg)
2746 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2747 .addUse(RegNo: ACmpRes)
2748 .addUse(RegNo: GR.getOrCreateUndef(I, SpvType: ResType, TII))
2749 .addImm(Val: 0)
2750 .constrainAllUses(TII, TRI, RBI);
2751 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeInsert))
2752 .addDef(RegNo: ResVReg)
2753 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2754 .addUse(RegNo: CmpSuccReg)
2755 .addUse(RegNo: TmpReg)
2756 .addImm(Val: 1)
2757 .constrainAllUses(TII, TRI, RBI);
2758 return true;
2759}
2760
2761static bool isUSMStorageClass(SPIRV::StorageClass::StorageClass SC) {
2762 switch (SC) {
2763 case SPIRV::StorageClass::DeviceOnlyINTEL:
2764 case SPIRV::StorageClass::HostOnlyINTEL:
2765 return true;
2766 default:
2767 return false;
2768 }
2769}
2770
2771// Returns true ResVReg is referred only from global vars and OpName's.
2772static bool isASCastInGVar(MachineRegisterInfo *MRI, Register ResVReg) {
2773 bool IsGRef = false;
2774 bool IsAllowedRefs =
2775 llvm::all_of(Range: MRI->use_instructions(Reg: ResVReg), P: [&IsGRef](auto const &It) {
2776 unsigned Opcode = It.getOpcode();
2777 if (Opcode == SPIRV::OpConstantComposite ||
2778 Opcode == SPIRV::OpSpecConstantComposite ||
2779 Opcode == SPIRV::OpVariable ||
2780 isSpvIntrinsic(It, Intrinsic::spv_init_global))
2781 return IsGRef = true;
2782 return Opcode == SPIRV::OpName;
2783 });
2784 return IsAllowedRefs && IsGRef;
2785}
2786
2787Register SPIRVInstructionSelector::getUcharPtrTypeReg(
2788 MachineInstr &I, SPIRV::StorageClass::StorageClass SC) const {
2789 return GR.getSPIRVTypeID(SpirvType: GR.getOrCreateSPIRVPointerType(
2790 BaseType: Type::getInt8Ty(C&: I.getMF()->getFunction().getContext()), I, SC));
2791}
2792
2793MachineInstrBuilder
2794SPIRVInstructionSelector::buildSpecConstantOp(MachineInstr &I, Register Dest,
2795 Register Src, Register DestType,
2796 uint32_t Opcode) const {
2797 return BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
2798 MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
2799 .addDef(RegNo: Dest)
2800 .addUse(RegNo: DestType)
2801 .addImm(Val: Opcode)
2802 .addUse(RegNo: Src);
2803}
2804
2805MachineInstrBuilder
2806SPIRVInstructionSelector::buildConstGenericPtr(MachineInstr &I, Register SrcPtr,
2807 SPIRVTypeInst SrcPtrTy) const {
2808 SPIRVTypeInst GenericPtrTy =
2809 GR.changePointerStorageClass(PtrType: SrcPtrTy, SC: SPIRV::StorageClass::Generic, I);
2810 Register Tmp = MRI->createVirtualRegister(RegClass: &SPIRV::pIDRegClass);
2811 MRI->setType(VReg: Tmp, Ty: LLT::pointer(AddressSpace: storageClassToAddressSpace(
2812 SC: SPIRV::StorageClass::Generic),
2813 SizeInBits: GR.getPointerSize()));
2814 MachineFunction *MF = I.getParent()->getParent();
2815 GR.assignSPIRVTypeToVReg(Type: GenericPtrTy, VReg: Tmp, MF: *MF);
2816 MachineInstrBuilder MIB = buildSpecConstantOp(
2817 I, Dest: Tmp, Src: SrcPtr, DestType: GR.getSPIRVTypeID(SpirvType: GenericPtrTy),
2818 Opcode: static_cast<uint32_t>(SPIRV::Opcode::PtrCastToGeneric));
2819 GR.add(Obj: MIB.getInstr(), MI: MIB);
2820 return MIB;
2821}
2822
2823// In SPIR-V address space casting can only happen to and from the Generic
2824// storage class. We can also only cast Workgroup, CrossWorkgroup, or Function
2825// pointers to and from Generic pointers. As such, we can convert e.g. from
2826// Workgroup to Function by going via a Generic pointer as an intermediary. All
2827// other combinations can only be done by a bitcast, and are probably not safe.
2828bool SPIRVInstructionSelector::selectAddrSpaceCast(Register ResVReg,
2829 SPIRVTypeInst ResType,
2830 MachineInstr &I) const {
2831 MachineBasicBlock &BB = *I.getParent();
2832 const DebugLoc &DL = I.getDebugLoc();
2833
2834 Register SrcPtr = I.getOperand(i: 1).getReg();
2835 SPIRVTypeInst SrcPtrTy = GR.getSPIRVTypeForVReg(VReg: SrcPtr);
2836
2837 // don't generate a cast for a null that may be represented by OpTypeInt
2838 if (SrcPtrTy->getOpcode() != SPIRV::OpTypePointer ||
2839 ResType->getOpcode() != SPIRV::OpTypePointer)
2840 return BuildCOPY(DestReg: ResVReg, SrcReg: SrcPtr, I);
2841
2842 SPIRV::StorageClass::StorageClass SrcSC = GR.getPointerStorageClass(Type: SrcPtrTy);
2843 SPIRV::StorageClass::StorageClass DstSC = GR.getPointerStorageClass(Type: ResType);
2844
2845 if (isASCastInGVar(MRI, ResVReg)) {
2846 // AddrSpaceCast uses within OpVariable and OpConstantComposite instructions
2847 // are expressed by OpSpecConstantOp with an Opcode.
2848 // TODO: maybe insert a check whether the Kernel capability was declared and
2849 // so PtrCastToGeneric/GenericCastToPtr are available.
2850 unsigned SpecOpcode =
2851 DstSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SC: SrcSC)
2852 ? static_cast<uint32_t>(SPIRV::Opcode::PtrCastToGeneric)
2853 : (SrcSC == SPIRV::StorageClass::Generic &&
2854 isGenericCastablePtr(SC: DstSC)
2855 ? static_cast<uint32_t>(SPIRV::Opcode::GenericCastToPtr)
2856 : 0);
2857 // TODO: OpConstantComposite expects i8*, so we are forced to forget a
2858 // correct value of ResType and use general i8* instead. Maybe this should
2859 // be addressed in the emit-intrinsic step to infer a correct
2860 // OpConstantComposite type.
2861 if (SpecOpcode) {
2862 buildSpecConstantOp(I, Dest: ResVReg, Src: SrcPtr, DestType: getUcharPtrTypeReg(I, SC: DstSC),
2863 Opcode: SpecOpcode)
2864 .constrainAllUses(TII, TRI, RBI);
2865 } else if (isGenericCastablePtr(SC: SrcSC) && isGenericCastablePtr(SC: DstSC)) {
2866 MachineInstrBuilder MIB = buildConstGenericPtr(I, SrcPtr, SrcPtrTy);
2867 MIB.constrainAllUses(TII, TRI, RBI);
2868 buildSpecConstantOp(
2869 I, Dest: ResVReg, Src: MIB->getOperand(i: 0).getReg(), DestType: getUcharPtrTypeReg(I, SC: DstSC),
2870 Opcode: static_cast<uint32_t>(SPIRV::Opcode::GenericCastToPtr))
2871 .constrainAllUses(TII, TRI, RBI);
2872 }
2873 return true;
2874 }
2875
2876 // don't generate a cast between identical storage classes
2877 if (SrcSC == DstSC)
2878 return BuildCOPY(DestReg: ResVReg, SrcReg: SrcPtr, I);
2879
2880 if ((SrcSC == SPIRV::StorageClass::Function &&
2881 DstSC == SPIRV::StorageClass::Private) ||
2882 (DstSC == SPIRV::StorageClass::Function &&
2883 SrcSC == SPIRV::StorageClass::Private))
2884 return BuildCOPY(DestReg: ResVReg, SrcReg: SrcPtr, I);
2885
2886 // Casting from an eligible pointer to Generic.
2887 if (DstSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SC: SrcSC))
2888 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpPtrCastToGeneric);
2889 // Casting from Generic to an eligible pointer.
2890 if (SrcSC == SPIRV::StorageClass::Generic && isGenericCastablePtr(SC: DstSC))
2891 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpGenericCastToPtr);
2892 // Casting between 2 eligible pointers using Generic as an intermediary.
2893 if (isGenericCastablePtr(SC: SrcSC) && isGenericCastablePtr(SC: DstSC)) {
2894 SPIRVTypeInst GenericPtrTy =
2895 GR.changePointerStorageClass(PtrType: SrcPtrTy, SC: SPIRV::StorageClass::Generic, I);
2896 Register Tmp = createVirtualRegister(SpvType: GenericPtrTy, GR: &GR, MRI, MF: MRI->getMF());
2897 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpPtrCastToGeneric))
2898 .addDef(RegNo: Tmp)
2899 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: GenericPtrTy))
2900 .addUse(RegNo: SrcPtr)
2901 .constrainAllUses(TII, TRI, RBI);
2902 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGenericCastToPtr))
2903 .addDef(RegNo: ResVReg)
2904 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2905 .addUse(RegNo: Tmp)
2906 .constrainAllUses(TII, TRI, RBI);
2907 return true;
2908 }
2909
2910 // Check if instructions from the SPV_INTEL_usm_storage_classes extension may
2911 // be applied
2912 if (isUSMStorageClass(SC: SrcSC) && DstSC == SPIRV::StorageClass::CrossWorkgroup)
2913 return selectUnOp(ResVReg, ResType, I,
2914 Opcode: SPIRV::OpPtrCastToCrossWorkgroupINTEL);
2915 if (SrcSC == SPIRV::StorageClass::CrossWorkgroup && isUSMStorageClass(SC: DstSC))
2916 return selectUnOp(ResVReg, ResType, I,
2917 Opcode: SPIRV::OpCrossWorkgroupCastToPtrINTEL);
2918 if (isUSMStorageClass(SC: SrcSC) && DstSC == SPIRV::StorageClass::Generic)
2919 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpPtrCastToGeneric);
2920 if (SrcSC == SPIRV::StorageClass::Generic && isUSMStorageClass(SC: DstSC))
2921 return selectUnOp(ResVReg, ResType, I, Opcode: SPIRV::OpGenericCastToPtr);
2922
2923 // Bitcast for pointers requires that the address spaces must match
2924 return false;
2925}
2926
2927// G_PTRMASK - Apply a bitmask to a pointer value.
2928// Result = Ptr & Mask
2929// We need to convert the pointer to an integer, perform the AND operation,
2930// and convert back to a pointer.
2931bool SPIRVInstructionSelector::selectPtrMask(Register ResVReg,
2932 SPIRVTypeInst ResType,
2933 MachineInstr &I) const {
2934 if (STI.isLogicalSPIRV())
2935 return diagnoseUnsupported(
2936 I, Msg: "G_PTRMASK is not supported with logical SPIR-V");
2937 MachineBasicBlock &BB = *I.getParent();
2938 MachineFunction &MF = *BB.getParent();
2939 const DebugLoc &DL = I.getDebugLoc();
2940
2941 Register PtrReg = I.getOperand(i: 1).getReg();
2942 Register MaskReg = I.getOperand(i: 2).getReg();
2943
2944 SPIRVTypeInst MaskType = GR.getSPIRVTypeForVReg(VReg: MaskReg);
2945
2946 // Convert pointer to integer.
2947 Register PtrAsInt = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: MaskType));
2948 GR.assignSPIRVTypeToVReg(Type: MaskType, VReg: PtrAsInt, MF);
2949
2950 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpConvertPtrToU))
2951 .addDef(RegNo: PtrAsInt)
2952 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MaskType))
2953 .addUse(RegNo: PtrReg)
2954 .constrainAllUses(TII, TRI, RBI);
2955
2956 // Perform bitwise AND.
2957 Register MaskedInt = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: MaskType));
2958 GR.assignSPIRVTypeToVReg(Type: MaskType, VReg: MaskedInt, MF);
2959
2960 unsigned AndOpcode = GR.getScalarOrVectorComponentCount(Type: MaskType) > 1
2961 ? SPIRV::OpBitwiseAndV
2962 : SPIRV::OpBitwiseAndS;
2963
2964 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: AndOpcode))
2965 .addDef(RegNo: MaskedInt)
2966 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MaskType))
2967 .addUse(RegNo: PtrAsInt)
2968 .addUse(RegNo: MaskReg)
2969 .constrainAllUses(TII, TRI, RBI);
2970
2971 // Convert integer back to pointer.
2972 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpConvertUToPtr))
2973 .addDef(RegNo: ResVReg)
2974 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
2975 .addUse(RegNo: MaskedInt)
2976 .constrainAllUses(TII, TRI, RBI);
2977
2978 return true;
2979}
2980
2981static unsigned getFCmpOpcode(unsigned PredNum) {
2982 auto Pred = static_cast<CmpInst::Predicate>(PredNum);
2983 switch (Pred) {
2984 case CmpInst::FCMP_OEQ:
2985 return SPIRV::OpFOrdEqual;
2986 case CmpInst::FCMP_OGE:
2987 return SPIRV::OpFOrdGreaterThanEqual;
2988 case CmpInst::FCMP_OGT:
2989 return SPIRV::OpFOrdGreaterThan;
2990 case CmpInst::FCMP_OLE:
2991 return SPIRV::OpFOrdLessThanEqual;
2992 case CmpInst::FCMP_OLT:
2993 return SPIRV::OpFOrdLessThan;
2994 case CmpInst::FCMP_ONE:
2995 return SPIRV::OpFOrdNotEqual;
2996 case CmpInst::FCMP_ORD:
2997 return SPIRV::OpOrdered;
2998 case CmpInst::FCMP_UEQ:
2999 return SPIRV::OpFUnordEqual;
3000 case CmpInst::FCMP_UGE:
3001 return SPIRV::OpFUnordGreaterThanEqual;
3002 case CmpInst::FCMP_UGT:
3003 return SPIRV::OpFUnordGreaterThan;
3004 case CmpInst::FCMP_ULE:
3005 return SPIRV::OpFUnordLessThanEqual;
3006 case CmpInst::FCMP_ULT:
3007 return SPIRV::OpFUnordLessThan;
3008 case CmpInst::FCMP_UNE:
3009 return SPIRV::OpFUnordNotEqual;
3010 case CmpInst::FCMP_UNO:
3011 return SPIRV::OpUnordered;
3012 default:
3013 llvm_unreachable("Unknown predicate type for FCmp");
3014 }
3015}
3016
3017static unsigned getICmpOpcode(unsigned PredNum) {
3018 auto Pred = static_cast<CmpInst::Predicate>(PredNum);
3019 switch (Pred) {
3020 case CmpInst::ICMP_EQ:
3021 return SPIRV::OpIEqual;
3022 case CmpInst::ICMP_NE:
3023 return SPIRV::OpINotEqual;
3024 case CmpInst::ICMP_SGE:
3025 return SPIRV::OpSGreaterThanEqual;
3026 case CmpInst::ICMP_SGT:
3027 return SPIRV::OpSGreaterThan;
3028 case CmpInst::ICMP_SLE:
3029 return SPIRV::OpSLessThanEqual;
3030 case CmpInst::ICMP_SLT:
3031 return SPIRV::OpSLessThan;
3032 case CmpInst::ICMP_UGE:
3033 return SPIRV::OpUGreaterThanEqual;
3034 case CmpInst::ICMP_UGT:
3035 return SPIRV::OpUGreaterThan;
3036 case CmpInst::ICMP_ULE:
3037 return SPIRV::OpULessThanEqual;
3038 case CmpInst::ICMP_ULT:
3039 return SPIRV::OpULessThan;
3040 default:
3041 llvm_unreachable("Unknown predicate type for ICmp");
3042 }
3043}
3044
3045static unsigned getPtrCmpOpcode(unsigned Pred) {
3046 switch (static_cast<CmpInst::Predicate>(Pred)) {
3047 case CmpInst::ICMP_EQ:
3048 return SPIRV::OpPtrEqual;
3049 case CmpInst::ICMP_NE:
3050 return SPIRV::OpPtrNotEqual;
3051 default:
3052 llvm_unreachable("Unknown predicate type for pointer comparison");
3053 }
3054}
3055
3056// Return the logical operation, or abort if none exists.
3057static unsigned getBoolCmpOpcode(unsigned PredNum) {
3058 auto Pred = static_cast<CmpInst::Predicate>(PredNum);
3059 switch (Pred) {
3060 case CmpInst::ICMP_EQ:
3061 return SPIRV::OpLogicalEqual;
3062 case CmpInst::ICMP_NE:
3063 return SPIRV::OpLogicalNotEqual;
3064 default:
3065 llvm_unreachable("Unknown predicate type for Bool comparison");
3066 }
3067}
3068
3069static APFloat getZeroFP(const Type *LLVMFloatTy) {
3070 if (!LLVMFloatTy)
3071 return APFloat::getZero(Sem: APFloat::IEEEsingle());
3072 switch (LLVMFloatTy->getScalarType()->getTypeID()) {
3073 case Type::HalfTyID:
3074 return APFloat::getZero(Sem: APFloat::IEEEhalf());
3075 case Type::BFloatTyID:
3076 return APFloat::getZero(Sem: APFloat::BFloat());
3077 default:
3078 case Type::FloatTyID:
3079 return APFloat::getZero(Sem: APFloat::IEEEsingle());
3080 case Type::DoubleTyID:
3081 return APFloat::getZero(Sem: APFloat::IEEEdouble());
3082 }
3083}
3084
3085static APFloat getOneFP(const Type *LLVMFloatTy) {
3086 if (!LLVMFloatTy)
3087 return APFloat::getOne(Sem: APFloat::IEEEsingle());
3088 switch (LLVMFloatTy->getScalarType()->getTypeID()) {
3089 case Type::HalfTyID:
3090 return APFloat::getOne(Sem: APFloat::IEEEhalf());
3091 case Type::BFloatTyID:
3092 return APFloat::getOne(Sem: APFloat::BFloat());
3093 default:
3094 case Type::FloatTyID:
3095 return APFloat::getOne(Sem: APFloat::IEEEsingle());
3096 case Type::DoubleTyID:
3097 return APFloat::getOne(Sem: APFloat::IEEEdouble());
3098 }
3099}
3100
3101bool SPIRVInstructionSelector::selectAnyOrAll(Register ResVReg,
3102 SPIRVTypeInst ResType,
3103 MachineInstr &I,
3104 unsigned OpAnyOrAll) const {
3105 assert(I.getNumOperands() == 3);
3106 assert(I.getOperand(2).isReg());
3107 MachineBasicBlock &BB = *I.getParent();
3108 Register InputRegister = I.getOperand(i: 2).getReg();
3109 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3110
3111 assert(InputType && "VReg has no type assigned");
3112
3113 bool IsBoolTy = GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeBool);
3114 bool IsVectorTy = InputType->getOpcode() == SPIRV::OpTypeVector;
3115 if (IsBoolTy && !IsVectorTy) {
3116 assert(ResVReg == I.getOperand(0).getReg());
3117 return BuildCOPY(DestReg: ResVReg, SrcReg: InputRegister, I);
3118 }
3119
3120 bool IsFloatTy = GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3121 unsigned SpirvNotEqualId =
3122 IsFloatTy ? SPIRV::OpFOrdNotEqual : SPIRV::OpINotEqual;
3123 SPIRVTypeInst SpvBoolScalarTy = GR.getOrCreateSPIRVBoolType(I, TII);
3124 SPIRVTypeInst SpvBoolTy = SpvBoolScalarTy;
3125 Register NotEqualReg = ResVReg;
3126
3127 if (IsVectorTy) {
3128 NotEqualReg =
3129 IsBoolTy ? InputRegister
3130 : createVirtualRegister(SpvType: SpvBoolTy, GR: &GR, MRI, MF: MRI->getMF());
3131 const unsigned NumElts = GR.getScalarOrVectorComponentCount(Type: InputType);
3132 SpvBoolTy = GR.getOrCreateSPIRVVectorType(BaseType: SpvBoolTy, NumElements: NumElts, I, TII);
3133 }
3134
3135 if (!IsBoolTy) {
3136 Register ConstZeroReg =
3137 IsFloatTy ? buildZerosValF(ResType: InputType, I) : buildZerosVal(ResType: InputType, I);
3138
3139 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SpirvNotEqualId))
3140 .addDef(RegNo: NotEqualReg)
3141 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvBoolTy))
3142 .addUse(RegNo: InputRegister)
3143 .addUse(RegNo: ConstZeroReg)
3144 .constrainAllUses(TII, TRI, RBI);
3145 }
3146
3147 if (IsVectorTy)
3148 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: OpAnyOrAll))
3149 .addDef(RegNo: ResVReg)
3150 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvBoolScalarTy))
3151 .addUse(RegNo: NotEqualReg)
3152 .constrainAllUses(TII, TRI, RBI);
3153 return true;
3154}
3155
3156bool SPIRVInstructionSelector::selectAll(Register ResVReg,
3157 SPIRVTypeInst ResType,
3158 MachineInstr &I) const {
3159 return selectAnyOrAll(ResVReg, ResType, I, OpAnyOrAll: SPIRV::OpAll);
3160}
3161
3162bool SPIRVInstructionSelector::selectAny(Register ResVReg,
3163 SPIRVTypeInst ResType,
3164 MachineInstr &I) const {
3165 return selectAnyOrAll(ResVReg, ResType, I, OpAnyOrAll: SPIRV::OpAny);
3166}
3167
3168// Select the OpDot instruction for the given float dot
3169bool SPIRVInstructionSelector::selectFloatDot(Register ResVReg,
3170 SPIRVTypeInst ResType,
3171 MachineInstr &I) const {
3172 assert(I.getNumOperands() == 4);
3173 assert(I.getOperand(2).isReg());
3174 assert(I.getOperand(3).isReg());
3175
3176 [[maybe_unused]] SPIRVTypeInst VecType =
3177 GR.getSPIRVTypeForVReg(VReg: I.getOperand(i: 2).getReg());
3178
3179 assert(VecType->getOpcode() == SPIRV::OpTypeVector &&
3180 GR.getScalarOrVectorComponentCount(VecType) > 1 &&
3181 "dot product requires a vector of at least 2 components");
3182
3183 [[maybe_unused]] SPIRVTypeInst EltType =
3184 GR.getScalarOrVectorComponentType(Type: VecType);
3185
3186 assert(EltType->getOpcode() == SPIRV::OpTypeFloat);
3187
3188 MachineBasicBlock &BB = *I.getParent();
3189 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpDot))
3190 .addDef(RegNo: ResVReg)
3191 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3192 .addUse(RegNo: I.getOperand(i: 2).getReg())
3193 .addUse(RegNo: I.getOperand(i: 3).getReg())
3194 .constrainAllUses(TII, TRI, RBI);
3195 return true;
3196}
3197
3198bool SPIRVInstructionSelector::selectIntegerDot(Register ResVReg,
3199 SPIRVTypeInst ResType,
3200 MachineInstr &I,
3201 bool Signed) const {
3202 assert(I.getNumOperands() == 4);
3203 assert(I.getOperand(2).isReg());
3204 assert(I.getOperand(3).isReg());
3205 MachineBasicBlock &BB = *I.getParent();
3206
3207 auto DotOp = Signed ? SPIRV::OpSDot : SPIRV::OpUDot;
3208 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DotOp))
3209 .addDef(RegNo: ResVReg)
3210 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3211 .addUse(RegNo: I.getOperand(i: 2).getReg())
3212 .addUse(RegNo: I.getOperand(i: 3).getReg())
3213 .constrainAllUses(TII, TRI, RBI);
3214 return true;
3215}
3216
3217// Since pre-1.6 SPIRV has no integer dot implementation,
3218// expand by piecewise multiplying and adding the results
3219bool SPIRVInstructionSelector::selectIntegerDotExpansion(
3220 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3221 assert(I.getNumOperands() == 4);
3222 assert(I.getOperand(2).isReg());
3223 assert(I.getOperand(3).isReg());
3224 MachineBasicBlock &BB = *I.getParent();
3225
3226 // Multiply the vectors, then sum the results
3227 Register Vec0 = I.getOperand(i: 2).getReg();
3228 Register Vec1 = I.getOperand(i: 3).getReg();
3229 Register TmpVec = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3230 SPIRVTypeInst VecType = GR.getSPIRVTypeForVReg(VReg: Vec0);
3231
3232 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIMulV))
3233 .addDef(RegNo: TmpVec)
3234 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: VecType))
3235 .addUse(RegNo: Vec0)
3236 .addUse(RegNo: Vec1)
3237 .constrainAllUses(TII, TRI, RBI);
3238
3239 assert(VecType->getOpcode() == SPIRV::OpTypeVector &&
3240 GR.getScalarOrVectorComponentCount(VecType) > 1 &&
3241 "dot product requires a vector of at least 2 components");
3242
3243 Register Res = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3244 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
3245 .addDef(RegNo: Res)
3246 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3247 .addUse(RegNo: TmpVec)
3248 .addImm(Val: 0)
3249 .constrainAllUses(TII, TRI, RBI);
3250
3251 for (unsigned i = 1; i < GR.getScalarOrVectorComponentCount(Type: VecType); i++) {
3252 Register Elt = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3253
3254 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
3255 .addDef(RegNo: Elt)
3256 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3257 .addUse(RegNo: TmpVec)
3258 .addImm(Val: i)
3259 .constrainAllUses(TII, TRI, RBI);
3260
3261 Register Sum = i < GR.getScalarOrVectorComponentCount(Type: VecType) - 1
3262 ? MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType))
3263 : ResVReg;
3264
3265 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
3266 .addDef(RegNo: Sum)
3267 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3268 .addUse(RegNo: Res)
3269 .addUse(RegNo: Elt)
3270 .constrainAllUses(TII, TRI, RBI);
3271 Res = Sum;
3272 }
3273
3274 return true;
3275}
3276
3277bool SPIRVInstructionSelector::selectOpIsInf(Register ResVReg,
3278 SPIRVTypeInst ResType,
3279 MachineInstr &I) const {
3280 MachineBasicBlock &BB = *I.getParent();
3281 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsInf))
3282 .addDef(RegNo: ResVReg)
3283 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3284 .addUse(RegNo: I.getOperand(i: 2).getReg())
3285 .constrainAllUses(TII, TRI, RBI);
3286 return true;
3287}
3288
3289bool SPIRVInstructionSelector::selectOpIsNan(Register ResVReg,
3290 SPIRVTypeInst ResType,
3291 MachineInstr &I) const {
3292 MachineBasicBlock &BB = *I.getParent();
3293 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsNan))
3294 .addDef(RegNo: ResVReg)
3295 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3296 .addUse(RegNo: I.getOperand(i: 2).getReg())
3297 .constrainAllUses(TII, TRI, RBI);
3298 return true;
3299}
3300
3301bool SPIRVInstructionSelector::selectOpIsFinite(Register ResVReg,
3302 SPIRVTypeInst ResType,
3303 MachineInstr &I) const {
3304 MachineBasicBlock &BB = *I.getParent();
3305 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsFinite))
3306 .addDef(RegNo: ResVReg)
3307 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3308 .addUse(RegNo: I.getOperand(i: 2).getReg())
3309 .constrainAllUses(TII, TRI, RBI);
3310 return true;
3311}
3312
3313bool SPIRVInstructionSelector::selectOpIsNormal(Register ResVReg,
3314 SPIRVTypeInst ResType,
3315 MachineInstr &I) const {
3316 MachineBasicBlock &BB = *I.getParent();
3317 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIsNormal))
3318 .addDef(RegNo: ResVReg)
3319 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3320 .addUse(RegNo: I.getOperand(i: 2).getReg())
3321 .constrainAllUses(TII, TRI, RBI);
3322 return true;
3323}
3324
3325template <bool Signed>
3326bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
3327 SPIRVTypeInst ResType,
3328 MachineInstr &I) const {
3329 assert(I.getNumOperands() == 5);
3330 assert(I.getOperand(2).isReg());
3331 assert(I.getOperand(3).isReg());
3332 assert(I.getOperand(4).isReg());
3333 MachineBasicBlock &BB = *I.getParent();
3334
3335 Register Acc = I.getOperand(i: 2).getReg();
3336 Register X = I.getOperand(i: 3).getReg();
3337 Register Y = I.getOperand(i: 4).getReg();
3338
3339 auto DotOp = Signed ? SPIRV::OpSDot : SPIRV::OpUDot;
3340 Register Dot = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
3341 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DotOp))
3342 .addDef(RegNo: Dot)
3343 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3344 .addUse(RegNo: X)
3345 .addUse(RegNo: Y);
3346 MIB.addImm(Val: SPIRV::BuiltIn::PackedVectorFormat4x8Bit);
3347 MIB.constrainAllUses(TII, TRI, RBI);
3348
3349 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
3350 .addDef(RegNo: ResVReg)
3351 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3352 .addUse(RegNo: Dot)
3353 .addUse(RegNo: Acc)
3354 .constrainAllUses(TII, TRI, RBI);
3355 return true;
3356}
3357
3358// Since pre-1.6 SPIRV has no DotProductInput4x8BitPacked implementation,
3359// extract the elements of the packed inputs, multiply them and add the result
3360// to the accumulator.
3361template <bool Signed>
3362bool SPIRVInstructionSelector::selectDot4AddPackedExpansion(
3363 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3364 assert(I.getNumOperands() == 5);
3365 assert(I.getOperand(2).isReg());
3366 assert(I.getOperand(3).isReg());
3367 assert(I.getOperand(4).isReg());
3368 MachineBasicBlock &BB = *I.getParent();
3369
3370 Register Acc = I.getOperand(i: 2).getReg();
3371 Register X = I.getOperand(i: 3).getReg();
3372 Register Y = I.getOperand(i: 4).getReg();
3373
3374 SPIRVTypeInst EltType = GR.getOrCreateSPIRVIntegerType(BitWidth: 8, I, TII);
3375 auto ExtractOp =
3376 Signed ? SPIRV::OpBitFieldSExtract : SPIRV::OpBitFieldUExtract;
3377
3378 bool ZeroAsNull = !STI.isShader();
3379 // Extract the i8 element, multiply and add it to the accumulator
3380 for (unsigned i = 0; i < 4; i++) {
3381 // A[i]
3382 Register AElt = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3383 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: ExtractOp))
3384 .addDef(RegNo: AElt)
3385 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3386 .addUse(RegNo: X)
3387 .addUse(RegNo: GR.getOrCreateConstInt(Val: i * 8, I, SpvType: EltType, TII, ZeroAsNull))
3388 .addUse(RegNo: GR.getOrCreateConstInt(Val: 8, I, SpvType: EltType, TII, ZeroAsNull))
3389 .constrainAllUses(TII, TRI, RBI);
3390
3391 // B[i]
3392 Register BElt = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3393 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: ExtractOp))
3394 .addDef(RegNo: BElt)
3395 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3396 .addUse(RegNo: Y)
3397 .addUse(RegNo: GR.getOrCreateConstInt(Val: i * 8, I, SpvType: EltType, TII, ZeroAsNull))
3398 .addUse(RegNo: GR.getOrCreateConstInt(Val: 8, I, SpvType: EltType, TII, ZeroAsNull))
3399 .constrainAllUses(TII, TRI, RBI);
3400
3401 // A[i] * B[i]
3402 Register Mul = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3403 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIMulS))
3404 .addDef(RegNo: Mul)
3405 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3406 .addUse(RegNo: AElt)
3407 .addUse(RegNo: BElt)
3408 .constrainAllUses(TII, TRI, RBI);
3409
3410 // Discard 24 highest-bits so that stored i32 register is i8 equivalent
3411 Register MaskMul = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3412 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: ExtractOp))
3413 .addDef(RegNo: MaskMul)
3414 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3415 .addUse(RegNo: Mul)
3416 .addUse(RegNo: GR.getOrCreateConstInt(Val: 0, I, SpvType: EltType, TII, ZeroAsNull))
3417 .addUse(RegNo: GR.getOrCreateConstInt(Val: 8, I, SpvType: EltType, TII, ZeroAsNull))
3418 .constrainAllUses(TII, TRI, RBI);
3419
3420 // Acc = Acc + A[i] * B[i]
3421 Register Sum =
3422 i < 3 ? MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass) : ResVReg;
3423 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
3424 .addDef(RegNo: Sum)
3425 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3426 .addUse(RegNo: Acc)
3427 .addUse(RegNo: MaskMul)
3428 .constrainAllUses(TII, TRI, RBI);
3429
3430 Acc = Sum;
3431 }
3432
3433 return true;
3434}
3435
3436/// Transform saturate(x) to clamp(x, 0.0f, 1.0f) as SPIRV
3437/// does not have a saturate builtin.
3438bool SPIRVInstructionSelector::selectSaturate(Register ResVReg,
3439 SPIRVTypeInst ResType,
3440 MachineInstr &I) const {
3441 assert(I.getNumOperands() == 3);
3442 assert(I.getOperand(2).isReg());
3443 MachineBasicBlock &BB = *I.getParent();
3444 Register VZero = buildZerosValF(ResType, I);
3445 Register VOne = buildOnesValF(ResType, I);
3446
3447 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
3448 .addDef(RegNo: ResVReg)
3449 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3450 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
3451 .addImm(Val: GL::FClamp)
3452 .addUse(RegNo: I.getOperand(i: 2).getReg())
3453 .addUse(RegNo: VZero)
3454 .addUse(RegNo: VOne)
3455 .constrainAllUses(TII, TRI, RBI);
3456 return true;
3457}
3458
3459bool SPIRVInstructionSelector::selectSign(Register ResVReg,
3460 SPIRVTypeInst ResType,
3461 MachineInstr &I) const {
3462 assert(I.getNumOperands() == 3);
3463 assert(I.getOperand(2).isReg());
3464 MachineBasicBlock &BB = *I.getParent();
3465 Register InputRegister = I.getOperand(i: 2).getReg();
3466 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3467 auto &DL = I.getDebugLoc();
3468
3469 if (!InputType)
3470 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3471
3472 bool IsFloatTy = GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3473
3474 unsigned SignBitWidth = GR.getScalarOrVectorBitWidth(Type: InputType);
3475 unsigned ResBitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
3476
3477 bool NeedsConversion = IsFloatTy || SignBitWidth != ResBitWidth;
3478
3479 auto SignOpcode = IsFloatTy ? GL::FSign : GL::SSign;
3480 Register SignReg = NeedsConversion
3481 ? MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass)
3482 : ResVReg;
3483
3484 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpExtInst))
3485 .addDef(RegNo: SignReg)
3486 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: InputType))
3487 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
3488 .addImm(Val: SignOpcode)
3489 .addUse(RegNo: InputRegister)
3490 .constrainAllUses(TII, TRI, RBI);
3491
3492 if (NeedsConversion) {
3493 auto ConvertOpcode = IsFloatTy ? SPIRV::OpConvertFToS : SPIRV::OpSConvert;
3494 BuildMI(BB&: *I.getParent(), I, MIMD: DL, MCID: TII.get(Opcode: ConvertOpcode))
3495 .addDef(RegNo: ResVReg)
3496 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3497 .addUse(RegNo: SignReg)
3498 .constrainAllUses(TII, TRI, RBI);
3499 }
3500
3501 return true;
3502}
3503
3504bool SPIRVInstructionSelector::selectWaveOpInst(Register ResVReg,
3505 SPIRVTypeInst ResType,
3506 MachineInstr &I,
3507 unsigned Opcode) const {
3508 MachineBasicBlock &BB = *I.getParent();
3509 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3510
3511 auto BMI = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
3512 .addDef(RegNo: ResVReg)
3513 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3514 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I,
3515 SpvType: IntTy, TII, ZeroAsNull: !STI.isShader()));
3516
3517 for (unsigned J = 2; J < I.getNumOperands(); J++) {
3518 BMI.addUse(RegNo: I.getOperand(i: J).getReg());
3519 }
3520
3521 BMI.constrainAllUses(TII, TRI, RBI);
3522 return true;
3523}
3524
3525bool SPIRVInstructionSelector::selectBarrierInst(MachineInstr &I,
3526 unsigned Scope,
3527 unsigned MemSem,
3528 bool WithGroupSync) const {
3529 auto BarrierType =
3530 WithGroupSync ? SPIRV::OpControlBarrier : SPIRV::OpMemoryBarrier;
3531
3532 MemSem |= SPIRV::MemorySemantics::AcquireRelease;
3533
3534 assert(((Scope != SPIRV::Scope::Workgroup) ||
3535 ((MemSem & SPIRV::MemorySemantics::WorkgroupMemory) > 0)) &&
3536 "Workgroup Scope must set WorkGroupMemory semantic "
3537 "in Barrier instruction");
3538
3539 assert(((Scope != SPIRV::Scope::Device) ||
3540 ((MemSem & SPIRV::MemorySemantics::UniformMemory) > 0 &&
3541 (MemSem & SPIRV::MemorySemantics::ImageMemory) > 0)) &&
3542 "Device Scope must set UniformMemory and ImageMemory semantic "
3543 "in Barrier instruction");
3544
3545 MachineBasicBlock &BB = *I.getParent();
3546 auto MI = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: BarrierType));
3547
3548 // OpControlBarrier needs to also set Execution Scope
3549 if (WithGroupSync) {
3550 Register ExecReg = buildI32Constant(Val: SPIRV::Scope::Workgroup, I);
3551 MI.addUse(RegNo: ExecReg);
3552 }
3553
3554 Register ScopeReg = buildI32Constant(Val: Scope, I);
3555 Register MemSemReg = buildI32Constant(Val: MemSem, I);
3556
3557 MI.addUse(RegNo: ScopeReg).addUse(RegNo: MemSemReg).constrainAllUses(TII, TRI, RBI);
3558 return true;
3559}
3560
3561bool SPIRVInstructionSelector::selectWaveActiveCountBits(
3562 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3563
3564 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3565 SPIRVTypeInst BallotType = GR.getOrCreateSPIRVVectorType(BaseType: IntTy, NumElements: 4, I, TII);
3566 Register BallotReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BallotType));
3567 if (!selectWaveOpInst(ResVReg: BallotReg, ResType: BallotType, I,
3568 Opcode: SPIRV::OpGroupNonUniformBallot))
3569 return false;
3570
3571 MachineBasicBlock &BB = *I.getParent();
3572 BuildMI(BB, I, MIMD: I.getDebugLoc(),
3573 MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformBallotBitCount))
3574 .addDef(RegNo: ResVReg)
3575 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3576 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3577 ZeroAsNull: !STI.isShader()))
3578 .addImm(Val: SPIRV::GroupOperation::Reduce)
3579 .addUse(RegNo: BallotReg)
3580 .constrainAllUses(TII, TRI, RBI);
3581
3582 return true;
3583}
3584
3585bool SPIRVInstructionSelector::selectWaveActiveAllEqual(Register ResVReg,
3586 SPIRVTypeInst ResType,
3587 MachineInstr &I) const {
3588 MachineBasicBlock &BB = *I.getParent();
3589 const DebugLoc &DL = I.getDebugLoc();
3590
3591 // Input to the intrinsic
3592 Register InputReg = I.getOperand(i: 2).getReg();
3593 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputReg);
3594
3595 // Determine if input is vector
3596 unsigned NumElems = GR.getScalarOrVectorComponentCount(Type: InputType);
3597 bool IsVector = NumElems > 1;
3598
3599 // Determine element types
3600 SPIRVTypeInst ElemInputType = GR.getScalarOrVectorComponentType(Type: InputType);
3601 SPIRVTypeInst ElemBoolType = GR.getScalarOrVectorComponentType(Type: ResType);
3602
3603 // Subgroup scope constant
3604 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3605 Register ScopeConst = GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy,
3606 TII, ZeroAsNull: !STI.isShader());
3607
3608 // Scalar case
3609 if (!IsVector) {
3610 return selectWaveOpInst(ResVReg, ResType: ElemBoolType, I,
3611 Opcode: SPIRV::OpGroupNonUniformAllEqual);
3612 }
3613
3614 // Vector case
3615 SmallVector<Register, 4> ElementResults;
3616 ElementResults.reserve(N: NumElems);
3617
3618 for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
3619 // Extract element
3620 Register ElemInput = InputReg;
3621 Register Extracted =
3622 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ElemInputType));
3623
3624 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
3625 .addDef(RegNo: Extracted)
3626 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ElemInputType))
3627 .addUse(RegNo: InputReg)
3628 .addImm(Val: Idx)
3629 .constrainAllUses(TII, TRI, RBI);
3630
3631 ElemInput = Extracted;
3632
3633 // Emit per-element AllEqual
3634 Register ElemResult =
3635 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ElemBoolType));
3636
3637 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformAllEqual))
3638 .addDef(RegNo: ElemResult)
3639 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ElemBoolType))
3640 .addUse(RegNo: ScopeConst)
3641 .addUse(RegNo: ElemInput)
3642 .constrainAllUses(TII, TRI, RBI);
3643
3644 ElementResults.push_back(Elt: ElemResult);
3645 }
3646
3647 // Reconstruct vector<bool>
3648 auto MIB = BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
3649 .addDef(RegNo: ResVReg)
3650 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
3651 for (Register R : ElementResults)
3652 MIB.addUse(RegNo: R);
3653
3654 MIB.constrainAllUses(TII, TRI, RBI);
3655
3656 return true;
3657}
3658
3659bool SPIRVInstructionSelector::selectWavePrefixBitCount(Register ResVReg,
3660 SPIRVTypeInst ResType,
3661 MachineInstr &I) const {
3662
3663 assert(I.getNumOperands() == 3);
3664
3665 auto Op = I.getOperand(i: 2);
3666 assert(Op.isReg());
3667
3668 MachineBasicBlock &BB = *I.getParent();
3669 DebugLoc DL = I.getDebugLoc();
3670
3671 Register InputRegister = Op.getReg();
3672 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3673
3674 if (!InputType)
3675 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3676
3677 if (InputType->getOpcode() != SPIRV::OpTypeBool)
3678 return diagnoseUnsupported(I, Msg: "WavePrefixBitCount requires boolean input");
3679
3680 // Types
3681 SPIRVTypeInst Int32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3682
3683 // Ballot result type: vector<uint32>
3684 // Match DXC: %v4uint for Subgroup size
3685 SPIRVTypeInst BallotTy = GR.getOrCreateSPIRVVectorType(BaseType: Int32Ty, NumElements: 4, I, TII);
3686
3687 // Create a vreg for the ballot result
3688 Register BallotVReg = MRI->createVirtualRegister(RegClass: &SPIRV::IDRegClass);
3689
3690 // 1. OpGroupNonUniformBallot
3691 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformBallot))
3692 .addDef(RegNo: BallotVReg)
3693 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: BallotTy))
3694 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: Int32Ty, TII))
3695 .addUse(RegNo: InputRegister)
3696 .constrainAllUses(TII, TRI, RBI);
3697
3698 // 2. OpGroupNonUniformBallotBitCount
3699 BuildMI(BB, I, MIMD: DL, MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformBallotBitCount))
3700 .addDef(RegNo: ResVReg)
3701 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3702 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: Int32Ty, TII))
3703 .addImm(Val: SPIRV::GroupOperation::ExclusiveScan)
3704 .addUse(RegNo: BallotVReg)
3705 .constrainAllUses(TII, TRI, RBI);
3706
3707 return true;
3708}
3709
3710bool SPIRVInstructionSelector::selectWaveReduceMax(Register ResVReg,
3711 SPIRVTypeInst ResType,
3712 MachineInstr &I,
3713 bool IsUnsigned) const {
3714 return selectWaveReduce(
3715 ResVReg, ResType, I, IsUnsigned,
3716 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3717 const bool IsFloatTy =
3718 GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3719 const auto IntOp = IsUnsigned ? SPIRV::OpGroupNonUniformUMax
3720 : SPIRV::OpGroupNonUniformSMax;
3721 return IsFloatTy ? SPIRV::OpGroupNonUniformFMax : IntOp;
3722 });
3723}
3724
3725bool SPIRVInstructionSelector::selectWaveReduceMin(Register ResVReg,
3726 SPIRVTypeInst ResType,
3727 MachineInstr &I,
3728 bool IsUnsigned) const {
3729 return selectWaveReduce(
3730 ResVReg, ResType, I, IsUnsigned,
3731 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3732 const bool IsFloatTy =
3733 GR.isScalarOrVectorOfType(VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3734 const auto IntOp = IsUnsigned ? SPIRV::OpGroupNonUniformUMin
3735 : SPIRV::OpGroupNonUniformSMin;
3736 return IsFloatTy ? SPIRV::OpGroupNonUniformFMin : IntOp;
3737 });
3738}
3739
3740bool SPIRVInstructionSelector::selectWaveReduceSum(Register ResVReg,
3741 SPIRVTypeInst ResType,
3742 MachineInstr &I) const {
3743 return selectWaveReduce(ResVReg, ResType, I, /*IsUnsigned*/ false,
3744 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3745 bool IsFloatTy = GR.isScalarOrVectorOfType(
3746 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3747 return IsFloatTy ? SPIRV::OpGroupNonUniformFAdd
3748 : SPIRV::OpGroupNonUniformIAdd;
3749 });
3750}
3751
3752bool SPIRVInstructionSelector::selectWaveReduceProduct(Register ResVReg,
3753 SPIRVTypeInst ResType,
3754 MachineInstr &I) const {
3755 return selectWaveReduce(ResVReg, ResType, I, /*IsUnsigned*/ false,
3756 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3757 bool IsFloatTy = GR.isScalarOrVectorOfType(
3758 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3759 return IsFloatTy ? SPIRV::OpGroupNonUniformFMul
3760 : SPIRV::OpGroupNonUniformIMul;
3761 });
3762}
3763
3764template <typename PickOpcodeFn>
3765bool SPIRVInstructionSelector::selectWaveReduce(
3766 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, bool IsUnsigned,
3767 PickOpcodeFn &&PickOpcode) const {
3768 assert(I.getNumOperands() == 3);
3769 assert(I.getOperand(2).isReg());
3770 MachineBasicBlock &BB = *I.getParent();
3771 Register InputRegister = I.getOperand(i: 2).getReg();
3772 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3773
3774 if (!InputType)
3775 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3776
3777 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3778 const unsigned Opcode = PickOpcode(InputRegister, IsUnsigned);
3779 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
3780 .addDef(RegNo: ResVReg)
3781 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3782 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3783 ZeroAsNull: !STI.isShader()))
3784 .addImm(Val: SPIRV::GroupOperation::Reduce)
3785 .addUse(RegNo: I.getOperand(i: 2).getReg())
3786 .constrainAllUses(TII, TRI, RBI);
3787 return true;
3788}
3789
3790bool SPIRVInstructionSelector::selectWaveReduceOp(Register ResVReg,
3791 SPIRVTypeInst ResType,
3792 MachineInstr &I,
3793 unsigned Opcode) const {
3794 return selectWaveReduce(
3795 ResVReg, ResType, I, IsUnsigned: false,
3796 PickOpcode: [&](Register InputRegister, bool IsUnsigned) { return Opcode; });
3797}
3798
3799bool SPIRVInstructionSelector::selectWaveExclusiveScanSum(
3800 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3801 return selectWaveExclusiveScan(ResVReg, ResType, I, /*IsUnsigned*/ false,
3802 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3803 bool IsFloatTy = GR.isScalarOrVectorOfType(
3804 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3805 return IsFloatTy
3806 ? SPIRV::OpGroupNonUniformFAdd
3807 : SPIRV::OpGroupNonUniformIAdd;
3808 });
3809}
3810
3811bool SPIRVInstructionSelector::selectWaveExclusiveScanProduct(
3812 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
3813 return selectWaveExclusiveScan(ResVReg, ResType, I, /*IsUnsigned*/ false,
3814 PickOpcode: [&](Register InputRegister, bool IsUnsigned) {
3815 bool IsFloatTy = GR.isScalarOrVectorOfType(
3816 VReg: InputRegister, TypeOpcode: SPIRV::OpTypeFloat);
3817 return IsFloatTy
3818 ? SPIRV::OpGroupNonUniformFMul
3819 : SPIRV::OpGroupNonUniformIMul;
3820 });
3821}
3822
3823template <typename PickOpcodeFn>
3824bool SPIRVInstructionSelector::selectWaveExclusiveScan(
3825 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, bool IsUnsigned,
3826 PickOpcodeFn &&PickOpcode) const {
3827 assert(I.getNumOperands() == 3);
3828 assert(I.getOperand(2).isReg());
3829 MachineBasicBlock &BB = *I.getParent();
3830 Register InputRegister = I.getOperand(i: 2).getReg();
3831 SPIRVTypeInst InputType = GR.getSPIRVTypeForVReg(VReg: InputRegister);
3832
3833 if (!InputType)
3834 return diagnoseUnsupported(I, Msg: "Input Type could not be determined.");
3835
3836 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3837 const unsigned Opcode = PickOpcode(InputRegister, IsUnsigned);
3838 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
3839 .addDef(RegNo: ResVReg)
3840 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3841 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3842 ZeroAsNull: !STI.isShader()))
3843 .addImm(Val: SPIRV::GroupOperation::ExclusiveScan)
3844 .addUse(RegNo: I.getOperand(i: 2).getReg())
3845 .constrainAllUses(TII, TRI, RBI);
3846 return true;
3847}
3848
3849bool SPIRVInstructionSelector::selectQuadSwap(Register ResVReg,
3850 SPIRVTypeInst ResType,
3851 MachineInstr &I,
3852 unsigned Direction) const {
3853 assert(I.getNumOperands() == 3);
3854 assert(I.getOperand(2).isReg());
3855 MachineBasicBlock &BB = *I.getParent();
3856 Register InputRegister = I.getOperand(i: 2).getReg();
3857
3858 SPIRVTypeInst IntTy = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3859 bool ZeroAsNull = !STI.isShader();
3860 Register DirectionReg =
3861 GR.getOrCreateConstInt(Val: Direction, I, SpvType: IntTy, TII, ZeroAsNull);
3862 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpGroupNonUniformQuadSwap))
3863 .addDef(RegNo: ResVReg)
3864 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
3865 .addUse(RegNo: GR.getOrCreateConstInt(Val: SPIRV::Scope::Subgroup, I, SpvType: IntTy, TII,
3866 ZeroAsNull))
3867 .addUse(RegNo: InputRegister)
3868 .addUse(RegNo: DirectionReg)
3869 .constrainAllUses(TII, TRI, RBI);
3870 return true;
3871}
3872
3873bool SPIRVInstructionSelector::selectBitreverseViaI32(Register ResVReg,
3874 SPIRVTypeInst ResType,
3875 MachineInstr &I,
3876 Register Op) const {
3877 SPIRVTypeInst Int32Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
3878 const unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
3879 Register ShiftConst =
3880 GR.getOrCreateConstInt(Val: 32 - BitWidth, I, SpvType: Int32Type, TII);
3881 unsigned ShiftOp = SPIRV::OpShiftRightLogicalS;
3882
3883 const unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
3884 const unsigned ExtendOpcode = GR.isScalarOrVectorSigned(Type: ResType)
3885 ? SPIRV::OpSConvert
3886 : SPIRV::OpUConvert;
3887
3888 if (N > 1) {
3889 Int32Type = GR.getOrCreateSPIRVVectorType(BaseType: Int32Type, NumElements: N, I, TII);
3890 ShiftOp = SPIRV::OpShiftRightLogicalV;
3891
3892 // Vector shifts require a composite constant
3893 const Register CompositeReg =
3894 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
3895 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
3896 MCID: TII.get(Opcode: SPIRV::OpConstantComposite))
3897 .addDef(RegNo: CompositeReg)
3898 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Int32Type));
3899 for (unsigned It = 0; It < N; ++It)
3900 MIB.addUse(RegNo: ShiftConst);
3901 MIB.constrainAllUses(TII, TRI, RBI);
3902
3903 ShiftConst = CompositeReg;
3904 }
3905
3906 // Converts the input to i32 (or vector of i32)
3907 Register ExtReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
3908 if (!selectOpWithSrcs(ResVReg: ExtReg, ResType: Int32Type, I, Srcs: {Op}, Opcode: ExtendOpcode))
3909 return false;
3910
3911 // Perform bitreverse on the i32 value
3912 Register BitrevReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
3913 if (!selectBitreverseNative(ResVReg: BitrevReg, ResType: Int32Type, I, Op: ExtReg))
3914 return false;
3915
3916 // Shift the bit-reversed value to get the final result.
3917 Register ShiftReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Int32Type));
3918 if (!selectOpWithSrcs(ResVReg: ShiftReg, ResType: Int32Type, I, Srcs: {BitrevReg, ShiftConst},
3919 Opcode: ShiftOp))
3920 return false;
3921
3922 // Finally, convert the result back.
3923 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {ShiftReg}, Opcode: ExtendOpcode);
3924}
3925
3926bool SPIRVInstructionSelector::handle64BitOverflow(
3927 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, Register SrcReg,
3928 unsigned int Opcode,
3929 std::function<bool(Register, SPIRVTypeInst, MachineInstr &, Register,
3930 unsigned)>
3931 CallbackFunction) const {
3932
3933 SPIRVTypeInst BaseType = GR.retrieveScalarOrVectorIntType(Type: ResType);
3934 assert(BaseType->getOpcode() == SPIRV::OpTypeInt &&
3935 "handle64BitOverflow should only be used for integer types");
3936 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: ResType);
3937 assert(ComponentCount < 5 && "Vec 5+ will generate invalid SPIR-V ops");
3938
3939 MachineIRBuilder MIRBuilder(I);
3940 SPIRVTypeInst I64Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 64, MIRBuilder);
3941 SPIRVTypeInst I64x2Type =
3942 GR.getOrCreateSPIRVVectorType(BaseType: I64Type, NumElements: 2, MIRBuilder, EmitIR: false);
3943 SPIRVTypeInst Vec2ResType =
3944 GR.getOrCreateSPIRVVectorType(BaseType, NumElements: 2, MIRBuilder, EmitIR: false);
3945
3946 std::vector<Register> PartialRegs;
3947
3948 unsigned CurrentComponent = 0;
3949 for (; CurrentComponent + 1 < ComponentCount; CurrentComponent += 2) {
3950 Register PopCountResult =
3951 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: I64x2Type));
3952
3953 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
3954 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
3955 .addDef(RegNo: PopCountResult)
3956 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: I64x2Type))
3957 .addUse(RegNo: SrcReg)
3958 .addUse(RegNo: SrcReg)
3959 .addImm(Val: CurrentComponent)
3960 .addImm(Val: CurrentComponent + 1);
3961
3962 MIB.constrainAllUses(TII, TRI, RBI);
3963
3964 Register SubVecReg =
3965 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Vec2ResType));
3966
3967 if (!CallbackFunction(SubVecReg, Vec2ResType, I, PopCountResult, Opcode))
3968 return false;
3969
3970 PartialRegs.push_back(x: SubVecReg);
3971 }
3972 // On odd component counts we need to handle one more component
3973 if (CurrentComponent != ComponentCount) {
3974 bool ZeroAsNull = !STI.isShader();
3975 Register FinalElemReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: I64Type));
3976 Register ConstIntLastIdx = GR.getOrCreateConstInt(
3977 Val: ComponentCount - 1, I, SpvType: BaseType, TII, ZeroAsNull);
3978
3979 if (!selectOpWithSrcs(ResVReg: FinalElemReg, ResType: I64Type, I, Srcs: {SrcReg, ConstIntLastIdx},
3980 Opcode: SPIRV::OpVectorExtractDynamic))
3981 return false;
3982
3983 Register FinalElemResReg =
3984 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BaseType));
3985
3986 if (!CallbackFunction(FinalElemResReg, BaseType, I, FinalElemReg, Opcode))
3987 return false;
3988
3989 PartialRegs.push_back(x: FinalElemResReg);
3990 }
3991 // Join all the resulting registers back into the return type in order
3992 // (ie i32x2, i32x2, i32x1 -> i32x5)
3993 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: PartialRegs,
3994 Opcode: SPIRV::OpCompositeConstruct);
3995}
3996
3997bool SPIRVInstructionSelector::selectBitreverse64(Register ResVReg,
3998 SPIRVTypeInst ResType,
3999 MachineInstr &I,
4000 Register SrcReg) const {
4001 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: ResType);
4002 if (ComponentCount > 2)
4003 return handle64BitOverflow(
4004 ResVReg, ResType, I, SrcReg, Opcode: SPIRV::OpBitReverse,
4005 CallbackFunction: [this](Register R, SPIRVTypeInst T, MachineInstr &I, Register S,
4006 unsigned O) { return this->selectBitreverse64(ResVReg: R, ResType: T, I, SrcReg: S); });
4007
4008 MachineIRBuilder MIRBuilder(I);
4009
4010 SPIRVTypeInst I32Type = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, MIRBuilder);
4011 SPIRVTypeInst VecI32Type = GR.getOrCreateSPIRVVectorType(
4012 BaseType: I32Type, NumElements: 2 * ComponentCount, MIRBuilder, /*IsSigned=*/EmitIR: false);
4013
4014 // Converts 64 bit into and array of 32 bit, containing 2 elements.
4015 Register Vec32 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: VecI32Type));
4016 if (!selectOpWithSrcs(ResVReg: Vec32, ResType: VecI32Type, I, Srcs: {SrcReg}, Opcode: SPIRV::OpBitcast))
4017 return false;
4018
4019 // Apply bitreverse on each 32 bit lane
4020 Register Reverse32 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: VecI32Type));
4021 if (!selectBitreverseNative(ResVReg: Reverse32, ResType: VecI32Type, I, Op: Vec32))
4022 return false;
4023
4024 // Reversing a 64-bit value = reverse each 32-bit half AND swap them,
4025 // so the old High word becomes lane 0 (low) and old Low becomes lane 1
4026 // (high).
4027 Register SwappedVec = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: VecI32Type));
4028 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4029 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
4030 .addDef(RegNo: SwappedVec)
4031 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: VecI32Type))
4032 .addUse(RegNo: Reverse32)
4033 .addUse(RegNo: Reverse32);
4034 for (unsigned J = 0; J < ComponentCount; ++J) {
4035 MIB.addImm(Val: 2 * J + 1);
4036 MIB.addImm(Val: 2 * J);
4037 }
4038 MIB.constrainAllUses(TII, TRI, RBI);
4039
4040 // Groups 32 bit vector back to 64 bit scalar.
4041 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {SwappedVec}, Opcode: SPIRV::OpBitcast);
4042}
4043
4044bool SPIRVInstructionSelector::selectBitreverseNative(Register ResVReg,
4045 SPIRVTypeInst ResType,
4046 MachineInstr &I,
4047 Register Op) const {
4048 MachineBasicBlock &BB = *I.getParent();
4049 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBitReverse))
4050 .addDef(RegNo: ResVReg)
4051 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4052 .addUse(RegNo: Op)
4053 .constrainAllUses(TII, TRI, RBI);
4054 return true;
4055}
4056
4057bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg,
4058 SPIRVTypeInst ResType,
4059 MachineInstr &I) const {
4060 Register OpReg = I.getOperand(i: 1).getReg();
4061
4062 // TODO: Fix shader behavior in case of VK_KHR_maintenance9 extension is set
4063 if (STI.isShader()) {
4064 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
4065 switch (GR.getScalarOrVectorBitWidth(Type: OpType)) {
4066 case 8:
4067 case 16:
4068 case 24:
4069 return selectBitreverseViaI32(ResVReg, ResType, I, Op: OpReg);
4070 case 32:
4071 return selectBitreverseNative(ResVReg, ResType, I, Op: OpReg);
4072 case 64:
4073 return selectBitreverse64(ResVReg, ResType, I, SrcReg: OpReg);
4074 }
4075 return SPIRVInstructionSelector::diagnoseUnsupported(
4076 I, Msg: "G_BITREVERSE only support 16,32,64 bits.");
4077 }
4078
4079 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_bit_instructions))
4080 return selectBitreverseNative(ResVReg, ResType, I, Op: OpReg);
4081
4082 // Expansion bitreverse using bit manipulation operations
4083 // Algo: https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
4084 const unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
4085 // TODO: add support for any bit width and bitwidth more than 64.
4086 if (BitWidth > 64 || !isPowerOf2_32(Value: BitWidth))
4087 return false;
4088
4089 const unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
4090
4091 unsigned AndOp = SPIRV::OpBitwiseAndS;
4092 unsigned OrOp = SPIRV::OpBitwiseOrS;
4093 unsigned ShlOp = SPIRV::OpShiftLeftLogicalS;
4094 unsigned ShrOp = SPIRV::OpShiftRightLogicalS;
4095 if (N > 1) {
4096 AndOp = SPIRV::OpBitwiseAndV;
4097 OrOp = SPIRV::OpBitwiseOrV;
4098 ShlOp = SPIRV::OpShiftLeftLogicalV;
4099 ShrOp = SPIRV::OpShiftRightLogicalV;
4100 }
4101
4102 // Helper, one swap per step: ((input>>shift)&mask)|((input&mask)<<shift),
4103 // RPN: input shift >> mask & input mask & shift << |
4104 auto SwapBits = [&](const Register Input, const uint64_t Mask,
4105 const unsigned Shift) -> Register {
4106 auto CreateConst = [&](const uint64_t Value) -> Register {
4107 if (N == 1)
4108 return GR.getOrCreateConstInt(
4109 Val: Value, I, SpvType: GR.retrieveScalarOrVectorIntType(Type: ResType), TII);
4110 return GR.getOrCreateConstVector(Val: Value, I, SpvType: ResType, TII);
4111 };
4112
4113 Register MaskReg = CreateConst(Mask);
4114 Register ShiftReg = CreateConst(Shift);
4115 Register T1 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4116 Register T2 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4117 Register T3 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4118 Register T4 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4119 Register Result = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4120
4121 if (!selectOpWithSrcs(ResVReg: T1, ResType, I, Srcs: {Input, ShiftReg}, Opcode: ShrOp) ||
4122 !selectOpWithSrcs(ResVReg: T2, ResType, I, Srcs: {T1, MaskReg}, Opcode: AndOp) ||
4123 !selectOpWithSrcs(ResVReg: T3, ResType, I, Srcs: {Input, MaskReg}, Opcode: AndOp) ||
4124 !selectOpWithSrcs(ResVReg: T4, ResType, I, Srcs: {T3, ShiftReg}, Opcode: ShlOp) ||
4125 !selectOpWithSrcs(ResVReg: Result, ResType, I, Srcs: {T2, T4}, Opcode: OrOp))
4126 return Register();
4127
4128 return Result;
4129 };
4130
4131 unsigned Shift = BitWidth;
4132 Register Result = OpReg;
4133 uint64_t Mask = ~0ull;
4134 while ((Shift >>= 1) > 0) {
4135 Mask ^= (Mask << Shift);
4136 Result = SwapBits(Result, Mask, Shift);
4137 if (!Result.isValid())
4138 return false;
4139 }
4140
4141 return BuildCOPY(DestReg: ResVReg, SrcReg: Result, I);
4142}
4143
4144bool SPIRVInstructionSelector::selectFreeze(Register ResVReg,
4145 SPIRVTypeInst ResType,
4146 MachineInstr &I) const {
4147 assert(I.getOperand(0).isReg() && I.getOperand(1).isReg() &&
4148 "G_FREEZE must define and use a register");
4149 Register OpReg = I.getOperand(i: 1).getReg();
4150
4151 // With SPV_KHR_poison_freeze, lower `freeze` to OpFreezeKHR.
4152 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_poison_freeze)) {
4153 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFreezeKHR))
4154 .addDef(RegNo: ResVReg)
4155 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4156 .addUse(RegNo: OpReg)
4157 .constrainAllUses(TII, TRI, RBI);
4158 return true;
4159 }
4160
4161 // There is no way to implement `freeze` correctly without support on SPIR-V
4162 // standard side, but we may at least address a simple (static) case when
4163 // undef/poison value presence is obvious. The main benefit of even
4164 // incomplete `freeze` support is preventing of translation from crashing due
4165 // to lack of support on legalization and instruction selection steps.
4166 if (MachineInstr *Def = MRI->getVRegDef(Reg: OpReg)) {
4167 if (Def->getOpcode() == TargetOpcode::COPY)
4168 Def = MRI->getVRegDef(Reg: Def->getOperand(i: 1).getReg());
4169 Register Reg;
4170 switch (Def->getOpcode()) {
4171 case SPIRV::ASSIGN_TYPE:
4172 if (MachineInstr *AssignToDef =
4173 MRI->getVRegDef(Reg: Def->getOperand(i: 1).getReg())) {
4174 if (AssignToDef->getOpcode() == TargetOpcode::G_IMPLICIT_DEF)
4175 Reg = Def->getOperand(i: 2).getReg();
4176 }
4177 break;
4178 case SPIRV::OpUndef:
4179 Reg = Def->getOperand(i: 1).getReg();
4180 break;
4181 }
4182 unsigned DestOpCode;
4183 if (Reg.isValid()) {
4184 DestOpCode = SPIRV::OpConstantNull;
4185 LLVM_DEBUG(dbgs() << "SPV_KHR_poison_freeze is not enabled. freeze of a "
4186 "static undef/poison lowered to OpConstantNull\n");
4187 } else {
4188 DestOpCode = TargetOpcode::COPY;
4189 Reg = OpReg;
4190 LLVM_DEBUG(dbgs() << "SPV_KHR_poison_freeze is not enabled. freeze "
4191 "skipped, lowered as a copy of the operand\n");
4192 }
4193 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DestOpCode))
4194 .addDef(RegNo: I.getOperand(i: 0).getReg())
4195 .addUse(RegNo: Reg)
4196 .constrainAllUses(TII, TRI, RBI);
4197 return true;
4198 }
4199 return false;
4200}
4201
4202bool SPIRVInstructionSelector::selectBuildVector(Register ResVReg,
4203 SPIRVTypeInst ResType,
4204 MachineInstr &I) const {
4205 unsigned N = 0;
4206 if (ResType->getOpcode() == SPIRV::OpTypeVector)
4207 N = GR.getScalarOrVectorComponentCount(Type: ResType);
4208 else if (ResType->getOpcode() == SPIRV::OpTypeArray)
4209 N = getArrayComponentCount(MRI, ResType);
4210 else
4211 report_fatal_error(reason: "Cannot select G_BUILD_VECTOR with a non-vector result");
4212 if (I.getNumExplicitOperands() - I.getNumExplicitDefs() != N)
4213 report_fatal_error(reason: "G_BUILD_VECTOR and the result type are inconsistent");
4214
4215 // check if we may construct a constant vector
4216 bool IsConst = true;
4217 for (unsigned i = I.getNumExplicitDefs();
4218 i < I.getNumExplicitOperands() && IsConst; ++i)
4219 if (!isConstReg(MRI, OpReg: I.getOperand(i).getReg()))
4220 IsConst = false;
4221
4222 if (!IsConst && N < 2)
4223 return diagnoseUnsupported(
4224 I, Msg: "There must be at least two constituent operands in a vector");
4225
4226 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
4227
4228 bool IsNullVector = IsConst && !STI.isShader();
4229 for (unsigned i = I.getNumExplicitDefs();
4230 i < I.getNumExplicitOperands() && IsNullVector; ++i) {
4231 MachineInstr *Def = getDef(MO: I.getOperand(i), MRI);
4232 IsNullVector = Def && isNullOrNullSplat(MI: *Def, MRI: *MRI);
4233 }
4234
4235 if (IsNullVector) {
4236 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
4237 .addDef(RegNo: ResVReg)
4238 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4239 .constrainAllUses(TII, TRI, RBI);
4240 return true;
4241 }
4242
4243 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4244 MCID: TII.get(Opcode: IsConst ? SPIRV::OpConstantComposite
4245 : SPIRV::OpCompositeConstruct))
4246 .addDef(RegNo: ResVReg)
4247 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
4248 for (unsigned i = I.getNumExplicitDefs(); i < I.getNumExplicitOperands(); ++i)
4249 MIB.addUse(RegNo: I.getOperand(i).getReg());
4250 MIB.constrainAllUses(TII, TRI, RBI);
4251 return true;
4252}
4253
4254bool SPIRVInstructionSelector::selectSplatVector(Register ResVReg,
4255 SPIRVTypeInst ResType,
4256 MachineInstr &I) const {
4257 unsigned N = 0;
4258 if (ResType->getOpcode() == SPIRV::OpTypeVector)
4259 N = GR.getScalarOrVectorComponentCount(Type: ResType);
4260 else if (ResType->getOpcode() == SPIRV::OpTypeArray)
4261 N = getArrayComponentCount(MRI, ResType);
4262 else
4263 report_fatal_error(reason: "Cannot select G_SPLAT_VECTOR with a non-vector result");
4264
4265 unsigned OpIdx = I.getNumExplicitDefs();
4266 if (!I.getOperand(i: OpIdx).isReg())
4267 report_fatal_error(reason: "Unexpected argument in G_SPLAT_VECTOR");
4268
4269 // check if we may construct a constant vector
4270 Register OpReg = I.getOperand(i: OpIdx).getReg();
4271 bool IsConst = isConstReg(MRI, OpReg);
4272
4273 if (!IsConst && N < 2)
4274 return diagnoseUnsupported(
4275 I, Msg: "There must be at least two constituent operands in a vector");
4276
4277 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
4278 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4279 MCID: TII.get(Opcode: IsConst ? SPIRV::OpConstantComposite
4280 : SPIRV::OpCompositeConstruct))
4281 .addDef(RegNo: ResVReg)
4282 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
4283 for (unsigned i = 0; i < N; ++i)
4284 MIB.addUse(RegNo: OpReg);
4285 MIB.constrainAllUses(TII, TRI, RBI);
4286 return true;
4287}
4288
4289bool SPIRVInstructionSelector::selectConcatVectors(Register ResVReg,
4290 SPIRVTypeInst ResType,
4291 MachineInstr &I) const {
4292 // Implement G_CONCAT_VECTORS using OpCompositeConstruct, which allows vector
4293 // constituents that share the result's component type to be
4294 // concatenated in operand order.
4295 if (ResType->getOpcode() != SPIRV::OpTypeVector)
4296 report_fatal_error(
4297 reason: "Cannot select G_CONCAT_VECTORS with a non-vector result");
4298
4299 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4300 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
4301 .addDef(RegNo: ResVReg)
4302 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
4303 for (unsigned OpIdx = I.getNumExplicitDefs();
4304 OpIdx < I.getNumExplicitOperands(); ++OpIdx)
4305 MIB.addUse(RegNo: I.getOperand(i: OpIdx).getReg());
4306 MIB.constrainAllUses(TII, TRI, RBI);
4307 return true;
4308}
4309
4310bool SPIRVInstructionSelector::selectDiscard(Register ResVReg,
4311 SPIRVTypeInst ResType,
4312 MachineInstr &I) const {
4313
4314 unsigned Opcode;
4315
4316 if (STI.canUseExtension(
4317 E: SPIRV::Extension::SPV_EXT_demote_to_helper_invocation) ||
4318 STI.isAtLeastSPIRVVer(VerToCompareTo: llvm::VersionTuple(1, 6))) {
4319 Opcode = SPIRV::OpDemoteToHelperInvocation;
4320 } else {
4321 Opcode = SPIRV::OpKill;
4322 // OpKill must be the last operation of any basic block.
4323 if (MachineInstr *NextI = I.getNextNode()) {
4324 GR.invalidateMachineInstr(MI: NextI);
4325 NextI->eraseFromParent();
4326 }
4327 }
4328
4329 MachineBasicBlock &BB = *I.getParent();
4330 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
4331 .constrainAllUses(TII, TRI, RBI);
4332 return true;
4333}
4334
4335bool SPIRVInstructionSelector::selectCmp(Register ResVReg,
4336 SPIRVTypeInst ResType, unsigned CmpOpc,
4337 MachineInstr &I) const {
4338 Register Cmp0 = I.getOperand(i: 2).getReg();
4339 Register Cmp1 = I.getOperand(i: 3).getReg();
4340 assert(GR.getSPIRVTypeForVReg(Cmp0)->getOpcode() ==
4341 GR.getSPIRVTypeForVReg(Cmp1)->getOpcode() &&
4342 "CMP operands should have the same type");
4343 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: CmpOpc))
4344 .addDef(RegNo: ResVReg)
4345 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4346 .addUse(RegNo: Cmp0)
4347 .addUse(RegNo: Cmp1)
4348 .setMIFlags(I.getFlags())
4349 .constrainAllUses(TII, TRI, RBI);
4350 return true;
4351}
4352
4353bool SPIRVInstructionSelector::selectICmp(Register ResVReg,
4354 SPIRVTypeInst ResType,
4355 MachineInstr &I) const {
4356 auto Pred = I.getOperand(i: 1).getPredicate();
4357 unsigned CmpOpc;
4358
4359 Register CmpOperand = I.getOperand(i: 2).getReg();
4360 if (GR.isScalarOfType(VReg: CmpOperand, TypeOpcode: SPIRV::OpTypePointer)) {
4361 CmpOpc = getPtrCmpOpcode(Pred);
4362 // OpPtrEqual/OpPtrNotEqual require both operands to share an identical
4363 // pointer type. If they are not OpBitcast is inserted.
4364 Register Op1 = I.getOperand(i: 3).getReg();
4365 SPIRVTypeInst Ty0 = GR.getSPIRVTypeForVReg(VReg: CmpOperand);
4366 if (Ty0 != GR.getSPIRVTypeForVReg(VReg: Op1)) {
4367 Register NewOp1 = createVirtualRegister(SpvType: Ty0, GR: &GR, MRI, MF: MRI->getMF());
4368 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBitcast))
4369 .addDef(RegNo: NewOp1)
4370 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Ty0))
4371 .addUse(RegNo: Op1)
4372 .constrainAllUses(TII, TRI, RBI);
4373 I.getOperand(i: 3).setReg(NewOp1);
4374 }
4375 } else if (GR.isScalarOrVectorOfType(VReg: CmpOperand, TypeOpcode: SPIRV::OpTypeBool))
4376 CmpOpc = getBoolCmpOpcode(PredNum: Pred);
4377 else
4378 CmpOpc = getICmpOpcode(PredNum: Pred);
4379 return selectCmp(ResVReg, ResType, CmpOpc, I);
4380}
4381
4382Register
4383SPIRVInstructionSelector::buildI32Constant(uint32_t Val, MachineInstr &I,
4384 SPIRVTypeInst ResType) const {
4385 Type *LLVMTy = IntegerType::get(C&: GR.CurMF->getFunction().getContext(), NumBits: 32);
4386 SPIRVTypeInst SpvI32Ty =
4387 ResType ? ResType : GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
4388 // Find a constant in DT or build a new one.
4389 auto ConstInt = ConstantInt::get(Ty: LLVMTy, V: Val);
4390 Register NewReg = GR.find(V: ConstInt, MF: GR.CurMF);
4391 if (!NewReg.isValid()) {
4392 NewReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
4393 MachineBasicBlock &BB = *I.getParent();
4394 MachineInstr *MI =
4395 Val == 0
4396 ? BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
4397 .addDef(RegNo: NewReg)
4398 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvI32Ty))
4399 : BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantI))
4400 .addDef(RegNo: NewReg)
4401 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SpvI32Ty))
4402 .addImm(Val: APInt(32, Val).getZExtValue());
4403 constrainSelectedInstRegOperands(I&: *MI, TII, TRI, RBI);
4404 GR.add(V: ConstInt, MI);
4405 }
4406 return NewReg;
4407}
4408
4409// Like buildI32Constant, but always inserts the constant definition in the
4410// entry block so it dominates all uses regardless of block ordering.
4411Register SPIRVInstructionSelector::buildI32ConstantInEntryBlock(
4412 uint32_t Val, MachineInstr &I, SPIRVTypeInst ResType) const {
4413 Type *LLVMTy = IntegerType::get(C&: GR.CurMF->getFunction().getContext(), NumBits: 32);
4414 SPIRVTypeInst SpvI32Ty =
4415 ResType ? ResType : GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
4416 auto *ConstInt = ConstantInt::get(Ty: LLVMTy, V: Val);
4417 Register NewReg = GR.find(V: ConstInt, MF: GR.CurMF);
4418 if (!NewReg.isValid()) {
4419 NewReg = MRI->createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 64));
4420 auto InsertIt = getOpVariableMBBIt(MF&: *I.getMF());
4421 MachineBasicBlock &EntryBB = *InsertIt->getParent();
4422 MachineInstr *MI = nullptr;
4423 Register TypeReg = GR.getSPIRVTypeID(SpirvType: SpvI32Ty);
4424 DebugLoc DbgLoc = I.getDebugLoc();
4425 if (Val == 0) {
4426 MI = BuildMI(BB&: EntryBB, I: InsertIt, MIMD: DbgLoc, MCID: TII.get(Opcode: SPIRV::OpConstantNull))
4427 .addDef(RegNo: NewReg)
4428 .addUse(RegNo: TypeReg);
4429 } else {
4430 uint64_t ImmVal = APInt(32, Val).getZExtValue();
4431 MI = BuildMI(BB&: EntryBB, I: InsertIt, MIMD: DbgLoc, MCID: TII.get(Opcode: SPIRV::OpConstantI))
4432 .addDef(RegNo: NewReg)
4433 .addUse(RegNo: TypeReg)
4434 .addImm(Val: ImmVal);
4435 }
4436 constrainSelectedInstRegOperands(I&: *MI, TII, TRI, RBI);
4437 GR.add(V: ConstInt, MI);
4438 }
4439 return NewReg;
4440}
4441
4442bool SPIRVInstructionSelector::selectFCmp(Register ResVReg,
4443 SPIRVTypeInst ResType,
4444 MachineInstr &I) const {
4445 unsigned CmpOp = getFCmpOpcode(PredNum: I.getOperand(i: 1).getPredicate());
4446 return selectCmp(ResVReg, ResType, CmpOpc: CmpOp, I);
4447}
4448
4449bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
4450 SPIRVTypeInst ResType,
4451 MachineInstr &I) const {
4452 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
4453 return selectExtInst(ResVReg, ResType, I, CLInst: CL::exp10);
4454 }
4455
4456 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::GLSL_std_450)) {
4457 /// There is no exp10 in GLSL. Use exp10(x) = exp2(x * log2(10)) instead
4458 /// log2(10) ~= 3.3219280948874l
4459
4460 if (ResType->getOpcode() != SPIRV::OpTypeVector &&
4461 ResType->getOpcode() != SPIRV::OpTypeFloat)
4462 return false;
4463
4464 MachineIRBuilder MIRBuilder(I);
4465
4466 SPIRVTypeInst SpirvScalarType = GR.getScalarOrVectorComponentType(Type: ResType);
4467
4468 // Match the literal precision to the scalar type so the OpConstant
4469 // literal does not contain non-zero high-order bits that would fail
4470 // SPIR-V validation when the type is narrower than 32 bits (e.g. half).
4471 APFloat ConstVal(3.3219280948873623);
4472 bool LosesInfo;
4473 ConstVal.convert(
4474 ToSemantics: getZeroFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: SpirvScalarType)).getSemantics(),
4475 RM: APFloat::rmNearestTiesToEven, losesInfo: &LosesInfo);
4476 Register ConstReg =
4477 GR.buildConstantFP(Val: ConstVal, MIRBuilder, SpvType: SpirvScalarType);
4478 Register ArgReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4479 auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector
4480 ? SPIRV::OpVectorTimesScalar
4481 : SPIRV::OpFMulS;
4482
4483 if (!selectOpWithSrcs(ResVReg: ArgReg, ResType, I,
4484 Srcs: {I.getOperand(i: 1).getReg(), ConstReg}, Opcode))
4485 return false;
4486 if (!selectExtInst(ResVReg, ResType, I,
4487 Insts: {{SPIRV::InstructionSet::GLSL_std_450, GL::Exp2}}, setMIFlags: false,
4488 useMISrc: false, SrcRegs: {ArgReg}))
4489 return false;
4490
4491 return true;
4492 }
4493
4494 return false;
4495}
4496
4497Register SPIRVInstructionSelector::buildZerosVal(SPIRVTypeInst ResType,
4498 MachineInstr &I) const {
4499 // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
4500 bool ZeroAsNull = !STI.isShader();
4501 if (ResType->getOpcode() == SPIRV::OpTypeVector)
4502 return GR.getOrCreateConstVector(Val: 0UL, I, SpvType: ResType, TII, ZeroAsNull);
4503 return GR.getOrCreateConstInt(Val: 0, I, SpvType: ResType, TII, ZeroAsNull);
4504}
4505
4506bool SPIRVInstructionSelector::isScalarOrVectorIntConstantZero(
4507 Register Reg) const {
4508 SPIRVTypeInst Type = GR.getSPIRVTypeForVReg(VReg: Reg);
4509 if (!Type)
4510 return false;
4511 SPIRVTypeInst CompType = GR.getScalarOrVectorComponentType(Type);
4512 if (!CompType || CompType->getOpcode() != SPIRV::OpTypeInt)
4513 return false;
4514
4515 auto IsZero = [this](Register Reg) {
4516 MachineInstr *Def = getDefInstrMaybeConstant(ConstReg&: Reg, MRI);
4517 if (!Def)
4518 return false;
4519
4520 if (Def->getOpcode() == SPIRV::OpConstantNull)
4521 return true;
4522
4523 if (Def->getOpcode() == TargetOpcode::G_CONSTANT ||
4524 Def->getOpcode() == SPIRV::OpConstantI)
4525 return getIConstVal(ConstReg: Reg, MRI) == 0;
4526
4527 return false;
4528 };
4529
4530 if (IsZero(Reg))
4531 return true;
4532
4533 MachineInstr *Def = MRI->getVRegDef(Reg);
4534 if (!Def)
4535 return false;
4536
4537 if (Def->getOpcode() == TargetOpcode::G_BUILD_VECTOR ||
4538 (Def->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
4539 cast<GIntrinsic>(Val: Def)->getIntrinsicID() ==
4540 Intrinsic::spv_const_composite)) {
4541 unsigned StartOp = Def->getOpcode() == TargetOpcode::G_BUILD_VECTOR ? 1 : 2;
4542 for (unsigned i = StartOp; i < Def->getNumOperands(); ++i) {
4543 if (!IsZero(Def->getOperand(i).getReg()))
4544 return false;
4545 }
4546 return true;
4547 }
4548
4549 return false;
4550}
4551
4552Register SPIRVInstructionSelector::buildZerosValF(SPIRVTypeInst ResType,
4553 MachineInstr &I) const {
4554 // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
4555 bool ZeroAsNull = !STI.isShader();
4556 APFloat VZero = getZeroFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: ResType));
4557 if (ResType->getOpcode() == SPIRV::OpTypeVector)
4558 return GR.getOrCreateConstVector(Val: VZero, I, SpvType: ResType, TII, ZeroAsNull);
4559 return GR.getOrCreateConstFP(Val: VZero, I, SpvType: ResType, TII, ZeroAsNull);
4560}
4561
4562Register SPIRVInstructionSelector::buildOnesValF(SPIRVTypeInst ResType,
4563 MachineInstr &I) const {
4564 // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
4565 bool ZeroAsNull = !STI.isShader();
4566 APFloat VOne = getOneFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: ResType));
4567 if (ResType->getOpcode() == SPIRV::OpTypeVector)
4568 return GR.getOrCreateConstVector(Val: VOne, I, SpvType: ResType, TII, ZeroAsNull);
4569 return GR.getOrCreateConstFP(Val: VOne, I, SpvType: ResType, TII, ZeroAsNull);
4570}
4571
4572Register SPIRVInstructionSelector::buildOnesVal(bool AllOnes,
4573 SPIRVTypeInst ResType,
4574 MachineInstr &I) const {
4575 unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
4576 APInt One =
4577 AllOnes ? APInt::getAllOnes(numBits: BitWidth) : APInt::getOneBitSet(numBits: BitWidth, BitNo: 0);
4578 if (ResType->getOpcode() == SPIRV::OpTypeVector)
4579 return GR.getOrCreateConstVector(Val: One, I, SpvType: ResType, TII);
4580 return GR.getOrCreateConstInt(Val: One, I, SpvType: ResType, TII);
4581}
4582
4583bool SPIRVInstructionSelector::selectSelect(Register ResVReg,
4584 SPIRVTypeInst ResType,
4585 MachineInstr &I) const {
4586 Register SelectFirstArg = I.getOperand(i: 2).getReg();
4587 Register SelectSecondArg = I.getOperand(i: 3).getReg();
4588 assert(ResType == GR.getSPIRVTypeForVReg(SelectFirstArg) &&
4589 ResType == GR.getSPIRVTypeForVReg(SelectSecondArg));
4590
4591 bool IsFloatTy =
4592 GR.isScalarOrVectorOfType(VReg: SelectFirstArg, TypeOpcode: SPIRV::OpTypeFloat);
4593 bool IsPtrTy =
4594 GR.isScalarOrVectorOfType(VReg: SelectFirstArg, TypeOpcode: SPIRV::OpTypePointer);
4595 bool IsVectorTy = GR.getSPIRVTypeForVReg(VReg: SelectFirstArg)->getOpcode() ==
4596 SPIRV::OpTypeVector;
4597
4598 bool IsScalarBool =
4599 GR.isScalarOfType(VReg: I.getOperand(i: 1).getReg(), TypeOpcode: SPIRV::OpTypeBool);
4600 unsigned Opcode;
4601 if (IsVectorTy) {
4602 if (IsFloatTy) {
4603 Opcode = IsScalarBool ? SPIRV::OpSelectVFSCond : SPIRV::OpSelectVFVCond;
4604 } else if (IsPtrTy) {
4605 Opcode = IsScalarBool ? SPIRV::OpSelectVPSCond : SPIRV::OpSelectVPVCond;
4606 } else {
4607 Opcode = IsScalarBool ? SPIRV::OpSelectVISCond : SPIRV::OpSelectVIVCond;
4608 }
4609 } else {
4610 assert(IsScalarBool && "OpSelect with a scalar result requires a scalar "
4611 "boolean condition");
4612 if (IsFloatTy) {
4613 Opcode = SPIRV::OpSelectSFSCond;
4614 } else if (IsPtrTy) {
4615 Opcode = SPIRV::OpSelectSPSCond;
4616 } else {
4617 Opcode = SPIRV::OpSelectSISCond;
4618 }
4619 }
4620 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
4621 .addDef(RegNo: ResVReg)
4622 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4623 .addUse(RegNo: I.getOperand(i: 1).getReg())
4624 .addUse(RegNo: SelectFirstArg)
4625 .addUse(RegNo: SelectSecondArg)
4626 .constrainAllUses(TII, TRI, RBI);
4627 return true;
4628}
4629
4630// This function is used to extend a bool or a vector of bools into an integer
4631// or vector of integers.
4632bool SPIRVInstructionSelector::selectBoolToInt(Register ResVReg,
4633 SPIRVTypeInst ResType,
4634 Register BooleanVReg,
4635 MachineInstr &InsertAt,
4636 bool IsSigned) const {
4637 // To extend a bool, we need to use OpSelect between constants.
4638 Register ZeroReg = buildZerosVal(ResType, I&: InsertAt);
4639 Register OneReg = buildOnesVal(AllOnes: IsSigned, ResType, I&: InsertAt);
4640 bool IsScalarBool = GR.isScalarOfType(VReg: BooleanVReg, TypeOpcode: SPIRV::OpTypeBool);
4641 unsigned Opcode =
4642 IsScalarBool ? SPIRV::OpSelectSISCond : SPIRV::OpSelectVIVCond;
4643 BuildMI(BB&: *InsertAt.getParent(), I&: InsertAt, MIMD: InsertAt.getDebugLoc(),
4644 MCID: TII.get(Opcode))
4645 .addDef(RegNo: ResVReg)
4646 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4647 .addUse(RegNo: BooleanVReg)
4648 .addUse(RegNo: OneReg)
4649 .addUse(RegNo: ZeroReg)
4650 .constrainAllUses(TII, TRI, RBI);
4651 return true;
4652}
4653
4654bool SPIRVInstructionSelector::selectIToF(Register ResVReg,
4655 SPIRVTypeInst ResType,
4656 MachineInstr &I, bool IsSigned,
4657 unsigned Opcode) const {
4658 Register SrcReg = I.getOperand(i: 1).getReg();
4659 // We can convert bool value directly to float type without OpConvert*ToF,
4660 // however the translator generates OpSelect+OpConvert*ToF, so we do the same.
4661 if (GR.isScalarOrVectorOfType(VReg: I.getOperand(i: 1).getReg(), TypeOpcode: SPIRV::OpTypeBool)) {
4662 unsigned BitWidth = GR.getScalarOrVectorBitWidth(Type: ResType);
4663 SPIRVTypeInst TmpType = GR.getOrCreateSPIRVIntegerType(BitWidth, I, TII);
4664 if (ResType->getOpcode() == SPIRV::OpTypeVector) {
4665 const unsigned NumElts = GR.getScalarOrVectorComponentCount(Type: ResType);
4666 TmpType = GR.getOrCreateSPIRVVectorType(BaseType: TmpType, NumElements: NumElts, I, TII);
4667 }
4668 SrcReg = createVirtualRegister(SpvType: TmpType, GR: &GR, MRI, MF: MRI->getMF());
4669 selectBoolToInt(ResVReg: SrcReg, ResType: TmpType, BooleanVReg: I.getOperand(i: 1).getReg(), InsertAt&: I, IsSigned);
4670 }
4671 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {SrcReg}, Opcode);
4672}
4673
4674bool SPIRVInstructionSelector::selectExt(Register ResVReg,
4675 SPIRVTypeInst ResType, MachineInstr &I,
4676 bool IsSigned) const {
4677 Register SrcReg = I.getOperand(i: 1).getReg();
4678 if (GR.isScalarOrVectorOfType(VReg: SrcReg, TypeOpcode: SPIRV::OpTypeBool))
4679 return selectBoolToInt(ResVReg, ResType, BooleanVReg: I.getOperand(i: 1).getReg(), InsertAt&: I,
4680 IsSigned);
4681
4682 SPIRVTypeInst SrcType = GR.getSPIRVTypeForVReg(VReg: SrcReg);
4683 if (ResType == SrcType)
4684 return BuildCOPY(DestReg: ResVReg, SrcReg, I);
4685
4686 unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
4687 return selectUnOp(ResVReg, ResType, I, Opcode);
4688}
4689
4690bool SPIRVInstructionSelector::selectSUCmp(Register ResVReg,
4691 SPIRVTypeInst ResType,
4692 MachineInstr &I,
4693 bool IsSigned) const {
4694 MachineIRBuilder MIRBuilder(I);
4695 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
4696 MachineBasicBlock &BB = *I.getParent();
4697 // Ensure we have bool.
4698 SPIRVTypeInst BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
4699 unsigned N = GR.getScalarOrVectorComponentCount(Type: ResType);
4700 if (N > 1)
4701 BoolType = GR.getOrCreateSPIRVVectorType(BaseType: BoolType, NumElements: N, I, TII);
4702 Register BoolTypeReg = GR.getSPIRVTypeID(SpirvType: BoolType);
4703 // Build less-than-equal and less-than.
4704 Register IsLessEqReg =
4705 createVirtualRegister(SpvType: BoolType, GR: &GR, MRI, MF: MIRBuilder.getMF());
4706 BuildMI(BB, I, MIMD: I.getDebugLoc(),
4707 MCID: TII.get(Opcode: IsSigned ? SPIRV::OpSLessThanEqual : SPIRV::OpULessThanEqual))
4708 .addDef(RegNo: IsLessEqReg)
4709 .addUse(RegNo: BoolTypeReg)
4710 .addUse(RegNo: I.getOperand(i: 1).getReg())
4711 .addUse(RegNo: I.getOperand(i: 2).getReg())
4712 .constrainAllUses(TII, TRI, RBI);
4713 Register IsLessReg =
4714 createVirtualRegister(SpvType: BoolType, GR: &GR, MRI, MF: MIRBuilder.getMF());
4715 BuildMI(BB, I, MIMD: I.getDebugLoc(),
4716 MCID: TII.get(Opcode: IsSigned ? SPIRV::OpSLessThan : SPIRV::OpULessThan))
4717 .addDef(RegNo: IsLessReg)
4718 .addUse(RegNo: BoolTypeReg)
4719 .addUse(RegNo: I.getOperand(i: 1).getReg())
4720 .addUse(RegNo: I.getOperand(i: 2).getReg())
4721 .constrainAllUses(TII, TRI, RBI);
4722 // Build selects.
4723 Register ResTypeReg = GR.getSPIRVTypeID(SpirvType: ResType);
4724 Register NegOneOrZeroReg =
4725 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
4726 MRI->setType(VReg: NegOneOrZeroReg, Ty: LLT::scalar(SizeInBits: 64));
4727 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: NegOneOrZeroReg, MF: MIRBuilder.getMF());
4728 unsigned SelectOpcode =
4729 N > 1 ? SPIRV::OpSelectVIVCond : SPIRV::OpSelectSISCond;
4730 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SelectOpcode))
4731 .addDef(RegNo: NegOneOrZeroReg)
4732 .addUse(RegNo: ResTypeReg)
4733 .addUse(RegNo: IsLessReg)
4734 .addUse(RegNo: buildOnesVal(AllOnes: true, ResType, I)) // -1
4735 .addUse(RegNo: buildZerosVal(ResType, I))
4736 .constrainAllUses(TII, TRI, RBI);
4737 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SelectOpcode))
4738 .addDef(RegNo: ResVReg)
4739 .addUse(RegNo: ResTypeReg)
4740 .addUse(RegNo: IsLessEqReg)
4741 .addUse(RegNo: NegOneOrZeroReg) // -1 or 0
4742 .addUse(RegNo: buildOnesVal(AllOnes: false, ResType, I))
4743 .constrainAllUses(TII, TRI, RBI);
4744 return true;
4745}
4746
4747bool SPIRVInstructionSelector::selectIntToBool(Register IntReg,
4748 Register ResVReg,
4749 MachineInstr &I,
4750 SPIRVTypeInst IntTy,
4751 SPIRVTypeInst BoolTy) const {
4752 // To truncate to a bool, we use OpBitwiseAnd 1 and OpINotEqual to zero.
4753 Register BitIntReg = createVirtualRegister(SpvType: IntTy, GR: &GR, MRI, MF: MRI->getMF());
4754 bool IsVectorTy = IntTy->getOpcode() == SPIRV::OpTypeVector;
4755 unsigned Opcode = IsVectorTy ? SPIRV::OpBitwiseAndV : SPIRV::OpBitwiseAndS;
4756 Register Zero = buildZerosVal(ResType: IntTy, I);
4757 Register One = buildOnesVal(AllOnes: false, ResType: IntTy, I);
4758 MachineBasicBlock &BB = *I.getParent();
4759 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
4760 .addDef(RegNo: BitIntReg)
4761 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: IntTy))
4762 .addUse(RegNo: IntReg)
4763 .addUse(RegNo: One)
4764 .constrainAllUses(TII, TRI, RBI);
4765 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpINotEqual))
4766 .addDef(RegNo: ResVReg)
4767 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: BoolTy))
4768 .addUse(RegNo: BitIntReg)
4769 .addUse(RegNo: Zero)
4770 .constrainAllUses(TII, TRI, RBI);
4771 return true;
4772}
4773
4774bool SPIRVInstructionSelector::selectTrunc(Register ResVReg,
4775 SPIRVTypeInst ResType,
4776 MachineInstr &I) const {
4777 Register IntReg = I.getOperand(i: 1).getReg();
4778 const SPIRVTypeInst ArgType = GR.getSPIRVTypeForVReg(VReg: IntReg);
4779 if (GR.isScalarOrVectorOfType(VReg: ResVReg, TypeOpcode: SPIRV::OpTypeBool))
4780 return selectIntToBool(IntReg, ResVReg, I, IntTy: ArgType, BoolTy: ResType);
4781 if (ArgType == ResType)
4782 return BuildCOPY(DestReg: ResVReg, SrcReg: IntReg, I);
4783 bool IsSigned = GR.isScalarOrVectorSigned(Type: ResType);
4784 unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
4785 return selectUnOp(ResVReg, ResType, I, Opcode);
4786}
4787
4788bool SPIRVInstructionSelector::selectConst(Register ResVReg,
4789 SPIRVTypeInst ResType,
4790 MachineInstr &I) const {
4791 unsigned Opcode = I.getOpcode();
4792 unsigned TpOpcode = ResType->getOpcode();
4793 Register Reg;
4794 if (TpOpcode == SPIRV::OpTypePointer || TpOpcode == SPIRV::OpTypeEvent) {
4795 assert(Opcode == TargetOpcode::G_CONSTANT &&
4796 I.getOperand(1).getCImm()->isZero());
4797 MachineBasicBlock &DepMBB = I.getMF()->front();
4798 MachineIRBuilder MIRBuilder(DepMBB, DepMBB.getFirstNonPHI());
4799 Reg = GR.getOrCreateConstNullPtr(MIRBuilder, SpvType: ResType);
4800 } else if (Opcode == TargetOpcode::G_FCONSTANT) {
4801 Reg = GR.getOrCreateConstFP(Val: I.getOperand(i: 1).getFPImm()->getValue(), I,
4802 SpvType: ResType, TII, ZeroAsNull: !STI.isShader());
4803 } else {
4804 Reg = GR.getOrCreateConstInt(Val: I.getOperand(i: 1).getCImm()->getValue(), I,
4805 SpvType: ResType, TII, ZeroAsNull: !STI.isShader());
4806 }
4807 return Reg == ResVReg ? true : BuildCOPY(DestReg: ResVReg, SrcReg: Reg, I);
4808}
4809
4810bool SPIRVInstructionSelector::selectOpUndef(Register ResVReg,
4811 SPIRVTypeInst ResType,
4812 MachineInstr &I) const {
4813 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
4814 .addDef(RegNo: ResVReg)
4815 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4816 .constrainAllUses(TII, TRI, RBI);
4817 return true;
4818}
4819
4820bool SPIRVInstructionSelector::selectInsertVal(Register ResVReg,
4821 SPIRVTypeInst ResType,
4822 MachineInstr &I) const {
4823 MachineBasicBlock &BB = *I.getParent();
4824 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeInsert))
4825 .addDef(RegNo: ResVReg)
4826 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4827 // object to insert
4828 .addUse(RegNo: I.getOperand(i: 3).getReg())
4829 // composite to insert into
4830 .addUse(RegNo: I.getOperand(i: 2).getReg());
4831 for (unsigned i = 4; i < I.getNumOperands(); i++)
4832 MIB.addImm(Val: foldImm(MO: I.getOperand(i), MRI));
4833 MIB.constrainAllUses(TII, TRI, RBI);
4834 return true;
4835}
4836
4837bool SPIRVInstructionSelector::selectExtractVal(Register ResVReg,
4838 SPIRVTypeInst ResType,
4839 MachineInstr &I) const {
4840 Type *MaybeResTy = nullptr;
4841 StringRef ResName;
4842 if (GR.findValueAttrs(Key: &I, Ty&: MaybeResTy, Name&: ResName) &&
4843 MaybeResTy != GR.getTypeForSPIRVType(Ty: ResType)) {
4844 assert((!MaybeResTy || MaybeResTy->isAggregateType()) &&
4845 "Expected aggregate type for extractv instruction");
4846 ResType = GR.getOrCreateSPIRVType(Type: MaybeResTy, I,
4847 AQ: SPIRV::AccessQualifier::ReadWrite, EmitIR: false);
4848 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: ResVReg, MF: *I.getMF());
4849 }
4850 MachineBasicBlock &BB = *I.getParent();
4851 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
4852 .addDef(RegNo: ResVReg)
4853 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4854 .addUse(RegNo: I.getOperand(i: 2).getReg());
4855 for (unsigned i = 3; i < I.getNumOperands(); i++)
4856 MIB.addImm(Val: foldImm(MO: I.getOperand(i), MRI));
4857 MIB.constrainAllUses(TII, TRI, RBI);
4858 return true;
4859}
4860
4861bool SPIRVInstructionSelector::selectInsertElt(Register ResVReg,
4862 SPIRVTypeInst ResType,
4863 MachineInstr &I) const {
4864 if (getImm(MO: I.getOperand(i: 4), MRI))
4865 return selectInsertVal(ResVReg, ResType, I);
4866 MachineBasicBlock &BB = *I.getParent();
4867 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVectorInsertDynamic))
4868 .addDef(RegNo: ResVReg)
4869 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4870 .addUse(RegNo: I.getOperand(i: 2).getReg())
4871 .addUse(RegNo: I.getOperand(i: 3).getReg())
4872 .addUse(RegNo: I.getOperand(i: 4).getReg())
4873 .constrainAllUses(TII, TRI, RBI);
4874 return true;
4875}
4876
4877bool SPIRVInstructionSelector::selectExtractElt(Register ResVReg,
4878 SPIRVTypeInst ResType,
4879 MachineInstr &I) const {
4880 if (getImm(MO: I.getOperand(i: 3), MRI))
4881 return selectExtractVal(ResVReg, ResType, I);
4882 MachineBasicBlock &BB = *I.getParent();
4883 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVectorExtractDynamic))
4884 .addDef(RegNo: ResVReg)
4885 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4886 .addUse(RegNo: I.getOperand(i: 2).getReg())
4887 .addUse(RegNo: I.getOperand(i: 3).getReg())
4888 .constrainAllUses(TII, TRI, RBI);
4889 return true;
4890}
4891
4892bool SPIRVInstructionSelector::selectGEP(Register ResVReg,
4893 SPIRVTypeInst ResType,
4894 MachineInstr &I) const {
4895 const bool IsGEPInBounds = I.getOperand(i: 2).getImm();
4896
4897 // OpAccessChain could be used for OpenCL, but the SPIRV-LLVM Translator only
4898 // relies on PtrAccessChain, so we'll try not to deviate. For Vulkan however,
4899 // we have to use Op[InBounds]AccessChain.
4900 const unsigned Opcode = STI.isLogicalSPIRV()
4901 ? (IsGEPInBounds ? SPIRV::OpInBoundsAccessChain
4902 : SPIRV::OpAccessChain)
4903 : (IsGEPInBounds ? SPIRV::OpInBoundsPtrAccessChain
4904 : SPIRV::OpPtrAccessChain);
4905
4906 auto Res = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
4907 .addDef(RegNo: ResVReg)
4908 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4909 // Object to get a pointer to.
4910 .addUse(RegNo: I.getOperand(i: 3).getReg());
4911 assert(
4912 (Opcode == SPIRV::OpPtrAccessChain ||
4913 Opcode == SPIRV::OpInBoundsPtrAccessChain ||
4914 (getImm(I.getOperand(4), MRI) && foldImm(I.getOperand(4), MRI) == 0)) &&
4915 "Cannot translate GEP to OpAccessChain. First index must be 0.");
4916
4917 // Adding indices.
4918 const unsigned StartingIndex =
4919 (Opcode == SPIRV::OpAccessChain || Opcode == SPIRV::OpInBoundsAccessChain)
4920 ? 5
4921 : 4;
4922 for (unsigned i = StartingIndex; i < I.getNumExplicitOperands(); ++i)
4923 Res.addUse(RegNo: I.getOperand(i).getReg());
4924 Res.constrainAllUses(TII, TRI, RBI);
4925 return true;
4926}
4927
4928// Maybe wrap a value into OpSpecConstantOp
4929bool SPIRVInstructionSelector::wrapIntoSpecConstantOp(
4930 MachineInstr &I, SmallVector<Register> &CompositeArgs) const {
4931 unsigned Lim = I.getNumExplicitOperands();
4932 for (unsigned i = I.getNumExplicitDefs() + 1; i < Lim; ++i) {
4933 Register OpReg = I.getOperand(i).getReg();
4934 MachineInstr *OpDefine = MRI->getVRegDef(Reg: OpReg);
4935 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
4936 if (!OpDefine || !OpType || isConstReg(MRI, OpDef: OpDefine) ||
4937 OpDefine->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST ||
4938 OpDefine->getOpcode() == TargetOpcode::G_INTTOPTR ||
4939 GR.isAggregateType(Type: OpType)) {
4940 // The case of G_ADDRSPACE_CAST inside spv_const_composite() is processed
4941 // by selectAddrSpaceCast(), and G_INTTOPTR is processed by selectUnOp()
4942 CompositeArgs.push_back(Elt: OpReg);
4943 continue;
4944 }
4945 MachineFunction *MF = I.getMF();
4946 Register WrapReg = GR.find(MI: OpDefine, MF);
4947 if (WrapReg.isValid()) {
4948 CompositeArgs.push_back(Elt: WrapReg);
4949 continue;
4950 }
4951 // Create a new register for the wrapper
4952 WrapReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: OpType));
4953 CompositeArgs.push_back(Elt: WrapReg);
4954 // Decorate the wrapper register and generate a new instruction
4955 MRI->setType(VReg: WrapReg, Ty: LLT::pointer(AddressSpace: 0, SizeInBits: 64));
4956 GR.assignSPIRVTypeToVReg(Type: OpType, VReg: WrapReg, MF: *MF);
4957 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
4958 MCID: TII.get(Opcode: SPIRV::OpSpecConstantOp))
4959 .addDef(RegNo: WrapReg)
4960 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: OpType))
4961 .addImm(Val: static_cast<uint32_t>(SPIRV::Opcode::Bitcast))
4962 .addUse(RegNo: OpReg);
4963 GR.add(Obj: OpDefine, MI: MIB);
4964 MIB.constrainAllUses(TII, TRI, RBI);
4965 }
4966 return true;
4967}
4968
4969bool SPIRVInstructionSelector::selectDerivativeInst(
4970 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
4971 const unsigned DPdOpCode) const {
4972 // TODO: This should check specifically for Fragment Execution Model, but STI
4973 // doesn't provide that information yet. See #167562
4974 if (!errorIfInstrOutsideShader(I))
4975 return false;
4976
4977 // If the arg/result types are half then we need to wrap the instr in
4978 // conversions to float
4979 // This case occurs because a half arg/result is legal in HLSL but not spirv.
4980 Register SrcReg = I.getOperand(i: 2).getReg();
4981 SPIRVTypeInst SrcType = GR.getSPIRVTypeForVReg(VReg: SrcReg);
4982 unsigned BitWidth = std::min(a: GR.getScalarOrVectorBitWidth(Type: SrcType),
4983 b: GR.getScalarOrVectorBitWidth(Type: ResType));
4984 if (BitWidth == 32)
4985 return BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DPdOpCode))
4986 .addDef(RegNo: ResVReg)
4987 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
4988 .addUse(RegNo: I.getOperand(i: 2).getReg());
4989
4990 MachineIRBuilder MIRBuilder(I);
4991 unsigned componentCount = GR.getScalarOrVectorComponentCount(Type: SrcType);
4992 SPIRVTypeInst F32ConvertTy = GR.getOrCreateSPIRVFloatType(BitWidth: 32, I, TII);
4993 if (componentCount != 1)
4994 F32ConvertTy = GR.getOrCreateSPIRVVectorType(BaseType: F32ConvertTy, NumElements: componentCount,
4995 MIRBuilder, EmitIR: false);
4996
4997 const TargetRegisterClass *RegClass = GR.getRegClass(SpvType: SrcType);
4998 Register ConvertToVReg = MRI->createVirtualRegister(RegClass);
4999 Register DpdOpVReg = MRI->createVirtualRegister(RegClass);
5000
5001 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFConvert))
5002 .addDef(RegNo: ConvertToVReg)
5003 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: F32ConvertTy))
5004 .addUse(RegNo: SrcReg)
5005 .constrainAllUses(TII, TRI, RBI);
5006 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: DPdOpCode))
5007 .addDef(RegNo: DpdOpVReg)
5008 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: F32ConvertTy))
5009 .addUse(RegNo: ConvertToVReg)
5010 .constrainAllUses(TII, TRI, RBI);
5011 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFConvert))
5012 .addDef(RegNo: ResVReg)
5013 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5014 .addUse(RegNo: DpdOpVReg)
5015 .constrainAllUses(TII, TRI, RBI);
5016 return true;
5017}
5018
5019bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
5020 SPIRVTypeInst ResType,
5021 MachineInstr &I) const {
5022 MachineBasicBlock &BB = *I.getParent();
5023 Intrinsic::ID IID = cast<GIntrinsic>(Val&: I).getIntrinsicID();
5024 switch (IID) {
5025 case Intrinsic::spv_load:
5026 return selectLoad(ResVReg, ResType, I);
5027 case Intrinsic::spv_atomic_load:
5028 return selectAtomicLoad(ResVReg, ResType, I);
5029 case Intrinsic::spv_store:
5030 return selectStore(I);
5031 case Intrinsic::spv_atomic_store:
5032 return selectAtomicStore(I);
5033 case Intrinsic::spv_extractv:
5034 return selectExtractVal(ResVReg, ResType, I);
5035 case Intrinsic::spv_insertv:
5036 return selectInsertVal(ResVReg, ResType, I);
5037 case Intrinsic::spv_extractelt:
5038 return selectExtractElt(ResVReg, ResType, I);
5039 case Intrinsic::spv_insertelt:
5040 return selectInsertElt(ResVReg, ResType, I);
5041 case Intrinsic::spv_gep:
5042 return selectGEP(ResVReg, ResType, I);
5043 case Intrinsic::spv_bitcast: {
5044 Register OpReg = I.getOperand(i: 2).getReg();
5045 SPIRVTypeInst OpType =
5046 OpReg.isValid() ? GR.getSPIRVTypeForVReg(VReg: OpReg) : nullptr;
5047 if (!GR.isBitcastCompatible(Type1: ResType, Type2: OpType))
5048 report_fatal_error(reason: "incompatible result and operand types in a bitcast");
5049 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {OpReg}, Opcode: SPIRV::OpBitcast);
5050 }
5051 case Intrinsic::spv_unref_global:
5052 case Intrinsic::spv_init_global: {
5053 MachineInstr *MI = MRI->getVRegDef(Reg: I.getOperand(i: 1).getReg());
5054 MachineInstr *Init = I.getNumExplicitOperands() > 2
5055 ? MRI->getVRegDef(Reg: I.getOperand(i: 2).getReg())
5056 : nullptr;
5057 assert(MI);
5058 Register GVarVReg = MI->getOperand(i: 0).getReg();
5059 if (!selectGlobalValue(ResVReg: GVarVReg, I&: *MI, Init))
5060 return false;
5061 // We violate SSA form by inserting OpVariable and still having a gMIR
5062 // instruction %vreg = G_GLOBAL_VALUE @gvar. We need to fix this by erasing
5063 // the duplicated definition.
5064 if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
5065 GR.invalidateMachineInstr(MI);
5066 MI->eraseFromParent();
5067 }
5068 return true;
5069 }
5070 case Intrinsic::spv_undef: {
5071 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
5072 .addDef(RegNo: ResVReg)
5073 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5074 MIB.constrainAllUses(TII, TRI, RBI);
5075 return true;
5076 }
5077 case Intrinsic::spv_poison:
5078 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpPoisonKHR))
5079 .addDef(RegNo: ResVReg)
5080 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5081 .constrainAllUses(TII, TRI, RBI);
5082 return true;
5083 case Intrinsic::spv_freeze:
5084 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpFreezeKHR))
5085 .addDef(RegNo: ResVReg)
5086 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5087 .addUse(RegNo: I.getOperand(i: 2).getReg())
5088 .constrainAllUses(TII, TRI, RBI);
5089 return true;
5090 case Intrinsic::spv_named_boolean_spec_constant: {
5091 auto Opcode = I.getOperand(i: 3).getImm() ? SPIRV::OpSpecConstantTrue
5092 : SPIRV::OpSpecConstantFalse;
5093
5094 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
5095 .addDef(RegNo: I.getOperand(i: 0).getReg())
5096 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5097 MIB.constrainAllUses(TII, TRI, RBI);
5098 unsigned SpecId = I.getOperand(i: 2).getImm();
5099 buildOpDecorate(Reg: I.getOperand(i: 0).getReg(), I&: *++MIB->getIterator(), TII,
5100 Dec: SPIRV::Decoration::SpecId, DecArgs: {SpecId});
5101
5102 return true;
5103 }
5104 case Intrinsic::spv_const_composite: {
5105 // If no values are attached, the composite is null constant.
5106 bool IsNull = I.getNumExplicitDefs() + 1 == I.getNumExplicitOperands();
5107 SmallVector<Register> CompositeArgs;
5108 MRI->setRegClass(Reg: ResVReg, RC: GR.getRegClass(SpvType: ResType));
5109
5110 // skip type MD node we already used when generated assign.type for this
5111 if (!IsNull) {
5112 if (!wrapIntoSpecConstantOp(I, CompositeArgs))
5113 return false;
5114 std::function<bool(Register)> HasSpecConstOperand =
5115 [&](Register Reg) -> bool {
5116 MachineInstr *Def = MRI->getVRegDef(Reg);
5117 if (!Def)
5118 return false;
5119 if (!isConstReg(MRI, OpDef: Def))
5120 return true;
5121 // Recurse into not-yet-selected spv_const_composite intrinsics
5122 // to detect transitive spec constant operands.
5123 if (isSpvIntrinsic(MI: *Def, IntrinsicID: Intrinsic::spv_const_composite)) {
5124 for (unsigned J = Def->getNumExplicitDefs() + 1;
5125 J < Def->getNumExplicitOperands(); ++J) {
5126 if (Def->getOperand(i: J).isReg() &&
5127 HasSpecConstOperand(Def->getOperand(i: J).getReg()))
5128 return true;
5129 }
5130 }
5131 return false;
5132 };
5133 bool HasSpecConst = llvm::any_of(Range&: CompositeArgs, P: HasSpecConstOperand);
5134 unsigned CompositeOpc = HasSpecConst ? SPIRV::OpSpecConstantComposite
5135 : SPIRV::OpConstantComposite;
5136 unsigned ContinuedOpc = HasSpecConst
5137 ? SPIRV::OpSpecConstantCompositeContinuedINTEL
5138 : SPIRV::OpConstantCompositeContinuedINTEL;
5139 MachineIRBuilder MIR(I);
5140 SmallVector<MachineInstr *, 4> Instructions = createContinuedInstructions(
5141 MIRBuilder&: MIR, Opcode: CompositeOpc, MinWC: 3, ContinuedOpcode: ContinuedOpc, Args: CompositeArgs, ReturnRegister: ResVReg,
5142 TypeID: GR.getSPIRVTypeID(SpirvType: ResType));
5143 for (auto *Instr : Instructions) {
5144 Instr->setDebugLoc(I.getDebugLoc());
5145 constrainSelectedInstRegOperands(I&: *Instr, TII, TRI, RBI);
5146 }
5147 return true;
5148 } else {
5149 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
5150 .addDef(RegNo: ResVReg)
5151 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5152 MIB.constrainAllUses(TII, TRI, RBI);
5153 return true;
5154 }
5155 }
5156 case Intrinsic::spv_assign_name: {
5157 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpName));
5158 MIB.addUse(RegNo: I.getOperand(i: I.getNumExplicitDefs() + 1).getReg());
5159 for (unsigned i = I.getNumExplicitDefs() + 2;
5160 i < I.getNumExplicitOperands(); ++i) {
5161 MIB.addImm(Val: I.getOperand(i).getImm());
5162 }
5163 MIB.constrainAllUses(TII, TRI, RBI);
5164 return true;
5165 }
5166 case Intrinsic::spv_switch: {
5167 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSwitch));
5168 for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
5169 if (I.getOperand(i).isReg())
5170 MIB.addReg(RegNo: I.getOperand(i).getReg());
5171 else if (I.getOperand(i).isCImm())
5172 addNumImm(Imm: I.getOperand(i).getCImm()->getValue(), MIB);
5173 else if (I.getOperand(i).isMBB())
5174 MIB.addMBB(MBB: I.getOperand(i).getMBB());
5175 else
5176 llvm_unreachable("Unexpected OpSwitch operand");
5177 }
5178 MIB.constrainAllUses(TII, TRI, RBI);
5179 return true;
5180 }
5181 case Intrinsic::spv_loop_merge: {
5182 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoopMerge));
5183 for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
5184 if (I.getOperand(i).isMBB())
5185 MIB.addMBB(MBB: I.getOperand(i).getMBB());
5186 else
5187 MIB.addImm(Val: foldImm(MO: I.getOperand(i), MRI));
5188 }
5189 MIB.constrainAllUses(TII, TRI, RBI);
5190 return true;
5191 }
5192 case Intrinsic::spv_loop_control_intel: {
5193 auto MIB =
5194 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoopControlINTEL));
5195 for (unsigned J = 1; J < I.getNumExplicitOperands(); ++J)
5196 MIB.addImm(Val: foldImm(MO: I.getOperand(i: J), MRI));
5197 MIB.constrainAllUses(TII, TRI, RBI);
5198 return true;
5199 }
5200 case Intrinsic::spv_selection_merge: {
5201 auto MIB =
5202 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSelectionMerge));
5203 assert(I.getOperand(1).isMBB() &&
5204 "operand 1 to spv_selection_merge must be a basic block");
5205 MIB.addMBB(MBB: I.getOperand(i: 1).getMBB());
5206 MIB.addImm(Val: getSelectionOperandForImm(Imm: I.getOperand(i: 2).getImm()));
5207 MIB.constrainAllUses(TII, TRI, RBI);
5208 return true;
5209 }
5210 case Intrinsic::spv_cmpxchg:
5211 return selectAtomicCmpXchg(ResVReg, ResType, I);
5212 case Intrinsic::spv_unreachable:
5213 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUnreachable))
5214 .constrainAllUses(TII, TRI, RBI);
5215 return true;
5216 case Intrinsic::spv_abort:
5217 return selectAbort(I);
5218 case Intrinsic::spv_alloca:
5219 return selectFrameIndex(ResVReg, ResType, I);
5220 case Intrinsic::spv_alloca_array:
5221 return selectAllocaArray(ResVReg, ResType, I);
5222 case Intrinsic::spv_assume:
5223 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_expect_assume)) {
5224 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAssumeTrueKHR))
5225 .addUse(RegNo: I.getOperand(i: 1).getReg())
5226 .constrainAllUses(TII, TRI, RBI);
5227 return true;
5228 }
5229 break;
5230 case Intrinsic::spv_expect:
5231 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_expect_assume)) {
5232 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExpectKHR))
5233 .addDef(RegNo: ResVReg)
5234 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5235 .addUse(RegNo: I.getOperand(i: 2).getReg())
5236 .addUse(RegNo: I.getOperand(i: 3).getReg())
5237 .constrainAllUses(TII, TRI, RBI);
5238 return true;
5239 }
5240 break;
5241 case Intrinsic::arithmetic_fence:
5242 if (STI.canUseExtension(E: SPIRV::Extension::SPV_EXT_arithmetic_fence)) {
5243 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpArithmeticFenceEXT))
5244 .addDef(RegNo: ResVReg)
5245 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5246 .addUse(RegNo: I.getOperand(i: 2).getReg())
5247 .constrainAllUses(TII, TRI, RBI);
5248 return true;
5249 } else
5250 return BuildCOPY(DestReg: ResVReg, SrcReg: I.getOperand(i: 2).getReg(), I);
5251 break;
5252 case Intrinsic::spv_thread_id:
5253 // The HLSL SV_DispatchThreadID semantic is lowered to llvm.spv.thread.id
5254 // intrinsic in LLVM IR for SPIR-V backend.
5255 //
5256 // In SPIR-V backend, llvm.spv.thread.id is now correctly translated to a
5257 // `GlobalInvocationId` builtin variable
5258 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::GlobalInvocationId, ResVReg,
5259 ResType, I);
5260 case Intrinsic::spv_thread_id_in_group:
5261 // The HLSL SV_GroupThreadId semantic is lowered to
5262 // llvm.spv.thread.id.in.group intrinsic in LLVM IR for SPIR-V backend.
5263 //
5264 // In SPIR-V backend, llvm.spv.thread.id.in.group is now correctly
5265 // translated to a `LocalInvocationId` builtin variable
5266 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::LocalInvocationId, ResVReg,
5267 ResType, I);
5268 case Intrinsic::spv_group_id:
5269 // The HLSL SV_GroupId semantic is lowered to
5270 // llvm.spv.group.id intrinsic in LLVM IR for SPIR-V backend.
5271 //
5272 // In SPIR-V backend, llvm.spv.group.id is now translated to a `WorkgroupId`
5273 // builtin variable
5274 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::WorkgroupId, ResVReg, ResType,
5275 I);
5276 case Intrinsic::spv_flattened_thread_id_in_group:
5277 // The HLSL SV_GroupIndex semantic is lowered to
5278 // llvm.spv.flattened.thread.id.in.group() intrinsic in LLVM IR for SPIR-V
5279 // backend.
5280 //
5281 // In SPIR-V backend, llvm.spv.flattened.thread.id.in.group is translated to
5282 // a `LocalInvocationIndex` builtin variable
5283 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::LocalInvocationIndex, ResVReg,
5284 ResType, I);
5285 case Intrinsic::spv_workgroup_size:
5286 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::WorkgroupSize, ResVReg,
5287 ResType, I);
5288 case Intrinsic::spv_global_size:
5289 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::GlobalSize, ResVReg, ResType,
5290 I);
5291 case Intrinsic::spv_global_offset:
5292 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::GlobalOffset, ResVReg,
5293 ResType, I);
5294 case Intrinsic::spv_num_workgroups:
5295 return loadVec3BuiltinInputID(BuiltInValue: SPIRV::BuiltIn::NumWorkgroups, ResVReg,
5296 ResType, I);
5297 case Intrinsic::spv_subgroup_size:
5298 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupSize, ResVReg, ResType,
5299 I);
5300 case Intrinsic::spv_num_subgroups:
5301 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::NumSubgroups, ResVReg, ResType,
5302 I);
5303 case Intrinsic::spv_subgroup_id:
5304 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupId, ResVReg, ResType, I);
5305 case Intrinsic::spv_subgroup_local_invocation_id:
5306 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupLocalInvocationId,
5307 ResVReg, ResType, I);
5308 case Intrinsic::spv_subgroup_max_size:
5309 return loadBuiltinInputID(BuiltInValue: SPIRV::BuiltIn::SubgroupMaxSize, ResVReg, ResType,
5310 I);
5311 case Intrinsic::spv_fdot:
5312 return selectFloatDot(ResVReg, ResType, I);
5313 case Intrinsic::spv_udot:
5314 case Intrinsic::spv_sdot:
5315 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_integer_dot_product) ||
5316 STI.isAtLeastSPIRVVer(VerToCompareTo: VersionTuple(1, 6)))
5317 return selectIntegerDot(ResVReg, ResType, I,
5318 /*Signed=*/IID == Intrinsic::spv_sdot);
5319 return selectIntegerDotExpansion(ResVReg, ResType, I);
5320 case Intrinsic::spv_dot4add_i8packed:
5321 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_integer_dot_product) ||
5322 STI.isAtLeastSPIRVVer(VerToCompareTo: VersionTuple(1, 6)))
5323 return selectDot4AddPacked<true>(ResVReg, ResType, I);
5324 return selectDot4AddPackedExpansion<true>(ResVReg, ResType, I);
5325 case Intrinsic::spv_dot4add_u8packed:
5326 if (STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_integer_dot_product) ||
5327 STI.isAtLeastSPIRVVer(VerToCompareTo: VersionTuple(1, 6)))
5328 return selectDot4AddPacked<false>(ResVReg, ResType, I);
5329 return selectDot4AddPackedExpansion<false>(ResVReg, ResType, I);
5330 case Intrinsic::spv_all:
5331 return selectAll(ResVReg, ResType, I);
5332 case Intrinsic::spv_any:
5333 return selectAny(ResVReg, ResType, I);
5334 case Intrinsic::spv_cross:
5335 return selectExtInst(ResVReg, ResType, I, CLInst: CL::cross, GLInst: GL::Cross);
5336 case Intrinsic::spv_distance:
5337 return selectExtInst(ResVReg, ResType, I, CLInst: CL::distance, GLInst: GL::Distance);
5338 case Intrinsic::spv_lerp:
5339 return selectExtInst(ResVReg, ResType, I, CLInst: CL::mix, GLInst: GL::FMix);
5340 case Intrinsic::spv_length:
5341 return selectExtInst(ResVReg, ResType, I, CLInst: CL::length, GLInst: GL::Length);
5342 case Intrinsic::spv_degrees:
5343 return selectExtInst(ResVReg, ResType, I, CLInst: CL::degrees, GLInst: GL::Degrees);
5344 case Intrinsic::spv_faceforward:
5345 return selectExtInst(ResVReg, ResType, I, GLInst: GL::FaceForward);
5346 case Intrinsic::spv_frac:
5347 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fract, GLInst: GL::Fract);
5348 case Intrinsic::spv_isinf:
5349 return selectOpIsInf(ResVReg, ResType, I);
5350 case Intrinsic::spv_isnan:
5351 return selectOpIsNan(ResVReg, ResType, I);
5352 case Intrinsic::spv_isfinite:
5353 return selectOpIsFinite(ResVReg, ResType, I);
5354 case Intrinsic::spv_isnormal:
5355 return selectOpIsNormal(ResVReg, ResType, I);
5356 case Intrinsic::spv_normalize:
5357 return selectExtInst(ResVReg, ResType, I, CLInst: CL::normalize, GLInst: GL::Normalize);
5358 case Intrinsic::spv_refract:
5359 return selectExtInst(ResVReg, ResType, I, GLInst: GL::Refract);
5360 case Intrinsic::spv_reflect:
5361 return selectExtInst(ResVReg, ResType, I, GLInst: GL::Reflect);
5362 case Intrinsic::spv_rsqrt:
5363 return selectExtInst(ResVReg, ResType, I, CLInst: CL::rsqrt, GLInst: GL::InverseSqrt);
5364 case Intrinsic::spv_sign:
5365 return selectSign(ResVReg, ResType, I);
5366 case Intrinsic::spv_smoothstep:
5367 return selectExtInst(ResVReg, ResType, I, CLInst: CL::smoothstep, GLInst: GL::SmoothStep);
5368 case Intrinsic::spv_firstbituhigh: // There is no CL equivalent of FindUMsb
5369 return selectFirstBitHigh(ResVReg, ResType, I, /*IsSigned=*/false);
5370 case Intrinsic::spv_firstbitshigh: // There is no CL equivalent of FindSMsb
5371 return selectFirstBitHigh(ResVReg, ResType, I, /*IsSigned=*/true);
5372 case Intrinsic::spv_firstbitlow: // There is no CL equivlent of FindILsb
5373 return selectFirstBitLow(ResVReg, ResType, I);
5374 case Intrinsic::spv_all_memory_barrier:
5375 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5376 MemSem: SPIRV::MemorySemantics::UniformMemory |
5377 SPIRV::MemorySemantics::ImageMemory |
5378 SPIRV::MemorySemantics::WorkgroupMemory,
5379 /*WithGroupSync*/ false);
5380 case Intrinsic::spv_all_memory_barrier_with_group_sync:
5381 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5382 MemSem: SPIRV::MemorySemantics::UniformMemory |
5383 SPIRV::MemorySemantics::ImageMemory |
5384 SPIRV::MemorySemantics::WorkgroupMemory,
5385 /*WithGroupSync*/ true);
5386 case Intrinsic::spv_device_memory_barrier:
5387 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5388 MemSem: SPIRV::MemorySemantics::UniformMemory |
5389 SPIRV::MemorySemantics::ImageMemory,
5390 /*WithGroupSync*/ false);
5391 case Intrinsic::spv_device_memory_barrier_with_group_sync:
5392 return selectBarrierInst(I, Scope: SPIRV::Scope::Device,
5393 MemSem: SPIRV::MemorySemantics::UniformMemory |
5394 SPIRV::MemorySemantics::ImageMemory,
5395 /*WithGroupSync*/ true);
5396 case Intrinsic::spv_group_memory_barrier:
5397 return selectBarrierInst(I, Scope: SPIRV::Scope::Workgroup,
5398 MemSem: SPIRV::MemorySemantics::WorkgroupMemory,
5399 /*WithGroupSync*/ false);
5400 case Intrinsic::spv_group_memory_barrier_with_group_sync:
5401 return selectBarrierInst(I, Scope: SPIRV::Scope::Workgroup,
5402 MemSem: SPIRV::MemorySemantics::WorkgroupMemory,
5403 /*WithGroupSync*/ true);
5404 case Intrinsic::spv_generic_cast_to_ptr_explicit: {
5405 Register PtrReg = I.getOperand(i: I.getNumExplicitDefs() + 1).getReg();
5406 SPIRV::StorageClass::StorageClass ResSC =
5407 GR.getPointerStorageClass(Type: ResType);
5408 if (!isGenericCastablePtr(SC: ResSC))
5409 return diagnoseUnsupported(I, Msg: "The target storage class is not castable "
5410 "from the Generic storage class");
5411 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpGenericCastToPtrExplicit))
5412 .addDef(RegNo: ResVReg)
5413 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5414 .addUse(RegNo: PtrReg)
5415 .addImm(Val: ResSC)
5416 .constrainAllUses(TII, TRI, RBI);
5417 return true;
5418 }
5419 case Intrinsic::spv_lifetime_start:
5420 case Intrinsic::spv_lifetime_end: {
5421 unsigned Op = IID == Intrinsic::spv_lifetime_start ? SPIRV::OpLifetimeStart
5422 : SPIRV::OpLifetimeStop;
5423 int64_t Size = I.getOperand(i: I.getNumExplicitDefs() + 1).getImm();
5424 Register PtrReg = I.getOperand(i: I.getNumExplicitDefs() + 2).getReg();
5425 if (Size == -1)
5426 Size = 0;
5427 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: Op))
5428 .addUse(RegNo: PtrReg)
5429 .addImm(Val: Size)
5430 .constrainAllUses(TII, TRI, RBI);
5431 return true;
5432 }
5433 case Intrinsic::spv_saturate:
5434 return selectSaturate(ResVReg, ResType, I);
5435 case Intrinsic::spv_nclamp:
5436 return selectExtInst(ResVReg, ResType, I, CLInst: CL::fclamp, GLInst: GL::NClamp);
5437 case Intrinsic::spv_uclamp:
5438 return selectExtInst(ResVReg, ResType, I, CLInst: CL::u_clamp, GLInst: GL::UClamp);
5439 case Intrinsic::spv_sclamp:
5440 return selectExtInst(ResVReg, ResType, I, CLInst: CL::s_clamp, GLInst: GL::SClamp);
5441 case Intrinsic::spv_subgroup_prefix_bit_count:
5442 return selectWavePrefixBitCount(ResVReg, ResType, I);
5443 case Intrinsic::spv_wave_active_countbits:
5444 return selectWaveActiveCountBits(ResVReg, ResType, I);
5445 case Intrinsic::spv_wave_all_equal:
5446 return selectWaveActiveAllEqual(ResVReg, ResType, I);
5447 case Intrinsic::spv_wave_all:
5448 return selectWaveOpInst(ResVReg, ResType, I, Opcode: SPIRV::OpGroupNonUniformAll);
5449 case Intrinsic::spv_wave_any:
5450 return selectWaveOpInst(ResVReg, ResType, I, Opcode: SPIRV::OpGroupNonUniformAny);
5451 case Intrinsic::spv_subgroup_ballot:
5452 return selectWaveOpInst(ResVReg, ResType, I,
5453 Opcode: SPIRV::OpGroupNonUniformBallot);
5454 case Intrinsic::spv_wave_is_first_lane:
5455 return selectWaveOpInst(ResVReg, ResType, I, Opcode: SPIRV::OpGroupNonUniformElect);
5456 case Intrinsic::spv_wave_reduce_or:
5457 return selectWaveReduceOp(ResVReg, ResType, I,
5458 Opcode: SPIRV::OpGroupNonUniformBitwiseOr);
5459 case Intrinsic::spv_wave_reduce_xor:
5460 return selectWaveReduceOp(ResVReg, ResType, I,
5461 Opcode: SPIRV::OpGroupNonUniformBitwiseXor);
5462 case Intrinsic::spv_wave_reduce_and:
5463 return selectWaveReduceOp(ResVReg, ResType, I,
5464 Opcode: SPIRV::OpGroupNonUniformBitwiseAnd);
5465 case Intrinsic::spv_interlocked_add:
5466 return selectInterlockedOp(ResVReg, ResType, I, Opcode: SPIRV::OpAtomicIAdd);
5467 case Intrinsic::spv_interlocked_or:
5468 return selectInterlockedOp(ResVReg, ResType, I, Opcode: SPIRV::OpAtomicOr);
5469 case Intrinsic::spv_wave_reduce_umax:
5470 return selectWaveReduceMax(ResVReg, ResType, I, /*IsUnsigned*/ true);
5471 case Intrinsic::spv_wave_reduce_max:
5472 return selectWaveReduceMax(ResVReg, ResType, I, /*IsUnsigned*/ false);
5473 case Intrinsic::spv_wave_reduce_umin:
5474 return selectWaveReduceMin(ResVReg, ResType, I, /*IsUnsigned*/ true);
5475 case Intrinsic::spv_wave_reduce_min:
5476 return selectWaveReduceMin(ResVReg, ResType, I, /*IsUnsigned*/ false);
5477 case Intrinsic::spv_wave_reduce_sum:
5478 return selectWaveReduceSum(ResVReg, ResType, I);
5479 case Intrinsic::spv_wave_product:
5480 return selectWaveReduceProduct(ResVReg, ResType, I);
5481 case Intrinsic::spv_wave_readlane:
5482 return selectWaveOpInst(ResVReg, ResType, I,
5483 Opcode: SPIRV::OpGroupNonUniformShuffle);
5484 case Intrinsic::spv_wave_prefix_sum:
5485 return selectWaveExclusiveScanSum(ResVReg, ResType, I);
5486 case Intrinsic::spv_wave_prefix_product:
5487 return selectWaveExclusiveScanProduct(ResVReg, ResType, I);
5488 case Intrinsic::spv_quad_read_across_x: {
5489 return selectQuadSwap(ResVReg, ResType, I, /*Direction*/ 0);
5490 }
5491 case Intrinsic::spv_quad_read_across_y: {
5492 return selectQuadSwap(ResVReg, ResType, I, /*Direction*/ 1);
5493 }
5494 case Intrinsic::spv_quad_read_across_diagonal: {
5495 return selectQuadSwap(ResVReg, ResType, I, /*Direction*/ 2);
5496 }
5497 case Intrinsic::spv_step:
5498 return selectExtInst(ResVReg, ResType, I, CLInst: CL::step, GLInst: GL::Step);
5499 case Intrinsic::spv_radians:
5500 return selectExtInst(ResVReg, ResType, I, CLInst: CL::radians, GLInst: GL::Radians);
5501 // Discard intrinsics which we do not expect to actually represent code after
5502 // lowering or intrinsics which are not implemented but should not crash when
5503 // found in a customer's LLVM IR input.
5504 case Intrinsic::instrprof_increment:
5505 case Intrinsic::instrprof_increment_step:
5506 case Intrinsic::instrprof_value_profile:
5507 break;
5508 // Discard internal intrinsics.
5509 case Intrinsic::spv_value_md:
5510 break;
5511 case Intrinsic::spv_resource_handlefrombinding: {
5512 return selectHandleFromBinding(ResVReg, ResType, I);
5513 }
5514 case Intrinsic::spv_resource_counterhandlefrombinding:
5515 return selectCounterHandleFromBinding(ResVReg, ResType, I);
5516 case Intrinsic::spv_resource_updatecounter:
5517 return selectUpdateCounter(ResVReg, ResType, I);
5518 case Intrinsic::spv_resource_store_typedbuffer: {
5519 return selectImageWriteIntrinsic(I);
5520 }
5521 case Intrinsic::spv_resource_load_typedbuffer: {
5522 return selectReadImageIntrinsic(ResVReg, ResType, I);
5523 }
5524 case Intrinsic::spv_resource_load_level: {
5525 return selectLoadLevelIntrinsic(ResVReg, ResType, I);
5526 }
5527 case Intrinsic::spv_resource_getdimensions_x:
5528 case Intrinsic::spv_resource_getdimensions_xy:
5529 case Intrinsic::spv_resource_getdimensions_xyz: {
5530 return selectGetDimensionsIntrinsic(ResVReg, ResType, I);
5531 }
5532 case Intrinsic::spv_resource_getdimensions_levels_x:
5533 case Intrinsic::spv_resource_getdimensions_levels_xy:
5534 case Intrinsic::spv_resource_getdimensions_levels_xyz: {
5535 return selectGetDimensionsLevelsIntrinsic(ResVReg, ResType, I);
5536 }
5537 case Intrinsic::spv_resource_getdimensions_ms_xy:
5538 case Intrinsic::spv_resource_getdimensions_ms_xyz: {
5539 return selectGetDimensionsMSIntrinsic(ResVReg, ResType, I);
5540 }
5541 case Intrinsic::spv_resource_calculate_lod:
5542 case Intrinsic::spv_resource_calculate_lod_unclamped:
5543 return selectCalculateLodIntrinsic(ResVReg, ResType, I);
5544 case Intrinsic::spv_resource_sample:
5545 case Intrinsic::spv_resource_sample_clamp:
5546 return selectSampleBasicIntrinsic(ResVReg, ResType, I);
5547 case Intrinsic::spv_resource_samplebias:
5548 case Intrinsic::spv_resource_samplebias_clamp:
5549 return selectSampleBiasIntrinsic(ResVReg, ResType, I);
5550 case Intrinsic::spv_resource_samplegrad:
5551 case Intrinsic::spv_resource_samplegrad_clamp:
5552 return selectSampleGradIntrinsic(ResVReg, ResType, I);
5553 case Intrinsic::spv_resource_samplelevel:
5554 return selectSampleLevelIntrinsic(ResVReg, ResType, I);
5555 case Intrinsic::spv_resource_samplecmp:
5556 case Intrinsic::spv_resource_samplecmp_clamp:
5557 return selectSampleCmpIntrinsic(ResVReg, ResType, I);
5558 case Intrinsic::spv_resource_samplecmplevelzero:
5559 return selectSampleCmpLevelZeroIntrinsic(ResVReg, ResType, I);
5560 case Intrinsic::spv_resource_gather:
5561 case Intrinsic::spv_resource_gather_cmp:
5562 return selectGatherIntrinsic(ResVReg, ResType, I);
5563 case Intrinsic::spv_resource_getbasepointer:
5564 case Intrinsic::spv_resource_getpointer: {
5565 return selectResourceGetPointer(ResVReg, ResType, I);
5566 }
5567 case Intrinsic::spv_pushconstant_getpointer: {
5568 return selectPushConstantGetPointer(ResVReg, ResType, I);
5569 }
5570 case Intrinsic::spv_discard: {
5571 return selectDiscard(ResVReg, ResType, I);
5572 }
5573 case Intrinsic::spv_resource_nonuniformindex: {
5574 return selectResourceNonUniformIndex(ResVReg, ResType, I);
5575 }
5576 case Intrinsic::spv_unpackhalf2x16: {
5577 return selectExtInst(ResVReg, ResType, I, GLInst: GL::UnpackHalf2x16);
5578 }
5579 case Intrinsic::spv_packhalf2x16: {
5580 return selectExtInst(ResVReg, ResType, I, GLInst: GL::PackHalf2x16);
5581 }
5582 case Intrinsic::spv_ddx:
5583 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdx);
5584 case Intrinsic::spv_ddy:
5585 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdy);
5586 case Intrinsic::spv_ddx_coarse:
5587 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdxCoarse);
5588 case Intrinsic::spv_ddy_coarse:
5589 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdyCoarse);
5590 case Intrinsic::spv_ddx_fine:
5591 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdxFine);
5592 case Intrinsic::spv_ddy_fine:
5593 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpDPdyFine);
5594 case Intrinsic::spv_fwidth:
5595 return selectDerivativeInst(ResVReg, ResType, I, DPdOpCode: SPIRV::OpFwidth);
5596 case Intrinsic::spv_masked_gather:
5597 if (STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_masked_gather_scatter))
5598 return selectMaskedGather(ResVReg, ResType, I);
5599 return diagnoseUnsupported(
5600 I, Msg: "llvm.masked.gather requires SPV_INTEL_masked_gather_scatter");
5601 case Intrinsic::spv_masked_scatter:
5602 if (STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_masked_gather_scatter))
5603 return selectMaskedScatter(I);
5604 return diagnoseUnsupported(
5605 I, Msg: "llvm.masked.scatter requires SPV_INTEL_masked_gather_scatter");
5606 case Intrinsic::returnaddress:
5607 case Intrinsic::frameaddress: {
5608 // SPIR-V does not have a stack or return address. Lower to null.
5609 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpConstantNull))
5610 .addDef(RegNo: ResVReg)
5611 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
5612 MIB.constrainAllUses(TII, TRI, RBI);
5613 return true;
5614 }
5615 default:
5616 return diagnoseUnsupported(I, Msg: "intrinsic selection not implemented.");
5617 }
5618 return true;
5619}
5620
5621bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
5622 SPIRVTypeInst ResType,
5623 MachineInstr &I) const {
5624 // The images need to be loaded in the same basic block as their use. We defer
5625 // loading the image to the intrinsic that uses it.
5626 if (ResType->getOpcode() == SPIRV::OpTypeImage)
5627 return true;
5628
5629 return loadHandleBeforePosition(HandleReg&: ResVReg, ResType: GR.getSPIRVTypeForVReg(VReg: ResVReg),
5630 HandleDef&: *cast<GIntrinsic>(Val: &I), Pos&: I);
5631}
5632
5633bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
5634 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
5635 auto &Intr = cast<GIntrinsic>(Val&: I);
5636 assert(Intr.getIntrinsicID() ==
5637 Intrinsic::spv_resource_counterhandlefrombinding);
5638
5639 // Extract information from the intrinsic call.
5640 Register MainHandleReg = Intr.getOperand(i: 2).getReg();
5641 auto *MainHandleDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: MainHandleReg));
5642 assert(MainHandleDef->getIntrinsicID() ==
5643 Intrinsic::spv_resource_handlefrombinding);
5644
5645 uint32_t Set = getIConstVal(ConstReg: Intr.getOperand(i: 4).getReg(), MRI);
5646 uint32_t Binding = getIConstVal(ConstReg: Intr.getOperand(i: 3).getReg(), MRI);
5647 uint32_t ArraySize = getIConstVal(ConstReg: MainHandleDef->getOperand(i: 4).getReg(), MRI);
5648 Register IndexReg = MainHandleDef->getOperand(i: 5).getReg();
5649 std::string CounterName =
5650 getStringValueFromReg(Reg: MainHandleDef->getOperand(i: 6).getReg(), MRI&: *MRI) +
5651 ".counter";
5652
5653 // Create the counter variable.
5654 MachineIRBuilder MIRBuilder(I);
5655 Register CounterVarReg =
5656 buildPointerToResource(ResType: SPIRVTypeInst(GR.getPointeeType(PtrType: ResType)),
5657 SC: GR.getPointerStorageClass(Type: ResType), Set, Binding,
5658 ArraySize, IndexReg, Name: CounterName, MIRBuilder);
5659
5660 return BuildCOPY(DestReg: ResVReg, SrcReg: CounterVarReg, I);
5661}
5662
5663bool SPIRVInstructionSelector::selectUpdateCounter(Register &ResVReg,
5664 SPIRVTypeInst ResType,
5665 MachineInstr &I) const {
5666 auto &Intr = cast<GIntrinsic>(Val&: I);
5667 assert(Intr.getIntrinsicID() == Intrinsic::spv_resource_updatecounter);
5668
5669 Register CounterHandleReg = Intr.getOperand(i: 2).getReg();
5670 Register IncrReg = Intr.getOperand(i: 3).getReg();
5671
5672 // The counter handle is a pointer to the counter variable (which is a struct
5673 // containing an i32). We need to get a pointer to that i32 member to do the
5674 // atomic operation.
5675#ifndef NDEBUG
5676 SPIRVTypeInst CounterVarType = GR.getSPIRVTypeForVReg(CounterHandleReg);
5677 SPIRVTypeInst CounterVarPointeeType = GR.getPointeeType(CounterVarType);
5678 assert(CounterVarPointeeType &&
5679 CounterVarPointeeType->getOpcode() == SPIRV::OpTypeStruct &&
5680 "Counter variable must be a struct");
5681 assert(GR.getPointerStorageClass(CounterVarType) ==
5682 SPIRV::StorageClass::StorageBuffer &&
5683 "Counter variable must be in the storage buffer storage class");
5684 assert(CounterVarPointeeType->getNumOperands() == 2 &&
5685 "Counter variable must have exactly 1 member in the struct");
5686 const SPIRVTypeInst MemberType =
5687 GR.getSPIRVTypeForVReg(CounterVarPointeeType->getOperand(1).getReg());
5688 assert(MemberType->getOpcode() == SPIRV::OpTypeInt &&
5689 "Counter variable struct must have a single i32 member");
5690#endif
5691
5692 // The struct has a single i32 member.
5693 MachineIRBuilder MIRBuilder(I);
5694 const Type *LLVMIntType =
5695 Type::getInt32Ty(C&: I.getMF()->getFunction().getContext());
5696
5697 SPIRVTypeInst IntPtrType = GR.getOrCreateSPIRVPointerType(
5698 BaseType: LLVMIntType, MIRBuilder, SC: SPIRV::StorageClass::StorageBuffer);
5699
5700 Register Zero = buildI32Constant(Val: 0, I);
5701
5702 Register PtrToCounter =
5703 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: IntPtrType));
5704 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAccessChain))
5705 .addDef(RegNo: PtrToCounter)
5706 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: IntPtrType))
5707 .addUse(RegNo: CounterHandleReg)
5708 .addUse(RegNo: Zero)
5709 .constrainAllUses(TII, TRI, RBI);
5710
5711 // For UAV/SSBO counters, the scope is Device. The counter variable is not
5712 // used as a flag. So the memory semantics can be None.
5713 Register Scope = buildI32Constant(Val: SPIRV::Scope::Device, I);
5714 Register Semantics = buildI32Constant(Val: SPIRV::MemorySemantics::None, I);
5715
5716 int64_t IncrVal = getIConstValSext(ConstReg: IncrReg, MRI);
5717 Register Incr = buildI32Constant(Val: static_cast<uint32_t>(IncrVal), I);
5718
5719 Register AtomicRes = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
5720 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAtomicIAdd))
5721 .addDef(RegNo: AtomicRes)
5722 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5723 .addUse(RegNo: PtrToCounter)
5724 .addUse(RegNo: Scope)
5725 .addUse(RegNo: Semantics)
5726 .addUse(RegNo: Incr)
5727 .constrainAllUses(TII, TRI, RBI);
5728 if (IncrVal >= 0) {
5729 return BuildCOPY(DestReg: ResVReg, SrcReg: AtomicRes, I);
5730 }
5731
5732 // In HLSL, IncrementCounter returns the value *before* the increment, while
5733 // DecrementCounter returns the value *after* the decrement. Both are lowered
5734 // to the same atomic intrinsic which returns the value *before* the
5735 // operation. So for decrements (negative IncrVal), we must subtract the
5736 // increment value from the result to get the post-decrement value.
5737 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpIAddS))
5738 .addDef(RegNo: ResVReg)
5739 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5740 .addUse(RegNo: AtomicRes)
5741 .addUse(RegNo: Incr)
5742 .constrainAllUses(TII, TRI, RBI);
5743 return true;
5744}
5745bool SPIRVInstructionSelector::selectReadImageIntrinsic(Register &ResVReg,
5746 SPIRVTypeInst ResType,
5747 MachineInstr &I) const {
5748
5749 // If the load of the image is in a different basic block, then
5750 // this will generate invalid code. A proper solution is to move
5751 // the OpLoad from selectHandleFromBinding here. However, to do
5752 // that we will need to change the return type of the intrinsic.
5753 // We will do that when we can, but for now trying to move forward with other
5754 // issues.
5755 Register ImageReg = I.getOperand(i: 2).getReg();
5756 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
5757 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
5758 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
5759 HandleDef&: *ImageDef, Pos&: I)) {
5760 return false;
5761 }
5762
5763 Register IdxReg = I.getOperand(i: 3).getReg();
5764 DebugLoc Loc = I.getDebugLoc();
5765 MachineInstr &Pos = I;
5766
5767 return generateImageReadOrFetch(ResVReg, ResType, ImageReg: NewImageReg, IdxReg, Loc,
5768 Pos);
5769}
5770
5771bool SPIRVInstructionSelector::generateSampleImage(
5772 Register ResVReg, SPIRVTypeInst ResType, Register ImageReg,
5773 Register SamplerReg, Register CoordinateReg, const ImageOperands &ImOps,
5774 DebugLoc Loc, MachineInstr &Pos) const {
5775 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
5776 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
5777 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
5778 HandleDef&: *ImageDef, Pos)) {
5779 return false;
5780 }
5781
5782 auto *SamplerDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: SamplerReg));
5783 Register NewSamplerReg =
5784 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: SamplerReg));
5785 if (!loadHandleBeforePosition(HandleReg&: NewSamplerReg,
5786 ResType: GR.getSPIRVTypeForVReg(VReg: SamplerReg), HandleDef&: *SamplerDef,
5787 Pos)) {
5788 return false;
5789 }
5790
5791 MachineIRBuilder MIRBuilder(Pos);
5792 SPIRVTypeInst SampledImageType = GR.getOrCreateOpTypeSampledImage(
5793 ImageType: GR.getSPIRVTypeForVReg(VReg: ImageReg), MIRBuilder);
5794 Register SampledImageReg =
5795 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: SampledImageType));
5796
5797 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode: SPIRV::OpSampledImage))
5798 .addDef(RegNo: SampledImageReg)
5799 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SampledImageType))
5800 .addUse(RegNo: NewImageReg)
5801 .addUse(RegNo: NewSamplerReg)
5802 .constrainAllUses(TII, TRI, RBI);
5803
5804 bool IsExplicitLod = ImOps.GradX.has_value() || ImOps.GradY.has_value() ||
5805 ImOps.Lod.has_value();
5806 unsigned Opcode = IsExplicitLod ? SPIRV::OpImageSampleExplicitLod
5807 : SPIRV::OpImageSampleImplicitLod;
5808 if (ImOps.Compare)
5809 Opcode = IsExplicitLod ? SPIRV::OpImageSampleDrefExplicitLod
5810 : SPIRV::OpImageSampleDrefImplicitLod;
5811
5812 auto MIB = BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode))
5813 .addDef(RegNo: ResVReg)
5814 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5815 .addUse(RegNo: SampledImageReg)
5816 .addUse(RegNo: CoordinateReg);
5817
5818 if (ImOps.Compare)
5819 MIB.addUse(RegNo: *ImOps.Compare);
5820
5821 uint32_t ImageOperands = 0;
5822 if (ImOps.Bias)
5823 ImageOperands |= SPIRV::ImageOperand::Bias;
5824 if (ImOps.Lod)
5825 ImageOperands |= SPIRV::ImageOperand::Lod;
5826 if (ImOps.GradX && ImOps.GradY)
5827 ImageOperands |= SPIRV::ImageOperand::Grad;
5828 if (ImOps.Offset && !isScalarOrVectorIntConstantZero(Reg: *ImOps.Offset)) {
5829 if (isConstReg(MRI, OpReg: *ImOps.Offset))
5830 ImageOperands |= SPIRV::ImageOperand::ConstOffset;
5831 else {
5832 Pos.emitGenericError(
5833 ErrMsg: "Non-constant offsets are not supported in sample instructions.");
5834 return false;
5835 }
5836 }
5837 if (ImOps.MinLod)
5838 ImageOperands |= SPIRV::ImageOperand::MinLod;
5839
5840 if (ImageOperands != 0) {
5841 MIB.addImm(Val: ImageOperands);
5842 if (ImageOperands & SPIRV::ImageOperand::Bias)
5843 MIB.addUse(RegNo: *ImOps.Bias);
5844 if (ImageOperands & SPIRV::ImageOperand::Lod)
5845 MIB.addUse(RegNo: *ImOps.Lod);
5846 if (ImageOperands & SPIRV::ImageOperand::Grad) {
5847 MIB.addUse(RegNo: *ImOps.GradX);
5848 MIB.addUse(RegNo: *ImOps.GradY);
5849 }
5850 if (ImageOperands &
5851 (SPIRV::ImageOperand::ConstOffset | SPIRV::ImageOperand::Offset))
5852 MIB.addUse(RegNo: *ImOps.Offset);
5853 if (ImageOperands & SPIRV::ImageOperand::MinLod)
5854 MIB.addUse(RegNo: *ImOps.MinLod);
5855 }
5856
5857 MIB.constrainAllUses(TII, TRI, RBI);
5858 return true;
5859}
5860
5861bool SPIRVInstructionSelector::selectImageQuerySize(
5862 Register ImageReg, Register &ResVReg, MachineInstr &I,
5863 std::optional<Register> LodReg) const {
5864 unsigned Opcode =
5865 LodReg ? SPIRV::OpImageQuerySizeLod : SPIRV::OpImageQuerySize;
5866 SPIRVTypeInst ImageType = GR.getSPIRVTypeForVReg(VReg: ImageReg);
5867 assert(ImageType && ImageType->getOpcode() == SPIRV::OpTypeImage &&
5868 "ImageReg is not an image type.");
5869
5870 auto Dim = static_cast<SPIRV::Dim::Dim>(ImageType->getOperand(i: 2).getImm());
5871 bool IsArray = ImageType->getOperand(i: 4).getImm() != 0;
5872 unsigned NumComponents = 0;
5873 switch (Dim) {
5874 case SPIRV::Dim::DIM_1D:
5875 case SPIRV::Dim::DIM_Buffer:
5876 NumComponents = IsArray ? 2 : 1;
5877 break;
5878 case SPIRV::Dim::DIM_2D:
5879 case SPIRV::Dim::DIM_Cube:
5880 case SPIRV::Dim::DIM_Rect:
5881 NumComponents = IsArray ? 3 : 2;
5882 break;
5883 case SPIRV::Dim::DIM_3D:
5884 NumComponents = 3;
5885 break;
5886 default:
5887 I.emitGenericError(ErrMsg: "Unsupported image dimension for OpImageQuerySize.");
5888 return false;
5889 }
5890
5891 SPIRVTypeInst I32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
5892 SPIRVTypeInst ResType =
5893 NumComponents == 1
5894 ? I32Ty
5895 : GR.getOrCreateSPIRVVectorType(BaseType: I32Ty, NumElements: NumComponents, I, TII);
5896
5897 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
5898 .addDef(RegNo: ResVReg)
5899 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5900 .addUse(RegNo: ImageReg);
5901 if (LodReg)
5902 MIB.addUse(RegNo: *LodReg);
5903 MIB.constrainAllUses(TII, TRI, RBI);
5904 return true;
5905}
5906
5907bool SPIRVInstructionSelector::selectGetDimensionsIntrinsic(
5908 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
5909 Register ImageReg = I.getOperand(i: 2).getReg();
5910 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
5911 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
5912 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
5913 HandleDef&: *ImageDef, Pos&: I)) {
5914 return false;
5915 }
5916 return selectImageQuerySize(ImageReg: NewImageReg, ResVReg, I);
5917}
5918
5919bool SPIRVInstructionSelector::selectGetDimensionsLevelsIntrinsic(
5920 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
5921 Register ImageReg = I.getOperand(i: 2).getReg();
5922 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
5923 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
5924 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
5925 HandleDef&: *ImageDef, Pos&: I)) {
5926 return false;
5927 }
5928
5929 Register SizeReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
5930 Register LodReg = I.getOperand(i: 3).getReg();
5931
5932 assert(GR.getSPIRVTypeForVReg(NewImageReg)->getOperand(6).getImm() == 1 &&
5933 "OpImageQuerySizeLod and OpImageQueryLevels require a sampled image");
5934
5935 if (!selectImageQuerySize(ImageReg: NewImageReg, ResVReg&: SizeReg, I, LodReg)) {
5936 return false;
5937 }
5938
5939 SPIRVTypeInst I32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
5940 Register LevelsReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
5941 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
5942 MCID: TII.get(Opcode: SPIRV::OpImageQueryLevels))
5943 .addDef(RegNo: LevelsReg)
5944 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: I32Ty))
5945 .addUse(RegNo: NewImageReg)
5946 .constrainAllUses(TII, TRI, RBI);
5947
5948 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
5949 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
5950 .addDef(RegNo: ResVReg)
5951 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5952 .addUse(RegNo: SizeReg)
5953 .addUse(RegNo: LevelsReg)
5954 .constrainAllUses(TII, TRI, RBI);
5955
5956 return true;
5957}
5958
5959bool SPIRVInstructionSelector::selectGetDimensionsMSIntrinsic(
5960 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
5961 Register ImageReg = I.getOperand(i: 2).getReg();
5962 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
5963 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
5964 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
5965 HandleDef&: *ImageDef, Pos&: I)) {
5966 return false;
5967 }
5968
5969 Register SizeReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
5970
5971 assert(GR.getSPIRVTypeForVReg(NewImageReg)->getOperand(5).getImm() == 1 &&
5972 "OpImageQuerySamples requires a multisampled image");
5973
5974 if (!selectImageQuerySize(ImageReg: NewImageReg, ResVReg&: SizeReg, I)) {
5975 return false;
5976 }
5977
5978 Register SamplesReg = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
5979
5980 SPIRVTypeInst I32Ty = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
5981 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
5982 MCID: TII.get(Opcode: SPIRV::OpImageQuerySamples))
5983 .addDef(RegNo: SamplesReg)
5984 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: I32Ty))
5985 .addUse(RegNo: NewImageReg)
5986 .constrainAllUses(TII, TRI, RBI);
5987
5988 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
5989 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
5990 .addDef(RegNo: ResVReg)
5991 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
5992 .addUse(RegNo: SizeReg)
5993 .addUse(RegNo: SamplesReg)
5994 .constrainAllUses(TII, TRI, RBI);
5995
5996 return true;
5997}
5998
5999bool SPIRVInstructionSelector::selectCalculateLodIntrinsic(
6000 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6001 Register ImageReg = I.getOperand(i: 2).getReg();
6002 Register SamplerReg = I.getOperand(i: 3).getReg();
6003 Register CoordinateReg = I.getOperand(i: 4).getReg();
6004
6005 auto *ImageDef = dyn_cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6006 if (!ImageDef)
6007 return false;
6008 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6009 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6010 HandleDef&: *ImageDef, Pos&: I)) {
6011 return false;
6012 }
6013
6014 auto *SamplerDef = dyn_cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: SamplerReg));
6015 if (!SamplerDef)
6016 return false;
6017 Register NewSamplerReg =
6018 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: SamplerReg));
6019 if (!loadHandleBeforePosition(
6020 HandleReg&: NewSamplerReg, ResType: GR.getSPIRVTypeForVReg(VReg: SamplerReg), HandleDef&: *SamplerDef, Pos&: I)) {
6021 return false;
6022 }
6023
6024 MachineIRBuilder MIRBuilder(I);
6025 SPIRVTypeInst SampledImageType = GR.getOrCreateOpTypeSampledImage(
6026 ImageType: GR.getSPIRVTypeForVReg(VReg: ImageReg), MIRBuilder);
6027 Register SampledImageReg =
6028 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: SampledImageType));
6029
6030 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSampledImage))
6031 .addDef(RegNo: SampledImageReg)
6032 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SampledImageType))
6033 .addUse(RegNo: NewImageReg)
6034 .addUse(RegNo: NewSamplerReg)
6035 .constrainAllUses(TII, TRI, RBI);
6036
6037 SPIRVTypeInst Vec2Ty = GR.getOrCreateSPIRVVectorType(BaseType: ResType, NumElements: 2, I, TII);
6038 Register QueryResultReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: Vec2Ty));
6039
6040 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpImageQueryLod))
6041 .addDef(RegNo: QueryResultReg)
6042 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Vec2Ty))
6043 .addUse(RegNo: SampledImageReg)
6044 .addUse(RegNo: CoordinateReg)
6045 .constrainAllUses(TII, TRI, RBI);
6046
6047 unsigned ExtractedIndex =
6048 cast<GIntrinsic>(Val&: I).getIntrinsicID() ==
6049 Intrinsic::spv_resource_calculate_lod_unclamped
6050 ? 1
6051 : 0;
6052
6053 MachineInstrBuilder MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6054 MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
6055 .addDef(RegNo: ResVReg)
6056 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6057 .addUse(RegNo: QueryResultReg)
6058 .addImm(Val: ExtractedIndex);
6059
6060 MIB.constrainAllUses(TII, TRI, RBI);
6061 return true;
6062}
6063
6064bool SPIRVInstructionSelector::selectSampleBasicIntrinsic(
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 if (I.getNumOperands() > 5)
6071 ImOps.Offset = I.getOperand(i: 5).getReg();
6072 if (I.getNumOperands() > 6)
6073 ImOps.MinLod = I.getOperand(i: 6).getReg();
6074 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6075 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6076}
6077
6078bool SPIRVInstructionSelector::selectSampleBiasIntrinsic(
6079 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6080 Register ImageReg = I.getOperand(i: 2).getReg();
6081 Register SamplerReg = I.getOperand(i: 3).getReg();
6082 Register CoordinateReg = I.getOperand(i: 4).getReg();
6083 ImageOperands ImOps;
6084 ImOps.Bias = I.getOperand(i: 5).getReg();
6085 if (I.getNumOperands() > 6)
6086 ImOps.Offset = I.getOperand(i: 6).getReg();
6087 if (I.getNumOperands() > 7)
6088 ImOps.MinLod = I.getOperand(i: 7).getReg();
6089 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6090 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6091}
6092
6093bool SPIRVInstructionSelector::selectSampleGradIntrinsic(
6094 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6095 Register ImageReg = I.getOperand(i: 2).getReg();
6096 Register SamplerReg = I.getOperand(i: 3).getReg();
6097 Register CoordinateReg = I.getOperand(i: 4).getReg();
6098 ImageOperands ImOps;
6099 ImOps.GradX = I.getOperand(i: 5).getReg();
6100 ImOps.GradY = I.getOperand(i: 6).getReg();
6101 if (I.getNumOperands() > 7)
6102 ImOps.Offset = I.getOperand(i: 7).getReg();
6103 if (I.getNumOperands() > 8)
6104 ImOps.MinLod = I.getOperand(i: 8).getReg();
6105 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6106 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6107}
6108
6109bool SPIRVInstructionSelector::selectSampleLevelIntrinsic(
6110 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6111 Register ImageReg = I.getOperand(i: 2).getReg();
6112 Register SamplerReg = I.getOperand(i: 3).getReg();
6113 Register CoordinateReg = I.getOperand(i: 4).getReg();
6114 ImageOperands ImOps;
6115 ImOps.Lod = I.getOperand(i: 5).getReg();
6116 if (I.getNumOperands() > 6)
6117 ImOps.Offset = I.getOperand(i: 6).getReg();
6118 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6119 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6120}
6121
6122bool SPIRVInstructionSelector::selectSampleCmpIntrinsic(Register &ResVReg,
6123 SPIRVTypeInst ResType,
6124 MachineInstr &I) const {
6125 Register ImageReg = I.getOperand(i: 2).getReg();
6126 Register SamplerReg = I.getOperand(i: 3).getReg();
6127 Register CoordinateReg = I.getOperand(i: 4).getReg();
6128 ImageOperands ImOps;
6129 ImOps.Compare = I.getOperand(i: 5).getReg();
6130 if (I.getNumOperands() > 6)
6131 ImOps.Offset = I.getOperand(i: 6).getReg();
6132 if (I.getNumOperands() > 7)
6133 ImOps.MinLod = I.getOperand(i: 7).getReg();
6134 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6135 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6136}
6137
6138bool SPIRVInstructionSelector::selectLoadLevelIntrinsic(Register &ResVReg,
6139 SPIRVTypeInst ResType,
6140 MachineInstr &I) const {
6141 Register ImageReg = I.getOperand(i: 2).getReg();
6142 Register CoordinateReg = I.getOperand(i: 3).getReg();
6143 Register LodReg = I.getOperand(i: 4).getReg();
6144
6145 ImageOperands ImOps;
6146 ImOps.Lod = LodReg;
6147 if (I.getNumOperands() > 5)
6148 ImOps.Offset = I.getOperand(i: 5).getReg();
6149
6150 auto *ImageDef = dyn_cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6151 if (!ImageDef)
6152 return false;
6153
6154 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6155 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6156 HandleDef&: *ImageDef, Pos&: I)) {
6157 return false;
6158 }
6159
6160 return generateImageReadOrFetch(ResVReg, ResType, ImageReg: NewImageReg, IdxReg: CoordinateReg,
6161 Loc: I.getDebugLoc(), Pos&: I, ImOps: &ImOps);
6162}
6163
6164bool SPIRVInstructionSelector::selectSampleCmpLevelZeroIntrinsic(
6165 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6166 Register ImageReg = I.getOperand(i: 2).getReg();
6167 Register SamplerReg = I.getOperand(i: 3).getReg();
6168 Register CoordinateReg = I.getOperand(i: 4).getReg();
6169 ImageOperands ImOps;
6170 ImOps.Compare = I.getOperand(i: 5).getReg();
6171 if (I.getNumOperands() > 6)
6172 ImOps.Offset = I.getOperand(i: 6).getReg();
6173 SPIRVTypeInst FloatTy = GR.getOrCreateSPIRVFloatType(BitWidth: 32, I, TII);
6174 ImOps.Lod = GR.getOrCreateConstFP(Val: APFloat(0.0f), I, SpvType: FloatTy, TII);
6175 return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
6176 CoordinateReg, ImOps, Loc: I.getDebugLoc(), Pos&: I);
6177}
6178
6179bool SPIRVInstructionSelector::selectGatherIntrinsic(Register &ResVReg,
6180 SPIRVTypeInst ResType,
6181 MachineInstr &I) const {
6182 Register ImageReg = I.getOperand(i: 2).getReg();
6183 Register SamplerReg = I.getOperand(i: 3).getReg();
6184 Register CoordinateReg = I.getOperand(i: 4).getReg();
6185 SPIRVTypeInst ImageType = GR.getSPIRVTypeForVReg(VReg: ImageReg);
6186 assert(ImageType && ImageType->getOpcode() == SPIRV::OpTypeImage &&
6187 "ImageReg is not an image type.");
6188
6189 Register ComponentOrCompareReg;
6190 Register OffsetReg;
6191
6192 ComponentOrCompareReg = I.getOperand(i: 5).getReg();
6193 OffsetReg = I.getOperand(i: 6).getReg();
6194 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6195 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6196 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: ImageType, HandleDef&: *ImageDef, Pos&: I)) {
6197 return false;
6198 }
6199
6200 auto Dim = static_cast<SPIRV::Dim::Dim>(ImageType->getOperand(i: 2).getImm());
6201 if (Dim != SPIRV::Dim::DIM_2D && Dim != SPIRV::Dim::DIM_Cube &&
6202 Dim != SPIRV::Dim::DIM_Rect) {
6203 I.emitGenericError(
6204 ErrMsg: "Gather operations are only supported for 2D, Cube, and Rect images.");
6205 return false;
6206 }
6207
6208 auto *SamplerDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: SamplerReg));
6209 Register NewSamplerReg =
6210 MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: SamplerReg));
6211 if (!loadHandleBeforePosition(
6212 HandleReg&: NewSamplerReg, ResType: GR.getSPIRVTypeForVReg(VReg: SamplerReg), HandleDef&: *SamplerDef, Pos&: I)) {
6213 return false;
6214 }
6215
6216 MachineIRBuilder MIRBuilder(I);
6217 SPIRVTypeInst SampledImageType =
6218 GR.getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
6219 Register SampledImageReg =
6220 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: SampledImageType));
6221
6222 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpSampledImage))
6223 .addDef(RegNo: SampledImageReg)
6224 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: SampledImageType))
6225 .addUse(RegNo: NewImageReg)
6226 .addUse(RegNo: NewSamplerReg)
6227 .constrainAllUses(TII, TRI, RBI);
6228
6229 auto IntrId = cast<GIntrinsic>(Val&: I).getIntrinsicID();
6230 bool IsGatherCmp = IntrId == Intrinsic::spv_resource_gather_cmp;
6231 unsigned Opcode =
6232 IsGatherCmp ? SPIRV::OpImageDrefGather : SPIRV::OpImageGather;
6233
6234 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
6235 .addDef(RegNo: ResVReg)
6236 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6237 .addUse(RegNo: SampledImageReg)
6238 .addUse(RegNo: CoordinateReg)
6239 .addUse(RegNo: ComponentOrCompareReg);
6240
6241 uint32_t ImageOperands = 0;
6242 if (OffsetReg && !isScalarOrVectorIntConstantZero(Reg: OffsetReg)) {
6243 if (Dim == SPIRV::Dim::DIM_Cube) {
6244 I.emitGenericError(
6245 ErrMsg: "Gather operations with offset are not supported for Cube images.");
6246 return false;
6247 }
6248 if (isConstReg(MRI, OpReg: OffsetReg))
6249 ImageOperands |= SPIRV::ImageOperand::ConstOffset;
6250 else {
6251 ImageOperands |= SPIRV::ImageOperand::Offset;
6252 }
6253 }
6254
6255 if (ImageOperands != 0) {
6256 MIB.addImm(Val: ImageOperands);
6257 if (ImageOperands &
6258 (SPIRV::ImageOperand::ConstOffset | SPIRV::ImageOperand::Offset))
6259 MIB.addUse(RegNo: OffsetReg);
6260 }
6261
6262 MIB.constrainAllUses(TII, TRI, RBI);
6263 return true;
6264}
6265
6266bool SPIRVInstructionSelector::generateImageReadOrFetch(
6267 Register &ResVReg, SPIRVTypeInst ResType, Register ImageReg,
6268 Register IdxReg, DebugLoc Loc, MachineInstr &Pos,
6269 const ImageOperands *ImOps) const {
6270 SPIRVTypeInst ImageType = GR.getSPIRVTypeForVReg(VReg: ImageReg);
6271 assert(ImageType && ImageType->getOpcode() == SPIRV::OpTypeImage &&
6272 "ImageReg is not an image type.");
6273
6274 bool IsSignedInteger =
6275 sampledTypeIsSignedInteger(HandleType: GR.getTypeForSPIRVType(Ty: ImageType));
6276 // Check if the "sampled" operand of the image type is 1.
6277 // https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpImageFetch
6278 auto SampledOp = ImageType->getOperand(i: 6);
6279 bool IsFetch = (SampledOp.getImm() == 1);
6280
6281 auto AddOperands = [&](MachineInstrBuilder &MIB) {
6282 uint32_t ImageOperandsMask = 0;
6283 if (IsSignedInteger)
6284 ImageOperandsMask |= 0x1000; // SignExtend
6285
6286 if (IsFetch && ImOps) {
6287 if (ImOps->Lod)
6288 ImageOperandsMask |= SPIRV::ImageOperand::Lod;
6289 if (ImOps->Offset && !isScalarOrVectorIntConstantZero(Reg: *ImOps->Offset)) {
6290 if (isConstReg(MRI, OpReg: *ImOps->Offset))
6291 ImageOperandsMask |= SPIRV::ImageOperand::ConstOffset;
6292 else
6293 ImageOperandsMask |= SPIRV::ImageOperand::Offset;
6294 }
6295 }
6296
6297 if (ImageOperandsMask != 0) {
6298 MIB.addImm(Val: ImageOperandsMask);
6299 if (IsFetch && ImOps) {
6300 if (ImOps->Lod)
6301 MIB.addUse(RegNo: *ImOps->Lod);
6302 if (ImOps->Offset &&
6303 (ImageOperandsMask &
6304 (SPIRV::ImageOperand::Offset | SPIRV::ImageOperand::ConstOffset)))
6305 MIB.addUse(RegNo: *ImOps->Offset);
6306 }
6307 }
6308 };
6309
6310 uint64_t ResultSize = GR.getScalarOrVectorComponentCount(Type: ResType);
6311
6312 // A wide element (e.g. int64_t2) is emulated with a narrower packed image, so
6313 // its sampled type is different from the result.
6314 SPIRVTypeInst SampledType =
6315 GR.getSPIRVTypeForVReg(VReg: ImageType->getOperand(i: 1).getReg());
6316 bool IsPacked = SampledType != GR.getScalarOrVectorComponentType(Type: ResType);
6317 SPIRVTypeInst ReadType =
6318 widenTypeToVec4(Type: IsPacked ? SampledType : ResType, I&: Pos);
6319 bool ReadTypeMatchesResult = ReadType == ResType;
6320 // Read directly into the result, or into a temporary to bitcast/extract.
6321 Register ReadReg = ReadTypeMatchesResult
6322 ? ResVReg
6323 : MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ReadType));
6324
6325 auto BMI =
6326 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc,
6327 MCID: TII.get(Opcode: IsFetch ? SPIRV::OpImageFetch : SPIRV::OpImageRead))
6328 .addDef(RegNo: ReadReg)
6329 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ReadType))
6330 .addUse(RegNo: ImageReg)
6331 .addUse(RegNo: IdxReg);
6332 AddOperands(BMI);
6333 BMI.constrainAllUses(TII, TRI, RBI);
6334
6335 if (ReadTypeMatchesResult)
6336 return true;
6337
6338 if (IsPacked) {
6339 // Reinterpret the packed vector as the wide result type.
6340 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode: SPIRV::OpBitcast))
6341 .addDef(RegNo: ResVReg)
6342 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6343 .addUse(RegNo: ReadReg)
6344 .constrainAllUses(TII, TRI, RBI);
6345 return true;
6346 }
6347
6348 if (ResultSize == 1) {
6349 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: Loc, MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
6350 .addDef(RegNo: ResVReg)
6351 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6352 .addUse(RegNo: ReadReg)
6353 .addImm(Val: 0)
6354 .constrainAllUses(TII, TRI, RBI);
6355 return true;
6356 }
6357 return extractSubvector(ResVReg, ResType, ReadReg, InsertionPoint&: Pos);
6358}
6359
6360bool SPIRVInstructionSelector::selectResourceGetPointer(Register &ResVReg,
6361 SPIRVTypeInst ResType,
6362 MachineInstr &I) const {
6363 Register ResourcePtr = I.getOperand(i: 2).getReg();
6364 SPIRVTypeInst RegType = GR.getSPIRVTypeForVReg(VReg: ResourcePtr, MF: I.getMF());
6365 if (RegType->getOpcode() == SPIRV::OpTypeImage) {
6366 // For texel buffers, the index into the image is part of the OpImageRead or
6367 // OpImageWrite instructions. So we will do nothing in this case. This
6368 // intrinsic will be combined with the load or store when selecting the load
6369 // or store.
6370 return true;
6371 }
6372
6373 assert(ResType->getOpcode() == SPIRV::OpTypePointer);
6374 MachineIRBuilder MIRBuilder(I);
6375
6376 Register ZeroReg =
6377 buildZerosVal(ResType: GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII), I);
6378 auto MIB =
6379 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAccessChain))
6380 .addDef(RegNo: ResVReg)
6381 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6382 .addUse(RegNo: ResourcePtr)
6383 .addUse(RegNo: ZeroReg);
6384
6385 if (I.getNumExplicitOperands() > 3) {
6386 Register IndexReg = I.getOperand(i: 3).getReg();
6387 MIB.addUse(RegNo: IndexReg);
6388 }
6389 MIB.constrainAllUses(TII, TRI, RBI);
6390 return true;
6391}
6392
6393bool SPIRVInstructionSelector::selectPushConstantGetPointer(
6394 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6395 MRI->replaceRegWith(FromReg: ResVReg, ToReg: I.getOperand(i: 2).getReg());
6396 return true;
6397}
6398
6399bool SPIRVInstructionSelector::selectResourceNonUniformIndex(
6400 Register &ResVReg, SPIRVTypeInst ResType, MachineInstr &I) const {
6401 Register ObjReg = I.getOperand(i: 2).getReg();
6402 if (!BuildCOPY(DestReg: ResVReg, SrcReg: ObjReg, I))
6403 return false;
6404
6405 buildOpDecorate(Reg: ResVReg, I, TII, Dec: SPIRV::Decoration::NonUniformEXT, DecArgs: {});
6406 // Check for the registers that use the index marked as non-uniform
6407 // and recursively mark them as non-uniform.
6408 // Per the spec, it's necessary that the final argument used for
6409 // load/store/sample/atomic must be decorated, so we need to propagate the
6410 // decoration through access chains and copies.
6411 // https://docs.vulkan.org/samples/latest/samples/extensions/descriptor_indexing/README.html#_when_to_use_non_uniform_indexing_qualifier
6412 decorateUsesAsNonUniform(NonUniformReg&: ResVReg);
6413 return true;
6414}
6415
6416void SPIRVInstructionSelector::decorateUsesAsNonUniform(
6417 Register &NonUniformReg) const {
6418 llvm::SmallVector<Register> WorkList = {NonUniformReg};
6419 while (WorkList.size() > 0) {
6420 Register CurrentReg = WorkList.back();
6421 WorkList.pop_back();
6422
6423 bool IsDecorated = false;
6424 for (MachineInstr &Use : MRI->use_instructions(Reg: CurrentReg)) {
6425 if (Use.getOpcode() == SPIRV::OpDecorate &&
6426 Use.getOperand(i: 1).getImm() == SPIRV::Decoration::NonUniformEXT) {
6427 IsDecorated = true;
6428 continue;
6429 }
6430 // Check if the instruction has the result register and add it to the
6431 // worklist.
6432 if (Use.getOperand(i: 0).isReg() && Use.getOperand(i: 0).isDef()) {
6433 Register ResultReg = Use.getOperand(i: 0).getReg();
6434 if (ResultReg == CurrentReg)
6435 continue;
6436 WorkList.push_back(Elt: ResultReg);
6437 }
6438 }
6439
6440 if (!IsDecorated) {
6441 buildOpDecorate(Reg: CurrentReg, I&: *MRI->getVRegDef(Reg: CurrentReg), TII,
6442 Dec: SPIRV::Decoration::NonUniformEXT, DecArgs: {});
6443 }
6444 }
6445}
6446
6447bool SPIRVInstructionSelector::extractSubvector(
6448 Register &ResVReg, SPIRVTypeInst ResType, Register &ReadReg,
6449 MachineInstr &InsertionPoint) const {
6450 SPIRVTypeInst InputType = GR.getResultType(VReg: ReadReg);
6451 [[maybe_unused]] uint64_t InputSize =
6452 GR.getScalarOrVectorComponentCount(Type: InputType);
6453 uint64_t ResultSize = GR.getScalarOrVectorComponentCount(Type: ResType);
6454 assert(InputSize > 1 && "The input must be a vector.");
6455 assert(ResultSize > 1 && "The result must be a vector.");
6456 assert(ResultSize < InputSize &&
6457 "Cannot extract more element than there are in the input.");
6458 SmallVector<Register> ComponentRegisters;
6459 SPIRVTypeInst ScalarType = GR.getScalarOrVectorComponentType(Type: ResType);
6460 const TargetRegisterClass *ScalarRegClass = GR.getRegClass(SpvType: ScalarType);
6461 for (uint64_t I = 0; I < ResultSize; I++) {
6462 Register ComponentReg = MRI->createVirtualRegister(RegClass: ScalarRegClass);
6463 BuildMI(BB&: *InsertionPoint.getParent(), I&: InsertionPoint,
6464 MIMD: InsertionPoint.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
6465 .addDef(RegNo: ComponentReg)
6466 .addUse(RegNo: ScalarType->getOperand(i: 0).getReg())
6467 .addUse(RegNo: ReadReg)
6468 .addImm(Val: I)
6469 .constrainAllUses(TII, TRI, RBI);
6470 ComponentRegisters.emplace_back(Args&: ComponentReg);
6471 }
6472
6473 MachineInstrBuilder MIB = BuildMI(BB&: *InsertionPoint.getParent(), I&: InsertionPoint,
6474 MIMD: InsertionPoint.getDebugLoc(),
6475 MCID: TII.get(Opcode: SPIRV::OpCompositeConstruct))
6476 .addDef(RegNo: ResVReg)
6477 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
6478
6479 for (Register ComponentReg : ComponentRegisters)
6480 MIB.addUse(RegNo: ComponentReg);
6481 MIB.constrainAllUses(TII, TRI, RBI);
6482 return true;
6483}
6484
6485bool SPIRVInstructionSelector::selectImageWriteIntrinsic(
6486 MachineInstr &I) const {
6487 // If the load of the image is in a different basic block, then
6488 // this will generate invalid code. A proper solution is to move
6489 // the OpLoad from selectHandleFromBinding here. However, to do
6490 // that we will need to change the return type of the intrinsic.
6491 // We will do that when we can, but for now trying to move forward with other
6492 // issues.
6493 Register ImageReg = I.getOperand(i: 1).getReg();
6494 auto *ImageDef = cast<GIntrinsic>(Val: getVRegDef(MRI&: *MRI, Reg: ImageReg));
6495 Register NewImageReg = MRI->createVirtualRegister(RegClass: MRI->getRegClass(Reg: ImageReg));
6496 if (!loadHandleBeforePosition(HandleReg&: NewImageReg, ResType: GR.getSPIRVTypeForVReg(VReg: ImageReg),
6497 HandleDef&: *ImageDef, Pos&: I)) {
6498 return false;
6499 }
6500
6501 Register CoordinateReg = I.getOperand(i: 2).getReg();
6502 Register DataReg = I.getOperand(i: 3).getReg();
6503 assert(GR.getResultType(DataReg)->getOpcode() == SPIRV::OpTypeVector);
6504 assert(GR.getScalarOrVectorComponentCount(GR.getResultType(DataReg)) == 4);
6505 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpImageWrite))
6506 .addUse(RegNo: NewImageReg)
6507 .addUse(RegNo: CoordinateReg)
6508 .addUse(RegNo: DataReg)
6509 .constrainAllUses(TII, TRI, RBI);
6510 return true;
6511}
6512
6513Register SPIRVInstructionSelector::buildPointerToResource(
6514 SPIRVTypeInst SpirvResType, SPIRV::StorageClass::StorageClass SC,
6515 uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg,
6516 StringRef Name, MachineIRBuilder MIRBuilder) const {
6517 const Type *ResType = GR.getTypeForSPIRVType(Ty: SpirvResType);
6518 if (ArraySize == 1) {
6519 SPIRVTypeInst PtrType =
6520 GR.getOrCreateSPIRVPointerType(BaseType: ResType, MIRBuilder, SC);
6521 assert(GR.getPointeeType(PtrType) == SpirvResType &&
6522 "SpirvResType did not have an explicit layout.");
6523 return GR.getOrCreateGlobalVariableWithBinding(VarType: PtrType, Set, Binding, Name,
6524 MIRBuilder);
6525 }
6526
6527 const Type *VarType = ArrayType::get(ElementType: const_cast<Type *>(ResType), NumElements: ArraySize);
6528 SPIRVTypeInst VarPointerType =
6529 GR.getOrCreateSPIRVPointerType(BaseType: VarType, MIRBuilder, SC);
6530 Register VarReg = GR.getOrCreateGlobalVariableWithBinding(
6531 VarType: VarPointerType, Set, Binding, Name, MIRBuilder);
6532
6533 SPIRVTypeInst ResPointerType =
6534 GR.getOrCreateSPIRVPointerType(BaseType: ResType, MIRBuilder, SC);
6535 Register AcReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResPointerType));
6536
6537 MIRBuilder.buildInstr(Opcode: SPIRV::OpAccessChain)
6538 .addDef(RegNo: AcReg)
6539 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResPointerType))
6540 .addUse(RegNo: VarReg)
6541 .addUse(RegNo: IndexReg);
6542
6543 return AcReg;
6544}
6545
6546bool SPIRVInstructionSelector::selectFirstBitSet16(
6547 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
6548 unsigned ExtendOpcode, unsigned BitSetOpcode) const {
6549 Register ExtReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6550 if (!selectOpWithSrcs(ResVReg: ExtReg, ResType, I, Srcs: {I.getOperand(i: 2).getReg()},
6551 Opcode: ExtendOpcode))
6552 return false;
6553
6554 return selectFirstBitSet32(ResVReg, ResType, I, SrcReg: ExtReg, BitSetOpcode);
6555}
6556
6557bool SPIRVInstructionSelector::selectFirstBitSet32(
6558 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, Register SrcReg,
6559 unsigned BitSetOpcode) const {
6560 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
6561 .addDef(RegNo: ResVReg)
6562 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6563 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
6564 .addImm(Val: BitSetOpcode)
6565 .addUse(RegNo: SrcReg)
6566 .constrainAllUses(TII, TRI, RBI);
6567 return true;
6568}
6569
6570bool SPIRVInstructionSelector::selectFirstBitSet64(
6571 Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I, Register SrcReg,
6572 unsigned BitSetOpcode, bool SwapPrimarySide) const {
6573 unsigned ComponentCount = GR.getScalarOrVectorComponentCount(Type: ResType);
6574 SPIRVTypeInst BaseType = GR.retrieveScalarOrVectorIntType(Type: ResType);
6575 bool ZeroAsNull = !STI.isShader();
6576 Register ConstIntZero =
6577 GR.getOrCreateConstInt(Val: 0, I, SpvType: BaseType, TII, ZeroAsNull);
6578 Register ConstIntOne =
6579 GR.getOrCreateConstInt(Val: 1, I, SpvType: BaseType, TII, ZeroAsNull);
6580
6581 // SPIRV doesn't support vectors with more than 4 components. Since the
6582 // algoritm below converts i64 -> i32x2 and i64x4 -> i32x8 it can only
6583 // operate on vectors with 2 or less components. When largers vectors are
6584 // seen. Split them, recurse, then recombine them.
6585 if (ComponentCount > 2) {
6586 auto Func = [this, SwapPrimarySide](Register ResVReg, SPIRVTypeInst ResType,
6587 MachineInstr &I, Register SrcReg,
6588 unsigned Opcode) -> bool {
6589 return this->selectFirstBitSet64(ResVReg, ResType, I, SrcReg, BitSetOpcode: Opcode,
6590 SwapPrimarySide);
6591 };
6592
6593 return handle64BitOverflow(ResVReg, ResType, I, SrcReg, Opcode: BitSetOpcode, CallbackFunction: Func);
6594 }
6595
6596 // 1. Split int64 into 2 pieces using a bitcast
6597 MachineIRBuilder MIRBuilder(I);
6598 SPIRVTypeInst PostCastType = GR.getOrCreateSPIRVVectorType(
6599 BaseType, NumElements: 2 * ComponentCount, MIRBuilder, EmitIR: false);
6600 Register BitcastReg =
6601 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PostCastType));
6602
6603 if (!selectOpWithSrcs(ResVReg: BitcastReg, ResType: PostCastType, I, Srcs: {SrcReg},
6604 Opcode: SPIRV::OpBitcast))
6605 return false;
6606
6607 // 2. Find the first set bit from the primary side for all the pieces in #1
6608 Register FBSReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PostCastType));
6609 if (!selectFirstBitSet32(ResVReg: FBSReg, ResType: PostCastType, I, SrcReg: BitcastReg, BitSetOpcode))
6610 return false;
6611
6612 // 3. Split result vector into high bits and low bits
6613 Register HighReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6614 Register LowReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6615
6616 bool IsScalarRes = ResType->getOpcode() != SPIRV::OpTypeVector;
6617 if (IsScalarRes) {
6618 // if scalar do a vector extract
6619 if (!selectOpWithSrcs(ResVReg: HighReg, ResType, I, Srcs: {FBSReg, ConstIntOne},
6620 Opcode: SPIRV::OpVectorExtractDynamic))
6621 return false;
6622 if (!selectOpWithSrcs(ResVReg: LowReg, ResType, I, Srcs: {FBSReg, ConstIntZero},
6623 Opcode: SPIRV::OpVectorExtractDynamic))
6624 return false;
6625 } else {
6626 // if vector do a shufflevector
6627 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6628 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
6629 .addDef(RegNo: HighReg)
6630 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6631 .addUse(RegNo: FBSReg)
6632 // Per the spec, repeat the vector if only one vec is needed
6633 .addUse(RegNo: FBSReg);
6634
6635 // high bits are stored in even natural indexes. Extract them from FBSReg
6636 for (unsigned J = 1; J < ComponentCount * 2; J += 2) {
6637 MIB.addImm(Val: J);
6638 }
6639
6640 MIB.constrainAllUses(TII, TRI, RBI);
6641
6642 MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(),
6643 MCID: TII.get(Opcode: SPIRV::OpVectorShuffle))
6644 .addDef(RegNo: LowReg)
6645 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6646 .addUse(RegNo: FBSReg)
6647 // Per the spec, repeat the vector if only one vec is needed
6648 .addUse(RegNo: FBSReg);
6649
6650 // low bits are stored in odd natural indices. Extract them from FBSReg
6651 for (unsigned J = 0; J < ComponentCount * 2; J += 2) {
6652 MIB.addImm(Val: J);
6653 }
6654 MIB.constrainAllUses(TII, TRI, RBI);
6655 }
6656
6657 // 4. Check the result. When primary bits == -1 use secondary, otherwise use
6658 // primary
6659 SPIRVTypeInst BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
6660 Register NegOneReg;
6661 Register Reg0;
6662 Register Reg32;
6663 unsigned SelectOp;
6664 unsigned AddOp;
6665
6666 if (IsScalarRes) {
6667 NegOneReg =
6668 GR.getOrCreateConstInt(Val: (unsigned)-1, I, SpvType: ResType, TII, ZeroAsNull);
6669 Reg0 = GR.getOrCreateConstInt(Val: 0, I, SpvType: ResType, TII, ZeroAsNull);
6670 Reg32 = GR.getOrCreateConstInt(Val: 32, I, SpvType: ResType, TII, ZeroAsNull);
6671 SelectOp = SPIRV::OpSelectSISCond;
6672 AddOp = SPIRV::OpIAddS;
6673 } else {
6674 BoolType = GR.getOrCreateSPIRVVectorType(BaseType: BoolType, NumElements: ComponentCount,
6675 MIRBuilder, EmitIR: false);
6676 NegOneReg =
6677 GR.getOrCreateConstVector(Val: (unsigned)-1, I, SpvType: ResType, TII, ZeroAsNull);
6678 Reg0 = GR.getOrCreateConstVector(Val: 0, I, SpvType: ResType, TII, ZeroAsNull);
6679 Reg32 = GR.getOrCreateConstVector(Val: 32, I, SpvType: ResType, TII, ZeroAsNull);
6680 SelectOp = SPIRV::OpSelectVIVCond;
6681 AddOp = SPIRV::OpIAddV;
6682 }
6683
6684 Register PrimaryReg = HighReg;
6685 Register SecondaryReg = LowReg;
6686 Register RegPrimaryOffset = Reg32;
6687 Register RegSecondaryOffset = Reg0;
6688
6689 // By default the emitted opcodes check for the set bit from the MSB side.
6690 // Setting SwapPrimarySide checks the set bit from the LSB side
6691 if (SwapPrimarySide) {
6692 PrimaryReg = LowReg;
6693 SecondaryReg = HighReg;
6694 RegPrimaryOffset = Reg0;
6695 RegSecondaryOffset = Reg32;
6696 }
6697
6698 Register RegSecondaryHasVal =
6699 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BoolType));
6700 if (!selectOpWithSrcs(ResVReg: RegSecondaryHasVal, ResType: BoolType, I,
6701 Srcs: {SecondaryReg, NegOneReg}, Opcode: SPIRV::OpINotEqual))
6702 return false;
6703
6704 Register RegPrimaryHasVal =
6705 MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: BoolType));
6706 if (!selectOpWithSrcs(ResVReg: RegPrimaryHasVal, ResType: BoolType, I, Srcs: {PrimaryReg, NegOneReg},
6707 Opcode: SPIRV::OpINotEqual))
6708 return false;
6709
6710 // Pass 1: seed with secondary (lower-priority fallback)
6711 // ReturnBits = secondaryHasVal ? SecondaryBits : -1
6712 // Add = secondaryHasVal ? SecondaryOffset : 0
6713 Register RegReturnBits = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6714 if (!selectOpWithSrcs(ResVReg: RegReturnBits, ResType, I,
6715 Srcs: {RegSecondaryHasVal, SecondaryReg, NegOneReg},
6716 Opcode: SelectOp))
6717 return false;
6718
6719 Register RegAdd;
6720 if (SwapPrimarySide) {
6721 RegAdd = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6722 if (!selectOpWithSrcs(ResVReg: RegAdd, ResType, I,
6723 Srcs: {RegSecondaryHasVal, RegSecondaryOffset, Reg0},
6724 Opcode: SelectOp))
6725 return false;
6726 } else {
6727 RegAdd = Reg0;
6728 }
6729
6730 // Pass 2: override with primary (higher priority) if it has a valid result
6731 // ReturnBits2 = primaryHasVal ? PrimaryBits : ReturnBits
6732 // Add2 = primaryHasVal ? PrimaryOffset : Add
6733 Register RegReturnBits2 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6734 if (!selectOpWithSrcs(ResVReg: RegReturnBits2, ResType, I,
6735 Srcs: {RegPrimaryHasVal, PrimaryReg, RegReturnBits},
6736 Opcode: SelectOp))
6737 return false;
6738
6739 Register RegAdd2 = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
6740 if (!selectOpWithSrcs(ResVReg: RegAdd2, ResType, I,
6741 Srcs: {RegPrimaryHasVal, RegPrimaryOffset, RegAdd}, Opcode: SelectOp))
6742 return false;
6743
6744 return selectOpWithSrcs(ResVReg, ResType, I, Srcs: {RegReturnBits2, RegAdd2},
6745 Opcode: AddOp);
6746}
6747
6748bool SPIRVInstructionSelector::selectFirstBitHigh(Register ResVReg,
6749 SPIRVTypeInst ResType,
6750 MachineInstr &I,
6751 bool IsSigned) const {
6752 // FindUMsb and FindSMsb intrinsics only support 32 bit integers
6753 Register OpReg = I.getOperand(i: 2).getReg();
6754 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
6755 // zero or sign extend
6756 unsigned ExtendOpcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
6757 unsigned BitSetOpcode = IsSigned ? GL::FindSMsb : GL::FindUMsb;
6758
6759 switch (GR.getScalarOrVectorBitWidth(Type: OpType)) {
6760 case 16:
6761 return selectFirstBitSet16(ResVReg, ResType, I, ExtendOpcode, BitSetOpcode);
6762 case 32:
6763 return selectFirstBitSet32(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode);
6764 case 64:
6765 return selectFirstBitSet64(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode,
6766 /*SwapPrimarySide=*/false);
6767 default:
6768 return diagnoseUnsupported(
6769 I,
6770 Msg: "spv_firstbituhigh and spv_firstbitshigh only support 16,32,64 bits.");
6771 }
6772}
6773
6774bool SPIRVInstructionSelector::selectFirstBitLow(Register ResVReg,
6775 SPIRVTypeInst ResType,
6776 MachineInstr &I) const {
6777 // FindILsb intrinsic only supports 32 bit integers
6778 Register OpReg = I.getOperand(i: 2).getReg();
6779 SPIRVTypeInst OpType = GR.getSPIRVTypeForVReg(VReg: OpReg);
6780 // OpUConvert treats the operand bits as an unsigned i16 and zero extends it
6781 // to an unsigned i32. As this leaves all the least significant bits unchanged
6782 // so the first set bit from the LSB side doesn't change.
6783 unsigned ExtendOpcode = SPIRV::OpUConvert;
6784 unsigned BitSetOpcode = GL::FindILsb;
6785
6786 switch (GR.getScalarOrVectorBitWidth(Type: OpType)) {
6787 case 16:
6788 return selectFirstBitSet16(ResVReg, ResType, I, ExtendOpcode, BitSetOpcode);
6789 case 32:
6790 return selectFirstBitSet32(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode);
6791 case 64:
6792 return selectFirstBitSet64(ResVReg, ResType, I, SrcReg: OpReg, BitSetOpcode,
6793 /*SwapPrimarySide=*/true);
6794 default:
6795 return diagnoseUnsupported(I,
6796 Msg: "spv_firstbitlow only supports 16,32,64 bits.");
6797 }
6798}
6799
6800bool SPIRVInstructionSelector::selectAllocaArray(Register ResVReg,
6801 SPIRVTypeInst ResType,
6802 MachineInstr &I) const {
6803 // there was an allocation size parameter to the allocation instruction
6804 // that is not 1
6805 MachineBasicBlock &BB = *I.getParent();
6806 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVariableLengthArrayINTEL))
6807 .addDef(RegNo: ResVReg)
6808 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6809 .addUse(RegNo: I.getOperand(i: 2).getReg())
6810 .constrainAllUses(TII, TRI, RBI);
6811 if (!STI.isShader()) {
6812 unsigned Alignment = I.getOperand(i: 3).getImm();
6813 buildOpDecorate(Reg: ResVReg, I, TII, Dec: SPIRV::Decoration::Alignment, DecArgs: {Alignment});
6814 }
6815 return true;
6816}
6817
6818// Returns true iff `Ty` is a concrete SPIR-V type per the SPV_KHR_abort
6819// definition: a numerical scalar (int/float), a (physical) pointer, a vector,
6820// matrix or any aggregate (array/struct) recursively containing only such
6821// types. OpTypeBool, OpTypeVoid, opaque handles and similar abstract
6822// non-concrete types are rejected.
6823static bool isConcreteSPIRVType(SPIRVTypeInst Ty,
6824 const SPIRVGlobalRegistry &GR) {
6825 SmallVector<SPIRVTypeInst, 4> Worklist{Ty};
6826 while (!Worklist.empty()) {
6827 SPIRVTypeInst T = Worklist.pop_back_val();
6828 switch (T->getOpcode()) {
6829 case SPIRV::OpTypeInt:
6830 case SPIRV::OpTypeFloat:
6831 case SPIRV::OpTypePointer:
6832 break;
6833 case SPIRV::OpTypeVector:
6834 case SPIRV::OpTypeMatrix:
6835 case SPIRV::OpTypeArray: {
6836 Register OperandReg = T->getOperand(i: 1).getReg();
6837 SPIRVTypeInst ElementT = GR.getSPIRVTypeForVReg(VReg: OperandReg);
6838 Worklist.push_back(Elt: ElementT);
6839 } break;
6840 case SPIRV::OpTypeStruct:
6841 for (unsigned Idx = 1, E = T->getNumOperands(); Idx < E; ++Idx) {
6842 Register OperandReg = T->getOperand(i: Idx).getReg();
6843 SPIRVTypeInst ElementT = GR.getSPIRVTypeForVReg(VReg: OperandReg);
6844 Worklist.push_back(Elt: ElementT);
6845 }
6846 break;
6847 default:
6848 return false;
6849 }
6850 }
6851 return true;
6852}
6853
6854bool SPIRVInstructionSelector::selectAbort(MachineInstr &I) const {
6855 assert(I.getNumExplicitOperands() == 2);
6856
6857 Register MsgReg = I.getOperand(i: 1).getReg();
6858 SPIRVTypeInst MsgType = GR.getSPIRVTypeForVReg(VReg: MsgReg);
6859 assert(MsgType && "Message argument of llvm.spv.abort has no SPIR-V type");
6860
6861 if (!isConcreteSPIRVType(Ty: MsgType, GR))
6862 return diagnoseUnsupported(
6863 I,
6864 Msg: "llvm.spv.abort message type must be a concrete SPIR-V type (numerical "
6865 "scalar, pointer, vector, matrix, or aggregate of such types)");
6866
6867 MachineBasicBlock &BB = *I.getParent();
6868 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAbortKHR))
6869 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MsgType))
6870 .addUse(RegNo: MsgReg)
6871 .constrainAllUses(TII, TRI, RBI);
6872 return true;
6873}
6874
6875bool SPIRVInstructionSelector::selectTrap(MachineInstr &I) const {
6876 // When the SPV_KHR_abort extension is disabled, drop the G_TRAP and
6877 // G_UBSANTRAP silently.
6878 if (!STI.canUseExtension(E: SPIRV::Extension::SPV_KHR_abort))
6879 return true;
6880
6881 // Use the 32-bit integer constant for the abort "message" argument:
6882 // - G_UBSANTRAP operand is zero-extended to 32 bits.
6883 // - "All ones" constant is used for G_TRAP.
6884 uint32_t MsgVal = ~0u;
6885 if (I.getOpcode() == TargetOpcode::G_UBSANTRAP)
6886 MsgVal = static_cast<uint32_t>(I.getOperand(i: 0).getImm());
6887
6888 SPIRVTypeInst MsgType = GR.getOrCreateSPIRVIntegerType(BitWidth: 32, I, TII);
6889 Register MsgReg = buildI32ConstantInEntryBlock(Val: MsgVal, I, ResType: MsgType);
6890
6891 MachineBasicBlock &BB = *I.getParent();
6892 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpAbortKHR))
6893 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: MsgType))
6894 .addUse(RegNo: MsgReg)
6895 .constrainAllUses(TII, TRI, RBI);
6896 return true;
6897}
6898
6899bool SPIRVInstructionSelector::selectFrameIndex(Register ResVReg,
6900 SPIRVTypeInst ResType,
6901 MachineInstr &I) const {
6902 // Change order of instructions if needed: all OpVariable instructions in a
6903 // function must be the first instructions in the first block
6904 auto It = getOpVariableMBBIt(MF&: *I.getMF());
6905 BuildMI(BB&: *It->getParent(), I: It, MIMD: It->getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVariable))
6906 .addDef(RegNo: ResVReg)
6907 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
6908 .addImm(Val: static_cast<uint32_t>(SPIRV::StorageClass::Function))
6909 .constrainAllUses(TII, TRI, RBI);
6910 if (!STI.isShader()) {
6911 unsigned Alignment = I.getOperand(i: 2).getImm();
6912 buildOpDecorate(Reg: ResVReg, I&: *It, TII, Dec: SPIRV::Decoration::Alignment,
6913 DecArgs: {Alignment});
6914 }
6915 return true;
6916}
6917
6918bool SPIRVInstructionSelector::selectBranch(MachineInstr &I) const {
6919 // InstructionSelector walks backwards through the instructions. We can use
6920 // both a G_BR and a G_BRCOND to create an OpBranchConditional. We hit G_BR
6921 // first, so can generate an OpBranchConditional here. If there is no
6922 // G_BRCOND, we just use OpBranch for a regular unconditional branch.
6923 const MachineInstr *PrevI = I.getPrevNode();
6924 MachineBasicBlock &MBB = *I.getParent();
6925 if (PrevI != nullptr && PrevI->getOpcode() == TargetOpcode::G_BRCOND) {
6926 BuildMI(BB&: MBB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBranchConditional))
6927 .addUse(RegNo: PrevI->getOperand(i: 0).getReg())
6928 .addMBB(MBB: PrevI->getOperand(i: 1).getMBB())
6929 .addMBB(MBB: I.getOperand(i: 0).getMBB())
6930 .constrainAllUses(TII, TRI, RBI);
6931 return true;
6932 }
6933 BuildMI(BB&: MBB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBranch))
6934 .addMBB(MBB: I.getOperand(i: 0).getMBB())
6935 .constrainAllUses(TII, TRI, RBI);
6936 return true;
6937}
6938
6939bool SPIRVInstructionSelector::selectBranchCond(MachineInstr &I) const {
6940 // InstructionSelector walks backwards through the instructions. For an
6941 // explicit conditional branch with no fallthrough, we use both a G_BR and a
6942 // G_BRCOND to create an OpBranchConditional. We should hit G_BR first, and
6943 // generate the OpBranchConditional in selectBranch above.
6944 //
6945 // If an OpBranchConditional has been generated, we simply return, as the work
6946 // is alread done. If there is no OpBranchConditional, LLVM must be relying on
6947 // implicit fallthrough to the next basic block, so we need to create an
6948 // OpBranchConditional with an explicit "false" argument pointing to the next
6949 // basic block that LLVM would fall through to.
6950 const MachineInstr *NextI = I.getNextNode();
6951 // Check if this has already been successfully selected.
6952 if (NextI != nullptr && NextI->getOpcode() == SPIRV::OpBranchConditional)
6953 return true;
6954 // Must be relying on implicit block fallthrough, so generate an
6955 // OpBranchConditional with the "next" basic block as the "false" target.
6956 MachineBasicBlock &MBB = *I.getParent();
6957 unsigned NextMBBNum = MBB.getNextNode()->getNumber();
6958 MachineBasicBlock *NextMBB = I.getMF()->getBlockNumbered(N: NextMBBNum);
6959 BuildMI(BB&: MBB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpBranchConditional))
6960 .addUse(RegNo: I.getOperand(i: 0).getReg())
6961 .addMBB(MBB: I.getOperand(i: 1).getMBB())
6962 .addMBB(MBB: NextMBB)
6963 .constrainAllUses(TII, TRI, RBI);
6964 return true;
6965}
6966
6967bool SPIRVInstructionSelector::selectPhi(Register ResVReg,
6968 MachineInstr &I) const {
6969 auto MIB =
6970 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: TargetOpcode::PHI))
6971 .addDef(RegNo: ResVReg);
6972 const unsigned NumOps = I.getNumOperands();
6973 for (unsigned i = 1; i < NumOps; i += 2) {
6974 MIB.addUse(RegNo: I.getOperand(i: i + 0).getReg());
6975 MIB.addMBB(MBB: I.getOperand(i: i + 1).getMBB());
6976 }
6977 MIB.constrainAllUses(TII, TRI, RBI);
6978 return true;
6979}
6980
6981bool SPIRVInstructionSelector::selectGlobalValue(
6982 Register ResVReg, MachineInstr &I, const MachineInstr *Init) const {
6983 // FIXME: don't use MachineIRBuilder here, replace it with BuildMI.
6984 MachineIRBuilder MIRBuilder(I);
6985 const GlobalValue *GV = I.getOperand(i: 1).getGlobal();
6986 Type *GVType = toTypedPointer(Ty: GR.getDeducedGlobalValueType(Global: GV));
6987
6988 std::string GlobalIdent;
6989 if (!GV->hasName()) {
6990 unsigned &ID = UnnamedGlobalIDs[GV];
6991 if (ID == 0)
6992 ID = UnnamedGlobalIDs.size();
6993 GlobalIdent = "__unnamed_" + Twine(ID).str();
6994 } else {
6995 GlobalIdent = GV->getName();
6996 }
6997
6998 // Behaviour of functions as operands depends on availability of the
6999 // corresponding extension (SPV_INTEL_function_pointers):
7000 // - If there is an extension to operate with functions as operands:
7001 // We create a proper constant operand and evaluate a correct type for a
7002 // function pointer.
7003 // - Without the required extension:
7004 // We have functions as operands in tests with blocks of instruction e.g. in
7005 // transcoding/global_block.ll. These operands are not used and should be
7006 // substituted by zero constants. Their type is expected to be always
7007 // OpTypePointer Function %uchar.
7008 if (isa<Function>(Val: GV)) {
7009 const Constant *ConstVal = GV;
7010 MachineBasicBlock &BB = *I.getParent();
7011 Register NewReg = GR.find(V: ConstVal, MF: GR.CurMF);
7012 if (!NewReg.isValid()) {
7013 const Function *GVFun =
7014 STI.canUseExtension(E: SPIRV::Extension::SPV_INTEL_function_pointers)
7015 ? dyn_cast<Function>(Val: GV)
7016 : nullptr;
7017 SPIRVTypeInst ResType = GR.getOrCreateSPIRVPointerType(
7018 BaseType: GVType, I,
7019 SC: GVFun ? SPIRV::StorageClass::CodeSectionINTEL
7020 : addressSpaceToStorageClass(AddrSpace: GV->getAddressSpace(), STI));
7021 if (GVFun) {
7022 // References to a function via function pointers generate virtual
7023 // registers without a definition. We will resolve it later, during
7024 // module analysis stage.
7025 Register ResTypeReg = GR.getSPIRVTypeID(SpirvType: ResType);
7026 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
7027 Register FuncVReg =
7028 MRI->createGenericVirtualRegister(Ty: GR.getRegType(SpvType: ResType));
7029 MRI->setRegClass(Reg: FuncVReg, RC: &SPIRV::pIDRegClass);
7030 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: FuncVReg, MF: *GR.CurMF);
7031 MachineInstrBuilder MIB1 =
7032 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
7033 .addDef(RegNo: FuncVReg)
7034 .addUse(RegNo: ResTypeReg);
7035 MachineInstrBuilder MIB2 =
7036 BuildMI(BB, I, MIMD: I.getDebugLoc(),
7037 MCID: TII.get(Opcode: SPIRV::OpConstantFunctionPointerINTEL))
7038 .addDef(RegNo: ResVReg)
7039 .addUse(RegNo: ResTypeReg)
7040 .addUse(RegNo: FuncVReg);
7041 GR.add(V: ConstVal, MI: MIB2);
7042 // mapping the function pointer to the used Function
7043 GR.recordFunctionPointer(MO: &MIB2.getInstr()->getOperand(i: 2), F: GVFun);
7044 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: ResVReg, MF: *GR.CurMF);
7045 MIB1.constrainAllUses(TII, TRI, RBI);
7046 MIB2.constrainAllUses(TII, TRI, RBI);
7047 return true;
7048 }
7049 MachineInstrBuilder MIB3 =
7050 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpUndef))
7051 .addDef(RegNo: ResVReg)
7052 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType));
7053 GR.add(V: ConstVal, MI: MIB3);
7054 GR.recordFunctionPointer(MO: &MIB3.getInstr()->getOperand(i: 0),
7055 F: cast<Function>(Val: GV));
7056 MIB3.constrainAllUses(TII, TRI, RBI);
7057 return true;
7058 }
7059 assert(NewReg != ResVReg);
7060 return BuildCOPY(DestReg: ResVReg, SrcReg: NewReg, I);
7061 }
7062 auto GlobalVar = cast<GlobalVariable>(Val: GV);
7063 assert(GlobalVar->getName() != "llvm.global.annotations");
7064
7065 // Skip empty declaration for GVs with initializers till we get the decl with
7066 // passed initializer.
7067 if (hasInitializer(GV: GlobalVar) && !Init)
7068 return true;
7069
7070 const std::optional<SPIRV::LinkageType::LinkageType> LnkType =
7071 getSpirvLinkageTypeFor(ST: STI, GV: *GV);
7072
7073 if (LnkType && *LnkType == SPIRV::LinkageType::Import)
7074 Init = nullptr;
7075
7076 const unsigned AddrSpace = GV->getAddressSpace();
7077 SPIRV::StorageClass::StorageClass StorageClass =
7078 addressSpaceToStorageClass(AddrSpace, STI);
7079 SPIRVTypeInst ResType =
7080 GR.getOrCreateSPIRVPointerType(BaseType: GVType, I, SC: StorageClass);
7081 Register Reg = GR.buildGlobalVariable(
7082 Reg: ResVReg, BaseType: ResType, Name: GlobalIdent, GV, Storage: StorageClass, Init,
7083 IsConst: GlobalVar->isConstant(), LinkageType: LnkType, MIRBuilder, IsInstSelector: true);
7084 // TODO: For AMDGCN, we pipe externally_initialized through via
7085 // HostAccessINTEL, with ReadWrite (3) access, which is we then handle during
7086 // reverse translation. We should remove this once SPIR-V gains the ability to
7087 // express the concept.
7088 if (GlobalVar->isExternallyInitialized() &&
7089 STI.getTargetTriple().getVendor() == Triple::AMD) {
7090 constexpr unsigned ReadWriteINTEL = 3u;
7091 buildOpDecorate(Reg, MIRBuilder, Dec: SPIRV::Decoration::HostAccessINTEL,
7092 DecArgs: {ReadWriteINTEL});
7093 MachineInstrBuilder MIB(*MF, --MIRBuilder.getInsertPt());
7094 addStringImm(Str: GV->getName(), MIB);
7095 }
7096 return Reg.isValid();
7097}
7098
7099bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
7100 SPIRVTypeInst ResType,
7101 MachineInstr &I) const {
7102 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
7103 return selectExtInst(ResVReg, ResType, I, CLInst: CL::log10);
7104 }
7105
7106 // There is no log10 instruction in the GLSL Extended Instruction set, so it
7107 // is implemented as:
7108 // log10(x) = log2(x) * (1 / log2(10))
7109 // = log2(x) * 0.30103
7110
7111 MachineIRBuilder MIRBuilder(I);
7112 MachineBasicBlock &BB = *I.getParent();
7113
7114 // Build log2(x).
7115 Register VarReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
7116 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
7117 .addDef(RegNo: VarReg)
7118 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7119 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
7120 .addImm(Val: GL::Log2)
7121 .add(MO: I.getOperand(i: 1))
7122 .constrainAllUses(TII, TRI, RBI);
7123
7124 // Build 0.30103.
7125 assert(ResType->getOpcode() == SPIRV::OpTypeVector ||
7126 ResType->getOpcode() == SPIRV::OpTypeFloat);
7127 // TODO: Add matrix implementation once supported by the HLSL frontend.
7128 SPIRVTypeInst SpirvScalarType = GR.getScalarOrVectorComponentType(Type: ResType);
7129 // The literal must match the precision of the scalar type, otherwise the
7130 // OpConstant will contain non-zero high-order bits and fail SPIR-V
7131 // validation when the type is narrower than 32 bits (e.g. half).
7132 APFloat ScaleVal(0.30103);
7133 bool LosesInfo;
7134 ScaleVal.convert(
7135 ToSemantics: getZeroFP(LLVMFloatTy: GR.getTypeForSPIRVType(Ty: SpirvScalarType)).getSemantics(),
7136 RM: APFloat::rmNearestTiesToEven, losesInfo: &LosesInfo);
7137 Register ScaleReg = GR.buildConstantFP(Val: ScaleVal, MIRBuilder, SpvType: SpirvScalarType);
7138
7139 // Multiply log2(x) by 0.30103 to get log10(x) result.
7140 auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector
7141 ? SPIRV::OpVectorTimesScalar
7142 : SPIRV::OpFMulS;
7143 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode))
7144 .addDef(RegNo: ResVReg)
7145 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7146 .addUse(RegNo: VarReg)
7147 .addUse(RegNo: ScaleReg)
7148 .constrainAllUses(TII, TRI, RBI);
7149 return true;
7150}
7151
7152bool SPIRVInstructionSelector::selectFpowi(Register ResVReg,
7153 SPIRVTypeInst ResType,
7154 MachineInstr &I) const {
7155 // On OpenCL targets, pown(gentype x, intn n) maps directly.
7156 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std))
7157 return selectExtInst(ResVReg, ResType, I, CLInst: CL::pown);
7158
7159 // On GLSL (Vulkan) targets, there is no integer-exponent power instruction.
7160 // Lower as: Pow(base, OpConvertSToF(exp)).
7161 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::GLSL_std_450)) {
7162 Register BaseReg = I.getOperand(i: 1).getReg();
7163 Register ExpReg = I.getOperand(i: 2).getReg();
7164 Register FloatExpReg = MRI->createVirtualRegister(RegClass: GR.getRegClass(SpvType: ResType));
7165 if (!selectOpWithSrcs(ResVReg: FloatExpReg, ResType, I, Srcs: {ExpReg},
7166 Opcode: SPIRV::OpConvertSToF))
7167 return false;
7168 return selectExtInst(ResVReg, ResType, I, GLInst: GL::Pow,
7169 /*setMIFlags=*/true, /*useMISrc=*/false,
7170 SrcRegs: {BaseReg, FloatExpReg});
7171 }
7172 return false;
7173}
7174
7175bool SPIRVInstructionSelector::selectModf(Register ResVReg,
7176 SPIRVTypeInst ResType,
7177 MachineInstr &I) const {
7178 // llvm.modf has a single arg --the number to be decomposed-- and returns a
7179 // struct { restype, restype }, while OpenCLLIB::modf has two args --the
7180 // number to be decomposed and a pointer--, returns the fractional part and
7181 // the integral part is stored in the pointer argument. Therefore, we can't
7182 // use directly the OpenCLLIB::modf intrinsic. However, we can do some
7183 // scaffolding to make it work. The idea is to create an alloca instruction
7184 // to get a ptr, pass this ptr to OpenCL::modf, and then load the value
7185 // from this ptr to place it in the struct. llvm.modf returns the fractional
7186 // part as the first element of the result, and the integral part as the
7187 // second element of the result.
7188
7189 // At this point, the return type is not a struct anymore, but rather two
7190 // independent elements of SPIRVResType. We can get each independent element
7191 // from I.getDefs() or I.getOperands().
7192 if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::OpenCL_std)) {
7193 MachineIRBuilder MIRBuilder(I);
7194 SPIRVTypeInst FloatType =
7195 GR.getSPIRVTypeForVReg(VReg: I.getOperand(i: I.getNumExplicitDefs()).getReg());
7196 // Get pointer type for alloca variable.
7197 const SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
7198 BaseType: FloatType, MIRBuilder, SC: SPIRV::StorageClass::Function);
7199 // Create new register for the pointer type of alloca variable.
7200 Register PtrTyReg =
7201 MIRBuilder.getMRI()->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
7202 MIRBuilder.getMRI()->setType(
7203 VReg: PtrTyReg,
7204 Ty: LLT::pointer(AddressSpace: storageClassToAddressSpace(SC: SPIRV::StorageClass::Function),
7205 SizeInBits: GR.getPointerSize()));
7206
7207 // Assign SPIR-V type of the pointer type of the alloca variable to the
7208 // new register.
7209 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: PtrTyReg, MF: MIRBuilder.getMF());
7210 MachineBasicBlock::iterator VarPos = getOpVariableMBBIt(MF&: *I.getMF());
7211 MachineBasicBlock &EntryBB = I.getMF()->front();
7212 auto AllocaMIB =
7213 BuildMI(BB&: EntryBB, I: VarPos, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpVariable))
7214 .addDef(RegNo: PtrTyReg)
7215 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: PtrType))
7216 .addImm(Val: static_cast<uint32_t>(SPIRV::StorageClass::Function));
7217 Register Variable = AllocaMIB->getOperand(i: 0).getReg();
7218
7219 MachineBasicBlock &BB = *I.getParent();
7220 // Create the OpenCLLIB::modf instruction.
7221 auto MIB =
7222 BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpExtInst))
7223 .addDef(RegNo: ResVReg)
7224 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: FloatType))
7225 .addImm(Val: static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
7226 .addImm(Val: CL::modf)
7227 .setMIFlags(I.getFlags())
7228 .add(MO: I.getOperand(i: I.getNumExplicitDefs())) // Floating point value.
7229 .addUse(RegNo: Variable); // Pointer to integral part.
7230 // Assign the integral part stored in the ptr to the second element of the
7231 // result.
7232 Register IntegralPartReg = I.getOperand(i: 1).getReg();
7233 if (IntegralPartReg.isValid() && !MRI->use_nodbg_empty(RegNo: IntegralPartReg)) {
7234 // Load the value from the pointer to integral part.
7235 auto LoadMIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
7236 .addDef(RegNo: IntegralPartReg)
7237 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: FloatType))
7238 .addUse(RegNo: Variable);
7239 LoadMIB.constrainAllUses(TII, TRI, RBI);
7240 }
7241
7242 MIB.constrainAllUses(TII, TRI, RBI);
7243 return true;
7244 } else if (STI.canUseExtInstSet(E: SPIRV::InstructionSet::GLSL_std_450)) {
7245 assert(false && "GLSL::Modf is deprecated.");
7246 // FIXME: GL::Modf is deprecated, use Modfstruct instead.
7247 return false;
7248 }
7249 return false;
7250}
7251
7252// Generate the instructions to load 3-element vector builtin input
7253// IDs/Indices.
7254// Like: GlobalInvocationId, LocalInvocationId, etc....
7255
7256bool SPIRVInstructionSelector::loadVec3BuiltinInputID(
7257 SPIRV::BuiltIn::BuiltIn BuiltInValue, Register ResVReg,
7258 SPIRVTypeInst ResType, MachineInstr &I) const {
7259 MachineIRBuilder MIRBuilder(I);
7260 const SPIRVTypeInst Vec3Ty =
7261 GR.getOrCreateSPIRVVectorType(BaseType: ResType, NumElements: 3, MIRBuilder, EmitIR: false);
7262 const SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
7263 BaseType: Vec3Ty, MIRBuilder, SC: SPIRV::StorageClass::Input);
7264
7265 // Create new register for the input ID builtin variable.
7266 Register NewRegister =
7267 MIRBuilder.getMRI()->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
7268 MIRBuilder.getMRI()->setType(VReg: NewRegister, Ty: LLT::pointer(AddressSpace: 0, SizeInBits: 64));
7269 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: NewRegister, MF: MIRBuilder.getMF());
7270
7271 // Build global variable with the necessary decorations for the input ID
7272 // builtin variable.
7273 Register Variable = GR.buildGlobalVariable(
7274 Reg: NewRegister, BaseType: PtrType, Name: getLinkStringForBuiltIn(BuiltInValue), GV: nullptr,
7275 Storage: SPIRV::StorageClass::Input, Init: nullptr, IsConst: true, LinkageType: std::nullopt, MIRBuilder,
7276 IsInstSelector: false);
7277
7278 // Create new register for loading value.
7279 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
7280 Register LoadedRegister = MRI->createVirtualRegister(RegClass: &SPIRV::iIDRegClass);
7281 MIRBuilder.getMRI()->setType(VReg: LoadedRegister, Ty: LLT::pointer(AddressSpace: 0, SizeInBits: 64));
7282 GR.assignSPIRVTypeToVReg(Type: Vec3Ty, VReg: LoadedRegister, MF: MIRBuilder.getMF());
7283
7284 // Load v3uint value from the global variable.
7285 BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
7286 .addDef(RegNo: LoadedRegister)
7287 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: Vec3Ty))
7288 .addUse(RegNo: Variable);
7289
7290 // Get the input ID index. Expecting operand is a constant immediate value,
7291 // wrapped in a type assignment.
7292 assert(I.getOperand(2).isReg());
7293 const uint32_t ThreadId = foldImm(MO: I.getOperand(i: 2), MRI);
7294
7295 // Extract the input ID from the loaded vector value.
7296 MachineBasicBlock &BB = *I.getParent();
7297 auto MIB = BuildMI(BB, I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpCompositeExtract))
7298 .addDef(RegNo: ResVReg)
7299 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7300 .addUse(RegNo: LoadedRegister)
7301 .addImm(Val: ThreadId);
7302 MIB.constrainAllUses(TII, TRI, RBI);
7303 return true;
7304}
7305
7306// Generate the instructions to load 32-bit integer builtin input IDs/Indices.
7307// Like LocalInvocationIndex
7308bool SPIRVInstructionSelector::loadBuiltinInputID(
7309 SPIRV::BuiltIn::BuiltIn BuiltInValue, Register ResVReg,
7310 SPIRVTypeInst ResType, MachineInstr &I) const {
7311 MachineIRBuilder MIRBuilder(I);
7312 const SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
7313 BaseType: ResType, MIRBuilder, SC: SPIRV::StorageClass::Input);
7314
7315 // Create new register for the input ID builtin variable.
7316 Register NewRegister =
7317 MIRBuilder.getMRI()->createVirtualRegister(RegClass: GR.getRegClass(SpvType: PtrType));
7318 MIRBuilder.getMRI()->setType(
7319 VReg: NewRegister,
7320 Ty: LLT::pointer(AddressSpace: storageClassToAddressSpace(SC: SPIRV::StorageClass::Input),
7321 SizeInBits: GR.getPointerSize()));
7322 GR.assignSPIRVTypeToVReg(Type: PtrType, VReg: NewRegister, MF: MIRBuilder.getMF());
7323
7324 // Build global variable with the necessary decorations for the input ID
7325 // builtin variable.
7326 Register Variable = GR.buildGlobalVariable(
7327 Reg: NewRegister, BaseType: PtrType, Name: getLinkStringForBuiltIn(BuiltInValue), GV: nullptr,
7328 Storage: SPIRV::StorageClass::Input, Init: nullptr, IsConst: true, LinkageType: std::nullopt, MIRBuilder,
7329 IsInstSelector: false);
7330
7331 // Load uint value from the global variable.
7332 auto MIB = BuildMI(BB&: *I.getParent(), I, MIMD: I.getDebugLoc(), MCID: TII.get(Opcode: SPIRV::OpLoad))
7333 .addDef(RegNo: ResVReg)
7334 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7335 .addUse(RegNo: Variable);
7336
7337 MIB.constrainAllUses(TII, TRI, RBI);
7338 return true;
7339}
7340
7341SPIRVTypeInst SPIRVInstructionSelector::widenTypeToVec4(SPIRVTypeInst Type,
7342 MachineInstr &I) const {
7343 MachineIRBuilder MIRBuilder(I);
7344 if (Type->getOpcode() != SPIRV::OpTypeVector)
7345 return GR.getOrCreateSPIRVVectorType(BaseType: Type, NumElements: 4, MIRBuilder, EmitIR: false);
7346
7347 if (GR.getScalarOrVectorComponentCount(Type) == 4)
7348 return Type;
7349
7350 SPIRVTypeInst ScalarType = GR.getScalarOrVectorComponentType(Type);
7351 return GR.getOrCreateSPIRVVectorType(BaseType: ScalarType, NumElements: 4, MIRBuilder, EmitIR: false);
7352}
7353
7354bool SPIRVInstructionSelector::loadHandleBeforePosition(
7355 Register &HandleReg, SPIRVTypeInst ResType, GIntrinsic &HandleDef,
7356 MachineInstr &Pos) const {
7357
7358 assert(HandleDef.getIntrinsicID() ==
7359 Intrinsic::spv_resource_handlefrombinding);
7360 uint32_t Set = foldImm(MO: HandleDef.getOperand(i: 2), MRI);
7361 uint32_t Binding = foldImm(MO: HandleDef.getOperand(i: 3), MRI);
7362 uint32_t ArraySize = foldImm(MO: HandleDef.getOperand(i: 4), MRI);
7363 Register IndexReg = HandleDef.getOperand(i: 5).getReg();
7364 std::string Name =
7365 getStringValueFromReg(Reg: HandleDef.getOperand(i: 6).getReg(), MRI&: *MRI);
7366
7367 bool IsStructuredBuffer = ResType->getOpcode() == SPIRV::OpTypePointer;
7368 MachineIRBuilder MIRBuilder(HandleDef);
7369 SPIRVTypeInst VarType = ResType;
7370 SPIRV::StorageClass::StorageClass SC = SPIRV::StorageClass::UniformConstant;
7371
7372 if (IsStructuredBuffer) {
7373 VarType = GR.getPointeeType(PtrType: ResType);
7374 SC = GR.getPointerStorageClass(Type: ResType);
7375 }
7376
7377 if (ResType->getOpcode() == SPIRV::OpTypeImage && ArraySize == 0)
7378 MIRBuilder.buildInstr(Opcode: SPIRV::OpCapability)
7379 .addImm(Val: SPIRV::Capability::RuntimeDescriptorArrayEXT);
7380
7381 Register VarReg =
7382 buildPointerToResource(SpirvResType: SPIRVTypeInst(VarType), SC, Set, Binding,
7383 ArraySize, IndexReg, Name, MIRBuilder);
7384
7385 // The handle for the buffer is the pointer to the resource. For an image, the
7386 // handle is the image object. So images get an extra load.
7387 uint32_t LoadOpcode =
7388 IsStructuredBuffer ? SPIRV::OpCopyObject : SPIRV::OpLoad;
7389 GR.assignSPIRVTypeToVReg(Type: ResType, VReg: HandleReg, MF: *Pos.getMF());
7390 BuildMI(BB&: *Pos.getParent(), I&: Pos, MIMD: HandleDef.getDebugLoc(), MCID: TII.get(Opcode: LoadOpcode))
7391 .addDef(RegNo: HandleReg)
7392 .addUse(RegNo: GR.getSPIRVTypeID(SpirvType: ResType))
7393 .addUse(RegNo: VarReg)
7394 .constrainAllUses(TII, TRI, RBI);
7395 return true;
7396}
7397
7398bool SPIRVInstructionSelector::errorIfInstrOutsideShader(
7399 MachineInstr &I) const {
7400 if (!STI.isShader())
7401 return diagnoseUnsupported(
7402 I, Msg: "this instruction is only supported in shaders.");
7403 return true;
7404}
7405
7406namespace llvm {
7407InstructionSelector *
7408createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
7409 const SPIRVSubtarget &Subtarget,
7410 const RegisterBankInfo &RBI) {
7411 return new SPIRVInstructionSelector(TM, Subtarget, RBI);
7412}
7413} // namespace llvm
7414