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