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