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