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