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