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