Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
35294 views
1
//===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- 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 lowering builtin function calls and types using their
10
// demangled names and TableGen records.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "SPIRVBuiltins.h"
15
#include "SPIRV.h"
16
#include "SPIRVSubtarget.h"
17
#include "SPIRVUtils.h"
18
#include "llvm/ADT/StringExtras.h"
19
#include "llvm/Analysis/ValueTracking.h"
20
#include "llvm/IR/IntrinsicsSPIRV.h"
21
#include <string>
22
#include <tuple>
23
24
#define DEBUG_TYPE "spirv-builtins"
25
26
namespace llvm {
27
namespace SPIRV {
28
#define GET_BuiltinGroup_DECL
29
#include "SPIRVGenTables.inc"
30
31
struct DemangledBuiltin {
32
StringRef Name;
33
InstructionSet::InstructionSet Set;
34
BuiltinGroup Group;
35
uint8_t MinNumArgs;
36
uint8_t MaxNumArgs;
37
};
38
39
#define GET_DemangledBuiltins_DECL
40
#define GET_DemangledBuiltins_IMPL
41
42
struct IncomingCall {
43
const std::string BuiltinName;
44
const DemangledBuiltin *Builtin;
45
46
const Register ReturnRegister;
47
const SPIRVType *ReturnType;
48
const SmallVectorImpl<Register> &Arguments;
49
50
IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin,
51
const Register ReturnRegister, const SPIRVType *ReturnType,
52
const SmallVectorImpl<Register> &Arguments)
53
: BuiltinName(BuiltinName), Builtin(Builtin),
54
ReturnRegister(ReturnRegister), ReturnType(ReturnType),
55
Arguments(Arguments) {}
56
57
bool isSpirvOp() const { return BuiltinName.rfind("__spirv_", 0) == 0; }
58
};
59
60
struct NativeBuiltin {
61
StringRef Name;
62
InstructionSet::InstructionSet Set;
63
uint32_t Opcode;
64
};
65
66
#define GET_NativeBuiltins_DECL
67
#define GET_NativeBuiltins_IMPL
68
69
struct GroupBuiltin {
70
StringRef Name;
71
uint32_t Opcode;
72
uint32_t GroupOperation;
73
bool IsElect;
74
bool IsAllOrAny;
75
bool IsAllEqual;
76
bool IsBallot;
77
bool IsInverseBallot;
78
bool IsBallotBitExtract;
79
bool IsBallotFindBit;
80
bool IsLogical;
81
bool NoGroupOperation;
82
bool HasBoolArg;
83
};
84
85
#define GET_GroupBuiltins_DECL
86
#define GET_GroupBuiltins_IMPL
87
88
struct IntelSubgroupsBuiltin {
89
StringRef Name;
90
uint32_t Opcode;
91
bool IsBlock;
92
bool IsWrite;
93
};
94
95
#define GET_IntelSubgroupsBuiltins_DECL
96
#define GET_IntelSubgroupsBuiltins_IMPL
97
98
struct AtomicFloatingBuiltin {
99
StringRef Name;
100
uint32_t Opcode;
101
};
102
103
#define GET_AtomicFloatingBuiltins_DECL
104
#define GET_AtomicFloatingBuiltins_IMPL
105
struct GroupUniformBuiltin {
106
StringRef Name;
107
uint32_t Opcode;
108
bool IsLogical;
109
};
110
111
#define GET_GroupUniformBuiltins_DECL
112
#define GET_GroupUniformBuiltins_IMPL
113
114
struct GetBuiltin {
115
StringRef Name;
116
InstructionSet::InstructionSet Set;
117
BuiltIn::BuiltIn Value;
118
};
119
120
using namespace BuiltIn;
121
#define GET_GetBuiltins_DECL
122
#define GET_GetBuiltins_IMPL
123
124
struct ImageQueryBuiltin {
125
StringRef Name;
126
InstructionSet::InstructionSet Set;
127
uint32_t Component;
128
};
129
130
#define GET_ImageQueryBuiltins_DECL
131
#define GET_ImageQueryBuiltins_IMPL
132
133
struct ConvertBuiltin {
134
StringRef Name;
135
InstructionSet::InstructionSet Set;
136
bool IsDestinationSigned;
137
bool IsSaturated;
138
bool IsRounded;
139
bool IsBfloat16;
140
FPRoundingMode::FPRoundingMode RoundingMode;
141
};
142
143
struct VectorLoadStoreBuiltin {
144
StringRef Name;
145
InstructionSet::InstructionSet Set;
146
uint32_t Number;
147
uint32_t ElementCount;
148
bool IsRounded;
149
FPRoundingMode::FPRoundingMode RoundingMode;
150
};
151
152
using namespace FPRoundingMode;
153
#define GET_ConvertBuiltins_DECL
154
#define GET_ConvertBuiltins_IMPL
155
156
using namespace InstructionSet;
157
#define GET_VectorLoadStoreBuiltins_DECL
158
#define GET_VectorLoadStoreBuiltins_IMPL
159
160
#define GET_CLMemoryScope_DECL
161
#define GET_CLSamplerAddressingMode_DECL
162
#define GET_CLMemoryFenceFlags_DECL
163
#define GET_ExtendedBuiltins_DECL
164
#include "SPIRVGenTables.inc"
165
} // namespace SPIRV
166
167
//===----------------------------------------------------------------------===//
168
// Misc functions for looking up builtins and veryfying requirements using
169
// TableGen records
170
//===----------------------------------------------------------------------===//
171
172
namespace SPIRV {
173
/// Parses the name part of the demangled builtin call.
174
std::string lookupBuiltinNameHelper(StringRef DemangledCall) {
175
const static std::string PassPrefix = "(anonymous namespace)::";
176
std::string BuiltinName;
177
// Itanium Demangler result may have "(anonymous namespace)::" prefix
178
if (DemangledCall.starts_with(PassPrefix.c_str()))
179
BuiltinName = DemangledCall.substr(PassPrefix.length());
180
else
181
BuiltinName = DemangledCall;
182
// Extract the builtin function name and types of arguments from the call
183
// skeleton.
184
BuiltinName = BuiltinName.substr(0, BuiltinName.find('('));
185
186
// Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR
187
if (BuiltinName.rfind("__spirv_ocl_", 0) == 0)
188
BuiltinName = BuiltinName.substr(12);
189
190
// Check if the extracted name contains type information between angle
191
// brackets. If so, the builtin is an instantiated template - needs to have
192
// the information after angle brackets and return type removed.
193
if (BuiltinName.find('<') && BuiltinName.back() == '>') {
194
BuiltinName = BuiltinName.substr(0, BuiltinName.find('<'));
195
BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(' ') + 1);
196
}
197
198
// Check if the extracted name begins with "__spirv_ImageSampleExplicitLod"
199
// contains return type information at the end "_R<type>", if so extract the
200
// plain builtin name without the type information.
201
if (StringRef(BuiltinName).contains("__spirv_ImageSampleExplicitLod") &&
202
StringRef(BuiltinName).contains("_R")) {
203
BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R"));
204
}
205
206
return BuiltinName;
207
}
208
} // namespace SPIRV
209
210
/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
211
/// the provided \p DemangledCall and specified \p Set.
212
///
213
/// The lookup follows the following algorithm, returning the first successful
214
/// match:
215
/// 1. Search with the plain demangled name (expecting a 1:1 match).
216
/// 2. Search with the prefix before or suffix after the demangled name
217
/// signyfying the type of the first argument.
218
///
219
/// \returns Wrapper around the demangled call and found builtin definition.
220
static std::unique_ptr<const SPIRV::IncomingCall>
221
lookupBuiltin(StringRef DemangledCall,
222
SPIRV::InstructionSet::InstructionSet Set,
223
Register ReturnRegister, const SPIRVType *ReturnType,
224
const SmallVectorImpl<Register> &Arguments) {
225
std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall);
226
227
SmallVector<StringRef, 10> BuiltinArgumentTypes;
228
StringRef BuiltinArgs =
229
DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
230
BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false);
231
232
// Look up the builtin in the defined set. Start with the plain demangled
233
// name, expecting a 1:1 match in the defined builtin set.
234
const SPIRV::DemangledBuiltin *Builtin;
235
if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set)))
236
return std::make_unique<SPIRV::IncomingCall>(
237
BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
238
239
// If the initial look up was unsuccessful and the demangled call takes at
240
// least 1 argument, add a prefix or suffix signifying the type of the first
241
// argument and repeat the search.
242
if (BuiltinArgumentTypes.size() >= 1) {
243
char FirstArgumentType = BuiltinArgumentTypes[0][0];
244
// Prefix to be added to the builtin's name for lookup.
245
// For example, OpenCL "abs" taking an unsigned value has a prefix "u_".
246
std::string Prefix;
247
248
switch (FirstArgumentType) {
249
// Unsigned:
250
case 'u':
251
if (Set == SPIRV::InstructionSet::OpenCL_std)
252
Prefix = "u_";
253
else if (Set == SPIRV::InstructionSet::GLSL_std_450)
254
Prefix = "u";
255
break;
256
// Signed:
257
case 'c':
258
case 's':
259
case 'i':
260
case 'l':
261
if (Set == SPIRV::InstructionSet::OpenCL_std)
262
Prefix = "s_";
263
else if (Set == SPIRV::InstructionSet::GLSL_std_450)
264
Prefix = "s";
265
break;
266
// Floating-point:
267
case 'f':
268
case 'd':
269
case 'h':
270
if (Set == SPIRV::InstructionSet::OpenCL_std ||
271
Set == SPIRV::InstructionSet::GLSL_std_450)
272
Prefix = "f";
273
break;
274
}
275
276
// If argument-type name prefix was added, look up the builtin again.
277
if (!Prefix.empty() &&
278
(Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set)))
279
return std::make_unique<SPIRV::IncomingCall>(
280
BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
281
282
// If lookup with a prefix failed, find a suffix to be added to the
283
// builtin's name for lookup. For example, OpenCL "group_reduce_max" taking
284
// an unsigned value has a suffix "u".
285
std::string Suffix;
286
287
switch (FirstArgumentType) {
288
// Unsigned:
289
case 'u':
290
Suffix = "u";
291
break;
292
// Signed:
293
case 'c':
294
case 's':
295
case 'i':
296
case 'l':
297
Suffix = "s";
298
break;
299
// Floating-point:
300
case 'f':
301
case 'd':
302
case 'h':
303
Suffix = "f";
304
break;
305
}
306
307
// If argument-type name suffix was added, look up the builtin again.
308
if (!Suffix.empty() &&
309
(Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set)))
310
return std::make_unique<SPIRV::IncomingCall>(
311
BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
312
}
313
314
// No builtin with such name was found in the set.
315
return nullptr;
316
}
317
318
static MachineInstr *getBlockStructInstr(Register ParamReg,
319
MachineRegisterInfo *MRI) {
320
// We expect the following sequence of instructions:
321
// %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)
322
// or = G_GLOBAL_VALUE @block_literal_global
323
// %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0
324
// %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)
325
MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
326
assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
327
MI->getOperand(1).isReg());
328
Register BitcastReg = MI->getOperand(1).getReg();
329
MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
330
assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&
331
BitcastMI->getOperand(2).isReg());
332
Register ValueReg = BitcastMI->getOperand(2).getReg();
333
MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);
334
return ValueMI;
335
}
336
337
// Return an integer constant corresponding to the given register and
338
// defined in spv_track_constant.
339
// TODO: maybe unify with prelegalizer pass.
340
static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) {
341
MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);
342
assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&
343
DefMI->getOperand(2).isReg());
344
MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());
345
assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&
346
DefMI2->getOperand(1).isCImm());
347
return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();
348
}
349
350
// Return type of the instruction result from spv_assign_type intrinsic.
351
// TODO: maybe unify with prelegalizer pass.
352
static const Type *getMachineInstrType(MachineInstr *MI) {
353
MachineInstr *NextMI = MI->getNextNode();
354
if (!NextMI)
355
return nullptr;
356
if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
357
if ((NextMI = NextMI->getNextNode()) == nullptr)
358
return nullptr;
359
Register ValueReg = MI->getOperand(0).getReg();
360
if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) &&
361
!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) ||
362
NextMI->getOperand(1).getReg() != ValueReg)
363
return nullptr;
364
Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
365
assert(Ty && "Type is expected");
366
return Ty;
367
}
368
369
static const Type *getBlockStructType(Register ParamReg,
370
MachineRegisterInfo *MRI) {
371
// In principle, this information should be passed to us from Clang via
372
// an elementtype attribute. However, said attribute requires that
373
// the function call be an intrinsic, which is not. Instead, we rely on being
374
// able to trace this to the declaration of a variable: OpenCL C specification
375
// section 6.12.5 should guarantee that we can do this.
376
MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);
377
if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
378
return MI->getOperand(1).getGlobal()->getType();
379
assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
380
"Blocks in OpenCL C must be traceable to allocation site");
381
return getMachineInstrType(MI);
382
}
383
384
//===----------------------------------------------------------------------===//
385
// Helper functions for building misc instructions
386
//===----------------------------------------------------------------------===//
387
388
/// Helper function building either a resulting scalar or vector bool register
389
/// depending on the expected \p ResultType.
390
///
391
/// \returns Tuple of the resulting register and its type.
392
static std::tuple<Register, SPIRVType *>
393
buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType,
394
SPIRVGlobalRegistry *GR) {
395
LLT Type;
396
SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
397
398
if (ResultType->getOpcode() == SPIRV::OpTypeVector) {
399
unsigned VectorElements = ResultType->getOperand(2).getImm();
400
BoolType =
401
GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder);
402
const FixedVectorType *LLVMVectorType =
403
cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType));
404
Type = LLT::vector(LLVMVectorType->getElementCount(), 1);
405
} else {
406
Type = LLT::scalar(1);
407
}
408
409
Register ResultRegister =
410
MIRBuilder.getMRI()->createGenericVirtualRegister(Type);
411
MIRBuilder.getMRI()->setRegClass(ResultRegister, &SPIRV::IDRegClass);
412
GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF());
413
return std::make_tuple(ResultRegister, BoolType);
414
}
415
416
/// Helper function for building either a vector or scalar select instruction
417
/// depending on the expected \p ResultType.
418
static bool buildSelectInst(MachineIRBuilder &MIRBuilder,
419
Register ReturnRegister, Register SourceRegister,
420
const SPIRVType *ReturnType,
421
SPIRVGlobalRegistry *GR) {
422
Register TrueConst, FalseConst;
423
424
if (ReturnType->getOpcode() == SPIRV::OpTypeVector) {
425
unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType);
426
uint64_t AllOnes = APInt::getAllOnes(Bits).getZExtValue();
427
TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType);
428
FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType);
429
} else {
430
TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType);
431
FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType);
432
}
433
return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst,
434
FalseConst);
435
}
436
437
/// Helper function for building a load instruction loading into the
438
/// \p DestinationReg.
439
static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister,
440
MachineIRBuilder &MIRBuilder,
441
SPIRVGlobalRegistry *GR, LLT LowLevelType,
442
Register DestinationReg = Register(0)) {
443
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
444
if (!DestinationReg.isValid()) {
445
DestinationReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
446
MRI->setType(DestinationReg, LLT::scalar(32));
447
GR->assignSPIRVTypeToVReg(BaseType, DestinationReg, MIRBuilder.getMF());
448
}
449
// TODO: consider using correct address space and alignment (p0 is canonical
450
// type for selection though).
451
MachinePointerInfo PtrInfo = MachinePointerInfo();
452
MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align());
453
return DestinationReg;
454
}
455
456
/// Helper function for building a load instruction for loading a builtin global
457
/// variable of \p BuiltinValue value.
458
static Register buildBuiltinVariableLoad(
459
MachineIRBuilder &MIRBuilder, SPIRVType *VariableType,
460
SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType,
461
Register Reg = Register(0), bool isConst = true, bool hasLinkageTy = true) {
462
Register NewRegister =
463
MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass);
464
MIRBuilder.getMRI()->setType(NewRegister,
465
LLT::pointer(0, GR->getPointerSize()));
466
SPIRVType *PtrType = GR->getOrCreateSPIRVPointerType(
467
VariableType, MIRBuilder, SPIRV::StorageClass::Input);
468
GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
469
470
// Set up the global OpVariable with the necessary builtin decorations.
471
Register Variable = GR->buildGlobalVariable(
472
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
473
SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst,
474
/* HasLinkageTy */ hasLinkageTy, SPIRV::LinkageType::Import, MIRBuilder,
475
false);
476
477
// Load the value from the global variable.
478
Register LoadedRegister =
479
buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg);
480
MIRBuilder.getMRI()->setType(LoadedRegister, LLType);
481
return LoadedRegister;
482
}
483
484
/// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg
485
/// and its definition, set the new register as a destination of the definition,
486
/// assign SPIRVType to both registers. If SpirvTy is provided, use it as
487
/// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in
488
/// SPIRVPreLegalizer.cpp.
489
extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
490
SPIRVGlobalRegistry *GR,
491
MachineIRBuilder &MIB,
492
MachineRegisterInfo &MRI);
493
494
// TODO: Move to TableGen.
495
static SPIRV::MemorySemantics::MemorySemantics
496
getSPIRVMemSemantics(std::memory_order MemOrder) {
497
switch (MemOrder) {
498
case std::memory_order::memory_order_relaxed:
499
return SPIRV::MemorySemantics::None;
500
case std::memory_order::memory_order_acquire:
501
return SPIRV::MemorySemantics::Acquire;
502
case std::memory_order::memory_order_release:
503
return SPIRV::MemorySemantics::Release;
504
case std::memory_order::memory_order_acq_rel:
505
return SPIRV::MemorySemantics::AcquireRelease;
506
case std::memory_order::memory_order_seq_cst:
507
return SPIRV::MemorySemantics::SequentiallyConsistent;
508
default:
509
report_fatal_error("Unknown CL memory scope");
510
}
511
}
512
513
static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) {
514
switch (ClScope) {
515
case SPIRV::CLMemoryScope::memory_scope_work_item:
516
return SPIRV::Scope::Invocation;
517
case SPIRV::CLMemoryScope::memory_scope_work_group:
518
return SPIRV::Scope::Workgroup;
519
case SPIRV::CLMemoryScope::memory_scope_device:
520
return SPIRV::Scope::Device;
521
case SPIRV::CLMemoryScope::memory_scope_all_svm_devices:
522
return SPIRV::Scope::CrossDevice;
523
case SPIRV::CLMemoryScope::memory_scope_sub_group:
524
return SPIRV::Scope::Subgroup;
525
}
526
report_fatal_error("Unknown CL memory scope");
527
}
528
529
static Register buildConstantIntReg(uint64_t Val, MachineIRBuilder &MIRBuilder,
530
SPIRVGlobalRegistry *GR,
531
unsigned BitWidth = 32) {
532
SPIRVType *IntType = GR->getOrCreateSPIRVIntegerType(BitWidth, MIRBuilder);
533
return GR->buildConstantInt(Val, MIRBuilder, IntType);
534
}
535
536
static Register buildScopeReg(Register CLScopeRegister,
537
SPIRV::Scope::Scope Scope,
538
MachineIRBuilder &MIRBuilder,
539
SPIRVGlobalRegistry *GR,
540
MachineRegisterInfo *MRI) {
541
if (CLScopeRegister.isValid()) {
542
auto CLScope =
543
static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI));
544
Scope = getSPIRVScope(CLScope);
545
546
if (CLScope == static_cast<unsigned>(Scope)) {
547
MRI->setRegClass(CLScopeRegister, &SPIRV::IDRegClass);
548
return CLScopeRegister;
549
}
550
}
551
return buildConstantIntReg(Scope, MIRBuilder, GR);
552
}
553
554
static Register buildMemSemanticsReg(Register SemanticsRegister,
555
Register PtrRegister, unsigned &Semantics,
556
MachineIRBuilder &MIRBuilder,
557
SPIRVGlobalRegistry *GR) {
558
if (SemanticsRegister.isValid()) {
559
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
560
std::memory_order Order =
561
static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI));
562
Semantics =
563
getSPIRVMemSemantics(Order) |
564
getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
565
566
if (Order == Semantics) {
567
MRI->setRegClass(SemanticsRegister, &SPIRV::IDRegClass);
568
return SemanticsRegister;
569
}
570
}
571
return buildConstantIntReg(Semantics, MIRBuilder, GR);
572
}
573
574
static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode,
575
const SPIRV::IncomingCall *Call,
576
Register TypeReg,
577
ArrayRef<uint32_t> ImmArgs = {}) {
578
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
579
auto MIB = MIRBuilder.buildInstr(Opcode);
580
if (TypeReg.isValid())
581
MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
582
unsigned Sz = Call->Arguments.size() - ImmArgs.size();
583
for (unsigned i = 0; i < Sz; ++i) {
584
Register ArgReg = Call->Arguments[i];
585
if (!MRI->getRegClassOrNull(ArgReg))
586
MRI->setRegClass(ArgReg, &SPIRV::IDRegClass);
587
MIB.addUse(ArgReg);
588
}
589
for (uint32_t ImmArg : ImmArgs)
590
MIB.addImm(ImmArg);
591
return true;
592
}
593
594
/// Helper function for translating atomic init to OpStore.
595
static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call,
596
MachineIRBuilder &MIRBuilder) {
597
if (Call->isSpirvOp())
598
return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0));
599
600
assert(Call->Arguments.size() == 2 &&
601
"Need 2 arguments for atomic init translation");
602
MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
603
MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
604
MIRBuilder.buildInstr(SPIRV::OpStore)
605
.addUse(Call->Arguments[0])
606
.addUse(Call->Arguments[1]);
607
return true;
608
}
609
610
/// Helper function for building an atomic load instruction.
611
static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call,
612
MachineIRBuilder &MIRBuilder,
613
SPIRVGlobalRegistry *GR) {
614
Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
615
if (Call->isSpirvOp())
616
return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg);
617
618
Register PtrRegister = Call->Arguments[0];
619
MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass);
620
// TODO: if true insert call to __translate_ocl_memory_sccope before
621
// OpAtomicLoad and the function implementation. We can use Translator's
622
// output for transcoding/atomic_explicit_arguments.cl as an example.
623
Register ScopeRegister;
624
if (Call->Arguments.size() > 1) {
625
ScopeRegister = Call->Arguments[1];
626
MIRBuilder.getMRI()->setRegClass(ScopeRegister, &SPIRV::IDRegClass);
627
} else
628
ScopeRegister = buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);
629
630
Register MemSemanticsReg;
631
if (Call->Arguments.size() > 2) {
632
// TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad.
633
MemSemanticsReg = Call->Arguments[2];
634
MIRBuilder.getMRI()->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);
635
} else {
636
int Semantics =
637
SPIRV::MemorySemantics::SequentiallyConsistent |
638
getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
639
MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
640
}
641
642
MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
643
.addDef(Call->ReturnRegister)
644
.addUse(TypeReg)
645
.addUse(PtrRegister)
646
.addUse(ScopeRegister)
647
.addUse(MemSemanticsReg);
648
return true;
649
}
650
651
/// Helper function for building an atomic store instruction.
652
static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call,
653
MachineIRBuilder &MIRBuilder,
654
SPIRVGlobalRegistry *GR) {
655
if (Call->isSpirvOp())
656
return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call, Register(0));
657
658
Register ScopeRegister =
659
buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);
660
Register PtrRegister = Call->Arguments[0];
661
MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass);
662
int Semantics =
663
SPIRV::MemorySemantics::SequentiallyConsistent |
664
getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));
665
Register MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);
666
MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
667
MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
668
.addUse(PtrRegister)
669
.addUse(ScopeRegister)
670
.addUse(MemSemanticsReg)
671
.addUse(Call->Arguments[1]);
672
return true;
673
}
674
675
/// Helper function for building an atomic compare-exchange instruction.
676
static bool buildAtomicCompareExchangeInst(
677
const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin,
678
unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
679
if (Call->isSpirvOp())
680
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
681
GR->getSPIRVTypeID(Call->ReturnType));
682
683
bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg");
684
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
685
686
Register ObjectPtr = Call->Arguments[0]; // Pointer (volatile A *object.)
687
Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).
688
Register Desired = Call->Arguments[2]; // Value (C Desired).
689
MRI->setRegClass(ObjectPtr, &SPIRV::IDRegClass);
690
MRI->setRegClass(ExpectedArg, &SPIRV::IDRegClass);
691
MRI->setRegClass(Desired, &SPIRV::IDRegClass);
692
SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
693
LLT DesiredLLT = MRI->getType(Desired);
694
695
assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() ==
696
SPIRV::OpTypePointer);
697
unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode();
698
(void)ExpectedType;
699
assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt
700
: ExpectedType == SPIRV::OpTypePointer);
701
assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
702
703
SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
704
assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected");
705
auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
706
SpvObjectPtrTy->getOperand(1).getImm());
707
auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
708
709
Register MemSemEqualReg;
710
Register MemSemUnequalReg;
711
uint64_t MemSemEqual =
712
IsCmpxchg
713
? SPIRV::MemorySemantics::None
714
: SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
715
uint64_t MemSemUnequal =
716
IsCmpxchg
717
? SPIRV::MemorySemantics::None
718
: SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
719
if (Call->Arguments.size() >= 4) {
720
assert(Call->Arguments.size() >= 5 &&
721
"Need 5+ args for explicit atomic cmpxchg");
722
auto MemOrdEq =
723
static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));
724
auto MemOrdNeq =
725
static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));
726
MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;
727
MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;
728
if (MemOrdEq == MemSemEqual)
729
MemSemEqualReg = Call->Arguments[3];
730
if (MemOrdNeq == MemSemEqual)
731
MemSemUnequalReg = Call->Arguments[4];
732
MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass);
733
MRI->setRegClass(Call->Arguments[4], &SPIRV::IDRegClass);
734
}
735
if (!MemSemEqualReg.isValid())
736
MemSemEqualReg = buildConstantIntReg(MemSemEqual, MIRBuilder, GR);
737
if (!MemSemUnequalReg.isValid())
738
MemSemUnequalReg = buildConstantIntReg(MemSemUnequal, MIRBuilder, GR);
739
740
Register ScopeReg;
741
auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;
742
if (Call->Arguments.size() >= 6) {
743
assert(Call->Arguments.size() == 6 &&
744
"Extra args for explicit atomic cmpxchg");
745
auto ClScope = static_cast<SPIRV::CLMemoryScope>(
746
getIConstVal(Call->Arguments[5], MRI));
747
Scope = getSPIRVScope(ClScope);
748
if (ClScope == static_cast<unsigned>(Scope))
749
ScopeReg = Call->Arguments[5];
750
MRI->setRegClass(Call->Arguments[5], &SPIRV::IDRegClass);
751
}
752
if (!ScopeReg.isValid())
753
ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);
754
755
Register Expected = IsCmpxchg
756
? ExpectedArg
757
: buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder,
758
GR, LLT::scalar(32));
759
MRI->setType(Expected, DesiredLLT);
760
Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)
761
: Call->ReturnRegister;
762
if (!MRI->getRegClassOrNull(Tmp))
763
MRI->setRegClass(Tmp, &SPIRV::IDRegClass);
764
GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());
765
766
SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
767
MIRBuilder.buildInstr(Opcode)
768
.addDef(Tmp)
769
.addUse(GR->getSPIRVTypeID(IntTy))
770
.addUse(ObjectPtr)
771
.addUse(ScopeReg)
772
.addUse(MemSemEqualReg)
773
.addUse(MemSemUnequalReg)
774
.addUse(Desired)
775
.addUse(Expected);
776
if (!IsCmpxchg) {
777
MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);
778
MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);
779
}
780
return true;
781
}
782
783
/// Helper function for building atomic instructions.
784
static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
785
MachineIRBuilder &MIRBuilder,
786
SPIRVGlobalRegistry *GR) {
787
if (Call->isSpirvOp())
788
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
789
GR->getSPIRVTypeID(Call->ReturnType));
790
791
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
792
Register ScopeRegister =
793
Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register();
794
795
assert(Call->Arguments.size() <= 4 &&
796
"Too many args for explicit atomic RMW");
797
ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup,
798
MIRBuilder, GR, MRI);
799
800
Register PtrRegister = Call->Arguments[0];
801
unsigned Semantics = SPIRV::MemorySemantics::None;
802
MRI->setRegClass(PtrRegister, &SPIRV::IDRegClass);
803
Register MemSemanticsReg =
804
Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
805
MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
806
Semantics, MIRBuilder, GR);
807
MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
808
Register ValueReg = Call->Arguments[1];
809
Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);
810
// support cl_ext_float_atomics
811
if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {
812
if (Opcode == SPIRV::OpAtomicIAdd) {
813
Opcode = SPIRV::OpAtomicFAddEXT;
814
} else if (Opcode == SPIRV::OpAtomicISub) {
815
// Translate OpAtomicISub applied to a floating type argument to
816
// OpAtomicFAddEXT with the negative value operand
817
Opcode = SPIRV::OpAtomicFAddEXT;
818
Register NegValueReg =
819
MRI->createGenericVirtualRegister(MRI->getType(ValueReg));
820
MRI->setRegClass(NegValueReg, &SPIRV::IDRegClass);
821
GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg,
822
MIRBuilder.getMF());
823
MIRBuilder.buildInstr(TargetOpcode::G_FNEG)
824
.addDef(NegValueReg)
825
.addUse(ValueReg);
826
insertAssignInstr(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder,
827
MIRBuilder.getMF().getRegInfo());
828
ValueReg = NegValueReg;
829
}
830
}
831
MIRBuilder.buildInstr(Opcode)
832
.addDef(Call->ReturnRegister)
833
.addUse(ValueTypeReg)
834
.addUse(PtrRegister)
835
.addUse(ScopeRegister)
836
.addUse(MemSemanticsReg)
837
.addUse(ValueReg);
838
return true;
839
}
840
841
/// Helper function for building an atomic floating-type instruction.
842
static bool buildAtomicFloatingRMWInst(const SPIRV::IncomingCall *Call,
843
unsigned Opcode,
844
MachineIRBuilder &MIRBuilder,
845
SPIRVGlobalRegistry *GR) {
846
assert(Call->Arguments.size() == 4 &&
847
"Wrong number of atomic floating-type builtin");
848
849
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
850
851
Register PtrReg = Call->Arguments[0];
852
MRI->setRegClass(PtrReg, &SPIRV::IDRegClass);
853
854
Register ScopeReg = Call->Arguments[1];
855
MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
856
857
Register MemSemanticsReg = Call->Arguments[2];
858
MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);
859
860
Register ValueReg = Call->Arguments[3];
861
MRI->setRegClass(ValueReg, &SPIRV::IDRegClass);
862
863
MIRBuilder.buildInstr(Opcode)
864
.addDef(Call->ReturnRegister)
865
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
866
.addUse(PtrReg)
867
.addUse(ScopeReg)
868
.addUse(MemSemanticsReg)
869
.addUse(ValueReg);
870
return true;
871
}
872
873
/// Helper function for building atomic flag instructions (e.g.
874
/// OpAtomicFlagTestAndSet).
875
static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,
876
unsigned Opcode, MachineIRBuilder &MIRBuilder,
877
SPIRVGlobalRegistry *GR) {
878
bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet;
879
Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
880
if (Call->isSpirvOp())
881
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
882
IsSet ? TypeReg : Register(0));
883
884
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
885
Register PtrRegister = Call->Arguments[0];
886
unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent;
887
Register MemSemanticsReg =
888
Call->Arguments.size() >= 2 ? Call->Arguments[1] : Register();
889
MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
890
Semantics, MIRBuilder, GR);
891
892
assert((Opcode != SPIRV::OpAtomicFlagClear ||
893
(Semantics != SPIRV::MemorySemantics::Acquire &&
894
Semantics != SPIRV::MemorySemantics::AcquireRelease)) &&
895
"Invalid memory order argument!");
896
897
Register ScopeRegister =
898
Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
899
ScopeRegister =
900
buildScopeReg(ScopeRegister, SPIRV::Scope::Device, MIRBuilder, GR, MRI);
901
902
auto MIB = MIRBuilder.buildInstr(Opcode);
903
if (IsSet)
904
MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
905
906
MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);
907
return true;
908
}
909
910
/// Helper function for building barriers, i.e., memory/control ordering
911
/// operations.
912
static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
913
MachineIRBuilder &MIRBuilder,
914
SPIRVGlobalRegistry *GR) {
915
if (Call->isSpirvOp())
916
return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
917
918
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
919
unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);
920
unsigned MemSemantics = SPIRV::MemorySemantics::None;
921
922
if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
923
MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
924
925
if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)
926
MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;
927
928
if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)
929
MemSemantics |= SPIRV::MemorySemantics::ImageMemory;
930
931
if (Opcode == SPIRV::OpMemoryBarrier) {
932
std::memory_order MemOrder =
933
static_cast<std::memory_order>(getIConstVal(Call->Arguments[1], MRI));
934
MemSemantics = getSPIRVMemSemantics(MemOrder) | MemSemantics;
935
} else {
936
MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;
937
}
938
939
Register MemSemanticsReg;
940
if (MemFlags == MemSemantics) {
941
MemSemanticsReg = Call->Arguments[0];
942
MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);
943
} else
944
MemSemanticsReg = buildConstantIntReg(MemSemantics, MIRBuilder, GR);
945
946
Register ScopeReg;
947
SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
948
SPIRV::Scope::Scope MemScope = Scope;
949
if (Call->Arguments.size() >= 2) {
950
assert(
951
((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||
952
(Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&
953
"Extra args for explicitly scoped barrier");
954
Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]
955
: Call->Arguments[1];
956
SPIRV::CLMemoryScope CLScope =
957
static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
958
MemScope = getSPIRVScope(CLScope);
959
if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
960
(Opcode == SPIRV::OpMemoryBarrier))
961
Scope = MemScope;
962
963
if (CLScope == static_cast<unsigned>(Scope)) {
964
ScopeReg = Call->Arguments[1];
965
MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
966
}
967
}
968
969
if (!ScopeReg.isValid())
970
ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);
971
972
auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);
973
if (Opcode != SPIRV::OpMemoryBarrier)
974
MIB.addUse(buildConstantIntReg(MemScope, MIRBuilder, GR));
975
MIB.addUse(MemSemanticsReg);
976
return true;
977
}
978
979
static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
980
switch (dim) {
981
case SPIRV::Dim::DIM_1D:
982
case SPIRV::Dim::DIM_Buffer:
983
return 1;
984
case SPIRV::Dim::DIM_2D:
985
case SPIRV::Dim::DIM_Cube:
986
case SPIRV::Dim::DIM_Rect:
987
return 2;
988
case SPIRV::Dim::DIM_3D:
989
return 3;
990
default:
991
report_fatal_error("Cannot get num components for given Dim");
992
}
993
}
994
995
/// Helper function for obtaining the number of size components.
996
static unsigned getNumSizeComponents(SPIRVType *imgType) {
997
assert(imgType->getOpcode() == SPIRV::OpTypeImage);
998
auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());
999
unsigned numComps = getNumComponentsForDim(dim);
1000
bool arrayed = imgType->getOperand(4).getImm() == 1;
1001
return arrayed ? numComps + 1 : numComps;
1002
}
1003
1004
//===----------------------------------------------------------------------===//
1005
// Implementation functions for each builtin group
1006
//===----------------------------------------------------------------------===//
1007
1008
static bool generateExtInst(const SPIRV::IncomingCall *Call,
1009
MachineIRBuilder &MIRBuilder,
1010
SPIRVGlobalRegistry *GR) {
1011
// Lookup the extended instruction number in the TableGen records.
1012
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1013
uint32_t Number =
1014
SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number;
1015
1016
// Build extended instruction.
1017
auto MIB =
1018
MIRBuilder.buildInstr(SPIRV::OpExtInst)
1019
.addDef(Call->ReturnRegister)
1020
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1021
.addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1022
.addImm(Number);
1023
1024
for (auto Argument : Call->Arguments)
1025
MIB.addUse(Argument);
1026
return true;
1027
}
1028
1029
static bool generateRelationalInst(const SPIRV::IncomingCall *Call,
1030
MachineIRBuilder &MIRBuilder,
1031
SPIRVGlobalRegistry *GR) {
1032
// Lookup the instruction opcode in the TableGen records.
1033
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1034
unsigned Opcode =
1035
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1036
1037
Register CompareRegister;
1038
SPIRVType *RelationType;
1039
std::tie(CompareRegister, RelationType) =
1040
buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1041
1042
// Build relational instruction.
1043
auto MIB = MIRBuilder.buildInstr(Opcode)
1044
.addDef(CompareRegister)
1045
.addUse(GR->getSPIRVTypeID(RelationType));
1046
1047
for (auto Argument : Call->Arguments)
1048
MIB.addUse(Argument);
1049
1050
// Build select instruction.
1051
return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,
1052
Call->ReturnType, GR);
1053
}
1054
1055
static bool generateGroupInst(const SPIRV::IncomingCall *Call,
1056
MachineIRBuilder &MIRBuilder,
1057
SPIRVGlobalRegistry *GR) {
1058
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1059
const SPIRV::GroupBuiltin *GroupBuiltin =
1060
SPIRV::lookupGroupBuiltin(Builtin->Name);
1061
1062
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1063
if (Call->isSpirvOp()) {
1064
if (GroupBuiltin->NoGroupOperation)
1065
return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call,
1066
GR->getSPIRVTypeID(Call->ReturnType));
1067
1068
// Group Operation is a literal
1069
Register GroupOpReg = Call->Arguments[1];
1070
const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI);
1071
if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT)
1072
report_fatal_error(
1073
"Group Operation parameter must be an integer constant");
1074
uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue();
1075
Register ScopeReg = Call->Arguments[0];
1076
if (!MRI->getRegClassOrNull(ScopeReg))
1077
MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
1078
auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1079
.addDef(Call->ReturnRegister)
1080
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1081
.addUse(ScopeReg)
1082
.addImm(GrpOp);
1083
for (unsigned i = 2; i < Call->Arguments.size(); ++i) {
1084
Register ArgReg = Call->Arguments[i];
1085
if (!MRI->getRegClassOrNull(ArgReg))
1086
MRI->setRegClass(ArgReg, &SPIRV::IDRegClass);
1087
MIB.addUse(ArgReg);
1088
}
1089
return true;
1090
}
1091
1092
Register Arg0;
1093
if (GroupBuiltin->HasBoolArg) {
1094
Register ConstRegister = Call->Arguments[0];
1095
auto ArgInstruction = getDefInstrMaybeConstant(ConstRegister, MRI);
1096
(void)ArgInstruction;
1097
// TODO: support non-constant bool values.
1098
assert(ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT &&
1099
"Only constant bool value args are supported");
1100
if (GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode() !=
1101
SPIRV::OpTypeBool)
1102
Arg0 = GR->buildConstantInt(getIConstVal(ConstRegister, MRI), MIRBuilder,
1103
GR->getOrCreateSPIRVBoolType(MIRBuilder));
1104
}
1105
1106
Register GroupResultRegister = Call->ReturnRegister;
1107
SPIRVType *GroupResultType = Call->ReturnType;
1108
1109
// TODO: maybe we need to check whether the result type is already boolean
1110
// and in this case do not insert select instruction.
1111
const bool HasBoolReturnTy =
1112
GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||
1113
GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||
1114
GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;
1115
1116
if (HasBoolReturnTy)
1117
std::tie(GroupResultRegister, GroupResultType) =
1118
buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1119
1120
auto Scope = Builtin->Name.starts_with("sub_group") ? SPIRV::Scope::Subgroup
1121
: SPIRV::Scope::Workgroup;
1122
Register ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR);
1123
1124
// Build work/sub group instruction.
1125
auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1126
.addDef(GroupResultRegister)
1127
.addUse(GR->getSPIRVTypeID(GroupResultType))
1128
.addUse(ScopeRegister);
1129
1130
if (!GroupBuiltin->NoGroupOperation)
1131
MIB.addImm(GroupBuiltin->GroupOperation);
1132
if (Call->Arguments.size() > 0) {
1133
MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);
1134
MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
1135
for (unsigned i = 1; i < Call->Arguments.size(); i++) {
1136
MIB.addUse(Call->Arguments[i]);
1137
MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass);
1138
}
1139
}
1140
1141
// Build select instruction.
1142
if (HasBoolReturnTy)
1143
buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,
1144
Call->ReturnType, GR);
1145
return true;
1146
}
1147
1148
static bool generateIntelSubgroupsInst(const SPIRV::IncomingCall *Call,
1149
MachineIRBuilder &MIRBuilder,
1150
SPIRVGlobalRegistry *GR) {
1151
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1152
MachineFunction &MF = MIRBuilder.getMF();
1153
const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1154
if (!ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1155
std::string DiagMsg = std::string(Builtin->Name) +
1156
": the builtin requires the following SPIR-V "
1157
"extension: SPV_INTEL_subgroups";
1158
report_fatal_error(DiagMsg.c_str(), false);
1159
}
1160
const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups =
1161
SPIRV::lookupIntelSubgroupsBuiltin(Builtin->Name);
1162
1163
uint32_t OpCode = IntelSubgroups->Opcode;
1164
if (Call->isSpirvOp()) {
1165
bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL &&
1166
OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL;
1167
return buildOpFromWrapper(MIRBuilder, OpCode, Call,
1168
IsSet ? GR->getSPIRVTypeID(Call->ReturnType)
1169
: Register(0));
1170
}
1171
1172
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1173
if (IntelSubgroups->IsBlock) {
1174
// Minimal number or arguments set in TableGen records is 1
1175
if (SPIRVType *Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) {
1176
if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) {
1177
// TODO: add required validation from the specification:
1178
// "'Image' must be an object whose type is OpTypeImage with a 'Sampled'
1179
// operand of 0 or 2. If the 'Sampled' operand is 2, then some
1180
// dimensions require a capability."
1181
switch (OpCode) {
1182
case SPIRV::OpSubgroupBlockReadINTEL:
1183
OpCode = SPIRV::OpSubgroupImageBlockReadINTEL;
1184
break;
1185
case SPIRV::OpSubgroupBlockWriteINTEL:
1186
OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL;
1187
break;
1188
}
1189
}
1190
}
1191
}
1192
1193
// TODO: opaque pointers types should be eventually resolved in such a way
1194
// that validation of block read is enabled with respect to the following
1195
// specification requirement:
1196
// "'Result Type' may be a scalar or vector type, and its component type must
1197
// be equal to the type pointed to by 'Ptr'."
1198
// For example, function parameter type should not be default i8 pointer, but
1199
// depend on the result type of the instruction where it is used as a pointer
1200
// argument of OpSubgroupBlockReadINTEL
1201
1202
// Build Intel subgroups instruction
1203
MachineInstrBuilder MIB =
1204
IntelSubgroups->IsWrite
1205
? MIRBuilder.buildInstr(OpCode)
1206
: MIRBuilder.buildInstr(OpCode)
1207
.addDef(Call->ReturnRegister)
1208
.addUse(GR->getSPIRVTypeID(Call->ReturnType));
1209
for (size_t i = 0; i < Call->Arguments.size(); ++i) {
1210
MIB.addUse(Call->Arguments[i]);
1211
MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass);
1212
}
1213
1214
return true;
1215
}
1216
1217
static bool generateGroupUniformInst(const SPIRV::IncomingCall *Call,
1218
MachineIRBuilder &MIRBuilder,
1219
SPIRVGlobalRegistry *GR) {
1220
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1221
MachineFunction &MF = MIRBuilder.getMF();
1222
const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1223
if (!ST->canUseExtension(
1224
SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {
1225
std::string DiagMsg = std::string(Builtin->Name) +
1226
": the builtin requires the following SPIR-V "
1227
"extension: SPV_KHR_uniform_group_instructions";
1228
report_fatal_error(DiagMsg.c_str(), false);
1229
}
1230
const SPIRV::GroupUniformBuiltin *GroupUniform =
1231
SPIRV::lookupGroupUniformBuiltin(Builtin->Name);
1232
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1233
1234
Register GroupResultReg = Call->ReturnRegister;
1235
MRI->setRegClass(GroupResultReg, &SPIRV::IDRegClass);
1236
1237
// Scope
1238
Register ScopeReg = Call->Arguments[0];
1239
MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);
1240
1241
// Group Operation
1242
Register ConstGroupOpReg = Call->Arguments[1];
1243
const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI);
1244
if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT)
1245
report_fatal_error(
1246
"expect a constant group operation for a uniform group instruction",
1247
false);
1248
const MachineOperand &ConstOperand = Const->getOperand(1);
1249
if (!ConstOperand.isCImm())
1250
report_fatal_error("uniform group instructions: group operation must be an "
1251
"integer constant",
1252
false);
1253
1254
// Value
1255
Register ValueReg = Call->Arguments[2];
1256
MRI->setRegClass(ValueReg, &SPIRV::IDRegClass);
1257
1258
auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode)
1259
.addDef(GroupResultReg)
1260
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1261
.addUse(ScopeReg);
1262
addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1263
MIB.addUse(ValueReg);
1264
1265
return true;
1266
}
1267
1268
static bool generateKernelClockInst(const SPIRV::IncomingCall *Call,
1269
MachineIRBuilder &MIRBuilder,
1270
SPIRVGlobalRegistry *GR) {
1271
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1272
MachineFunction &MF = MIRBuilder.getMF();
1273
const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1274
if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) {
1275
std::string DiagMsg = std::string(Builtin->Name) +
1276
": the builtin requires the following SPIR-V "
1277
"extension: SPV_KHR_shader_clock";
1278
report_fatal_error(DiagMsg.c_str(), false);
1279
}
1280
1281
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1282
Register ResultReg = Call->ReturnRegister;
1283
MRI->setRegClass(ResultReg, &SPIRV::IDRegClass);
1284
1285
// Deduce the `Scope` operand from the builtin function name.
1286
SPIRV::Scope::Scope ScopeArg =
1287
StringSwitch<SPIRV::Scope::Scope>(Builtin->Name)
1288
.EndsWith("device", SPIRV::Scope::Scope::Device)
1289
.EndsWith("work_group", SPIRV::Scope::Scope::Workgroup)
1290
.EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup);
1291
Register ScopeReg = buildConstantIntReg(ScopeArg, MIRBuilder, GR);
1292
1293
MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
1294
.addDef(ResultReg)
1295
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1296
.addUse(ScopeReg);
1297
1298
return true;
1299
}
1300
1301
// These queries ask for a single size_t result for a given dimension index, e.g
1302
// size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to
1303
// these values are all vec3 types, so we need to extract the correct index or
1304
// return defaultVal (0 or 1 depending on the query). We also handle extending
1305
// or tuncating in case size_t does not match the expected result type's
1306
// bitwidth.
1307
//
1308
// For a constant index >= 3 we generate:
1309
// %res = OpConstant %SizeT 0
1310
//
1311
// For other indices we generate:
1312
// %g = OpVariable %ptr_V3_SizeT Input
1313
// OpDecorate %g BuiltIn XXX
1314
// OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"
1315
// OpDecorate %g Constant
1316
// %loadedVec = OpLoad %V3_SizeT %g
1317
//
1318
// Then, if the index is constant < 3, we generate:
1319
// %res = OpCompositeExtract %SizeT %loadedVec idx
1320
// If the index is dynamic, we generate:
1321
// %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx
1322
// %cmp = OpULessThan %bool %idx %const_3
1323
// %res = OpSelect %SizeT %cmp %tmp %const_0
1324
//
1325
// If the bitwidth of %res does not match the expected return type, we add an
1326
// extend or truncate.
1327
static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call,
1328
MachineIRBuilder &MIRBuilder,
1329
SPIRVGlobalRegistry *GR,
1330
SPIRV::BuiltIn::BuiltIn BuiltinValue,
1331
uint64_t DefaultValue) {
1332
Register IndexRegister = Call->Arguments[0];
1333
const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
1334
const unsigned PointerSize = GR->getPointerSize();
1335
const SPIRVType *PointerSizeType =
1336
GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);
1337
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1338
auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);
1339
1340
// Set up the final register to do truncation or extension on at the end.
1341
Register ToTruncate = Call->ReturnRegister;
1342
1343
// If the index is constant, we can statically determine if it is in range.
1344
bool IsConstantIndex =
1345
IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;
1346
1347
// If it's out of range (max dimension is 3), we can just return the constant
1348
// default value (0 or 1 depending on which query function).
1349
if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {
1350
Register DefaultReg = Call->ReturnRegister;
1351
if (PointerSize != ResultWidth) {
1352
DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1353
MRI->setRegClass(DefaultReg, &SPIRV::IDRegClass);
1354
GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg,
1355
MIRBuilder.getMF());
1356
ToTruncate = DefaultReg;
1357
}
1358
auto NewRegister =
1359
GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
1360
MIRBuilder.buildCopy(DefaultReg, NewRegister);
1361
} else { // If it could be in range, we need to load from the given builtin.
1362
auto Vec3Ty =
1363
GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder);
1364
Register LoadedVector =
1365
buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,
1366
LLT::fixed_vector(3, PointerSize));
1367
// Set up the vreg to extract the result to (possibly a new temporary one).
1368
Register Extracted = Call->ReturnRegister;
1369
if (!IsConstantIndex || PointerSize != ResultWidth) {
1370
Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1371
MRI->setRegClass(Extracted, &SPIRV::IDRegClass);
1372
GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());
1373
}
1374
// Use Intrinsic::spv_extractelt so dynamic vs static extraction is
1375
// handled later: extr = spv_extractelt LoadedVector, IndexRegister.
1376
MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(
1377
Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false);
1378
ExtractInst.addUse(LoadedVector).addUse(IndexRegister);
1379
1380
// If the index is dynamic, need check if it's < 3, and then use a select.
1381
if (!IsConstantIndex) {
1382
insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder,
1383
*MRI);
1384
1385
auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);
1386
auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
1387
1388
Register CompareRegister =
1389
MRI->createGenericVirtualRegister(LLT::scalar(1));
1390
MRI->setRegClass(CompareRegister, &SPIRV::IDRegClass);
1391
GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());
1392
1393
// Use G_ICMP to check if idxVReg < 3.
1394
MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister,
1395
GR->buildConstantInt(3, MIRBuilder, IndexType));
1396
1397
// Get constant for the default value (0 or 1 depending on which
1398
// function).
1399
Register DefaultRegister =
1400
GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
1401
1402
// Get a register for the selection result (possibly a new temporary one).
1403
Register SelectionResult = Call->ReturnRegister;
1404
if (PointerSize != ResultWidth) {
1405
SelectionResult =
1406
MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1407
MRI->setRegClass(SelectionResult, &SPIRV::IDRegClass);
1408
GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,
1409
MIRBuilder.getMF());
1410
}
1411
// Create the final G_SELECT to return the extracted value or the default.
1412
MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,
1413
DefaultRegister);
1414
ToTruncate = SelectionResult;
1415
} else {
1416
ToTruncate = Extracted;
1417
}
1418
}
1419
// Alter the result's bitwidth if it does not match the SizeT value extracted.
1420
if (PointerSize != ResultWidth)
1421
MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);
1422
return true;
1423
}
1424
1425
static bool generateBuiltinVar(const SPIRV::IncomingCall *Call,
1426
MachineIRBuilder &MIRBuilder,
1427
SPIRVGlobalRegistry *GR) {
1428
// Lookup the builtin variable record.
1429
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1430
SPIRV::BuiltIn::BuiltIn Value =
1431
SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1432
1433
if (Value == SPIRV::BuiltIn::GlobalInvocationId)
1434
return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);
1435
1436
// Build a load instruction for the builtin variable.
1437
unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);
1438
LLT LLType;
1439
if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector)
1440
LLType =
1441
LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth);
1442
else
1443
LLType = LLT::scalar(BitWidth);
1444
1445
return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,
1446
LLType, Call->ReturnRegister);
1447
}
1448
1449
static bool generateAtomicInst(const SPIRV::IncomingCall *Call,
1450
MachineIRBuilder &MIRBuilder,
1451
SPIRVGlobalRegistry *GR) {
1452
// Lookup the instruction opcode in the TableGen records.
1453
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1454
unsigned Opcode =
1455
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1456
1457
switch (Opcode) {
1458
case SPIRV::OpStore:
1459
return buildAtomicInitInst(Call, MIRBuilder);
1460
case SPIRV::OpAtomicLoad:
1461
return buildAtomicLoadInst(Call, MIRBuilder, GR);
1462
case SPIRV::OpAtomicStore:
1463
return buildAtomicStoreInst(Call, MIRBuilder, GR);
1464
case SPIRV::OpAtomicCompareExchange:
1465
case SPIRV::OpAtomicCompareExchangeWeak:
1466
return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder,
1467
GR);
1468
case SPIRV::OpAtomicIAdd:
1469
case SPIRV::OpAtomicISub:
1470
case SPIRV::OpAtomicOr:
1471
case SPIRV::OpAtomicXor:
1472
case SPIRV::OpAtomicAnd:
1473
case SPIRV::OpAtomicExchange:
1474
return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
1475
case SPIRV::OpMemoryBarrier:
1476
return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
1477
case SPIRV::OpAtomicFlagTestAndSet:
1478
case SPIRV::OpAtomicFlagClear:
1479
return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);
1480
default:
1481
if (Call->isSpirvOp())
1482
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1483
GR->getSPIRVTypeID(Call->ReturnType));
1484
return false;
1485
}
1486
}
1487
1488
static bool generateAtomicFloatingInst(const SPIRV::IncomingCall *Call,
1489
MachineIRBuilder &MIRBuilder,
1490
SPIRVGlobalRegistry *GR) {
1491
// Lookup the instruction opcode in the TableGen records.
1492
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1493
unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->Name)->Opcode;
1494
1495
switch (Opcode) {
1496
case SPIRV::OpAtomicFAddEXT:
1497
case SPIRV::OpAtomicFMinEXT:
1498
case SPIRV::OpAtomicFMaxEXT:
1499
return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR);
1500
default:
1501
return false;
1502
}
1503
}
1504
1505
static bool generateBarrierInst(const SPIRV::IncomingCall *Call,
1506
MachineIRBuilder &MIRBuilder,
1507
SPIRVGlobalRegistry *GR) {
1508
// Lookup the instruction opcode in the TableGen records.
1509
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1510
unsigned Opcode =
1511
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1512
1513
return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
1514
}
1515
1516
static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call,
1517
MachineIRBuilder &MIRBuilder) {
1518
MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)
1519
.addDef(Call->ReturnRegister)
1520
.addUse(Call->Arguments[0]);
1521
return true;
1522
}
1523
1524
static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call,
1525
MachineIRBuilder &MIRBuilder,
1526
SPIRVGlobalRegistry *GR) {
1527
if (Call->isSpirvOp())
1528
return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call,
1529
GR->getSPIRVTypeID(Call->ReturnType));
1530
unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode();
1531
bool IsVec = Opcode == SPIRV::OpTypeVector;
1532
// Use OpDot only in case of vector args and OpFMul in case of scalar args.
1533
MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS)
1534
.addDef(Call->ReturnRegister)
1535
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1536
.addUse(Call->Arguments[0])
1537
.addUse(Call->Arguments[1]);
1538
return true;
1539
}
1540
1541
static bool generateWaveInst(const SPIRV::IncomingCall *Call,
1542
MachineIRBuilder &MIRBuilder,
1543
SPIRVGlobalRegistry *GR) {
1544
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1545
SPIRV::BuiltIn::BuiltIn Value =
1546
SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1547
1548
// For now, we only support a single Wave intrinsic with a single return type.
1549
assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt);
1550
LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType));
1551
1552
return buildBuiltinVariableLoad(
1553
MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister,
1554
/* isConst= */ false, /* hasLinkageTy= */ false);
1555
}
1556
1557
static bool generateGetQueryInst(const SPIRV::IncomingCall *Call,
1558
MachineIRBuilder &MIRBuilder,
1559
SPIRVGlobalRegistry *GR) {
1560
// Lookup the builtin record.
1561
SPIRV::BuiltIn::BuiltIn Value =
1562
SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value;
1563
uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize ||
1564
Value == SPIRV::BuiltIn::WorkgroupSize ||
1565
Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);
1566
return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0);
1567
}
1568
1569
static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call,
1570
MachineIRBuilder &MIRBuilder,
1571
SPIRVGlobalRegistry *GR) {
1572
// Lookup the image size query component number in the TableGen records.
1573
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1574
uint32_t Component =
1575
SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component;
1576
// Query result may either be a vector or a scalar. If return type is not a
1577
// vector, expect only a single size component. Otherwise get the number of
1578
// expected components.
1579
SPIRVType *RetTy = Call->ReturnType;
1580
unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector
1581
? RetTy->getOperand(2).getImm()
1582
: 1;
1583
// Get the actual number of query result/size components.
1584
SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1585
unsigned NumActualRetComponents = getNumSizeComponents(ImgType);
1586
Register QueryResult = Call->ReturnRegister;
1587
SPIRVType *QueryResultType = Call->ReturnType;
1588
if (NumExpectedRetComponents != NumActualRetComponents) {
1589
QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(
1590
LLT::fixed_vector(NumActualRetComponents, 32));
1591
MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::IDRegClass);
1592
SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
1593
QueryResultType = GR->getOrCreateSPIRVVectorType(
1594
IntTy, NumActualRetComponents, MIRBuilder);
1595
GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());
1596
}
1597
bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;
1598
unsigned Opcode =
1599
IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;
1600
MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
1601
auto MIB = MIRBuilder.buildInstr(Opcode)
1602
.addDef(QueryResult)
1603
.addUse(GR->getSPIRVTypeID(QueryResultType))
1604
.addUse(Call->Arguments[0]);
1605
if (!IsDimBuf)
1606
MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Lod id.
1607
if (NumExpectedRetComponents == NumActualRetComponents)
1608
return true;
1609
if (NumExpectedRetComponents == 1) {
1610
// Only 1 component is expected, build OpCompositeExtract instruction.
1611
unsigned ExtractedComposite =
1612
Component == 3 ? NumActualRetComponents - 1 : Component;
1613
assert(ExtractedComposite < NumActualRetComponents &&
1614
"Invalid composite index!");
1615
Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
1616
SPIRVType *NewType = nullptr;
1617
if (QueryResultType->getOpcode() == SPIRV::OpTypeVector) {
1618
Register NewTypeReg = QueryResultType->getOperand(1).getReg();
1619
if (TypeReg != NewTypeReg &&
1620
(NewType = GR->getSPIRVTypeForVReg(NewTypeReg)) != nullptr)
1621
TypeReg = NewTypeReg;
1622
}
1623
MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1624
.addDef(Call->ReturnRegister)
1625
.addUse(TypeReg)
1626
.addUse(QueryResult)
1627
.addImm(ExtractedComposite);
1628
if (NewType != nullptr)
1629
insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
1630
MIRBuilder.getMF().getRegInfo());
1631
} else {
1632
// More than 1 component is expected, fill a new vector.
1633
auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)
1634
.addDef(Call->ReturnRegister)
1635
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1636
.addUse(QueryResult)
1637
.addUse(QueryResult);
1638
for (unsigned i = 0; i < NumExpectedRetComponents; ++i)
1639
MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);
1640
}
1641
return true;
1642
}
1643
1644
static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call,
1645
MachineIRBuilder &MIRBuilder,
1646
SPIRVGlobalRegistry *GR) {
1647
assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&
1648
"Image samples query result must be of int type!");
1649
1650
// Lookup the instruction opcode in the TableGen records.
1651
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1652
unsigned Opcode =
1653
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1654
1655
Register Image = Call->Arguments[0];
1656
MIRBuilder.getMRI()->setRegClass(Image, &SPIRV::IDRegClass);
1657
SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(
1658
GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());
1659
(void)ImageDimensionality;
1660
1661
switch (Opcode) {
1662
case SPIRV::OpImageQuerySamples:
1663
assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&
1664
"Image must be of 2D dimensionality");
1665
break;
1666
case SPIRV::OpImageQueryLevels:
1667
assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||
1668
ImageDimensionality == SPIRV::Dim::DIM_2D ||
1669
ImageDimensionality == SPIRV::Dim::DIM_3D ||
1670
ImageDimensionality == SPIRV::Dim::DIM_Cube) &&
1671
"Image must be of 1D/2D/3D/Cube dimensionality");
1672
break;
1673
}
1674
1675
MIRBuilder.buildInstr(Opcode)
1676
.addDef(Call->ReturnRegister)
1677
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1678
.addUse(Image);
1679
return true;
1680
}
1681
1682
// TODO: Move to TableGen.
1683
static SPIRV::SamplerAddressingMode::SamplerAddressingMode
1684
getSamplerAddressingModeFromBitmask(unsigned Bitmask) {
1685
switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {
1686
case SPIRV::CLK_ADDRESS_CLAMP:
1687
return SPIRV::SamplerAddressingMode::Clamp;
1688
case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:
1689
return SPIRV::SamplerAddressingMode::ClampToEdge;
1690
case SPIRV::CLK_ADDRESS_REPEAT:
1691
return SPIRV::SamplerAddressingMode::Repeat;
1692
case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:
1693
return SPIRV::SamplerAddressingMode::RepeatMirrored;
1694
case SPIRV::CLK_ADDRESS_NONE:
1695
return SPIRV::SamplerAddressingMode::None;
1696
default:
1697
report_fatal_error("Unknown CL address mode");
1698
}
1699
}
1700
1701
static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {
1702
return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;
1703
}
1704
1705
static SPIRV::SamplerFilterMode::SamplerFilterMode
1706
getSamplerFilterModeFromBitmask(unsigned Bitmask) {
1707
if (Bitmask & SPIRV::CLK_FILTER_LINEAR)
1708
return SPIRV::SamplerFilterMode::Linear;
1709
if (Bitmask & SPIRV::CLK_FILTER_NEAREST)
1710
return SPIRV::SamplerFilterMode::Nearest;
1711
return SPIRV::SamplerFilterMode::Nearest;
1712
}
1713
1714
static bool generateReadImageInst(const StringRef DemangledCall,
1715
const SPIRV::IncomingCall *Call,
1716
MachineIRBuilder &MIRBuilder,
1717
SPIRVGlobalRegistry *GR) {
1718
Register Image = Call->Arguments[0];
1719
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1720
MRI->setRegClass(Image, &SPIRV::IDRegClass);
1721
MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
1722
bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler");
1723
bool HasMsaa = DemangledCall.contains_insensitive("msaa");
1724
if (HasOclSampler || HasMsaa)
1725
MRI->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);
1726
if (HasOclSampler) {
1727
Register Sampler = Call->Arguments[1];
1728
1729
if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&
1730
getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {
1731
uint64_t SamplerMask = getIConstVal(Sampler, MRI);
1732
Sampler = GR->buildConstantSampler(
1733
Register(), getSamplerAddressingModeFromBitmask(SamplerMask),
1734
getSamplerParamFromBitmask(SamplerMask),
1735
getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder,
1736
GR->getSPIRVTypeForVReg(Sampler));
1737
}
1738
SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1739
SPIRVType *SampledImageType =
1740
GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1741
Register SampledImage = MRI->createVirtualRegister(&SPIRV::IDRegClass);
1742
1743
MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1744
.addDef(SampledImage)
1745
.addUse(GR->getSPIRVTypeID(SampledImageType))
1746
.addUse(Image)
1747
.addUse(Sampler);
1748
1749
Register Lod = GR->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()),
1750
MIRBuilder);
1751
SPIRVType *TempType = Call->ReturnType;
1752
bool NeedsExtraction = false;
1753
if (TempType->getOpcode() != SPIRV::OpTypeVector) {
1754
TempType =
1755
GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder);
1756
NeedsExtraction = true;
1757
}
1758
LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(TempType));
1759
Register TempRegister = MRI->createGenericVirtualRegister(LLType);
1760
MRI->setRegClass(TempRegister, &SPIRV::IDRegClass);
1761
GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());
1762
1763
MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1764
.addDef(NeedsExtraction ? TempRegister : Call->ReturnRegister)
1765
.addUse(GR->getSPIRVTypeID(TempType))
1766
.addUse(SampledImage)
1767
.addUse(Call->Arguments[2]) // Coordinate.
1768
.addImm(SPIRV::ImageOperand::Lod)
1769
.addUse(Lod);
1770
1771
if (NeedsExtraction)
1772
MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1773
.addDef(Call->ReturnRegister)
1774
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1775
.addUse(TempRegister)
1776
.addImm(0);
1777
} else if (HasMsaa) {
1778
MIRBuilder.buildInstr(SPIRV::OpImageRead)
1779
.addDef(Call->ReturnRegister)
1780
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1781
.addUse(Image)
1782
.addUse(Call->Arguments[1]) // Coordinate.
1783
.addImm(SPIRV::ImageOperand::Sample)
1784
.addUse(Call->Arguments[2]);
1785
} else {
1786
MIRBuilder.buildInstr(SPIRV::OpImageRead)
1787
.addDef(Call->ReturnRegister)
1788
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
1789
.addUse(Image)
1790
.addUse(Call->Arguments[1]); // Coordinate.
1791
}
1792
return true;
1793
}
1794
1795
static bool generateWriteImageInst(const SPIRV::IncomingCall *Call,
1796
MachineIRBuilder &MIRBuilder,
1797
SPIRVGlobalRegistry *GR) {
1798
MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
1799
MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
1800
MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);
1801
MIRBuilder.buildInstr(SPIRV::OpImageWrite)
1802
.addUse(Call->Arguments[0]) // Image.
1803
.addUse(Call->Arguments[1]) // Coordinate.
1804
.addUse(Call->Arguments[2]); // Texel.
1805
return true;
1806
}
1807
1808
static bool generateSampleImageInst(const StringRef DemangledCall,
1809
const SPIRV::IncomingCall *Call,
1810
MachineIRBuilder &MIRBuilder,
1811
SPIRVGlobalRegistry *GR) {
1812
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1813
if (Call->Builtin->Name.contains_insensitive(
1814
"__translate_sampler_initializer")) {
1815
// Build sampler literal.
1816
uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI);
1817
Register Sampler = GR->buildConstantSampler(
1818
Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),
1819
getSamplerParamFromBitmask(Bitmask),
1820
getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType);
1821
return Sampler.isValid();
1822
} else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) {
1823
// Create OpSampledImage.
1824
Register Image = Call->Arguments[0];
1825
SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1826
SPIRVType *SampledImageType =
1827
GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1828
Register SampledImage =
1829
Call->ReturnRegister.isValid()
1830
? Call->ReturnRegister
1831
: MRI->createVirtualRegister(&SPIRV::IDRegClass);
1832
MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1833
.addDef(SampledImage)
1834
.addUse(GR->getSPIRVTypeID(SampledImageType))
1835
.addUse(Image)
1836
.addUse(Call->Arguments[1]); // Sampler.
1837
return true;
1838
} else if (Call->Builtin->Name.contains_insensitive(
1839
"__spirv_ImageSampleExplicitLod")) {
1840
// Sample an image using an explicit level of detail.
1841
std::string ReturnType = DemangledCall.str();
1842
if (DemangledCall.contains("_R")) {
1843
ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);
1844
ReturnType = ReturnType.substr(0, ReturnType.find('('));
1845
}
1846
SPIRVType *Type =
1847
Call->ReturnType
1848
? Call->ReturnType
1849
: GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder);
1850
if (!Type) {
1851
std::string DiagMsg =
1852
"Unable to recognize SPIRV type name: " + ReturnType;
1853
report_fatal_error(DiagMsg.c_str());
1854
}
1855
MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
1856
MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
1857
MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass);
1858
1859
MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1860
.addDef(Call->ReturnRegister)
1861
.addUse(GR->getSPIRVTypeID(Type))
1862
.addUse(Call->Arguments[0]) // Image.
1863
.addUse(Call->Arguments[1]) // Coordinate.
1864
.addImm(SPIRV::ImageOperand::Lod)
1865
.addUse(Call->Arguments[3]);
1866
return true;
1867
}
1868
return false;
1869
}
1870
1871
static bool generateSelectInst(const SPIRV::IncomingCall *Call,
1872
MachineIRBuilder &MIRBuilder) {
1873
MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],
1874
Call->Arguments[1], Call->Arguments[2]);
1875
return true;
1876
}
1877
1878
static bool generateConstructInst(const SPIRV::IncomingCall *Call,
1879
MachineIRBuilder &MIRBuilder,
1880
SPIRVGlobalRegistry *GR) {
1881
return buildOpFromWrapper(MIRBuilder, SPIRV::OpCompositeConstruct, Call,
1882
GR->getSPIRVTypeID(Call->ReturnType));
1883
}
1884
1885
static bool generateCoopMatrInst(const SPIRV::IncomingCall *Call,
1886
MachineIRBuilder &MIRBuilder,
1887
SPIRVGlobalRegistry *GR) {
1888
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1889
unsigned Opcode =
1890
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1891
bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR;
1892
unsigned ArgSz = Call->Arguments.size();
1893
unsigned LiteralIdx = 0;
1894
if (Opcode == SPIRV::OpCooperativeMatrixLoadKHR && ArgSz > 3)
1895
LiteralIdx = 3;
1896
else if (Opcode == SPIRV::OpCooperativeMatrixStoreKHR && ArgSz > 4)
1897
LiteralIdx = 4;
1898
SmallVector<uint32_t, 1> ImmArgs;
1899
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1900
if (LiteralIdx > 0)
1901
ImmArgs.push_back(getConstFromIntrinsic(Call->Arguments[LiteralIdx], MRI));
1902
Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
1903
if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) {
1904
SPIRVType *CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1905
if (!CoopMatrType)
1906
report_fatal_error("Can't find a register's type definition");
1907
MIRBuilder.buildInstr(Opcode)
1908
.addDef(Call->ReturnRegister)
1909
.addUse(TypeReg)
1910
.addUse(CoopMatrType->getOperand(0).getReg());
1911
return true;
1912
}
1913
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1914
IsSet ? TypeReg : Register(0), ImmArgs);
1915
}
1916
1917
static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call,
1918
MachineIRBuilder &MIRBuilder,
1919
SPIRVGlobalRegistry *GR) {
1920
// Lookup the instruction opcode in the TableGen records.
1921
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1922
unsigned Opcode =
1923
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1924
const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1925
1926
switch (Opcode) {
1927
case SPIRV::OpSpecConstant: {
1928
// Build the SpecID decoration.
1929
unsigned SpecId =
1930
static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));
1931
buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,
1932
{SpecId});
1933
// Determine the constant MI.
1934
Register ConstRegister = Call->Arguments[1];
1935
const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);
1936
assert(Const &&
1937
(Const->getOpcode() == TargetOpcode::G_CONSTANT ||
1938
Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&
1939
"Argument should be either an int or floating-point constant");
1940
// Determine the opcode and built the OpSpec MI.
1941
const MachineOperand &ConstOperand = Const->getOperand(1);
1942
if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {
1943
assert(ConstOperand.isCImm() && "Int constant operand is expected");
1944
Opcode = ConstOperand.getCImm()->getValue().getZExtValue()
1945
? SPIRV::OpSpecConstantTrue
1946
: SPIRV::OpSpecConstantFalse;
1947
}
1948
auto MIB = MIRBuilder.buildInstr(Opcode)
1949
.addDef(Call->ReturnRegister)
1950
.addUse(GR->getSPIRVTypeID(Call->ReturnType));
1951
1952
if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {
1953
if (Const->getOpcode() == TargetOpcode::G_CONSTANT)
1954
addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1955
else
1956
addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);
1957
}
1958
return true;
1959
}
1960
case SPIRV::OpSpecConstantComposite: {
1961
auto MIB = MIRBuilder.buildInstr(Opcode)
1962
.addDef(Call->ReturnRegister)
1963
.addUse(GR->getSPIRVTypeID(Call->ReturnType));
1964
for (unsigned i = 0; i < Call->Arguments.size(); i++)
1965
MIB.addUse(Call->Arguments[i]);
1966
return true;
1967
}
1968
default:
1969
return false;
1970
}
1971
}
1972
1973
static bool buildNDRange(const SPIRV::IncomingCall *Call,
1974
MachineIRBuilder &MIRBuilder,
1975
SPIRVGlobalRegistry *GR) {
1976
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1977
MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
1978
SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1979
assert(PtrType->getOpcode() == SPIRV::OpTypePointer &&
1980
PtrType->getOperand(2).isReg());
1981
Register TypeReg = PtrType->getOperand(2).getReg();
1982
SPIRVType *StructType = GR->getSPIRVTypeForVReg(TypeReg);
1983
MachineFunction &MF = MIRBuilder.getMF();
1984
Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
1985
GR->assignSPIRVTypeToVReg(StructType, TmpReg, MF);
1986
// Skip the first arg, it's the destination pointer. OpBuildNDRange takes
1987
// three other arguments, so pass zero constant on absence.
1988
unsigned NumArgs = Call->Arguments.size();
1989
assert(NumArgs >= 2);
1990
Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2];
1991
MRI->setRegClass(GlobalWorkSize, &SPIRV::IDRegClass);
1992
Register LocalWorkSize =
1993
NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3];
1994
if (LocalWorkSize.isValid())
1995
MRI->setRegClass(LocalWorkSize, &SPIRV::IDRegClass);
1996
Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1];
1997
if (GlobalWorkOffset.isValid())
1998
MRI->setRegClass(GlobalWorkOffset, &SPIRV::IDRegClass);
1999
if (NumArgs < 4) {
2000
Register Const;
2001
SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize);
2002
if (SpvTy->getOpcode() == SPIRV::OpTypePointer) {
2003
MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize);
2004
assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) &&
2005
DefInstr->getOperand(3).isReg());
2006
Register GWSPtr = DefInstr->getOperand(3).getReg();
2007
if (!MRI->getRegClassOrNull(GWSPtr))
2008
MRI->setRegClass(GWSPtr, &SPIRV::IDRegClass);
2009
// TODO: Maybe simplify generation of the type of the fields.
2010
unsigned Size = Call->Builtin->Name == "ndrange_3D" ? 3 : 2;
2011
unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32;
2012
Type *BaseTy = IntegerType::get(MF.getFunction().getContext(), BitWidth);
2013
Type *FieldTy = ArrayType::get(BaseTy, Size);
2014
SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder);
2015
GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::IDRegClass);
2016
GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize, MF);
2017
MIRBuilder.buildInstr(SPIRV::OpLoad)
2018
.addDef(GlobalWorkSize)
2019
.addUse(GR->getSPIRVTypeID(SpvFieldTy))
2020
.addUse(GWSPtr);
2021
const SPIRVSubtarget &ST =
2022
cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
2023
Const = GR->getOrCreateConstIntArray(0, Size, *MIRBuilder.getInsertPt(),
2024
SpvFieldTy, *ST.getInstrInfo());
2025
} else {
2026
Const = GR->buildConstantInt(0, MIRBuilder, SpvTy);
2027
}
2028
if (!LocalWorkSize.isValid())
2029
LocalWorkSize = Const;
2030
if (!GlobalWorkOffset.isValid())
2031
GlobalWorkOffset = Const;
2032
}
2033
assert(LocalWorkSize.isValid() && GlobalWorkOffset.isValid());
2034
MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
2035
.addDef(TmpReg)
2036
.addUse(TypeReg)
2037
.addUse(GlobalWorkSize)
2038
.addUse(LocalWorkSize)
2039
.addUse(GlobalWorkOffset);
2040
return MIRBuilder.buildInstr(SPIRV::OpStore)
2041
.addUse(Call->Arguments[0])
2042
.addUse(TmpReg);
2043
}
2044
2045
// TODO: maybe move to the global register.
2046
static SPIRVType *
2047
getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder,
2048
SPIRVGlobalRegistry *GR) {
2049
LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
2050
Type *OpaqueType = StructType::getTypeByName(Context, "spirv.DeviceEvent");
2051
if (!OpaqueType)
2052
OpaqueType = StructType::getTypeByName(Context, "opencl.clk_event_t");
2053
if (!OpaqueType)
2054
OpaqueType = StructType::create(Context, "spirv.DeviceEvent");
2055
unsigned SC0 = storageClassToAddressSpace(SPIRV::StorageClass::Function);
2056
unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2057
Type *PtrType = PointerType::get(PointerType::get(OpaqueType, SC0), SC1);
2058
return GR->getOrCreateSPIRVType(PtrType, MIRBuilder);
2059
}
2060
2061
static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call,
2062
MachineIRBuilder &MIRBuilder,
2063
SPIRVGlobalRegistry *GR) {
2064
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2065
const DataLayout &DL = MIRBuilder.getDataLayout();
2066
bool IsSpirvOp = Call->isSpirvOp();
2067
bool HasEvents = Call->Builtin->Name.contains("events") || IsSpirvOp;
2068
const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
2069
2070
// Make vararg instructions before OpEnqueueKernel.
2071
// Local sizes arguments: Sizes of block invoke arguments. Clang generates
2072
// local size operands as an array, so we need to unpack them.
2073
SmallVector<Register, 16> LocalSizes;
2074
if (Call->Builtin->Name.contains("_varargs") || IsSpirvOp) {
2075
const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6;
2076
Register GepReg = Call->Arguments[LocalSizeArrayIdx];
2077
MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg);
2078
assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) &&
2079
GepMI->getOperand(3).isReg());
2080
Register ArrayReg = GepMI->getOperand(3).getReg();
2081
MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg);
2082
const Type *LocalSizeTy = getMachineInstrType(ArrayMI);
2083
assert(LocalSizeTy && "Local size type is expected");
2084
const uint64_t LocalSizeNum =
2085
cast<ArrayType>(LocalSizeTy)->getNumElements();
2086
unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2087
const LLT LLType = LLT::pointer(SC, GR->getPointerSize());
2088
const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType(
2089
Int32Ty, MIRBuilder, SPIRV::StorageClass::Function);
2090
for (unsigned I = 0; I < LocalSizeNum; ++I) {
2091
Register Reg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
2092
MRI->setType(Reg, LLType);
2093
GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF());
2094
auto GEPInst = MIRBuilder.buildIntrinsic(
2095
Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false);
2096
GEPInst
2097
.addImm(GepMI->getOperand(2).getImm()) // In bound.
2098
.addUse(ArrayMI->getOperand(0).getReg()) // Alloca.
2099
.addUse(buildConstantIntReg(0, MIRBuilder, GR)) // Indices.
2100
.addUse(buildConstantIntReg(I, MIRBuilder, GR));
2101
LocalSizes.push_back(Reg);
2102
}
2103
}
2104
2105
// SPIRV OpEnqueueKernel instruction has 10+ arguments.
2106
auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)
2107
.addDef(Call->ReturnRegister)
2108
.addUse(GR->getSPIRVTypeID(Int32Ty));
2109
2110
// Copy all arguments before block invoke function pointer.
2111
const unsigned BlockFIdx = HasEvents ? 6 : 3;
2112
for (unsigned i = 0; i < BlockFIdx; i++)
2113
MIB.addUse(Call->Arguments[i]);
2114
2115
// If there are no event arguments in the original call, add dummy ones.
2116
if (!HasEvents) {
2117
MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Dummy num events.
2118
Register NullPtr = GR->getOrCreateConstNullPtr(
2119
MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR));
2120
MIB.addUse(NullPtr); // Dummy wait events.
2121
MIB.addUse(NullPtr); // Dummy ret event.
2122
}
2123
2124
MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI);
2125
assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
2126
// Invoke: Pointer to invoke function.
2127
MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal());
2128
2129
Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1];
2130
// Param: Pointer to block literal.
2131
MIB.addUse(BlockLiteralReg);
2132
2133
Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
2134
// TODO: these numbers should be obtained from block literal structure.
2135
// Param Size: Size of block literal structure.
2136
MIB.addUse(buildConstantIntReg(DL.getTypeStoreSize(PType), MIRBuilder, GR));
2137
// Param Aligment: Aligment of block literal structure.
2138
MIB.addUse(
2139
buildConstantIntReg(DL.getPrefTypeAlign(PType).value(), MIRBuilder, GR));
2140
2141
for (unsigned i = 0; i < LocalSizes.size(); i++)
2142
MIB.addUse(LocalSizes[i]);
2143
return true;
2144
}
2145
2146
static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,
2147
MachineIRBuilder &MIRBuilder,
2148
SPIRVGlobalRegistry *GR) {
2149
// Lookup the instruction opcode in the TableGen records.
2150
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2151
unsigned Opcode =
2152
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2153
2154
switch (Opcode) {
2155
case SPIRV::OpRetainEvent:
2156
case SPIRV::OpReleaseEvent:
2157
MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
2158
return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);
2159
case SPIRV::OpCreateUserEvent:
2160
case SPIRV::OpGetDefaultQueue:
2161
return MIRBuilder.buildInstr(Opcode)
2162
.addDef(Call->ReturnRegister)
2163
.addUse(GR->getSPIRVTypeID(Call->ReturnType));
2164
case SPIRV::OpIsValidEvent:
2165
MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
2166
return MIRBuilder.buildInstr(Opcode)
2167
.addDef(Call->ReturnRegister)
2168
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
2169
.addUse(Call->Arguments[0]);
2170
case SPIRV::OpSetUserEventStatus:
2171
MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
2172
MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
2173
return MIRBuilder.buildInstr(Opcode)
2174
.addUse(Call->Arguments[0])
2175
.addUse(Call->Arguments[1]);
2176
case SPIRV::OpCaptureEventProfilingInfo:
2177
MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
2178
MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
2179
MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);
2180
return MIRBuilder.buildInstr(Opcode)
2181
.addUse(Call->Arguments[0])
2182
.addUse(Call->Arguments[1])
2183
.addUse(Call->Arguments[2]);
2184
case SPIRV::OpBuildNDRange:
2185
return buildNDRange(Call, MIRBuilder, GR);
2186
case SPIRV::OpEnqueueKernel:
2187
return buildEnqueueKernel(Call, MIRBuilder, GR);
2188
default:
2189
return false;
2190
}
2191
}
2192
2193
static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,
2194
MachineIRBuilder &MIRBuilder,
2195
SPIRVGlobalRegistry *GR) {
2196
// Lookup the instruction opcode in the TableGen records.
2197
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2198
unsigned Opcode =
2199
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2200
2201
bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;
2202
Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2203
if (Call->isSpirvOp())
2204
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2205
IsSet ? TypeReg : Register(0));
2206
2207
auto Scope = buildConstantIntReg(SPIRV::Scope::Workgroup, MIRBuilder, GR);
2208
2209
switch (Opcode) {
2210
case SPIRV::OpGroupAsyncCopy: {
2211
SPIRVType *NewType =
2212
Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent
2213
? nullptr
2214
: GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder);
2215
Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);
2216
unsigned NumArgs = Call->Arguments.size();
2217
Register EventReg = Call->Arguments[NumArgs - 1];
2218
bool Res = MIRBuilder.buildInstr(Opcode)
2219
.addDef(Call->ReturnRegister)
2220
.addUse(TypeReg)
2221
.addUse(Scope)
2222
.addUse(Call->Arguments[0])
2223
.addUse(Call->Arguments[1])
2224
.addUse(Call->Arguments[2])
2225
.addUse(Call->Arguments.size() > 4
2226
? Call->Arguments[3]
2227
: buildConstantIntReg(1, MIRBuilder, GR))
2228
.addUse(EventReg);
2229
if (NewType != nullptr)
2230
insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
2231
MIRBuilder.getMF().getRegInfo());
2232
return Res;
2233
}
2234
case SPIRV::OpGroupWaitEvents:
2235
return MIRBuilder.buildInstr(Opcode)
2236
.addUse(Scope)
2237
.addUse(Call->Arguments[0])
2238
.addUse(Call->Arguments[1]);
2239
default:
2240
return false;
2241
}
2242
}
2243
2244
static bool generateConvertInst(const StringRef DemangledCall,
2245
const SPIRV::IncomingCall *Call,
2246
MachineIRBuilder &MIRBuilder,
2247
SPIRVGlobalRegistry *GR) {
2248
// Lookup the conversion builtin in the TableGen records.
2249
const SPIRV::ConvertBuiltin *Builtin =
2250
SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set);
2251
2252
if (!Builtin && Call->isSpirvOp()) {
2253
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2254
unsigned Opcode =
2255
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2256
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2257
GR->getSPIRVTypeID(Call->ReturnType));
2258
}
2259
2260
if (Builtin->IsSaturated)
2261
buildOpDecorate(Call->ReturnRegister, MIRBuilder,
2262
SPIRV::Decoration::SaturatedConversion, {});
2263
if (Builtin->IsRounded)
2264
buildOpDecorate(Call->ReturnRegister, MIRBuilder,
2265
SPIRV::Decoration::FPRoundingMode,
2266
{(unsigned)Builtin->RoundingMode});
2267
2268
std::string NeedExtMsg; // no errors if empty
2269
bool IsRightComponentsNumber = true; // check if input/output accepts vectors
2270
unsigned Opcode = SPIRV::OpNop;
2271
if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {
2272
// Int -> ...
2273
if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2274
// Int -> Int
2275
if (Builtin->IsSaturated)
2276
Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS
2277
: SPIRV::OpSatConvertSToU;
2278
else
2279
Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert
2280
: SPIRV::OpSConvert;
2281
} else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
2282
SPIRV::OpTypeFloat)) {
2283
// Int -> Float
2284
if (Builtin->IsBfloat16) {
2285
const auto *ST = static_cast<const SPIRVSubtarget *>(
2286
&MIRBuilder.getMF().getSubtarget());
2287
if (!ST->canUseExtension(
2288
SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
2289
NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
2290
IsRightComponentsNumber =
2291
GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
2292
GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
2293
Opcode = SPIRV::OpConvertBF16ToFINTEL;
2294
} else {
2295
bool IsSourceSigned =
2296
DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';
2297
Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;
2298
}
2299
}
2300
} else if (GR->isScalarOrVectorOfType(Call->Arguments[0],
2301
SPIRV::OpTypeFloat)) {
2302
// Float -> ...
2303
if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2304
// Float -> Int
2305
if (Builtin->IsBfloat16) {
2306
const auto *ST = static_cast<const SPIRVSubtarget *>(
2307
&MIRBuilder.getMF().getSubtarget());
2308
if (!ST->canUseExtension(
2309
SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
2310
NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
2311
IsRightComponentsNumber =
2312
GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
2313
GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
2314
Opcode = SPIRV::OpConvertFToBF16INTEL;
2315
} else {
2316
Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS
2317
: SPIRV::OpConvertFToU;
2318
}
2319
} else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
2320
SPIRV::OpTypeFloat)) {
2321
// Float -> Float
2322
Opcode = SPIRV::OpFConvert;
2323
}
2324
}
2325
2326
if (!NeedExtMsg.empty()) {
2327
std::string DiagMsg = std::string(Builtin->Name) +
2328
": the builtin requires the following SPIR-V "
2329
"extension: " +
2330
NeedExtMsg;
2331
report_fatal_error(DiagMsg.c_str(), false);
2332
}
2333
if (!IsRightComponentsNumber) {
2334
std::string DiagMsg =
2335
std::string(Builtin->Name) +
2336
": result and argument must have the same number of components";
2337
report_fatal_error(DiagMsg.c_str(), false);
2338
}
2339
assert(Opcode != SPIRV::OpNop &&
2340
"Conversion between the types not implemented!");
2341
2342
MIRBuilder.buildInstr(Opcode)
2343
.addDef(Call->ReturnRegister)
2344
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
2345
.addUse(Call->Arguments[0]);
2346
return true;
2347
}
2348
2349
static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call,
2350
MachineIRBuilder &MIRBuilder,
2351
SPIRVGlobalRegistry *GR) {
2352
// Lookup the vector load/store builtin in the TableGen records.
2353
const SPIRV::VectorLoadStoreBuiltin *Builtin =
2354
SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2355
Call->Builtin->Set);
2356
// Build extended instruction.
2357
auto MIB =
2358
MIRBuilder.buildInstr(SPIRV::OpExtInst)
2359
.addDef(Call->ReturnRegister)
2360
.addUse(GR->getSPIRVTypeID(Call->ReturnType))
2361
.addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
2362
.addImm(Builtin->Number);
2363
for (auto Argument : Call->Arguments)
2364
MIB.addUse(Argument);
2365
if (Builtin->Name.contains("load") && Builtin->ElementCount > 1)
2366
MIB.addImm(Builtin->ElementCount);
2367
2368
// Rounding mode should be passed as a last argument in the MI for builtins
2369
// like "vstorea_halfn_r".
2370
if (Builtin->IsRounded)
2371
MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));
2372
return true;
2373
}
2374
2375
static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call,
2376
MachineIRBuilder &MIRBuilder,
2377
SPIRVGlobalRegistry *GR) {
2378
// Lookup the instruction opcode in the TableGen records.
2379
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2380
unsigned Opcode =
2381
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2382
bool IsLoad = Opcode == SPIRV::OpLoad;
2383
// Build the instruction.
2384
auto MIB = MIRBuilder.buildInstr(Opcode);
2385
if (IsLoad) {
2386
MIB.addDef(Call->ReturnRegister);
2387
MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
2388
}
2389
// Add a pointer to the value to load/store.
2390
MIB.addUse(Call->Arguments[0]);
2391
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2392
MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);
2393
// Add a value to store.
2394
if (!IsLoad) {
2395
MIB.addUse(Call->Arguments[1]);
2396
MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);
2397
}
2398
// Add optional memory attributes and an alignment.
2399
unsigned NumArgs = Call->Arguments.size();
2400
if ((IsLoad && NumArgs >= 2) || NumArgs >= 3) {
2401
MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI));
2402
MRI->setRegClass(Call->Arguments[IsLoad ? 1 : 2], &SPIRV::IDRegClass);
2403
}
2404
if ((IsLoad && NumArgs >= 3) || NumArgs >= 4) {
2405
MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI));
2406
MRI->setRegClass(Call->Arguments[IsLoad ? 2 : 3], &SPIRV::IDRegClass);
2407
}
2408
return true;
2409
}
2410
2411
namespace SPIRV {
2412
// Try to find a builtin function attributes by a demangled function name and
2413
// return a tuple <builtin group, op code, ext instruction number>, or a special
2414
// tuple value <-1, 0, 0> if the builtin function is not found.
2415
// Not all builtin functions are supported, only those with a ready-to-use op
2416
// code or instruction number defined in TableGen.
2417
// TODO: consider a major rework of mapping demangled calls into a builtin
2418
// functions to unify search and decrease number of individual cases.
2419
std::tuple<int, unsigned, unsigned>
2420
mapBuiltinToOpcode(const StringRef DemangledCall,
2421
SPIRV::InstructionSet::InstructionSet Set) {
2422
Register Reg;
2423
SmallVector<Register> Args;
2424
std::unique_ptr<const IncomingCall> Call =
2425
lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args);
2426
if (!Call)
2427
return std::make_tuple(-1, 0, 0);
2428
2429
switch (Call->Builtin->Group) {
2430
case SPIRV::Relational:
2431
case SPIRV::Atomic:
2432
case SPIRV::Barrier:
2433
case SPIRV::CastToPtr:
2434
case SPIRV::ImageMiscQuery:
2435
case SPIRV::SpecConstant:
2436
case SPIRV::Enqueue:
2437
case SPIRV::AsyncCopy:
2438
case SPIRV::LoadStore:
2439
case SPIRV::CoopMatr:
2440
if (const auto *R =
2441
SPIRV::lookupNativeBuiltin(Call->Builtin->Name, Call->Builtin->Set))
2442
return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2443
break;
2444
case SPIRV::Extended:
2445
if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->Name,
2446
Call->Builtin->Set))
2447
return std::make_tuple(Call->Builtin->Group, 0, R->Number);
2448
break;
2449
case SPIRV::VectorLoadStore:
2450
if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2451
Call->Builtin->Set))
2452
return std::make_tuple(SPIRV::Extended, 0, R->Number);
2453
break;
2454
case SPIRV::Group:
2455
if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->Name))
2456
return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2457
break;
2458
case SPIRV::AtomicFloating:
2459
if (const auto *R = SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->Name))
2460
return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2461
break;
2462
case SPIRV::IntelSubgroups:
2463
if (const auto *R = SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->Name))
2464
return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2465
break;
2466
case SPIRV::GroupUniform:
2467
if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->Name))
2468
return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2469
break;
2470
case SPIRV::WriteImage:
2471
return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0);
2472
case SPIRV::Select:
2473
return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0);
2474
case SPIRV::Construct:
2475
return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct,
2476
0);
2477
case SPIRV::KernelClock:
2478
return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0);
2479
default:
2480
return std::make_tuple(-1, 0, 0);
2481
}
2482
return std::make_tuple(-1, 0, 0);
2483
}
2484
2485
std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
2486
SPIRV::InstructionSet::InstructionSet Set,
2487
MachineIRBuilder &MIRBuilder,
2488
const Register OrigRet, const Type *OrigRetTy,
2489
const SmallVectorImpl<Register> &Args,
2490
SPIRVGlobalRegistry *GR) {
2491
LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");
2492
2493
// SPIR-V type and return register.
2494
Register ReturnRegister = OrigRet;
2495
SPIRVType *ReturnType = nullptr;
2496
if (OrigRetTy && !OrigRetTy->isVoidTy()) {
2497
ReturnType = GR->assignTypeToVReg(OrigRetTy, OrigRet, MIRBuilder);
2498
if (!MIRBuilder.getMRI()->getRegClassOrNull(ReturnRegister))
2499
MIRBuilder.getMRI()->setRegClass(ReturnRegister, &SPIRV::IDRegClass);
2500
} else if (OrigRetTy && OrigRetTy->isVoidTy()) {
2501
ReturnRegister = MIRBuilder.getMRI()->createVirtualRegister(&IDRegClass);
2502
MIRBuilder.getMRI()->setType(ReturnRegister, LLT::scalar(32));
2503
ReturnType = GR->assignTypeToVReg(OrigRetTy, ReturnRegister, MIRBuilder);
2504
}
2505
2506
// Lookup the builtin in the TableGen records.
2507
std::unique_ptr<const IncomingCall> Call =
2508
lookupBuiltin(DemangledCall, Set, ReturnRegister, ReturnType, Args);
2509
2510
if (!Call) {
2511
LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
2512
return std::nullopt;
2513
}
2514
2515
// TODO: check if the provided args meet the builtin requirments.
2516
assert(Args.size() >= Call->Builtin->MinNumArgs &&
2517
"Too few arguments to generate the builtin");
2518
if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs)
2519
LLVM_DEBUG(dbgs() << "More arguments provided than required!\n");
2520
2521
// Match the builtin with implementation based on the grouping.
2522
switch (Call->Builtin->Group) {
2523
case SPIRV::Extended:
2524
return generateExtInst(Call.get(), MIRBuilder, GR);
2525
case SPIRV::Relational:
2526
return generateRelationalInst(Call.get(), MIRBuilder, GR);
2527
case SPIRV::Group:
2528
return generateGroupInst(Call.get(), MIRBuilder, GR);
2529
case SPIRV::Variable:
2530
return generateBuiltinVar(Call.get(), MIRBuilder, GR);
2531
case SPIRV::Atomic:
2532
return generateAtomicInst(Call.get(), MIRBuilder, GR);
2533
case SPIRV::AtomicFloating:
2534
return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);
2535
case SPIRV::Barrier:
2536
return generateBarrierInst(Call.get(), MIRBuilder, GR);
2537
case SPIRV::CastToPtr:
2538
return generateCastToPtrInst(Call.get(), MIRBuilder);
2539
case SPIRV::Dot:
2540
return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);
2541
case SPIRV::Wave:
2542
return generateWaveInst(Call.get(), MIRBuilder, GR);
2543
case SPIRV::GetQuery:
2544
return generateGetQueryInst(Call.get(), MIRBuilder, GR);
2545
case SPIRV::ImageSizeQuery:
2546
return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);
2547
case SPIRV::ImageMiscQuery:
2548
return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);
2549
case SPIRV::ReadImage:
2550
return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2551
case SPIRV::WriteImage:
2552
return generateWriteImageInst(Call.get(), MIRBuilder, GR);
2553
case SPIRV::SampleImage:
2554
return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2555
case SPIRV::Select:
2556
return generateSelectInst(Call.get(), MIRBuilder);
2557
case SPIRV::Construct:
2558
return generateConstructInst(Call.get(), MIRBuilder, GR);
2559
case SPIRV::SpecConstant:
2560
return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
2561
case SPIRV::Enqueue:
2562
return generateEnqueueInst(Call.get(), MIRBuilder, GR);
2563
case SPIRV::AsyncCopy:
2564
return generateAsyncCopy(Call.get(), MIRBuilder, GR);
2565
case SPIRV::Convert:
2566
return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
2567
case SPIRV::VectorLoadStore:
2568
return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);
2569
case SPIRV::LoadStore:
2570
return generateLoadStoreInst(Call.get(), MIRBuilder, GR);
2571
case SPIRV::IntelSubgroups:
2572
return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR);
2573
case SPIRV::GroupUniform:
2574
return generateGroupUniformInst(Call.get(), MIRBuilder, GR);
2575
case SPIRV::KernelClock:
2576
return generateKernelClockInst(Call.get(), MIRBuilder, GR);
2577
case SPIRV::CoopMatr:
2578
return generateCoopMatrInst(Call.get(), MIRBuilder, GR);
2579
}
2580
return false;
2581
}
2582
2583
Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall,
2584
unsigned ArgIdx, LLVMContext &Ctx) {
2585
SmallVector<StringRef, 10> BuiltinArgsTypeStrs;
2586
StringRef BuiltinArgs =
2587
DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
2588
BuiltinArgs.split(BuiltinArgsTypeStrs, ',', -1, false);
2589
if (ArgIdx >= BuiltinArgsTypeStrs.size())
2590
return nullptr;
2591
StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim();
2592
2593
// Parse strings representing OpenCL builtin types.
2594
if (hasBuiltinTypePrefix(TypeStr)) {
2595
// OpenCL builtin types in demangled call strings have the following format:
2596
// e.g. ocl_image2d_ro
2597
[[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_");
2598
assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix");
2599
2600
// Check if this is pointer to a builtin type and not just pointer
2601
// representing a builtin type. In case it is a pointer to builtin type,
2602
// this will require additional handling in the method calling
2603
// parseBuiltinCallArgumentBaseType(...) as this function only retrieves the
2604
// base types.
2605
if (TypeStr.ends_with("*"))
2606
TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));
2607
2608
return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",
2609
Ctx);
2610
}
2611
2612
// Parse type name in either "typeN" or "type vector[N]" format, where
2613
// N is the number of elements of the vector.
2614
Type *BaseType;
2615
unsigned VecElts = 0;
2616
2617
BaseType = parseBasicTypeName(TypeStr, Ctx);
2618
if (!BaseType)
2619
// Unable to recognize SPIRV type name.
2620
return nullptr;
2621
2622
// Handle "typeN*" or "type vector[N]*".
2623
TypeStr.consume_back("*");
2624
2625
if (TypeStr.consume_front(" vector["))
2626
TypeStr = TypeStr.substr(0, TypeStr.find(']'));
2627
2628
TypeStr.getAsInteger(10, VecElts);
2629
if (VecElts > 0)
2630
BaseType = VectorType::get(
2631
BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);
2632
2633
return BaseType;
2634
}
2635
2636
struct BuiltinType {
2637
StringRef Name;
2638
uint32_t Opcode;
2639
};
2640
2641
#define GET_BuiltinTypes_DECL
2642
#define GET_BuiltinTypes_IMPL
2643
2644
struct OpenCLType {
2645
StringRef Name;
2646
StringRef SpirvTypeLiteral;
2647
};
2648
2649
#define GET_OpenCLTypes_DECL
2650
#define GET_OpenCLTypes_IMPL
2651
2652
#include "SPIRVGenTables.inc"
2653
} // namespace SPIRV
2654
2655
//===----------------------------------------------------------------------===//
2656
// Misc functions for parsing builtin types.
2657
//===----------------------------------------------------------------------===//
2658
2659
static Type *parseTypeString(const StringRef Name, LLVMContext &Context) {
2660
if (Name.starts_with("void"))
2661
return Type::getVoidTy(Context);
2662
else if (Name.starts_with("int") || Name.starts_with("uint"))
2663
return Type::getInt32Ty(Context);
2664
else if (Name.starts_with("float"))
2665
return Type::getFloatTy(Context);
2666
else if (Name.starts_with("half"))
2667
return Type::getHalfTy(Context);
2668
report_fatal_error("Unable to recognize type!");
2669
}
2670
2671
//===----------------------------------------------------------------------===//
2672
// Implementation functions for builtin types.
2673
//===----------------------------------------------------------------------===//
2674
2675
static SPIRVType *getNonParameterizedType(const TargetExtType *ExtensionType,
2676
const SPIRV::BuiltinType *TypeRecord,
2677
MachineIRBuilder &MIRBuilder,
2678
SPIRVGlobalRegistry *GR) {
2679
unsigned Opcode = TypeRecord->Opcode;
2680
// Create or get an existing type from GlobalRegistry.
2681
return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode);
2682
}
2683
2684
static SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder,
2685
SPIRVGlobalRegistry *GR) {
2686
// Create or get an existing type from GlobalRegistry.
2687
return GR->getOrCreateOpTypeSampler(MIRBuilder);
2688
}
2689
2690
static SPIRVType *getPipeType(const TargetExtType *ExtensionType,
2691
MachineIRBuilder &MIRBuilder,
2692
SPIRVGlobalRegistry *GR) {
2693
assert(ExtensionType->getNumIntParameters() == 1 &&
2694
"Invalid number of parameters for SPIR-V pipe builtin!");
2695
// Create or get an existing type from GlobalRegistry.
2696
return GR->getOrCreateOpTypePipe(MIRBuilder,
2697
SPIRV::AccessQualifier::AccessQualifier(
2698
ExtensionType->getIntParameter(0)));
2699
}
2700
2701
static SPIRVType *getCoopMatrType(const TargetExtType *ExtensionType,
2702
MachineIRBuilder &MIRBuilder,
2703
SPIRVGlobalRegistry *GR) {
2704
assert(ExtensionType->getNumIntParameters() == 4 &&
2705
"Invalid number of parameters for SPIR-V coop matrices builtin!");
2706
assert(ExtensionType->getNumTypeParameters() == 1 &&
2707
"SPIR-V coop matrices builtin type must have a type parameter!");
2708
const SPIRVType *ElemType =
2709
GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
2710
// Create or get an existing type from GlobalRegistry.
2711
return GR->getOrCreateOpTypeCoopMatr(
2712
MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0),
2713
ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
2714
ExtensionType->getIntParameter(3));
2715
}
2716
2717
static SPIRVType *
2718
getImageType(const TargetExtType *ExtensionType,
2719
const SPIRV::AccessQualifier::AccessQualifier Qualifier,
2720
MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
2721
assert(ExtensionType->getNumTypeParameters() == 1 &&
2722
"SPIR-V image builtin type must have sampled type parameter!");
2723
const SPIRVType *SampledType =
2724
GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
2725
assert(ExtensionType->getNumIntParameters() == 7 &&
2726
"Invalid number of parameters for SPIR-V image builtin!");
2727
// Create or get an existing type from GlobalRegistry.
2728
return GR->getOrCreateOpTypeImage(
2729
MIRBuilder, SampledType,
2730
SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)),
2731
ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
2732
ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4),
2733
SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)),
2734
Qualifier == SPIRV::AccessQualifier::WriteOnly
2735
? SPIRV::AccessQualifier::WriteOnly
2736
: SPIRV::AccessQualifier::AccessQualifier(
2737
ExtensionType->getIntParameter(6)));
2738
}
2739
2740
static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType,
2741
MachineIRBuilder &MIRBuilder,
2742
SPIRVGlobalRegistry *GR) {
2743
SPIRVType *OpaqueImageType = getImageType(
2744
OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder, GR);
2745
// Create or get an existing type from GlobalRegistry.
2746
return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
2747
}
2748
2749
namespace SPIRV {
2750
TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,
2751
LLVMContext &Context) {
2752
StringRef NameWithParameters = TypeName;
2753
2754
// Pointers-to-opaque-structs representing OpenCL types are first translated
2755
// to equivalent SPIR-V types. OpenCL builtin type names should have the
2756
// following format: e.g. %opencl.event_t
2757
if (NameWithParameters.starts_with("opencl.")) {
2758
const SPIRV::OpenCLType *OCLTypeRecord =
2759
SPIRV::lookupOpenCLType(NameWithParameters);
2760
if (!OCLTypeRecord)
2761
report_fatal_error("Missing TableGen record for OpenCL type: " +
2762
NameWithParameters);
2763
NameWithParameters = OCLTypeRecord->SpirvTypeLiteral;
2764
// Continue with the SPIR-V builtin type...
2765
}
2766
2767
// Names of the opaque structs representing a SPIR-V builtins without
2768
// parameters should have the following format: e.g. %spirv.Event
2769
assert(NameWithParameters.starts_with("spirv.") &&
2770
"Unknown builtin opaque type!");
2771
2772
// Parameterized SPIR-V builtins names follow this format:
2773
// e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._0
2774
if (!NameWithParameters.contains('_'))
2775
return TargetExtType::get(Context, NameWithParameters);
2776
2777
SmallVector<StringRef> Parameters;
2778
unsigned BaseNameLength = NameWithParameters.find('_') - 1;
2779
SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_");
2780
2781
SmallVector<Type *, 1> TypeParameters;
2782
bool HasTypeParameter = !isDigit(Parameters[0][0]);
2783
if (HasTypeParameter)
2784
TypeParameters.push_back(parseTypeString(Parameters[0], Context));
2785
SmallVector<unsigned> IntParameters;
2786
for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) {
2787
unsigned IntParameter = 0;
2788
bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter);
2789
(void)ValidLiteral;
2790
assert(ValidLiteral &&
2791
"Invalid format of SPIR-V builtin parameter literal!");
2792
IntParameters.push_back(IntParameter);
2793
}
2794
return TargetExtType::get(Context,
2795
NameWithParameters.substr(0, BaseNameLength),
2796
TypeParameters, IntParameters);
2797
}
2798
2799
SPIRVType *lowerBuiltinType(const Type *OpaqueType,
2800
SPIRV::AccessQualifier::AccessQualifier AccessQual,
2801
MachineIRBuilder &MIRBuilder,
2802
SPIRVGlobalRegistry *GR) {
2803
// In LLVM IR, SPIR-V and OpenCL builtin types are represented as either
2804
// target(...) target extension types or pointers-to-opaque-structs. The
2805
// approach relying on structs is deprecated and works only in the non-opaque
2806
// pointer mode (-opaque-pointers=0).
2807
// In order to maintain compatibility with LLVM IR generated by older versions
2808
// of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are
2809
// "translated" to target extension types. This translation is temporary and
2810
// will be removed in the future release of LLVM.
2811
const TargetExtType *BuiltinType = dyn_cast<TargetExtType>(OpaqueType);
2812
if (!BuiltinType)
2813
BuiltinType = parseBuiltinTypeNameToTargetExtType(
2814
OpaqueType->getStructName().str(), MIRBuilder.getContext());
2815
2816
unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
2817
2818
const StringRef Name = BuiltinType->getName();
2819
LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
2820
2821
// Lookup the demangled builtin type in the TableGen records.
2822
const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
2823
if (!TypeRecord)
2824
report_fatal_error("Missing TableGen record for builtin type: " + Name);
2825
2826
// "Lower" the BuiltinType into TargetType. The following get<...>Type methods
2827
// use the implementation details from TableGen records or TargetExtType
2828
// parameters to either create a new OpType<...> machine instruction or get an
2829
// existing equivalent SPIRVType from GlobalRegistry.
2830
SPIRVType *TargetType;
2831
switch (TypeRecord->Opcode) {
2832
case SPIRV::OpTypeImage:
2833
TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
2834
break;
2835
case SPIRV::OpTypePipe:
2836
TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
2837
break;
2838
case SPIRV::OpTypeDeviceEvent:
2839
TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
2840
break;
2841
case SPIRV::OpTypeSampler:
2842
TargetType = getSamplerType(MIRBuilder, GR);
2843
break;
2844
case SPIRV::OpTypeSampledImage:
2845
TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
2846
break;
2847
case SPIRV::OpTypeCooperativeMatrixKHR:
2848
TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
2849
break;
2850
default:
2851
TargetType =
2852
getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
2853
break;
2854
}
2855
2856
// Emit OpName instruction if a new OpType<...> instruction was added
2857
// (equivalent type was not found in GlobalRegistry).
2858
if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
2859
buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder);
2860
2861
return TargetType;
2862
}
2863
} // namespace SPIRV
2864
} // namespace llvm
2865
2866