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 (ResultType->getOpcode() == SPIRV::OpTypeVector) {
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 (ReturnType->getOpcode() == SPIRV::OpTypeVector) {
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 scope");
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
582static Register buildMemSemanticsReg(Register SemanticsRegister,
583 Register PtrRegister, unsigned &Semantics,
584 MachineIRBuilder &MIRBuilder,
586 if (SemanticsRegister.isValid()) {
587 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
588 std::memory_order Order =
589 static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI));
590 Semantics =
591 getSPIRVMemSemantics(Order) |
593 if (static_cast<unsigned>(Order) == Semantics) {
594 MRI->setRegClass(SemanticsRegister, &SPIRV::iIDRegClass);
595 return SemanticsRegister;
596 }
597 }
598 return buildConstantIntReg32(Semantics, MIRBuilder, GR);
599}
600
601static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode,
603 Register TypeReg,
604 ArrayRef<uint32_t> ImmArgs = {}) {
605 auto MIB = MIRBuilder.buildInstr(Opcode);
606 if (TypeReg.isValid())
607 MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
608 unsigned Sz = Call->Arguments.size() - ImmArgs.size();
609 for (unsigned i = 0; i < Sz; ++i)
610 MIB.addUse(Call->Arguments[i]);
611 for (uint32_t ImmArg : ImmArgs)
612 MIB.addImm(ImmArg);
613 return true;
614}
615
616/// Helper function for translating atomic init to OpStore.
618 MachineIRBuilder &MIRBuilder) {
619 if (Call->isSpirvOp())
620 return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0));
621
622 assert(Call->Arguments.size() == 2 &&
623 "Need 2 arguments for atomic init translation");
624 MIRBuilder.buildInstr(SPIRV::OpStore)
625 .addUse(Call->Arguments[0])
626 .addUse(Call->Arguments[1]);
627 return true;
628}
629
630/// Helper function for building an atomic load instruction.
632 MachineIRBuilder &MIRBuilder,
634 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
635 if (Call->isSpirvOp())
636 return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg);
637
638 Register PtrRegister = Call->Arguments[0];
639 // TODO: if true insert call to __translate_ocl_memory_sccope before
640 // OpAtomicLoad and the function implementation. We can use Translator's
641 // output for transcoding/atomic_explicit_arguments.cl as an example.
642 Register ScopeRegister =
643 Call->Arguments.size() > 1
644 ? Call->Arguments[1]
645 : buildConstantIntReg32(SPIRV::Scope::Device, MIRBuilder, GR);
646 Register MemSemanticsReg;
647 if (Call->Arguments.size() > 2) {
648 // TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad.
649 MemSemanticsReg = Call->Arguments[2];
650 } else {
651 int Semantics =
652 SPIRV::MemorySemantics::SequentiallyConsistent |
654 MemSemanticsReg = buildConstantIntReg32(Semantics, MIRBuilder, GR);
655 }
656
657 MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
658 .addDef(Call->ReturnRegister)
659 .addUse(TypeReg)
660 .addUse(PtrRegister)
661 .addUse(ScopeRegister)
662 .addUse(MemSemanticsReg);
663 return true;
664}
665
666/// Helper function for building an atomic store instruction.
668 MachineIRBuilder &MIRBuilder,
670 if (Call->isSpirvOp())
671 return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call,
672 Register(0));
673
674 Register ScopeRegister =
675 buildConstantIntReg32(SPIRV::Scope::Device, MIRBuilder, GR);
676 Register PtrRegister = Call->Arguments[0];
677 int Semantics =
678 SPIRV::MemorySemantics::SequentiallyConsistent |
680 Register MemSemanticsReg = buildConstantIntReg32(Semantics, MIRBuilder, GR);
681 MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
682 .addUse(PtrRegister)
683 .addUse(ScopeRegister)
684 .addUse(MemSemanticsReg)
685 .addUse(Call->Arguments[1]);
686 return true;
687}
688
689/// Helper function for building an atomic compare-exchange instruction.
691 const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin,
692 unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
693 if (Call->isSpirvOp())
694 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
695 GR->getSPIRVTypeID(Call->ReturnType));
696
697 bool IsCmpxchg = Call->Builtin->name().contains("cmpxchg");
698 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
699
700 Register ObjectPtr = Call->Arguments[0]; // Pointer (volatile A *object.)
701 Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).
702 Register Desired = Call->Arguments[2]; // Value (C Desired).
703 SPIRVTypeInst SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
704 LLT DesiredLLT = MRI->getType(Desired);
705
706 assert(GR->getSPIRVTypeForVReg(ObjectPtr).isPointer());
707 [[maybe_unused]] SPIRVTypeInst ExpectedTy =
708 GR->getSPIRVTypeForVReg(ExpectedArg);
709 assert(IsCmpxchg ? ExpectedTy->getOpcode() == SPIRV::OpTypeInt
710 : ExpectedTy.isPointer());
711 assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
712
713 SPIRVTypeInst SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
714 assert((SpvObjectPtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR ||
715 SpvObjectPtrTy->getOperand(2).isReg()) &&
716 "SPIRV type is expected");
717 auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
718 SpvObjectPtrTy->getOperand(1).getImm());
719 auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
720
721 Register MemSemEqualReg;
722 Register MemSemUnequalReg;
723 uint64_t MemSemEqual =
724 IsCmpxchg
725 ? SPIRV::MemorySemantics::None
726 : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
727 uint64_t MemSemUnequal =
728 IsCmpxchg
729 ? SPIRV::MemorySemantics::None
730 : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
731 if (Call->Arguments.size() >= 4) {
732 assert(Call->Arguments.size() >= 5 &&
733 "Need 5+ args for explicit atomic cmpxchg");
734 auto MemOrdEq =
735 static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));
736 auto MemOrdNeq =
737 static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));
738 MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;
739 MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;
740 if (static_cast<unsigned>(MemOrdEq) == MemSemEqual)
741 MemSemEqualReg = Call->Arguments[3];
742 if (static_cast<unsigned>(MemOrdNeq) == MemSemUnequal)
743 MemSemUnequalReg = Call->Arguments[4];
744 }
745 if (!MemSemEqualReg.isValid())
746 MemSemEqualReg = buildConstantIntReg32(MemSemEqual, MIRBuilder, GR);
747 if (!MemSemUnequalReg.isValid())
748 MemSemUnequalReg = buildConstantIntReg32(MemSemUnequal, MIRBuilder, GR);
749
750 Register ScopeReg;
751 auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;
752 if (Call->Arguments.size() >= 6) {
753 assert(Call->Arguments.size() == 6 &&
754 "Extra args for explicit atomic cmpxchg");
755 auto ClScope = static_cast<SPIRV::CLMemoryScope>(
756 getIConstVal(Call->Arguments[5], MRI));
757 Scope = getSPIRVScope(ClScope);
758 if (ClScope == static_cast<unsigned>(Scope))
759 ScopeReg = Call->Arguments[5];
760 }
761 if (!ScopeReg.isValid())
762 ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
763
765 IsCmpxchg ? ExpectedArg
766 : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder, GR);
767 MRI->setType(Expected, DesiredLLT);
768 Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)
769 : Call->ReturnRegister;
770 if (!MRI->getRegClassOrNull(Tmp))
771 MRI->setRegClass(Tmp, GR->getRegClass(SpvDesiredTy));
772 GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());
773
774 MIRBuilder.buildInstr(Opcode)
775 .addDef(Tmp)
776 .addUse(GR->getSPIRVTypeID(SpvDesiredTy))
777 .addUse(ObjectPtr)
778 .addUse(ScopeReg)
779 .addUse(MemSemEqualReg)
780 .addUse(MemSemUnequalReg)
781 .addUse(Desired)
783 if (!IsCmpxchg) {
784 MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);
785 MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);
786 }
787 return true;
788}
789
790/// Helper function for building atomic instructions.
791static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
792 MachineIRBuilder &MIRBuilder,
794 if (Call->isSpirvOp())
795 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
796 GR->getSPIRVTypeID(Call->ReturnType));
797
798 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
799 Register ScopeRegister =
800 Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register();
801
802 assert(Call->Arguments.size() <= 4 &&
803 "Too many args for explicit atomic RMW");
804 ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup,
805 MIRBuilder, GR, MRI);
806
807 Register PtrRegister = Call->Arguments[0];
808 unsigned Semantics = SPIRV::MemorySemantics::None;
809 Register MemSemanticsReg =
810 Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
811 MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
812 Semantics, MIRBuilder, GR);
813 Register ValueReg = Call->Arguments[1];
814 Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);
815 // support cl_ext_float_atomics
816 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {
817 if (Opcode == SPIRV::OpAtomicIAdd) {
818 Opcode = SPIRV::OpAtomicFAddEXT;
819 } else if (Opcode == SPIRV::OpAtomicISub) {
820 // Translate OpAtomicISub applied to a floating type argument to
821 // OpAtomicFAddEXT with the negative value operand
822 Opcode = SPIRV::OpAtomicFAddEXT;
823 Register NegValueReg =
824 MRI->createGenericVirtualRegister(MRI->getType(ValueReg));
825 MRI->setRegClass(NegValueReg, GR->getRegClass(Call->ReturnType));
826 GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg,
827 MIRBuilder.getMF());
828 MIRBuilder.buildInstr(TargetOpcode::G_FNEG)
829 .addDef(NegValueReg)
830 .addUse(ValueReg);
831 updateRegType(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder,
832 MIRBuilder.getMF().getRegInfo());
833 ValueReg = NegValueReg;
834 }
835 }
836 MIRBuilder.buildInstr(Opcode)
837 .addDef(Call->ReturnRegister)
838 .addUse(ValueTypeReg)
839 .addUse(PtrRegister)
840 .addUse(ScopeRegister)
841 .addUse(MemSemanticsReg)
842 .addUse(ValueReg);
843 return true;
844}
845
846/// Helper function for building an atomic floating-type instruction.
848 unsigned Opcode,
849 MachineIRBuilder &MIRBuilder,
851 assert(Call->Arguments.size() == 4 &&
852 "Wrong number of atomic floating-type builtin");
853 Register PtrReg = Call->Arguments[0];
854 Register ScopeReg = Call->Arguments[1];
855 Register MemSemanticsReg = Call->Arguments[2];
856 Register ValueReg = Call->Arguments[3];
857 MIRBuilder.buildInstr(Opcode)
858 .addDef(Call->ReturnRegister)
859 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
860 .addUse(PtrReg)
861 .addUse(ScopeReg)
862 .addUse(MemSemanticsReg)
863 .addUse(ValueReg);
864 return true;
865}
866
867/// Helper function for building atomic flag instructions (e.g.
868/// OpAtomicFlagTestAndSet).
870 unsigned Opcode, MachineIRBuilder &MIRBuilder,
872 bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet;
873 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
874 if (Call->isSpirvOp())
875 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
876 IsSet ? TypeReg : Register(0));
877
878 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
879 Register PtrRegister = Call->Arguments[0];
880 unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent;
881 Register MemSemanticsReg =
882 Call->Arguments.size() >= 2 ? Call->Arguments[1] : Register();
883 MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
884 Semantics, MIRBuilder, GR);
885
886 assert((Opcode != SPIRV::OpAtomicFlagClear ||
887 (Semantics != SPIRV::MemorySemantics::Acquire &&
888 Semantics != SPIRV::MemorySemantics::AcquireRelease)) &&
889 "Invalid memory order argument!");
890
891 Register ScopeRegister =
892 Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
893 ScopeRegister =
894 buildScopeReg(ScopeRegister, SPIRV::Scope::Device, MIRBuilder, GR, MRI);
895
896 auto MIB = MIRBuilder.buildInstr(Opcode);
897 if (IsSet)
898 MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
899
900 MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);
901 return true;
902}
903
904/// Helper function for building barriers, i.e., memory/control ordering
905/// operations.
906static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
907 MachineIRBuilder &MIRBuilder,
909 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
910 const auto *ST =
911 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
912 if ((Opcode == SPIRV::OpControlBarrierArriveINTEL ||
913 Opcode == SPIRV::OpControlBarrierWaitINTEL) &&
914 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_split_barrier)) {
915 std::string DiagMsg = std::string(Builtin->name()) +
916 ": the builtin requires the following SPIR-V "
917 "extension: SPV_INTEL_split_barrier";
918 report_fatal_error(DiagMsg.c_str(), false);
919 }
920
921 if (Call->isSpirvOp())
922 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
923
924 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
925 unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);
926 unsigned MemSemantics = SPIRV::MemorySemantics::None;
927
928 if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
929 MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
930
931 if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)
932 MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;
933
934 if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)
935 MemSemantics |= SPIRV::MemorySemantics::ImageMemory;
936
937 if (Opcode == SPIRV::OpMemoryBarrier)
938 MemSemantics = getSPIRVMemSemantics(static_cast<std::memory_order>(
939 getIConstVal(Call->Arguments[1], MRI))) |
940 MemSemantics;
941 else if (Opcode == SPIRV::OpControlBarrierArriveINTEL)
942 MemSemantics |= SPIRV::MemorySemantics::Release;
943 else if (Opcode == SPIRV::OpControlBarrierWaitINTEL)
944 MemSemantics |= SPIRV::MemorySemantics::Acquire;
945 else
946 MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;
947
948 Register MemSemanticsReg =
949 MemFlags == MemSemantics
950 ? Call->Arguments[0]
951 : buildConstantIntReg32(MemSemantics, MIRBuilder, GR);
952 Register ScopeReg;
953 SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
954 SPIRV::Scope::Scope MemScope = Scope;
955 if (Call->Arguments.size() >= 2) {
956 assert(
957 ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||
958 (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&
959 "Extra args for explicitly scoped barrier");
960 Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]
961 : Call->Arguments[1];
962 SPIRV::CLMemoryScope CLScope =
963 static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
964 MemScope = getSPIRVScope(CLScope);
965 if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
966 (Opcode == SPIRV::OpMemoryBarrier))
967 Scope = MemScope;
968 if (CLScope == static_cast<unsigned>(Scope))
969 ScopeReg = Call->Arguments[1];
970 }
971
972 if (!ScopeReg.isValid())
973 ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
974
975 auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);
976 if (Opcode != SPIRV::OpMemoryBarrier)
977 MIB.addUse(buildConstantIntReg32(MemScope, MIRBuilder, GR));
978 MIB.addUse(MemSemanticsReg);
979 return true;
980}
981
982/// Helper function for building extended bit operations.
984 unsigned Opcode,
985 MachineIRBuilder &MIRBuilder,
987 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
988 const auto *ST =
989 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
990 if ((Opcode == SPIRV::OpBitFieldInsert ||
991 Opcode == SPIRV::OpBitFieldSExtract ||
992 Opcode == SPIRV::OpBitFieldUExtract || Opcode == SPIRV::OpBitReverse) &&
993 !ST->canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions)) {
994 std::string DiagMsg = std::string(Builtin->name()) +
995 ": the builtin requires the following SPIR-V "
996 "extension: SPV_KHR_bit_instructions";
997 report_fatal_error(DiagMsg.c_str(), false);
998 }
999
1000 // Generate SPIRV instruction accordingly.
1001 if (Call->isSpirvOp())
1002 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1003 GR->getSPIRVTypeID(Call->ReturnType));
1004
1005 auto MIB = MIRBuilder.buildInstr(Opcode)
1006 .addDef(Call->ReturnRegister)
1007 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1008 for (unsigned i = 0; i < Call->Arguments.size(); ++i)
1009 MIB.addUse(Call->Arguments[i]);
1010
1011 return true;
1012}
1013
1014/// Helper function for building Intel's bindless image instructions.
1016 unsigned Opcode,
1017 MachineIRBuilder &MIRBuilder,
1018 SPIRVGlobalRegistry *GR) {
1019 // Generate SPIRV instruction accordingly.
1020 if (Call->isSpirvOp())
1021 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1022 GR->getSPIRVTypeID(Call->ReturnType));
1023
1024 MIRBuilder.buildInstr(Opcode)
1025 .addDef(Call->ReturnRegister)
1026 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1027 .addUse(Call->Arguments[0]);
1028
1029 return true;
1030}
1031
1032/// Helper function for building Intel's OpBitwiseFunctionINTEL instruction.
1034 const SPIRV::IncomingCall *Call, unsigned Opcode,
1035 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
1036 // Generate SPIRV instruction accordingly.
1037 if (Call->isSpirvOp())
1038 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1039 GR->getSPIRVTypeID(Call->ReturnType));
1040
1041 auto MIB = MIRBuilder.buildInstr(Opcode)
1042 .addDef(Call->ReturnRegister)
1043 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1044 for (unsigned i = 0; i < Call->Arguments.size(); ++i)
1045 MIB.addUse(Call->Arguments[i]);
1046
1047 return true;
1048}
1049
1051 unsigned Opcode,
1052 MachineIRBuilder &MIRBuilder,
1053 SPIRVGlobalRegistry *GR) {
1054 if (Call->isSpirvOp())
1055 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1056 GR->getSPIRVTypeID(Call->ReturnType));
1057
1058 auto MIB = MIRBuilder.buildInstr(Opcode)
1059 .addDef(Call->ReturnRegister)
1060 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1061 for (unsigned i = 0; i < Call->Arguments.size(); ++i)
1062 MIB.addUse(Call->Arguments[i]);
1063
1064 return true;
1065}
1066
1067/// Helper function for building Intel's 2d block io instructions.
1069 unsigned Opcode,
1070 MachineIRBuilder &MIRBuilder,
1071 SPIRVGlobalRegistry *GR) {
1072 // Generate SPIRV instruction accordingly.
1073 if (Call->isSpirvOp())
1074 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
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
1085static bool buildPipeInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
1086 unsigned Scope, MachineIRBuilder &MIRBuilder,
1087 SPIRVGlobalRegistry *GR) {
1088 switch (Opcode) {
1089 case SPIRV::OpCommitReadPipe:
1090 case SPIRV::OpCommitWritePipe:
1091 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
1092 case SPIRV::OpGroupCommitReadPipe:
1093 case SPIRV::OpGroupCommitWritePipe:
1094 case SPIRV::OpGroupReserveReadPipePackets:
1095 case SPIRV::OpGroupReserveWritePipePackets: {
1096 Register ScopeConstReg =
1097 MIRBuilder.buildConstant(LLT::scalar(32), Scope).getReg(0);
1098 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1099 MRI->setRegClass(ScopeConstReg, &SPIRV::iIDRegClass);
1101 MIB = MIRBuilder.buildInstr(Opcode);
1102 // Add Return register and type.
1103 if (Opcode == SPIRV::OpGroupReserveReadPipePackets ||
1104 Opcode == SPIRV::OpGroupReserveWritePipePackets)
1105 MIB.addDef(Call->ReturnRegister)
1106 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1107
1108 MIB.addUse(ScopeConstReg);
1109 for (unsigned int i = 0; i < Call->Arguments.size(); ++i)
1110 MIB.addUse(Call->Arguments[i]);
1111
1112 return true;
1113 }
1114 default:
1115 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1116 GR->getSPIRVTypeID(Call->ReturnType));
1117 }
1118}
1119
1120static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
1121 switch (dim) {
1122 case SPIRV::Dim::DIM_1D:
1123 case SPIRV::Dim::DIM_Buffer:
1124 return 1;
1125 case SPIRV::Dim::DIM_2D:
1126 case SPIRV::Dim::DIM_Cube:
1127 case SPIRV::Dim::DIM_Rect:
1128 return 2;
1129 case SPIRV::Dim::DIM_3D:
1130 return 3;
1131 default:
1132 report_fatal_error("Cannot get num components for given Dim");
1133 }
1134}
1135
1136/// Helper function for obtaining the number of size components.
1137static unsigned getNumSizeComponents(SPIRVTypeInst imgType) {
1138 assert(imgType->getOpcode() == SPIRV::OpTypeImage);
1139 auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());
1140 unsigned numComps = getNumComponentsForDim(dim);
1141 bool arrayed = imgType->getOperand(4).getImm() == 1;
1142 return arrayed ? numComps + 1 : numComps;
1143}
1144
1145static bool builtinMayNeedPromotionToVec(uint32_t BuiltinNumber) {
1146 switch (BuiltinNumber) {
1147 case SPIRV::OpenCLExtInst::s_min:
1148 case SPIRV::OpenCLExtInst::u_min:
1149 case SPIRV::OpenCLExtInst::s_max:
1150 case SPIRV::OpenCLExtInst::u_max:
1151 case SPIRV::OpenCLExtInst::fmax:
1152 case SPIRV::OpenCLExtInst::fmin:
1153 case SPIRV::OpenCLExtInst::fmax_common:
1154 case SPIRV::OpenCLExtInst::fmin_common:
1155 case SPIRV::OpenCLExtInst::s_clamp:
1156 case SPIRV::OpenCLExtInst::fclamp:
1157 case SPIRV::OpenCLExtInst::u_clamp:
1158 case SPIRV::OpenCLExtInst::mix:
1159 case SPIRV::OpenCLExtInst::step:
1160 case SPIRV::OpenCLExtInst::smoothstep:
1161 case SPIRV::OpenCLExtInst::ldexp:
1162 case SPIRV::OpenCLExtInst::pown:
1163 case SPIRV::OpenCLExtInst::rootn:
1164 return true;
1165 default:
1166 break;
1167 }
1168 return false;
1169}
1170
1171//===----------------------------------------------------------------------===//
1172// Implementation functions for each builtin group
1173//===----------------------------------------------------------------------===//
1174
1177 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
1178
1179 Register ReturnTypeId = GR->getSPIRVTypeID(Call->ReturnType);
1180 unsigned ResultElementCount =
1181 GR->getScalarOrVectorComponentCount(ReturnTypeId);
1182 bool MayNeedPromotionToVec =
1183 builtinMayNeedPromotionToVec(BuiltinNumber) && ResultElementCount > 1;
1184
1185 if (!MayNeedPromotionToVec)
1186 return {Call->Arguments.begin(), Call->Arguments.end()};
1187
1189 for (Register Argument : Call->Arguments) {
1190 Register VecArg = Argument;
1191 SPIRVTypeInst ArgumentType = GR->getSPIRVTypeForVReg(Argument);
1192 if (GR->getScalarOrVectorComponentCount(ArgumentType) == 1 &&
1193 ArgumentType != Call->ReturnType) {
1195 ArgumentType, ResultElementCount, MIRBuilder, /*EmitIR=*/true);
1196 VecArg = createVirtualRegister(VecType, GR, MIRBuilder);
1197 Register VecTypeId = GR->getSPIRVTypeID(VecType);
1198 auto VecSplat = MIRBuilder.buildInstr(SPIRV::OpCompositeConstruct)
1199 .addDef(VecArg)
1200 .addUse(VecTypeId);
1201 for (unsigned I = 0; I != ResultElementCount; ++I)
1202 VecSplat.addUse(Argument);
1203 }
1204 Arguments.push_back(VecArg);
1205 }
1206 return Arguments;
1207}
1208
1210 MachineIRBuilder &MIRBuilder,
1211 SPIRVGlobalRegistry *GR, const CallBase &CB) {
1212 // Lookup the extended instruction number in the TableGen records.
1213 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1215 SPIRV::lookupExtendedBuiltin(Builtin->name(), Builtin->Set)->Number;
1216 // fmin_common and fmax_common are now deprecated, and we should use fmin and
1217 // fmax with NotInf and NotNaN flags instead. Keep original number to add
1218 // later the NoNans and NoInfs flags.
1219 uint32_t OrigNumber = Number;
1220 const SPIRVSubtarget &ST =
1221 cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
1222 if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2) &&
1223 (Number == SPIRV::OpenCLExtInst::fmin_common ||
1224 Number == SPIRV::OpenCLExtInst::fmax_common)) {
1225 Number = (Number == SPIRV::OpenCLExtInst::fmin_common)
1226 ? SPIRV::OpenCLExtInst::fmin
1227 : SPIRV::OpenCLExtInst::fmax;
1228 }
1229
1230 // ExtInst prefetch cannot take an untyped pointer, so emit
1231 // OpUntypedPrefetchKHR with Num Bytes = num elements * element byte size.
1232 if (Number == SPIRV::OpenCLExtInst::prefetch && Call->Arguments.size() >= 2) {
1233 Register PtrReg = Call->Arguments[0];
1234 SPIRVTypeInst PtrTy = GR->getSPIRVTypeForVReg(PtrReg);
1235 if (PtrTy && PtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR) {
1236 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1237 Register NumElems = Call->Arguments[1];
1238 SPIRVTypeInst SizeTy = GR->getSPIRVTypeForVReg(NumElems);
1239 assert(SizeTy && "Expected a type for the number of elements");
1240 unsigned ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(0));
1241 Register NumBytes = NumElems;
1242 // A byte sized element already makes the element count a byte count. A
1243 // size of 0 means the element type could not be deduced, which the typed
1244 // lowering resolves to i8, so treat it as a single byte here as well.
1245 if (ElemBytes > 1) {
1246 Register ElemBytesReg = GR->buildConstantInt(ElemBytes, MIRBuilder,
1247 SizeTy, /*EmitIR=*/true);
1248 Register Mul =
1249 MRI->createGenericVirtualRegister(MRI->getType(NumElems));
1250 MRI->setRegClass(Mul, GR->getRegClass(SizeTy));
1251 GR->assignSPIRVTypeToVReg(SizeTy, Mul, MIRBuilder.getMF());
1252 MIRBuilder.buildInstr(TargetOpcode::G_MUL)
1253 .addDef(Mul)
1254 .addUse(NumElems)
1255 .addUse(ElemBytesReg);
1256 updateRegType(Mul, /*Ty=*/nullptr, SizeTy, GR, MIRBuilder, *MRI);
1257 NumBytes = Mul;
1258 }
1259 MIRBuilder.buildInstr(SPIRV::OpUntypedPrefetchKHR)
1260 .addUse(PtrReg)
1261 .addUse(NumBytes);
1262 return true;
1263 }
1264 }
1265
1266 Register ReturnTypeId = GR->getSPIRVTypeID(Call->ReturnType);
1268 getBuiltinCallArguments(Call, Number, MIRBuilder, GR);
1269
1271 if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_fma) &&
1272 Number == SPIRV::OpenCLExtInst::fma) {
1273 // Use the SPIR-V fma instruction instead of the OpenCL extended
1274 // instruction if the extension is available.
1275 MIB = MIRBuilder.buildInstr(SPIRV::OpFmaKHR)
1276 .addDef(Call->ReturnRegister)
1277 .addUse(ReturnTypeId);
1278 } else {
1279 // Build extended instruction.
1280 MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst)
1281 .addDef(Call->ReturnRegister)
1282 .addUse(ReturnTypeId)
1283 .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1284 .addImm(Number);
1285 }
1286
1288 MIB.addUse(Argument);
1289
1290 MIB.getInstr()->copyIRFlags(CB);
1291 if (OrigNumber == SPIRV::OpenCLExtInst::fmin_common ||
1292 OrigNumber == SPIRV::OpenCLExtInst::fmax_common) {
1293 // Add NoNans and NoInfs flags to fmin/fmax instruction.
1296 }
1297
1298 // Derive fast-math flags from nofpclass attributes on the called function.
1299 // FPFastMathMode decoration is valid on ExtInst in Kernel environments
1300 // (SPIR-V core) or with SPV_KHR_float_controls2 for any environment.
1301 if (ST.isKernel() ||
1302 ST.canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
1303 if (const Function *F = CB.getCalledFunction()) {
1304 bool AddNoNan = CB.getRetNoFPClass() & fcNan;
1305 bool AddNoInf = CB.getRetNoFPClass() & fcInf;
1306 FunctionType *FTy = F->getFunctionType();
1307 for (unsigned I = 0, E = FTy->getNumParams();
1308 I != E && (AddNoNan || AddNoInf); ++I) {
1309 if (!FTy->getParamType(I)->isFloatingPointTy())
1310 continue;
1311 FPClassTest ArgTest = CB.getParamNoFPClass(I);
1312 AddNoNan = AddNoNan && ArgTest & fcNan;
1313 AddNoInf = AddNoInf && ArgTest & fcInf;
1314 }
1315 if (AddNoNan)
1317 if (AddNoInf)
1319 }
1320 }
1321
1322 return true;
1323}
1324
1326 MachineIRBuilder &MIRBuilder,
1327 SPIRVGlobalRegistry *GR) {
1328 // Lookup the instruction opcode in the TableGen records.
1329 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1330 unsigned Opcode =
1331 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1332
1333 Register CompareRegister;
1334 SPIRVTypeInst RelationType = nullptr;
1335 std::tie(CompareRegister, RelationType) =
1336 buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1337
1338 // OpAny/OpAll require a boolean vector input, but OpenCL any()/all()
1339 // builtins receive integer vectors. Convert via OpINotEqual against zero.
1340 SmallVector<Register> Arguments(Call->Arguments.begin(),
1341 Call->Arguments.end());
1342 if ((Opcode == SPIRV::OpAny || Opcode == SPIRV::OpAll) &&
1343 !GR->isScalarOrVectorOfType(Arguments[0], SPIRV::OpTypeBool)) {
1345 unsigned NumElts = GR->getScalarOrVectorComponentCount(ArgType);
1347 GR->getOrCreateSPIRVBoolType(MIRBuilder, /*EmitIR=*/true), NumElts,
1348 MIRBuilder, /*EmitIR=*/true);
1349 Register ZeroReg =
1350 GR->getOrCreateConsIntVector(uint64_t(0), MIRBuilder, ArgType,
1351 /*EmitIR=*/true);
1352 Register BoolVecReg = createVirtualRegister(BoolVecTy, GR, MIRBuilder);
1353 MIRBuilder.buildInstr(SPIRV::OpINotEqual)
1354 .addDef(BoolVecReg)
1355 .addUse(GR->getSPIRVTypeID(BoolVecTy))
1356 .addUse(Arguments[0])
1357 .addUse(ZeroReg);
1358 Arguments[0] = BoolVecReg;
1359 }
1360
1361 // Build relational instruction.
1362 auto MIB = MIRBuilder.buildInstr(Opcode)
1363 .addDef(CompareRegister)
1364 .addUse(GR->getSPIRVTypeID(RelationType));
1365
1366 for (auto Argument : Arguments)
1367 MIB.addUse(Argument);
1368
1369 // Build select instruction.
1370 return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,
1371 Call->ReturnType, GR);
1372}
1373
1375 MachineIRBuilder &MIRBuilder,
1376 SPIRVGlobalRegistry *GR) {
1377 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1378 const SPIRV::GroupBuiltin *GroupBuiltin =
1379 SPIRV::lookupGroupBuiltin(Builtin->name());
1380
1381 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1382 if (Call->isSpirvOp()) {
1383 if (GroupBuiltin->NoGroupOperation) {
1385 if (GroupBuiltin->Opcode ==
1386 SPIRV::OpSubgroupMatrixMultiplyAccumulateINTEL &&
1387 Call->Arguments.size() > 4)
1388 ImmArgs.push_back(getIConstVal(Call->Arguments[4], MRI));
1389 return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call,
1390 GR->getSPIRVTypeID(Call->ReturnType), ImmArgs);
1391 }
1392
1393 // Group Operation is a literal
1394 Register GroupOpReg = Call->Arguments[1];
1395 const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI);
1396 if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT)
1398 "Group Operation parameter must be an integer constant");
1399 uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue();
1400 Register ScopeReg = Call->Arguments[0];
1401 auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1402 .addDef(Call->ReturnRegister)
1403 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1404 .addUse(ScopeReg)
1405 .addImm(GrpOp);
1406 for (unsigned i = 2; i < Call->Arguments.size(); ++i)
1407 MIB.addUse(Call->Arguments[i]);
1408 return true;
1409 }
1410
1411 Register Arg0;
1412 if (GroupBuiltin->HasBoolArg) {
1413 SPIRVTypeInst BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder, true);
1414 Register BoolReg = Call->Arguments[0];
1415 SPIRVTypeInst BoolRegType = GR->getSPIRVTypeForVReg(BoolReg);
1416 if (!BoolRegType)
1417 report_fatal_error("Can't find a register's type definition");
1418 MachineInstr *ArgInstruction = getDefInstrMaybeConstant(BoolReg, MRI);
1419 if (ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT) {
1420 if (BoolRegType->getOpcode() != SPIRV::OpTypeBool)
1421 Arg0 = GR->buildConstantInt(getIConstVal(BoolReg, MRI) != 0, MIRBuilder,
1422 BoolType, true);
1423 } else {
1424 if (BoolRegType->getOpcode() == SPIRV::OpTypeInt) {
1426 MRI->setRegClass(Arg0, &SPIRV::iIDRegClass);
1427 GR->assignSPIRVTypeToVReg(BoolType, Arg0, MIRBuilder.getMF());
1428 MIRBuilder.buildICmp(
1429 CmpInst::ICMP_NE, Arg0, BoolReg,
1430 GR->buildConstantInt(0, MIRBuilder, BoolRegType, true));
1431 updateRegType(Arg0, nullptr, BoolType, GR, MIRBuilder,
1432 MIRBuilder.getMF().getRegInfo());
1433 } else if (BoolRegType->getOpcode() != SPIRV::OpTypeBool) {
1434 report_fatal_error("Expect a boolean argument");
1435 }
1436 // if BoolReg is a boolean register, we don't need to do anything
1437 }
1438 }
1439
1440 Register GroupResultRegister = Call->ReturnRegister;
1441 SPIRVTypeInst GroupResultType = Call->ReturnType;
1442
1443 // TODO: maybe we need to check whether the result type is already boolean
1444 // and in this case do not insert select instruction.
1445 const bool HasBoolReturnTy =
1446 GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||
1447 GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||
1448 GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;
1449
1450 if (HasBoolReturnTy)
1451 std::tie(GroupResultRegister, GroupResultType) =
1452 buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1453
1454 auto Scope = Builtin->name().starts_with("sub_group")
1455 ? SPIRV::Scope::Subgroup
1456 : SPIRV::Scope::Workgroup;
1457 Register ScopeRegister = buildConstantIntReg32(Scope, MIRBuilder, GR);
1458
1459 Register VecReg;
1460 if (GroupBuiltin->Opcode == SPIRV::OpGroupBroadcast &&
1461 Call->Arguments.size() > 2) {
1462 // For OpGroupBroadcast "LocalId must be an integer datatype. It must be a
1463 // scalar, a vector with 2 components, or a vector with 3 components.",
1464 // meaning that we must create a vector from the function arguments if
1465 // it's a work_group_broadcast(val, local_id_x, local_id_y) or
1466 // work_group_broadcast(val, local_id_x, local_id_y, local_id_z) call.
1467 Register ElemReg = Call->Arguments[1];
1468 SPIRVTypeInst ElemType = GR->getSPIRVTypeForVReg(ElemReg);
1469 if (!ElemType || ElemType->getOpcode() != SPIRV::OpTypeInt)
1470 report_fatal_error("Expect an integer <LocalId> argument");
1471 unsigned VecLen = Call->Arguments.size() - 1;
1472 VecReg = MRI->createGenericVirtualRegister(
1473 LLT::fixed_vector(VecLen, MRI->getType(ElemReg)));
1474 MRI->setRegClass(VecReg, &SPIRV::viIDRegClass);
1475 SPIRVTypeInst VecType =
1476 GR->getOrCreateSPIRVVectorType(ElemType, VecLen, MIRBuilder, true);
1477 GR->assignSPIRVTypeToVReg(VecType, VecReg, MIRBuilder.getMF());
1478 auto MIB =
1479 MIRBuilder.buildInstr(TargetOpcode::G_BUILD_VECTOR).addDef(VecReg);
1480 for (unsigned i = 1; i < Call->Arguments.size(); i++) {
1481 MIB.addUse(Call->Arguments[i]);
1482 setRegClassIfNull(Call->Arguments[i], MRI, GR);
1483 }
1484 updateRegType(VecReg, nullptr, VecType, GR, MIRBuilder,
1485 MIRBuilder.getMF().getRegInfo());
1486 }
1487
1488 // Build work/sub group instruction.
1489 auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1490 .addDef(GroupResultRegister)
1491 .addUse(GR->getSPIRVTypeID(GroupResultType))
1492 .addUse(ScopeRegister);
1493
1494 if (!GroupBuiltin->NoGroupOperation)
1495 MIB.addImm(GroupBuiltin->GroupOperation);
1496 if (Call->Arguments.size() > 0) {
1497 MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);
1498 setRegClassIfNull(Call->Arguments[0], MRI, GR);
1499 if (VecReg.isValid())
1500 MIB.addUse(VecReg);
1501 else
1502 for (unsigned i = 1; i < Call->Arguments.size(); i++)
1503 MIB.addUse(Call->Arguments[i]);
1504 }
1505
1506 // Build select instruction.
1507 if (HasBoolReturnTy)
1508 buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,
1509 Call->ReturnType, GR);
1510 return true;
1511}
1512
1514 MachineIRBuilder &MIRBuilder,
1515 SPIRVGlobalRegistry *GR) {
1516 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1517 MachineFunction &MF = MIRBuilder.getMF();
1518 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1519 const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups =
1520 SPIRV::lookupIntelSubgroupsBuiltin(Builtin->name());
1521
1522 if (IntelSubgroups->IsMedia &&
1523 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_media_block_io)) {
1524 std::string DiagMsg = std::string(Builtin->name()) +
1525 ": the builtin requires the following SPIR-V "
1526 "extension: SPV_INTEL_media_block_io";
1527 report_fatal_error(DiagMsg.c_str(), false);
1528 } else if (!IntelSubgroups->IsMedia &&
1529 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1530 std::string DiagMsg = std::string(Builtin->name()) +
1531 ": the builtin requires the following SPIR-V "
1532 "extension: SPV_INTEL_subgroups";
1533 report_fatal_error(DiagMsg.c_str(), false);
1534 }
1535
1536 uint32_t OpCode = IntelSubgroups->Opcode;
1537 if (Call->isSpirvOp()) {
1538 bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL &&
1539 OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL &&
1540 OpCode != SPIRV::OpSubgroupImageMediaBlockWriteINTEL;
1541 return buildOpFromWrapper(MIRBuilder, OpCode, Call,
1542 IsSet ? GR->getSPIRVTypeID(Call->ReturnType)
1543 : Register(0));
1544 }
1545
1546 if (IntelSubgroups->IsBlock) {
1547 // Minimal number or arguments set in TableGen records is 1
1548 if (SPIRVTypeInst Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) {
1549 if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) {
1550 // TODO: add required validation from the specification:
1551 // "'Image' must be an object whose type is OpTypeImage with a 'Sampled'
1552 // operand of 0 or 2. If the 'Sampled' operand is 2, then some
1553 // dimensions require a capability."
1554 switch (OpCode) {
1555 case SPIRV::OpSubgroupBlockReadINTEL:
1556 OpCode = SPIRV::OpSubgroupImageBlockReadINTEL;
1557 break;
1558 case SPIRV::OpSubgroupBlockWriteINTEL:
1559 OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL;
1560 break;
1561 }
1562 }
1563 }
1564 }
1565
1566 // TODO: opaque pointers types should be eventually resolved in such a way
1567 // that validation of block read is enabled with respect to the following
1568 // specification requirement:
1569 // "'Result Type' may be a scalar or vector type, and its component type must
1570 // be equal to the type pointed to by 'Ptr'."
1571 // For example, function parameter type should not be default i8 pointer, but
1572 // depend on the result type of the instruction where it is used as a pointer
1573 // argument of OpSubgroupBlockReadINTEL
1574
1575 // Build Intel subgroups instruction
1577 IntelSubgroups->IsWrite
1578 ? MIRBuilder.buildInstr(OpCode)
1579 : MIRBuilder.buildInstr(OpCode)
1580 .addDef(Call->ReturnRegister)
1581 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1582 for (size_t i = 0; i < Call->Arguments.size(); ++i)
1583 MIB.addUse(Call->Arguments[i]);
1584 return true;
1585}
1586
1588 MachineIRBuilder &MIRBuilder,
1589 SPIRVGlobalRegistry *GR) {
1590 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1591 MachineFunction &MF = MIRBuilder.getMF();
1592 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1593 if (!ST->canUseExtension(
1594 SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {
1595 std::string DiagMsg = std::string(Builtin->name()) +
1596 ": the builtin requires the following SPIR-V "
1597 "extension: SPV_KHR_uniform_group_instructions";
1598 report_fatal_error(DiagMsg.c_str(), false);
1599 }
1600 const SPIRV::GroupUniformBuiltin *GroupUniform =
1601 SPIRV::lookupGroupUniformBuiltin(Builtin->name());
1602 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1603
1604 Register GroupResultReg = Call->ReturnRegister;
1605 Register ScopeReg = Call->Arguments[0];
1606 Register ValueReg = Call->Arguments[2];
1607
1608 // Group Operation
1609 Register ConstGroupOpReg = Call->Arguments[1];
1610 const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI);
1611 if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT)
1613 "expect a constant group operation for a uniform group instruction",
1614 false);
1615 const MachineOperand &ConstOperand = Const->getOperand(1);
1616 if (!ConstOperand.isCImm())
1617 report_fatal_error("uniform group instructions: group operation must be an "
1618 "integer constant",
1619 false);
1620
1621 auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode)
1622 .addDef(GroupResultReg)
1623 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1624 .addUse(ScopeReg);
1625 addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1626 MIB.addUse(ValueReg);
1627
1628 return true;
1629}
1630
1632 MachineIRBuilder &MIRBuilder,
1633 SPIRVGlobalRegistry *GR) {
1634 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1635 MachineFunction &MF = MIRBuilder.getMF();
1636 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1637 if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) {
1638 std::string DiagMsg = std::string(Builtin->name()) +
1639 ": the builtin requires the following SPIR-V "
1640 "extension: SPV_KHR_shader_clock";
1641 report_fatal_error(DiagMsg.c_str(), false);
1642 }
1643
1644 Register ResultReg = Call->ReturnRegister;
1645
1646 if (Builtin->name() == "__spirv_ReadClockKHR") {
1647 MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
1648 .addDef(ResultReg)
1649 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1650 .addUse(Call->Arguments[0]);
1651 } else {
1652 // Deduce the `Scope` operand from the builtin function name.
1653 SPIRV::Scope::Scope ScopeArg =
1655 .EndsWith("device", SPIRV::Scope::Scope::Device)
1656 .EndsWith("work_group", SPIRV::Scope::Scope::Workgroup)
1657 .EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup);
1658 Register ScopeReg = buildConstantIntReg32(ScopeArg, MIRBuilder, GR);
1659
1660 MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
1661 .addDef(ResultReg)
1662 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1663 .addUse(ScopeReg);
1664 }
1665
1666 return true;
1667}
1668
1669// These queries ask for a single size_t result for a given dimension index,
1670// e.g. size_t get_global_id(uint dimindex). In SPIR-V, the builtins
1671// corresponding to these values are all vec3 types, so we need to extract the
1672// correct index or return DefaultValue (0 or 1 depending on the query). We also
1673// handle extending or truncating in case size_t does not match the expected
1674// result type's bitwidth.
1675//
1676// For a constant index >= 3 we generate:
1677// %res = OpConstant %SizeT DefaultValue
1678//
1679// For other indices we generate:
1680// %g = OpVariable %ptr_V3_SizeT Input
1681// OpDecorate %g BuiltIn XXX
1682// OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"
1683// OpDecorate %g Constant
1684// %loadedVec = OpLoad %V3_SizeT %g
1685//
1686// Then, if the index is constant < 3, we generate:
1687// %res = OpCompositeExtract %SizeT %loadedVec idx
1688// If the index is dynamic, we generate:
1689// %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx
1690// %cmp = OpULessThan %bool %idx %const_3
1691// %res = OpSelect %SizeT %cmp %tmp %const_<DefaultValue>
1692//
1693// If the bitwidth of %res does not match the expected return type, we add an
1694// extend or truncate.
1696 MachineIRBuilder &MIRBuilder,
1698 SPIRV::BuiltIn::BuiltIn BuiltinValue,
1699 uint64_t DefaultValue) {
1700 Register IndexRegister = Call->Arguments[0];
1701 const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
1702 const unsigned PointerSize = GR->getPointerSize();
1703 const SPIRVTypeInst PointerSizeType =
1704 GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);
1705 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1706 auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);
1707
1708 // Set up the final register to do truncation or extension on at the end.
1709 Register ToTruncate = Call->ReturnRegister;
1710
1711 // If the index is constant, we can statically determine if it is in range.
1712 bool IsConstantIndex =
1713 IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;
1714
1715 // If it's out of range (max dimension is 3), we can just return the constant
1716 // default value (0 or 1 depending on which query function).
1717 if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {
1718 Register DefaultReg = Call->ReturnRegister;
1719 if (PointerSize != ResultWidth) {
1720 DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1721 MRI->setRegClass(DefaultReg, &SPIRV::iIDRegClass);
1722 GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg,
1723 MIRBuilder.getMF());
1724 ToTruncate = DefaultReg;
1725 }
1726 auto NewRegister =
1727 GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType, true);
1728 MIRBuilder.buildCopy(DefaultReg, NewRegister);
1729 } else { // If it could be in range, we need to load from the given builtin.
1730 auto Vec3Ty =
1731 GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder, true);
1732 Register LoadedVector =
1733 buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,
1734 LLT::fixed_vector(3, PointerSize));
1735 // Set up the vreg to extract the result to (possibly a new temporary one).
1736 Register Extracted = Call->ReturnRegister;
1737 if (!IsConstantIndex || PointerSize != ResultWidth) {
1738 Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1739 MRI->setRegClass(Extracted, &SPIRV::iIDRegClass);
1740 GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());
1741 }
1742 // Use Intrinsic::spv_extractelt so dynamic vs static extraction is
1743 // handled later: extr = spv_extractelt LoadedVector, IndexRegister.
1744 MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(
1745 Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false);
1746 ExtractInst.addUse(LoadedVector).addUse(IndexRegister);
1747
1748 // If the index is dynamic, need check if it's < 3, and then use a select.
1749 if (!IsConstantIndex) {
1750 updateRegType(Extracted, nullptr, PointerSizeType, GR, MIRBuilder, *MRI);
1751
1752 auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);
1753 auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder, true);
1754
1755 Register CompareRegister =
1757 MRI->setRegClass(CompareRegister, &SPIRV::iIDRegClass);
1758 GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());
1759
1760 // Use G_ICMP to check if idxVReg < 3.
1761 MIRBuilder.buildICmp(
1762 CmpInst::ICMP_ULT, CompareRegister, IndexRegister,
1763 GR->buildConstantInt(3, MIRBuilder, IndexType, true));
1764
1765 // Get constant for the default value (0 or 1 depending on which
1766 // function).
1767 Register DefaultRegister =
1768 GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType, true);
1769
1770 // Get a register for the selection result (possibly a new temporary one).
1771 Register SelectionResult = Call->ReturnRegister;
1772 if (PointerSize != ResultWidth) {
1773 SelectionResult =
1774 MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1775 MRI->setRegClass(SelectionResult, &SPIRV::iIDRegClass);
1776 GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,
1777 MIRBuilder.getMF());
1778 }
1779 // Create the final G_SELECT to return the extracted value or the default.
1780 MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,
1781 DefaultRegister);
1782 ToTruncate = SelectionResult;
1783 } else {
1784 ToTruncate = Extracted;
1785 }
1786 }
1787 // Alter the result's bitwidth if it does not match the SizeT value extracted.
1788 if (PointerSize != ResultWidth)
1789 MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);
1790 return true;
1791}
1792
1794 MachineIRBuilder &MIRBuilder,
1795 SPIRVGlobalRegistry *GR) {
1796 // Lookup the builtin variable record.
1797 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1798 SPIRV::BuiltIn::BuiltIn Value =
1799 SPIRV::lookupGetBuiltin(Builtin->name(), Builtin->Set)->Value;
1800
1801 if (Value == SPIRV::BuiltIn::GlobalInvocationId)
1802 return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);
1803
1804 // Build a load instruction for the builtin variable.
1805 unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);
1806 LLT LLType;
1807 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector)
1808 LLType = LLT::fixed_vector(
1810 else
1811 LLType = LLT::scalar(BitWidth);
1812
1813 return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,
1814 LLType, Call->ReturnRegister);
1815}
1816
1818 MachineIRBuilder &MIRBuilder,
1819 SPIRVGlobalRegistry *GR) {
1820 // Lookup the instruction opcode in the TableGen records.
1821 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1822 unsigned Opcode =
1823 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1824
1825 switch (Opcode) {
1826 case SPIRV::OpStore:
1827 return buildAtomicInitInst(Call, MIRBuilder);
1828 case SPIRV::OpAtomicLoad:
1829 return buildAtomicLoadInst(Call, MIRBuilder, GR);
1830 case SPIRV::OpAtomicStore:
1831 return buildAtomicStoreInst(Call, MIRBuilder, GR);
1832 case SPIRV::OpAtomicCompareExchange:
1833 case SPIRV::OpAtomicCompareExchangeWeak:
1834 return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder,
1835 GR);
1836 case SPIRV::OpAtomicIAdd:
1837 case SPIRV::OpAtomicISub:
1838 case SPIRV::OpAtomicOr:
1839 case SPIRV::OpAtomicXor:
1840 case SPIRV::OpAtomicAnd:
1841 case SPIRV::OpAtomicExchange:
1842 case SPIRV::OpAtomicSMax:
1843 case SPIRV::OpAtomicSMin:
1844 case SPIRV::OpAtomicUMax:
1845 case SPIRV::OpAtomicUMin:
1846 return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
1847 case SPIRV::OpMemoryBarrier:
1848 return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
1849 case SPIRV::OpAtomicFlagTestAndSet:
1850 case SPIRV::OpAtomicFlagClear:
1851 return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);
1852 default:
1853 if (Call->isSpirvOp())
1854 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1855 GR->getSPIRVTypeID(Call->ReturnType));
1856 return false;
1857 }
1858}
1859
1861 MachineIRBuilder &MIRBuilder,
1862 SPIRVGlobalRegistry *GR) {
1863 // Lookup the instruction opcode in the TableGen records.
1864 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1865 unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->name())->Opcode;
1866
1867 switch (Opcode) {
1868 case SPIRV::OpAtomicFAddEXT:
1869 case SPIRV::OpAtomicFMinEXT:
1870 case SPIRV::OpAtomicFMaxEXT:
1871 return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR);
1872 default:
1873 return false;
1874 }
1875}
1876
1878 MachineIRBuilder &MIRBuilder,
1879 SPIRVGlobalRegistry *GR) {
1880 // Lookup the instruction opcode in the TableGen records.
1881 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1882 unsigned Opcode =
1883 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1884
1885 return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
1886}
1887
1889 MachineIRBuilder &MIRBuilder,
1890 SPIRVGlobalRegistry *GR) {
1891 // Lookup the instruction opcode in the TableGen records.
1892 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1893 unsigned Opcode =
1894 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
1895
1896 if (Opcode == SPIRV::OpGenericCastToPtrExplicit) {
1897 SPIRV::StorageClass::StorageClass ResSC =
1898 GR->getPointerStorageClass(Call->ReturnRegister);
1899 if (!isGenericCastablePtr(ResSC))
1900 return false;
1901
1902 MIRBuilder.buildInstr(Opcode)
1903 .addDef(Call->ReturnRegister)
1904 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1905 .addUse(Call->Arguments[0])
1906 .addImm(ResSC);
1907 } else {
1908 MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)
1909 .addDef(Call->ReturnRegister)
1910 .addUse(Call->Arguments[0]);
1911 }
1912 return true;
1913}
1914
1915static bool generateDotOrFMulInst(StringRef DemangledCall,
1917 MachineIRBuilder &MIRBuilder,
1918 SPIRVGlobalRegistry *GR) {
1919 if (Call->isSpirvOp())
1920 return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call,
1921 GR->getSPIRVTypeID(Call->ReturnType));
1922
1923 bool IsVec = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode() ==
1924 SPIRV::OpTypeVector;
1925 // Use OpDot only in case of vector args and OpFMul in case of scalar args.
1926 uint32_t OC = IsVec ? SPIRV::OpDot : SPIRV::OpFMulS;
1927 bool IsSwapReq = false;
1928
1929 const auto *ST =
1930 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
1931 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt) &&
1932 (ST->canUseExtension(SPIRV::Extension::SPV_KHR_integer_dot_product) ||
1933 ST->isAtLeastSPIRVVer(VersionTuple(1, 6)))) {
1934 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1935 const SPIRV::IntegerDotProductBuiltin *IntDot =
1936 SPIRV::lookupIntegerDotProductBuiltin(Builtin->name());
1937 if (IntDot) {
1938 OC = IntDot->Opcode;
1939 IsSwapReq = IntDot->IsSwapReq;
1940 } else if (IsVec) {
1941 // Handling "dot" and "dot_acc_sat" builtins which use vectors of
1942 // integers.
1943 LLVMContext &Ctx = MIRBuilder.getContext();
1945 SPIRV::parseBuiltinTypeStr(TypeStrs, DemangledCall, Ctx);
1946 bool IsFirstSigned = TypeStrs[0].trim()[0] != 'u';
1947 bool IsSecondSigned = TypeStrs[1].trim()[0] != 'u';
1948
1949 if (Call->BuiltinName == "dot") {
1950 if (IsFirstSigned && IsSecondSigned)
1951 OC = SPIRV::OpSDot;
1952 else if (!IsFirstSigned && !IsSecondSigned)
1953 OC = SPIRV::OpUDot;
1954 else {
1955 OC = SPIRV::OpSUDot;
1956 if (!IsFirstSigned)
1957 IsSwapReq = true;
1958 }
1959 } else if (Call->BuiltinName == "dot_acc_sat") {
1960 if (IsFirstSigned && IsSecondSigned)
1961 OC = SPIRV::OpSDotAccSat;
1962 else if (!IsFirstSigned && !IsSecondSigned)
1963 OC = SPIRV::OpUDotAccSat;
1964 else {
1965 OC = SPIRV::OpSUDotAccSat;
1966 if (!IsFirstSigned)
1967 IsSwapReq = true;
1968 }
1969 }
1970 }
1971 }
1972
1973 MachineInstrBuilder MIB = MIRBuilder.buildInstr(OC)
1974 .addDef(Call->ReturnRegister)
1975 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1976
1977 if (IsSwapReq) {
1978 MIB.addUse(Call->Arguments[1]);
1979 MIB.addUse(Call->Arguments[0]);
1980 // needed for dot_acc_sat* builtins
1981 for (size_t i = 2; i < Call->Arguments.size(); ++i)
1982 MIB.addUse(Call->Arguments[i]);
1983 } else {
1984 for (size_t i = 0; i < Call->Arguments.size(); ++i)
1985 MIB.addUse(Call->Arguments[i]);
1986 }
1987
1988 // Add Packed Vector Format for Integer dot product builtins if arguments are
1989 // scalar
1990 if (!IsVec && OC != SPIRV::OpFMulS)
1991 MIB.addImm(SPIRV::PackedVectorFormat4x8Bit);
1992
1993 return true;
1994}
1995
1997 MachineIRBuilder &MIRBuilder,
1998 SPIRVGlobalRegistry *GR) {
1999 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2000 SPIRV::BuiltIn::BuiltIn Value =
2001 SPIRV::lookupGetBuiltin(Builtin->name(), Builtin->Set)->Value;
2002
2003 // For now, we only support a single Wave intrinsic with a single return type.
2004 assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt);
2005 LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType));
2006
2008 MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister,
2009 /* isConst= */ false, /* LinkageType= */ std::nullopt);
2010}
2011
2012// Build a SPIR-V instruction with struct return via sret pointer:
2013// Res = Opcode RetType Op1 Op2
2014// OpStore SRetReg Res
2015static void buildSRetInst(unsigned Opcode, Register SRetReg, Register Op1Reg,
2016 Register Op2Reg, SPIRVTypeInst RetType,
2017 MachineIRBuilder &MIRBuilder,
2018 SPIRVGlobalRegistry *GR) {
2019 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2020 Register ResReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2021 if (const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Op1Reg)) {
2022 MRI->setRegClass(ResReg, DstRC);
2023 MRI->setType(ResReg, MRI->getType(Op1Reg));
2024 }
2025 GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
2026 MIRBuilder.buildInstr(Opcode)
2027 .addDef(ResReg)
2028 .addUse(GR->getSPIRVTypeID(RetType))
2029 .addUse(Op1Reg)
2030 .addUse(Op2Reg);
2031 MIRBuilder.buildInstr(SPIRV::OpStore).addUse(SRetReg).addUse(ResReg);
2032}
2033
2034// Find the pointee type of an sret pointer argument. A typed pointer gives us
2035// the type directly. An untyped one does not, so fall back to the element type
2036// we deduced for the matching IR argument, or null if there is nothing to fall
2037// back to.
2039 const Value *SRetArg,
2040 MachineIRBuilder &MIRBuilder,
2041 SPIRVGlobalRegistry *GR) {
2042 SPIRVTypeInst RetType = GR->getPointeeType(GR->getSPIRVTypeForVReg(SRetReg));
2043 if (!RetType)
2044 if (Type *ElemTy = GR->findDeducedElementType(SRetArg))
2045 RetType = GR->getOrCreateSPIRVType(
2046 ElemTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
2047 return RetType;
2048}
2049
2050// We expect a builtin
2051// Name(ptr sret([RetType]) %result, Type %operand1, Type %operand1)
2052// where %result is a pointer to where the result of the builtin execution
2053// is to be stored, and generate the following instructions:
2054// Res = Opcode RetType Operand1 Operand1
2055// OpStore RetVariable Res
2057 MachineIRBuilder &MIRBuilder,
2059 const CallBase &CB) {
2060 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2061 unsigned Opcode =
2062 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2063
2064 Register SRetReg = Call->Arguments[0];
2065 SPIRVTypeInst RetType =
2066 deduceSRetPointeeType(SRetReg, CB.getArgOperand(0), MIRBuilder, GR);
2067 if (!RetType)
2068 report_fatal_error("The first parameter must be a pointer");
2069 if (RetType->getOpcode() != SPIRV::OpTypeStruct)
2070 report_fatal_error("Expected struct type result for the arithmetic with "
2071 "overflow builtins");
2072
2073 SPIRVTypeInst OpType1 = GR->getSPIRVTypeForVReg(Call->Arguments[1]);
2074 SPIRVTypeInst OpType2 = GR->getSPIRVTypeForVReg(Call->Arguments[2]);
2075 if (!OpType1 || !OpType2 || OpType1 != OpType2)
2076 report_fatal_error("Operands must have the same type");
2077 if (OpType1->getOpcode() == SPIRV::OpTypeVector)
2078 switch (Opcode) {
2079 case SPIRV::OpIAddCarryS:
2080 Opcode = SPIRV::OpIAddCarryV;
2081 break;
2082 case SPIRV::OpISubBorrowS:
2083 Opcode = SPIRV::OpISubBorrowV;
2084 break;
2085 }
2086
2087 buildSRetInst(Opcode, SRetReg, Call->Arguments[1], Call->Arguments[2],
2088 RetType, MIRBuilder, GR);
2089 return true;
2090}
2091
2092// We expect a builtin in one of two forms:
2093//
2094// (1) sret convention (3 arguments):
2095// void Name(ptr sret([RetType]) %result, Type %operand1, Type %operand2)
2096// => Res = Opcode RetType Operand1 Operand2
2097// OpStore %result Res
2098//
2099// (2) direct return convention (2 arguments):
2100// RetType Name(Type %operand1, Type %operand2)
2101// => Res = Opcode RetType Operand1 Operand2
2102//
2103// RetType is a struct with two members of the same type as the operands.
2105 MachineIRBuilder &MIRBuilder,
2107 const CallBase &CB) {
2108 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2109 unsigned Opcode =
2110 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2111 assert((Opcode == SPIRV::OpUMulExtended || Opcode == SPIRV::OpSMulExtended) &&
2112 "Expected OpUMulExtended or OpSMulExtended");
2113
2114 const bool IsSret =
2115 !Call->ReturnType || Call->ReturnType->getOpcode() == SPIRV::OpTypeVoid;
2116 Register Op1Reg = IsSret ? Call->Arguments[1] : Call->Arguments[0];
2117 Register Op2Reg = IsSret ? Call->Arguments[2] : Call->Arguments[1];
2118
2119 SPIRVTypeInst RetType = nullptr;
2120 if (IsSret) {
2121 Register SRetReg = Call->Arguments[0];
2122 RetType =
2123 deduceSRetPointeeType(SRetReg, CB.getArgOperand(0), MIRBuilder, GR);
2124 if (!RetType)
2125 report_fatal_error("The first parameter must be a pointer");
2126 } else {
2127 RetType = Call->ReturnType;
2128 }
2129
2130 if (!RetType || RetType->getOpcode() != SPIRV::OpTypeStruct)
2131 report_fatal_error("Expected struct type result for the extended "
2132 "multiplication builtins");
2133 if (RetType->getNumOperands() != 3)
2134 report_fatal_error("Expected struct with exactly two members for the "
2135 "extended multiplication builtins");
2136 SPIRVTypeInst Member0Type =
2137 GR->getSPIRVTypeForVReg(RetType->getOperand(1).getReg());
2138 SPIRVTypeInst Member1Type =
2139 GR->getSPIRVTypeForVReg(RetType->getOperand(2).getReg());
2140 if (!Member0Type || !Member1Type || Member0Type != Member1Type)
2141 report_fatal_error("Both struct members must be the same type");
2142
2143 SPIRVTypeInst OpType1 = GR->getSPIRVTypeForVReg(Op1Reg);
2144 SPIRVTypeInst OpType2 = GR->getSPIRVTypeForVReg(Op2Reg);
2145 if (!OpType1 || !OpType2 || OpType1 != OpType2)
2146 report_fatal_error("Operands must have the same type");
2147 if (OpType1 != Member0Type)
2148 report_fatal_error("Operand type must match the struct member type");
2149
2150 if (IsSret) {
2151 buildSRetInst(Opcode, Call->Arguments[0], Op1Reg, Op2Reg, RetType,
2152 MIRBuilder, GR);
2153 } else {
2154 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2155 Register ResReg = Call->ReturnRegister;
2156 if (const TargetRegisterClass *DstRC = MRI->getRegClassOrNull(Op1Reg)) {
2157 MRI->setRegClass(ResReg, DstRC);
2158 }
2159 GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
2160 MIRBuilder.buildInstr(Opcode)
2161 .addDef(ResReg)
2162 .addUse(GR->getSPIRVTypeID(RetType))
2163 .addUse(Op1Reg)
2164 .addUse(Op2Reg);
2165 }
2166 return true;
2167}
2168
2170 MachineIRBuilder &MIRBuilder,
2171 SPIRVGlobalRegistry *GR) {
2172 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2173 unsigned Opcode =
2174 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2175
2176 auto MIB = MIRBuilder.buildInstr(Opcode)
2177 .addDef(Call->ReturnRegister)
2178 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2179 for (Register Arg : Call->Arguments)
2180 MIB.addUse(Arg);
2181 return true;
2182}
2183
2185 MachineIRBuilder &MIRBuilder,
2186 SPIRVGlobalRegistry *GR) {
2187 // Lookup the builtin record.
2188 SPIRV::BuiltIn::BuiltIn Value =
2189 SPIRV::lookupGetBuiltin(Call->Builtin->name(), Call->Builtin->Set)->Value;
2190 const bool IsDefaultOne = (Value == SPIRV::BuiltIn::GlobalSize ||
2191 Value == SPIRV::BuiltIn::NumWorkgroups ||
2192 Value == SPIRV::BuiltIn::WorkgroupSize ||
2193 Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);
2194 return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefaultOne ? 1 : 0);
2195}
2196
2198 MachineIRBuilder &MIRBuilder,
2199 SPIRVGlobalRegistry *GR) {
2200 // Lookup the image size query component number in the TableGen records.
2201 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2202 uint32_t Component =
2203 SPIRV::lookupImageQueryBuiltin(Builtin->name(), Builtin->Set)->Component;
2204 // Query result may either be a vector or a scalar. If return type is not a
2205 // vector, expect only a single size component. Otherwise get the number of
2206 // expected components.
2207 unsigned NumExpectedRetComponents =
2208 GR->getScalarOrVectorComponentCount(Call->ReturnType);
2209 // Get the actual number of query result/size components.
2210 SPIRVTypeInst ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
2211 unsigned NumActualRetComponents = getNumSizeComponents(ImgType);
2212 Register QueryResult = Call->ReturnRegister;
2213 SPIRVTypeInst QueryResultType = Call->ReturnType;
2214 if (NumExpectedRetComponents != NumActualRetComponents) {
2215 unsigned Bitwidth = Call->ReturnType->getOpcode() == SPIRV::OpTypeInt
2216 ? Call->ReturnType->getOperand(1).getImm()
2217 : 32;
2218 QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(
2219 LLT::fixed_vector(NumActualRetComponents, Bitwidth));
2220 MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::viIDRegClass);
2221 SPIRVTypeInst IntTy = GR->getOrCreateSPIRVIntegerType(Bitwidth, MIRBuilder);
2222 QueryResultType = GR->getOrCreateSPIRVVectorType(
2223 IntTy, NumActualRetComponents, MIRBuilder, true);
2224 GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());
2225 }
2226 bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;
2227 bool IsMultisampled = ImgType->getOperand(5).getImm() != 0;
2228 bool UseQuerySize = IsDimBuf || IsMultisampled;
2229 unsigned Opcode =
2230 UseQuerySize ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;
2231 auto MIB = MIRBuilder.buildInstr(Opcode)
2232 .addDef(QueryResult)
2233 .addUse(GR->getSPIRVTypeID(QueryResultType))
2234 .addUse(Call->Arguments[0]);
2235 if (!UseQuerySize)
2236 MIB.addUse(buildConstantIntReg32(0, MIRBuilder, GR)); // Lod id.
2237 if (NumExpectedRetComponents == NumActualRetComponents)
2238 return true;
2239 if (NumExpectedRetComponents == 1) {
2240 // Only 1 component is expected, build OpCompositeExtract instruction.
2241 unsigned ExtractedComposite =
2242 Component == 3 ? NumActualRetComponents - 1 : Component;
2243 assert(ExtractedComposite < NumActualRetComponents &&
2244 "Invalid composite index!");
2245 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2246 SPIRVTypeInst NewType = nullptr;
2247 if (QueryResultType->getOpcode() == SPIRV::OpTypeVector) {
2248 NewType = GR->getScalarOrVectorComponentType(QueryResultType);
2249 Register NewTypeReg = GR->getSPIRVTypeID(NewType);
2250 if (TypeReg != NewTypeReg)
2251 TypeReg = NewTypeReg;
2252 else
2253 NewType = nullptr;
2254 }
2255 MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
2256 .addDef(Call->ReturnRegister)
2257 .addUse(TypeReg)
2258 .addUse(QueryResult)
2259 .addImm(ExtractedComposite);
2260 if (NewType)
2261 updateRegType(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
2262 MIRBuilder.getMF().getRegInfo());
2263 } else {
2264 // More than 1 component is expected, fill a new vector.
2265 auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)
2266 .addDef(Call->ReturnRegister)
2267 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2268 .addUse(QueryResult)
2269 .addUse(QueryResult);
2270 for (unsigned i = 0; i < NumExpectedRetComponents; ++i)
2271 MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);
2272 }
2273 return true;
2274}
2275
2277 MachineIRBuilder &MIRBuilder,
2278 SPIRVGlobalRegistry *GR) {
2279 assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&
2280 "Image samples query result must be of int type!");
2281
2282 // Lookup the instruction opcode in the TableGen records.
2283 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2284 unsigned Opcode =
2285 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2286
2287 Register Image = Call->Arguments[0];
2288 SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(
2289 GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());
2290 (void)ImageDimensionality;
2291
2292 switch (Opcode) {
2293 case SPIRV::OpImageQuerySamples:
2294 assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&
2295 "Image must be of 2D dimensionality");
2296 break;
2297 case SPIRV::OpImageQueryLevels:
2298 assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||
2299 ImageDimensionality == SPIRV::Dim::DIM_2D ||
2300 ImageDimensionality == SPIRV::Dim::DIM_3D ||
2301 ImageDimensionality == SPIRV::Dim::DIM_Cube) &&
2302 "Image must be of 1D/2D/3D/Cube dimensionality");
2303 break;
2304 }
2305
2306 MIRBuilder.buildInstr(Opcode)
2307 .addDef(Call->ReturnRegister)
2308 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2309 .addUse(Image);
2310 return true;
2311}
2312
2313// TODO: Move to TableGen.
2314static SPIRV::SamplerAddressingMode::SamplerAddressingMode
2316 switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {
2317 case SPIRV::CLK_ADDRESS_CLAMP:
2318 return SPIRV::SamplerAddressingMode::Clamp;
2319 case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:
2320 return SPIRV::SamplerAddressingMode::ClampToEdge;
2321 case SPIRV::CLK_ADDRESS_REPEAT:
2322 return SPIRV::SamplerAddressingMode::Repeat;
2323 case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:
2324 return SPIRV::SamplerAddressingMode::RepeatMirrored;
2325 case SPIRV::CLK_ADDRESS_NONE:
2326 return SPIRV::SamplerAddressingMode::None;
2327 default:
2328 report_fatal_error("Unknown CL address mode");
2329 }
2330}
2331
2332static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {
2333 return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;
2334}
2335
2336static SPIRV::SamplerFilterMode::SamplerFilterMode
2338 if (Bitmask & SPIRV::CLK_FILTER_LINEAR)
2339 return SPIRV::SamplerFilterMode::Linear;
2340 if (Bitmask & SPIRV::CLK_FILTER_NEAREST)
2341 return SPIRV::SamplerFilterMode::Nearest;
2342 return SPIRV::SamplerFilterMode::Nearest;
2343}
2344
2345static bool generateReadImageInst(StringRef DemangledCall,
2347 MachineIRBuilder &MIRBuilder,
2348 SPIRVGlobalRegistry *GR) {
2349 if (Call->isSpirvOp())
2350 return buildOpFromWrapper(MIRBuilder, SPIRV::OpImageRead, Call,
2351 GR->getSPIRVTypeID(Call->ReturnType));
2352 Register Image = Call->Arguments[0];
2353 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2354 bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler");
2355 bool HasMsaa = DemangledCall.contains_insensitive("msaa");
2356 if (HasOclSampler) {
2357 Register Sampler = Call->Arguments[1];
2358
2359 if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&
2360 getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {
2361 uint64_t SamplerMask = getIConstVal(Sampler, MRI);
2364 getSamplerParamFromBitmask(SamplerMask),
2365 getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder);
2366 }
2367 SPIRVTypeInst ImageType = GR->getSPIRVTypeForVReg(Image);
2368 SPIRVTypeInst SampledImageType =
2369 GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
2370 Register SampledImage = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2371
2372 MIRBuilder.buildInstr(SPIRV::OpSampledImage)
2373 .addDef(SampledImage)
2374 .addUse(GR->getSPIRVTypeID(SampledImageType))
2375 .addUse(Image)
2376 .addUse(Sampler);
2377
2379 MIRBuilder);
2380
2381 if (Call->ReturnType->getOpcode() != SPIRV::OpTypeVector) {
2382 SPIRVTypeInst TempType =
2383 GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder, true);
2384 Register TempRegister =
2385 MRI->createGenericVirtualRegister(GR->getRegType(TempType));
2386 MRI->setRegClass(TempRegister, GR->getRegClass(TempType));
2387 GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());
2388 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
2389 .addDef(TempRegister)
2390 .addUse(GR->getSPIRVTypeID(TempType))
2391 .addUse(SampledImage)
2392 .addUse(Call->Arguments[2]) // Coordinate.
2393 .addImm(SPIRV::ImageOperand::Lod)
2394 .addUse(Lod);
2395 MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
2396 .addDef(Call->ReturnRegister)
2397 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2398 .addUse(TempRegister)
2399 .addImm(0);
2400 } else {
2401 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
2402 .addDef(Call->ReturnRegister)
2403 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2404 .addUse(SampledImage)
2405 .addUse(Call->Arguments[2]) // Coordinate.
2406 .addImm(SPIRV::ImageOperand::Lod)
2407 .addUse(Lod);
2408 }
2409 } else if (HasMsaa) {
2410 MIRBuilder.buildInstr(SPIRV::OpImageRead)
2411 .addDef(Call->ReturnRegister)
2412 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2413 .addUse(Image)
2414 .addUse(Call->Arguments[1]) // Coordinate.
2415 .addImm(SPIRV::ImageOperand::Sample)
2416 .addUse(Call->Arguments[2]);
2417 } else {
2418 MIRBuilder.buildInstr(SPIRV::OpImageRead)
2419 .addDef(Call->ReturnRegister)
2420 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2421 .addUse(Image)
2422 .addUse(Call->Arguments[1]); // Coordinate.
2423 }
2424 return true;
2425}
2426
2428 MachineIRBuilder &MIRBuilder,
2429 SPIRVGlobalRegistry *GR) {
2430 if (Call->isSpirvOp())
2431 return buildOpFromWrapper(MIRBuilder, SPIRV::OpImageWrite, Call,
2432 Register(0));
2433 MIRBuilder.buildInstr(SPIRV::OpImageWrite)
2434 .addUse(Call->Arguments[0]) // Image.
2435 .addUse(Call->Arguments[1]) // Coordinate.
2436 .addUse(Call->Arguments[2]); // Texel.
2437 return true;
2438}
2439
2440static bool generateSampleImageInst(StringRef DemangledCall,
2442 MachineIRBuilder &MIRBuilder,
2443 SPIRVGlobalRegistry *GR) {
2444 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2445 if (Call->Builtin->name().contains_insensitive(
2446 "__translate_sampler_initializer")) {
2447 // Build sampler literal.
2448 uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI);
2450 Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),
2452 getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder);
2453 return Sampler.isValid();
2454 } else if (Call->Builtin->name().contains_insensitive(
2455 "__spirv_SampledImage")) {
2456 // Create OpSampledImage.
2457 Register Image = Call->Arguments[0];
2458 SPIRVTypeInst ImageType = GR->getSPIRVTypeForVReg(Image);
2459 SPIRVTypeInst SampledImageType =
2460 GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
2461 Register SampledImage =
2462 Call->ReturnRegister.isValid()
2463 ? Call->ReturnRegister
2464 : MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2465 MIRBuilder.buildInstr(SPIRV::OpSampledImage)
2466 .addDef(SampledImage)
2467 .addUse(GR->getSPIRVTypeID(SampledImageType))
2468 .addUse(Image)
2469 .addUse(Call->Arguments[1]); // Sampler.
2470 return true;
2471 } else if (Call->Builtin->name().contains_insensitive(
2472 "__spirv_ImageSampleExplicitLod")) {
2473 // Sample an image using an explicit level of detail.
2474 std::string ReturnType = DemangledCall.str();
2475 if (DemangledCall.contains("_R")) {
2476 ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);
2477 ReturnType = ReturnType.substr(0, ReturnType.find('('));
2478 }
2479 SPIRVTypeInst Type = Call->ReturnType
2480 ? Call->ReturnType
2482 ReturnType, MIRBuilder, true));
2483 if (!Type) {
2484 std::string DiagMsg =
2485 "Unable to recognize SPIRV type name: " + ReturnType;
2486 report_fatal_error(DiagMsg.c_str());
2487 }
2488 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
2489 .addDef(Call->ReturnRegister)
2491 .addUse(Call->Arguments[0]) // Image.
2492 .addUse(Call->Arguments[1]) // Coordinate.
2493 .addImm(SPIRV::ImageOperand::Lod)
2494 .addUse(Call->Arguments[3]);
2495 return true;
2496 }
2497 return false;
2498}
2499
2501 MachineIRBuilder &MIRBuilder) {
2502 const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2503 LLT ResTy = MRI->getType(Call->ReturnRegister);
2504 LLT CondTy = MRI->getType(Call->Arguments[0]);
2505 if (!ResTy.isVector() && CondTy.isVector())
2506 report_fatal_error("OpSelect with a scalar result requires a scalar "
2507 "boolean condition");
2508 MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],
2509 Call->Arguments[1], Call->Arguments[2]);
2510 return true;
2511}
2512
2514 MachineIRBuilder &MIRBuilder,
2515 SPIRVGlobalRegistry *GR) {
2516 createContinuedInstructions(MIRBuilder, SPIRV::OpCompositeConstruct, 3,
2517 SPIRV::OpCompositeConstructContinuedINTEL,
2518 Call->Arguments, Call->ReturnRegister,
2519 GR->getSPIRVTypeID(Call->ReturnType));
2520 return true;
2521}
2522
2524 MachineIRBuilder &MIRBuilder,
2525 SPIRVGlobalRegistry *GR) {
2526 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2527 unsigned Opcode =
2528 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2529 bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR &&
2530 Opcode != SPIRV::OpCooperativeMatrixStoreCheckedINTEL &&
2531 Opcode != SPIRV::OpCooperativeMatrixPrefetchINTEL;
2532 unsigned ArgSz = Call->Arguments.size();
2533 unsigned LiteralIdx = 0;
2534 switch (Opcode) {
2535 // Memory operand is optional and is literal.
2536 case SPIRV::OpCooperativeMatrixLoadKHR:
2537 LiteralIdx = ArgSz > 3 ? 3 : 0;
2538 break;
2539 case SPIRV::OpCooperativeMatrixStoreKHR:
2540 LiteralIdx = ArgSz > 4 ? 4 : 0;
2541 break;
2542 case SPIRV::OpCooperativeMatrixLoadCheckedINTEL:
2543 LiteralIdx = ArgSz > 7 ? 7 : 0;
2544 break;
2545 case SPIRV::OpCooperativeMatrixStoreCheckedINTEL:
2546 LiteralIdx = ArgSz > 8 ? 8 : 0;
2547 break;
2548 // Cooperative Matrix Operands operand is optional and is literal.
2549 case SPIRV::OpCooperativeMatrixMulAddKHR:
2550 LiteralIdx = ArgSz > 3 ? 3 : 0;
2551 break;
2552 };
2553
2555 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2556 if (Opcode == SPIRV::OpCooperativeMatrixPrefetchINTEL) {
2557 const uint32_t CacheLevel = getIConstVal(Call->Arguments[3], MRI);
2558 auto MIB = MIRBuilder.buildInstr(SPIRV::OpCooperativeMatrixPrefetchINTEL)
2559 .addUse(Call->Arguments[0]) // pointer
2560 .addUse(Call->Arguments[1]) // rows
2561 .addUse(Call->Arguments[2]) // columns
2562 .addImm(CacheLevel) // cache level
2563 .addUse(Call->Arguments[4]); // memory layout
2564 if (ArgSz > 5)
2565 MIB.addUse(Call->Arguments[5]); // stride
2566 if (ArgSz > 6) {
2567 const uint32_t MemOp = getIConstVal(Call->Arguments[6], MRI);
2568 MIB.addImm(MemOp); // memory operand
2569 }
2570 return true;
2571 }
2572 if (LiteralIdx > 0)
2573 ImmArgs.push_back(getIConstVal(Call->Arguments[LiteralIdx], MRI));
2574 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2575 if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) {
2576 SPIRVTypeInst CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
2577 if (!CoopMatrType)
2578 report_fatal_error("Can't find a register's type definition");
2579 MIRBuilder.buildInstr(Opcode)
2580 .addDef(Call->ReturnRegister)
2581 .addUse(TypeReg)
2582 .addUse(CoopMatrType->getOperand(0).getReg());
2583 return true;
2584 }
2585 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2586 IsSet ? TypeReg : Register(0), ImmArgs);
2587}
2588
2590 MachineIRBuilder &MIRBuilder,
2591 SPIRVGlobalRegistry *GR) {
2592 // Lookup the instruction opcode in the TableGen records.
2593 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2594 unsigned Opcode =
2595 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2596 const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2597
2598 switch (Opcode) {
2599 case SPIRV::OpSpecConstant: {
2600 // Determine the constant MI.
2601 Register ConstRegister = Call->Arguments[1];
2602 const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);
2603 assert(Const &&
2604 (Const->getOpcode() == TargetOpcode::G_CONSTANT ||
2605 Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&
2606 "Argument should be either an int or floating-point constant");
2607 // Determine the opcode and built the OpSpec MI.
2608 const MachineOperand &ConstOperand = Const->getOperand(1);
2609 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {
2610 assert(ConstOperand.isCImm() && "Int constant operand is expected");
2611 Opcode = ConstOperand.getCImm()->getValue().getZExtValue()
2612 ? SPIRV::OpSpecConstantTrue
2613 : SPIRV::OpSpecConstantFalse;
2614 }
2615 auto MIB = MIRBuilder.buildInstr(Opcode)
2616 .addDef(Call->ReturnRegister)
2617 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2618
2619 if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {
2620 if (Const->getOpcode() == TargetOpcode::G_CONSTANT)
2621 addNumImm(ConstOperand.getCImm()->getValue(), MIB);
2622 else
2623 addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);
2624 }
2625 // Build the SpecID decoration.
2626 unsigned SpecId =
2627 static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));
2628 buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,
2629 {SpecId});
2630 return true;
2631 }
2632 case SPIRV::OpSpecConstantComposite: {
2633 createContinuedInstructions(MIRBuilder, Opcode, 3,
2634 SPIRV::OpSpecConstantCompositeContinuedINTEL,
2635 Call->Arguments, Call->ReturnRegister,
2636 GR->getSPIRVTypeID(Call->ReturnType));
2637 return true;
2638 }
2639 default:
2640 return false;
2641 }
2642}
2643
2645 MachineIRBuilder &MIRBuilder,
2646 SPIRVGlobalRegistry *GR) {
2647 // Lookup the instruction opcode in the TableGen records.
2648 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2649 unsigned Opcode =
2650 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2651
2652 return buildExtendedBitOpsInst(Call, Opcode, MIRBuilder, GR);
2653}
2654
2656 MachineIRBuilder &MIRBuilder,
2657 SPIRVGlobalRegistry *GR) {
2658 // Lookup the instruction opcode in the TableGen records.
2659 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2660 unsigned Opcode =
2661 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2662
2663 return buildBindlessImageINTELInst(Call, Opcode, MIRBuilder, GR);
2664}
2665
2667 MachineIRBuilder &MIRBuilder,
2668 SPIRVGlobalRegistry *GR) {
2669 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2670 unsigned Opcode =
2671 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2672 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
2673}
2674
2676 unsigned Opcode, MachineIRBuilder &MIRBuilder,
2677 SPIRVGlobalRegistry *GR, const CallBase &CB) {
2678 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2680 Register InputReg = Call->Arguments[0];
2681 const Type *RetTy = GR->getTypeForSPIRVType(Call->ReturnType);
2682 bool IsSRet = RetTy->isVoidTy();
2683
2684 if (IsSRet) {
2685 const LLT ValTy = MRI->getType(InputReg);
2686 Register ActualRetValReg = MRI->createGenericVirtualRegister(ValTy);
2687 SPIRVTypeInst InstructionType =
2688 deduceSRetPointeeType(InputReg, CB.getArgOperand(0), MIRBuilder, GR);
2689 InputReg = Call->Arguments[1];
2690 auto InputType = GR->getTypeForSPIRVType(GR->getSPIRVTypeForVReg(InputReg));
2691 Register PtrInputReg;
2692 if (InputType->getTypeID() == llvm::Type::TypeID::TypedPointerTyID) {
2693 LLT InputLLT = MRI->getType(InputReg);
2694 PtrInputReg = MRI->createGenericVirtualRegister(InputLLT);
2695 SPIRVTypeInst PtrType =
2696 GR->getPointeeType(GR->getSPIRVTypeForVReg(InputReg));
2697 MachineMemOperand *MMO1 = MIRBuilder.getMF().getMachineMemOperand(
2699 InputLLT.getSizeInBytes(), Align(4));
2700 MIRBuilder.buildLoad(PtrInputReg, InputReg, *MMO1);
2701 MRI->setRegClass(PtrInputReg, &SPIRV::iIDRegClass);
2702 GR->assignSPIRVTypeToVReg(PtrType, PtrInputReg, MIRBuilder.getMF());
2703 }
2704
2705 for (unsigned index = 2; index < 7; index++) {
2706 ImmArgs.push_back(getIConstVal(Call->Arguments[index], MRI));
2707 }
2708
2709 // Emit the instruction
2710 auto MIB = MIRBuilder.buildInstr(Opcode)
2711 .addDef(ActualRetValReg)
2712 .addUse(GR->getSPIRVTypeID(InstructionType));
2713 if (PtrInputReg)
2714 MIB.addUse(PtrInputReg);
2715 else
2716 MIB.addUse(InputReg);
2717
2718 for (uint32_t Imm : ImmArgs)
2719 MIB.addImm(Imm);
2720 unsigned Size = ValTy.getSizeInBytes();
2721 // Store result to the pointer passed in Arg[0]
2722 MachineMemOperand *MMO = MIRBuilder.getMF().getMachineMemOperand(
2724 MRI->setRegClass(ActualRetValReg, &SPIRV::pIDRegClass);
2725 MIRBuilder.buildStore(ActualRetValReg, Call->Arguments[0], *MMO);
2726 return true;
2727 } else {
2728 for (unsigned index = 1; index < 6; index++)
2729 ImmArgs.push_back(getIConstVal(Call->Arguments[index], MRI));
2730
2731 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2732 GR->getSPIRVTypeID(Call->ReturnType), ImmArgs);
2733 }
2734}
2735
2737 MachineIRBuilder &MIRBuilder,
2739 const CallBase &CB) {
2740 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2741 unsigned Opcode =
2742 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2743
2744 return buildAPFixedPointInst(Call, Opcode, MIRBuilder, GR, CB);
2745}
2746
2747static bool
2749 MachineIRBuilder &MIRBuilder,
2750 SPIRVGlobalRegistry *GR) {
2751 // Lookup the instruction opcode in the TableGen records.
2752 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2753 unsigned Opcode =
2754 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2755
2756 return buildTernaryBitwiseFunctionINTELInst(Call, Opcode, MIRBuilder, GR);
2757}
2758
2760 MachineIRBuilder &MIRBuilder,
2761 SPIRVGlobalRegistry *GR) {
2762 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2763 unsigned Opcode =
2764 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2765
2766 return buildImageChannelDataTypeInst(Call, Opcode, MIRBuilder, GR);
2767}
2768
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 build2DBlockIOINTELInst(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 unsigned Scope = SPIRV::Scope::Workgroup;
2788 if (Builtin->name().contains("sub_group"))
2789 Scope = SPIRV::Scope::Subgroup;
2790
2791 return buildPipeInst(Call, Opcode, Scope, MIRBuilder, GR);
2792}
2793
2795 MachineIRBuilder &MIRBuilder,
2796 SPIRVGlobalRegistry *GR) {
2797 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2798 unsigned Opcode =
2799 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
2800
2801 bool IsSet = Opcode != SPIRV::OpPredicatedStoreINTEL;
2802 unsigned ArgSz = Call->Arguments.size();
2804 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2805 // Memory operand is optional and is literal.
2806 if (ArgSz > 3)
2807 ImmArgs.push_back(getIConstVal(Call->Arguments[/*Literal index*/ 3], MRI));
2808
2809 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2810 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2811 IsSet ? TypeReg : Register(0), ImmArgs);
2812}
2813
2815 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR,
2816 const CallBase &CB) {
2817 // The OpenCL ndrange_*D functions are overloaded and support 1D, 2D, and 3D
2818 // variants, accepting 1 to 3 arguments:
2819 // (global_work_size)
2820 // (global_work_size, local_work_size)
2821 // (global_work_offset, global_work_size, local_work_size)
2822 // Note: When all three arguments are provided, they are reordered compared
2823 // to the one- or two-argument form.
2824 //
2825 // The function may return data through an sret argument at position 0 (with
2826 // a void function return type). When present, all other argument indices are
2827 // adjusted accordingly.
2828 //
2829 // SPIR-V's OpBuildNDRange requires all three arguments (GlobalWorkSize,
2830 // LocalWorkSize, GlobalWorkOffset). For 1D kernels, the values are scalars;
2831 // for 2D/3D kernels, they are arrays of 2 or 3 elements. Missing arguments
2832 // default to zero.
2833 //
2834 // Calculate argument indices based on the number of arguments and presence
2835 // of sret:
2836 const unsigned NumCallArgs = Call->Arguments.size();
2837 const unsigned MaxCallArgs = Call->Builtin->MaxNumArgs;
2838 const unsigned IncorrectArgIdx = MaxCallArgs + 1;
2839
2840 const Type *RetTy = GR->getTypeForSPIRVType(Call->ReturnType);
2841 bool HasSRetArg = RetTy->isVoidTy();
2842
2843 const unsigned SRetArgIdx = HasSRetArg ? 0 : IncorrectArgIdx;
2844 const unsigned ArgBase = HasSRetArg ? 1 : 0;
2845 const unsigned MaxNDRangeArgs = 3;
2846 const unsigned NumNDRangeArgs = NumCallArgs - ArgBase;
2847
2848 const unsigned GlobalWorkSizeArgIdx =
2849 NumNDRangeArgs < MaxNDRangeArgs ? ArgBase : ArgBase + 1;
2850 const unsigned LocalWorkSizeArgIdx =
2851 (NumNDRangeArgs == 1)
2852 ? IncorrectArgIdx
2853 : (NumNDRangeArgs == MaxNDRangeArgs ? ArgBase + 2 : ArgBase + 1);
2854 const unsigned GlobalWorkOffsetArgIdx =
2855 NumNDRangeArgs == MaxNDRangeArgs ? ArgBase : IncorrectArgIdx;
2856
2857 // Each nd_range field is an array of <Dimension> integers matching the
2858 // address model width (32 or 64 bits).
2859 const unsigned AddressModelBits = GR->getPointerSize();
2860 assert(AddressModelBits == 64 || AddressModelBits == 32);
2861
2862 // The dimension is encoded in the function name as "ndrange_XD" where X is
2863 // 1, 2, or 3.
2864 unsigned Dimension = 0;
2865 Call->Builtin->name().substr(8, 1).getAsInteger(10, Dimension);
2866 assert(Dimension <= 3 && Dimension >= 1);
2867
2868 // Determine the work size type based on the dimension. For missing arguments,
2869 // create a zero constant of the appropriate type.
2870 MachineFunction &MF = MIRBuilder.getMF();
2871 SPIRVTypeInst SpvFieldTy;
2872 Register ConstZero;
2873 if (Dimension == 1) {
2874 SpvFieldTy = GR->getSPIRVTypeForVReg(Call->Arguments[GlobalWorkSizeArgIdx]);
2875 assert(SpvFieldTy && SpvFieldTy->getOpcode() == SPIRV::OpTypeInt &&
2876 "Expected scalar integer type");
2877
2878 if (NumNDRangeArgs < MaxNDRangeArgs)
2879 ConstZero = GR->buildConstantInt(0, MIRBuilder, SpvFieldTy, true);
2880 } else {
2881 Type *BaseTy =
2882 IntegerType::get(MF.getFunction().getContext(), AddressModelBits);
2883 Type *FieldTy = ArrayType::get(BaseTy, Dimension);
2884 SpvFieldTy = GR->getOrCreateSPIRVType(
2885 FieldTy, MIRBuilder, SPIRV::AccessQualifier::ReadOnly, true);
2886
2887 if (NumNDRangeArgs < MaxNDRangeArgs) {
2888 auto InsertIt = MIRBuilder.getInsertPt();
2889 MachineBasicBlock &MBB = MIRBuilder.getMBB();
2890 MachineInstr &InsertMI = (InsertIt != MBB.end()) ? *InsertIt : MBB.back();
2892 ConstZero = GR->getOrCreateConstIntArray(0, Dimension, InsertMI,
2893 SpvFieldTy, *ST.getInstrInfo());
2894 }
2895 }
2896
2897 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2898
2899 auto CreateDataRegister = [&](unsigned Idx) -> Register {
2900 Register Reg = (Idx == IncorrectArgIdx) ? ConstZero : Call->Arguments[Idx];
2901
2902 if (GR->getSPIRVTypeForVReg(Reg) == SpvFieldTy) {
2903 // Already has the correct type.
2904 return Reg;
2905 }
2906
2908 "Only pointer types are supported for loading values");
2909
2910 Register Ptr = Reg;
2911
2912 Reg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2913 GR->assignSPIRVTypeToVReg(SpvFieldTy, Reg, MF);
2914
2915 MIRBuilder.buildInstr(SPIRV::OpLoad)
2916 .addDef(Reg)
2917 .addUse(GR->getSPIRVTypeID(SpvFieldTy))
2918 .addUse(Ptr);
2919 return Reg;
2920 };
2921
2922 Register GlobalWorkSize = CreateDataRegister(GlobalWorkSizeArgIdx);
2923 Register LocalWorkSize = CreateDataRegister(LocalWorkSizeArgIdx);
2924 Register GlobalWorkOffset = CreateDataRegister(GlobalWorkOffsetArgIdx);
2925
2926 if (!HasSRetArg) {
2927 return MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
2928 .addDef(Call->ReturnRegister)
2929 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2930 .addUse(GlobalWorkSize)
2931 .addUse(LocalWorkSize)
2932 .addUse(GlobalWorkOffset);
2933 }
2934
2935 // When sret is used, store nd_range struct through the pointer in the first
2936 // argument.
2937 Register SRetReg = Call->Arguments[SRetArgIdx];
2939 SRetReg, CB.getArgOperand(SRetArgIdx), MIRBuilder, GR);
2940
2941 Register TmpReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2942 GR->assignSPIRVTypeToVReg(SRetType, TmpReg, MF);
2943
2944 MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
2945 .addDef(TmpReg)
2946 .addUse(GR->getSPIRVTypeID(SRetType))
2947 .addUse(GlobalWorkSize)
2948 .addUse(LocalWorkSize)
2949 .addUse(GlobalWorkOffset);
2950 return MIRBuilder.buildInstr(SPIRV::OpStore)
2951 .addUse(Call->Arguments[SRetArgIdx])
2952 .addUse(TmpReg);
2953}
2954
2956 MachineIRBuilder &MIRBuilder,
2957 SPIRVGlobalRegistry *GR) {
2958 // In this function there are three stages:
2959 // 1. prepare call indexes in order we expect them.
2960 // 2. process all arguments which requered preparation.
2961 // 3. create a SPIRV operator with arguments.
2962
2963 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2964 const DataLayout &DL = MIRBuilder.getDataLayout();
2965 const SPIRVTypeInst Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
2966
2967 // 1. prepare call indexes in order we expect them.
2968 // Based on clang sources, clang/lib/CodeGen/CGBuiltin.cpp, BIenqueue_kernel,
2969 // We expect 4 different layouts of call arguments:
2970 // 1) No events, no vargs: {Queue, Flags, Range, Kernel, Block};
2971 // 2) No events, varargs: {Queue, Flags, Range, Kernel, Block, NumElem,
2972 // ElemPtr};
2973 // 3) events, no varargs: {Queue, Flags, Range, NumEvents,
2974 // EventWaitList, EventRet, Kernel, Block};
2975 // 4) events, varargs: {Queue,
2976 // Flags, Range, NumEvents, EventWaitList, EventRet, Kernel, Block,
2977 // NumElem, ElemPtr};
2978 //
2979 // We also may expect __spirv_EnqueueKernel
2980
2981 bool IsSpirvOp = Call->isSpirvOp();
2982 bool HasEvents = Call->Builtin->name().contains("_events") || IsSpirvOp;
2983 bool HasVarArgs = Call->Builtin->name().contains("_varargs") || IsSpirvOp;
2984
2985 const unsigned NumArgs = Call->Arguments.size();
2986 const unsigned BaseArgIdx = 0;
2987 const unsigned IncorrectIdx = NumArgs + 1;
2988
2989 const unsigned QueueIdx = BaseArgIdx;
2990 const unsigned FlagsIdx = BaseArgIdx + 1;
2991 const unsigned NDRangeIdx = BaseArgIdx + 2;
2992 const unsigned NumEventsIdx = HasEvents ? BaseArgIdx + 3 : IncorrectIdx;
2993 const unsigned WaitEventsIdx = HasEvents ? BaseArgIdx + 4 : IncorrectIdx;
2994 const unsigned RetEventIdx = HasEvents ? BaseArgIdx + 5 : IncorrectIdx;
2995 const unsigned InvokeIdx = BaseArgIdx + 3 + (HasEvents ? 3 : 0);
2996 const unsigned ParamIdx = BaseArgIdx + 4 + (HasEvents ? 3 : 0);
2997 const unsigned LocalSizeNumElemIdx =
2998 HasVarArgs ? (BaseArgIdx + 5 + (HasEvents ? 3 : 0)) : IncorrectIdx;
2999 const unsigned LocalSizeElemPtrIdx =
3000 HasVarArgs ? (BaseArgIdx + 6 + (HasEvents ? 3 : 0)) : IncorrectIdx;
3001
3002 [[maybe_unused]] const unsigned LastArgIdx =
3003 (BaseArgIdx + 4 + (HasEvents ? 3 : 0) + (HasVarArgs ? 2 : 0));
3004 assert(LastArgIdx < NumArgs && "Incorrect number arguments");
3005
3006 // 2. Process all arguments which requered preparation.
3007 // 2.1 Events - use Call arguments, or use dummy nulls in case of absence of
3008 // events
3009
3010 auto BuildDeviceEventNullPtr = [&]() {
3011 LLVMContext &Ctx = MIRBuilder.getMF().getFunction().getContext();
3012 Type *DeviceEventTy = TargetExtType::get(Ctx, "spirv.DeviceEvent");
3013 SPIRVTypeInst DeviceEventPtrTy = GR->getOrCreateSPIRVPointerType(
3014 DeviceEventTy, MIRBuilder, SPIRV::StorageClass::Generic);
3015 return GR->getOrCreateConstNullPtr(MIRBuilder, DeviceEventPtrTy);
3016 };
3017
3018 Register NumEventsReg;
3019 Register WaitEventsReg;
3020 Register RetEventReg;
3021 if (HasEvents) {
3022 auto IsNullEvent = [&](Register R) {
3024 return Def->getOpcode() == TargetOpcode::G_CONSTANT &&
3025 Def->getOperand(1).getCImm()->isZero();
3026 };
3027
3028 NumEventsReg = Call->Arguments[NumEventsIdx];
3029 WaitEventsReg = Call->Arguments[WaitEventsIdx];
3030 RetEventReg = Call->Arguments[RetEventIdx];
3031 if (IsNullEvent(WaitEventsReg))
3032 WaitEventsReg = BuildDeviceEventNullPtr();
3033 if (IsNullEvent(RetEventReg))
3034 RetEventReg = BuildDeviceEventNullPtr();
3035 } else {
3036 NumEventsReg = buildConstantIntReg32(0, MIRBuilder, GR);
3037 Register NullPtr = BuildDeviceEventNullPtr();
3038 WaitEventsReg = NullPtr;
3039 RetEventReg = NullPtr;
3040 }
3041
3042 // 2.2 Invoke (Kernel)
3043 // The Invoke operand of OpEnqueueKernel must be the function's <id>
3044 // (per SPIR-V spec). The frontend hands us the result of an
3045 // addrspacecast of @block_invoke_kernel; bypass that cast so the
3046 // operand references the underlying G_GLOBAL_VALUE register, which
3047 // selectGlobalValue lowers to a placeholder later rewritten by
3048 // SPIRVModuleAnalysis to the OpFunction <id>.
3049 MachineInstr *InvokeGlobalMI =
3050 getBlockStructInstr(Call->Arguments[InvokeIdx], MRI);
3051 assert(InvokeGlobalMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
3052 Register InvokeReg = InvokeGlobalMI->getOperand(0).getReg();
3053 // OpEnqueueKernel's Invoke operand uses the pID register class.
3054 MRI->setRegClass(InvokeReg, &SPIRV::pIDRegClass);
3055
3056 // 2.3 Param, Param Size, Param Align
3057 Register BlockLiteralReg = Call->Arguments[ParamIdx];
3058 const SPIRVTypeInst Int8Ty = GR->getOrCreateSPIRVIntegerType(8, MIRBuilder);
3059 const SPIRVTypeInst Int8PtrGen = GR->getOrCreateSPIRVPointerType(
3060 Int8Ty, MIRBuilder, SPIRV::StorageClass::Generic);
3061 Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
3062
3063 Register ParamReg = createVirtualRegister(Int8PtrGen, GR, MIRBuilder);
3064 MIRBuilder.buildInstr(SPIRV::OpBitcast)
3065 .addDef(ParamReg)
3066 .addUse(GR->getSPIRVTypeID(Int8PtrGen))
3067 .addUse(BlockLiteralReg);
3068 // TODO: these numbers should be obtained from block literal structure.
3069 Register ParamSizeReg =
3070 buildConstantIntReg32(DL.getTypeStoreSize(PType), MIRBuilder, GR);
3071 Register ParamAlignReg =
3072 buildConstantIntReg32(DL.getPrefTypeAlign(PType).value(), MIRBuilder, GR);
3073
3074 // 2.4 Local Size Array
3075 SmallVector<Register, 16> LocalSizes;
3076 if (HasVarArgs) {
3077 Register LocalSizeNumElem = Call->Arguments[LocalSizeNumElemIdx];
3078 MachineInstr *LocalSizeNumElemMI = MRI->getUniqueVRegDef(LocalSizeNumElem);
3079 const MachineOperand &ConstOp = LocalSizeNumElemMI->getOperand(1);
3080 assert(LocalSizeNumElemMI->getOpcode() == TargetOpcode::G_CONSTANT &&
3081 ConstOp.isCImm() && "Expected constant immediate");
3082 uint64_t NumElem = ConstOp.getCImm()->getValue().getZExtValue();
3083
3084 Register LocalSizeArrayReg = Call->Arguments[LocalSizeElemPtrIdx];
3085
3086 for (unsigned i = 0; i < NumElem; ++i) {
3087 Register Reg = MRI->createVirtualRegister(&SPIRV::pIDRegClass);
3088 auto GEPInst = MIRBuilder.buildIntrinsic(
3089 Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false);
3090 GEPInst
3091 .addImm(0) // In bound.
3092 .addUse(LocalSizeArrayReg) // Base pointer.
3093 .addUse(buildConstantIntReg32(0, MIRBuilder, GR)) // Indices.
3094 .addUse(buildConstantIntReg32(i, MIRBuilder, GR));
3095 LocalSizes.push_back(Reg);
3096 }
3097 }
3098
3099 // 3. create a SPIRV operator with arguments.
3100 auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)
3101 .addDef(Call->ReturnRegister)
3102 .addUse(GR->getSPIRVTypeID(Int32Ty))
3103 .addUse(Call->Arguments[QueueIdx])
3104 .addUse(Call->Arguments[FlagsIdx])
3105 .addUse(Call->Arguments[NDRangeIdx])
3106 .addUse(NumEventsReg)
3107 .addUse(WaitEventsReg)
3108 .addUse(RetEventReg)
3109 .addUse(InvokeReg)
3110 .addUse(ParamReg)
3111 .addUse(ParamSizeReg)
3112 .addUse(ParamAlignReg);
3113 for (auto &LocalSize : LocalSizes)
3114 MIB.addUse(LocalSize);
3115
3116 return true;
3117}
3118
3120 MachineIRBuilder &MIRBuilder,
3121 SPIRVGlobalRegistry *GR, const CallBase &CB) {
3122 // Lookup the instruction opcode in the TableGen records.
3123 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3124 unsigned Opcode =
3125 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3126
3127 switch (Opcode) {
3128 case SPIRV::OpRetainEvent:
3129 case SPIRV::OpReleaseEvent:
3130 return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);
3131 case SPIRV::OpCreateUserEvent:
3132 case SPIRV::OpGetDefaultQueue:
3133 return MIRBuilder.buildInstr(Opcode)
3134 .addDef(Call->ReturnRegister)
3135 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
3136 case SPIRV::OpIsValidEvent:
3137 return MIRBuilder.buildInstr(Opcode)
3138 .addDef(Call->ReturnRegister)
3139 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
3140 .addUse(Call->Arguments[0]);
3141 case SPIRV::OpSetUserEventStatus:
3142 return MIRBuilder.buildInstr(Opcode)
3143 .addUse(Call->Arguments[0])
3144 .addUse(Call->Arguments[1]);
3145 case SPIRV::OpCaptureEventProfilingInfo:
3146 return MIRBuilder.buildInstr(Opcode)
3147 .addUse(Call->Arguments[0])
3148 .addUse(Call->Arguments[1])
3149 .addUse(Call->Arguments[2]);
3150 case SPIRV::OpBuildNDRange:
3151 return buildNDRange(Call, MIRBuilder, GR, CB);
3152 case SPIRV::OpEnqueueKernel:
3153 return buildEnqueueKernel(Call, MIRBuilder, GR);
3154 default:
3155 return false;
3156 }
3157}
3158
3160 MachineIRBuilder &MIRBuilder,
3161 SPIRVGlobalRegistry *GR, const CallBase &CB) {
3162 // Lookup the instruction opcode in the TableGen records.
3163 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3164 unsigned Opcode =
3165 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3166
3167 bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;
3168 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
3169 if (Call->isSpirvOp())
3170 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
3171 IsSet ? TypeReg : Register(0));
3172
3173 auto Scope = buildConstantIntReg32(SPIRV::Scope::Workgroup, MIRBuilder, GR);
3174
3175 switch (Opcode) {
3176 case SPIRV::OpGroupAsyncCopy: {
3177 SPIRVTypeInst NewType =
3178 Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent
3179 ? nullptr
3180 : GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder, true);
3181 Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);
3182 unsigned NumArgs = Call->Arguments.size();
3183 Register EventReg = Call->Arguments[NumArgs - 1];
3184 SPIRVTypeInst EventType = GR->getSPIRVTypeForVReg(EventReg);
3185 if (!EventType || EventType->getOpcode() != SPIRV::OpTypeEvent) {
3186 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
3187 Register ConstReg = EventReg;
3188 MachineInstr *Def = getDefInstrMaybeConstant(ConstReg, MRI);
3189 if (Def->getOpcode() == TargetOpcode::G_CONSTANT &&
3190 Def->getOperand(1).getCImm()->isZero()) {
3191 // Only substitute a null Event for the "ptr null" idiom, not for a
3192 // real event value that just is not typed as OpTypeEvent yet.
3193 SPIRVTypeInst EventTy = NewType ? NewType
3195 "spirv.Event", MIRBuilder, true);
3196 Register EventTyReg = GR->getSPIRVTypeID(EventTy);
3197 Register NullEventReg = createVirtualRegister(EventTy, GR, MIRBuilder);
3198 MIRBuilder.buildInstr(SPIRV::OpConstantNull)
3199 .addDef(NullEventReg)
3200 .addUse(EventTyReg);
3201 EventReg = NullEventReg;
3202 }
3203 }
3204 Register NumElemReg = Call->Arguments[2];
3205
3206 // Untyped pointers use OpUntypedGroupAsyncCopyKHR, which adds an explicit
3207 // Element Num Bytes operand.
3208 SPIRVTypeInst DestPtrTy = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
3209 bool IsUntyped =
3210 DestPtrTy && DestPtrTy->getOpcode() == SPIRV::OpTypeUntypedPointerKHR;
3211 SPIRVTypeInst SizeTy = GR->getSPIRVTypeForVReg(NumElemReg);
3212 Register StrideReg =
3213 Call->Arguments.size() > 4
3214 ? Call->Arguments[3]
3215 : (IsUntyped ? GR->buildConstantInt(1, MIRBuilder, SizeTy,
3216 /*EmitIR=*/true)
3217 : buildConstantIntReg32(1, MIRBuilder, GR));
3218
3219 auto MIB = MIRBuilder
3220 .buildInstr(IsUntyped ? SPIRV::OpUntypedGroupAsyncCopyKHR
3221 : SPIRV::OpGroupAsyncCopy)
3222 .addDef(Call->ReturnRegister)
3223 .addUse(TypeReg)
3224 .addUse(Scope)
3225 .addUse(Call->Arguments[0])
3226 .addUse(Call->Arguments[1]);
3227 if (IsUntyped) {
3228 // Element Num Bytes from the deduced element type of dest (or source).
3229 unsigned ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(0));
3230 if (!ElemBytes)
3231 ElemBytes = GR->getDeducedPointeeByteSize(CB.getArgOperand(1));
3232 if (!ElemBytes)
3233 report_fatal_error("Could not deduce the element type of an untyped "
3234 "async copy pointer argument");
3235 MIB.addUse(GR->buildConstantInt(ElemBytes, MIRBuilder, SizeTy,
3236 /*EmitIR=*/true));
3237 }
3238 MIB.addUse(NumElemReg);
3239 MIB.addUse(StrideReg);
3240 MIB.addUse(EventReg);
3241 if (NewType)
3242 updateRegType(Call->ReturnRegister, /*Ty=*/nullptr, NewType, GR,
3243 MIRBuilder, MIRBuilder.getMF().getRegInfo());
3244 return true;
3245 }
3246 case SPIRV::OpGroupWaitEvents:
3247 return MIRBuilder.buildInstr(Opcode)
3248 .addUse(Scope)
3249 .addUse(Call->Arguments[0])
3250 .addUse(Call->Arguments[1]);
3251 default:
3252 return false;
3253 }
3254}
3255
3256static bool generateConvertInst(StringRef DemangledCall,
3258 MachineIRBuilder &MIRBuilder,
3259 SPIRVGlobalRegistry *GR) {
3260 // Lookup the conversion builtin in the TableGen records.
3261 const SPIRV::ConvertBuiltin *Builtin =
3262 SPIRV::lookupConvertBuiltin(Call->Builtin->name(), Call->Builtin->Set);
3263
3264 if (!Builtin && Call->isSpirvOp()) {
3265 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3266 unsigned Opcode =
3267 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3268 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
3269 GR->getSPIRVTypeID(Call->ReturnType));
3270 }
3271
3272 assert(Builtin && "Conversion builtin not found.");
3273 if (Builtin->IsSaturated)
3274 buildOpDecorate(Call->ReturnRegister, MIRBuilder,
3275 SPIRV::Decoration::SaturatedConversion, {});
3276
3277 if (Builtin->IsRounded) {
3278 bool AnyTypeIsFloat =
3279 GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeFloat) ||
3280 GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeFloat);
3281
3282 // Rounding mode decorations are only valid for floating point types.
3283 // Conversion builtins from integer to integer are equivalent to their
3284 // non-rounded counterparts.
3285 if (AnyTypeIsFloat) {
3286 buildOpDecorate(Call->ReturnRegister, MIRBuilder,
3287 SPIRV::Decoration::FPRoundingMode,
3288 {(unsigned)Builtin->RoundingMode});
3289 }
3290 }
3291
3292 std::string NeedExtMsg; // no errors if empty
3293 bool IsRightComponentsNumber = true; // check if input/output accepts vectors
3294 unsigned Opcode = SPIRV::OpNop;
3295 if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {
3296 // Int -> ...
3297 bool IsSourceSigned =
3298 DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';
3299 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
3300 // Int -> Int
3301 if (Builtin->IsSaturated)
3302 Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS
3303 : SPIRV::OpSatConvertSToU;
3304 else
3305 Opcode = IsSourceSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
3306 } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
3307 SPIRV::OpTypeFloat)) {
3308 // Int -> Float
3309 if (Builtin->IsBfloat16) {
3310 const auto *ST = static_cast<const SPIRVSubtarget *>(
3311 &MIRBuilder.getMF().getSubtarget());
3312 if (!ST->canUseExtension(
3313 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
3314 NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
3315 IsRightComponentsNumber =
3316 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
3317 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
3318 Opcode = SPIRV::OpConvertBF16ToFINTEL;
3319 } else {
3320 Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;
3321 }
3322 }
3323 } else if (GR->isScalarOrVectorOfType(Call->Arguments[0],
3324 SPIRV::OpTypeFloat)) {
3325 // Float -> ...
3326 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
3327 // Float -> Int
3328 if (Builtin->IsBfloat16) {
3329 const auto *ST = static_cast<const SPIRVSubtarget *>(
3330 &MIRBuilder.getMF().getSubtarget());
3331 if (!ST->canUseExtension(
3332 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
3333 NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
3334 IsRightComponentsNumber =
3335 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
3336 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
3337 Opcode = SPIRV::OpConvertFToBF16INTEL;
3338 } else {
3339 Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS
3340 : SPIRV::OpConvertFToU;
3341 }
3342 } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
3343 SPIRV::OpTypeFloat)) {
3344 if (Builtin->IsTF32) {
3345 const auto *ST = static_cast<const SPIRVSubtarget *>(
3346 &MIRBuilder.getMF().getSubtarget());
3347 if (!ST->canUseExtension(
3348 SPIRV::Extension::SPV_INTEL_tensor_float32_conversion))
3349 NeedExtMsg = "SPV_INTEL_tensor_float32_conversion";
3350 IsRightComponentsNumber =
3351 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
3352 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
3353 Opcode = SPIRV::OpRoundFToTF32INTEL;
3354 } else {
3355 // Float -> Float
3356 Opcode = SPIRV::OpFConvert;
3357 }
3358 }
3359 }
3360
3361 StringRef BuiltinName = SPIRV::getConvertBuiltinStr(Builtin->Name);
3362 if (!NeedExtMsg.empty()) {
3363 std::string DiagMsg = std::string(BuiltinName) +
3364 ": the builtin requires the following SPIR-V "
3365 "extension: " +
3366 NeedExtMsg;
3367 report_fatal_error(DiagMsg.c_str(), false);
3368 }
3369 if (!IsRightComponentsNumber) {
3370 std::string DiagMsg =
3371 std::string(BuiltinName) +
3372 ": result and argument must have the same number of components";
3373 report_fatal_error(DiagMsg.c_str(), false);
3374 }
3375 assert(Opcode != SPIRV::OpNop &&
3376 "Conversion between the types not implemented!");
3377
3378 MIRBuilder.buildInstr(Opcode)
3379 .addDef(Call->ReturnRegister)
3380 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
3381 .addUse(Call->Arguments[0]);
3382 return true;
3383}
3384
3386 MachineIRBuilder &MIRBuilder,
3387 SPIRVGlobalRegistry *GR) {
3388 // Lookup the vector load/store builtin in the TableGen records.
3389 const SPIRV::VectorLoadStoreBuiltin *Builtin =
3390 SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->name(),
3391 Call->Builtin->Set);
3392 // Build extended instruction.
3393 auto MIB =
3394 MIRBuilder.buildInstr(SPIRV::OpExtInst)
3395 .addDef(Call->ReturnRegister)
3396 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
3397 .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
3398 .addImm(Builtin->Number);
3399 for (auto Argument : Call->Arguments)
3400 MIB.addUse(Argument);
3401 StringRef BuiltinName = SPIRV::getVectorLoadStoreBuiltinStr(Builtin->Name);
3402 if (BuiltinName.contains("load") && Builtin->ElementCount > 1)
3403 MIB.addImm(Builtin->ElementCount);
3404
3405 // Rounding mode should be passed as a last argument in the MI for builtins
3406 // like "vstorea_halfn_r".
3407 if (Builtin->IsRounded)
3408 MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));
3409 return true;
3410}
3411
3413 MachineIRBuilder &MIRBuilder,
3414 SPIRVGlobalRegistry *GR) {
3415 const auto *Builtin = Call->Builtin;
3416 auto *MRI = MIRBuilder.getMRI();
3417 unsigned Opcode =
3418 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3419 const Type *RetTy = GR->getTypeForSPIRVType(Call->ReturnType);
3420 bool IsVoid = RetTy->isVoidTy();
3421 auto MIB = MIRBuilder.buildInstr(Opcode);
3422 Register DestReg;
3423 if (IsVoid) {
3424 LLT PtrTy = MRI->getType(Call->Arguments[0]);
3425 DestReg = MRI->createGenericVirtualRegister(PtrTy);
3426 MRI->setRegClass(DestReg, &SPIRV::pIDRegClass);
3427 SPIRVTypeInst PointeeTy =
3428 GR->getPointeeType(GR->getSPIRVTypeForVReg(Call->Arguments[0]));
3429 MIB.addDef(DestReg);
3430 MIB.addUse(GR->getSPIRVTypeID(PointeeTy));
3431 } else {
3432 MIB.addDef(Call->ReturnRegister);
3433 MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
3434 }
3435 for (unsigned i = IsVoid ? 1 : 0; i < Call->Arguments.size(); ++i) {
3436 Register Arg = Call->Arguments[i];
3437 MachineInstr *DefMI = MRI->getUniqueVRegDef(Arg);
3438 if (DefMI->getOpcode() == TargetOpcode::G_CONSTANT &&
3439 DefMI->getOperand(1).isCImm()) {
3440 MIB.addImm(getIConstVal(Arg, MRI));
3441 } else {
3442 MIB.addUse(Arg);
3443 }
3444 }
3445 if (IsVoid) {
3446 LLT PtrTy = MRI->getType(Call->Arguments[0]);
3447 MachineMemOperand *MMO = MIRBuilder.getMF().getMachineMemOperand(
3449 PtrTy.getSizeInBytes(), Align(4));
3450 MIRBuilder.buildStore(DestReg, Call->Arguments[0], *MMO);
3451 }
3452 return true;
3453}
3454
3456 MachineIRBuilder &MIRBuilder,
3457 SPIRVGlobalRegistry *GR) {
3458 // Lookup the instruction opcode in the TableGen records.
3459 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
3460 unsigned Opcode =
3461 SPIRV::lookupNativeBuiltin(Builtin->name(), Builtin->Set)->Opcode;
3462 bool IsLoad = Opcode == SPIRV::OpLoad;
3463 // Build the instruction.
3464 auto MIB = MIRBuilder.buildInstr(Opcode);
3465 if (IsLoad) {
3466 MIB.addDef(Call->ReturnRegister);
3467 MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
3468 }
3469 // Add a pointer to the value to load/store.
3470 MIB.addUse(Call->Arguments[0]);
3471 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
3472 // Add a value to store.
3473 if (!IsLoad)
3474 MIB.addUse(Call->Arguments[1]);
3475 // Add optional memory attributes and an alignment.
3476 unsigned NumArgs = Call->Arguments.size();
3477 if ((IsLoad && NumArgs >= 2) || NumArgs >= 3)
3478 MIB.addImm(getIConstVal(Call->Arguments[IsLoad ? 1 : 2], MRI));
3479 if ((IsLoad && NumArgs >= 3) || NumArgs >= 4)
3480 MIB.addImm(getIConstVal(Call->Arguments[IsLoad ? 2 : 3], MRI));
3481 return true;
3482}
3483
3484namespace SPIRV {
3485// Try to find a builtin function attributes by a demangled function name and
3486// return a tuple <builtin group, op code, ext instruction number>, or a special
3487// tuple value <-1, 0, 0> if the builtin function is not found.
3488// Not all builtin functions are supported, only those with a ready-to-use op
3489// code or instruction number defined in TableGen.
3490// TODO: consider a major rework of mapping demangled calls into a builtin
3491// functions to unify search and decrease number of individual cases.
3492std::tuple<int, unsigned, unsigned>
3494 SPIRV::InstructionSet::InstructionSet Set) {
3495 Register Reg;
3497 std::unique_ptr<const IncomingCall> Call =
3498 lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args);
3499 if (!Call)
3500 return std::make_tuple(-1, 0, 0);
3501
3502 switch (Call->Builtin->Group) {
3503 case SPIRV::Relational:
3504 case SPIRV::Atomic:
3505 case SPIRV::Barrier:
3506 case SPIRV::CastToPtr:
3507 case SPIRV::ImageMiscQuery:
3508 case SPIRV::SpecConstant:
3509 case SPIRV::Enqueue:
3510 case SPIRV::AsyncCopy:
3511 case SPIRV::LoadStore:
3512 case SPIRV::CoopMatr:
3513 case SPIRV::Arithmetic:
3514 if (const auto *R = SPIRV::lookupNativeBuiltin(Call->Builtin->name(),
3515 Call->Builtin->Set))
3516 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3517 break;
3518 case SPIRV::Extended:
3519 if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->name(),
3520 Call->Builtin->Set))
3521 return std::make_tuple(Call->Builtin->Group, 0, R->Number);
3522 break;
3523 case SPIRV::VectorLoadStore:
3524 if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(
3525 Call->Builtin->name(), Call->Builtin->Set))
3526 return std::make_tuple(SPIRV::Extended, 0, R->Number);
3527 break;
3528 case SPIRV::Group:
3529 if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->name()))
3530 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3531 break;
3532 case SPIRV::AtomicFloating:
3533 if (const auto *R =
3534 SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->name()))
3535 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3536 break;
3537 case SPIRV::IntelSubgroups:
3538 if (const auto *R =
3539 SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->name()))
3540 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3541 break;
3542 case SPIRV::GroupUniform:
3543 if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->name()))
3544 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3545 break;
3546 case SPIRV::IntegerDot:
3547 if (const auto *R =
3548 SPIRV::lookupIntegerDotProductBuiltin(Call->Builtin->name()))
3549 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
3550 break;
3551 case SPIRV::WriteImage:
3552 return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0);
3553 case SPIRV::Select:
3554 return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0);
3555 case SPIRV::Construct:
3556 return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct,
3557 0);
3558 case SPIRV::KernelClock:
3559 return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0);
3560 default:
3561 return std::make_tuple(-1, 0, 0);
3562 }
3563 return std::make_tuple(-1, 0, 0);
3564}
3565
3566/// Checks that scalar/vector numeric arguments of \p Call match the types
3567/// implied by their mangling in \p DemangledCall. Pointers and opaque
3568/// builtin types (images, samplers, pipes, etc.) are not validated here, as
3569/// mangling does not enforce their exact spelling.
3570///
3571/// \returns false if a numeric argument's SPIR-V type disagrees with the
3572/// type implied by the mangled name, true otherwise.
3574 StringRef DemangledCall,
3576 const CallBase &CB) {
3577 if (Call->isSpirvOp())
3578 return true;
3579
3580 SmallVector<StringRef, 10> ArgTypeStrs;
3581 if (!SPIRV::parseBuiltinTypeStr(ArgTypeStrs, DemangledCall, Ctx))
3582 return true;
3583
3584 unsigned ArgBase = CB.hasStructRetAttr() ? 1 : 0;
3585 if (Call->Arguments.size() < ArgBase)
3586 return true;
3587 unsigned NumMangledArgs = Call->Arguments.size() - ArgBase;
3588 unsigned NumArgsToCheck =
3589 std::min<unsigned>(NumMangledArgs, ArgTypeStrs.size());
3590 for (unsigned ArgIdx = 0; ArgIdx < NumArgsToCheck; ++ArgIdx) {
3591 StringRef ArgTypeStr = ArgTypeStrs[ArgIdx].trim();
3592 // Opaque/builtin OpenCL and SPIR-V types (images, samplers, pipes,
3593 // reserve_id, etc.) are not validated here, as mangling does not enforce
3594 // their exact spelling, and some builtin type names have no TableGen
3595 // record and would otherwise abort compilation when parsed.
3596 if (hasBuiltinTypePrefix(ArgTypeStr))
3597 continue;
3598
3599 Type *ExpectedType = SPIRV::parseBuiltinCallArgumentType(ArgTypeStr, Ctx);
3600 if (!ExpectedType || ExpectedType->isVoidTy() ||
3601 ExpectedType->isPointerTy() || ExpectedType->isTargetExtTy())
3602 continue;
3603
3604 SPIRVTypeInst ArgType =
3605 GR->getSPIRVTypeForVReg(Call->Arguments[ArgIdx + ArgBase]);
3606 if (!ArgType)
3607 continue;
3608 unsigned ArgTypeOpcode = ArgType->getOpcode();
3609 if (ArgTypeOpcode != SPIRV::OpTypeInt &&
3610 ArgTypeOpcode != SPIRV::OpTypeFloat &&
3611 ArgTypeOpcode != SPIRV::OpTypeBool &&
3612 ArgTypeOpcode != SPIRV::OpTypeVector)
3613 continue;
3614
3615 auto *ExpectedVecType = dyn_cast<VectorType>(ExpectedType);
3616 Type *ExpectedScalarType =
3617 ExpectedVecType ? ExpectedVecType->getElementType() : ExpectedType;
3618 SPIRVTypeInst ArgScalarType = GR->getScalarOrVectorComponentType(ArgType);
3619 if (!ArgScalarType)
3620 continue;
3621
3622 bool ExpectedIsInt = ExpectedScalarType->isIntegerTy();
3623 unsigned ArgOpcode = ArgScalarType->getOpcode();
3624 bool ArgIsInt =
3625 ArgOpcode == SPIRV::OpTypeInt || ArgOpcode == SPIRV::OpTypeBool;
3626
3627 if (ExpectedIsInt != ArgIsInt)
3628 return false;
3629
3630 unsigned ExpectedElts =
3631 ExpectedVecType ? ExpectedVecType->getElementCount().getFixedValue()
3632 : 1;
3633 if (ExpectedElts != GR->getScalarOrVectorComponentCount(ArgType))
3634 return false;
3635 }
3636 return true;
3637}
3638
3639std::optional<bool> lowerBuiltin(StringRef DemangledCall,
3640 SPIRV::InstructionSet::InstructionSet Set,
3641 MachineIRBuilder &MIRBuilder,
3642 const Register OrigRet, const Type *OrigRetTy,
3643 const SmallVectorImpl<Register> &Args,
3644 SPIRVGlobalRegistry *GR, const CallBase &CB) {
3645 LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");
3646
3647 // Lookup the builtin in the TableGen records.
3648 SPIRVTypeInst SpvType = GR->getSPIRVTypeForVReg(OrigRet);
3649 assert(SpvType && "Inconsistent return register: expected valid type info");
3650 std::unique_ptr<const IncomingCall> Call =
3651 lookupBuiltin(DemangledCall, Set, OrigRet, SpvType, Args);
3652
3653 if (!Call) {
3654 LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
3655 return std::nullopt;
3656 }
3657
3658 // Check if the provided args meet the builtin requirements. If not, treat
3659 // the call as a regular function call rather than crashing.
3660 if (Args.size() < Call->Builtin->MinNumArgs) {
3661 LLVM_DEBUG(dbgs() << "Too few arguments for builtin " << DemangledCall
3662 << ": expected at least " << Call->Builtin->MinNumArgs
3663 << ", got " << Args.size()
3664 << "; treating as a normal function\n");
3665 return std::nullopt;
3666 }
3667 if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs) {
3668 LLVM_DEBUG(dbgs() << "Too many arguments for builtin " << DemangledCall
3669 << ": expected at most " << Call->Builtin->MaxNumArgs
3670 << ", got " << Args.size()
3671 << "; treating as a normal function\n");
3672 return std::nullopt;
3673 }
3674
3675 // Check that argument types match what the mangling implies. If not
3676 // (e.g. broken mangling), treat the call as a regular function call
3677 // rather than crashing.
3678 if (!demangledArgTypesMatchIR(Call.get(), DemangledCall, GR,
3679 MIRBuilder.getContext(), CB)) {
3680 LLVM_DEBUG(dbgs() << "Argument types do not match mangled types for "
3681 << "builtin " << DemangledCall
3682 << "; treating as a normal function\n");
3683 return std::nullopt;
3684 }
3685
3686 // Match the builtin with implementation based on the grouping.
3687 switch (Call->Builtin->Group) {
3688 case SPIRV::Extended:
3689 return generateExtInst(Call.get(), MIRBuilder, GR, CB);
3690 case SPIRV::Relational:
3691 return generateRelationalInst(Call.get(), MIRBuilder, GR);
3692 case SPIRV::Group:
3693 return generateGroupInst(Call.get(), MIRBuilder, GR);
3694 case SPIRV::Variable:
3695 return generateBuiltinVar(Call.get(), MIRBuilder, GR);
3696 case SPIRV::Atomic:
3697 return generateAtomicInst(Call.get(), MIRBuilder, GR);
3698 case SPIRV::AtomicFloating:
3699 return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);
3700 case SPIRV::Barrier:
3701 return generateBarrierInst(Call.get(), MIRBuilder, GR);
3702 case SPIRV::CastToPtr:
3703 return generateCastToPtrInst(Call.get(), MIRBuilder, GR);
3704 case SPIRV::Dot:
3705 case SPIRV::IntegerDot:
3706 return generateDotOrFMulInst(DemangledCall, Call.get(), MIRBuilder, GR);
3707 case SPIRV::Wave:
3708 return generateWaveInst(Call.get(), MIRBuilder, GR);
3709 case SPIRV::ICarryBorrow:
3710 return generateICarryBorrowInst(Call.get(), MIRBuilder, GR, CB);
3711 case SPIRV::MulExtended:
3712 return generateMulExtendedInst(Call.get(), MIRBuilder, GR, CB);
3713 case SPIRV::Arithmetic:
3714 return generateArithmeticInst(Call.get(), MIRBuilder, GR);
3715 case SPIRV::GetQuery:
3716 return generateGetQueryInst(Call.get(), MIRBuilder, GR);
3717 case SPIRV::ImageSizeQuery:
3718 return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);
3719 case SPIRV::ImageMiscQuery:
3720 return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);
3721 case SPIRV::ReadImage:
3722 return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
3723 case SPIRV::WriteImage:
3724 return generateWriteImageInst(Call.get(), MIRBuilder, GR);
3725 case SPIRV::SampleImage:
3726 return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
3727 case SPIRV::Select:
3728 return generateSelectInst(Call.get(), MIRBuilder);
3729 case SPIRV::Construct:
3730 return generateConstructInst(Call.get(), MIRBuilder, GR);
3731 case SPIRV::SpecConstant:
3732 return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
3733 case SPIRV::Enqueue:
3734 return generateEnqueueInst(Call.get(), MIRBuilder, GR, CB);
3735 case SPIRV::AsyncCopy:
3736 return generateAsyncCopy(Call.get(), MIRBuilder, GR, CB);
3737 case SPIRV::Convert:
3738 return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
3739 case SPIRV::VectorLoadStore:
3740 return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);
3741 case SPIRV::LoadStore:
3742 return generateLoadStoreInst(Call.get(), MIRBuilder, GR);
3743 case SPIRV::IntelSubgroups:
3744 return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR);
3745 case SPIRV::GroupUniform:
3746 return generateGroupUniformInst(Call.get(), MIRBuilder, GR);
3747 case SPIRV::KernelClock:
3748 return generateKernelClockInst(Call.get(), MIRBuilder, GR);
3749 case SPIRV::CoopMatr:
3750 return generateCoopMatrInst(Call.get(), MIRBuilder, GR);
3751 case SPIRV::ExtendedBitOps:
3752 return generateExtendedBitOpsInst(Call.get(), MIRBuilder, GR);
3753 case SPIRV::BindlessINTEL:
3754 return generateBindlessImageINTELInst(Call.get(), MIRBuilder, GR);
3755 case SPIRV::TernaryBitwiseINTEL:
3756 return generateTernaryBitwiseFunctionINTELInst(Call.get(), MIRBuilder, GR);
3757 case SPIRV::Block2DLoadStore:
3758 return generate2DBlockIOINTELInst(Call.get(), MIRBuilder, GR);
3759 case SPIRV::Pipe:
3760 return generatePipeInst(Call.get(), MIRBuilder, GR);
3761 case SPIRV::PredicatedLoadStore:
3762 return generatePredicatedLoadStoreInst(Call.get(), MIRBuilder, GR);
3763 case SPIRV::BlockingPipes:
3764 return generateBlockingPipesInst(Call.get(), MIRBuilder, GR);
3765 case SPIRV::ArbitraryPrecisionFixedPoint:
3766 return generateAPFixedPointInst(Call.get(), MIRBuilder, GR, CB);
3767 case SPIRV::ImageChannelDataTypes:
3768 return generateImageChannelDataTypeInst(Call.get(), MIRBuilder, GR);
3769 case SPIRV::ArbitraryFloatingPoint:
3770 return generateAFPInst(Call.get(), MIRBuilder, GR);
3771 }
3772 return false;
3773}
3774
3776 // Parse strings representing OpenCL builtin types.
3777 if (hasBuiltinTypePrefix(TypeStr)) {
3778 // OpenCL builtin types in demangled call strings have the following format:
3779 // e.g. ocl_image2d_ro
3780 [[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_");
3781 assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix");
3782
3783 // Check if this is pointer to a builtin type and not just pointer
3784 // representing a builtin type. In case it is a pointer to builtin type,
3785 // this will require additional handling in the method calling
3786 // parseBuiltinCallArgumentBaseType(...) as this function only retrieves the
3787 // base types.
3788 if (TypeStr.ends_with("*"))
3789 TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));
3790
3791 return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",
3792 Ctx);
3793 }
3794
3795 // Parse type name in either "typeN" or "type vector[N]" format, where
3796 // N is the number of elements of the vector.
3797 Type *BaseType;
3798 unsigned VecElts = 0;
3799
3800 BaseType = parseBasicTypeName(TypeStr, Ctx);
3801 if (!BaseType)
3802 // Unable to recognize SPIRV type name.
3803 return nullptr;
3804
3805 // Handle "typeN*" or "type vector[N]*".
3806 TypeStr.consume_back("*");
3807
3808 if (TypeStr.consume_front(" vector["))
3809 TypeStr = TypeStr.substr(0, TypeStr.find(']'));
3810
3811 TypeStr.getAsInteger(10, VecElts);
3812 if (VecElts > 0)
3814 BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);
3815
3816 return BaseType;
3817}
3818
3820 StringRef DemangledCall, LLVMContext &Ctx) {
3821 auto Pos1 = DemangledCall.find('(');
3822 if (Pos1 == StringRef::npos)
3823 return false;
3824 auto Pos2 = DemangledCall.find(')');
3825 if (Pos2 == StringRef::npos || Pos1 > Pos2)
3826 return false;
3827 DemangledCall.slice(Pos1 + 1, Pos2)
3828 .split(BuiltinArgsTypeStrs, ',', -1, false);
3829 return true;
3830}
3831
3832Type *parseBuiltinCallArgumentBaseType(StringRef DemangledCall, unsigned ArgIdx,
3833 LLVMContext &Ctx) {
3834 SmallVector<StringRef, 10> BuiltinArgsTypeStrs;
3835 parseBuiltinTypeStr(BuiltinArgsTypeStrs, DemangledCall, Ctx);
3836 if (ArgIdx >= BuiltinArgsTypeStrs.size())
3837 return nullptr;
3838 StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim();
3839 return parseBuiltinCallArgumentType(TypeStr, Ctx);
3840}
3841
3846
3847#define GET_BuiltinTypes_DECL
3848#define GET_BuiltinTypes_IMPL
3849
3854
3855#define GET_OpenCLTypes_DECL
3856#define GET_OpenCLTypes_IMPL
3857
3858#include "SPIRVGenTables.inc"
3859} // namespace SPIRV
3860
3861//===----------------------------------------------------------------------===//
3862// Misc functions for parsing builtin types.
3863//===----------------------------------------------------------------------===//
3864
3866 if (Name.starts_with("void"))
3867 return Type::getVoidTy(Context);
3868 else if (Name.starts_with("int") || Name.starts_with("uint"))
3869 return Type::getInt32Ty(Context);
3870 else if (Name.starts_with("bfloat"))
3871 return Type::getBFloatTy(Context);
3872 else if (Name.starts_with("float"))
3873 return Type::getFloatTy(Context);
3874 else if (Name.starts_with("half"))
3875 return Type::getHalfTy(Context);
3876 else if (Name.starts_with("double"))
3877 return Type::getDoubleTy(Context);
3878 report_fatal_error("Unable to recognize type!");
3879}
3880
3881//===----------------------------------------------------------------------===//
3882// Implementation functions for builtin types.
3883//===----------------------------------------------------------------------===//
3884
3885static SPIRVTypeInst
3887 const SPIRV::BuiltinType *TypeRecord,
3888 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
3889 unsigned Opcode = TypeRecord->Opcode;
3890 // Create or get an existing type from GlobalRegistry.
3891 return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode);
3892}
3893
3895 SPIRVGlobalRegistry *GR) {
3896 // Create or get an existing type from GlobalRegistry.
3897 return GR->getOrCreateOpTypeSampler(MIRBuilder);
3898}
3899
3900static SPIRVTypeInst getPipeType(const TargetExtType *ExtensionType,
3901 MachineIRBuilder &MIRBuilder,
3902 SPIRVGlobalRegistry *GR) {
3903 assert(ExtensionType->getNumIntParameters() == 1 &&
3904 "Invalid number of parameters for SPIR-V pipe builtin!");
3905 // Create or get an existing type from GlobalRegistry.
3906 return GR->getOrCreateOpTypePipe(MIRBuilder,
3907 SPIRV::AccessQualifier::AccessQualifier(
3908 ExtensionType->getIntParameter(0)));
3909}
3910
3911static SPIRVTypeInst getCoopMatrType(const TargetExtType *ExtensionType,
3912 MachineIRBuilder &MIRBuilder,
3913 SPIRVGlobalRegistry *GR) {
3914 assert(ExtensionType->getNumIntParameters() == 4 &&
3915 "Invalid number of parameters for SPIR-V coop matrices builtin!");
3916 assert(ExtensionType->getNumTypeParameters() == 1 &&
3917 "SPIR-V coop matrices builtin type must have a type parameter!");
3918 SPIRVTypeInst ElemType =
3919 GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder,
3920 SPIRV::AccessQualifier::ReadWrite, true);
3921 // Create or get an existing type from GlobalRegistry.
3922 return GR->getOrCreateOpTypeCoopMatr(
3923 MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0),
3924 ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
3925 ExtensionType->getIntParameter(3), true);
3926}
3927
3929 MachineIRBuilder &MIRBuilder,
3930 SPIRVGlobalRegistry *GR) {
3931 SPIRVTypeInst OpaqueImageType = GR->getImageType(
3932 OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder);
3933 // Create or get an existing type from GlobalRegistry.
3934 return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
3935}
3936
3938 MachineIRBuilder &MIRBuilder,
3939 SPIRVGlobalRegistry *GR) {
3940 assert(ExtensionType->getNumIntParameters() == 3 &&
3941 "Inline SPIR-V type builtin takes an opcode, size, and alignment "
3942 "parameter");
3943 auto Opcode = ExtensionType->getIntParameter(0);
3944
3946 for (Type *Param : ExtensionType->type_params()) {
3947 if (const TargetExtType *ParamEType = dyn_cast<TargetExtType>(Param)) {
3948 if (ParamEType->getName() == "spirv.IntegralConstant") {
3949 assert(ParamEType->getNumTypeParameters() == 1 &&
3950 "Inline SPIR-V integral constant builtin must have a type "
3951 "parameter");
3952 assert(ParamEType->getNumIntParameters() == 1 &&
3953 "Inline SPIR-V integral constant builtin must have a "
3954 "value parameter");
3955
3956 auto OperandValue = ParamEType->getIntParameter(0);
3957 auto *OperandType = ParamEType->getTypeParameter(0);
3958
3959 SPIRVTypeInst OperandSPIRVType = GR->getOrCreateSPIRVType(
3960 OperandType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, true);
3961
3963 OperandValue, MIRBuilder, OperandSPIRVType, true)));
3964 continue;
3965 } else if (ParamEType->getName() == "spirv.Literal") {
3966 assert(ParamEType->getNumTypeParameters() == 0 &&
3967 "Inline SPIR-V literal builtin does not take type "
3968 "parameters");
3969 assert(ParamEType->getNumIntParameters() == 1 &&
3970 "Inline SPIR-V literal builtin must have an integer "
3971 "parameter");
3972
3973 auto OperandValue = ParamEType->getIntParameter(0);
3974
3975 Operands.push_back(MCOperand::createImm(OperandValue));
3976 continue;
3977 }
3978 }
3979 SPIRVTypeInst TypeOperand = GR->getOrCreateSPIRVType(
3980 Param, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, true);
3981 Operands.push_back(MCOperand::createReg(GR->getSPIRVTypeID(TypeOperand)));
3982 }
3983
3984 return GR->getOrCreateUnknownType(ExtensionType, MIRBuilder, Opcode,
3985 Operands);
3986}
3987
3989 MachineIRBuilder &MIRBuilder,
3990 SPIRVGlobalRegistry *GR) {
3991 assert(ExtensionType->getNumTypeParameters() == 1 &&
3992 "Vulkan buffers have exactly one type for the type of the buffer.");
3993 assert(ExtensionType->getNumIntParameters() == 2 &&
3994 "Vulkan buffer have 2 integer parameters: storage class and is "
3995 "writable.");
3996
3997 auto *T = ExtensionType->getTypeParameter(0);
3998 auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
3999 ExtensionType->getIntParameter(0));
4000 bool IsWritable = ExtensionType->getIntParameter(1);
4001 return GR->getOrCreateVulkanBufferType(MIRBuilder, T, SC, IsWritable);
4002}
4003
4004static SPIRVTypeInst
4006 MachineIRBuilder &MIRBuilder,
4007 SPIRVGlobalRegistry *GR) {
4008 assert(ExtensionType->getNumTypeParameters() == 1 &&
4009 "Vulkan push constants have exactly one type as argument.");
4010 auto *T = ExtensionType->getTypeParameter(0);
4011 return GR->getOrCreateVulkanPushConstantType(MIRBuilder, T);
4012}
4013
4014static SPIRVTypeInst getLayoutType(const TargetExtType *ExtensionType,
4015 MachineIRBuilder &MIRBuilder,
4016 SPIRVGlobalRegistry *GR) {
4017 return GR->getOrCreateLayoutType(MIRBuilder, ExtensionType);
4018}
4019
4020namespace SPIRV {
4022 LLVMContext &Context) {
4023 StringRef NameWithParameters = TypeName;
4024
4025 // Pointers-to-opaque-structs representing OpenCL types are first translated
4026 // to equivalent SPIR-V types. OpenCL builtin type names should have the
4027 // following format: e.g. %opencl.event_t
4028 if (NameWithParameters.starts_with("opencl.")) {
4029 const SPIRV::OpenCLType *OCLTypeRecord =
4030 SPIRV::lookupOpenCLType(NameWithParameters);
4031 if (!OCLTypeRecord)
4032 report_fatal_error("Missing TableGen record for OpenCL type: " +
4033 NameWithParameters);
4034 NameWithParameters =
4035 SPIRV::getOpenCLTypeStr(OCLTypeRecord->SpirvTypeLiteral);
4036 // Continue with the SPIR-V builtin type...
4037 }
4038
4039 // Names of the opaque structs representing a SPIR-V builtins without
4040 // parameters should have the following format: e.g. %spirv.Event
4041 assert(NameWithParameters.starts_with("spirv.") &&
4042 "Unknown builtin opaque type!");
4043
4044 // Parameterized SPIR-V builtins names follow this format:
4045 // e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._0
4046 if (!NameWithParameters.contains('_'))
4047 return TargetExtType::get(Context, NameWithParameters);
4048
4049 SmallVector<StringRef> Parameters;
4050 unsigned BaseNameLength = NameWithParameters.find('_') - 1;
4051 SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_");
4052
4053 SmallVector<Type *, 1> TypeParameters;
4054 bool HasTypeParameter = !isDigit(Parameters[0][0]);
4055 if (HasTypeParameter)
4056 TypeParameters.push_back(parseTypeString(Parameters[0], Context));
4057 SmallVector<unsigned> IntParameters;
4058 for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) {
4059 unsigned IntParameter = 0;
4060 bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter);
4061 (void)ValidLiteral;
4062 assert(ValidLiteral &&
4063 "Invalid format of SPIR-V builtin parameter literal!");
4064 IntParameters.push_back(IntParameter);
4065 }
4066 return TargetExtType::get(Context,
4067 NameWithParameters.substr(0, BaseNameLength),
4068 TypeParameters, IntParameters);
4069}
4070
4072lowerBuiltinType(const Type *OpaqueType,
4073 SPIRV::AccessQualifier::AccessQualifier AccessQual,
4074 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
4075 // In LLVM IR, SPIR-V and OpenCL builtin types are represented as either
4076 // target(...) target extension types or pointers-to-opaque-structs. The
4077 // approach relying on structs is deprecated and works only in the non-opaque
4078 // pointer mode (-opaque-pointers=0).
4079 // In order to maintain compatibility with LLVM IR generated by older versions
4080 // of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are
4081 // "translated" to target extension types. This translation is temporary and
4082 // will be removed in the future release of LLVM.
4084 if (!BuiltinType)
4086 OpaqueType->getStructName().str(), MIRBuilder.getContext());
4087
4088 unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
4089
4090 StringRef Name = BuiltinType->getName();
4091 LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
4092
4093 SPIRVTypeInst TargetType = nullptr;
4094 if (Name == "spirv.Type") {
4095 TargetType = getInlineSpirvType(BuiltinType, MIRBuilder, GR);
4096 } else if (Name == "spirv.VulkanBuffer") {
4097 TargetType = getVulkanBufferType(BuiltinType, MIRBuilder, GR);
4098 } else if (Name == "spirv.Padding") {
4099 TargetType = GR->getOrCreatePaddingType(MIRBuilder);
4100 } else if (Name == "spirv.PushConstant") {
4101 TargetType = getVulkanPushConstantType(BuiltinType, MIRBuilder, GR);
4102 } else if (Name == "spirv.Layout") {
4103 TargetType = getLayoutType(BuiltinType, MIRBuilder, GR);
4104 } else {
4105 // Lookup the demangled builtin type in the TableGen records.
4106 const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
4107 if (!TypeRecord)
4108 report_fatal_error("Missing TableGen record for builtin type: " + Name);
4109
4110 // "Lower" the BuiltinType into TargetType. The following get<...>Type
4111 // methods use the implementation details from TableGen records or
4112 // TargetExtType parameters to either create a new OpType<...> machine
4113 // instruction or get an existing equivalent SPIRV type from
4114 // GlobalRegistry.
4115
4116 switch (TypeRecord->Opcode) {
4117 case SPIRV::OpTypeImage:
4118 TargetType = GR->getImageType(BuiltinType, AccessQual, MIRBuilder);
4119 break;
4120 case SPIRV::OpTypePipe:
4121 TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
4122 break;
4123 case SPIRV::OpTypeDeviceEvent:
4124 TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
4125 break;
4126 case SPIRV::OpTypeSampler:
4127 TargetType = getSamplerType(MIRBuilder, GR);
4128 break;
4129 case SPIRV::OpTypeSampledImage:
4130 TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
4131 break;
4132 case SPIRV::OpTypeCooperativeMatrixKHR:
4133 TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
4134 break;
4135 default:
4136 TargetType =
4137 getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
4138 break;
4139 }
4140 }
4141
4142 // Emit OpName instruction if a new OpType<...> instruction was added
4143 // (equivalent type was not found in GlobalRegistry).
4144 if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
4145 buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder);
4146
4147 return TargetType;
4148}
4149
4151 const DemangledBuiltin *Builtin = lookupBuiltin(Name, OpenCL_std);
4152 if (!Builtin)
4153 return false;
4154 return Builtin->Group == Pipe || Builtin->Group == CastToPtr ||
4155 Builtin->Group == BlockingPipes;
4156}
4157} // namespace SPIRV
4158} // namespace llvm
MachineInstrBuilder MachineInstrBuilder & DefMI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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:1467
static APFloat getZero(const fltSemantics &Sem, bool Negative=false)
Factory for Positive and Negative Zero.
Definition APFloat.h:1175
static APInt getAllOnes(unsigned numBits)
Return an APInt of a specified width with all bits set.
Definition APInt.h:235
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1565
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:353
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:348
static constexpr LLT 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 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:960
Type * getTypeParameter(unsigned i) const
unsigned getNumTypeParameters() const
unsigned getIntParameter(unsigned i) const
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:309
bool isPointerTy() const
True if this is an instance of PointerType.
Definition Type.h:282
LLVM_ABI StringRef getStructName() const
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:282
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
Definition Type.cpp:307
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:257
static LLVM_ABI Type * getDoubleTy(LLVMContext &C)
Definition Type.cpp:287
static LLVM_ABI Type * getFloatTy(LLVMContext &C)
Definition Type.cpp:286
static LLVM_ABI Type * getBFloatTy(LLVMContext &C)
Definition Type.cpp:285
static LLVM_ABI Type * getHalfTy(LLVMContext &C)
Definition Type.cpp:284
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:922
#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.
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:567
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:565
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
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 Register buildMemSemanticsReg(Register SemanticsRegister, Register PtrRegister, unsigned &Semantics, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
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 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:1917
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