LLVM 24.0.0git
SPIRVBuiltins.cpp
Go to the documentation of this file.
1//===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements lowering builtin function calls and types using their
10// demangled names and TableGen records.
11//
12//===----------------------------------------------------------------------===//
13
14#include "SPIRVBuiltins.h"
15#include "SPIRV.h"
16#include "SPIRVSubtarget.h"
17#include "SPIRVUtils.h"
21#include "llvm/IR/IntrinsicsSPIRV.h"
22#include <regex>
23#include <string>
24#include <tuple>
25
26#define DEBUG_TYPE "spirv-builtins"
27
28namespace llvm {
29namespace SPIRV {
30#define GET_BuiltinGroup_DECL
31#include "SPIRVGenTables.inc"
32
35 InstructionSet::InstructionSet Set;
36 BuiltinGroup Group;
39
40 StringRef name() const;
41};
42
43#define GET_DemangledBuiltins_DECL
44#define GET_DemangledBuiltins_IMPL
45
63
66 InstructionSet::InstructionSet Set;
68};
69
70#define GET_NativeBuiltins_DECL
71#define GET_NativeBuiltins_IMPL
72
86
87#define GET_GroupBuiltins_DECL
88#define GET_GroupBuiltins_IMPL
89
97
98#define GET_IntelSubgroupsBuiltins_DECL
99#define GET_IntelSubgroupsBuiltins_IMPL
100
105
106#define GET_AtomicFloatingBuiltins_DECL
107#define GET_AtomicFloatingBuiltins_IMPL
113
114#define GET_GroupUniformBuiltins_DECL
115#define GET_GroupUniformBuiltins_IMPL
116
119 InstructionSet::InstructionSet Set;
120 BuiltIn::BuiltIn Value;
121};
122
123using namespace BuiltIn;
124#define GET_GetBuiltins_DECL
125#define GET_GetBuiltins_IMPL
126
129 InstructionSet::InstructionSet Set;
131};
132
133#define GET_ImageQueryBuiltins_DECL
134#define GET_ImageQueryBuiltins_IMPL
135
141
142#define GET_IntegerDotProductBuiltins_DECL
143#define GET_IntegerDotProductBuiltins_IMPL
144
147 InstructionSet::InstructionSet Set;
152 bool IsTF32;
153 FPRoundingMode::FPRoundingMode RoundingMode;
154};
155
158 InstructionSet::InstructionSet Set;
162 FPRoundingMode::FPRoundingMode RoundingMode;
163};
164
165using namespace FPRoundingMode;
166#define GET_ConvertBuiltins_DECL
167#define GET_ConvertBuiltins_IMPL
168
169using namespace InstructionSet;
170#define GET_VectorLoadStoreBuiltins_DECL
171#define GET_VectorLoadStoreBuiltins_IMPL
172
173#define GET_CLMemoryScope_DECL
174#define GET_CLSamplerAddressingMode_DECL
175#define GET_CLMemoryFenceFlags_DECL
176#define GET_ExtendedBuiltins_DECL
177#include "SPIRVGenTables.inc"
178
179// Defined here to reference declarations from tablegen.
181 return getDemangledBuiltinStr(Name);
182}
183} // namespace SPIRV
184
185//===----------------------------------------------------------------------===//
186// Misc functions for looking up builtins and veryfying requirements using
187// TableGen records
188//===----------------------------------------------------------------------===//
189
190namespace SPIRV {
191/// Parses the name part of the demangled builtin call.
192std::string lookupBuiltinNameHelper(StringRef DemangledCall,
193 FPDecorationId *DecorationId) {
194 StringRef PassPrefix = "(anonymous namespace)::";
195 StringRef SpvPrefix = "__spv::";
196 std::string BuiltinName = DemangledCall.str();
197
198 // Check if the extracted name contains type information between angle
199 // brackets. If so, the builtin is an instantiated template - needs to have
200 // the information after angle brackets and return type removed.
201 std::size_t Pos = BuiltinName.find(">(");
202 if (Pos != std::string::npos) {
203 BuiltinName = BuiltinName.substr(0, BuiltinName.rfind('<', Pos));
204 } else {
205 Pos = BuiltinName.find('(');
206 if (Pos != std::string::npos)
207 BuiltinName = BuiltinName.substr(0, Pos);
208 }
209 BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(' ') + 1);
210
211 // Itanium Demangler result may have "(anonymous namespace)::" or "__spv::"
212 // prefix.
213 if (BuiltinName.find(PassPrefix) == 0)
214 BuiltinName = BuiltinName.substr(PassPrefix.size());
215 else if (BuiltinName.find(SpvPrefix) == 0)
216 BuiltinName = BuiltinName.substr(SpvPrefix.size());
217
218 // Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR
219 if (BuiltinName.rfind("__spirv_ocl_", 0) == 0)
220 BuiltinName = BuiltinName.substr(12);
221
222 // Check if the extracted name begins with:
223 // - "__spirv_ImageSampleExplicitLod"
224 // - "__spirv_ImageRead"
225 // - "__spirv_ImageWrite"
226 // - "__spirv_ImageQuerySizeLod"
227 // - "__spirv_UDotKHR"
228 // - "__spirv_SDotKHR"
229 // - "__spirv_SUDotKHR"
230 // - "__spirv_SDotAccSatKHR"
231 // - "__spirv_UDotAccSatKHR"
232 // - "__spirv_SUDotAccSatKHR"
233 // - "__spirv_ReadClockKHR"
234 // - "__spirv_SubgroupBlockReadINTEL"
235 // - "__spirv_SubgroupImageBlockReadINTEL"
236 // - "__spirv_SubgroupImageMediaBlockReadINTEL"
237 // - "__spirv_SubgroupImageMediaBlockWriteINTEL"
238 // - "__spirv_Convert"
239 // - "__spirv_Round"
240 // - "__spirv_UConvert"
241 // - "__spirv_SConvert"
242 // - "__spirv_FConvert"
243 // - "__spirv_SatConvert"
244 // and maybe contains return type information at the end "_R<type>".
245 // If so, extract the plain builtin name without the type information.
246 static const std::regex SpvWithR(
247 "(__spirv_(ImageSampleExplicitLod|ImageRead|ImageWrite|ImageQuerySizeLod|"
248 "UDotKHR|"
249 "SDotKHR|SUDotKHR|SDotAccSatKHR|UDotAccSatKHR|SUDotAccSatKHR|"
250 "ReadClockKHR|SubgroupBlockReadINTEL|SubgroupImageBlockReadINTEL|"
251 "SubgroupImageMediaBlockReadINTEL|SubgroupImageMediaBlockWriteINTEL|"
252 "Convert|Round|"
253 "UConvert|SConvert|FConvert|SatConvert)[^_]*)(_R[^_]*_?(\\w+)?.*)?");
254 std::smatch Match;
255 if (std::regex_match(BuiltinName, Match, SpvWithR) && Match.size() > 1) {
256 std::ssub_match SubMatch;
257 if (DecorationId && Match.size() > 3) {
258 SubMatch = Match[4];
259 *DecorationId = demangledPostfixToDecorationId(SubMatch.str());
260 }
261 SubMatch = Match[1];
262 BuiltinName = SubMatch.str();
263 }
264
265 return BuiltinName;
266}
267} // namespace SPIRV
268
269/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
270/// the provided \p DemangledCall and specified \p Set.
271///
272/// The lookup follows the following algorithm, returning the first successful
273/// match:
274/// 1. Search with the plain demangled name (expecting a 1:1 match).
275/// 2. Search with the prefix before or suffix after the demangled name
276/// signyfying the type of the first argument.
277///
278/// \returns Wrapper around the demangled call and found builtin definition.
279static std::unique_ptr<const SPIRV::IncomingCall>
281 SPIRV::InstructionSet::InstructionSet Set,
282 Register ReturnRegister, SPIRVTypeInst ReturnType,
284 std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall);
285
286 SmallVector<StringRef, 10> BuiltinArgumentTypes;
287 StringRef BuiltinArgs =
288 DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
289 BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false);
290
291 // Look up the builtin in the defined set. Start with the plain demangled
292 // name, expecting a 1:1 match in the defined builtin set.
293 const SPIRV::DemangledBuiltin *Builtin;
294 if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set)))
295 return std::make_unique<SPIRV::IncomingCall>(
296 BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
297
298 // If the initial look up was unsuccessful and the demangled call takes at
299 // least 1 argument, add a prefix or suffix signifying the type of the first
300 // argument and repeat the search.
301 if (BuiltinArgumentTypes.size() >= 1) {
302 char FirstArgumentType = BuiltinArgumentTypes[0][0];
303 // Prefix and suffix to be added to the builtin's name for lookup.
304 // For example, OpenCL "abs" taking an unsigned value has a prefix "u_",
305 // and "group_reduce_max" taking an unsigned value has a suffix "u".
306 StringRef Prefix;
307 StringRef Suffix;
308
309 switch (FirstArgumentType) {
310 // Unsigned:
311 case 'u':
312 if (Set == SPIRV::InstructionSet::OpenCL_std)
313 Prefix = "u_";
314 else if (Set == SPIRV::InstructionSet::GLSL_std_450)
315 Prefix = "u";
316 Suffix = "u";
317 break;
318 // Signed:
319 case 'c':
320 case 's':
321 case 'i':
322 case 'l':
323 if (Set == SPIRV::InstructionSet::OpenCL_std)
324 Prefix = "s_";
325 else if (Set == SPIRV::InstructionSet::GLSL_std_450)
326 Prefix = "s";
327 Suffix = "s";
328 break;
329 // Floating-point:
330 case 'f':
331 case 'd':
332 case 'h':
333 if (Set == SPIRV::InstructionSet::OpenCL_std ||
334 Set == SPIRV::InstructionSet::GLSL_std_450)
335 Prefix = "f";
336 Suffix = "f";
337 break;
338 }
339
340 // If argument-type name prefix was added, look up the builtin again.
341 if (!Prefix.empty() &&
342 (Builtin = SPIRV::lookupBuiltin((Prefix + BuiltinName).str(), Set)))
343 return std::make_unique<SPIRV::IncomingCall>(
344 BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
345
346 if (!Suffix.empty() &&
347 (Builtin = SPIRV::lookupBuiltin((BuiltinName + Suffix).str(), Set)))
348 return std::make_unique<SPIRV::IncomingCall>(
349 BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
350 }
351
352 // No builtin with such name was found in the set.
353 return nullptr;
354}
355
357 MachineRegisterInfo *MRI) {
358 // We expect ParamReg to be defined by G_ADDRSPACE_CAST with a source from
359 // G_GLOBAL_VALUE or spv_alloca. Returns the source instruction.
360 MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
361 assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
362 MI->getOperand(1).isReg());
363 Register BitcastReg = MI->getOperand(1).getReg();
364 MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
365 assert(BitcastMI && "Definition for source reg not found.");
366 if (BitcastMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
367 isSpvIntrinsic(*BitcastMI, Intrinsic::spv_alloca))
368 return BitcastMI;
369 llvm_unreachable("getBlockStructInstr: unexpected instruction pattern");
370}
371
372// Return type of the instruction result from spv_assign_type intrinsic.
373// TODO: maybe unify with prelegalizer pass.
375 MachineInstr *NextMI = MI->getNextNode();
376 if (!NextMI)
377 return nullptr;
378 if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
379 if ((NextMI = NextMI->getNextNode()) == nullptr)
380 return nullptr;
381 Register ValueReg = MI->getOperand(0).getReg();
382 if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) &&
383 !isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) ||
384 NextMI->getOperand(1).getReg() != ValueReg)
385 return nullptr;
386 Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
387 assert(Ty && "Type is expected");
388 return Ty;
389}
390
391static const Type *getBlockStructType(Register ParamReg,
392 MachineRegisterInfo *MRI) {
393 // In principle, this information should be passed to us from Clang via
394 // an elementtype attribute. However, said attribute requires that
395 // the function call be an intrinsic, which is not. Instead, we rely on being
396 // able to trace this to the declaration of a variable: OpenCL C specification
397 // section 6.12.5 should guarantee that we can do this.
398 MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);
399 if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
400 return MI->getOperand(1).getGlobal()->getValueType();
401 assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
402 "Blocks in OpenCL C must be traceable to allocation site");
403 return getMachineInstrType(MI);
404}
405
406//===----------------------------------------------------------------------===//
407// Helper functions for building misc instructions
408//===----------------------------------------------------------------------===//
409
410/// Helper function building either a resulting scalar or vector bool register
411/// depending on the expected \p ResultType.
412///
413/// \returns Tuple of the resulting register and its type.
414static std::tuple<Register, SPIRVTypeInst>
417 LLT Type;
418 SPIRVTypeInst BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder, true);
419
420 if (isVectorType(ResultType)) {
421 unsigned VectorElements = GR->getScalarOrVectorComponentCount(ResultType);
422 BoolType = GR->getOrCreateSPIRVVectorType(BoolType, VectorElements,
423 MIRBuilder, true);
426 Type = LLT::vector(LLVMVectorType->getElementCount(), 1);
427 } else {
428 Type = LLT::scalar(1);
429 }
430
431 Register ResultRegister =
433 MIRBuilder.getMRI()->setRegClass(ResultRegister, GR->getRegClass(ResultType));
434 GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF());
435 return std::make_tuple(ResultRegister, BoolType);
436}
437
438/// Helper function for building either a vector or scalar select instruction
439/// depending on the expected \p ResultType.
440static bool buildSelectInst(MachineIRBuilder &MIRBuilder,
441 Register ReturnRegister, Register SourceRegister,
442 SPIRVTypeInst ReturnType, SPIRVGlobalRegistry *GR) {
443 Register TrueConst, FalseConst;
444
445 if (isVectorType(ReturnType)) {
446 unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType);
448 TrueConst =
449 GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType, true);
450 FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType, true);
451 } else {
452 TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType, true);
453 FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType, true);
454 }
455
456 return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst,
457 FalseConst);
458}
459
460/// Helper function for building a load instruction loading into the
461/// \p DestinationReg.
463 MachineIRBuilder &MIRBuilder,
465 Register DestinationReg = Register(0)) {
466 if (!DestinationReg.isValid())
467 DestinationReg = createVirtualRegister(BaseType, GR, MIRBuilder);
468 // TODO: consider using correct address space and alignment (p0 is canonical
469 // type for selection though).
471 MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align());
472 return DestinationReg;
473}
474
475/// Helper function for building a load instruction for loading a builtin global
476/// variable of \p BuiltinValue value.
478 MachineIRBuilder &MIRBuilder, SPIRVTypeInst VariableType,
479 SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType,
480 Register Reg = Register(0), bool isConst = true,
481 const std::optional<SPIRV::LinkageType::LinkageType> &LinkageTy = {
482 SPIRV::LinkageType::Import}) {
483 Register NewRegister =
484 MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::pIDRegClass);
485 MIRBuilder.getMRI()->setType(
486 NewRegister,
487 LLT::pointer(storageClassToAddressSpace(SPIRV::StorageClass::Function),
488 GR->getPointerSize()));
489 SPIRVTypeInst PtrType = GR->getOrCreateSPIRVPointerType(
490 VariableType, MIRBuilder, SPIRV::StorageClass::Input);
491 GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
492
493 // Set up the global OpVariable with the necessary builtin decorations.
494 Register Variable = GR->buildGlobalVariable(
495 NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
496 SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst, LinkageTy,
497 MIRBuilder, false);
498
499 // Load the value from the global variable.
500 Register LoadedRegister =
501 buildLoadInst(VariableType, Variable, MIRBuilder, GR, Reg);
502 MIRBuilder.getMRI()->setType(LoadedRegister, LLType);
503 return LoadedRegister;
504}
505
506/// Helper external function for assigning a SPIRV type to a register, ensuring
507/// the register class and type are set in MRI. Defined in
508/// SPIRVPreLegalizer.cpp.
509extern void updateRegType(Register Reg, Type *Ty, SPIRVTypeInst SpirvTy,
512
513// TODO: Move to TableGen.
514static SPIRV::MemorySemantics::MemorySemantics
515getSPIRVMemSemantics(std::memory_order MemOrder) {
516 switch (MemOrder) {
517 case std::memory_order_relaxed:
518 return SPIRV::MemorySemantics::None;
519 case std::memory_order_acquire:
520 return SPIRV::MemorySemantics::Acquire;
521 case std::memory_order_release:
522 return SPIRV::MemorySemantics::Release;
523 case std::memory_order_acq_rel:
524 return SPIRV::MemorySemantics::AcquireRelease;
525 case std::memory_order_seq_cst:
526 return SPIRV::MemorySemantics::SequentiallyConsistent;
527 default:
528 report_fatal_error("Unknown CL memory order");
529 }
530}
531
532static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) {
533 switch (ClScope) {
534 case SPIRV::CLMemoryScope::memory_scope_work_item:
535 return SPIRV::Scope::Invocation;
536 case SPIRV::CLMemoryScope::memory_scope_work_group:
537 return SPIRV::Scope::Workgroup;
538 case SPIRV::CLMemoryScope::memory_scope_device:
539 return SPIRV::Scope::Device;
540 case SPIRV::CLMemoryScope::memory_scope_all_svm_devices:
541 return SPIRV::Scope::CrossDevice;
542 case SPIRV::CLMemoryScope::memory_scope_sub_group:
543 return SPIRV::Scope::Subgroup;
544 }
545 report_fatal_error("Unknown CL memory scope");
546}
547
549 MachineIRBuilder &MIRBuilder,
551 return GR->buildConstantInt(
552 Val, MIRBuilder, GR->getOrCreateSPIRVIntegerType(32, MIRBuilder), true);
553}
554
555static Register buildScopeReg(Register CLScopeRegister,
556 SPIRV::Scope::Scope Scope,
557 MachineIRBuilder &MIRBuilder,
559 MachineRegisterInfo *MRI) {
560 if (CLScopeRegister.isValid()) {
561 auto CLScope =
562 static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI));
563 Scope = getSPIRVScope(CLScope);
564
565 if (CLScope == static_cast<unsigned>(Scope)) {
566 MRI->setRegClass(CLScopeRegister, &SPIRV::iIDRegClass);
567 return CLScopeRegister;
568 }
569 }
570 return buildConstantIntReg32(Scope, MIRBuilder, GR);
571}
572
575 if (MRI->getRegClassOrNull(Reg))
576 return;
578 MRI->setRegClass(Reg,
579 SpvType ? GR->getRegClass(SpvType) : &SPIRV::iIDRegClass);
580}
581
582/// Translates an OpenCL memory_order argument into the memory ordering part of
583/// the SPIR-V memory semantics.
584static SPIRV::MemorySemantics::MemorySemantics
587 static_cast<std::memory_order>(getIConstVal(OrderRegister, MRI)));
588}
589
590/// Combines the memory ordering with the storage-class part of the memory
591/// semantics into a constant register.
592static Register
593buildMemSemanticsReg(SPIRV::MemorySemantics::MemorySemantics Ordering,
594 unsigned StorageClassSem, MachineIRBuilder &MIRBuilder,
596 const auto *ST =
597 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
599 getMemSemanticsWithStorageClass(ST->getTargetTriple(), Ordering,
600 StorageClassSem),
601 MIRBuilder, GR);
602}
603
604static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode,
606 Register TypeReg,
607 ArrayRef<uint32_t> ImmArgs = {}) {
608 auto MIB = MIRBuilder.buildInstr(Opcode);
609 if (TypeReg.isValid())
610 MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
611 unsigned Sz = Call->Arguments.size() - ImmArgs.size();
612 for (unsigned i = 0; i < Sz; ++i)
613 MIB.addUse(Call->Arguments[i]);
614 for (uint32_t ImmArg : ImmArgs)
615 MIB.addImm(ImmArg);
616 return true;
617}
618
619/// Helper function for translating atomic init to OpStore.
621 MachineIRBuilder &MIRBuilder) {
622 if (Call->isSpirvOp())
623 return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0));
624
625 assert(Call->Arguments.size() == 2 &&
626 "Need 2 arguments for atomic init translation");
627 MIRBuilder.buildInstr(SPIRV::OpStore)
628 .addUse(Call->Arguments[0])
629 .addUse(Call->Arguments[1]);
630 return true;
631}
632
633/// Helper function for building an atomic load instruction.
635 MachineIRBuilder &MIRBuilder,
637 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
638 if (Call->isSpirvOp())
639 return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg);
640
641 // atomic_load_explicit(ptr, memory_order[, memory_scope]).
642 Register PtrRegister = Call->Arguments[0];
643 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
644
645 const SPIRV::MemorySemantics::MemorySemantics Ordering =
646 Call->Arguments.size() >= 2
647 ? getMemOrdering(Call->Arguments[1], MRI)
648 : SPIRV::MemorySemantics::SequentiallyConsistent;
649 const unsigned StorageClassSem =
651 Register MemSemanticsReg =
652 buildMemSemanticsReg(Ordering, StorageClassSem, MIRBuilder, GR);
653
654 Register ScopeRegister = buildScopeReg(
655 Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register(),
656 SPIRV::Scope::Device, MIRBuilder, GR, MRI);
657
658 MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
659 .addDef(Call->ReturnRegister)
660 .addUse(TypeReg)
661 .addUse(PtrRegister)
662 .addUse(ScopeRegister)
663 .addUse(MemSemanticsReg);
664 return true;
665}
666
667/// Helper function for building an atomic store instruction.
669 MachineIRBuilder &MIRBuilder,
671 if (Call->isSpirvOp())
672 return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call,
673 Register(0));
674
675 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
676 Register PtrRegister = Call->Arguments[0];
677 // atomic_store_explicit(ptr, value, memory_order[, memory_scope]).
678 const SPIRV::MemorySemantics::MemorySemantics Ordering =
679 Call->Arguments.size() >= 3
680 ? getMemOrdering(Call->Arguments[2], MRI)
681 : SPIRV::MemorySemantics::SequentiallyConsistent;
682 const unsigned StorageClassSem =
684 Register MemSemanticsReg =
685 buildMemSemanticsReg(Ordering, StorageClassSem, MIRBuilder, GR);
686 Register ScopeRegister = buildScopeReg(
687 Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register(),
688 SPIRV::Scope::Device, MIRBuilder, GR, MRI);
689 MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
690 .addUse(PtrRegister)
691 .addUse(ScopeRegister)
692 .addUse(MemSemanticsReg)
693 .addUse(Call->Arguments[1]);
694 return true;
695}
696
697/// Helper function for building an atomic compare-exchange instruction.
699 const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin,
700 unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
701 if (Call->isSpirvOp())
702 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
703 GR->getSPIRVTypeID(Call->ReturnType));
704
705 bool IsCmpxchg = Call->Builtin->name().contains("cmpxchg");
706 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
707
708 Register ObjectPtr = Call->Arguments[0]; // Pointer (volatile A *object.)
709 Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).
710 Register Desired = Call->Arguments[2]; // Value (C Desired).
711 SPIRVTypeInst SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
712 LLT DesiredLLT = MRI->getType(Desired);
713
714 assert(GR->getSPIRVTypeForVReg(ObjectPtr).isPointer());
715 [[maybe_unused]] SPIRVTypeInst ExpectedTy =
716 GR->getSPIRVTypeForVReg(ExpectedArg);
717 assert(IsCmpxchg ? ExpectedTy->getOpcode() == SPIRV::OpTypeInt
718 : ExpectedTy.isPointer());
719 assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
720
721 SPIRVTypeInst SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
722 assert((SpvObjectPtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR ||
723 SpvObjectPtrTy->getOperand(2).isReg()) &&
724 "SPIRV type is expected");
725 auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
726 SpvObjectPtrTy->getOperand(1).getImm());
727 auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
728
729 Register MemSemEqualReg;
730 Register MemSemUnequalReg;
731 uint64_t MemSemEqual =
732 IsCmpxchg
733 ? SPIRV::MemorySemantics::None
734 : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
735 uint64_t MemSemUnequal =
736 IsCmpxchg
737 ? SPIRV::MemorySemantics::None
738 : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
739 if (Call->Arguments.size() >= 4) {
740 assert(Call->Arguments.size() >= 5 &&
741 "Need 5+ args for explicit atomic cmpxchg");
742 auto MemOrdEq =
743 static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));
744 auto MemOrdNeq =
745 static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));
746 MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;
747 MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;
748 if (static_cast<unsigned>(MemOrdEq) == MemSemEqual)
749 MemSemEqualReg = Call->Arguments[3];
750 if (static_cast<unsigned>(MemOrdNeq) == MemSemUnequal)
751 MemSemUnequalReg = Call->Arguments[4];
752 }
753 if (!MemSemEqualReg.isValid())
754 MemSemEqualReg = buildConstantIntReg32(MemSemEqual, MIRBuilder, GR);
755 if (!MemSemUnequalReg.isValid())
756 MemSemUnequalReg = buildConstantIntReg32(MemSemUnequal, MIRBuilder, GR);
757
758 Register ScopeReg;
759 auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;
760 if (Call->Arguments.size() >= 6) {
761 assert(Call->Arguments.size() == 6 &&
762 "Extra args for explicit atomic cmpxchg");
763 auto ClScope = static_cast<SPIRV::CLMemoryScope>(
764 getIConstVal(Call->Arguments[5], MRI));
765 Scope = getSPIRVScope(ClScope);
766 if (ClScope == static_cast<unsigned>(Scope))
767 ScopeReg = Call->Arguments[5];
768 }
769 if (!ScopeReg.isValid())
770 ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
771
773 IsCmpxchg ? ExpectedArg
774 : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder, GR);
775 MRI->setType(Expected, DesiredLLT);
776 Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)
777 : Call->ReturnRegister;
778 if (!MRI->getRegClassOrNull(Tmp))
779 MRI->setRegClass(Tmp, GR->getRegClass(SpvDesiredTy));
780 GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());
781
782 MIRBuilder.buildInstr(Opcode)
783 .addDef(Tmp)
784 .addUse(GR->getSPIRVTypeID(SpvDesiredTy))
785 .addUse(ObjectPtr)
786 .addUse(ScopeReg)
787 .addUse(MemSemEqualReg)
788 .addUse(MemSemUnequalReg)
789 .addUse(Desired)
791 if (!IsCmpxchg) {
792 MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);
793 MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);
794 }
795 return true;
796}
797
798/// Helper function for building atomic instructions.
799static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
800 MachineIRBuilder &MIRBuilder,
802 if (Call->isSpirvOp())
803 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
804 GR->getSPIRVTypeID(Call->ReturnType));
805
806 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
807 Register ScopeRegister =
808 Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register();
809
810 assert(Call->Arguments.size() <= 4 &&
811 "Too many args for explicit atomic RMW");
812 ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup,
813 MIRBuilder, GR, MRI);
814
815 Register PtrRegister = Call->Arguments[0];
816 SPIRV::MemorySemantics::MemorySemantics Ordering =
817 SPIRV::MemorySemantics::None;
818 unsigned StorageClassSem = SPIRV::MemorySemantics::None;
819 if (Call->Arguments.size() >= 3) {
820 Ordering = getMemOrdering(Call->Arguments[2], MRI);
821 StorageClassSem =
823 }
824 Register MemSemanticsReg =
825 buildMemSemanticsReg(Ordering, StorageClassSem, MIRBuilder, GR);
826 Register ValueReg = Call->Arguments[1];
827 Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);
828 // support cl_ext_float_atomics
829 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {
830 if (Opcode == SPIRV::OpAtomicIAdd) {
831 Opcode = SPIRV::OpAtomicFAddEXT;
832 } else if (Opcode == SPIRV::OpAtomicISub) {
833 // Translate OpAtomicISub applied to a floating type argument to
834 // OpAtomicFAddEXT with the negative value operand
835 Opcode = SPIRV::OpAtomicFAddEXT;
836 Register NegValueReg =
837 MRI->createGenericVirtualRegister(MRI->getType(ValueReg));
838 MRI->setRegClass(NegValueReg, GR->getRegClass(Call->ReturnType));
839 GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg,
840 MIRBuilder.getMF());
841 MIRBuilder.buildInstr(TargetOpcode::G_FNEG)
842 .addDef(NegValueReg)
843 .addUse(ValueReg);
844 updateRegType(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder,
845 MIRBuilder.getMF().getRegInfo());
846 ValueReg = NegValueReg;
847 }
848 }
849 MIRBuilder.buildInstr(Opcode)
850 .addDef(Call->ReturnRegister)
851 .addUse(ValueTypeReg)
852 .addUse(PtrRegister)
853 .addUse(ScopeRegister)
854 .addUse(MemSemanticsReg)
855 .addUse(ValueReg);
856 return true;
857}
858
859/// Helper function for building an atomic floating-type instruction.
861 unsigned Opcode,
862 MachineIRBuilder &MIRBuilder,
864 assert(Call->Arguments.size() == 4 &&
865 "Wrong number of atomic floating-type builtin");
866 Register PtrReg = Call->Arguments[0];
867 Register ScopeReg = Call->Arguments[1];
868 Register MemSemanticsReg = Call->Arguments[2];
869 Register ValueReg = Call->Arguments[3];
870 MIRBuilder.buildInstr(Opcode)
871 .addDef(Call->ReturnRegister)
872 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
873 .addUse(PtrReg)
874 .addUse(ScopeReg)
875 .addUse(MemSemanticsReg)
876 .addUse(ValueReg);
877 return true;
878}
879
880/// Helper function for building atomic flag instructions (e.g.
881/// OpAtomicFlagTestAndSet).
883 unsigned Opcode, MachineIRBuilder &MIRBuilder,
885 bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet;
886 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
887 if (Call->isSpirvOp())
888 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
889 IsSet ? TypeReg : Register(0));
890
891 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
892 Register PtrRegister = Call->Arguments[0];
893 SPIRV::MemorySemantics::MemorySemantics Ordering =
894 SPIRV::MemorySemantics::SequentiallyConsistent;
895 unsigned StorageClassSem = SPIRV::MemorySemantics::None;
896 if (Call->Arguments.size() >= 2) {
897 Ordering = getMemOrdering(Call->Arguments[1], MRI);
898 StorageClassSem =
900 }
901
902 assert((Opcode != SPIRV::OpAtomicFlagClear ||
903 (Ordering != SPIRV::MemorySemantics::Acquire &&
904 Ordering != SPIRV::MemorySemantics::AcquireRelease)) &&
905 "Invalid memory order argument!");
906
907 Register MemSemanticsReg =
908 buildMemSemanticsReg(Ordering, StorageClassSem, MIRBuilder, GR);
909
910 Register ScopeRegister = buildScopeReg(
911 Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register(),
912 SPIRV::Scope::Device, MIRBuilder, GR, MRI);
913
914 auto MIB = MIRBuilder.buildInstr(Opcode);
915 if (IsSet)
916 MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
917
918 MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);
919 return true;
920}
921
922/// Helper function for building barriers, i.e., memory/control ordering
923/// operations.
924static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
925 MachineIRBuilder &MIRBuilder,
927 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
928 const auto *ST =
929 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
930 if ((Opcode == SPIRV::OpControlBarrierArriveINTEL ||
931 Opcode == SPIRV::OpControlBarrierWaitINTEL) &&
932 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_split_barrier)) {
933 std::string DiagMsg = std::string(Builtin->name()) +
934 ": the builtin requires the following SPIR-V "
935 "extension: SPV_INTEL_split_barrier";
936 report_fatal_error(DiagMsg.c_str(), false);
937 }
938
939 if (Call->isSpirvOp())
940 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
941
942 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
943 unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);
944 unsigned MemSemantics = SPIRV::MemorySemantics::None;
945
946 if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
947 MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
948
949 if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)
950 MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;
951
952 if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)
953 MemSemantics |= SPIRV::MemorySemantics::ImageMemory;
954
955 if (Opcode == SPIRV::OpMemoryBarrier)
956 MemSemantics = getSPIRVMemSemantics(static_cast<std::memory_order>(
957 getIConstVal(Call->Arguments[1], MRI))) |
958 MemSemantics;
959 else if (Opcode == SPIRV::OpControlBarrierArriveINTEL)
960 MemSemantics |= SPIRV::MemorySemantics::Release;
961 else if (Opcode == SPIRV::OpControlBarrierWaitINTEL)
962 MemSemantics |= SPIRV::MemorySemantics::Acquire;
963 else
964 MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;
965
966 Register MemSemanticsReg =
967 MemFlags == MemSemantics
968 ? Call->Arguments[0]
969 : buildConstantIntReg32(MemSemantics, MIRBuilder, GR);
970 Register ScopeReg;
971 SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
972 SPIRV::Scope::Scope MemScope = Scope;
973 if (Call->Arguments.size() >= 2) {
974 assert(
975 ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||
976 (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&
977 "Extra args for explicitly scoped barrier");
978 Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]
979 : Call->Arguments[1];
980 SPIRV::CLMemoryScope CLScope =
981 static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
982 MemScope = getSPIRVScope(CLScope);
983 if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
984 (Opcode == SPIRV::OpMemoryBarrier))
985 Scope = MemScope;
986 if (CLScope == static_cast<unsigned>(Scope))
987 ScopeReg = Call->Arguments[1];
988 }
989
990 if (!ScopeReg.isValid())
991 ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
992
993 auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);
994 if (Opcode != SPIRV::OpMemoryBarrier)
995 MIB.addUse(buildConstantIntReg32(MemScope, MIRBuilder, GR));
996 MIB.addUse(MemSemanticsReg);
997 return true;
998}
999
1000/// Helper function for building extended bit operations.
1002 unsigned Opcode,
1003 MachineIRBuilder &MIRBuilder,
1004 SPIRVGlobalRegistry *GR) {
1005 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1006 const auto *ST =
1007 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
1008 if ((Opcode == SPIRV::OpBitFieldInsert ||
1009 Opcode == SPIRV::OpBitFieldSExtract ||
1010 Opcode == SPIRV::OpBitFieldUExtract || Opcode == SPIRV::OpBitReverse) &&
1011 !ST->canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions)) {
1012 std::string DiagMsg = std::string(Builtin->name()) +
1013 ": the builtin requires the following SPIR-V "
1014 "extension: SPV_KHR_bit_instructions";
1015 report_fatal_error(DiagMsg.c_str(), false);
1016 }
1017
1018 // Generate SPIRV instruction accordingly.
1019 if (Call->isSpirvOp())
1020 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1021 GR->getSPIRVTypeID(Call->ReturnType));
1022
1023 auto MIB = MIRBuilder.buildInstr(Opcode)
1024 .addDef(Call->ReturnRegister)
1025 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1026 for (unsigned i = 0; i < Call->Arguments.size(); ++i)
1027 MIB.addUse(Call->Arguments[i]);
1028
1029 return true;
1030}
1031
1032/// Helper function for building Intel's bindless image instructions.
1034 unsigned Opcode,
1035 MachineIRBuilder &MIRBuilder,
1036 SPIRVGlobalRegistry *GR) {
1037 // Generate SPIRV instruction accordingly.
1038 if (Call->isSpirvOp())
1039 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1040 GR->getSPIRVTypeID(Call->ReturnType));
1041
1042 MIRBuilder.buildInstr(Opcode)
1043 .addDef(Call->ReturnRegister)
1044 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1045 .addUse(Call->Arguments[0]);
1046
1047 return true;
1048}
1049
1050/// Helper function for building Intel's OpBitwiseFunctionINTEL instruction.
1052 const SPIRV::IncomingCall *Call, unsigned Opcode,
1053 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
1054 // Generate SPIRV instruction accordingly.
1055 if (Call->isSpirvOp())
1056 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1057 GR->getSPIRVTypeID(Call->ReturnType));
1058
1059 auto MIB = MIRBuilder.buildInstr(Opcode)
1060 .addDef(Call->ReturnRegister)
1061 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1062 for (unsigned i = 0; i < Call->Arguments.size(); ++i)
1063 MIB.addUse(Call->Arguments[i]);
1064
1065 return true;
1066}
1067
1069 unsigned Opcode,
1070 MachineIRBuilder &MIRBuilder,
1071 SPIRVGlobalRegistry *GR) {
1072 if (Call->isSpirvOp())
1073 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1074 GR->getSPIRVTypeID(Call->ReturnType));
1075
1076 auto MIB = MIRBuilder.buildInstr(Opcode)
1077 .addDef(Call->ReturnRegister)
1078 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1079 for (unsigned i = 0; i < Call->Arguments.size(); ++i)
1080 MIB.addUse(Call->Arguments[i]);
1081
1082 return true;
1083}
1084
1085/// Helper function for building Intel's 2d block io instructions.
1087 unsigned Opcode,
1088 MachineIRBuilder &MIRBuilder,
1089 SPIRVGlobalRegistry *GR) {
1090 // Generate SPIRV instruction accordingly.
1091 if (Call->isSpirvOp())
1092 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
1093
1094 auto MIB = MIRBuilder.buildInstr(Opcode)
1095 .addDef(Call->ReturnRegister)
1096 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1097 for (unsigned i = 0; i < Call->Arguments.size(); ++i)
1098 MIB.addUse(Call->Arguments[i]);
1099
1100 return true;
1101}
1102
1103static bool buildPipeInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
1104 unsigned Scope, MachineIRBuilder &MIRBuilder,
1105 SPIRVGlobalRegistry *GR) {
1106 switch (Opcode) {
1107 case SPIRV::OpCommitReadPipe:
1108 case SPIRV::OpCommitWritePipe:
1109 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
1110 case SPIRV::OpGroupCommitReadPipe:
1111 case SPIRV::OpGroupCommitWritePipe:
1112 case SPIRV::OpGroupReserveReadPipePackets:
1113 case SPIRV::OpGroupReserveWritePipePackets: {
1114 Register ScopeConstReg =
1115 MIRBuilder.buildConstant(LLT::scalar(32), Scope).getReg(0);
1116 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1117 MRI->setRegClass(ScopeConstReg, &SPIRV::iIDRegClass);
1119 MIB = MIRBuilder.buildInstr(Opcode);
1120 // Add Return register and type.
1121 if (Opcode == SPIRV::OpGroupReserveReadPipePackets ||
1122 Opcode == SPIRV::OpGroupReserveWritePipePackets)
1123 MIB.addDef(Call->ReturnRegister)
1124 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1125
1126 MIB.addUse(ScopeConstReg);
1127 for (unsigned int i = 0; i < Call->Arguments.size(); ++i)
1128 MIB.addUse(Call->Arguments[i]);
1129
1130 return true;
1131 }
1132 default:
1133 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1134 GR->getSPIRVTypeID(Call->ReturnType));
1135 }
1136}
1137
1138static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
1139 switch (dim) {
1140 case SPIRV::Dim::DIM_1D:
1141 case SPIRV::Dim::DIM_Buffer:
1142 return 1;
1143 case SPIRV::Dim::DIM_2D:
1144 case SPIRV::Dim::DIM_Cube:
1145 case SPIRV::Dim::DIM_Rect:
1146 return 2;
1147 case SPIRV::Dim::DIM_3D:
1148 return 3;
1149 default:
1150 report_fatal_error("Cannot get num components for given Dim");
1151 }
1152}
1153
1154/// Helper function for obtaining the number of size components.
1155static unsigned getNumSizeComponents(SPIRVTypeInst imgType) {
1156 assert(imgType->getOpcode() == SPIRV::OpTypeImage);
1157 auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());
1158 unsigned numComps = getNumComponentsForDim(dim);
1159 bool arrayed = imgType->getOperand(4).getImm() == 1;
1160 return arrayed ? numComps + 1 : numComps;
1161}
1162
1163static bool builtinMayNeedPromotionToVec(uint32_t BuiltinNumber) {
1164 switch (BuiltinNumber) {
1165 case SPIRV::OpenCLExtInst::s_min:
1166 case SPIRV::OpenCLExtInst::u_min:
1167 case SPIRV::OpenCLExtInst::s_max:
1168 case SPIRV::OpenCLExtInst::u_max:
1169 case SPIRV::OpenCLExtInst::fmax:
1170 case SPIRV::OpenCLExtInst::fmin:
1171 case SPIRV::OpenCLExtInst::fmax_common:
1172 case SPIRV::OpenCLExtInst::fmin_common:
1173 case SPIRV::OpenCLExtInst::s_clamp:
1174 case SPIRV::OpenCLExtInst::fclamp:
1175 case SPIRV::OpenCLExtInst::u_clamp:
1176 case SPIRV::OpenCLExtInst::mix:
1177 case SPIRV::OpenCLExtInst::step:
1178 case SPIRV::OpenCLExtInst::smoothstep:
1179 case SPIRV::OpenCLExtInst::ldexp:
1180 case SPIRV::OpenCLExtInst::pown:
1181 case SPIRV::OpenCLExtInst::rootn:
1182 return true;
1183 default:
1184 break;
1185 }
1186 return false;
1187}
1188
1189//===----------------------------------------------------------------------===//
1190// Implementation functions for each builtin group
1191//===----------------------------------------------------------------------===//
1192
1195 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
1196
1197 Register ReturnTypeId = GR->getSPIRVTypeID(Call->ReturnType);
1198 unsigned ResultElementCount =
1199 GR->getScalarOrVectorComponentCount(ReturnTypeId);
1200 bool MayNeedPromotionToVec =
1201 builtinMayNeedPromotionToVec(BuiltinNumber) && ResultElementCount > 1;
1202
1203 if (!MayNeedPromotionToVec)
1204 return {Call->Arguments.begin(), Call->Arguments.end()};
1205
1207 for (Register Argument : Call->Arguments) {
1208 Register VecArg = Argument;
1209 SPIRVTypeInst ArgumentType = GR->getSPIRVTypeForVReg(Argument);
1210 if (GR->getScalarOrVectorComponentCount(ArgumentType) == 1 &&
1211 ArgumentType != Call->ReturnType) {
1213 ArgumentType, ResultElementCount, MIRBuilder, /*EmitIR=*/true);
1214 VecArg = createVirtualRegister(VecType, GR, MIRBuilder);
1215 Register VecTypeId = GR->getSPIRVTypeID(VecType);
1216 auto VecSplat = MIRBuilder.buildInstr(SPIRV::OpCompositeConstruct)
1217 .addDef(VecArg)
1218 .addUse(VecTypeId);
1219 for (unsigned I = 0; I != ResultElementCount; ++I)
1220 VecSplat.addUse(Argument);
1221 }
1222 Arguments.push_back(VecArg);
1223 }
1224 return Arguments;
1225}
1226
1228 MachineIRBuilder &MIRBuilder,
1229 SPIRVGlobalRegistry *GR, const CallBase &CB) {
1230 // Lookup the extended instruction number in the TableGen records.
1231 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1233 SPIRV::lookupExtendedBuiltin(Builtin->name(), Builtin->Set)->Number;
1234 // fmin_common and fmax_common are now deprecated, and we should use fmin and
1235 // fmax with NotInf and NotNaN flags instead. Keep original number to add
1236 // later the NoNans and NoInfs flags.
1237 uint32_t OrigNumber = Number;
1238 const SPIRVSubtarget &ST =
1239 cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
1240 if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2) &&
1241 (Number == SPIRV::OpenCLExtInst::fmin_common ||
1242 Number == SPIRV::OpenCLExtInst::fmax_common)) {
1243 Number = (Number == SPIRV::OpenCLExtInst::fmin_common)
1244 ? SPIRV::OpenCLExtInst::fmin
1245 : SPIRV::OpenCLExtInst::fmax;
1246 }
1247
1248 // ExtInst prefetch cannot take an untyped pointer, so emit
1249 // OpUntypedPrefetchKHR with Num Bytes = num elements * element byte size.
1250 if (Number == SPIRV::OpenCLExtInst::prefetch && Call->Arguments.size() >= 2) {
1251 Register PtrReg = Call->Arguments[0];
1252 SPIRVTypeInst PtrTy = GR->getSPIRVTypeForVReg(PtrReg);
1253 if (PtrTy && PtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR) {
1254 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1255 Register NumElems = Call->Arguments[1];
1256 SPIRVTypeInst SizeTy = GR->getSPIRVTypeForVReg(NumElems);
1257 assert(SizeTy && "Expected a type for the number of elements");
1258 unsigned ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(0));
1259 Register NumBytes = NumElems;
1260 // A byte sized element already makes the element count a byte count. A
1261 // size of 0 means the element type could not be deduced, which the typed
1262 // lowering resolves to i8, so treat it as a single byte here as well.
1263 if (ElemBytes > 1) {
1264 Register ElemBytesReg = GR->buildConstantInt(ElemBytes, MIRBuilder,
1265 SizeTy, /*EmitIR=*/true);
1266 Register Mul =
1267 MRI->createGenericVirtualRegister(MRI->getType(NumElems));
1268 MRI->setRegClass(Mul, GR->getRegClass(SizeTy));
1269 GR->assignSPIRVTypeToVReg(SizeTy, Mul, MIRBuilder.getMF());
1270 MIRBuilder.buildInstr(TargetOpcode::G_MUL)
1271 .addDef(Mul)
1272 .addUse(NumElems)
1273 .addUse(ElemBytesReg);
1274 updateRegType(Mul, /*Ty=*/nullptr, SizeTy, GR, MIRBuilder, *MRI);
1275 NumBytes = Mul;
1276 }
1277 MIRBuilder.buildInstr(SPIRV::OpUntypedPrefetchKHR)
1278 .addUse(PtrReg)
1279 .addUse(NumBytes);
1280 return true;
1281 }
1282 }
1283
1284 Register ReturnTypeId = GR->getSPIRVTypeID(Call->ReturnType);
1286 getBuiltinCallArguments(Call, Number, MIRBuilder, GR);
1287
1289 if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_fma) &&
1290 Number == SPIRV::OpenCLExtInst::fma) {
1291 // Use the SPIR-V fma instruction instead of the OpenCL extended
1292 // instruction if the extension is available.
1293 MIB = MIRBuilder.buildInstr(SPIRV::OpFmaKHR)
1294 .addDef(Call->ReturnRegister)
1295 .addUse(ReturnTypeId);
1296 } else {
1297 // Build extended instruction.
1298 MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst)
1299 .addDef(Call->ReturnRegister)
1300 .addUse(ReturnTypeId)
1301 .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1302 .addImm(Number);
1303 }
1304
1306 MIB.addUse(Argument);
1307
1308 MIB.getInstr()->copyIRFlags(CB);
1309 if (OrigNumber == SPIRV::OpenCLExtInst::fmin_common ||
1310 OrigNumber == SPIRV::OpenCLExtInst::fmax_common) {
1311 // Add NoNans and NoInfs flags to fmin/fmax instruction.
1314 }
1315
1316 // Derive fast-math flags from nofpclass attributes on the called function.
1317 // FPFastMathMode decoration is valid on ExtInst in Kernel environments
1318 // (SPIR-V core) or with SPV_KHR_float_controls2 for any environment.
1319 if (ST.isKernel() ||
1320 ST.canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
1321 if (const Function *F = CB.getCalledFunction()) {
1322 bool AddNoNan = CB.getRetNoFPClass() & fcNan;
1323 bool AddNoInf = CB.getRetNoFPClass() & fcInf;
1324 FunctionType *FTy = F->getFunctionType();
1325 for (unsigned I = 0, E = FTy->getNumParams();
1326 I != E && (AddNoNan || AddNoInf); ++I) {
1327 if (!FTy->getParamType(I)->isFloatingPointTy())
1328 continue;
1329 FPClassTest ArgTest = CB.getParamNoFPClass(I);
1330 AddNoNan = AddNoNan && ArgTest & fcNan;
1331 AddNoInf = AddNoInf && ArgTest & fcInf;
1332 }
1333 if (AddNoNan)
1335 if (AddNoInf)
1337 }
1338 }
1339
1340 return true;
1341}
1342
1344 MachineIRBuilder &MIRBuilder,
1345 SPIRVGlobalRegistry *GR) {
1346 // Lookup the instruction opcode in the TableGen records.
1347 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1348 unsigned Opcode =
1349 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1350
1351 Register CompareRegister;
1352 SPIRVTypeInst RelationType = nullptr;
1353 std::tie(CompareRegister, RelationType) =
1354 buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1355
1356 // OpAny/OpAll require a boolean vector input, but OpenCL any()/all()
1357 // builtins receive integer vectors. Convert via OpINotEqual against zero.
1358 SmallVector<Register> Arguments(Call->Arguments.begin(),
1359 Call->Arguments.end());
1360 if ((Opcode == SPIRV::OpAny || Opcode == SPIRV::OpAll) &&
1361 !GR->isScalarOrVectorOfType(Arguments[0], SPIRV::OpTypeBool)) {
1363 unsigned NumElts = GR->getScalarOrVectorComponentCount(ArgType);
1365 GR->getOrCreateSPIRVBoolType(MIRBuilder, /*EmitIR=*/true), NumElts,
1366 MIRBuilder, /*EmitIR=*/true);
1367 Register ZeroReg =
1368 GR->getOrCreateConsIntVector(uint64_t(0), MIRBuilder, ArgType,
1369 /*EmitIR=*/true);
1370 Register BoolVecReg = createVirtualRegister(BoolVecTy, GR, MIRBuilder);
1371 MIRBuilder.buildInstr(SPIRV::OpINotEqual)
1372 .addDef(BoolVecReg)
1373 .addUse(GR->getSPIRVTypeID(BoolVecTy))
1374 .addUse(Arguments[0])
1375 .addUse(ZeroReg);
1376 Arguments[0] = BoolVecReg;
1377 }
1378
1379 // Build relational instruction.
1380 auto MIB = MIRBuilder.buildInstr(Opcode)
1381 .addDef(CompareRegister)
1382 .addUse(GR->getSPIRVTypeID(RelationType));
1383
1384 for (auto Argument : Arguments)
1385 MIB.addUse(Argument);
1386
1387 // Build select instruction.
1388 return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,
1389 Call->ReturnType, GR);
1390}
1391
1393 MachineIRBuilder &MIRBuilder,
1394 SPIRVGlobalRegistry *GR) {
1395 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1396 const SPIRV::GroupBuiltin *GroupBuiltin =
1397 SPIRV::lookupGroupBuiltin(Builtin->name());
1398
1399 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1400 if (Call->isSpirvOp()) {
1401 if (GroupBuiltin->NoGroupOperation) {
1403 if (GroupBuiltin->Opcode ==
1404 SPIRV::OpSubgroupMatrixMultiplyAccumulateINTEL &&
1405 Call->Arguments.size() > 4)
1406 ImmArgs.push_back(getIConstVal(Call->Arguments[4], MRI));
1407 return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call,
1408 GR->getSPIRVTypeID(Call->ReturnType), ImmArgs);
1409 }
1410
1411 // Group Operation is a literal
1412 Register GroupOpReg = Call->Arguments[1];
1413 const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI);
1414 if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT)
1416 "Group Operation parameter must be an integer constant");
1417 uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue();
1418 Register ScopeReg = Call->Arguments[0];
1419 auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1420 .addDef(Call->ReturnRegister)
1421 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1422 .addUse(ScopeReg)
1423 .addImm(GrpOp);
1424 for (unsigned i = 2; i < Call->Arguments.size(); ++i)
1425 MIB.addUse(Call->Arguments[i]);
1426 return true;
1427 }
1428
1429 Register Arg0;
1430 if (GroupBuiltin->HasBoolArg) {
1431 SPIRVTypeInst BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder, true);
1432 Register BoolReg = Call->Arguments[0];
1433 SPIRVTypeInst BoolRegType = GR->getSPIRVTypeForVReg(BoolReg);
1434 if (!BoolRegType)
1435 report_fatal_error("Can't find a register's type definition");
1436 MachineInstr *ArgInstruction = getDefInstrMaybeConstant(BoolReg, MRI);
1437 if (ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT) {
1438 if (BoolRegType->getOpcode() != SPIRV::OpTypeBool)
1439 Arg0 = GR->buildConstantInt(getIConstVal(BoolReg, MRI) != 0, MIRBuilder,
1440 BoolType, true);
1441 } else {
1442 if (BoolRegType->getOpcode() == SPIRV::OpTypeInt) {
1444 MRI->setRegClass(Arg0, &SPIRV::iIDRegClass);
1445 GR->assignSPIRVTypeToVReg(BoolType, Arg0, MIRBuilder.getMF());
1446 MIRBuilder.buildICmp(
1447 CmpInst::ICMP_NE, Arg0, BoolReg,
1448 GR->buildConstantInt(0, MIRBuilder, BoolRegType, true));
1449 updateRegType(Arg0, nullptr, BoolType, GR, MIRBuilder,
1450 MIRBuilder.getMF().getRegInfo());
1451 } else if (BoolRegType->getOpcode() != SPIRV::OpTypeBool) {
1452 report_fatal_error("Expect a boolean argument");
1453 }
1454 // if BoolReg is a boolean register, we don't need to do anything
1455 }
1456 }
1457
1458 Register GroupResultRegister = Call->ReturnRegister;
1459 SPIRVTypeInst GroupResultType = Call->ReturnType;
1460
1461 // TODO: maybe we need to check whether the result type is already boolean
1462 // and in this case do not insert select instruction.
1463 const bool HasBoolReturnTy =
1464 GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||
1465 GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||
1466 GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;
1467
1468 if (HasBoolReturnTy)
1469 std::tie(GroupResultRegister, GroupResultType) =
1470 buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1471
1472 auto Scope = Builtin->name().starts_with("sub_group")
1473 ? SPIRV::Scope::Subgroup
1474 : SPIRV::Scope::Workgroup;
1475 Register ScopeRegister = buildConstantIntReg32(Scope, MIRBuilder, GR);
1476
1477 Register VecReg;
1478 if (GroupBuiltin->Opcode == SPIRV::OpGroupBroadcast &&
1479 Call->Arguments.size() > 2) {
1480 // For OpGroupBroadcast "LocalId must be an integer datatype. It must be a
1481 // scalar, a vector with 2 components, or a vector with 3 components.",
1482 // meaning that we must create a vector from the function arguments if
1483 // it's a work_group_broadcast(val, local_id_x, local_id_y) or
1484 // work_group_broadcast(val, local_id_x, local_id_y, local_id_z) call.
1485 Register ElemReg = Call->Arguments[1];
1486 SPIRVTypeInst ElemType = GR->getSPIRVTypeForVReg(ElemReg);
1487 if (!ElemType || ElemType->getOpcode() != SPIRV::OpTypeInt)
1488 report_fatal_error("Expect an integer <LocalId> argument");
1489 unsigned VecLen = Call->Arguments.size() - 1;
1490 VecReg = MRI->createGenericVirtualRegister(
1491 LLT::fixed_vector(VecLen, MRI->getType(ElemReg)));
1492 MRI->setRegClass(VecReg, &SPIRV::viIDRegClass);
1493 SPIRVTypeInst VecType =
1494 GR->getOrCreateSPIRVVectorType(ElemType, VecLen, MIRBuilder, true);
1495 GR->assignSPIRVTypeToVReg(VecType, VecReg, MIRBuilder.getMF());
1496 auto MIB =
1497 MIRBuilder.buildInstr(TargetOpcode::G_BUILD_VECTOR).addDef(VecReg);
1498 for (unsigned i = 1; i < Call->Arguments.size(); i++) {
1499 MIB.addUse(Call->Arguments[i]);
1500 setRegClassIfNull(Call->Arguments[i], MRI, GR);
1501 }
1502 updateRegType(VecReg, nullptr, VecType, GR, MIRBuilder,
1503 MIRBuilder.getMF().getRegInfo());
1504 }
1505
1506 // Build work/sub group instruction.
1507 auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1508 .addDef(GroupResultRegister)
1509 .addUse(GR->getSPIRVTypeID(GroupResultType))
1510 .addUse(ScopeRegister);
1511
1512 if (!GroupBuiltin->NoGroupOperation)
1513 MIB.addImm(GroupBuiltin->GroupOperation);
1514 if (Call->Arguments.size() > 0) {
1515 MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);
1516 setRegClassIfNull(Call->Arguments[0], MRI, GR);
1517 if (VecReg.isValid())
1518 MIB.addUse(VecReg);
1519 else
1520 for (unsigned i = 1; i < Call->Arguments.size(); i++)
1521 MIB.addUse(Call->Arguments[i]);
1522 }
1523
1524 // Build select instruction.
1525 if (HasBoolReturnTy)
1526 buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,
1527 Call->ReturnType, GR);
1528 return true;
1529}
1530
1532 MachineIRBuilder &MIRBuilder,
1533 SPIRVGlobalRegistry *GR) {
1534 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1535 MachineFunction &MF = MIRBuilder.getMF();
1536 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1537 const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups =
1538 SPIRV::lookupIntelSubgroupsBuiltin(Builtin->name());
1539
1540 if (IntelSubgroups->IsMedia &&
1541 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_media_block_io)) {
1542 std::string DiagMsg = std::string(Builtin->name()) +
1543 ": the builtin requires the following SPIR-V "
1544 "extension: SPV_INTEL_media_block_io";
1545 report_fatal_error(DiagMsg.c_str(), false);
1546 } else if (!IntelSubgroups->IsMedia &&
1547 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1548 std::string DiagMsg = std::string(Builtin->name()) +
1549 ": the builtin requires the following SPIR-V "
1550 "extension: SPV_INTEL_subgroups";
1551 report_fatal_error(DiagMsg.c_str(), false);
1552 }
1553
1554 uint32_t OpCode = IntelSubgroups->Opcode;
1555 if (Call->isSpirvOp()) {
1556 bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL &&
1557 OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL &&
1558 OpCode != SPIRV::OpSubgroupImageMediaBlockWriteINTEL;
1559 return buildOpFromWrapper(MIRBuilder, OpCode, Call,
1560 IsSet ? GR->getSPIRVTypeID(Call->ReturnType)
1561 : Register(0));
1562 }
1563
1564 if (IntelSubgroups->IsBlock) {
1565 // Minimal number or arguments set in TableGen records is 1
1566 if (SPIRVTypeInst Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) {
1567 if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) {
1568 // TODO: add required validation from the specification:
1569 // "'Image' must be an object whose type is OpTypeImage with a 'Sampled'
1570 // operand of 0 or 2. If the 'Sampled' operand is 2, then some
1571 // dimensions require a capability."
1572 switch (OpCode) {
1573 case SPIRV::OpSubgroupBlockReadINTEL:
1574 OpCode = SPIRV::OpSubgroupImageBlockReadINTEL;
1575 break;
1576 case SPIRV::OpSubgroupBlockWriteINTEL:
1577 OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL;
1578 break;
1579 }
1580 }
1581 }
1582 }
1583
1584 // TODO: opaque pointers types should be eventually resolved in such a way
1585 // that validation of block read is enabled with respect to the following
1586 // specification requirement:
1587 // "'Result Type' may be a scalar or vector type, and its component type must
1588 // be equal to the type pointed to by 'Ptr'."
1589 // For example, function parameter type should not be default i8 pointer, but
1590 // depend on the result type of the instruction where it is used as a pointer
1591 // argument of OpSubgroupBlockReadINTEL
1592
1593 // Build Intel subgroups instruction
1595 IntelSubgroups->IsWrite
1596 ? MIRBuilder.buildInstr(OpCode)
1597 : MIRBuilder.buildInstr(OpCode)
1598 .addDef(Call->ReturnRegister)
1599 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1600 for (size_t i = 0; i < Call->Arguments.size(); ++i)
1601 MIB.addUse(Call->Arguments[i]);
1602 return true;
1603}
1604
1606 MachineIRBuilder &MIRBuilder,
1607 SPIRVGlobalRegistry *GR) {
1608 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1609 MachineFunction &MF = MIRBuilder.getMF();
1610 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1611 if (!ST->canUseExtension(
1612 SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {
1613 std::string DiagMsg = std::string(Builtin->name()) +
1614 ": the builtin requires the following SPIR-V "
1615 "extension: SPV_KHR_uniform_group_instructions";
1616 report_fatal_error(DiagMsg.c_str(), false);
1617 }
1618 const SPIRV::GroupUniformBuiltin *GroupUniform =
1619 SPIRV::lookupGroupUniformBuiltin(Builtin->name());
1620 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1621
1622 Register GroupResultReg = Call->ReturnRegister;
1623 Register ScopeReg = Call->Arguments[0];
1624 Register ValueReg = Call->Arguments[2];
1625
1626 // Group Operation
1627 Register ConstGroupOpReg = Call->Arguments[1];
1628 const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI);
1629 if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT)
1631 "expect a constant group operation for a uniform group instruction",
1632 false);
1633 const MachineOperand &ConstOperand = Const->getOperand(1);
1634 if (!ConstOperand.isCImm())
1635 report_fatal_error("uniform group instructions: group operation must be an "
1636 "integer constant",
1637 false);
1638
1639 auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode)
1640 .addDef(GroupResultReg)
1641 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1642 .addUse(ScopeReg);
1643 addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1644 MIB.addUse(ValueReg);
1645
1646 return true;
1647}
1648
1650 MachineIRBuilder &MIRBuilder,
1651 SPIRVGlobalRegistry *GR) {
1652 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1653 MachineFunction &MF = MIRBuilder.getMF();
1654 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1655 if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) {
1656 std::string DiagMsg = std::string(Builtin->name()) +
1657 ": the builtin requires the following SPIR-V "
1658 "extension: SPV_KHR_shader_clock";
1659 report_fatal_error(DiagMsg.c_str(), false);
1660 }
1661
1662 Register ResultReg = Call->ReturnRegister;
1663
1664 if (Builtin->name() == "__spirv_ReadClockKHR") {
1665 MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
1666 .addDef(ResultReg)
1667 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1668 .addUse(Call->Arguments[0]);
1669 } else {
1670 // Deduce the `Scope` operand from the builtin function name.
1671 SPIRV::Scope::Scope ScopeArg =
1673 .EndsWith("device", SPIRV::Scope::Scope::Device)
1674 .EndsWith("work_group", SPIRV::Scope::Scope::Workgroup)
1675 .EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup);
1676 Register ScopeReg = buildConstantIntReg32(ScopeArg, MIRBuilder, GR);
1677
1678 MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
1679 .addDef(ResultReg)
1680 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1681 .addUse(ScopeReg);
1682 }
1683
1684 return true;
1685}
1686
1687// These queries ask for a single size_t result for a given dimension index,
1688// e.g. size_t get_global_id(uint dimindex). In SPIR-V, the builtins
1689// corresponding to these values are all vec3 types, so we need to extract the
1690// correct index or return DefaultValue (0 or 1 depending on the query). We also
1691// handle extending or truncating in case size_t does not match the expected
1692// result type's bitwidth.
1693//
1694// For a constant index >= 3 we generate:
1695// %res = OpConstant %SizeT DefaultValue
1696//
1697// For other indices we generate:
1698// %g = OpVariable %ptr_V3_SizeT Input
1699// OpDecorate %g BuiltIn XXX
1700// OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"
1701// OpDecorate %g Constant
1702// %loadedVec = OpLoad %V3_SizeT %g
1703//
1704// Then, if the index is constant < 3, we generate:
1705// %res = OpCompositeExtract %SizeT %loadedVec idx
1706// If the index is dynamic, we generate:
1707// %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx
1708// %cmp = OpULessThan %bool %idx %const_3
1709// %res = OpSelect %SizeT %cmp %tmp %const_<DefaultValue>
1710//
1711// If the bitwidth of %res does not match the expected return type, we add an
1712// extend or truncate.
1714 MachineIRBuilder &MIRBuilder,
1716 SPIRV::BuiltIn::BuiltIn BuiltinValue,
1717 uint64_t DefaultValue) {
1718 Register IndexRegister = Call->Arguments[0];
1719 const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
1720 const unsigned PointerSize = GR->getPointerSize();
1721 const SPIRVTypeInst PointerSizeType =
1722 GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);
1723 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1724 auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);
1725
1726 // Set up the final register to do truncation or extension on at the end.
1727 Register ToTruncate = Call->ReturnRegister;
1728
1729 // If the index is constant, we can statically determine if it is in range.
1730 bool IsConstantIndex =
1731 IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;
1732
1733 // If it's out of range (max dimension is 3), we can just return the constant
1734 // default value (0 or 1 depending on which query function).
1735 if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {
1736 Register DefaultReg = Call->ReturnRegister;
1737 if (PointerSize != ResultWidth) {
1738 DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1739 MRI->setRegClass(DefaultReg, &SPIRV::iIDRegClass);
1740 GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg,
1741 MIRBuilder.getMF());
1742 ToTruncate = DefaultReg;
1743 }
1744 auto NewRegister =
1745 GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType, true);
1746 MIRBuilder.buildCopy(DefaultReg, NewRegister);
1747 } else { // If it could be in range, we need to load from the given builtin.
1748 auto Vec3Ty =
1749 GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder, true);
1750 Register LoadedVector =
1751 buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,
1752 LLT::fixed_vector(3, PointerSize));
1753 // Set up the vreg to extract the result to (possibly a new temporary one).
1754 Register Extracted = Call->ReturnRegister;
1755 if (!IsConstantIndex || PointerSize != ResultWidth) {
1756 Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1757 MRI->setRegClass(Extracted, &SPIRV::iIDRegClass);
1758 GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());
1759 }
1760 // Use Intrinsic::spv_extractelt so dynamic vs static extraction is
1761 // handled later: extr = spv_extractelt LoadedVector, IndexRegister.
1762 MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(
1763 Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false);
1764 ExtractInst.addUse(LoadedVector).addUse(IndexRegister);
1765
1766 // If the index is dynamic, need check if it's < 3, and then use a select.
1767 if (!IsConstantIndex) {
1768 updateRegType(Extracted, nullptr, PointerSizeType, GR, MIRBuilder, *MRI);
1769
1770 auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);
1771 auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder, true);
1772
1773 Register CompareRegister =
1775 MRI->setRegClass(CompareRegister, &SPIRV::iIDRegClass);
1776 GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());
1777
1778 // Use G_ICMP to check if idxVReg < 3.
1779 MIRBuilder.buildICmp(
1780 CmpInst::ICMP_ULT, CompareRegister, IndexRegister,
1781 GR->buildConstantInt(3, MIRBuilder, IndexType, true));
1782
1783 // Get constant for the default value (0 or 1 depending on which
1784 // function).
1785 Register DefaultRegister =
1786 GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType, true);
1787
1788 // Get a register for the selection result (possibly a new temporary one).
1789 Register SelectionResult = Call->ReturnRegister;
1790 if (PointerSize != ResultWidth) {
1791 SelectionResult =
1792 MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1793 MRI->setRegClass(SelectionResult, &SPIRV::iIDRegClass);
1794 GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,
1795 MIRBuilder.getMF());
1796 }
1797 // Create the final G_SELECT to return the extracted value or the default.
1798 MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,
1799 DefaultRegister);
1800 ToTruncate = SelectionResult;
1801 } else {
1802 ToTruncate = Extracted;
1803 }
1804 }
1805 // Alter the result's bitwidth if it does not match the SizeT value extracted.
1806 if (PointerSize != ResultWidth)
1807 MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);
1808 return true;
1809}
1810
1812 MachineIRBuilder &MIRBuilder,
1813 SPIRVGlobalRegistry *GR) {
1814 // Lookup the builtin variable record.
1815 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1816 SPIRV::BuiltIn::BuiltIn Value =
1817 SPIRV::lookupGetBuiltin(Builtin->name(), Builtin->Set)->Value;
1818
1819 if (Value == SPIRV::BuiltIn::GlobalInvocationId)
1820 return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);
1821
1822 // Build a load instruction for the builtin variable.
1823 unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);
1824 LLT LLType;
1825 if (isVectorType(Call->ReturnType))
1826 LLType = LLT::fixed_vector(
1828 else
1829 LLType = LLT::scalar(BitWidth);
1830
1831 return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,
1832 LLType, Call->ReturnRegister);
1833}
1834
1836 MachineIRBuilder &MIRBuilder,
1837 SPIRVGlobalRegistry *GR) {
1838 // Lookup the instruction opcode in the TableGen records.
1839 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1840 unsigned Opcode =
1841 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1842
1843 switch (Opcode) {
1844 case SPIRV::OpStore:
1845 return buildAtomicInitInst(Call, MIRBuilder);
1846 case SPIRV::OpAtomicLoad:
1847 return buildAtomicLoadInst(Call, MIRBuilder, GR);
1848 case SPIRV::OpAtomicStore:
1849 return buildAtomicStoreInst(Call, MIRBuilder, GR);
1850 case SPIRV::OpAtomicCompareExchange:
1851 case SPIRV::OpAtomicCompareExchangeWeak:
1852 return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder,
1853 GR);
1854 case SPIRV::OpAtomicIAdd:
1855 case SPIRV::OpAtomicISub:
1856 case SPIRV::OpAtomicOr:
1857 case SPIRV::OpAtomicXor:
1858 case SPIRV::OpAtomicAnd:
1859 case SPIRV::OpAtomicExchange:
1860 case SPIRV::OpAtomicSMax:
1861 case SPIRV::OpAtomicSMin:
1862 case SPIRV::OpAtomicUMax:
1863 case SPIRV::OpAtomicUMin:
1864 return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
1865 case SPIRV::OpMemoryBarrier:
1866 return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
1867 case SPIRV::OpAtomicFlagTestAndSet:
1868 case SPIRV::OpAtomicFlagClear:
1869 return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);
1870 default:
1871 if (Call->isSpirvOp())
1872 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1873 GR->getSPIRVTypeID(Call->ReturnType));
1874 return false;
1875 }
1876}
1877
1879 MachineIRBuilder &MIRBuilder,
1880 SPIRVGlobalRegistry *GR) {
1881 // Lookup the instruction opcode in the TableGen records.
1882 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1883 unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->name())->Opcode;
1884
1885 switch (Opcode) {
1886 case SPIRV::OpAtomicFAddEXT:
1887 case SPIRV::OpAtomicFMinEXT:
1888 case SPIRV::OpAtomicFMaxEXT:
1889 return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR);
1890 default:
1891 return false;
1892 }
1893}
1894
1896 MachineIRBuilder &MIRBuilder,
1897 SPIRVGlobalRegistry *GR) {
1898 // Lookup the instruction opcode in the TableGen records.
1899 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1900 unsigned Opcode =
1901 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1902
1903 return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
1904}
1905
1907 MachineIRBuilder &MIRBuilder,
1908 SPIRVGlobalRegistry *GR) {
1909 // Lookup the instruction opcode in the TableGen records.
1910 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1911 unsigned Opcode =
1912 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1913
1914 if (Opcode == SPIRV::OpGenericCastToPtrExplicit) {
1915 SPIRV::StorageClass::StorageClass ResSC =
1916 GR->getPointerStorageClass(Call->ReturnRegister);
1917 if (!isGenericCastablePtr(ResSC))
1918 return false;
1919
1920 MIRBuilder.buildInstr(Opcode)
1921 .addDef(Call->ReturnRegister)
1922 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1923 .addUse(Call->Arguments[0])
1924 .addImm(ResSC);
1925 } else {
1926 MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)
1927 .addDef(Call->ReturnRegister)
1928 .addUse(Call->Arguments[0]);
1929 }
1930 return true;
1931}
1932
1933static bool generateDotOrFMulInst(StringRef DemangledCall,
1935 MachineIRBuilder &MIRBuilder,
1936 SPIRVGlobalRegistry *GR) {
1937 if (Call->isSpirvOp())
1938 return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call,
1939 GR->getSPIRVTypeID(Call->ReturnType));
1940
1941 // Use OpDot only in case of vector args and OpFMul in case of scalar args.
1942 bool IsVec = isVectorType(GR->getSPIRVTypeForVReg(Call->Arguments[0]));
1943 uint32_t OC = IsVec ? SPIRV::OpDot : SPIRV::OpFMulS;
1944 bool IsSwapReq = false;
1945
1946 const auto *ST =
1947 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
1948 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
1949 if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_integer_dot_product) &&
1950 !ST->isAtLeastSPIRVVer(VersionTuple(1, 6)))
1951 report_fatal_error(Twine(Call->Builtin->name()) +
1952 ": the builtin requires the following SPIR-V "
1953 "extension: SPV_KHR_integer_dot_product",
1954 false);
1955 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1956 const SPIRV::IntegerDotProductBuiltin *IntDot =
1957 SPIRV::lookupIntegerDotProductBuiltin(Builtin->name());
1958 if (IntDot) {
1959 OC = IntDot->Opcode;
1960 IsSwapReq = IntDot->IsSwapReq;
1961 } else if (IsVec) {
1962 // Handling "dot" and "dot_acc_sat" builtins which use vectors of
1963 // integers.
1964 LLVMContext &Ctx = MIRBuilder.getContext();
1966 SPIRV::parseBuiltinTypeStr(TypeStrs, DemangledCall, Ctx);
1967 bool IsFirstSigned = TypeStrs[0].trim()[0] != 'u';
1968 bool IsSecondSigned = TypeStrs[1].trim()[0] != 'u';
1969
1970 if (Call->BuiltinName == "dot") {
1971 if (IsFirstSigned && IsSecondSigned)
1972 OC = SPIRV::OpSDot;
1973 else if (!IsFirstSigned && !IsSecondSigned)
1974 OC = SPIRV::OpUDot;
1975 else {
1976 OC = SPIRV::OpSUDot;
1977 if (!IsFirstSigned)
1978 IsSwapReq = true;
1979 }
1980 } else if (Call->BuiltinName == "dot_acc_sat") {
1981 if (IsFirstSigned && IsSecondSigned)
1982 OC = SPIRV::OpSDotAccSat;
1983 else if (!IsFirstSigned && !IsSecondSigned)
1984 OC = SPIRV::OpUDotAccSat;
1985 else {
1986 OC = SPIRV::OpSUDotAccSat;
1987 if (!IsFirstSigned)
1988 IsSwapReq = true;
1989 }
1990 }
1991 }
1992 }
1993
1994 MachineInstrBuilder MIB = MIRBuilder.buildInstr(OC)
1995 .addDef(Call->ReturnRegister)
1996 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1997
1998 if (IsSwapReq) {
1999 MIB.addUse(Call->Arguments[1]);
2000 MIB.addUse(Call->Arguments[0]);
2001 // needed for dot_acc_sat* builtins
2002 for (size_t i = 2; i < Call->Arguments.size(); ++i)
2003 MIB.addUse(Call->Arguments[i]);
2004 } else {
2005 for (size_t i = 0; i < Call->Arguments.size(); ++i)
2006 MIB.addUse(Call->Arguments[i]);
2007 }
2008
2009 // Add Packed Vector Format for Integer dot product builtins if arguments are
2010 // scalar
2011 if (!IsVec && OC != SPIRV::OpFMulS)
2012 MIB.addImm(SPIRV::PackedVectorFormat4x8Bit);
2013
2014 return true;
2015}
2016
2018 MachineIRBuilder &MIRBuilder,
2019 SPIRVGlobalRegistry *GR) {
2020 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2021 SPIRV::BuiltIn::BuiltIn Value =
2022 SPIRV::lookupGetBuiltin(Builtin->name(), Builtin->Set)->Value;
2023
2024 // For now, we only support a single Wave intrinsic with a single return type.
2025 assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt);
2026 LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType));
2027
2029 MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister,
2030 /* isConst= */ false, /* LinkageType= */ std::nullopt);
2031}
2032
2033// Build a SPIR-V instruction with struct return via sret pointer:
2034// Res = Opcode RetType Op1 Op2
2035// OpStore SRetReg Res
2036static void buildSRetInst(unsigned Opcode, Register SRetReg, Register Op1Reg,
2037 Register Op2Reg, SPIRVTypeInst RetType,
2038 MachineIRBuilder &MIRBuilder,
2039 SPIRVGlobalRegistry *GR) {
2040 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2041 Register ResReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2042 if (const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Op1Reg)) {
2043 MRI->setRegClass(ResReg, DstRC);
2044 MRI->setType(ResReg, MRI->getType(Op1Reg));
2045 }
2046 GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
2047 MIRBuilder.buildInstr(Opcode)
2048 .addDef(ResReg)
2049 .addUse(GR->getSPIRVTypeID(RetType))
2050 .addUse(Op1Reg)
2051 .addUse(Op2Reg);
2052 MIRBuilder.buildInstr(SPIRV::OpStore).addUse(SRetReg).addUse(ResReg);
2053}
2054
2055// Find the pointee type of an sret pointer argument. A typed pointer gives us
2056// the type directly. An untyped one does not, so fall back to the element type
2057// we deduced for the matching IR argument, or null if there is nothing to fall
2058// back to.
2060 const Value *SRetArg,
2061 MachineIRBuilder &MIRBuilder,
2062 SPIRVGlobalRegistry *GR) {
2063 SPIRVTypeInst RetType = GR->getPointeeType(GR->getSPIRVTypeForVReg(SRetReg));
2064 if (!RetType)
2065 if (Type *ElemTy = GR->findDeducedElementType(SRetArg))
2066 RetType = GR->getOrCreateSPIRVType(
2067 ElemTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
2068 return RetType;
2069}
2070
2071// We expect a builtin
2072// Name(ptr sret([RetType]) %result, Type %operand1, Type %operand1)
2073// where %result is a pointer to where the result of the builtin execution
2074// is to be stored, and generate the following instructions:
2075// Res = Opcode RetType Operand1 Operand1
2076// OpStore RetVariable Res
2078 MachineIRBuilder &MIRBuilder,
2080 const CallBase &CB) {
2081 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2082 unsigned Opcode =
2083 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2084
2085 Register SRetReg = Call->Arguments[0];
2086 SPIRVTypeInst RetType =
2087 deduceSRetPointeeType(SRetReg, CB.getArgOperand(0), MIRBuilder, GR);
2088 if (!RetType)
2089 report_fatal_error("The first parameter must be a pointer");
2090 if (RetType->getOpcode() != SPIRV::OpTypeStruct)
2091 report_fatal_error("Expected struct type result for the arithmetic with "
2092 "overflow builtins");
2093
2094 SPIRVTypeInst OpType1 = GR->getSPIRVTypeForVReg(Call->Arguments[1]);
2095 SPIRVTypeInst OpType2 = GR->getSPIRVTypeForVReg(Call->Arguments[2]);
2096 if (!OpType1 || !OpType2 || OpType1 != OpType2)
2097 report_fatal_error("Operands must have the same type");
2098 if (isVectorType(OpType1))
2099 switch (Opcode) {
2100 case SPIRV::OpIAddCarryS:
2101 Opcode = SPIRV::OpIAddCarryV;
2102 break;
2103 case SPIRV::OpISubBorrowS:
2104 Opcode = SPIRV::OpISubBorrowV;
2105 break;
2106 }
2107
2108 buildSRetInst(Opcode, SRetReg, Call->Arguments[1], Call->Arguments[2],
2109 RetType, MIRBuilder, GR);
2110 return true;
2111}
2112
2113// We expect a builtin in one of two forms:
2114//
2115// (1) sret convention (3 arguments):
2116// void Name(ptr sret([RetType]) %result, Type %operand1, Type %operand2)
2117// => Res = Opcode RetType Operand1 Operand2
2118// OpStore %result Res
2119//
2120// (2) direct return convention (2 arguments):
2121// RetType Name(Type %operand1, Type %operand2)
2122// => Res = Opcode RetType Operand1 Operand2
2123//
2124// RetType is a struct with two members of the same type as the operands.
2126 MachineIRBuilder &MIRBuilder,
2128 const CallBase &CB) {
2129 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2130 unsigned Opcode =
2131 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2132 assert((Opcode == SPIRV::OpUMulExtended || Opcode == SPIRV::OpSMulExtended) &&
2133 "Expected OpUMulExtended or OpSMulExtended");
2134
2135 const bool IsSret =
2136 !Call->ReturnType || Call->ReturnType->getOpcode() == SPIRV::OpTypeVoid;
2137 Register Op1Reg = IsSret ? Call->Arguments[1] : Call->Arguments[0];
2138 Register Op2Reg = IsSret ? Call->Arguments[2] : Call->Arguments[1];
2139
2140 SPIRVTypeInst RetType = nullptr;
2141 if (IsSret) {
2142 Register SRetReg = Call->Arguments[0];
2143 RetType =
2144 deduceSRetPointeeType(SRetReg, CB.getArgOperand(0), MIRBuilder, GR);
2145 if (!RetType)
2146 report_fatal_error("The first parameter must be a pointer");
2147 } else {
2148 RetType = Call->ReturnType;
2149 }
2150
2151 if (!RetType || RetType->getOpcode() != SPIRV::OpTypeStruct)
2152 report_fatal_error("Expected struct type result for the extended "
2153 "multiplication builtins");
2154 if (RetType->getNumOperands() != 3)
2155 report_fatal_error("Expected struct with exactly two members for the "
2156 "extended multiplication builtins");
2157 SPIRVTypeInst Member0Type =
2158 GR->getSPIRVTypeForVReg(RetType->getOperand(1).getReg());
2159 SPIRVTypeInst Member1Type =
2160 GR->getSPIRVTypeForVReg(RetType->getOperand(2).getReg());
2161 if (!Member0Type || !Member1Type || Member0Type != Member1Type)
2162 report_fatal_error("Both struct members must be the same type");
2163
2164 SPIRVTypeInst OpType1 = GR->getSPIRVTypeForVReg(Op1Reg);
2165 SPIRVTypeInst OpType2 = GR->getSPIRVTypeForVReg(Op2Reg);
2166 if (!OpType1 || !OpType2 || OpType1 != OpType2)
2167 report_fatal_error("Operands must have the same type");
2168 if (OpType1 != Member0Type)
2169 report_fatal_error("Operand type must match the struct member type");
2170
2171 if (IsSret) {
2172 buildSRetInst(Opcode, Call->Arguments[0], Op1Reg, Op2Reg, RetType,
2173 MIRBuilder, GR);
2174 } else {
2175 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2176 Register ResReg = Call->ReturnRegister;
2177 if (const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Op1Reg)) {
2178 MRI->setRegClass(ResReg, DstRC);
2179 }
2180 GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
2181 MIRBuilder.buildInstr(Opcode)
2182 .addDef(ResReg)
2183 .addUse(GR->getSPIRVTypeID(RetType))
2184 .addUse(Op1Reg)
2185 .addUse(Op2Reg);
2186 }
2187 return true;
2188}
2189
2191 MachineIRBuilder &MIRBuilder,
2192 SPIRVGlobalRegistry *GR) {
2193 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2194 unsigned Opcode =
2195 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2196
2197 auto MIB = MIRBuilder.buildInstr(Opcode)
2198 .addDef(Call->ReturnRegister)
2199 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2200 for (Register Arg : Call->Arguments)
2201 MIB.addUse(Arg);
2202 return true;
2203}
2204
2206 MachineIRBuilder &MIRBuilder,
2207 SPIRVGlobalRegistry *GR) {
2208 // Lookup the builtin record.
2209 SPIRV::BuiltIn::BuiltIn Value =
2210 SPIRV::lookupGetBuiltin(Call->Builtin->name(), Call->Builtin->Set)->Value;
2211 const bool IsDefaultOne = (Value == SPIRV::BuiltIn::GlobalSize ||
2212 Value == SPIRV::BuiltIn::NumWorkgroups ||
2213 Value == SPIRV::BuiltIn::WorkgroupSize ||
2214 Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);
2215 return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefaultOne ? 1 : 0);
2216}
2217
2219 MachineIRBuilder &MIRBuilder,
2220 SPIRVGlobalRegistry *GR) {
2221 // Lookup the image size query component number in the TableGen records.
2222 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2223 uint32_t Component =
2224 SPIRV::lookupImageQueryBuiltin(Builtin->name(), Builtin->Set)->Component;
2225 // Query result may either be a vector or a scalar. If return type is not a
2226 // vector, expect only a single size component. Otherwise get the number of
2227 // expected components.
2228 unsigned NumExpectedRetComponents =
2229 GR->getScalarOrVectorComponentCount(Call->ReturnType);
2230 // Get the actual number of query result/size components.
2231 SPIRVTypeInst ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
2232 unsigned NumActualRetComponents = getNumSizeComponents(ImgType);
2233 Register QueryResult = Call->ReturnRegister;
2234 SPIRVTypeInst QueryResultType = Call->ReturnType;
2235 if (NumExpectedRetComponents != NumActualRetComponents) {
2236 unsigned Bitwidth = Call->ReturnType->getOpcode() == SPIRV::OpTypeInt
2237 ? Call->ReturnType->getOperand(1).getImm()
2238 : 32;
2239 QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(
2240 LLT::fixed_vector(NumActualRetComponents, Bitwidth));
2241 MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::viIDRegClass);
2242 SPIRVTypeInst IntTy = GR->getOrCreateSPIRVIntegerType(Bitwidth, MIRBuilder);
2243 QueryResultType = GR->getOrCreateSPIRVVectorType(
2244 IntTy, NumActualRetComponents, MIRBuilder, true);
2245 GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());
2246 }
2247 bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;
2248 bool IsMultisampled = ImgType->getOperand(5).getImm() != 0;
2249 bool UseQuerySize = IsDimBuf || IsMultisampled;
2250 unsigned Opcode =
2251 UseQuerySize ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;
2252 auto MIB = MIRBuilder.buildInstr(Opcode)
2253 .addDef(QueryResult)
2254 .addUse(GR->getSPIRVTypeID(QueryResultType))
2255 .addUse(Call->Arguments[0]);
2256 if (!UseQuerySize)
2257 MIB.addUse(buildConstantIntReg32(0, MIRBuilder, GR)); // Lod id.
2258 if (NumExpectedRetComponents == NumActualRetComponents)
2259 return true;
2260 if (NumExpectedRetComponents == 1) {
2261 // Only 1 component is expected, build OpCompositeExtract instruction.
2262 unsigned ExtractedComposite =
2263 Component == 3 ? NumActualRetComponents - 1 : Component;
2264 assert(ExtractedComposite < NumActualRetComponents &&
2265 "Invalid composite index!");
2266 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2267 SPIRVTypeInst NewType = nullptr;
2268 if (isVectorType(QueryResultType)) {
2269 NewType = GR->getScalarOrVectorComponentType(QueryResultType);
2270 Register NewTypeReg = GR->getSPIRVTypeID(NewType);
2271 if (TypeReg != NewTypeReg)
2272 TypeReg = NewTypeReg;
2273 else
2274 NewType = nullptr;
2275 }
2276 MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
2277 .addDef(Call->ReturnRegister)
2278 .addUse(TypeReg)
2279 .addUse(QueryResult)
2280 .addImm(ExtractedComposite);
2281 if (NewType)
2282 updateRegType(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
2283 MIRBuilder.getMF().getRegInfo());
2284 } else {
2285 // More than 1 component is expected, fill a new vector.
2286 auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)
2287 .addDef(Call->ReturnRegister)
2288 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2289 .addUse(QueryResult)
2290 .addUse(QueryResult);
2291 for (unsigned i = 0; i < NumExpectedRetComponents; ++i)
2292 MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);
2293 }
2294 return true;
2295}
2296
2298 MachineIRBuilder &MIRBuilder,
2299 SPIRVGlobalRegistry *GR) {
2300 assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&
2301 "Image samples query result must be of int type!");
2302
2303 // Lookup the instruction opcode in the TableGen records.
2304 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2305 unsigned Opcode =
2306 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2307
2308 Register Image = Call->Arguments[0];
2309 SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(
2310 GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());
2311 (void)ImageDimensionality;
2312
2313 switch (Opcode) {
2314 case SPIRV::OpImageQuerySamples:
2315 assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&
2316 "Image must be of 2D dimensionality");
2317 break;
2318 case SPIRV::OpImageQueryLevels:
2319 assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||
2320 ImageDimensionality == SPIRV::Dim::DIM_2D ||
2321 ImageDimensionality == SPIRV::Dim::DIM_3D ||
2322 ImageDimensionality == SPIRV::Dim::DIM_Cube) &&
2323 "Image must be of 1D/2D/3D/Cube dimensionality");
2324 break;
2325 }
2326
2327 MIRBuilder.buildInstr(Opcode)
2328 .addDef(Call->ReturnRegister)
2329 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2330 .addUse(Image);
2331 return true;
2332}
2333
2334// TODO: Move to TableGen.
2335static SPIRV::SamplerAddressingMode::SamplerAddressingMode
2337 switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {
2338 case SPIRV::CLK_ADDRESS_CLAMP:
2339 return SPIRV::SamplerAddressingMode::Clamp;
2340 case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:
2341 return SPIRV::SamplerAddressingMode::ClampToEdge;
2342 case SPIRV::CLK_ADDRESS_REPEAT:
2343 return SPIRV::SamplerAddressingMode::Repeat;
2344 case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:
2345 return SPIRV::SamplerAddressingMode::RepeatMirrored;
2346 case SPIRV::CLK_ADDRESS_NONE:
2347 return SPIRV::SamplerAddressingMode::None;
2348 default:
2349 report_fatal_error("Unknown CL address mode");
2350 }
2351}
2352
2353static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {
2354 return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;
2355}
2356
2357static SPIRV::SamplerFilterMode::SamplerFilterMode
2359 if (Bitmask & SPIRV::CLK_FILTER_LINEAR)
2360 return SPIRV::SamplerFilterMode::Linear;
2361 if (Bitmask & SPIRV::CLK_FILTER_NEAREST)
2362 return SPIRV::SamplerFilterMode::Nearest;
2363 return SPIRV::SamplerFilterMode::Nearest;
2364}
2365
2366static bool generateReadImageInst(StringRef DemangledCall,
2368 MachineIRBuilder &MIRBuilder,
2369 SPIRVGlobalRegistry *GR) {
2370 if (Call->isSpirvOp())
2371 return buildOpFromWrapper(MIRBuilder, SPIRV::OpImageRead, Call,
2372 GR->getSPIRVTypeID(Call->ReturnType));
2373 Register Image = Call->Arguments[0];
2374 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2375 bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler");
2376 bool HasMsaa = DemangledCall.contains_insensitive("msaa");
2377 if (HasOclSampler) {
2378 Register Sampler = Call->Arguments[1];
2379
2380 if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&
2381 getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {
2382 uint64_t SamplerMask = getIConstVal(Sampler, MRI);
2385 getSamplerParamFromBitmask(SamplerMask),
2386 getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder);
2387 }
2388 SPIRVTypeInst ImageType = GR->getSPIRVTypeForVReg(Image);
2389 SPIRVTypeInst SampledImageType =
2390 GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
2391 Register SampledImage = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2392
2393 MIRBuilder.buildInstr(SPIRV::OpSampledImage)
2394 .addDef(SampledImage)
2395 .addUse(GR->getSPIRVTypeID(SampledImageType))
2396 .addUse(Image)
2397 .addUse(Sampler);
2398
2400 MIRBuilder);
2401
2402 if (!isVectorType(Call->ReturnType)) {
2403 SPIRVTypeInst TempType =
2404 GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder, true);
2405 Register TempRegister =
2406 MRI->createGenericVirtualRegister(GR->getRegType(TempType));
2407 MRI->setRegClass(TempRegister, GR->getRegClass(TempType));
2408 GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());
2409 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
2410 .addDef(TempRegister)
2411 .addUse(GR->getSPIRVTypeID(TempType))
2412 .addUse(SampledImage)
2413 .addUse(Call->Arguments[2]) // Coordinate.
2414 .addImm(SPIRV::ImageOperand::Lod)
2415 .addUse(Lod);
2416 MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
2417 .addDef(Call->ReturnRegister)
2418 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2419 .addUse(TempRegister)
2420 .addImm(0);
2421 } else {
2422 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
2423 .addDef(Call->ReturnRegister)
2424 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2425 .addUse(SampledImage)
2426 .addUse(Call->Arguments[2]) // Coordinate.
2427 .addImm(SPIRV::ImageOperand::Lod)
2428 .addUse(Lod);
2429 }
2430 } else if (HasMsaa) {
2431 MIRBuilder.buildInstr(SPIRV::OpImageRead)
2432 .addDef(Call->ReturnRegister)
2433 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2434 .addUse(Image)
2435 .addUse(Call->Arguments[1]) // Coordinate.
2436 .addImm(SPIRV::ImageOperand::Sample)
2437 .addUse(Call->Arguments[2]);
2438 } else {
2439 MIRBuilder.buildInstr(SPIRV::OpImageRead)
2440 .addDef(Call->ReturnRegister)
2441 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2442 .addUse(Image)
2443 .addUse(Call->Arguments[1]); // Coordinate.
2444 }
2445 return true;
2446}
2447
2449 MachineIRBuilder &MIRBuilder,
2450 SPIRVGlobalRegistry *GR) {
2451 if (Call->isSpirvOp())
2452 return buildOpFromWrapper(MIRBuilder, SPIRV::OpImageWrite, Call,
2453 Register(0));
2454 MIRBuilder.buildInstr(SPIRV::OpImageWrite)
2455 .addUse(Call->Arguments[0]) // Image.
2456 .addUse(Call->Arguments[1]) // Coordinate.
2457 .addUse(Call->Arguments[2]); // Texel.
2458 return true;
2459}
2460
2461static bool generateSampleImageInst(StringRef DemangledCall,
2463 MachineIRBuilder &MIRBuilder,
2464 SPIRVGlobalRegistry *GR) {
2465 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2466 if (Call->Builtin->name().contains_insensitive(
2467 "__translate_sampler_initializer")) {
2468 // Build sampler literal.
2469 uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI);
2471 Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),
2473 getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder);
2474 return Sampler.isValid();
2475 } else if (Call->Builtin->name().contains_insensitive(
2476 "__spirv_SampledImage")) {
2477 // Create OpSampledImage.
2478 Register Image = Call->Arguments[0];
2479 SPIRVTypeInst ImageType = GR->getSPIRVTypeForVReg(Image);
2480 SPIRVTypeInst SampledImageType =
2481 GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
2482 Register SampledImage =
2483 Call->ReturnRegister.isValid()
2484 ? Call->ReturnRegister
2485 : MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2486 MIRBuilder.buildInstr(SPIRV::OpSampledImage)
2487 .addDef(SampledImage)
2488 .addUse(GR->getSPIRVTypeID(SampledImageType))
2489 .addUse(Image)
2490 .addUse(Call->Arguments[1]); // Sampler.
2491 return true;
2492 } else if (Call->Builtin->name().contains_insensitive(
2493 "__spirv_ImageSampleExplicitLod")) {
2494 // Sample an image using an explicit level of detail.
2495 std::string ReturnType = DemangledCall.str();
2496 if (DemangledCall.contains("_R")) {
2497 ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);
2498 ReturnType = ReturnType.substr(0, ReturnType.find('('));
2499 }
2500 SPIRVTypeInst Type = Call->ReturnType
2501 ? Call->ReturnType
2503 ReturnType, MIRBuilder, true));
2504 if (!Type) {
2505 std::string DiagMsg =
2506 "Unable to recognize SPIRV type name: " + ReturnType;
2507 report_fatal_error(DiagMsg.c_str());
2508 }
2509 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
2510 .addDef(Call->ReturnRegister)
2512 .addUse(Call->Arguments[0]) // Image.
2513 .addUse(Call->Arguments[1]) // Coordinate.
2514 .addImm(SPIRV::ImageOperand::Lod)
2515 .addUse(Call->Arguments[3]);
2516 return true;
2517 }
2518 return false;
2519}
2520
2522 MachineIRBuilder &MIRBuilder) {
2523 const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2524 LLT ResTy = MRI->getType(Call->ReturnRegister);
2525 LLT CondTy = MRI->getType(Call->Arguments[0]);
2526 if (!ResTy.isVector() && CondTy.isVector())
2527 report_fatal_error("OpSelect with a scalar result requires a scalar "
2528 "boolean condition");
2529 MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],
2530 Call->Arguments[1], Call->Arguments[2]);
2531 return true;
2532}
2533
2535 MachineIRBuilder &MIRBuilder,
2536 SPIRVGlobalRegistry *GR) {
2537 createContinuedInstructions(MIRBuilder, SPIRV::OpCompositeConstruct, 3,
2538 SPIRV::OpCompositeConstructContinuedINTEL,
2539 Call->Arguments, Call->ReturnRegister,
2540 GR->getSPIRVTypeID(Call->ReturnType));
2541 return true;
2542}
2543
2545 MachineIRBuilder &MIRBuilder,
2546 SPIRVGlobalRegistry *GR) {
2547 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2548 unsigned Opcode =
2549 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2550 bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR &&
2551 Opcode != SPIRV::OpCooperativeMatrixStoreCheckedINTEL &&
2552 Opcode != SPIRV::OpCooperativeMatrixPrefetchINTEL;
2553 unsigned ArgSz = Call->Arguments.size();
2554 unsigned LiteralIdx = 0;
2555 switch (Opcode) {
2556 // Memory operand is optional and is literal.
2557 case SPIRV::OpCooperativeMatrixLoadKHR:
2558 LiteralIdx = ArgSz > 3 ? 3 : 0;
2559 break;
2560 case SPIRV::OpCooperativeMatrixStoreKHR:
2561 LiteralIdx = ArgSz > 4 ? 4 : 0;
2562 break;
2563 case SPIRV::OpCooperativeMatrixLoadCheckedINTEL:
2564 LiteralIdx = ArgSz > 7 ? 7 : 0;
2565 break;
2566 case SPIRV::OpCooperativeMatrixStoreCheckedINTEL:
2567 LiteralIdx = ArgSz > 8 ? 8 : 0;
2568 break;
2569 // Cooperative Matrix Operands operand is optional and is literal.
2570 case SPIRV::OpCooperativeMatrixMulAddKHR:
2571 LiteralIdx = ArgSz > 3 ? 3 : 0;
2572 break;
2573 };
2574
2576 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2577 if (Opcode == SPIRV::OpCooperativeMatrixPrefetchINTEL) {
2578 const uint32_t CacheLevel = getIConstVal(Call->Arguments[3], MRI);
2579 auto MIB = MIRBuilder.buildInstr(SPIRV::OpCooperativeMatrixPrefetchINTEL)
2580 .addUse(Call->Arguments[0]) // pointer
2581 .addUse(Call->Arguments[1]) // rows
2582 .addUse(Call->Arguments[2]) // columns
2583 .addImm(CacheLevel) // cache level
2584 .addUse(Call->Arguments[4]); // memory layout
2585 if (ArgSz > 5)
2586 MIB.addUse(Call->Arguments[5]); // stride
2587 if (ArgSz > 6) {
2588 const uint32_t MemOp = getIConstVal(Call->Arguments[6], MRI);
2589 MIB.addImm(MemOp); // memory operand
2590 }
2591 return true;
2592 }
2593 if (LiteralIdx > 0)
2594 ImmArgs.push_back(getIConstVal(Call->Arguments[LiteralIdx], MRI));
2595 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2596 if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) {
2597 SPIRVTypeInst CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
2598 if (!CoopMatrType)
2599 report_fatal_error("Can't find a register's type definition");
2600 MIRBuilder.buildInstr(Opcode)
2601 .addDef(Call->ReturnRegister)
2602 .addUse(TypeReg)
2603 .addUse(CoopMatrType->getOperand(0).getReg());
2604 return true;
2605 }
2606 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2607 IsSet ? TypeReg : Register(0), ImmArgs);
2608}
2609
2611 MachineIRBuilder &MIRBuilder,
2612 SPIRVGlobalRegistry *GR) {
2613 // Lookup the instruction opcode in the TableGen records.
2614 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2615 unsigned Opcode =
2616 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2617 const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2618
2619 switch (Opcode) {
2620 case SPIRV::OpSpecConstant: {
2621 // Determine the constant MI.
2622 Register ConstRegister = Call->Arguments[1];
2623 const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);
2624 assert(Const &&
2625 (Const->getOpcode() == TargetOpcode::G_CONSTANT ||
2626 Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&
2627 "Argument should be either an int or floating-point constant");
2628 // Determine the opcode and built the OpSpec MI.
2629 const MachineOperand &ConstOperand = Const->getOperand(1);
2630 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {
2631 assert(ConstOperand.isCImm() && "Int constant operand is expected");
2632 Opcode = ConstOperand.getCImm()->getValue().getZExtValue()
2633 ? SPIRV::OpSpecConstantTrue
2634 : SPIRV::OpSpecConstantFalse;
2635 }
2636 auto MIB = MIRBuilder.buildInstr(Opcode)
2637 .addDef(Call->ReturnRegister)
2638 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2639
2640 if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {
2641 if (Const->getOpcode() == TargetOpcode::G_CONSTANT)
2642 addNumImm(ConstOperand.getCImm()->getValue(), MIB);
2643 else
2644 addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);
2645 }
2646 // Build the SpecID decoration.
2647 unsigned SpecId =
2648 static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));
2649 buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,
2650 {SpecId});
2651 return true;
2652 }
2653 case SPIRV::OpSpecConstantComposite: {
2654 createContinuedInstructions(MIRBuilder, Opcode, 3,
2655 SPIRV::OpSpecConstantCompositeContinuedINTEL,
2656 Call->Arguments, Call->ReturnRegister,
2657 GR->getSPIRVTypeID(Call->ReturnType));
2658 return true;
2659 }
2660 default:
2661 return false;
2662 }
2663}
2664
2666 MachineIRBuilder &MIRBuilder,
2667 SPIRVGlobalRegistry *GR) {
2668 // Lookup the instruction opcode in the TableGen records.
2669 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2670 unsigned Opcode =
2671 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2672
2673 return buildExtendedBitOpsInst(Call, Opcode, MIRBuilder, GR);
2674}
2675
2677 MachineIRBuilder &MIRBuilder,
2678 SPIRVGlobalRegistry *GR) {
2679 // Lookup the instruction opcode in the TableGen records.
2680 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2681 unsigned Opcode =
2682 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2683
2684 return buildBindlessImageINTELInst(Call, Opcode, MIRBuilder, GR);
2685}
2686
2688 MachineIRBuilder &MIRBuilder,
2689 SPIRVGlobalRegistry *GR) {
2690 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2691 unsigned Opcode =
2692 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2693 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
2694}
2695
2697 unsigned Opcode, MachineIRBuilder &MIRBuilder,
2698 SPIRVGlobalRegistry *GR, const CallBase &CB) {
2699 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2701 Register InputReg = Call->Arguments[0];
2702 const Type *RetTy = GR->getTypeForSPIRVType(Call->ReturnType);
2703 bool IsSRet = RetTy->isVoidTy();
2704
2705 if (IsSRet) {
2706 const LLT ValTy = MRI->getType(InputReg);
2707 Register ActualRetValReg = MRI->createGenericVirtualRegister(ValTy);
2708 SPIRVTypeInst InstructionType =
2709 deduceSRetPointeeType(InputReg, CB.getArgOperand(0), MIRBuilder, GR);
2710 InputReg = Call->Arguments[1];
2711 auto InputType = GR->getTypeForSPIRVType(GR->getSPIRVTypeForVReg(InputReg));
2712 Register PtrInputReg;
2713 if (InputType->getTypeID() == llvm::Type::TypeID::TypedPointerTyID) {
2714 LLT InputLLT = MRI->getType(InputReg);
2715 PtrInputReg = MRI->createGenericVirtualRegister(InputLLT);
2716 SPIRVTypeInst PtrType =
2717 GR->getPointeeType(GR->getSPIRVTypeForVReg(InputReg));
2718 MachineMemOperand *MMO1 = MIRBuilder.getMF().getMachineMemOperand(
2720 InputLLT.getSizeInBytes(), Align(4));
2721 MIRBuilder.buildLoad(PtrInputReg, InputReg, *MMO1);
2722 MRI->setRegClass(PtrInputReg, &SPIRV::iIDRegClass);
2723 GR->assignSPIRVTypeToVReg(PtrType, PtrInputReg, MIRBuilder.getMF());
2724 }
2725
2726 for (unsigned index = 2; index < 7; index++) {
2727 ImmArgs.push_back(getIConstVal(Call->Arguments[index], MRI));
2728 }
2729
2730 // Emit the instruction
2731 auto MIB = MIRBuilder.buildInstr(Opcode)
2732 .addDef(ActualRetValReg)
2733 .addUse(GR->getSPIRVTypeID(InstructionType));
2734 if (PtrInputReg)
2735 MIB.addUse(PtrInputReg);
2736 else
2737 MIB.addUse(InputReg);
2738
2739 for (uint32_t Imm : ImmArgs)
2740 MIB.addImm(Imm);
2741 unsigned Size = ValTy.getSizeInBytes();
2742 // Store result to the pointer passed in Arg[0]
2743 MachineMemOperand *MMO = MIRBuilder.getMF().getMachineMemOperand(
2745 MRI->setRegClass(ActualRetValReg, &SPIRV::pIDRegClass);
2746 MIRBuilder.buildStore(ActualRetValReg, Call->Arguments[0], *MMO);
2747 return true;
2748 } else {
2749 for (unsigned index = 1; index < 6; index++)
2750 ImmArgs.push_back(getIConstVal(Call->Arguments[index], MRI));
2751
2752 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2753 GR->getSPIRVTypeID(Call->ReturnType), ImmArgs);
2754 }
2755}
2756
2758 MachineIRBuilder &MIRBuilder,
2760 const CallBase &CB) {
2761 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2762 unsigned Opcode =
2763 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2764
2765 return buildAPFixedPointInst(Call, Opcode, MIRBuilder, GR, CB);
2766}
2767
2768static bool
2770 MachineIRBuilder &MIRBuilder,
2771 SPIRVGlobalRegistry *GR) {
2772 // Lookup the instruction opcode in the TableGen records.
2773 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2774 unsigned Opcode =
2775 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2776
2777 return buildTernaryBitwiseFunctionINTELInst(Call, Opcode, MIRBuilder, GR);
2778}
2779
2781 MachineIRBuilder &MIRBuilder,
2782 SPIRVGlobalRegistry *GR) {
2783 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2784 unsigned Opcode =
2785 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2786
2787 return buildImageChannelDataTypeInst(Call, Opcode, MIRBuilder, GR);
2788}
2789
2791 MachineIRBuilder &MIRBuilder,
2792 SPIRVGlobalRegistry *GR) {
2793 // Lookup the instruction opcode in the TableGen records.
2794 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2795 unsigned Opcode =
2796 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2797
2798 return build2DBlockIOINTELInst(Call, Opcode, MIRBuilder, GR);
2799}
2800
2802 MachineIRBuilder &MIRBuilder,
2803 SPIRVGlobalRegistry *GR) {
2804 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2805 unsigned Opcode =
2806 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2807
2808 unsigned Scope = SPIRV::Scope::Workgroup;
2809 if (Builtin->name().contains("sub_group"))
2810 Scope = SPIRV::Scope::Subgroup;
2811
2812 return buildPipeInst(Call, Opcode, Scope, MIRBuilder, GR);
2813}
2814
2816 MachineIRBuilder &MIRBuilder,
2817 SPIRVGlobalRegistry *GR) {
2818 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2819 unsigned Opcode =
2820 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2821
2822 bool IsSet = Opcode != SPIRV::OpPredicatedStoreINTEL;
2823 unsigned ArgSz = Call->Arguments.size();
2825 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2826 // Memory operand is optional and is literal.
2827 if (ArgSz > 3)
2828 ImmArgs.push_back(getIConstVal(Call->Arguments[/*Literal index*/ 3], MRI));
2829
2830 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2831 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2832 IsSet ? TypeReg : Register(0), ImmArgs);
2833}
2834
2836 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR,
2837 const CallBase &CB) {
2838 // The OpenCL ndrange_*D functions are overloaded and support 1D, 2D, and 3D
2839 // variants, accepting 1 to 3 arguments:
2840 // (global_work_size)
2841 // (global_work_size, local_work_size)
2842 // (global_work_offset, global_work_size, local_work_size)
2843 // Note: When all three arguments are provided, they are reordered compared
2844 // to the one- or two-argument form.
2845 //
2846 // The function may return data through an sret argument at position 0 (with
2847 // a void function return type). When present, all other argument indices are
2848 // adjusted accordingly.
2849 //
2850 // SPIR-V's OpBuildNDRange requires all three arguments (GlobalWorkSize,
2851 // LocalWorkSize, GlobalWorkOffset). For 1D kernels, the values are scalars;
2852 // for 2D/3D kernels, they are arrays of 2 or 3 elements. Missing arguments
2853 // default to zero.
2854 //
2855 // Calculate argument indices based on the number of arguments and presence
2856 // of sret:
2857 const unsigned NumCallArgs = Call->Arguments.size();
2858 const unsigned MaxCallArgs = Call->Builtin->MaxNumArgs;
2859 const unsigned IncorrectArgIdx = MaxCallArgs + 1;
2860
2861 const Type *RetTy = GR->getTypeForSPIRVType(Call->ReturnType);
2862 bool HasSRetArg = RetTy->isVoidTy();
2863
2864 const unsigned SRetArgIdx = HasSRetArg ? 0 : IncorrectArgIdx;
2865 const unsigned ArgBase = HasSRetArg ? 1 : 0;
2866 const unsigned MaxNDRangeArgs = 3;
2867 const unsigned NumNDRangeArgs = NumCallArgs - ArgBase;
2868
2869 const unsigned GlobalWorkSizeArgIdx =
2870 NumNDRangeArgs < MaxNDRangeArgs ? ArgBase : ArgBase + 1;
2871 const unsigned LocalWorkSizeArgIdx =
2872 (NumNDRangeArgs == 1)
2873 ? IncorrectArgIdx
2874 : (NumNDRangeArgs == MaxNDRangeArgs ? ArgBase + 2 : ArgBase + 1);
2875 const unsigned GlobalWorkOffsetArgIdx =
2876 NumNDRangeArgs == MaxNDRangeArgs ? ArgBase : IncorrectArgIdx;
2877
2878 // Each nd_range field is an array of <Dimension> integers matching the
2879 // address model width (32 or 64 bits).
2880 const unsigned AddressModelBits = GR->getPointerSize();
2881 assert(AddressModelBits == 64 || AddressModelBits == 32);
2882
2883 // The dimension is encoded in the function name as "ndrange_XD" where X is
2884 // 1, 2, or 3.
2885 unsigned Dimension = 0;
2886 Call->Builtin->name().substr(8, 1).getAsInteger(10, Dimension);
2887 assert(Dimension <= 3 && Dimension >= 1);
2888
2889 // Determine the work size type based on the dimension. For missing arguments,
2890 // create a zero constant of the appropriate type.
2891 MachineFunction &MF = MIRBuilder.getMF();
2892 SPIRVTypeInst SpvFieldTy;
2893 Register ConstZero;
2894 if (Dimension == 1) {
2895 SpvFieldTy = GR->getSPIRVTypeForVReg(Call->Arguments[GlobalWorkSizeArgIdx]);
2896 assert(SpvFieldTy && SpvFieldTy->getOpcode() == SPIRV::OpTypeInt &&
2897 "Expected scalar integer type");
2898
2899 if (NumNDRangeArgs < MaxNDRangeArgs)
2900 ConstZero = GR->buildConstantInt(0, MIRBuilder, SpvFieldTy, true);
2901 } else {
2902 Type *BaseTy =
2903 IntegerType::get(MF.getFunction().getContext(), AddressModelBits);
2904 Type *FieldTy = ArrayType::get(BaseTy, Dimension);
2905 SpvFieldTy = GR->getOrCreateSPIRVType(
2906 FieldTy, MIRBuilder, SPIRV::AccessQualifier::ReadOnly, true);
2907
2908 if (NumNDRangeArgs < MaxNDRangeArgs) {
2909 auto InsertIt = MIRBuilder.getInsertPt();
2910 MachineBasicBlock &MBB = MIRBuilder.getMBB();
2911 MachineInstr &InsertMI = (InsertIt != MBB.end()) ? *InsertIt : MBB.back();
2913 ConstZero = GR->getOrCreateConstIntArray(0, Dimension, InsertMI,
2914 SpvFieldTy, *ST.getInstrInfo());
2915 }
2916 }
2917
2918 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2919
2920 auto CreateDataRegister = [&](unsigned Idx) -> Register {
2921 Register Reg = (Idx == IncorrectArgIdx) ? ConstZero : Call->Arguments[Idx];
2922
2923 if (GR->getSPIRVTypeForVReg(Reg) == SpvFieldTy) {
2924 // Already has the correct type.
2925 return Reg;
2926 }
2927
2929 "Only pointer types are supported for loading values");
2930
2931 Register Ptr = Reg;
2932
2933 Reg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2934 GR->assignSPIRVTypeToVReg(SpvFieldTy, Reg, MF);
2935
2936 MIRBuilder.buildInstr(SPIRV::OpLoad)
2937 .addDef(Reg)
2938 .addUse(GR->getSPIRVTypeID(SpvFieldTy))
2939 .addUse(Ptr);
2940 return Reg;
2941 };
2942
2943 Register GlobalWorkSize = CreateDataRegister(GlobalWorkSizeArgIdx);
2944 Register LocalWorkSize = CreateDataRegister(LocalWorkSizeArgIdx);
2945 Register GlobalWorkOffset = CreateDataRegister(GlobalWorkOffsetArgIdx);
2946
2947 if (!HasSRetArg) {
2948 return MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
2949 .addDef(Call->ReturnRegister)
2950 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2951 .addUse(GlobalWorkSize)
2952 .addUse(LocalWorkSize)
2953 .addUse(GlobalWorkOffset);
2954 }
2955
2956 // When sret is used, store nd_range struct through the pointer in the first
2957 // argument.
2958 Register SRetReg = Call->Arguments[SRetArgIdx];
2960 SRetReg, CB.getArgOperand(SRetArgIdx), MIRBuilder, GR);
2961
2962 Register TmpReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2963 GR->assignSPIRVTypeToVReg(SRetType, TmpReg, MF);
2964
2965 MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
2966 .addDef(TmpReg)
2967 .addUse(GR->getSPIRVTypeID(SRetType))
2968 .addUse(GlobalWorkSize)
2969 .addUse(LocalWorkSize)
2970 .addUse(GlobalWorkOffset);
2971 return MIRBuilder.buildInstr(SPIRV::OpStore)
2972 .addUse(Call->Arguments[SRetArgIdx])
2973 .addUse(TmpReg);
2974}
2975
2977 MachineIRBuilder &MIRBuilder,
2978 SPIRVGlobalRegistry *GR) {
2979 // In this function there are three stages:
2980 // 1. prepare call indexes in order we expect them.
2981 // 2. process all arguments which requered preparation.
2982 // 3. create a SPIRV operator with arguments.
2983
2984 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2985 const DataLayout &DL = MIRBuilder.getDataLayout();
2986 const SPIRVTypeInst Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
2987
2988 // 1. prepare call indexes in order we expect them.
2989 // Based on clang sources, clang/lib/CodeGen/CGBuiltin.cpp, BIenqueue_kernel,
2990 // We expect 4 different layouts of call arguments:
2991 // 1) No events, no vargs: {Queue, Flags, Range, Kernel, Block};
2992 // 2) No events, varargs: {Queue, Flags, Range, Kernel, Block, NumElem,
2993 // ElemPtr};
2994 // 3) events, no varargs: {Queue, Flags, Range, NumEvents,
2995 // EventWaitList, EventRet, Kernel, Block};
2996 // 4) events, varargs: {Queue,
2997 // Flags, Range, NumEvents, EventWaitList, EventRet, Kernel, Block,
2998 // NumElem, ElemPtr};
2999 //
3000 // We also may expect __spirv_EnqueueKernel
3001
3002 bool IsSpirvOp = Call->isSpirvOp();
3003 bool HasEvents = Call->Builtin->name().contains("_events") || IsSpirvOp;
3004 bool HasVarArgs = Call->Builtin->name().contains("_varargs") || IsSpirvOp;
3005
3006 const unsigned NumArgs = Call->Arguments.size();
3007 const unsigned BaseArgIdx = 0;
3008 const unsigned IncorrectIdx = NumArgs + 1;
3009
3010 const unsigned QueueIdx = BaseArgIdx;
3011 const unsigned FlagsIdx = BaseArgIdx + 1;
3012 const unsigned NDRangeIdx = BaseArgIdx + 2;
3013 const unsigned NumEventsIdx = HasEvents ? BaseArgIdx + 3 : IncorrectIdx;
3014 const unsigned WaitEventsIdx = HasEvents ? BaseArgIdx + 4 : IncorrectIdx;
3015 const unsigned RetEventIdx = HasEvents ? BaseArgIdx + 5 : IncorrectIdx;
3016 const unsigned InvokeIdx = BaseArgIdx + 3 + (HasEvents ? 3 : 0);
3017 const unsigned ParamIdx = BaseArgIdx + 4 + (HasEvents ? 3 : 0);
3018 const unsigned LocalSizeNumElemIdx =
3019 HasVarArgs ? (BaseArgIdx + 5 + (HasEvents ? 3 : 0)) : IncorrectIdx;
3020 const unsigned LocalSizeElemPtrIdx =
3021 HasVarArgs ? (BaseArgIdx + 6 + (HasEvents ? 3 : 0)) : IncorrectIdx;
3022
3023 [[maybe_unused]] const unsigned LastArgIdx =
3024 (BaseArgIdx + 4 + (HasEvents ? 3 : 0) + (HasVarArgs ? 2 : 0));
3025 assert(LastArgIdx < NumArgs && "Incorrect number arguments");
3026
3027 // 2. Process all arguments which requered preparation.
3028 // 2.1 Events - use Call arguments, or use dummy nulls in case of absence of
3029 // events
3030
3031 auto BuildDeviceEventNullPtr = [&]() {
3032 LLVMContext &Ctx = MIRBuilder.getMF().getFunction().getContext();
3033 Type *DeviceEventTy = TargetExtType::get(Ctx, "spirv.DeviceEvent");
3034 SPIRVTypeInst DeviceEventPtrTy = GR->getOrCreateSPIRVPointerType(
3035 DeviceEventTy, MIRBuilder, SPIRV::StorageClass::Generic);
3036 return GR->getOrCreateConstNullPtr(MIRBuilder, DeviceEventPtrTy);
3037 };
3038
3039 Register NumEventsReg;
3040 Register WaitEventsReg;
3041 Register RetEventReg;
3042 if (HasEvents) {
3043 auto IsNullEvent = [&](Register R) {
3045 return Def->getOpcode() == TargetOpcode::G_CONSTANT &&
3046 Def->getOperand(1).getCImm()->isZero();
3047 };
3048
3049 NumEventsReg = Call->Arguments[NumEventsIdx];
3050 WaitEventsReg = Call->Arguments[WaitEventsIdx];
3051 RetEventReg = Call->Arguments[RetEventIdx];
3052 if (IsNullEvent(WaitEventsReg))
3053 WaitEventsReg = BuildDeviceEventNullPtr();
3054 if (IsNullEvent(RetEventReg))
3055 RetEventReg = BuildDeviceEventNullPtr();
3056 } else {
3057 NumEventsReg = buildConstantIntReg32(0, MIRBuilder, GR);
3058 Register NullPtr = BuildDeviceEventNullPtr();
3059 WaitEventsReg = NullPtr;
3060 RetEventReg = NullPtr;
3061 }
3062
3063 // 2.2 Invoke (Kernel)
3064 // The Invoke operand of OpEnqueueKernel must be the function's <id>
3065 // (per SPIR-V spec). The frontend hands us the result of an
3066 // addrspacecast of @block_invoke_kernel; bypass that cast so the
3067 // operand references the underlying G_GLOBAL_VALUE register, which
3068 // selectGlobalValue lowers to a placeholder later rewritten by
3069 // SPIRVModuleAnalysis to the OpFunction <id>.
3070 MachineInstr *InvokeGlobalMI =
3071 getBlockStructInstr(Call->Arguments[InvokeIdx], MRI);
3072 assert(InvokeGlobalMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
3073 Register InvokeReg = InvokeGlobalMI->getOperand(0).getReg();
3074 // OpEnqueueKernel's Invoke operand uses the pID register class.
3075 MRI->setRegClass(InvokeReg, &SPIRV::pIDRegClass);
3076
3077 // 2.3 Param, Param Size, Param Align
3078 Register BlockLiteralReg = Call->Arguments[ParamIdx];
3079 const SPIRVTypeInst Int8Ty = GR->getOrCreateSPIRVIntegerType(8, MIRBuilder);
3080 const SPIRVTypeInst Int8PtrGen = GR->getOrCreateSPIRVPointerType(
3081 Int8Ty, MIRBuilder, SPIRV::StorageClass::Generic);
3082 Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
3083
3084 Register ParamReg = createVirtualRegister(Int8PtrGen, GR, MIRBuilder);
3085 MIRBuilder.buildInstr(SPIRV::OpBitcast)
3086 .addDef(ParamReg)
3087 .addUse(GR->getSPIRVTypeID(Int8PtrGen))
3088 .addUse(BlockLiteralReg);
3089 // TODO: these numbers should be obtained from block literal structure.
3090 Register ParamSizeReg =
3091 buildConstantIntReg32(DL.getTypeStoreSize(PType), MIRBuilder, GR);
3092 Register ParamAlignReg =
3093 buildConstantIntReg32(DL.getPrefTypeAlign(PType).value(), MIRBuilder, GR);
3094
3095 // 2.4 Local Size Array
3096 SmallVector<Register, 16> LocalSizes;
3097 if (HasVarArgs) {
3098 Register LocalSizeNumElem = Call->Arguments[LocalSizeNumElemIdx];
3099 MachineInstr *LocalSizeNumElemMI = MRI->getUniqueVRegDef(LocalSizeNumElem);
3100 const MachineOperand &ConstOp = LocalSizeNumElemMI->getOperand(1);
3101 assert(LocalSizeNumElemMI->getOpcode() == TargetOpcode::G_CONSTANT &&
3102 ConstOp.isCImm() && "Expected constant immediate");
3103 uint64_t NumElem = ConstOp.getCImm()->getValue().getZExtValue();
3104
3105 Register LocalSizeArrayReg = Call->Arguments[LocalSizeElemPtrIdx];
3106
3107 for (unsigned i = 0; i < NumElem; ++i) {
3108 Register Reg = MRI->createVirtualRegister(&SPIRV::pIDRegClass);
3109 auto GEPInst = MIRBuilder.buildIntrinsic(
3110 Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false);
3111 GEPInst
3112 .addImm(0) // In bound.
3113 .addUse(LocalSizeArrayReg) // Base pointer.
3114 .addUse(buildConstantIntReg32(0, MIRBuilder, GR)) // Indices.
3115 .addUse(buildConstantIntReg32(i, MIRBuilder, GR));
3116 LocalSizes.push_back(Reg);
3117 }
3118 }
3119
3120 // 3. create a SPIRV operator with arguments.
3121 auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)
3122 .addDef(Call->ReturnRegister)
3123 .addUse(GR->getSPIRVTypeID(Int32Ty))
3124 .addUse(Call->Arguments[QueueIdx])
3125 .addUse(Call->Arguments[FlagsIdx])
3126 .addUse(Call->Arguments[NDRangeIdx])
3127 .addUse(NumEventsReg)
3128 .addUse(WaitEventsReg)
3129 .addUse(RetEventReg)
3130 .addUse(InvokeReg)
3131 .addUse(ParamReg)
3132 .addUse(ParamSizeReg)
3133 .addUse(ParamAlignReg);
3134 for (auto &LocalSize : LocalSizes)
3135 MIB.addUse(LocalSize);
3136
3137 return true;
3138}
3139
3141 MachineIRBuilder &MIRBuilder,
3142 SPIRVGlobalRegistry *GR, const CallBase &CB) {
3143 // Lookup the instruction opcode in the TableGen records.
3144 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3145 unsigned Opcode =
3146 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3147
3148 switch (Opcode) {
3149 case SPIRV::OpRetainEvent:
3150 case SPIRV::OpReleaseEvent:
3151 return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);
3152 case SPIRV::OpCreateUserEvent:
3153 case SPIRV::OpGetDefaultQueue:
3154 return MIRBuilder.buildInstr(Opcode)
3155 .addDef(Call->ReturnRegister)
3156 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
3157 case SPIRV::OpIsValidEvent:
3158 return MIRBuilder.buildInstr(Opcode)
3159 .addDef(Call->ReturnRegister)
3160 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
3161 .addUse(Call->Arguments[0]);
3162 case SPIRV::OpSetUserEventStatus:
3163 return MIRBuilder.buildInstr(Opcode)
3164 .addUse(Call->Arguments[0])
3165 .addUse(Call->Arguments[1]);
3166 case SPIRV::OpCaptureEventProfilingInfo:
3167 return MIRBuilder.buildInstr(Opcode)
3168 .addUse(Call->Arguments[0])
3169 .addUse(Call->Arguments[1])
3170 .addUse(Call->Arguments[2]);
3171 case SPIRV::OpBuildNDRange:
3172 return buildNDRange(Call, MIRBuilder, GR, CB);
3173 case SPIRV::OpEnqueueKernel:
3174 return buildEnqueueKernel(Call, MIRBuilder, GR);
3175 default:
3176 return false;
3177 }
3178}
3179
3181 MachineIRBuilder &MIRBuilder,
3182 SPIRVGlobalRegistry *GR, const CallBase &CB) {
3183 // Lookup the instruction opcode in the TableGen records.
3184 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3185 unsigned Opcode =
3186 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3187
3188 bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;
3189 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
3190 if (Call->isSpirvOp())
3191 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
3192 IsSet ? TypeReg : Register(0));
3193
3194 auto Scope = buildConstantIntReg32(SPIRV::Scope::Workgroup, MIRBuilder, GR);
3195
3196 switch (Opcode) {
3197 case SPIRV::OpGroupAsyncCopy: {
3198 SPIRVTypeInst NewType =
3199 Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent
3200 ? nullptr
3201 : GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder, true);
3202 Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);
3203 unsigned NumArgs = Call->Arguments.size();
3204 Register EventReg = Call->Arguments[NumArgs - 1];
3205 SPIRVTypeInst EventType = GR->getSPIRVTypeForVReg(EventReg);
3206 if (!EventType || EventType->getOpcode() != SPIRV::OpTypeEvent) {
3207 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
3208 Register ConstReg = EventReg;
3209 MachineInstr *Def = getDefInstrMaybeConstant(ConstReg, MRI);
3210 SPIRVTypeInst EventPointeeType =
3211 EventType && EventType->getOpcode() == SPIRV::OpTypePointer
3212 ? GR->getPointeeType(EventType)
3213 : nullptr;
3214 if (Def->getOpcode() == TargetOpcode::G_CONSTANT &&
3215 Def->getOperand(1).getCImm()->isZero()) {
3216 // Only substitute a null Event for the "ptr null" idiom, not for a
3217 // real event value that just is not typed as OpTypeEvent yet.
3218 SPIRVTypeInst EventTy = NewType ? NewType
3220 "spirv.Event", MIRBuilder, true);
3221 Register EventTyReg = GR->getSPIRVTypeID(EventTy);
3222 Register NullEventReg = createVirtualRegister(EventTy, GR, MIRBuilder);
3223 MIRBuilder.buildInstr(SPIRV::OpConstantNull)
3224 .addDef(NullEventReg)
3225 .addUse(EventTyReg);
3226 EventReg = NullEventReg;
3227 } else if (EventPointeeType &&
3228 EventPointeeType->getOpcode() == SPIRV::OpTypeEvent) {
3229 // Dereference: a real event can end up typed as pointer-to-Event
3230 // after round-tripping through a stack slot under the legacy
3231 // opaque-ptr ocl_event ABI.
3232 Register EventTyReg = GR->getSPIRVTypeID(EventPointeeType);
3233 Register LoadedReg =
3234 createVirtualRegister(EventPointeeType, GR, MIRBuilder);
3235 MIRBuilder.buildInstr(SPIRV::OpLoad)
3236 .addDef(LoadedReg)
3237 .addUse(EventTyReg)
3238 .addUse(EventReg);
3239 EventReg = LoadedReg;
3240 }
3241 }
3242 Register NumElemReg = Call->Arguments[2];
3243
3244 // Untyped pointers use OpUntypedGroupAsyncCopyKHR, which adds an explicit
3245 // Element Num Bytes operand.
3246 SPIRVTypeInst DestPtrTy = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
3247 bool IsUntyped =
3248 DestPtrTy && DestPtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
3249 SPIRVTypeInst SizeTy = GR->getSPIRVTypeForVReg(NumElemReg);
3250 Register StrideReg =
3251 Call->Arguments.size() > 4
3252 ? Call->Arguments[3]
3253 : (IsUntyped ? GR->buildConstantInt(1, MIRBuilder, SizeTy,
3254 /*EmitIR=*/true)
3255 : buildConstantIntReg32(1, MIRBuilder, GR));
3256
3257 auto MIB = MIRBuilder
3258 .buildInstr(IsUntyped ? SPIRV::OpUntypedGroupAsyncCopyKHR
3259 : SPIRV::OpGroupAsyncCopy)
3260 .addDef(Call->ReturnRegister)
3261 .addUse(TypeReg)
3262 .addUse(Scope)
3263 .addUse(Call->Arguments[0])
3264 .addUse(Call->Arguments[1]);
3265 if (IsUntyped) {
3266 // Element Num Bytes from the deduced element type of dest (or source).
3267 unsigned ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(0));
3268 if (!ElemBytes)
3269 ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(1));
3270 if (!ElemBytes)
3271 report_fatal_error("Could not deduce the element type of an untyped "
3272 "async copy pointer argument");
3273 MIB.addUse(GR->buildConstantInt(ElemBytes, MIRBuilder, SizeTy,
3274 /*EmitIR=*/true));
3275 }
3276 MIB.addUse(NumElemReg);
3277 MIB.addUse(StrideReg);
3278 MIB.addUse(EventReg);
3279 if (NewType)
3280 updateRegType(Call->ReturnRegister, /*Ty=*/nullptr, NewType, GR,
3281 MIRBuilder, MIRBuilder.getMF().getRegInfo());
3282 return true;
3283 }
3284 case SPIRV::OpGroupWaitEvents:
3285 return MIRBuilder.buildInstr(Opcode)
3286 .addUse(Scope)
3287 .addUse(Call->Arguments[0])
3288 .addUse(Call->Arguments[1]);
3289 default:
3290 return false;
3291 }
3292}
3293
3294// Same type S/U/FConvert are invalid. OpSatConvert* are valid and must stay.
3295static bool foldNoOpConvert(unsigned Opcode, const SPIRV::IncomingCall *Call,
3296 MachineIRBuilder &MIRBuilder,
3297 SPIRVGlobalRegistry *GR) {
3298 if (Opcode != SPIRV::OpSConvert && Opcode != SPIRV::OpUConvert &&
3299 Opcode != SPIRV::OpFConvert)
3300 return false;
3301 if (Call->Arguments.size() != 1 ||
3302 GR->getSPIRVTypeForVReg(Call->Arguments[0]) != Call->ReturnType)
3303 return false;
3304 MIRBuilder.buildCopy(Call->ReturnRegister, Call->Arguments[0]);
3305 return true;
3306}
3307
3308static bool generateConvertInst(StringRef DemangledCall,
3310 MachineIRBuilder &MIRBuilder,
3311 SPIRVGlobalRegistry *GR) {
3312 // Lookup the conversion builtin in the TableGen records.
3313 const SPIRV::ConvertBuiltin *Builtin =
3314 SPIRV::lookupConvertBuiltin(Call->Builtin->name(), Call->Builtin->Set);
3315
3316 if (!Builtin && Call->isSpirvOp()) {
3317 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3318 unsigned Opcode =
3319 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3320 if (foldNoOpConvert(Opcode, Call, MIRBuilder, GR))
3321 return true;
3322 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
3323 GR->getSPIRVTypeID(Call->ReturnType));
3324 }
3325
3326 assert(Builtin && "Conversion builtin not found.");
3327
3328 std::string NeedExtMsg; // no errors if empty
3329 bool IsRightComponentsNumber = true; // check if input/output accepts vectors
3330 unsigned Opcode = SPIRV::OpNop;
3331 if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {
3332 // Int -> ...
3333 bool IsSourceSigned =
3334 DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';
3335 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
3336 // Int -> Int
3337 if (Builtin->IsSaturated)
3338 Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS
3339 : SPIRV::OpSatConvertSToU;
3340 else
3341 Opcode = IsSourceSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
3342 } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
3343 SPIRV::OpTypeFloat)) {
3344 // Int -> Float
3345 if (Builtin->IsBfloat16) {
3346 const auto *ST = static_cast<const SPIRVSubtarget *>(
3347 &MIRBuilder.getMF().getSubtarget());
3348 if (!ST->canUseExtension(
3349 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
3350 NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
3351 IsRightComponentsNumber =
3352 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
3353 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
3354 Opcode = SPIRV::OpConvertBF16ToFINTEL;
3355 } else {
3356 Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;
3357 }
3358 }
3359 } else if (GR->isScalarOrVectorOfType(Call->Arguments[0],
3360 SPIRV::OpTypeFloat)) {
3361 // Float -> ...
3362 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
3363 // Float -> Int
3364 if (Builtin->IsBfloat16) {
3365 const auto *ST = static_cast<const SPIRVSubtarget *>(
3366 &MIRBuilder.getMF().getSubtarget());
3367 if (!ST->canUseExtension(
3368 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
3369 NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
3370 IsRightComponentsNumber =
3371 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
3372 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
3373 Opcode = SPIRV::OpConvertFToBF16INTEL;
3374 } else {
3375 Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS
3376 : SPIRV::OpConvertFToU;
3377 }
3378 } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
3379 SPIRV::OpTypeFloat)) {
3380 if (Builtin->IsTF32) {
3381 const auto *ST = static_cast<const SPIRVSubtarget *>(
3382 &MIRBuilder.getMF().getSubtarget());
3383 if (!ST->canUseExtension(
3384 SPIRV::Extension::SPV_INTEL_tensor_float32_conversion))
3385 NeedExtMsg = "SPV_INTEL_tensor_float32_conversion";
3386 IsRightComponentsNumber =
3387 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
3388 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
3389 Opcode = SPIRV::OpRoundFToTF32INTEL;
3390 } else {
3391 // Float -> Float
3392 Opcode = SPIRV::OpFConvert;
3393 }
3394 }
3395 }
3396
3397 StringRef BuiltinName = SPIRV::getConvertBuiltinStr(Builtin->Name);
3398 if (!NeedExtMsg.empty()) {
3399 std::string DiagMsg = std::string(BuiltinName) +
3400 ": the builtin requires the following SPIR-V "
3401 "extension: " +
3402 NeedExtMsg;
3403 report_fatal_error(DiagMsg.c_str(), false);
3404 }
3405 if (!IsRightComponentsNumber) {
3406 std::string DiagMsg =
3407 std::string(BuiltinName) +
3408 ": result and argument must have the same number of components";
3409 report_fatal_error(DiagMsg.c_str(), false);
3410 }
3411 assert(Opcode != SPIRV::OpNop &&
3412 "Conversion between the types not implemented!");
3413
3414 // Must run before the decorations below: a folded conversion has none.
3415 if (foldNoOpConvert(Opcode, Call, MIRBuilder, GR))
3416 return true;
3417
3418 if (Builtin->IsSaturated)
3419 buildOpDecorate(Call->ReturnRegister, MIRBuilder,
3420 SPIRV::Decoration::SaturatedConversion, {});
3421
3422 if (Builtin->IsRounded) {
3423 bool AnyTypeIsFloat =
3424 GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeFloat) ||
3425 GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeFloat);
3426
3427 // Rounding mode decorations are only valid for floating point types.
3428 // Conversion builtins from integer to integer are equivalent to their
3429 // non-rounded counterparts.
3430 if (AnyTypeIsFloat) {
3431 buildOpDecorate(Call->ReturnRegister, MIRBuilder,
3432 SPIRV::Decoration::FPRoundingMode,
3433 {(unsigned)Builtin->RoundingMode});
3434 }
3435 }
3436
3437 MIRBuilder.buildInstr(Opcode)
3438 .addDef(Call->ReturnRegister)
3439 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
3440 .addUse(Call->Arguments[0]);
3441 return true;
3442}
3443
3445 MachineIRBuilder &MIRBuilder,
3446 SPIRVGlobalRegistry *GR) {
3447 // Lookup the vector load/store builtin in the TableGen records.
3448 const SPIRV::VectorLoadStoreBuiltin *Builtin =
3449 SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->name(),
3450 Call->Builtin->Set);
3451 // Build extended instruction.
3452 auto MIB =
3453 MIRBuilder.buildInstr(SPIRV::OpExtInst)
3454 .addDef(Call->ReturnRegister)
3455 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
3456 .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
3457 .addImm(Builtin->Number);
3458 for (auto Argument : Call->Arguments)
3459 MIB.addUse(Argument);
3460 StringRef BuiltinName = SPIRV::getVectorLoadStoreBuiltinStr(Builtin->Name);
3461 if (BuiltinName.contains("load") && Builtin->ElementCount > 1)
3462 MIB.addImm(Builtin->ElementCount);
3463
3464 // Rounding mode should be passed as a last argument in the MI for builtins
3465 // like "vstorea_halfn_r".
3466 if (Builtin->IsRounded)
3467 MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));
3468 return true;
3469}
3470
3472 MachineIRBuilder &MIRBuilder,
3473 SPIRVGlobalRegistry *GR) {
3474 const auto *Builtin = Call->Builtin;
3475 auto *MRI = MIRBuilder.getMRI();
3476 unsigned Opcode =
3477 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3478 const Type *RetTy = GR->getTypeForSPIRVType(Call->ReturnType);
3479 bool IsVoid = RetTy->isVoidTy();
3480 auto MIB = MIRBuilder.buildInstr(Opcode);
3481 Register DestReg;
3482 if (IsVoid) {
3483 LLT PtrTy = MRI->getType(Call->Arguments[0]);
3484 DestReg = MRI->createGenericVirtualRegister(PtrTy);
3485 MRI->setRegClass(DestReg, &SPIRV::pIDRegClass);
3486 SPIRVTypeInst PointeeTy =
3487 GR->getPointeeType(GR->getSPIRVTypeForVReg(Call->Arguments[0]));
3488 MIB.addDef(DestReg);
3489 MIB.addUse(GR->getSPIRVTypeID(PointeeTy));
3490 } else {
3491 MIB.addDef(Call->ReturnRegister);
3492 MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
3493 }
3494 for (unsigned i = IsVoid ? 1 : 0; i < Call->Arguments.size(); ++i) {
3495 Register Arg = Call->Arguments[i];
3496 MachineInstr *DefMI = MRI->getUniqueVRegDef(Arg);
3497 if (DefMI->getOpcode() == TargetOpcode::G_CONSTANT &&
3498 DefMI->getOperand(1).isCImm()) {
3499 MIB.addImm(getIConstVal(Arg, MRI));
3500 } else {
3501 MIB.addUse(Arg);
3502 }
3503 }
3504 if (IsVoid) {
3505 LLT PtrTy = MRI->getType(Call->Arguments[0]);
3506 MachineMemOperand *MMO = MIRBuilder.getMF().getMachineMemOperand(
3508 PtrTy.getSizeInBytes(), Align(4));
3509 MIRBuilder.buildStore(DestReg, Call->Arguments[0], *MMO);
3510 }
3511 return true;
3512}
3513
3515 MachineIRBuilder &MIRBuilder,
3516 SPIRVGlobalRegistry *GR) {
3517 // Lookup the instruction opcode in the TableGen records.
3518 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3519 unsigned Opcode =
3520 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3521 bool IsLoad = Opcode == SPIRV::OpLoad;
3522 // Build the instruction.
3523 auto MIB = MIRBuilder.buildInstr(Opcode);
3524 if (IsLoad) {
3525 MIB.addDef(Call->ReturnRegister);
3526 MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
3527 }
3528 // Add a pointer to the value to load/store.
3529 MIB.addUse(Call->Arguments[0]);
3530 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
3531 // Add a value to store.
3532 if (!IsLoad)
3533 MIB.addUse(Call->Arguments[1]);
3534 // Add optional memory attributes and an alignment.
3535 unsigned NumArgs = Call->Arguments.size();
3536 if ((IsLoad && NumArgs >= 2) || NumArgs >= 3)
3537 MIB.addImm(getIConstVal(Call->Arguments[IsLoad ? 1 : 2], MRI));
3538 if ((IsLoad && NumArgs >= 3) || NumArgs >= 4)
3539 MIB.addImm(getIConstVal(Call->Arguments[IsLoad ? 2 : 3], MRI));
3540 return true;
3541}
3542
3543namespace SPIRV {
3544// Try to find a builtin function attributes by a demangled function name and
3545// return a tuple <builtin group, op code, ext instruction number>, or a special
3546// tuple value <-1, 0, 0> if the builtin function is not found.
3547// Not all builtin functions are supported, only those with a ready-to-use op
3548// code or instruction number defined in TableGen.
3549// TODO: consider a major rework of mapping demangled calls into a builtin
3550// functions to unify search and decrease number of individual cases.
3551std::tuple<int, unsigned, unsigned>
3553 SPIRV::InstructionSet::InstructionSet Set) {
3554 Register Reg;
3556 std::unique_ptr<const IncomingCall> Call =
3557 lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args);
3558 if (!Call)
3559 return std::make_tuple(-1, 0, 0);
3560
3561 switch (Call->Builtin->Group) {
3562 case SPIRV::Relational:
3563 case SPIRV::Atomic:
3564 case SPIRV::Barrier:
3565 case SPIRV::CastToPtr:
3566 case SPIRV::ImageMiscQuery:
3567 case SPIRV::SpecConstant:
3568 case SPIRV::Enqueue:
3569 case SPIRV::AsyncCopy:
3570 case SPIRV::LoadStore:
3571 case SPIRV::CoopMatr:
3572 case SPIRV::Arithmetic:
3573 if (const auto *R = SPIRV::lookupNativeBuiltin(Call->Builtin->name(),
3574 Call->Builtin->Set))
3575 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3576 break;
3577 case SPIRV::Extended:
3578 if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->name(),
3579 Call->Builtin->Set))
3580 return std::make_tuple(Call->Builtin->Group, 0, R->Number);
3581 break;
3582 case SPIRV::VectorLoadStore:
3583 if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(
3584 Call->Builtin->name(), Call->Builtin->Set))
3585 return std::make_tuple(SPIRV::Extended, 0, R->Number);
3586 break;
3587 case SPIRV::Group:
3588 if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->name()))
3589 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3590 break;
3591 case SPIRV::AtomicFloating:
3592 if (const auto *R =
3593 SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->name()))
3594 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3595 break;
3596 case SPIRV::IntelSubgroups:
3597 if (const auto *R =
3598 SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->name()))
3599 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3600 break;
3601 case SPIRV::GroupUniform:
3602 if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->name()))
3603 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3604 break;
3605 case SPIRV::IntegerDot:
3606 if (const auto *R =
3607 SPIRV::lookupIntegerDotProductBuiltin(Call->Builtin->name()))
3608 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3609 break;
3610 case SPIRV::WriteImage:
3611 return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0);
3612 case SPIRV::Select:
3613 return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0);
3614 case SPIRV::Construct:
3615 return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct,
3616 0);
3617 case SPIRV::KernelClock:
3618 return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0);
3619 default:
3620 return std::make_tuple(-1, 0, 0);
3621 }
3622 return std::make_tuple(-1, 0, 0);
3623}
3624
3625/// Checks that scalar/vector numeric arguments of \p Call match the types
3626/// implied by their mangling in \p DemangledCall. Pointers and opaque
3627/// builtin types (images, samplers, pipes, etc.) are not validated here, as
3628/// mangling does not enforce their exact spelling.
3629///
3630/// \returns false if a numeric argument's SPIR-V type disagrees with the
3631/// type implied by the mangled name, true otherwise.
3633 StringRef DemangledCall,
3635 const CallBase &CB) {
3636 if (Call->isSpirvOp())
3637 return true;
3638
3639 SmallVector<StringRef, 10> ArgTypeStrs;
3640 if (!SPIRV::parseBuiltinTypeStr(ArgTypeStrs, DemangledCall, Ctx))
3641 return true;
3642
3643 unsigned ArgBase = CB.hasStructRetAttr() ? 1 : 0;
3644 if (Call->Arguments.size() < ArgBase)
3645 return true;
3646 unsigned NumMangledArgs = Call->Arguments.size() - ArgBase;
3647 unsigned NumArgsToCheck =
3648 std::min<unsigned>(NumMangledArgs, ArgTypeStrs.size());
3649 for (unsigned ArgIdx = 0; ArgIdx < NumArgsToCheck; ++ArgIdx) {
3650 StringRef ArgTypeStr = ArgTypeStrs[ArgIdx].trim();
3651 // Opaque/builtin OpenCL and SPIR-V types (images, samplers, pipes,
3652 // reserve_id, etc.) are not validated here, as mangling does not enforce
3653 // their exact spelling, and some builtin type names have no TableGen
3654 // record and would otherwise abort compilation when parsed.
3655 if (hasBuiltinTypePrefix(ArgTypeStr))
3656 continue;
3657
3658 Type *ExpectedType = SPIRV::parseBuiltinCallArgumentType(ArgTypeStr, Ctx);
3659 if (!ExpectedType || ExpectedType->isVoidTy() ||
3660 ExpectedType->isPointerTy() || ExpectedType->isTargetExtTy())
3661 continue;
3662
3663 SPIRVTypeInst ArgType =
3664 GR->getSPIRVTypeForVReg(Call->Arguments[ArgIdx + ArgBase]);
3665 if (!ArgType)
3666 continue;
3667 unsigned ArgTypeOpcode = ArgType->getOpcode();
3668 if (ArgTypeOpcode != SPIRV::OpTypeInt &&
3669 ArgTypeOpcode != SPIRV::OpTypeFloat &&
3670 ArgTypeOpcode != SPIRV::OpTypeBool &&
3671 ArgTypeOpcode != SPIRV::OpTypeVector)
3672 continue;
3673
3674 auto *ExpectedVecType = dyn_cast<VectorType>(ExpectedType);
3675 Type *ExpectedScalarType =
3676 ExpectedVecType ? ExpectedVecType->getElementType() : ExpectedType;
3677 SPIRVTypeInst ArgScalarType = GR->getScalarOrVectorComponentType(ArgType);
3678 if (!ArgScalarType)
3679 continue;
3680
3681 bool ExpectedIsInt = ExpectedScalarType->isIntegerTy();
3682 unsigned ArgOpcode = ArgScalarType->getOpcode();
3683 bool ArgIsInt =
3684 ArgOpcode == SPIRV::OpTypeInt || ArgOpcode == SPIRV::OpTypeBool;
3685
3686 if (ExpectedIsInt != ArgIsInt)
3687 return false;
3688
3689 unsigned ExpectedElts =
3690 ExpectedVecType ? ExpectedVecType->getElementCount().getFixedValue()
3691 : 1;
3692 if (ExpectedElts != GR->getScalarOrVectorComponentCount(ArgType))
3693 return false;
3694 }
3695 return true;
3696}
3697
3698std::optional<bool> lowerBuiltin(StringRef DemangledCall,
3699 SPIRV::InstructionSet::InstructionSet Set,
3700 MachineIRBuilder &MIRBuilder,
3701 const Register OrigRet, const Type *OrigRetTy,
3702 const SmallVectorImpl<Register> &Args,
3703 SPIRVGlobalRegistry *GR, const CallBase &CB) {
3704 LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");
3705
3706 // Lookup the builtin in the TableGen records.
3707 SPIRVTypeInst SpvType = GR->getSPIRVTypeForVReg(OrigRet);
3708 assert(SpvType && "Inconsistent return register: expected valid type info");
3709 std::unique_ptr<const IncomingCall> Call =
3710 lookupBuiltin(DemangledCall, Set, OrigRet, SpvType, Args);
3711
3712 if (!Call) {
3713 LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
3714 return std::nullopt;
3715 }
3716
3717 // Check if the provided args meet the builtin requirements. If not, treat
3718 // the call as a regular function call rather than crashing.
3719 if (Args.size() < Call->Builtin->MinNumArgs) {
3720 LLVM_DEBUG(dbgs() << "Too few arguments for builtin " << DemangledCall
3721 << ": expected at least " << Call->Builtin->MinNumArgs
3722 << ", got " << Args.size()
3723 << "; treating as a normal function\n");
3724 return std::nullopt;
3725 }
3726 if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs) {
3727 LLVM_DEBUG(dbgs() << "Too many arguments for builtin " << DemangledCall
3728 << ": expected at most " << Call->Builtin->MaxNumArgs
3729 << ", got " << Args.size()
3730 << "; treating as a normal function\n");
3731 return std::nullopt;
3732 }
3733
3734 // Check that argument types match what the mangling implies. If not
3735 // (e.g. broken mangling), treat the call as a regular function call
3736 // rather than crashing.
3737 if (!demangledArgTypesMatchIR(Call.get(), DemangledCall, GR,
3738 MIRBuilder.getContext(), CB)) {
3739 LLVM_DEBUG(dbgs() << "Argument types do not match mangled types for "
3740 << "builtin " << DemangledCall
3741 << "; treating as a normal function\n");
3742 return std::nullopt;
3743 }
3744
3745 // Match the builtin with implementation based on the grouping.
3746 switch (Call->Builtin->Group) {
3747 case SPIRV::Extended:
3748 return generateExtInst(Call.get(), MIRBuilder, GR, CB);
3749 case SPIRV::Relational:
3750 return generateRelationalInst(Call.get(), MIRBuilder, GR);
3751 case SPIRV::Group:
3752 return generateGroupInst(Call.get(), MIRBuilder, GR);
3753 case SPIRV::Variable:
3754 return generateBuiltinVar(Call.get(), MIRBuilder, GR);
3755 case SPIRV::Atomic:
3756 return generateAtomicInst(Call.get(), MIRBuilder, GR);
3757 case SPIRV::AtomicFloating:
3758 return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);
3759 case SPIRV::Barrier:
3760 return generateBarrierInst(Call.get(), MIRBuilder, GR);
3761 case SPIRV::CastToPtr:
3762 return generateCastToPtrInst(Call.get(), MIRBuilder, GR);
3763 case SPIRV::Dot:
3764 case SPIRV::IntegerDot:
3765 return generateDotOrFMulInst(DemangledCall, Call.get(), MIRBuilder, GR);
3766 case SPIRV::Wave:
3767 return generateWaveInst(Call.get(), MIRBuilder, GR);
3768 case SPIRV::ICarryBorrow:
3769 return generateICarryBorrowInst(Call.get(), MIRBuilder, GR, CB);
3770 case SPIRV::MulExtended:
3771 return generateMulExtendedInst(Call.get(), MIRBuilder, GR, CB);
3772 case SPIRV::Arithmetic:
3773 return generateArithmeticInst(Call.get(), MIRBuilder, GR);
3774 case SPIRV::GetQuery:
3775 return generateGetQueryInst(Call.get(), MIRBuilder, GR);
3776 case SPIRV::ImageSizeQuery:
3777 return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);
3778 case SPIRV::ImageMiscQuery:
3779 return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);
3780 case SPIRV::ReadImage:
3781 return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
3782 case SPIRV::WriteImage:
3783 return generateWriteImageInst(Call.get(), MIRBuilder, GR);
3784 case SPIRV::SampleImage:
3785 return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
3786 case SPIRV::Select:
3787 return generateSelectInst(Call.get(), MIRBuilder);
3788 case SPIRV::Construct:
3789 return generateConstructInst(Call.get(), MIRBuilder, GR);
3790 case SPIRV::SpecConstant:
3791 return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
3792 case SPIRV::Enqueue:
3793 return generateEnqueueInst(Call.get(), MIRBuilder, GR, CB);
3794 case SPIRV::AsyncCopy:
3795 return generateAsyncCopy(Call.get(), MIRBuilder, GR, CB);
3796 case SPIRV::Convert:
3797 return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
3798 case SPIRV::VectorLoadStore:
3799 return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);
3800 case SPIRV::LoadStore:
3801 return generateLoadStoreInst(Call.get(), MIRBuilder, GR);
3802 case SPIRV::IntelSubgroups:
3803 return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR);
3804 case SPIRV::GroupUniform:
3805 return generateGroupUniformInst(Call.get(), MIRBuilder, GR);
3806 case SPIRV::KernelClock:
3807 return generateKernelClockInst(Call.get(), MIRBuilder, GR);
3808 case SPIRV::CoopMatr:
3809 return generateCoopMatrInst(Call.get(), MIRBuilder, GR);
3810 case SPIRV::ExtendedBitOps:
3811 return generateExtendedBitOpsInst(Call.get(), MIRBuilder, GR);
3812 case SPIRV::BindlessINTEL:
3813 return generateBindlessImageINTELInst(Call.get(), MIRBuilder, GR);
3814 case SPIRV::TernaryBitwiseINTEL:
3815 return generateTernaryBitwiseFunctionINTELInst(Call.get(), MIRBuilder, GR);
3816 case SPIRV::Block2DLoadStore:
3817 return generate2DBlockIOINTELInst(Call.get(), MIRBuilder, GR);
3818 case SPIRV::Pipe:
3819 return generatePipeInst(Call.get(), MIRBuilder, GR);
3820 case SPIRV::PredicatedLoadStore:
3821 return generatePredicatedLoadStoreInst(Call.get(), MIRBuilder, GR);
3822 case SPIRV::BlockingPipes:
3823 return generateBlockingPipesInst(Call.get(), MIRBuilder, GR);
3824 case SPIRV::ArbitraryPrecisionFixedPoint:
3825 return generateAPFixedPointInst(Call.get(), MIRBuilder, GR, CB);
3826 case SPIRV::ImageChannelDataTypes:
3827 return generateImageChannelDataTypeInst(Call.get(), MIRBuilder, GR);
3828 case SPIRV::ArbitraryFloatingPoint:
3829 return generateAFPInst(Call.get(), MIRBuilder, GR);
3830 }
3831 return false;
3832}
3833
3835 // Parse strings representing OpenCL builtin types.
3836 if (hasBuiltinTypePrefix(TypeStr)) {
3837 // OpenCL builtin types in demangled call strings have the following format:
3838 // e.g. ocl_image2d_ro
3839 [[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_");
3840 assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix");
3841
3842 // Check if this is pointer to a builtin type and not just pointer
3843 // representing a builtin type. In case it is a pointer to builtin type,
3844 // this will require additional handling in the method calling
3845 // parseBuiltinCallArgumentBaseType(...) as this function only retrieves the
3846 // base types.
3847 if (TypeStr.ends_with("*"))
3848 TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));
3849
3850 return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",
3851 Ctx);
3852 }
3853
3854 // Parse type name in either "typeN" or "type vector[N]" format, where
3855 // N is the number of elements of the vector.
3856 Type *BaseType;
3857 unsigned VecElts = 0;
3858
3859 BaseType = parseBasicTypeName(TypeStr, Ctx);
3860 if (!BaseType)
3861 // Unable to recognize SPIRV type name.
3862 return nullptr;
3863
3864 // Handle "typeN*" or "type vector[N]*".
3865 TypeStr.consume_back("*");
3866
3867 if (TypeStr.consume_front(" vector["))
3868 TypeStr = TypeStr.substr(0, TypeStr.find(']'));
3869
3870 TypeStr.getAsInteger(10, VecElts);
3871 if (VecElts > 0)
3873 BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);
3874
3875 return BaseType;
3876}
3877
3879 StringRef DemangledCall, LLVMContext &Ctx) {
3880 auto Pos1 = DemangledCall.find('(');
3881 if (Pos1 == StringRef::npos)
3882 return false;
3883 auto Pos2 = DemangledCall.find(')');
3884 if (Pos2 == StringRef::npos || Pos1 > Pos2)
3885 return false;
3886 DemangledCall.slice(Pos1 + 1, Pos2)
3887 .split(BuiltinArgsTypeStrs, ',', -1, false);
3888 return true;
3889}
3890
3891Type *parseBuiltinCallArgumentBaseType(StringRef DemangledCall, unsigned ArgIdx,
3892 LLVMContext &Ctx) {
3893 SmallVector<StringRef, 10> BuiltinArgsTypeStrs;
3894 parseBuiltinTypeStr(BuiltinArgsTypeStrs, DemangledCall, Ctx);
3895 if (ArgIdx >= BuiltinArgsTypeStrs.size())
3896 return nullptr;
3897 StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim();
3898 return parseBuiltinCallArgumentType(TypeStr, Ctx);
3899}
3900
3905
3906#define GET_BuiltinTypes_DECL
3907#define GET_BuiltinTypes_IMPL
3908
3913
3914#define GET_OpenCLTypes_DECL
3915#define GET_OpenCLTypes_IMPL
3916
3917#include "SPIRVGenTables.inc"
3918} // namespace SPIRV
3919
3920//===----------------------------------------------------------------------===//
3921// Misc functions for parsing builtin types.
3922//===----------------------------------------------------------------------===//
3923
3925 if (Name.starts_with("void"))
3926 return Type::getVoidTy(Context);
3927 else if (Name.starts_with("int") || Name.starts_with("uint"))
3928 return Type::getInt32Ty(Context);
3929 else if (Name.starts_with("bfloat"))
3930 return Type::getBFloatTy(Context);
3931 else if (Name.starts_with("float"))
3932 return Type::getFloatTy(Context);
3933 else if (Name.starts_with("half"))
3934 return Type::getHalfTy(Context);
3935 else if (Name.starts_with("double"))
3936 return Type::getDoubleTy(Context);
3937 report_fatal_error("Unable to recognize type!");
3938}
3939
3940//===----------------------------------------------------------------------===//
3941// Implementation functions for builtin types.
3942//===----------------------------------------------------------------------===//
3943
3944static SPIRVTypeInst
3946 const SPIRV::BuiltinType *TypeRecord,
3947 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
3948 unsigned Opcode = TypeRecord->Opcode;
3949 // Create or get an existing type from GlobalRegistry.
3950 return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode);
3951}
3952
3954 SPIRVGlobalRegistry *GR) {
3955 // Create or get an existing type from GlobalRegistry.
3956 return GR->getOrCreateOpTypeSampler(MIRBuilder);
3957}
3958
3959static SPIRVTypeInst getPipeType(const TargetExtType *ExtensionType,
3960 MachineIRBuilder &MIRBuilder,
3961 SPIRVGlobalRegistry *GR) {
3962 assert(ExtensionType->getNumIntParameters() == 1 &&
3963 "Invalid number of parameters for SPIR-V pipe builtin!");
3964 // Create or get an existing type from GlobalRegistry.
3965 return GR->getOrCreateOpTypePipe(MIRBuilder,
3966 SPIRV::AccessQualifier::AccessQualifier(
3967 ExtensionType->getIntParameter(0)));
3968}
3969
3970static SPIRVTypeInst getCoopMatrType(const TargetExtType *ExtensionType,
3971 MachineIRBuilder &MIRBuilder,
3972 SPIRVGlobalRegistry *GR) {
3973 assert(ExtensionType->getNumIntParameters() == 4 &&
3974 "Invalid number of parameters for SPIR-V coop matrices builtin!");
3975 assert(ExtensionType->getNumTypeParameters() == 1 &&
3976 "SPIR-V coop matrices builtin type must have a type parameter!");
3977 SPIRVTypeInst ElemType =
3978 GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder,
3979 SPIRV::AccessQualifier::ReadWrite, true);
3980 // Create or get an existing type from GlobalRegistry.
3981 return GR->getOrCreateOpTypeCoopMatr(
3982 MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0),
3983 ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
3984 ExtensionType->getIntParameter(3), true);
3985}
3986
3988 MachineIRBuilder &MIRBuilder,
3989 SPIRVGlobalRegistry *GR) {
3990 SPIRVTypeInst OpaqueImageType = GR->getImageType(
3991 OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder);
3992 // Create or get an existing type from GlobalRegistry.
3993 return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
3994}
3995
3997 MachineIRBuilder &MIRBuilder,
3998 SPIRVGlobalRegistry *GR) {
3999 assert(ExtensionType->getNumIntParameters() == 3 &&
4000 "Inline SPIR-V type builtin takes an opcode, size, and alignment "
4001 "parameter");
4002 auto Opcode = ExtensionType->getIntParameter(0);
4003
4005 for (Type *Param : ExtensionType->type_params()) {
4006 if (const TargetExtType *ParamEType = dyn_cast<TargetExtType>(Param)) {
4007 if (ParamEType->getName() == "spirv.IntegralConstant") {
4008 assert(ParamEType->getNumTypeParameters() == 1 &&
4009 "Inline SPIR-V integral constant builtin must have a type "
4010 "parameter");
4011 assert(ParamEType->getNumIntParameters() == 1 &&
4012 "Inline SPIR-V integral constant builtin must have a "
4013 "value parameter");
4014
4015 auto OperandValue = ParamEType->getIntParameter(0);
4016 auto *OperandType = ParamEType->getTypeParameter(0);
4017
4018 SPIRVTypeInst OperandSPIRVType = GR->getOrCreateSPIRVType(
4019 OperandType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, true);
4020
4022 OperandValue, MIRBuilder, OperandSPIRVType, true)));
4023 continue;
4024 } else if (ParamEType->getName() == "spirv.Literal") {
4025 assert(ParamEType->getNumTypeParameters() == 0 &&
4026 "Inline SPIR-V literal builtin does not take type "
4027 "parameters");
4028 assert(ParamEType->getNumIntParameters() == 1 &&
4029 "Inline SPIR-V literal builtin must have an integer "
4030 "parameter");
4031
4032 auto OperandValue = ParamEType->getIntParameter(0);
4033
4034 Operands.push_back(MCOperand::createImm(OperandValue));
4035 continue;
4036 }
4037 }
4038 SPIRVTypeInst TypeOperand = GR->getOrCreateSPIRVType(
4039 Param, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, true);
4040 Operands.push_back(MCOperand::createReg(GR->getSPIRVTypeID(TypeOperand)));
4041 }
4042
4043 return GR->getOrCreateUnknownType(ExtensionType, MIRBuilder, Opcode,
4044 Operands);
4045}
4046
4048 MachineIRBuilder &MIRBuilder,
4049 SPIRVGlobalRegistry *GR) {
4050 assert(ExtensionType->getNumTypeParameters() == 1 &&
4051 "Vulkan buffers have exactly one type for the type of the buffer.");
4052 assert(ExtensionType->getNumIntParameters() == 2 &&
4053 "Vulkan buffer have 2 integer parameters: storage class and is "
4054 "writable.");
4055
4056 auto *T = ExtensionType->getTypeParameter(0);
4057 auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
4058 ExtensionType->getIntParameter(0));
4059 bool IsWritable = ExtensionType->getIntParameter(1);
4060 return GR->getOrCreateVulkanBufferType(MIRBuilder, T, SC, IsWritable);
4061}
4062
4063static SPIRVTypeInst
4065 MachineIRBuilder &MIRBuilder,
4066 SPIRVGlobalRegistry *GR) {
4067 assert(ExtensionType->getNumTypeParameters() == 1 &&
4068 "Vulkan push constants have exactly one type as argument.");
4069 auto *T = ExtensionType->getTypeParameter(0);
4070 return GR->getOrCreateVulkanPushConstantType(MIRBuilder, T);
4071}
4072
4073static SPIRVTypeInst getLayoutType(const TargetExtType *ExtensionType,
4074 MachineIRBuilder &MIRBuilder,
4075 SPIRVGlobalRegistry *GR) {
4076 return GR->getOrCreateLayoutType(MIRBuilder, ExtensionType);
4077}
4078
4079namespace SPIRV {
4081 LLVMContext &Context) {
4082 StringRef NameWithParameters = TypeName;
4083
4084 // Pointers-to-opaque-structs representing OpenCL types are first translated
4085 // to equivalent SPIR-V types. OpenCL builtin type names should have the
4086 // following format: e.g. %opencl.event_t
4087 if (NameWithParameters.starts_with("opencl.")) {
4088 const SPIRV::OpenCLType *OCLTypeRecord =
4089 SPIRV::lookupOpenCLType(NameWithParameters);
4090 if (!OCLTypeRecord)
4091 report_fatal_error("Missing TableGen record for OpenCL type: " +
4092 NameWithParameters);
4093 NameWithParameters =
4094 SPIRV::getOpenCLTypeStr(OCLTypeRecord->SpirvTypeLiteral);
4095 // Continue with the SPIR-V builtin type...
4096 }
4097
4098 // Names of the opaque structs representing a SPIR-V builtins without
4099 // parameters should have the following format: e.g. %spirv.Event
4100 assert(NameWithParameters.starts_with("spirv.") &&
4101 "Unknown builtin opaque type!");
4102
4103 // Parameterized SPIR-V builtins names follow this format:
4104 // e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._0
4105 if (!NameWithParameters.contains('_'))
4106 return TargetExtType::get(Context, NameWithParameters);
4107
4108 SmallVector<StringRef> Parameters;
4109 unsigned BaseNameLength = NameWithParameters.find('_') - 1;
4110 SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_");
4111
4112 SmallVector<Type *, 1> TypeParameters;
4113 bool HasTypeParameter = !isDigit(Parameters[0][0]);
4114 if (HasTypeParameter)
4115 TypeParameters.push_back(parseTypeString(Parameters[0], Context));
4116 SmallVector<unsigned> IntParameters;
4117 for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) {
4118 unsigned IntParameter = 0;
4119 bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter);
4120 (void)ValidLiteral;
4121 assert(ValidLiteral &&
4122 "Invalid format of SPIR-V builtin parameter literal!");
4123 IntParameters.push_back(IntParameter);
4124 }
4125 return TargetExtType::get(Context,
4126 NameWithParameters.substr(0, BaseNameLength),
4127 TypeParameters, IntParameters);
4128}
4129
4131lowerBuiltinType(const Type *OpaqueType,
4132 SPIRV::AccessQualifier::AccessQualifier AccessQual,
4133 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
4134 // In LLVM IR, SPIR-V and OpenCL builtin types are represented as either
4135 // target(...) target extension types or pointers-to-opaque-structs. The
4136 // approach relying on structs is deprecated and works only in the non-opaque
4137 // pointer mode (-opaque-pointers=0).
4138 // In order to maintain compatibility with LLVM IR generated by older versions
4139 // of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are
4140 // "translated" to target extension types. This translation is temporary and
4141 // will be removed in the future release of LLVM.
4143 if (!BuiltinType)
4145 OpaqueType->getStructName().str(), MIRBuilder.getContext());
4146
4147 unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
4148
4149 StringRef Name = BuiltinType->getName();
4150 LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
4151
4152 SPIRVTypeInst TargetType = nullptr;
4153 if (Name == "spirv.Type") {
4154 TargetType = getInlineSpirvType(BuiltinType, MIRBuilder, GR);
4155 } else if (Name == "spirv.VulkanBuffer") {
4156 TargetType = getVulkanBufferType(BuiltinType, MIRBuilder, GR);
4157 } else if (Name == "spirv.Padding") {
4158 TargetType = GR->getOrCreatePaddingType(MIRBuilder);
4159 } else if (Name == "spirv.PushConstant") {
4160 TargetType = getVulkanPushConstantType(BuiltinType, MIRBuilder, GR);
4161 } else if (Name == "spirv.Layout") {
4162 TargetType = getLayoutType(BuiltinType, MIRBuilder, GR);
4163 } else {
4164 // Lookup the demangled builtin type in the TableGen records.
4165 const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
4166 if (!TypeRecord)
4167 report_fatal_error("Missing TableGen record for builtin type: " + Name);
4168
4169 // "Lower" the BuiltinType into TargetType. The following get<...>Type
4170 // methods use the implementation details from TableGen records or
4171 // TargetExtType parameters to either create a new OpType<...> machine
4172 // instruction or get an existing equivalent SPIRV type from
4173 // GlobalRegistry.
4174
4175 switch (TypeRecord->Opcode) {
4176 case SPIRV::OpTypeImage:
4177 TargetType = GR->getImageType(BuiltinType, AccessQual, MIRBuilder);
4178 break;
4179 case SPIRV::OpTypePipe:
4180 TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
4181 break;
4182 case SPIRV::OpTypeDeviceEvent:
4183 TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
4184 break;
4185 case SPIRV::OpTypeSampler:
4186 TargetType = getSamplerType(MIRBuilder, GR);
4187 break;
4188 case SPIRV::OpTypeSampledImage:
4189 TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
4190 break;
4191 case SPIRV::OpTypeCooperativeMatrixKHR:
4192 TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
4193 break;
4194 default:
4195 TargetType =
4196 getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
4197 break;
4198 }
4199 }
4200
4201 // Emit OpName instruction if a new OpType<...> instruction was added
4202 // (equivalent type was not found in GlobalRegistry).
4203 if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
4204 buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder);
4205
4206 return TargetType;
4207}
4208
4210 const DemangledBuiltin *Builtin = lookupBuiltin(Name, OpenCL_std);
4211 if (!Builtin)
4212 return false;
4213 return Builtin->Group == Pipe || Builtin->Group == CastToPtr ||
4214 Builtin->Group == BlockingPipes;
4215}
4216} // namespace SPIRV
4217} // namespace llvm
MachineInstrBuilder MachineInstrBuilder & DefMI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned Imm
unsigned uint64_t
AMDGPU Lower Kernel Arguments
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define T
SI Fold Operands
BaseType
A given derived pointer can have multiple base pointers through phi/selects.
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static const fltSemantics & IEEEsingle()
Definition APFloat.h:304
APInt bitcastToAPInt() const
Definition APFloat.h:1475
static APFloat getZero(const fltSemantics &Sem, bool Negative=false)
Factory for Positive and Negative Zero.
Definition APFloat.h:1183
static APInt getAllOnes(unsigned numBits)
Return an APInt of a specified width with all bits set.
Definition APInt.h:230
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1560
This class represents an incoming formal argument to a Function.
Definition Argument.h:32
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
LLVM_ABI FPClassTest getParamNoFPClass(unsigned i) const
Extract a test mask for disallowed floating-point value classes for the parameter.
LLVM_ABI FPClassTest getRetNoFPClass() const
Extract a test mask for disallowed floating-point value classes for the return value.
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
Value * getArgOperand(unsigned i) const
bool hasStructRetAttr() const
Determine if the call returns a structure through first pointer argument.
@ ICMP_ULT
unsigned less than
Definition InstrTypes.h:765
@ ICMP_NE
not equal
Definition InstrTypes.h:762
const APFloat & getValueAPF() const
Definition Constants.h:463
const APInt & getValue() const
Return the constant as an APInt value reference.
Definition Constants.h:159
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
Tagged union holding either a T or a Error.
Definition Error.h:485
Class to represent fixed width SIMD vectors.
Class to represent function types.
unsigned getNumParams() const
Return the number of fixed parameters this function type requires.
Type * getParamType(unsigned i) const
Parameter type accessors.
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:356
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:338
static constexpr LLT vector(ElementCount EC, unsigned ScalarSizeInBits)
Get a low-level vector of some number of elements and element width.
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
constexpr bool isVector() const
static constexpr LLT pointer(unsigned AddressSpace, unsigned SizeInBits)
Get a low-level pointer in the given address space.
static constexpr LLT fixed_vector(unsigned NumElements, unsigned ScalarSizeInBits)
Get a low-level fixed-width vector of some number of elements and element width.
constexpr TypeSize getSizeInBytes() const
Returns the total size of the type in bytes, i.e.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
static MCOperand createReg(MCRegister Reg)
Definition MCInst.h:138
static MCOperand createImm(int64_t Val)
Definition MCInst.h:145
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
MachineMemOperand * getMachineMemOperand(MachinePointerInfo PtrInfo, MachineMemOperand::Flags F, LLT MemTy, Align BaseAlignment, const MMOMetadata &Metadata=MMOMetadata(), SyncScope::ID SSID=SyncScope::System, AtomicOrdering Ordering=AtomicOrdering::NotAtomic, AtomicOrdering FailureOrdering=AtomicOrdering::NotAtomic)
getMachineMemOperand - Allocate a new MachineMemOperand.
Helper class to build MachineInstr.
LLVMContext & getContext() const
MachineInstrBuilder buildSelect(const DstOp &Res, const SrcOp &Tst, const SrcOp &Op0, const SrcOp &Op1, std::optional< unsigned > Flags=std::nullopt)
Build and insert a Res = G_SELECT Tst, Op0, Op1.
MachineInstrBuilder buildICmp(CmpInst::Predicate Pred, const DstOp &Res, const SrcOp &Op0, const SrcOp &Op1, std::optional< unsigned > Flags=std::nullopt)
Build and insert a Res = G_ICMP Pred, Op0, Op1.
MachineBasicBlock::iterator getInsertPt()
Current insertion point for new instructions.
MachineInstrBuilder buildIntrinsic(Intrinsic::ID ID, ArrayRef< Register > Res, bool HasSideEffects, bool isConvergent)
Build and insert a G_INTRINSIC instruction.
MachineInstrBuilder buildLoad(const DstOp &Res, const SrcOp &Addr, MachineMemOperand &MMO)
Build and insert Res = G_LOAD Addr, MMO.
MachineInstrBuilder buildZExtOrTrunc(const DstOp &Res, const SrcOp &Op)
Build and insert Res = G_ZEXT Op, Res = G_TRUNC Op, or Res = COPY Op depending on the differing sizes...
MachineInstrBuilder buildStore(const SrcOp &Val, const SrcOp &Addr, MachineMemOperand &MMO)
Build and insert G_STORE Val, Addr, MMO.
MachineInstrBuilder buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
MachineFunction & getMF()
Getter for the function we currently build.
const MachineBasicBlock & getMBB() const
Getter for the basic block we currently build.
MachineRegisterInfo * getMRI()
Getter for MRI.
MachineInstrBuilder buildCopy(const DstOp &Res, const SrcOp &Op)
Build and insert Res = COPY Op.
const DataLayout & getDataLayout() const
virtual MachineInstrBuilder buildConstant(const DstOp &Res, const ConstantInt &Val)
Build and insert Res = G_CONSTANT Val.
Register getReg(unsigned Idx) const
Get the register for the operand index.
const MachineInstrBuilder & addUse(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register use operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addDef(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register definition operand.
MachineInstr * getInstr() const
If conversion operators fail, use this method to get the MachineInstr explicitly.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
unsigned getNumOperands() const
Retuns the total number of operands.
LLVM_ABI void copyIRFlags(const Instruction &I)
Copy all flags to MachineInst MIFlags.
void setFlag(MIFlag Flag)
Set a MI flag.
const MachineOperand & getOperand(unsigned i) const
A description of a memory reference used in the backend.
@ MOLoad
The memory access reads data.
@ MOStore
The memory access writes data.
MachineOperand class - Representation of each machine instruction operand.
const ConstantInt * getCImm() const
bool isCImm() const
isCImm - Test if this is a MO_CImmediate operand.
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
const MDNode * getMetadata() const
Register getReg() const
getReg - Returns the register number.
const ConstantFP * getFPImm() const
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
LLT getType(Register Reg) const
Get the low-level type of Reg or LLT{} if Reg is not a generic (target independent) virtual register.
LLVM_ABI void setType(Register VReg, LLT Ty)
Set the low-level type of VReg to Ty.
LLVM_ABI void setRegClass(Register Reg, const TargetRegisterClass *RC)
setRegClass - Set the register class of the specified virtual register.
LLVM_ABI Register createGenericVirtualRegister(LLT Ty, StringRef Name="")
Create and return a new generic virtual register with low-level type Ty.
const TargetRegisterClass * getRegClassOrNull(Register Reg) const
Return the register class of Reg, or null if Reg has not been assigned a register class yet.
unsigned getNumVirtRegs() const
getNumVirtRegs - Return the number of virtual registers created.
LLVM_ABI LLVM_READONLY MachineInstr * getUniqueVRegDef(Register Reg) const
getUniqueVRegDef - Return the unique machine instr that defines the specified virtual register or nul...
Wrapper class representing virtual and physical registers.
Definition Register.h:20
constexpr bool isValid() const
Definition Register.h:112
SPIRVTypeInst getImageType(const TargetExtType *ExtensionType, const SPIRV::AccessQualifier::AccessQualifier Qualifier, MachineIRBuilder &MIRBuilder)
SPIRVTypeInst getOrCreateOpTypeSampledImage(SPIRVTypeInst ImageType, MachineIRBuilder &MIRBuilder)
void assignSPIRVTypeToVReg(SPIRVTypeInst Type, Register VReg, const MachineFunction &MF)
SPIRVTypeInst getOrCreateSPIRVPointerType(const Type *BaseType, MachineIRBuilder &MIRBuilder, SPIRV::StorageClass::StorageClass SC, bool ForceTyped=false)
const TargetRegisterClass * getRegClass(SPIRVTypeInst SpvType) const
unsigned getScalarOrVectorBitWidth(SPIRVTypeInst Type) const
SPIRVTypeInst getOrCreateSPIRVIntegerType(unsigned BitWidth, MachineIRBuilder &MIRBuilder)
SPIRVTypeInst getOrCreateSPIRVVectorType(SPIRVTypeInst BaseType, unsigned NumElements, MachineIRBuilder &MIRBuilder, bool EmitIR)
SPIRVTypeInst getOrCreateSPIRVTypeByName(StringRef TypeStr, MachineIRBuilder &MIRBuilder, bool EmitIR, SPIRV::StorageClass::StorageClass SC=SPIRV::StorageClass::Function, SPIRV::AccessQualifier::AccessQualifier AQ=SPIRV::AccessQualifier::ReadWrite)
Register buildGlobalVariable(Register Reg, SPIRVTypeInst BaseType, StringRef Name, const GlobalValue *GV, SPIRV::StorageClass::StorageClass Storage, const MachineInstr *Init, bool IsConst, const std::optional< SPIRV::LinkageType::LinkageType > &LinkageType, MachineIRBuilder &MIRBuilder, bool IsInstSelector)
SPIRVTypeInst getOrCreateOpTypeByOpcode(const Type *Ty, MachineIRBuilder &MIRBuilder, unsigned Opcode)
unsigned getScalarOrVectorComponentCount(Register VReg) const
const Type * getTypeForSPIRVType(SPIRVTypeInst Ty) const
SPIRVTypeInst getOrCreatePaddingType(MachineIRBuilder &MIRBuilder)
LLT getRegType(SPIRVTypeInst SpvType) const
SPIRVTypeInst getOrCreateSPIRVBoolType(MachineIRBuilder &MIRBuilder, bool EmitIR)
bool isScalarOfType(Register VReg, unsigned TypeOpcode) const
Register getSPIRVTypeID(SPIRVTypeInst SpirvType) const
Register getOrCreateConstIntArray(uint64_t Val, size_t Num, MachineInstr &I, SPIRVTypeInst SpvType, const SPIRVInstrInfo &TII)
unsigned getDeducedPointeeByteSize(const Value *PtrVal)
SPIRVTypeInst getOrCreateOpTypeCoopMatr(MachineIRBuilder &MIRBuilder, const TargetExtType *ExtensionType, SPIRVTypeInst ElemType, uint32_t Scope, uint32_t Rows, uint32_t Columns, uint32_t Use, bool EmitIR)
SPIRVTypeInst getOrCreateUnknownType(const Type *Ty, MachineIRBuilder &MIRBuilder, unsigned Opcode, const ArrayRef< MCOperand > Operands)
Register buildConstantFP(APFloat Val, MachineIRBuilder &MIRBuilder, SPIRVTypeInst SpvType=nullptr)
SPIRVTypeInst getOrCreateOpTypePipe(MachineIRBuilder &MIRBuilder, SPIRV::AccessQualifier::AccessQualifier AccQual)
SPIRVTypeInst getScalarOrVectorComponentType(SPIRVTypeInst Type) const
SPIRVTypeInst getOrCreateVulkanBufferType(MachineIRBuilder &MIRBuilder, Type *ElemType, SPIRV::StorageClass::StorageClass SC, bool IsWritable, bool EmitIr=false)
SPIRVTypeInst getPointeeType(SPIRVTypeInst PtrType)
SPIRVTypeInst getOrCreateSPIRVType(const Type *Type, MachineInstr &I, SPIRV::AccessQualifier::AccessQualifier AQ, bool EmitIR)
Register getOrCreateConsIntVector(uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVTypeInst SpvType, bool EmitIR)
bool isScalarOrVectorOfType(Register VReg, unsigned TypeOpcode) const
SPIRVTypeInst getOrCreateLayoutType(MachineIRBuilder &MIRBuilder, const TargetExtType *T, bool EmitIr=false)
Register getOrCreateConstNullPtr(MachineIRBuilder &MIRBuilder, SPIRVTypeInst SpvType)
SPIRVTypeInst getSPIRVTypeForVReg(Register VReg, const MachineFunction *MF=nullptr) const
SPIRVTypeInst getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder)
SPIRV::StorageClass::StorageClass getPointerStorageClass(Register VReg) const
Type * findDeducedElementType(const Value *Val)
Register buildConstantSampler(Register Res, unsigned AddrMode, unsigned Param, unsigned FilerMode, MachineIRBuilder &MIRBuilder)
Register buildConstantInt(uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVTypeInst SpvType, bool EmitIR, bool ZeroAsNull=true)
SPIRVTypeInst getOrCreateVulkanPushConstantType(MachineIRBuilder &MIRBuilder, Type *ElemType)
SPIRVTypeInst getOrCreateOpTypeDeviceEvent(MachineIRBuilder &MIRBuilder)
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition StringRef.h:736
static constexpr size_t npos
Definition StringRef.h:58
bool consume_back(StringRef Suffix)
Returns true if this StringRef has the given suffix and removes that suffix.
Definition StringRef.h:691
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition StringRef.h:490
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition StringRef.h:597
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:258
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
bool contains_insensitive(StringRef Other) const
Return true if the given string is a substring of *this, and false otherwise.
Definition StringRef.h:456
StringRef slice(size_t Start, size_t End) const
Return a reference to the substring from [Start, End).
Definition StringRef.h:720
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
bool contains(StringRef Other) const
Return true if the given string is a substring of *this, and false otherwise.
Definition StringRef.h:446
size_t find_first_of(char C, size_t From=0) const
Find the first character in the string that is C, or npos if not found.
Definition StringRef.h:396
size_t find(char C, size_t From=0) const
Search for the first character C in the string.
Definition StringRef.h:290
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition StringRef.h:270
bool consume_front(char Prefix)
Returns true if this StringRef has the given prefix and removes that prefix.
Definition StringRef.h:661
A switch()-like statement whose cases are string literals.
StringSwitch & EndsWith(StringLiteral S, T Value)
Class to represent target extensions types, which are generally unintrospectable from target-independ...
ArrayRef< Type * > type_params() const
Return the type parameters for this particular target extension type.
unsigned getNumIntParameters() const
static LLVM_ABI TargetExtType * get(LLVMContext &Context, StringRef Name, ArrayRef< Type * > Types={}, ArrayRef< unsigned > Ints={})
Return a target extension type having the specified name and optional type and integer parameters.
Definition Type.cpp:936
Type * getTypeParameter(unsigned i) const
unsigned getNumTypeParameters() const
unsigned getIntParameter(unsigned i) const
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:277
LLVM_ABI StringRef getStructName() const
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
Definition Type.cpp:297
bool isTargetExtTy() const
Return true if this is a target extension type.
Definition Type.h:205
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:252
static LLVM_ABI Type * getDoubleTy(LLVMContext &C)
Definition Type.cpp:277
static LLVM_ABI Type * getFloatTy(LLVMContext &C)
Definition Type.cpp:276
static LLVM_ABI Type * getBFloatTy(LLVMContext &C)
Definition Type.cpp:275
static LLVM_ABI Type * getHalfTy(LLVMContext &C)
Definition Type.cpp:274
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
LLVM Value Representation.
Definition Value.h:75
LLVM_ABI Value(Type *Ty, unsigned scid)
Definition Value.cpp:54
static LLVM_ABI VectorType * get(Type *ElementType, ElementCount EC)
This static method is the primary way to construct an VectorType.
Represents a version number in the form major[.minor[.subminor[.build]]].
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
CallInst * Call
LLVM_C_ABI LLVMTypeRef LLVMVectorType(LLVMTypeRef ElementType, unsigned ElementCount)
Create a vector type that contains a defined type and has a specific number of elements.
Definition Core.cpp:924
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
bool parseBuiltinTypeStr(SmallVector< StringRef, 10 > &BuiltinArgsTypeStrs, StringRef DemangledCall, LLVMContext &Ctx)
std::string lookupBuiltinNameHelper(StringRef DemangledCall, FPDecorationId *DecorationId)
Parses the name part of the demangled builtin call.
Type * parseBuiltinCallArgumentType(StringRef TypeStr, LLVMContext &Ctx)
bool isPipeOrAddressSpaceCastBuiltin(StringRef Name)
Returns true if Name is a pipe or address-space-cast OpenCL builtin.
std::optional< bool > lowerBuiltin(StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set, MachineIRBuilder &MIRBuilder, const Register OrigRet, const Type *OrigRetTy, const SmallVectorImpl< Register > &Args, SPIRVGlobalRegistry *GR, const CallBase &CB)
Type * parseBuiltinCallArgumentBaseType(StringRef DemangledCall, unsigned ArgIdx, LLVMContext &Ctx)
Parses the provided ArgIdx argument base type in the DemangledCall skeleton.
std::tuple< int, unsigned, unsigned > mapBuiltinToOpcode(StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set)
Helper function for finding a builtin function attributes by a demangled function name.
TargetExtType * parseBuiltinTypeNameToTargetExtType(std::string TypeName, LLVMContext &Context)
Translates a string representing a SPIR-V or OpenCL builtin type to a TargetExtType that can be furth...
static bool demangledArgTypesMatchIR(const SPIRV::IncomingCall *Call, StringRef DemangledCall, SPIRVGlobalRegistry *GR, LLVMContext &Ctx, const CallBase &CB)
Checks that scalar/vector numeric arguments of Call match the types implied by their mangling in Dema...
SPIRVTypeInst lowerBuiltinType(const Type *OpaqueType, SPIRV::AccessQualifier::AccessQualifier AccessQual, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
This is an optimization pass for GlobalISel generic memory operations.
static bool build2DBlockIOINTELInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building Intel's 2d block io instructions.
static bool generateExtInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static void buildSRetInst(unsigned Opcode, Register SRetReg, Register Op1Reg, Register Op2Reg, SPIRVTypeInst RetType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVTypeInst deduceSRetPointeeType(Register SRetReg, const Value *SRetArg, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateBindlessImageINTELInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateGetQueryInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateConstructInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building atomic flag instructions (e.g.
static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRV::SamplerFilterMode::SamplerFilterMode getSamplerFilterModeFromBitmask(unsigned Bitmask)
static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building an atomic store instruction.
uint32_t getMemSemanticsWithStorageClass(const Triple &TT, uint32_t OrderSem, uint32_t StorageClassSem)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB)
static bool buildExtendedBitOpsInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building extended bit operations.
static const Type * getBlockStructType(Register ParamReg, MachineRegisterInfo *MRI)
static bool generateGroupInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateConvertInst(StringRef DemangledCall, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
FPDecorationId demangledPostfixToDecorationId(const std::string &S)
Definition SPIRVUtils.h:591
static SPIRVTypeInst getSamplerType(MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim)
static bool generateImageChannelDataTypeInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool builtinMayNeedPromotionToVec(uint32_t BuiltinNumber)
static std::tuple< Register, SPIRVTypeInst > buildBoolRegister(MachineIRBuilder &MIRBuilder, SPIRVTypeInst ResultType, SPIRVGlobalRegistry *GR)
Helper function building either a resulting scalar or vector bool register depending on the expected ...
Register createVirtualRegister(SPIRVTypeInst SpvType, SPIRVGlobalRegistry *GR, MachineRegisterInfo *MRI, const MachineFunction &MF)
static Register buildScopeReg(Register CLScopeRegister, SPIRV::Scope::Scope Scope, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, MachineRegisterInfo *MRI)
FPDecorationId
Definition SPIRVUtils.h:589
void updateRegType(Register Reg, Type *Ty, SPIRVTypeInst SpirvTy, SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB, MachineRegisterInfo &MRI)
Helper external function for assigning a SPIRV type to a register, ensuring the register class and ty...
void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, SPIRV::Decoration::Decoration Dec, ArrayRef< uint32_t > DecArgs, StringRef StrImm)
static SPIRVTypeInst getInlineSpirvType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI)
static Register buildConstantIntReg32(uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
SmallVector< MachineInstr *, 4 > createContinuedInstructions(MachineIRBuilder &MIRBuilder, unsigned Opcode, unsigned MinWC, unsigned ContinuedOpcode, ArrayRef< Register > Args, Register ReturnRegister, Register TypeID)
static unsigned getNumSizeComponents(SPIRVTypeInst imgType)
Helper function for obtaining the number of size components.
SPIRV::MemorySemantics::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC)
constexpr unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC)
Definition SPIRVUtils.h:245
bool isVectorType(SPIRVTypeInst SPVTy)
static bool generateBarrierInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVTypeInst getLayoutType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
LLVM_ABI void SplitString(StringRef Source, SmallVectorImpl< StringRef > &OutFragments, StringRef Delimiters=" \t\n\v\f\r")
SplitString - Split up the specified string according to the specified delimiters,...
static bool generateAPFixedPointInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static bool generateMulExtendedInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static SPIRVTypeInst getVulkanPushConstantType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildImageChannelDataTypeInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateKernelClockInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static void setRegClassIfNull(Register Reg, MachineRegisterInfo *MRI, SPIRVGlobalRegistry *GR)
void buildOpName(Register Target, StringRef Name, MachineIRBuilder &MIRBuilder)
static bool generateGroupUniformInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateWaveInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
static bool buildNDRange(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static bool generateEnqueueInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building barriers, i.e., memory/control ordering operations.
static Register buildBuiltinVariableLoad(MachineIRBuilder &MIRBuilder, SPIRVTypeInst VariableType, SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType, Register Reg=Register(0), bool isConst=true, const std::optional< SPIRV::LinkageType::LinkageType > &LinkageTy={ SPIRV::LinkageType::Import})
Helper function for building a load instruction for loading a builtin global variable of BuiltinValue...
FPClassTest
Floating-point class tests, supported by 'is_fpclass' intrinsic.
static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope)
static bool generateBlockingPipesInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
static bool generateSampleImageInst(StringRef DemangledCall, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static Type * parseTypeString(StringRef Name, LLVMContext &Context)
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
static const Type * getMachineInstrType(MachineInstr *MI)
static bool generateDotOrFMulInst(StringRef DemangledCall, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
bool isDigit(char C)
Checks if character C is one of the 10 decimal digits.
static SPIRV::SamplerAddressingMode::SamplerAddressingMode getSamplerAddressingModeFromBitmask(unsigned Bitmask)
static bool generateAtomicInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateTernaryBitwiseFunctionINTELInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static std::unique_ptr< const SPIRV::IncomingCall > lookupBuiltin(StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set, Register ReturnRegister, SPIRVTypeInst ReturnType, const SmallVectorImpl< Register > &Arguments)
Looks up the demangled builtin call in the SPIRVBuiltins.td records using the provided DemangledCall ...
static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
constexpr bool isGenericCastablePtr(SPIRV::StorageClass::StorageClass SC)
Definition SPIRVUtils.h:229
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
static bool buildSelectInst(MachineIRBuilder &MIRBuilder, Register ReturnRegister, Register SourceRegister, SPIRVTypeInst ReturnType, SPIRVGlobalRegistry *GR)
Helper function for building either a vector or scalar select instruction depending on the expected R...
static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool foldNoOpConvert(unsigned Opcode, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRV::MemorySemantics::MemorySemantics getMemOrdering(Register OrderRegister, MachineRegisterInfo *MRI)
Translates an OpenCL memory_order argument into the memory ordering part of the SPIR-V memory semanti...
static bool generateSelectInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder)
static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building an atomic load instruction.
static bool generateIntelSubgroupsInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVTypeInst getCoopMatrType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateExtendedBitOpsInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildPipeInst(const SPIRV::IncomingCall *Call, unsigned Opcode, unsigned Scope, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static Register buildLoadInst(SPIRVTypeInst BaseType, Register PtrRegister, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, Register DestinationReg=Register(0))
Helper function for building a load instruction loading into the DestinationReg.
static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
@ Mul
Product of integers.
Type * parseBasicTypeName(StringRef &TypeName, LLVMContext &Ctx)
static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, uint64_t DefaultValue)
static bool generateCoopMatrInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SmallVector< Register > getBuiltinCallArguments(const SPIRV::IncomingCall *Call, uint32_t BuiltinNumber, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVTypeInst getNonParameterizedType(const TargetExtType *ExtensionType, const SPIRV::BuiltinType *TypeRecord, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static Register buildMemSemanticsReg(SPIRV::MemorySemantics::MemorySemantics Ordering, unsigned StorageClassSem, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Combines the memory ordering with the storage-class part of the memory semantics into a constant regi...
static bool buildBindlessImageINTELInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building Intel's bindless image instructions.
static bool buildAtomicFloatingRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building an atomic floating-type instruction.
MachineInstr * getDefInstrMaybeConstant(Register &ConstReg, const MachineRegisterInfo *MRI)
static bool generateReadImageInst(StringRef DemangledCall, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
constexpr unsigned BitWidth
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1933
static bool generate2DBlockIOINTELInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
bool hasBuiltinTypePrefix(StringRef Name)
static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Type * getMDOperandAsType(const MDNode *N, unsigned I)
static bool buildAPFixedPointInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static bool generatePipeInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildTernaryBitwiseFunctionINTELInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building Intel's OpBitwiseFunctionINTEL instruction.
static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building atomic instructions.
static SPIRV::MemorySemantics::MemorySemantics getSPIRVMemSemantics(std::memory_order MemOrder)
static bool generateRelationalInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVTypeInst getPipeType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder)
Helper function for translating atomic init to OpStore.
static bool generateWriteImageInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateAsyncCopy(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static bool generateArithmeticInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID)
static bool generatePredicatedLoadStoreInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateAtomicFloatingInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateAFPInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static MachineInstr * getBlockStructInstr(Register ParamReg, MachineRegisterInfo *MRI)
static SPIRVTypeInst getSampledImageType(const TargetExtType *OpaqueType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode, const SPIRV::IncomingCall *Call, Register TypeReg, ArrayRef< uint32_t > ImmArgs={})
static unsigned getSamplerParamFromBitmask(unsigned Bitmask)
static bool generateICarryBorrowInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, const CallBase &CB)
static SPIRVTypeInst getVulkanBufferType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildAtomicCompareExchangeInst(const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building an atomic compare-exchange instruction.
std::string getLinkStringForBuiltIn(SPIRV::BuiltIn::BuiltIn BuiltInValue)
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
static bool generateBuiltinVar(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
This class contains a discriminated union of information about pointers in memory operands,...
StringTable::Offset Name
FPRoundingMode::FPRoundingMode RoundingMode
InstructionSet::InstructionSet Set
InstructionSet::InstructionSet Set
InstructionSet::InstructionSet Set
StringTable::Offset Name
StringTable::Offset Name
InstructionSet::InstructionSet Set
const SmallVectorImpl< Register > & Arguments
const SPIRVTypeInst ReturnType
IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin, const Register ReturnRegister, SPIRVTypeInst ReturnType, const SmallVectorImpl< Register > &Arguments)
const std::string BuiltinName
const DemangledBuiltin * Builtin
StringTable::Offset Name
InstructionSet::InstructionSet Set
StringTable::Offset Name
StringTable::Offset SpirvTypeLiteral
InstructionSet::InstructionSet Set
FPRoundingMode::FPRoundingMode RoundingMode