1 //===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements lowering builtin function calls and types using their
10 // demangled names and TableGen records.
12 //===----------------------------------------------------------------------===//
14 #include "SPIRVBuiltins.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"
24 #define DEBUG_TYPE "spirv-builtins"
28 #define GET_BuiltinGroup_DECL
29 #include "SPIRVGenTables.inc"
31 struct DemangledBuiltin
{
33 InstructionSet::InstructionSet Set
;
39 #define GET_DemangledBuiltins_DECL
40 #define GET_DemangledBuiltins_IMPL
43 const std::string BuiltinName
;
44 const DemangledBuiltin
*Builtin
;
46 const Register ReturnRegister
;
47 const SPIRVType
*ReturnType
;
48 const SmallVectorImpl
<Register
> &Arguments
;
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
) {}
57 bool isSpirvOp() const { return BuiltinName
.rfind("__spirv_", 0) == 0; }
60 struct NativeBuiltin
{
62 InstructionSet::InstructionSet Set
;
66 #define GET_NativeBuiltins_DECL
67 #define GET_NativeBuiltins_IMPL
72 uint32_t GroupOperation
;
78 bool IsBallotBitExtract
;
81 bool NoGroupOperation
;
85 #define GET_GroupBuiltins_DECL
86 #define GET_GroupBuiltins_IMPL
88 struct IntelSubgroupsBuiltin
{
95 #define GET_IntelSubgroupsBuiltins_DECL
96 #define GET_IntelSubgroupsBuiltins_IMPL
98 struct AtomicFloatingBuiltin
{
103 #define GET_AtomicFloatingBuiltins_DECL
104 #define GET_AtomicFloatingBuiltins_IMPL
105 struct GroupUniformBuiltin
{
111 #define GET_GroupUniformBuiltins_DECL
112 #define GET_GroupUniformBuiltins_IMPL
116 InstructionSet::InstructionSet Set
;
117 BuiltIn::BuiltIn Value
;
120 using namespace BuiltIn
;
121 #define GET_GetBuiltins_DECL
122 #define GET_GetBuiltins_IMPL
124 struct ImageQueryBuiltin
{
126 InstructionSet::InstructionSet Set
;
130 #define GET_ImageQueryBuiltins_DECL
131 #define GET_ImageQueryBuiltins_IMPL
133 struct ConvertBuiltin
{
135 InstructionSet::InstructionSet Set
;
136 bool IsDestinationSigned
;
140 FPRoundingMode::FPRoundingMode RoundingMode
;
143 struct VectorLoadStoreBuiltin
{
145 InstructionSet::InstructionSet Set
;
147 uint32_t ElementCount
;
149 FPRoundingMode::FPRoundingMode RoundingMode
;
152 using namespace FPRoundingMode
;
153 #define GET_ConvertBuiltins_DECL
154 #define GET_ConvertBuiltins_IMPL
156 using namespace InstructionSet
;
157 #define GET_VectorLoadStoreBuiltins_DECL
158 #define GET_VectorLoadStoreBuiltins_IMPL
160 #define GET_CLMemoryScope_DECL
161 #define GET_CLSamplerAddressingMode_DECL
162 #define GET_CLMemoryFenceFlags_DECL
163 #define GET_ExtendedBuiltins_DECL
164 #include "SPIRVGenTables.inc"
167 //===----------------------------------------------------------------------===//
168 // Misc functions for looking up builtins and veryfying requirements using
170 //===----------------------------------------------------------------------===//
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());
181 BuiltinName
= DemangledCall
;
182 // Extract the builtin function name and types of arguments from the call
184 BuiltinName
= BuiltinName
.substr(0, BuiltinName
.find('('));
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);
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);
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"));
210 /// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
211 /// the provided \p DemangledCall and specified \p Set.
213 /// The lookup follows the following algorithm, returning the first successful
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.
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
);
227 SmallVector
<StringRef
, 10> BuiltinArgumentTypes
;
228 StringRef BuiltinArgs
=
229 DemangledCall
.slice(DemangledCall
.find('(') + 1, DemangledCall
.find(')'));
230 BuiltinArgs
.split(BuiltinArgumentTypes
, ',', -1, false);
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
);
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_".
248 switch (FirstArgumentType
) {
251 if (Set
== SPIRV::InstructionSet::OpenCL_std
)
253 else if (Set
== SPIRV::InstructionSet::GLSL_std_450
)
261 if (Set
== SPIRV::InstructionSet::OpenCL_std
)
263 else if (Set
== SPIRV::InstructionSet::GLSL_std_450
)
270 if (Set
== SPIRV::InstructionSet::OpenCL_std
||
271 Set
== SPIRV::InstructionSet::GLSL_std_450
)
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
);
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".
287 switch (FirstArgumentType
) {
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
);
314 // No builtin with such name was found in the set.
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
);
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();
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();
356 if (isSpvIntrinsic(*NextMI
, Intrinsic::spv_assign_name
))
357 if ((NextMI
= NextMI
->getNextNode()) == 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
)
364 Type
*Ty
= getMDOperandAsType(NextMI
->getOperand(2).getMetadata(), 0);
365 assert(Ty
&& "Type is expected");
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
);
384 //===----------------------------------------------------------------------===//
385 // Helper functions for building misc instructions
386 //===----------------------------------------------------------------------===//
388 /// Helper function building either a resulting scalar or vector bool register
389 /// depending on the expected \p ResultType.
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
) {
396 SPIRVType
*BoolType
= GR
->getOrCreateSPIRVBoolType(MIRBuilder
);
398 if (ResultType
->getOpcode() == SPIRV::OpTypeVector
) {
399 unsigned VectorElements
= ResultType
->getOperand(2).getImm();
401 GR
->getOrCreateSPIRVVectorType(BoolType
, VectorElements
, MIRBuilder
);
402 const FixedVectorType
*LLVMVectorType
=
403 cast
<FixedVectorType
>(GR
->getTypeForSPIRVType(BoolType
));
404 Type
= LLT::vector(LLVMVectorType
->getElementCount(), 1);
406 Type
= LLT::scalar(1);
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
);
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
;
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
);
430 TrueConst
= GR
->buildConstantInt(1, MIRBuilder
, ReturnType
);
431 FalseConst
= GR
->buildConstantInt(0, MIRBuilder
, ReturnType
);
433 return MIRBuilder
.buildSelect(ReturnRegister
, SourceRegister
, TrueConst
,
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());
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
;
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());
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
,
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
;
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
);
494 // TODO: Move to TableGen.
495 static SPIRV::MemorySemantics::MemorySemantics
496 getSPIRVMemSemantics(std::memory_order 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
;
509 report_fatal_error("Unknown CL memory scope");
513 static SPIRV::Scope::Scope
getSPIRVScope(SPIRV::CLMemoryScope 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
;
526 report_fatal_error("Unknown CL memory scope");
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
);
536 static Register
buildScopeReg(Register CLScopeRegister
,
537 SPIRV::Scope::Scope Scope
,
538 MachineIRBuilder
&MIRBuilder
,
539 SPIRVGlobalRegistry
*GR
,
540 MachineRegisterInfo
*MRI
) {
541 if (CLScopeRegister
.isValid()) {
543 static_cast<SPIRV::CLMemoryScope
>(getIConstVal(CLScopeRegister
, MRI
));
544 Scope
= getSPIRVScope(CLScope
);
546 if (CLScope
== static_cast<unsigned>(Scope
)) {
547 MRI
->setRegClass(CLScopeRegister
, &SPIRV::IDRegClass
);
548 return CLScopeRegister
;
551 return buildConstantIntReg(Scope
, MIRBuilder
, GR
);
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
));
563 getSPIRVMemSemantics(Order
) |
564 getMemSemanticsForStorageClass(GR
->getPointerStorageClass(PtrRegister
));
566 if (Order
== Semantics
) {
567 MRI
->setRegClass(SemanticsRegister
, &SPIRV::IDRegClass
);
568 return SemanticsRegister
;
571 return buildConstantIntReg(Semantics
, MIRBuilder
, GR
);
574 static bool buildOpFromWrapper(MachineIRBuilder
&MIRBuilder
, unsigned Opcode
,
575 const SPIRV::IncomingCall
*Call
,
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
);
589 for (uint32_t ImmArg
: ImmArgs
)
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));
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]);
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
);
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
);
628 ScopeRegister
= buildConstantIntReg(SPIRV::Scope::Device
, MIRBuilder
, GR
);
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
);
637 SPIRV::MemorySemantics::SequentiallyConsistent
|
638 getMemSemanticsForStorageClass(GR
->getPointerStorageClass(PtrRegister
));
639 MemSemanticsReg
= buildConstantIntReg(Semantics
, MIRBuilder
, GR
);
642 MIRBuilder
.buildInstr(SPIRV::OpAtomicLoad
)
643 .addDef(Call
->ReturnRegister
)
646 .addUse(ScopeRegister
)
647 .addUse(MemSemanticsReg
);
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));
658 Register ScopeRegister
=
659 buildConstantIntReg(SPIRV::Scope::Device
, MIRBuilder
, GR
);
660 Register PtrRegister
= Call
->Arguments
[0];
661 MIRBuilder
.getMRI()->setRegClass(PtrRegister
, &SPIRV::IDRegClass
);
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
)
669 .addUse(ScopeRegister
)
670 .addUse(MemSemanticsReg
)
671 .addUse(Call
->Arguments
[1]);
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
));
683 bool IsCmpxchg
= Call
->Builtin
->Name
.contains("cmpxchg");
684 MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
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
);
695 assert(GR
->getSPIRVTypeForVReg(ObjectPtr
)->getOpcode() ==
696 SPIRV::OpTypePointer
);
697 unsigned ExpectedType
= GR
->getSPIRVTypeForVReg(ExpectedArg
)->getOpcode();
699 assert(IsCmpxchg
? ExpectedType
== SPIRV::OpTypeInt
700 : ExpectedType
== SPIRV::OpTypePointer
);
701 assert(GR
->isScalarOfType(Desired
, SPIRV::OpTypeInt
));
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
);
709 Register MemSemEqualReg
;
710 Register MemSemUnequalReg
;
711 uint64_t MemSemEqual
=
713 ? SPIRV::MemorySemantics::None
714 : SPIRV::MemorySemantics::SequentiallyConsistent
| MemSemStorage
;
715 uint64_t MemSemUnequal
=
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");
723 static_cast<std::memory_order
>(getIConstVal(Call
->Arguments
[3], MRI
));
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
);
735 if (!MemSemEqualReg
.isValid())
736 MemSemEqualReg
= buildConstantIntReg(MemSemEqual
, MIRBuilder
, GR
);
737 if (!MemSemUnequalReg
.isValid())
738 MemSemUnequalReg
= buildConstantIntReg(MemSemUnequal
, MIRBuilder
, GR
);
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
);
752 if (!ScopeReg
.isValid())
753 ScopeReg
= buildConstantIntReg(Scope
, MIRBuilder
, GR
);
755 Register Expected
= IsCmpxchg
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());
766 SPIRVType
*IntTy
= GR
->getOrCreateSPIRVIntegerType(32, MIRBuilder
);
767 MIRBuilder
.buildInstr(Opcode
)
769 .addUse(GR
->getSPIRVTypeID(IntTy
))
772 .addUse(MemSemEqualReg
)
773 .addUse(MemSemUnequalReg
)
777 MIRBuilder
.buildInstr(SPIRV::OpStore
).addUse(ExpectedArg
).addUse(Tmp
);
778 MIRBuilder
.buildICmp(CmpInst::ICMP_EQ
, Call
->ReturnRegister
, Tmp
, Expected
);
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
));
791 MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
792 Register ScopeRegister
=
793 Call
->Arguments
.size() >= 4 ? Call
->Arguments
[3] : Register();
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
);
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
,
823 MIRBuilder
.buildInstr(TargetOpcode::G_FNEG
)
826 insertAssignInstr(NegValueReg
, nullptr, Call
->ReturnType
, GR
, MIRBuilder
,
827 MIRBuilder
.getMF().getRegInfo());
828 ValueReg
= NegValueReg
;
831 MIRBuilder
.buildInstr(Opcode
)
832 .addDef(Call
->ReturnRegister
)
833 .addUse(ValueTypeReg
)
835 .addUse(ScopeRegister
)
836 .addUse(MemSemanticsReg
)
841 /// Helper function for building an atomic floating-type instruction.
842 static bool buildAtomicFloatingRMWInst(const SPIRV::IncomingCall
*Call
,
844 MachineIRBuilder
&MIRBuilder
,
845 SPIRVGlobalRegistry
*GR
) {
846 assert(Call
->Arguments
.size() == 4 &&
847 "Wrong number of atomic floating-type builtin");
849 MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
851 Register PtrReg
= Call
->Arguments
[0];
852 MRI
->setRegClass(PtrReg
, &SPIRV::IDRegClass
);
854 Register ScopeReg
= Call
->Arguments
[1];
855 MRI
->setRegClass(ScopeReg
, &SPIRV::IDRegClass
);
857 Register MemSemanticsReg
= Call
->Arguments
[2];
858 MRI
->setRegClass(MemSemanticsReg
, &SPIRV::IDRegClass
);
860 Register ValueReg
= Call
->Arguments
[3];
861 MRI
->setRegClass(ValueReg
, &SPIRV::IDRegClass
);
863 MIRBuilder
.buildInstr(Opcode
)
864 .addDef(Call
->ReturnRegister
)
865 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
868 .addUse(MemSemanticsReg
)
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));
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
);
892 assert((Opcode
!= SPIRV::OpAtomicFlagClear
||
893 (Semantics
!= SPIRV::MemorySemantics::Acquire
&&
894 Semantics
!= SPIRV::MemorySemantics::AcquireRelease
)) &&
895 "Invalid memory order argument!");
897 Register ScopeRegister
=
898 Call
->Arguments
.size() >= 3 ? Call
->Arguments
[2] : Register();
900 buildScopeReg(ScopeRegister
, SPIRV::Scope::Device
, MIRBuilder
, GR
, MRI
);
902 auto MIB
= MIRBuilder
.buildInstr(Opcode
);
904 MIB
.addDef(Call
->ReturnRegister
).addUse(TypeReg
);
906 MIB
.addUse(PtrRegister
).addUse(ScopeRegister
).addUse(MemSemanticsReg
);
910 /// Helper function for building barriers, i.e., memory/control ordering
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));
918 MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
919 unsigned MemFlags
= getIConstVal(Call
->Arguments
[0], MRI
);
920 unsigned MemSemantics
= SPIRV::MemorySemantics::None
;
922 if (MemFlags
& SPIRV::CLK_LOCAL_MEM_FENCE
)
923 MemSemantics
|= SPIRV::MemorySemantics::WorkgroupMemory
;
925 if (MemFlags
& SPIRV::CLK_GLOBAL_MEM_FENCE
)
926 MemSemantics
|= SPIRV::MemorySemantics::CrossWorkgroupMemory
;
928 if (MemFlags
& SPIRV::CLK_IMAGE_MEM_FENCE
)
929 MemSemantics
|= SPIRV::MemorySemantics::ImageMemory
;
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
;
936 MemSemantics
|= SPIRV::MemorySemantics::SequentiallyConsistent
;
939 Register MemSemanticsReg
;
940 if (MemFlags
== MemSemantics
) {
941 MemSemanticsReg
= Call
->Arguments
[0];
942 MRI
->setRegClass(MemSemanticsReg
, &SPIRV::IDRegClass
);
944 MemSemanticsReg
= buildConstantIntReg(MemSemantics
, MIRBuilder
, GR
);
947 SPIRV::Scope::Scope Scope
= SPIRV::Scope::Workgroup
;
948 SPIRV::Scope::Scope MemScope
= Scope
;
949 if (Call
->Arguments
.size() >= 2) {
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
))
963 if (CLScope
== static_cast<unsigned>(Scope
)) {
964 ScopeReg
= Call
->Arguments
[1];
965 MRI
->setRegClass(ScopeReg
, &SPIRV::IDRegClass
);
969 if (!ScopeReg
.isValid())
970 ScopeReg
= buildConstantIntReg(Scope
, MIRBuilder
, GR
);
972 auto MIB
= MIRBuilder
.buildInstr(Opcode
).addUse(ScopeReg
);
973 if (Opcode
!= SPIRV::OpMemoryBarrier
)
974 MIB
.addUse(buildConstantIntReg(MemScope
, MIRBuilder
, GR
));
975 MIB
.addUse(MemSemanticsReg
);
979 static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim
) {
981 case SPIRV::Dim::DIM_1D
:
982 case SPIRV::Dim::DIM_Buffer
:
984 case SPIRV::Dim::DIM_2D
:
985 case SPIRV::Dim::DIM_Cube
:
986 case SPIRV::Dim::DIM_Rect
:
988 case SPIRV::Dim::DIM_3D
:
991 report_fatal_error("Cannot get num components for given Dim");
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
;
1004 //===----------------------------------------------------------------------===//
1005 // Implementation functions for each builtin group
1006 //===----------------------------------------------------------------------===//
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
;
1014 SPIRV::lookupExtendedBuiltin(Builtin
->Name
, Builtin
->Set
)->Number
;
1016 // Build extended instruction.
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
))
1024 for (auto Argument
: Call
->Arguments
)
1025 MIB
.addUse(Argument
);
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
;
1035 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->Opcode
;
1037 Register CompareRegister
;
1038 SPIRVType
*RelationType
;
1039 std::tie(CompareRegister
, RelationType
) =
1040 buildBoolRegister(MIRBuilder
, Call
->ReturnType
, GR
);
1042 // Build relational instruction.
1043 auto MIB
= MIRBuilder
.buildInstr(Opcode
)
1044 .addDef(CompareRegister
)
1045 .addUse(GR
->getSPIRVTypeID(RelationType
));
1047 for (auto Argument
: Call
->Arguments
)
1048 MIB
.addUse(Argument
);
1050 // Build select instruction.
1051 return buildSelectInst(MIRBuilder
, Call
->ReturnRegister
, CompareRegister
,
1052 Call
->ReturnType
, GR
);
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
);
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
));
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
)
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
))
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
);
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() !=
1102 Arg0
= GR
->buildConstantInt(getIConstVal(ConstRegister
, MRI
), MIRBuilder
,
1103 GR
->getOrCreateSPIRVBoolType(MIRBuilder
));
1106 Register GroupResultRegister
= Call
->ReturnRegister
;
1107 SPIRVType
*GroupResultType
= Call
->ReturnType
;
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
;
1116 if (HasBoolReturnTy
)
1117 std::tie(GroupResultRegister
, GroupResultType
) =
1118 buildBoolRegister(MIRBuilder
, Call
->ReturnType
, GR
);
1120 auto Scope
= Builtin
->Name
.starts_with("sub_group") ? SPIRV::Scope::Subgroup
1121 : SPIRV::Scope::Workgroup
;
1122 Register ScopeRegister
= buildConstantIntReg(Scope
, MIRBuilder
, GR
);
1124 // Build work/sub group instruction.
1125 auto MIB
= MIRBuilder
.buildInstr(GroupBuiltin
->Opcode
)
1126 .addDef(GroupResultRegister
)
1127 .addUse(GR
->getSPIRVTypeID(GroupResultType
))
1128 .addUse(ScopeRegister
);
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
);
1141 // Build select instruction.
1142 if (HasBoolReturnTy
)
1143 buildSelectInst(MIRBuilder
, Call
->ReturnRegister
, GroupResultRegister
,
1144 Call
->ReturnType
, GR
);
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);
1160 const SPIRV::IntelSubgroupsBuiltin
*IntelSubgroups
=
1161 SPIRV::lookupIntelSubgroupsBuiltin(Builtin
->Name
);
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
)
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."
1182 case SPIRV::OpSubgroupBlockReadINTEL
:
1183 OpCode
= SPIRV::OpSubgroupImageBlockReadINTEL
;
1185 case SPIRV::OpSubgroupBlockWriteINTEL
:
1186 OpCode
= SPIRV::OpSubgroupImageBlockWriteINTEL
;
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
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
);
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);
1230 const SPIRV::GroupUniformBuiltin
*GroupUniform
=
1231 SPIRV::lookupGroupUniformBuiltin(Builtin
->Name
);
1232 MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
1234 Register GroupResultReg
= Call
->ReturnRegister
;
1235 MRI
->setRegClass(GroupResultReg
, &SPIRV::IDRegClass
);
1238 Register ScopeReg
= Call
->Arguments
[0];
1239 MRI
->setRegClass(ScopeReg
, &SPIRV::IDRegClass
);
1242 Register ConstGroupOpReg
= Call
->Arguments
[1];
1243 const MachineInstr
*Const
= getDefInstrMaybeConstant(ConstGroupOpReg
, MRI
);
1244 if (!Const
|| Const
->getOpcode() != TargetOpcode::G_CONSTANT
)
1246 "expect a constant group operation for a uniform group instruction",
1248 const MachineOperand
&ConstOperand
= Const
->getOperand(1);
1249 if (!ConstOperand
.isCImm())
1250 report_fatal_error("uniform group instructions: group operation must be an "
1255 Register ValueReg
= Call
->Arguments
[2];
1256 MRI
->setRegClass(ValueReg
, &SPIRV::IDRegClass
);
1258 auto MIB
= MIRBuilder
.buildInstr(GroupUniform
->Opcode
)
1259 .addDef(GroupResultReg
)
1260 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
1262 addNumImm(ConstOperand
.getCImm()->getValue(), MIB
);
1263 MIB
.addUse(ValueReg
);
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);
1281 MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
1282 Register ResultReg
= Call
->ReturnRegister
;
1283 MRI
->setRegClass(ResultReg
, &SPIRV::IDRegClass
);
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
);
1293 MIRBuilder
.buildInstr(SPIRV::OpReadClockKHR
)
1295 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
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
1308 // For a constant index >= 3 we generate:
1309 // %res = OpConstant %SizeT 0
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
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
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
);
1340 // Set up the final register to do truncation or extension on at the end.
1341 Register ToTruncate
= Call
->ReturnRegister
;
1343 // If the index is constant, we can statically determine if it is in range.
1344 bool IsConstantIndex
=
1345 IndexInstruction
->getOpcode() == TargetOpcode::G_CONSTANT
;
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
;
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.
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());
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
);
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
,
1385 auto IndexType
= GR
->getSPIRVTypeForVReg(IndexRegister
);
1386 auto BoolType
= GR
->getOrCreateSPIRVBoolType(MIRBuilder
);
1388 Register CompareRegister
=
1389 MRI
->createGenericVirtualRegister(LLT::scalar(1));
1390 MRI
->setRegClass(CompareRegister
, &SPIRV::IDRegClass
);
1391 GR
->assignSPIRVTypeToVReg(BoolType
, CompareRegister
, MIRBuilder
.getMF());
1393 // Use G_ICMP to check if idxVReg < 3.
1394 MIRBuilder
.buildICmp(CmpInst::ICMP_ULT
, CompareRegister
, IndexRegister
,
1395 GR
->buildConstantInt(3, MIRBuilder
, IndexType
));
1397 // Get constant for the default value (0 or 1 depending on which
1399 Register DefaultRegister
=
1400 GR
->buildConstantInt(DefaultValue
, MIRBuilder
, PointerSizeType
);
1402 // Get a register for the selection result (possibly a new temporary one).
1403 Register SelectionResult
= Call
->ReturnRegister
;
1404 if (PointerSize
!= ResultWidth
) {
1406 MRI
->createGenericVirtualRegister(LLT::scalar(PointerSize
));
1407 MRI
->setRegClass(SelectionResult
, &SPIRV::IDRegClass
);
1408 GR
->assignSPIRVTypeToVReg(PointerSizeType
, SelectionResult
,
1409 MIRBuilder
.getMF());
1411 // Create the final G_SELECT to return the extracted value or the default.
1412 MIRBuilder
.buildSelect(SelectionResult
, CompareRegister
, Extracted
,
1414 ToTruncate
= SelectionResult
;
1416 ToTruncate
= Extracted
;
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
);
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
;
1433 if (Value
== SPIRV::BuiltIn::GlobalInvocationId
)
1434 return genWorkgroupQuery(Call
, MIRBuilder
, GR
, Value
, 0);
1436 // Build a load instruction for the builtin variable.
1437 unsigned BitWidth
= GR
->getScalarOrVectorBitWidth(Call
->ReturnType
);
1439 if (Call
->ReturnType
->getOpcode() == SPIRV::OpTypeVector
)
1441 LLT::fixed_vector(Call
->ReturnType
->getOperand(2).getImm(), BitWidth
);
1443 LLType
= LLT::scalar(BitWidth
);
1445 return buildBuiltinVariableLoad(MIRBuilder
, Call
->ReturnType
, GR
, Value
,
1446 LLType
, Call
->ReturnRegister
);
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
;
1455 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->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
,
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
);
1481 if (Call
->isSpirvOp())
1482 return buildOpFromWrapper(MIRBuilder
, Opcode
, Call
,
1483 GR
->getSPIRVTypeID(Call
->ReturnType
));
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
;
1496 case SPIRV::OpAtomicFAddEXT
:
1497 case SPIRV::OpAtomicFMinEXT
:
1498 case SPIRV::OpAtomicFMaxEXT
:
1499 return buildAtomicFloatingRMWInst(Call
, Opcode
, MIRBuilder
, GR
);
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
;
1511 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->Opcode
;
1513 return buildBarrierInst(Call
, Opcode
, MIRBuilder
, GR
);
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]);
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]);
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
;
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
));
1552 return buildBuiltinVariableLoad(
1553 MIRBuilder
, Call
->ReturnType
, GR
, Value
, LLType
, Call
->ReturnRegister
,
1554 /* isConst= */ false, /* hasLinkageTy= */ false);
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);
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()
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());
1597 bool IsDimBuf
= ImgType
->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer
;
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]);
1606 MIB
.addUse(buildConstantIntReg(0, MIRBuilder
, GR
)); // Lod id.
1607 if (NumExpectedRetComponents
== NumActualRetComponents
)
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
;
1623 MIRBuilder
.buildInstr(SPIRV::OpCompositeExtract
)
1624 .addDef(Call
->ReturnRegister
)
1626 .addUse(QueryResult
)
1627 .addImm(ExtractedComposite
);
1628 if (NewType
!= nullptr)
1629 insertAssignInstr(Call
->ReturnRegister
, nullptr, NewType
, GR
, MIRBuilder
,
1630 MIRBuilder
.getMF().getRegInfo());
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);
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!");
1650 // Lookup the instruction opcode in the TableGen records.
1651 const SPIRV::DemangledBuiltin
*Builtin
= Call
->Builtin
;
1653 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->Opcode
;
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
;
1662 case SPIRV::OpImageQuerySamples
:
1663 assert(ImageDimensionality
== SPIRV::Dim::DIM_2D
&&
1664 "Image must be of 2D dimensionality");
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");
1675 MIRBuilder
.buildInstr(Opcode
)
1676 .addDef(Call
->ReturnRegister
)
1677 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
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
;
1697 report_fatal_error("Unknown CL address mode");
1701 static unsigned getSamplerParamFromBitmask(unsigned Bitmask
) {
1702 return (Bitmask
& SPIRV::CLK_NORMALIZED_COORDS_TRUE
) ? 1 : 0;
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
;
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];
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
));
1738 SPIRVType
*ImageType
= GR
->getSPIRVTypeForVReg(Image
);
1739 SPIRVType
*SampledImageType
=
1740 GR
->getOrCreateOpTypeSampledImage(ImageType
, MIRBuilder
);
1741 Register SampledImage
= MRI
->createVirtualRegister(&SPIRV::IDRegClass
);
1743 MIRBuilder
.buildInstr(SPIRV::OpSampledImage
)
1744 .addDef(SampledImage
)
1745 .addUse(GR
->getSPIRVTypeID(SampledImageType
))
1749 Register Lod
= GR
->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()),
1751 SPIRVType
*TempType
= Call
->ReturnType
;
1752 bool NeedsExtraction
= false;
1753 if (TempType
->getOpcode() != SPIRV::OpTypeVector
) {
1755 GR
->getOrCreateSPIRVVectorType(Call
->ReturnType
, 4, MIRBuilder
);
1756 NeedsExtraction
= true;
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());
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
)
1771 if (NeedsExtraction
)
1772 MIRBuilder
.buildInstr(SPIRV::OpCompositeExtract
)
1773 .addDef(Call
->ReturnRegister
)
1774 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
1775 .addUse(TempRegister
)
1777 } else if (HasMsaa
) {
1778 MIRBuilder
.buildInstr(SPIRV::OpImageRead
)
1779 .addDef(Call
->ReturnRegister
)
1780 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
1782 .addUse(Call
->Arguments
[1]) // Coordinate.
1783 .addImm(SPIRV::ImageOperand::Sample
)
1784 .addUse(Call
->Arguments
[2]);
1786 MIRBuilder
.buildInstr(SPIRV::OpImageRead
)
1787 .addDef(Call
->ReturnRegister
)
1788 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
1790 .addUse(Call
->Arguments
[1]); // Coordinate.
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.
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
))
1836 .addUse(Call
->Arguments
[1]); // Sampler.
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('('));
1849 : GR
->getOrCreateSPIRVTypeByName(ReturnType
, MIRBuilder
);
1851 std::string DiagMsg
=
1852 "Unable to recognize SPIRV type name: " + ReturnType
;
1853 report_fatal_error(DiagMsg
.c_str());
1855 MRI
->setRegClass(Call
->Arguments
[0], &SPIRV::IDRegClass
);
1856 MRI
->setRegClass(Call
->Arguments
[1], &SPIRV::IDRegClass
);
1857 MRI
->setRegClass(Call
->Arguments
[3], &SPIRV::IDRegClass
);
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]);
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]);
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
));
1885 static bool generateCoopMatrInst(const SPIRV::IncomingCall
*Call
,
1886 MachineIRBuilder
&MIRBuilder
,
1887 SPIRVGlobalRegistry
*GR
) {
1888 const SPIRV::DemangledBuiltin
*Builtin
= Call
->Builtin
;
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)
1896 else if (Opcode
== SPIRV::OpCooperativeMatrixStoreKHR
&& ArgSz
> 4)
1898 SmallVector
<uint32_t, 1> ImmArgs
;
1899 MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
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]);
1906 report_fatal_error("Can't find a register's type definition");
1907 MIRBuilder
.buildInstr(Opcode
)
1908 .addDef(Call
->ReturnRegister
)
1910 .addUse(CoopMatrType
->getOperand(0).getReg());
1913 return buildOpFromWrapper(MIRBuilder
, Opcode
, Call
,
1914 IsSet
? TypeReg
: Register(0), ImmArgs
);
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
;
1923 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->Opcode
;
1924 const MachineRegisterInfo
*MRI
= MIRBuilder
.getMRI();
1927 case SPIRV::OpSpecConstant
: {
1928 // Build the SpecID decoration.
1930 static_cast<unsigned>(getIConstVal(Call
->Arguments
[0], MRI
));
1931 buildOpDecorate(Call
->ReturnRegister
, MIRBuilder
, SPIRV::Decoration::SpecId
,
1933 // Determine the constant MI.
1934 Register ConstRegister
= Call
->Arguments
[1];
1935 const MachineInstr
*Const
= getDefInstrMaybeConstant(ConstRegister
, MRI
);
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
;
1948 auto MIB
= MIRBuilder
.buildInstr(Opcode
)
1949 .addDef(Call
->ReturnRegister
)
1950 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
));
1952 if (Call
->ReturnType
->getOpcode() != SPIRV::OpTypeBool
) {
1953 if (Const
->getOpcode() == TargetOpcode::G_CONSTANT
)
1954 addNumImm(ConstOperand
.getCImm()->getValue(), MIB
);
1956 addNumImm(ConstOperand
.getFPImm()->getValueAPF().bitcastToAPInt(), MIB
);
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
]);
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
);
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
))
2021 const SPIRVSubtarget
&ST
=
2022 cast
<SPIRVSubtarget
>(MIRBuilder
.getMF().getSubtarget());
2023 Const
= GR
->getOrCreateConstIntArray(0, Size
, *MIRBuilder
.getInsertPt(),
2024 SpvFieldTy
, *ST
.getInstrInfo());
2026 Const
= GR
->buildConstantInt(0, MIRBuilder
, SpvTy
);
2028 if (!LocalWorkSize
.isValid())
2029 LocalWorkSize
= Const
;
2030 if (!GlobalWorkOffset
.isValid())
2031 GlobalWorkOffset
= Const
;
2033 assert(LocalWorkSize
.isValid() && GlobalWorkOffset
.isValid());
2034 MIRBuilder
.buildInstr(SPIRV::OpBuildNDRange
)
2037 .addUse(GlobalWorkSize
)
2038 .addUse(LocalWorkSize
)
2039 .addUse(GlobalWorkOffset
);
2040 return MIRBuilder
.buildInstr(SPIRV::OpStore
)
2041 .addUse(Call
->Arguments
[0])
2045 // TODO: maybe move to the global register.
2047 getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder
&MIRBuilder
,
2048 SPIRVGlobalRegistry
*GR
) {
2049 LLVMContext
&Context
= MIRBuilder
.getMF().getFunction().getContext();
2050 Type
*OpaqueType
= StructType::getTypeByName(Context
, "spirv.DeviceEvent");
2052 OpaqueType
= StructType::getTypeByName(Context
, "opencl.clk_event_t");
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
);
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
);
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);
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
);
2105 // SPIRV OpEnqueueKernel instruction has 10+ arguments.
2106 auto MIB
= MIRBuilder
.buildInstr(SPIRV::OpEnqueueKernel
)
2107 .addDef(Call
->ReturnRegister
)
2108 .addUse(GR
->getSPIRVTypeID(Int32Ty
));
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
]);
2115 // If there are no event arguments in the original call, add dummy ones.
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.
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());
2129 Register BlockLiteralReg
= Call
->Arguments
[BlockFIdx
+ 1];
2130 // Param: Pointer to block literal.
2131 MIB
.addUse(BlockLiteralReg
);
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.
2139 buildConstantIntReg(DL
.getPrefTypeAlign(PType
).value(), MIRBuilder
, GR
));
2141 for (unsigned i
= 0; i
< LocalSizes
.size(); i
++)
2142 MIB
.addUse(LocalSizes
[i
]);
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
;
2152 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->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
);
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
;
2199 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->Opcode
;
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));
2207 auto Scope
= buildConstantIntReg(SPIRV::Scope::Workgroup
, MIRBuilder
, GR
);
2210 case SPIRV::OpGroupAsyncCopy
: {
2211 SPIRVType
*NewType
=
2212 Call
->ReturnType
->getOpcode() == SPIRV::OpTypeEvent
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
)
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
))
2229 if (NewType
!= nullptr)
2230 insertAssignInstr(Call
->ReturnRegister
, nullptr, NewType
, GR
, MIRBuilder
,
2231 MIRBuilder
.getMF().getRegInfo());
2234 case SPIRV::OpGroupWaitEvents
:
2235 return MIRBuilder
.buildInstr(Opcode
)
2237 .addUse(Call
->Arguments
[0])
2238 .addUse(Call
->Arguments
[1]);
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
);
2252 if (!Builtin
&& Call
->isSpirvOp()) {
2253 const SPIRV::DemangledBuiltin
*Builtin
= Call
->Builtin
;
2255 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->Opcode
;
2256 return buildOpFromWrapper(MIRBuilder
, Opcode
, Call
,
2257 GR
->getSPIRVTypeID(Call
->ReturnType
));
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
});
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
)) {
2273 if (GR
->isScalarOrVectorOfType(Call
->ReturnRegister
, SPIRV::OpTypeInt
)) {
2275 if (Builtin
->IsSaturated
)
2276 Opcode
= Builtin
->IsDestinationSigned
? SPIRV::OpSatConvertUToS
2277 : SPIRV::OpSatConvertSToU
;
2279 Opcode
= Builtin
->IsDestinationSigned
? SPIRV::OpUConvert
2280 : SPIRV::OpSConvert
;
2281 } else if (GR
->isScalarOrVectorOfType(Call
->ReturnRegister
,
2282 SPIRV::OpTypeFloat
)) {
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
;
2295 bool IsSourceSigned
=
2296 DemangledCall
[DemangledCall
.find_first_of('(') + 1] != 'u';
2297 Opcode
= IsSourceSigned
? SPIRV::OpConvertSToF
: SPIRV::OpConvertUToF
;
2300 } else if (GR
->isScalarOrVectorOfType(Call
->Arguments
[0],
2301 SPIRV::OpTypeFloat
)) {
2303 if (GR
->isScalarOrVectorOfType(Call
->ReturnRegister
, SPIRV::OpTypeInt
)) {
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
;
2316 Opcode
= Builtin
->IsDestinationSigned
? SPIRV::OpConvertFToS
2317 : SPIRV::OpConvertFToU
;
2319 } else if (GR
->isScalarOrVectorOfType(Call
->ReturnRegister
,
2320 SPIRV::OpTypeFloat
)) {
2322 Opcode
= SPIRV::OpFConvert
;
2326 if (!NeedExtMsg
.empty()) {
2327 std::string DiagMsg
= std::string(Builtin
->Name
) +
2328 ": the builtin requires the following SPIR-V "
2331 report_fatal_error(DiagMsg
.c_str(), false);
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);
2339 assert(Opcode
!= SPIRV::OpNop
&&
2340 "Conversion between the types not implemented!");
2342 MIRBuilder
.buildInstr(Opcode
)
2343 .addDef(Call
->ReturnRegister
)
2344 .addUse(GR
->getSPIRVTypeID(Call
->ReturnType
))
2345 .addUse(Call
->Arguments
[0]);
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.
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
);
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
));
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
;
2381 SPIRV::lookupNativeBuiltin(Builtin
->Name
, Builtin
->Set
)->Opcode
;
2382 bool IsLoad
= Opcode
== SPIRV::OpLoad
;
2383 // Build the instruction.
2384 auto MIB
= MIRBuilder
.buildInstr(Opcode
);
2386 MIB
.addDef(Call
->ReturnRegister
);
2387 MIB
.addUse(GR
->getSPIRVTypeID(Call
->ReturnType
));
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.
2395 MIB
.addUse(Call
->Arguments
[1]);
2396 MRI
->setRegClass(Call
->Arguments
[1], &SPIRV::IDRegClass
);
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
);
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
);
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
) {
2423 SmallVector
<Register
> Args
;
2424 std::unique_ptr
<const IncomingCall
> Call
=
2425 lookupBuiltin(DemangledCall
, Set
, Reg
, nullptr, Args
);
2427 return std::make_tuple(-1, 0, 0);
2429 switch (Call
->Builtin
->Group
) {
2430 case SPIRV::Relational
:
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
:
2441 SPIRV::lookupNativeBuiltin(Call
->Builtin
->Name
, Call
->Builtin
->Set
))
2442 return std::make_tuple(Call
->Builtin
->Group
, R
->Opcode
, 0);
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
);
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
);
2455 if (const auto *R
= SPIRV::lookupGroupBuiltin(Call
->Builtin
->Name
))
2456 return std::make_tuple(Call
->Builtin
->Group
, R
->Opcode
, 0);
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);
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);
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);
2470 case SPIRV::WriteImage
:
2471 return std::make_tuple(Call
->Builtin
->Group
, SPIRV::OpImageWrite
, 0);
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
,
2477 case SPIRV::KernelClock
:
2478 return std::make_tuple(Call
->Builtin
->Group
, SPIRV::OpReadClockKHR
, 0);
2480 return std::make_tuple(-1, 0, 0);
2482 return std::make_tuple(-1, 0, 0);
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");
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
);
2506 // Lookup the builtin in the TableGen records.
2507 std::unique_ptr
<const IncomingCall
> Call
=
2508 lookupBuiltin(DemangledCall
, Set
, ReturnRegister
, ReturnType
, Args
);
2511 LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
2512 return std::nullopt
;
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");
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
);
2528 return generateGroupInst(Call
.get(), MIRBuilder
, GR
);
2529 case SPIRV::Variable
:
2530 return generateBuiltinVar(Call
.get(), MIRBuilder
, GR
);
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
);
2540 return generateDotOrFMulInst(Call
.get(), MIRBuilder
, GR
);
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
);
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
);
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())
2591 StringRef TypeStr
= BuiltinArgsTypeStrs
[ArgIdx
].trim();
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");
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
2605 if (TypeStr
.ends_with("*"))
2606 TypeStr
= TypeStr
.slice(0, TypeStr
.find_first_of(" *"));
2608 return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr
.str() + "_t",
2612 // Parse type name in either "typeN" or "type vector[N]" format, where
2613 // N is the number of elements of the vector.
2615 unsigned VecElts
= 0;
2617 BaseType
= parseBasicTypeName(TypeStr
, Ctx
);
2619 // Unable to recognize SPIRV type name.
2622 // Handle "typeN*" or "type vector[N]*".
2623 TypeStr
.consume_back("*");
2625 if (TypeStr
.consume_front(" vector["))
2626 TypeStr
= TypeStr
.substr(0, TypeStr
.find(']'));
2628 TypeStr
.getAsInteger(10, VecElts
);
2630 BaseType
= VectorType::get(
2631 BaseType
->isVoidTy() ? Type::getInt8Ty(Ctx
) : BaseType
, VecElts
, false);
2636 struct BuiltinType
{
2641 #define GET_BuiltinTypes_DECL
2642 #define GET_BuiltinTypes_IMPL
2646 StringRef SpirvTypeLiteral
;
2649 #define GET_OpenCLTypes_DECL
2650 #define GET_OpenCLTypes_IMPL
2652 #include "SPIRVGenTables.inc"
2653 } // namespace SPIRV
2655 //===----------------------------------------------------------------------===//
2656 // Misc functions for parsing builtin types.
2657 //===----------------------------------------------------------------------===//
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!");
2671 //===----------------------------------------------------------------------===//
2672 // Implementation functions for builtin types.
2673 //===----------------------------------------------------------------------===//
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
);
2684 static SPIRVType
*getSamplerType(MachineIRBuilder
&MIRBuilder
,
2685 SPIRVGlobalRegistry
*GR
) {
2686 // Create or get an existing type from GlobalRegistry.
2687 return GR
->getOrCreateOpTypeSampler(MIRBuilder
);
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)));
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));
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)));
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
);
2750 TargetExtType
*parseBuiltinTypeNameToTargetExtType(std::string TypeName
,
2751 LLVMContext
&Context
) {
2752 StringRef NameWithParameters
= TypeName
;
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
);
2761 report_fatal_error("Missing TableGen record for OpenCL type: " +
2762 NameWithParameters
);
2763 NameWithParameters
= OCLTypeRecord
->SpirvTypeLiteral
;
2764 // Continue with the SPIR-V builtin type...
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!");
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
);
2777 SmallVector
<StringRef
> Parameters
;
2778 unsigned BaseNameLength
= NameWithParameters
.find('_') - 1;
2779 SplitString(NameWithParameters
.substr(BaseNameLength
+ 1), Parameters
, "_");
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
);
2790 assert(ValidLiteral
&&
2791 "Invalid format of SPIR-V builtin parameter literal!");
2792 IntParameters
.push_back(IntParameter
);
2794 return TargetExtType::get(Context
,
2795 NameWithParameters
.substr(0, BaseNameLength
),
2796 TypeParameters
, IntParameters
);
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
);
2813 BuiltinType
= parseBuiltinTypeNameToTargetExtType(
2814 OpaqueType
->getStructName().str(), MIRBuilder
.getContext());
2816 unsigned NumStartingVRegs
= MIRBuilder
.getMRI()->getNumVirtRegs();
2818 const StringRef Name
= BuiltinType
->getName();
2819 LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name
<< "\n");
2821 // Lookup the demangled builtin type in the TableGen records.
2822 const SPIRV::BuiltinType
*TypeRecord
= SPIRV::lookupBuiltinType(Name
);
2824 report_fatal_error("Missing TableGen record for builtin type: " + Name
);
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
);
2835 case SPIRV::OpTypePipe
:
2836 TargetType
= getPipeType(BuiltinType
, MIRBuilder
, GR
);
2838 case SPIRV::OpTypeDeviceEvent
:
2839 TargetType
= GR
->getOrCreateOpTypeDeviceEvent(MIRBuilder
);
2841 case SPIRV::OpTypeSampler
:
2842 TargetType
= getSamplerType(MIRBuilder
, GR
);
2844 case SPIRV::OpTypeSampledImage
:
2845 TargetType
= getSampledImageType(BuiltinType
, MIRBuilder
, GR
);
2847 case SPIRV::OpTypeCooperativeMatrixKHR
:
2848 TargetType
= getCoopMatrType(BuiltinType
, MIRBuilder
, GR
);
2852 getNonParameterizedType(BuiltinType
, TypeRecord
, MIRBuilder
, GR
);
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
);
2863 } // namespace SPIRV