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