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