LLVM 20.0.0git
SPIRVBuiltins.cpp
Go to the documentation of this file.
1//===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements lowering builtin function calls and types using their
10// demangled names and TableGen records.
11//
12//===----------------------------------------------------------------------===//
13
14#include "SPIRVBuiltins.h"
15#include "SPIRV.h"
16#include "SPIRVSubtarget.h"
17#include "SPIRVUtils.h"
20#include "llvm/IR/IntrinsicsSPIRV.h"
21#include <regex>
22#include <string>
23#include <tuple>
24
25#define DEBUG_TYPE "spirv-builtins"
26
27namespace llvm {
28namespace SPIRV {
29#define GET_BuiltinGroup_DECL
30#include "SPIRVGenTables.inc"
31
34 InstructionSet::InstructionSet Set;
35 BuiltinGroup Group;
38};
39
40#define GET_DemangledBuiltins_DECL
41#define GET_DemangledBuiltins_IMPL
42
44 const std::string BuiltinName;
46
50
57
58 bool isSpirvOp() const { return BuiltinName.rfind("__spirv_", 0) == 0; }
59};
60
63 InstructionSet::InstructionSet Set;
65};
66
67#define GET_NativeBuiltins_DECL
68#define GET_NativeBuiltins_IMPL
69
74 bool IsElect;
84};
85
86#define GET_GroupBuiltins_DECL
87#define GET_GroupBuiltins_IMPL
88
92 bool IsBlock;
93 bool IsWrite;
94 bool IsMedia;
95};
96
97#define GET_IntelSubgroupsBuiltins_DECL
98#define GET_IntelSubgroupsBuiltins_IMPL
99
103};
104
105#define GET_AtomicFloatingBuiltins_DECL
106#define GET_AtomicFloatingBuiltins_IMPL
111};
112
113#define GET_GroupUniformBuiltins_DECL
114#define GET_GroupUniformBuiltins_IMPL
115
118 InstructionSet::InstructionSet Set;
119 BuiltIn::BuiltIn Value;
120};
121
122using namespace BuiltIn;
123#define GET_GetBuiltins_DECL
124#define GET_GetBuiltins_IMPL
125
128 InstructionSet::InstructionSet Set;
130};
131
132#define GET_ImageQueryBuiltins_DECL
133#define GET_ImageQueryBuiltins_IMPL
134
137 InstructionSet::InstructionSet Set;
142 FPRoundingMode::FPRoundingMode RoundingMode;
143};
144
147 InstructionSet::InstructionSet Set;
151 FPRoundingMode::FPRoundingMode RoundingMode;
152};
153
154using namespace FPRoundingMode;
155#define GET_ConvertBuiltins_DECL
156#define GET_ConvertBuiltins_IMPL
157
158using namespace InstructionSet;
159#define GET_VectorLoadStoreBuiltins_DECL
160#define GET_VectorLoadStoreBuiltins_IMPL
161
162#define GET_CLMemoryScope_DECL
163#define GET_CLSamplerAddressingMode_DECL
164#define GET_CLMemoryFenceFlags_DECL
165#define GET_ExtendedBuiltins_DECL
166#include "SPIRVGenTables.inc"
167} // namespace SPIRV
168
169//===----------------------------------------------------------------------===//
170// Misc functions for looking up builtins and veryfying requirements using
171// TableGen records
172//===----------------------------------------------------------------------===//
173
174namespace SPIRV {
175/// Parses the name part of the demangled builtin call.
176std::string lookupBuiltinNameHelper(StringRef DemangledCall,
177 FPDecorationId *DecorationId) {
178 const static std::string PassPrefix = "(anonymous namespace)::";
179 std::string BuiltinName;
180 // Itanium Demangler result may have "(anonymous namespace)::" prefix
181 if (DemangledCall.starts_with(PassPrefix.c_str()))
182 BuiltinName = DemangledCall.substr(PassPrefix.length());
183 else
184 BuiltinName = DemangledCall;
185 // Extract the builtin function name and types of arguments from the call
186 // skeleton.
187 BuiltinName = BuiltinName.substr(0, BuiltinName.find('('));
188
189 // Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR
190 if (BuiltinName.rfind("__spirv_ocl_", 0) == 0)
191 BuiltinName = BuiltinName.substr(12);
192
193 // Check if the extracted name contains type information between angle
194 // brackets. If so, the builtin is an instantiated template - needs to have
195 // the information after angle brackets and return type removed.
196 std::size_t Pos1 = BuiltinName.rfind('<');
197 if (Pos1 != std::string::npos && BuiltinName.back() == '>') {
198 std::size_t Pos2 = BuiltinName.rfind(' ', Pos1);
199 if (Pos2 == std::string::npos)
200 Pos2 = 0;
201 else
202 ++Pos2;
203 BuiltinName = BuiltinName.substr(Pos2, Pos1 - Pos2);
204 BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(' ') + 1);
205 }
206
207 // Check if the extracted name begins with:
208 // - "__spirv_ImageSampleExplicitLod"
209 // - "__spirv_ImageRead"
210 // - "__spirv_ImageQuerySizeLod"
211 // - "__spirv_UDotKHR"
212 // - "__spirv_SDotKHR"
213 // - "__spirv_SUDotKHR"
214 // - "__spirv_SDotAccSatKHR"
215 // - "__spirv_UDotAccSatKHR"
216 // - "__spirv_SUDotAccSatKHR"
217 // - "__spirv_ReadClockKHR"
218 // - "__spirv_SubgroupBlockReadINTEL"
219 // - "__spirv_SubgroupImageBlockReadINTEL"
220 // - "__spirv_SubgroupImageMediaBlockReadINTEL"
221 // - "__spirv_SubgroupImageMediaBlockWriteINTEL"
222 // - "__spirv_Convert"
223 // - "__spirv_UConvert"
224 // - "__spirv_SConvert"
225 // - "__spirv_FConvert"
226 // - "__spirv_SatConvert"
227 // and contains return type information at the end "_R<type>".
228 // If so, extract the plain builtin name without the type information.
229 static const std::regex SpvWithR(
230 "(__spirv_(ImageSampleExplicitLod|ImageRead|ImageQuerySizeLod|UDotKHR|"
231 "SDotKHR|SUDotKHR|SDotAccSatKHR|UDotAccSatKHR|SUDotAccSatKHR|"
232 "ReadClockKHR|SubgroupBlockReadINTEL|SubgroupImageBlockReadINTEL|"
233 "SubgroupImageMediaBlockReadINTEL|SubgroupImageMediaBlockWriteINTEL|"
234 "Convert|"
235 "UConvert|SConvert|FConvert|SatConvert).*)_R[^_]*_?(\\w+)?.*");
236 std::smatch Match;
237 if (std::regex_match(BuiltinName, Match, SpvWithR) && Match.size() > 1) {
238 std::ssub_match SubMatch;
239 if (DecorationId && Match.size() > 3) {
240 SubMatch = Match[3];
241 *DecorationId = demangledPostfixToDecorationId(SubMatch.str());
242 }
243 SubMatch = Match[1];
244 BuiltinName = SubMatch.str();
245 }
246
247 return BuiltinName;
248}
249} // namespace SPIRV
250
251/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
252/// the provided \p DemangledCall and specified \p Set.
253///
254/// The lookup follows the following algorithm, returning the first successful
255/// match:
256/// 1. Search with the plain demangled name (expecting a 1:1 match).
257/// 2. Search with the prefix before or suffix after the demangled name
258/// signyfying the type of the first argument.
259///
260/// \returns Wrapper around the demangled call and found builtin definition.
261static std::unique_ptr<const SPIRV::IncomingCall>
263 SPIRV::InstructionSet::InstructionSet Set,
264 Register ReturnRegister, const SPIRVType *ReturnType,
266 std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall);
267
268 SmallVector<StringRef, 10> BuiltinArgumentTypes;
269 StringRef BuiltinArgs =
270 DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
271 BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false);
272
273 // Look up the builtin in the defined set. Start with the plain demangled
274 // name, expecting a 1:1 match in the defined builtin set.
275 const SPIRV::DemangledBuiltin *Builtin;
276 if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set)))
277 return std::make_unique<SPIRV::IncomingCall>(
278 BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
279
280 // If the initial look up was unsuccessful and the demangled call takes at
281 // least 1 argument, add a prefix or suffix signifying the type of the first
282 // argument and repeat the search.
283 if (BuiltinArgumentTypes.size() >= 1) {
284 char FirstArgumentType = BuiltinArgumentTypes[0][0];
285 // Prefix to be added to the builtin's name for lookup.
286 // For example, OpenCL "abs" taking an unsigned value has a prefix "u_".
287 std::string Prefix;
288
289 switch (FirstArgumentType) {
290 // Unsigned:
291 case 'u':
292 if (Set == SPIRV::InstructionSet::OpenCL_std)
293 Prefix = "u_";
294 else if (Set == SPIRV::InstructionSet::GLSL_std_450)
295 Prefix = "u";
296 break;
297 // Signed:
298 case 'c':
299 case 's':
300 case 'i':
301 case 'l':
302 if (Set == SPIRV::InstructionSet::OpenCL_std)
303 Prefix = "s_";
304 else if (Set == SPIRV::InstructionSet::GLSL_std_450)
305 Prefix = "s";
306 break;
307 // Floating-point:
308 case 'f':
309 case 'd':
310 case 'h':
311 if (Set == SPIRV::InstructionSet::OpenCL_std ||
312 Set == SPIRV::InstructionSet::GLSL_std_450)
313 Prefix = "f";
314 break;
315 }
316
317 // If argument-type name prefix was added, look up the builtin again.
318 if (!Prefix.empty() &&
319 (Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set)))
320 return std::make_unique<SPIRV::IncomingCall>(
321 BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
322
323 // If lookup with a prefix failed, find a suffix to be added to the
324 // builtin's name for lookup. For example, OpenCL "group_reduce_max" taking
325 // an unsigned value has a suffix "u".
326 std::string Suffix;
327
328 switch (FirstArgumentType) {
329 // Unsigned:
330 case 'u':
331 Suffix = "u";
332 break;
333 // Signed:
334 case 'c':
335 case 's':
336 case 'i':
337 case 'l':
338 Suffix = "s";
339 break;
340 // Floating-point:
341 case 'f':
342 case 'd':
343 case 'h':
344 Suffix = "f";
345 break;
346 }
347
348 // If argument-type name suffix was added, look up the builtin again.
349 if (!Suffix.empty() &&
350 (Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set)))
351 return std::make_unique<SPIRV::IncomingCall>(
352 BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);
353 }
354
355 // No builtin with such name was found in the set.
356 return nullptr;
357}
358
361 // We expect the following sequence of instructions:
362 // %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)
363 // or = G_GLOBAL_VALUE @block_literal_global
364 // %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0
365 // %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)
366 MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);
367 assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&
368 MI->getOperand(1).isReg());
369 Register BitcastReg = MI->getOperand(1).getReg();
370 MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);
371 assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&
372 BitcastMI->getOperand(2).isReg());
373 Register ValueReg = BitcastMI->getOperand(2).getReg();
374 MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);
375 return ValueMI;
376}
377
378// Return an integer constant corresponding to the given register and
379// defined in spv_track_constant.
380// TODO: maybe unify with prelegalizer pass.
382 MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);
383 assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&
384 DefMI->getOperand(2).isReg());
385 MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());
386 assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&
387 DefMI2->getOperand(1).isCImm());
388 return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();
389}
390
391// Return type of the instruction result from spv_assign_type intrinsic.
392// TODO: maybe unify with prelegalizer pass.
394 MachineInstr *NextMI = MI->getNextNode();
395 if (!NextMI)
396 return nullptr;
397 if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))
398 if ((NextMI = NextMI->getNextNode()) == nullptr)
399 return nullptr;
400 Register ValueReg = MI->getOperand(0).getReg();
401 if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) &&
402 !isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) ||
403 NextMI->getOperand(1).getReg() != ValueReg)
404 return nullptr;
405 Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);
406 assert(Ty && "Type is expected");
407 return Ty;
408}
409
410static const Type *getBlockStructType(Register ParamReg,
412 // In principle, this information should be passed to us from Clang via
413 // an elementtype attribute. However, said attribute requires that
414 // the function call be an intrinsic, which is not. Instead, we rely on being
415 // able to trace this to the declaration of a variable: OpenCL C specification
416 // section 6.12.5 should guarantee that we can do this.
418 if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)
419 return MI->getOperand(1).getGlobal()->getType();
420 assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&
421 "Blocks in OpenCL C must be traceable to allocation site");
422 return getMachineInstrType(MI);
423}
424
425//===----------------------------------------------------------------------===//
426// Helper functions for building misc instructions
427//===----------------------------------------------------------------------===//
428
429/// Helper function building either a resulting scalar or vector bool register
430/// depending on the expected \p ResultType.
431///
432/// \returns Tuple of the resulting register and its type.
433static std::tuple<Register, SPIRVType *>
434buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType,
436 LLT Type;
437 SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
438
439 if (ResultType->getOpcode() == SPIRV::OpTypeVector) {
440 unsigned VectorElements = ResultType->getOperand(2).getImm();
441 BoolType =
442 GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder);
444 cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType));
445 Type = LLT::vector(LLVMVectorType->getElementCount(), 1);
446 } else {
447 Type = LLT::scalar(1);
448 }
449
450 Register ResultRegister =
452 MIRBuilder.getMRI()->setRegClass(ResultRegister, GR->getRegClass(ResultType));
453 GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF());
454 return std::make_tuple(ResultRegister, BoolType);
455}
456
457/// Helper function for building either a vector or scalar select instruction
458/// depending on the expected \p ResultType.
459static bool buildSelectInst(MachineIRBuilder &MIRBuilder,
460 Register ReturnRegister, Register SourceRegister,
461 const SPIRVType *ReturnType,
463 Register TrueConst, FalseConst;
464
465 if (ReturnType->getOpcode() == SPIRV::OpTypeVector) {
466 unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType);
468 TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType);
469 FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType);
470 } else {
471 TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType);
472 FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType);
473 }
474
475 return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst,
476 FalseConst);
477}
478
479/// Helper function for building a load instruction loading into the
480/// \p DestinationReg.
482 MachineIRBuilder &MIRBuilder,
483 SPIRVGlobalRegistry *GR, LLT LowLevelType,
484 Register DestinationReg = Register(0)) {
485 if (!DestinationReg.isValid())
486 DestinationReg = createVirtualRegister(BaseType, GR, MIRBuilder);
487 // TODO: consider using correct address space and alignment (p0 is canonical
488 // type for selection though).
490 MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align());
491 return DestinationReg;
492}
493
494/// Helper function for building a load instruction for loading a builtin global
495/// variable of \p BuiltinValue value.
497 MachineIRBuilder &MIRBuilder, SPIRVType *VariableType,
498 SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType,
499 Register Reg = Register(0), bool isConst = true, bool hasLinkageTy = true) {
500 Register NewRegister =
501 MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::pIDRegClass);
502 MIRBuilder.getMRI()->setType(
503 NewRegister,
504 LLT::pointer(storageClassToAddressSpace(SPIRV::StorageClass::Function),
505 GR->getPointerSize()));
507 VariableType, MIRBuilder, SPIRV::StorageClass::Input);
508 GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
509
510 // Set up the global OpVariable with the necessary builtin decorations.
511 Register Variable = GR->buildGlobalVariable(
512 NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
513 SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst,
514 /* HasLinkageTy */ hasLinkageTy, SPIRV::LinkageType::Import, MIRBuilder,
515 false);
516
517 // Load the value from the global variable.
518 Register LoadedRegister =
519 buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg);
520 MIRBuilder.getMRI()->setType(LoadedRegister, LLType);
521 return LoadedRegister;
522}
523
524/// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg
525/// and its definition, set the new register as a destination of the definition,
526/// assign SPIRVType to both registers. If SpirvTy is provided, use it as
527/// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in
528/// SPIRVPreLegalizer.cpp.
529extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
530 SPIRVGlobalRegistry *GR,
531 MachineIRBuilder &MIB,
532 MachineRegisterInfo &MRI);
533
534// TODO: Move to TableGen.
535static SPIRV::MemorySemantics::MemorySemantics
536getSPIRVMemSemantics(std::memory_order MemOrder) {
537 switch (MemOrder) {
538 case std::memory_order::memory_order_relaxed:
539 return SPIRV::MemorySemantics::None;
540 case std::memory_order::memory_order_acquire:
541 return SPIRV::MemorySemantics::Acquire;
542 case std::memory_order::memory_order_release:
543 return SPIRV::MemorySemantics::Release;
544 case std::memory_order::memory_order_acq_rel:
545 return SPIRV::MemorySemantics::AcquireRelease;
546 case std::memory_order::memory_order_seq_cst:
547 return SPIRV::MemorySemantics::SequentiallyConsistent;
548 default:
549 report_fatal_error("Unknown CL memory scope");
550 }
551}
552
553static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) {
554 switch (ClScope) {
555 case SPIRV::CLMemoryScope::memory_scope_work_item:
556 return SPIRV::Scope::Invocation;
557 case SPIRV::CLMemoryScope::memory_scope_work_group:
558 return SPIRV::Scope::Workgroup;
559 case SPIRV::CLMemoryScope::memory_scope_device:
560 return SPIRV::Scope::Device;
561 case SPIRV::CLMemoryScope::memory_scope_all_svm_devices:
562 return SPIRV::Scope::CrossDevice;
563 case SPIRV::CLMemoryScope::memory_scope_sub_group:
564 return SPIRV::Scope::Subgroup;
565 }
566 report_fatal_error("Unknown CL memory scope");
567}
568
570 MachineIRBuilder &MIRBuilder,
572 return GR->buildConstantInt(Val, MIRBuilder,
573 GR->getOrCreateSPIRVIntegerType(32, MIRBuilder));
574}
575
576static Register buildScopeReg(Register CLScopeRegister,
577 SPIRV::Scope::Scope Scope,
578 MachineIRBuilder &MIRBuilder,
581 if (CLScopeRegister.isValid()) {
582 auto CLScope =
583 static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI));
584 Scope = getSPIRVScope(CLScope);
585
586 if (CLScope == static_cast<unsigned>(Scope)) {
587 MRI->setRegClass(CLScopeRegister, &SPIRV::iIDRegClass);
588 return CLScopeRegister;
589 }
590 }
591 return buildConstantIntReg32(Scope, MIRBuilder, GR);
592}
593
596 if (MRI->getRegClassOrNull(Reg))
597 return;
598 SPIRVType *SpvType = GR->getSPIRVTypeForVReg(Reg);
599 MRI->setRegClass(Reg,
600 SpvType ? GR->getRegClass(SpvType) : &SPIRV::iIDRegClass);
601}
602
603static Register buildMemSemanticsReg(Register SemanticsRegister,
604 Register PtrRegister, unsigned &Semantics,
605 MachineIRBuilder &MIRBuilder,
607 if (SemanticsRegister.isValid()) {
608 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
609 std::memory_order Order =
610 static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI));
611 Semantics =
612 getSPIRVMemSemantics(Order) |
614
615 if (Order == Semantics) {
616 MRI->setRegClass(SemanticsRegister, &SPIRV::iIDRegClass);
617 return SemanticsRegister;
618 }
619 }
620 return buildConstantIntReg32(Semantics, MIRBuilder, GR);
621}
622
623static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode,
624 const SPIRV::IncomingCall *Call,
625 Register TypeReg,
626 ArrayRef<uint32_t> ImmArgs = {}) {
627 auto MIB = MIRBuilder.buildInstr(Opcode);
628 if (TypeReg.isValid())
629 MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
630 unsigned Sz = Call->Arguments.size() - ImmArgs.size();
631 for (unsigned i = 0; i < Sz; ++i)
632 MIB.addUse(Call->Arguments[i]);
633 for (uint32_t ImmArg : ImmArgs)
634 MIB.addImm(ImmArg);
635 return true;
636}
637
638/// Helper function for translating atomic init to OpStore.
640 MachineIRBuilder &MIRBuilder) {
641 if (Call->isSpirvOp())
642 return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0));
643
644 assert(Call->Arguments.size() == 2 &&
645 "Need 2 arguments for atomic init translation");
646 MIRBuilder.buildInstr(SPIRV::OpStore)
647 .addUse(Call->Arguments[0])
648 .addUse(Call->Arguments[1]);
649 return true;
650}
651
652/// Helper function for building an atomic load instruction.
654 MachineIRBuilder &MIRBuilder,
656 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
657 if (Call->isSpirvOp())
658 return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg);
659
660 Register PtrRegister = Call->Arguments[0];
661 // TODO: if true insert call to __translate_ocl_memory_sccope before
662 // OpAtomicLoad and the function implementation. We can use Translator's
663 // output for transcoding/atomic_explicit_arguments.cl as an example.
664 Register ScopeRegister =
665 Call->Arguments.size() > 1
666 ? Call->Arguments[1]
667 : buildConstantIntReg32(SPIRV::Scope::Device, MIRBuilder, GR);
668 Register MemSemanticsReg;
669 if (Call->Arguments.size() > 2) {
670 // TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad.
671 MemSemanticsReg = Call->Arguments[2];
672 } else {
673 int Semantics =
674 SPIRV::MemorySemantics::SequentiallyConsistent |
676 MemSemanticsReg = buildConstantIntReg32(Semantics, MIRBuilder, GR);
677 }
678
679 MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
680 .addDef(Call->ReturnRegister)
681 .addUse(TypeReg)
682 .addUse(PtrRegister)
683 .addUse(ScopeRegister)
684 .addUse(MemSemanticsReg);
685 return true;
686}
687
688/// Helper function for building an atomic store instruction.
690 MachineIRBuilder &MIRBuilder,
692 if (Call->isSpirvOp())
693 return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call, Register(0));
694
695 Register ScopeRegister =
696 buildConstantIntReg32(SPIRV::Scope::Device, MIRBuilder, GR);
697 Register PtrRegister = Call->Arguments[0];
698 int Semantics =
699 SPIRV::MemorySemantics::SequentiallyConsistent |
701 Register MemSemanticsReg = buildConstantIntReg32(Semantics, MIRBuilder, GR);
702 MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
703 .addUse(PtrRegister)
704 .addUse(ScopeRegister)
705 .addUse(MemSemanticsReg)
706 .addUse(Call->Arguments[1]);
707 return true;
708}
709
710/// Helper function for building an atomic compare-exchange instruction.
712 const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin,
713 unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
714 if (Call->isSpirvOp())
715 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
716 GR->getSPIRVTypeID(Call->ReturnType));
717
718 bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg");
719 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
720
721 Register ObjectPtr = Call->Arguments[0]; // Pointer (volatile A *object.)
722 Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).
723 Register Desired = Call->Arguments[2]; // Value (C Desired).
724 SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);
725 LLT DesiredLLT = MRI->getType(Desired);
726
727 assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() ==
728 SPIRV::OpTypePointer);
729 unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode();
730 (void)ExpectedType;
731 assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt
732 : ExpectedType == SPIRV::OpTypePointer);
733 assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));
734
735 SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);
736 assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected");
737 auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(
738 SpvObjectPtrTy->getOperand(1).getImm());
739 auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);
740
741 Register MemSemEqualReg;
742 Register MemSemUnequalReg;
743 uint64_t MemSemEqual =
744 IsCmpxchg
745 ? SPIRV::MemorySemantics::None
746 : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
747 uint64_t MemSemUnequal =
748 IsCmpxchg
749 ? SPIRV::MemorySemantics::None
750 : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;
751 if (Call->Arguments.size() >= 4) {
752 assert(Call->Arguments.size() >= 5 &&
753 "Need 5+ args for explicit atomic cmpxchg");
754 auto MemOrdEq =
755 static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));
756 auto MemOrdNeq =
757 static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));
758 MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;
759 MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;
760 if (MemOrdEq == MemSemEqual)
761 MemSemEqualReg = Call->Arguments[3];
762 if (MemOrdNeq == MemSemEqual)
763 MemSemUnequalReg = Call->Arguments[4];
764 }
765 if (!MemSemEqualReg.isValid())
766 MemSemEqualReg = buildConstantIntReg32(MemSemEqual, MIRBuilder, GR);
767 if (!MemSemUnequalReg.isValid())
768 MemSemUnequalReg = buildConstantIntReg32(MemSemUnequal, MIRBuilder, GR);
769
770 Register ScopeReg;
771 auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;
772 if (Call->Arguments.size() >= 6) {
773 assert(Call->Arguments.size() == 6 &&
774 "Extra args for explicit atomic cmpxchg");
775 auto ClScope = static_cast<SPIRV::CLMemoryScope>(
776 getIConstVal(Call->Arguments[5], MRI));
777 Scope = getSPIRVScope(ClScope);
778 if (ClScope == static_cast<unsigned>(Scope))
779 ScopeReg = Call->Arguments[5];
780 }
781 if (!ScopeReg.isValid())
782 ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
783
784 Register Expected = IsCmpxchg
785 ? ExpectedArg
786 : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder,
787 GR, LLT::scalar(64));
788 MRI->setType(Expected, DesiredLLT);
789 Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)
790 : Call->ReturnRegister;
791 if (!MRI->getRegClassOrNull(Tmp))
792 MRI->setRegClass(Tmp, GR->getRegClass(SpvDesiredTy));
793 GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());
794
795 SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
796 MIRBuilder.buildInstr(Opcode)
797 .addDef(Tmp)
798 .addUse(GR->getSPIRVTypeID(IntTy))
799 .addUse(ObjectPtr)
800 .addUse(ScopeReg)
801 .addUse(MemSemEqualReg)
802 .addUse(MemSemUnequalReg)
803 .addUse(Desired)
805 if (!IsCmpxchg) {
806 MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);
807 MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);
808 }
809 return true;
810}
811
812/// Helper function for building atomic instructions.
813static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
814 MachineIRBuilder &MIRBuilder,
816 if (Call->isSpirvOp())
817 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
818 GR->getSPIRVTypeID(Call->ReturnType));
819
820 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
821 Register ScopeRegister =
822 Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register();
823
824 assert(Call->Arguments.size() <= 4 &&
825 "Too many args for explicit atomic RMW");
826 ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup,
827 MIRBuilder, GR, MRI);
828
829 Register PtrRegister = Call->Arguments[0];
830 unsigned Semantics = SPIRV::MemorySemantics::None;
831 Register MemSemanticsReg =
832 Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
833 MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
834 Semantics, MIRBuilder, GR);
835 Register ValueReg = Call->Arguments[1];
836 Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);
837 // support cl_ext_float_atomics
838 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {
839 if (Opcode == SPIRV::OpAtomicIAdd) {
840 Opcode = SPIRV::OpAtomicFAddEXT;
841 } else if (Opcode == SPIRV::OpAtomicISub) {
842 // Translate OpAtomicISub applied to a floating type argument to
843 // OpAtomicFAddEXT with the negative value operand
844 Opcode = SPIRV::OpAtomicFAddEXT;
845 Register NegValueReg =
846 MRI->createGenericVirtualRegister(MRI->getType(ValueReg));
847 MRI->setRegClass(NegValueReg, GR->getRegClass(Call->ReturnType));
848 GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg,
849 MIRBuilder.getMF());
850 MIRBuilder.buildInstr(TargetOpcode::G_FNEG)
851 .addDef(NegValueReg)
852 .addUse(ValueReg);
853 insertAssignInstr(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder,
854 MIRBuilder.getMF().getRegInfo());
855 ValueReg = NegValueReg;
856 }
857 }
858 MIRBuilder.buildInstr(Opcode)
859 .addDef(Call->ReturnRegister)
860 .addUse(ValueTypeReg)
861 .addUse(PtrRegister)
862 .addUse(ScopeRegister)
863 .addUse(MemSemanticsReg)
864 .addUse(ValueReg);
865 return true;
866}
867
868/// Helper function for building an atomic floating-type instruction.
870 unsigned Opcode,
871 MachineIRBuilder &MIRBuilder,
873 assert(Call->Arguments.size() == 4 &&
874 "Wrong number of atomic floating-type builtin");
875 Register PtrReg = Call->Arguments[0];
876 Register ScopeReg = Call->Arguments[1];
877 Register MemSemanticsReg = Call->Arguments[2];
878 Register ValueReg = Call->Arguments[3];
879 MIRBuilder.buildInstr(Opcode)
880 .addDef(Call->ReturnRegister)
881 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
882 .addUse(PtrReg)
883 .addUse(ScopeReg)
884 .addUse(MemSemanticsReg)
885 .addUse(ValueReg);
886 return true;
887}
888
889/// Helper function for building atomic flag instructions (e.g.
890/// OpAtomicFlagTestAndSet).
892 unsigned Opcode, MachineIRBuilder &MIRBuilder,
894 bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet;
895 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
896 if (Call->isSpirvOp())
897 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
898 IsSet ? TypeReg : Register(0));
899
900 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
901 Register PtrRegister = Call->Arguments[0];
902 unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent;
903 Register MemSemanticsReg =
904 Call->Arguments.size() >= 2 ? Call->Arguments[1] : Register();
905 MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,
906 Semantics, MIRBuilder, GR);
907
908 assert((Opcode != SPIRV::OpAtomicFlagClear ||
909 (Semantics != SPIRV::MemorySemantics::Acquire &&
910 Semantics != SPIRV::MemorySemantics::AcquireRelease)) &&
911 "Invalid memory order argument!");
912
913 Register ScopeRegister =
914 Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();
915 ScopeRegister =
916 buildScopeReg(ScopeRegister, SPIRV::Scope::Device, MIRBuilder, GR, MRI);
917
918 auto MIB = MIRBuilder.buildInstr(Opcode);
919 if (IsSet)
920 MIB.addDef(Call->ReturnRegister).addUse(TypeReg);
921
922 MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);
923 return true;
924}
925
926/// Helper function for building barriers, i.e., memory/control ordering
927/// operations.
928static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,
929 MachineIRBuilder &MIRBuilder,
931 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
932 const auto *ST =
933 static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
934 if ((Opcode == SPIRV::OpControlBarrierArriveINTEL ||
935 Opcode == SPIRV::OpControlBarrierWaitINTEL) &&
936 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_split_barrier)) {
937 std::string DiagMsg = std::string(Builtin->Name) +
938 ": the builtin requires the following SPIR-V "
939 "extension: SPV_INTEL_split_barrier";
940 report_fatal_error(DiagMsg.c_str(), false);
941 }
942
943 if (Call->isSpirvOp())
944 return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));
945
946 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
947 unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);
948 unsigned MemSemantics = SPIRV::MemorySemantics::None;
949
950 if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)
951 MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;
952
953 if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)
954 MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;
955
956 if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)
957 MemSemantics |= SPIRV::MemorySemantics::ImageMemory;
958
959 if (Opcode == SPIRV::OpMemoryBarrier)
960 MemSemantics = getSPIRVMemSemantics(static_cast<std::memory_order>(
961 getIConstVal(Call->Arguments[1], MRI))) |
962 MemSemantics;
963 else if (Opcode == SPIRV::OpControlBarrierArriveINTEL)
964 MemSemantics |= SPIRV::MemorySemantics::Release;
965 else if (Opcode == SPIRV::OpControlBarrierWaitINTEL)
966 MemSemantics |= SPIRV::MemorySemantics::Acquire;
967 else
968 MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;
969
970 Register MemSemanticsReg =
971 MemFlags == MemSemantics
972 ? Call->Arguments[0]
973 : buildConstantIntReg32(MemSemantics, MIRBuilder, GR);
974 Register ScopeReg;
975 SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;
976 SPIRV::Scope::Scope MemScope = Scope;
977 if (Call->Arguments.size() >= 2) {
978 assert(
979 ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||
980 (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&
981 "Extra args for explicitly scoped barrier");
982 Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]
983 : Call->Arguments[1];
984 SPIRV::CLMemoryScope CLScope =
985 static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));
986 MemScope = getSPIRVScope(CLScope);
987 if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||
988 (Opcode == SPIRV::OpMemoryBarrier))
989 Scope = MemScope;
990 if (CLScope == static_cast<unsigned>(Scope))
991 ScopeReg = Call->Arguments[1];
992 }
993
994 if (!ScopeReg.isValid())
995 ScopeReg = buildConstantIntReg32(Scope, MIRBuilder, GR);
996
997 auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);
998 if (Opcode != SPIRV::OpMemoryBarrier)
999 MIB.addUse(buildConstantIntReg32(MemScope, MIRBuilder, GR));
1000 MIB.addUse(MemSemanticsReg);
1001 return true;
1002}
1003
1004static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {
1005 switch (dim) {
1006 case SPIRV::Dim::DIM_1D:
1007 case SPIRV::Dim::DIM_Buffer:
1008 return 1;
1009 case SPIRV::Dim::DIM_2D:
1010 case SPIRV::Dim::DIM_Cube:
1011 case SPIRV::Dim::DIM_Rect:
1012 return 2;
1013 case SPIRV::Dim::DIM_3D:
1014 return 3;
1015 default:
1016 report_fatal_error("Cannot get num components for given Dim");
1017 }
1018}
1019
1020/// Helper function for obtaining the number of size components.
1021static unsigned getNumSizeComponents(SPIRVType *imgType) {
1022 assert(imgType->getOpcode() == SPIRV::OpTypeImage);
1023 auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());
1024 unsigned numComps = getNumComponentsForDim(dim);
1025 bool arrayed = imgType->getOperand(4).getImm() == 1;
1026 return arrayed ? numComps + 1 : numComps;
1027}
1028
1029//===----------------------------------------------------------------------===//
1030// Implementation functions for each builtin group
1031//===----------------------------------------------------------------------===//
1032
1033static bool generateExtInst(const SPIRV::IncomingCall *Call,
1034 MachineIRBuilder &MIRBuilder,
1035 SPIRVGlobalRegistry *GR) {
1036 // Lookup the extended instruction number in the TableGen records.
1037 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1039 SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number;
1040
1041 // Build extended instruction.
1042 auto MIB =
1043 MIRBuilder.buildInstr(SPIRV::OpExtInst)
1044 .addDef(Call->ReturnRegister)
1045 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1046 .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
1047 .addImm(Number);
1048
1049 for (auto Argument : Call->Arguments)
1050 MIB.addUse(Argument);
1051 return true;
1052}
1053
1055 MachineIRBuilder &MIRBuilder,
1056 SPIRVGlobalRegistry *GR) {
1057 // Lookup the instruction opcode in the TableGen records.
1058 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1059 unsigned Opcode =
1060 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1061
1062 Register CompareRegister;
1063 SPIRVType *RelationType;
1064 std::tie(CompareRegister, RelationType) =
1065 buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1066
1067 // Build relational instruction.
1068 auto MIB = MIRBuilder.buildInstr(Opcode)
1069 .addDef(CompareRegister)
1070 .addUse(GR->getSPIRVTypeID(RelationType));
1071
1072 for (auto Argument : Call->Arguments)
1073 MIB.addUse(Argument);
1074
1075 // Build select instruction.
1076 return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,
1077 Call->ReturnType, GR);
1078}
1079
1081 MachineIRBuilder &MIRBuilder,
1082 SPIRVGlobalRegistry *GR) {
1083 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1084 const SPIRV::GroupBuiltin *GroupBuiltin =
1085 SPIRV::lookupGroupBuiltin(Builtin->Name);
1086
1087 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1088 if (Call->isSpirvOp()) {
1089 if (GroupBuiltin->NoGroupOperation)
1090 return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call,
1091 GR->getSPIRVTypeID(Call->ReturnType));
1092
1093 // Group Operation is a literal
1094 Register GroupOpReg = Call->Arguments[1];
1095 const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI);
1096 if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT)
1098 "Group Operation parameter must be an integer constant");
1099 uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue();
1100 Register ScopeReg = Call->Arguments[0];
1101 auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1102 .addDef(Call->ReturnRegister)
1103 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1104 .addUse(ScopeReg)
1105 .addImm(GrpOp);
1106 for (unsigned i = 2; i < Call->Arguments.size(); ++i)
1107 MIB.addUse(Call->Arguments[i]);
1108 return true;
1109 }
1110
1111 Register Arg0;
1112 if (GroupBuiltin->HasBoolArg) {
1113 SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
1114 Register BoolReg = Call->Arguments[0];
1115 SPIRVType *BoolRegType = GR->getSPIRVTypeForVReg(BoolReg);
1116 if (!BoolRegType)
1117 report_fatal_error("Can't find a register's type definition");
1118 MachineInstr *ArgInstruction = getDefInstrMaybeConstant(BoolReg, MRI);
1119 if (ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT) {
1120 if (BoolRegType->getOpcode() != SPIRV::OpTypeBool)
1121 Arg0 = GR->buildConstantInt(getIConstVal(BoolReg, MRI), MIRBuilder,
1122 BoolType);
1123 } else {
1124 if (BoolRegType->getOpcode() == SPIRV::OpTypeInt) {
1125 Arg0 = MRI->createGenericVirtualRegister(LLT::scalar(1));
1126 MRI->setRegClass(Arg0, &SPIRV::iIDRegClass);
1127 GR->assignSPIRVTypeToVReg(BoolType, Arg0, MIRBuilder.getMF());
1128 MIRBuilder.buildICmp(CmpInst::ICMP_NE, Arg0, BoolReg,
1129 GR->buildConstantInt(0, MIRBuilder, BoolRegType));
1130 insertAssignInstr(Arg0, nullptr, BoolType, GR, MIRBuilder,
1131 MIRBuilder.getMF().getRegInfo());
1132 } else if (BoolRegType->getOpcode() != SPIRV::OpTypeBool) {
1133 report_fatal_error("Expect a boolean argument");
1134 }
1135 // if BoolReg is a boolean register, we don't need to do anything
1136 }
1137 }
1138
1139 Register GroupResultRegister = Call->ReturnRegister;
1140 SPIRVType *GroupResultType = Call->ReturnType;
1141
1142 // TODO: maybe we need to check whether the result type is already boolean
1143 // and in this case do not insert select instruction.
1144 const bool HasBoolReturnTy =
1145 GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||
1146 GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||
1147 GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;
1148
1149 if (HasBoolReturnTy)
1150 std::tie(GroupResultRegister, GroupResultType) =
1151 buildBoolRegister(MIRBuilder, Call->ReturnType, GR);
1152
1153 auto Scope = Builtin->Name.starts_with("sub_group") ? SPIRV::Scope::Subgroup
1154 : SPIRV::Scope::Workgroup;
1155 Register ScopeRegister = buildConstantIntReg32(Scope, MIRBuilder, GR);
1156
1157 Register VecReg;
1158 if (GroupBuiltin->Opcode == SPIRV::OpGroupBroadcast &&
1159 Call->Arguments.size() > 2) {
1160 // For OpGroupBroadcast "LocalId must be an integer datatype. It must be a
1161 // scalar, a vector with 2 components, or a vector with 3 components.",
1162 // meaning that we must create a vector from the function arguments if
1163 // it's a work_group_broadcast(val, local_id_x, local_id_y) or
1164 // work_group_broadcast(val, local_id_x, local_id_y, local_id_z) call.
1165 Register ElemReg = Call->Arguments[1];
1166 SPIRVType *ElemType = GR->getSPIRVTypeForVReg(ElemReg);
1167 if (!ElemType || ElemType->getOpcode() != SPIRV::OpTypeInt)
1168 report_fatal_error("Expect an integer <LocalId> argument");
1169 unsigned VecLen = Call->Arguments.size() - 1;
1170 VecReg = MRI->createGenericVirtualRegister(
1171 LLT::fixed_vector(VecLen, MRI->getType(ElemReg)));
1172 MRI->setRegClass(VecReg, &SPIRV::vIDRegClass);
1173 SPIRVType *VecType =
1174 GR->getOrCreateSPIRVVectorType(ElemType, VecLen, MIRBuilder);
1175 GR->assignSPIRVTypeToVReg(VecType, VecReg, MIRBuilder.getMF());
1176 auto MIB =
1177 MIRBuilder.buildInstr(TargetOpcode::G_BUILD_VECTOR).addDef(VecReg);
1178 for (unsigned i = 1; i < Call->Arguments.size(); i++) {
1179 MIB.addUse(Call->Arguments[i]);
1180 setRegClassIfNull(Call->Arguments[i], MRI, GR);
1181 }
1182 insertAssignInstr(VecReg, nullptr, VecType, GR, MIRBuilder,
1183 MIRBuilder.getMF().getRegInfo());
1184 }
1185
1186 // Build work/sub group instruction.
1187 auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)
1188 .addDef(GroupResultRegister)
1189 .addUse(GR->getSPIRVTypeID(GroupResultType))
1190 .addUse(ScopeRegister);
1191
1192 if (!GroupBuiltin->NoGroupOperation)
1193 MIB.addImm(GroupBuiltin->GroupOperation);
1194 if (Call->Arguments.size() > 0) {
1195 MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);
1196 setRegClassIfNull(Call->Arguments[0], MRI, GR);
1197 if (VecReg.isValid())
1198 MIB.addUse(VecReg);
1199 else
1200 for (unsigned i = 1; i < Call->Arguments.size(); i++)
1201 MIB.addUse(Call->Arguments[i]);
1202 }
1203
1204 // Build select instruction.
1205 if (HasBoolReturnTy)
1206 buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,
1207 Call->ReturnType, GR);
1208 return true;
1209}
1210
1212 MachineIRBuilder &MIRBuilder,
1213 SPIRVGlobalRegistry *GR) {
1214 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1215 MachineFunction &MF = MIRBuilder.getMF();
1216 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1217 const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups =
1218 SPIRV::lookupIntelSubgroupsBuiltin(Builtin->Name);
1219
1220 if (IntelSubgroups->IsMedia &&
1221 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_media_block_io)) {
1222 std::string DiagMsg = std::string(Builtin->Name) +
1223 ": the builtin requires the following SPIR-V "
1224 "extension: SPV_INTEL_media_block_io";
1225 report_fatal_error(DiagMsg.c_str(), false);
1226 } else if (!IntelSubgroups->IsMedia &&
1227 !ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1228 std::string DiagMsg = std::string(Builtin->Name) +
1229 ": the builtin requires the following SPIR-V "
1230 "extension: SPV_INTEL_subgroups";
1231 report_fatal_error(DiagMsg.c_str(), false);
1232 }
1233
1234 uint32_t OpCode = IntelSubgroups->Opcode;
1235 if (Call->isSpirvOp()) {
1236 bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL &&
1237 OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL &&
1238 OpCode != SPIRV::OpSubgroupImageMediaBlockWriteINTEL;
1239 return buildOpFromWrapper(MIRBuilder, OpCode, Call,
1240 IsSet ? GR->getSPIRVTypeID(Call->ReturnType)
1241 : Register(0));
1242 }
1243
1244 if (IntelSubgroups->IsBlock) {
1245 // Minimal number or arguments set in TableGen records is 1
1246 if (SPIRVType *Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) {
1247 if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) {
1248 // TODO: add required validation from the specification:
1249 // "'Image' must be an object whose type is OpTypeImage with a 'Sampled'
1250 // operand of 0 or 2. If the 'Sampled' operand is 2, then some
1251 // dimensions require a capability."
1252 switch (OpCode) {
1253 case SPIRV::OpSubgroupBlockReadINTEL:
1254 OpCode = SPIRV::OpSubgroupImageBlockReadINTEL;
1255 break;
1256 case SPIRV::OpSubgroupBlockWriteINTEL:
1257 OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL;
1258 break;
1259 }
1260 }
1261 }
1262 }
1263
1264 // TODO: opaque pointers types should be eventually resolved in such a way
1265 // that validation of block read is enabled with respect to the following
1266 // specification requirement:
1267 // "'Result Type' may be a scalar or vector type, and its component type must
1268 // be equal to the type pointed to by 'Ptr'."
1269 // For example, function parameter type should not be default i8 pointer, but
1270 // depend on the result type of the instruction where it is used as a pointer
1271 // argument of OpSubgroupBlockReadINTEL
1272
1273 // Build Intel subgroups instruction
1275 IntelSubgroups->IsWrite
1276 ? MIRBuilder.buildInstr(OpCode)
1277 : MIRBuilder.buildInstr(OpCode)
1278 .addDef(Call->ReturnRegister)
1279 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
1280 for (size_t i = 0; i < Call->Arguments.size(); ++i)
1281 MIB.addUse(Call->Arguments[i]);
1282 return true;
1283}
1284
1286 MachineIRBuilder &MIRBuilder,
1287 SPIRVGlobalRegistry *GR) {
1288 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1289 MachineFunction &MF = MIRBuilder.getMF();
1290 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1291 if (!ST->canUseExtension(
1292 SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {
1293 std::string DiagMsg = std::string(Builtin->Name) +
1294 ": the builtin requires the following SPIR-V "
1295 "extension: SPV_KHR_uniform_group_instructions";
1296 report_fatal_error(DiagMsg.c_str(), false);
1297 }
1298 const SPIRV::GroupUniformBuiltin *GroupUniform =
1299 SPIRV::lookupGroupUniformBuiltin(Builtin->Name);
1300 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1301
1302 Register GroupResultReg = Call->ReturnRegister;
1303 Register ScopeReg = Call->Arguments[0];
1304 Register ValueReg = Call->Arguments[2];
1305
1306 // Group Operation
1307 Register ConstGroupOpReg = Call->Arguments[1];
1308 const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI);
1309 if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT)
1311 "expect a constant group operation for a uniform group instruction",
1312 false);
1313 const MachineOperand &ConstOperand = Const->getOperand(1);
1314 if (!ConstOperand.isCImm())
1315 report_fatal_error("uniform group instructions: group operation must be an "
1316 "integer constant",
1317 false);
1318
1319 auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode)
1320 .addDef(GroupResultReg)
1321 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1322 .addUse(ScopeReg);
1323 addNumImm(ConstOperand.getCImm()->getValue(), MIB);
1324 MIB.addUse(ValueReg);
1325
1326 return true;
1327}
1328
1330 MachineIRBuilder &MIRBuilder,
1331 SPIRVGlobalRegistry *GR) {
1332 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1333 MachineFunction &MF = MIRBuilder.getMF();
1334 const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());
1335 if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) {
1336 std::string DiagMsg = std::string(Builtin->Name) +
1337 ": the builtin requires the following SPIR-V "
1338 "extension: SPV_KHR_shader_clock";
1339 report_fatal_error(DiagMsg.c_str(), false);
1340 }
1341
1342 Register ResultReg = Call->ReturnRegister;
1343
1344 // Deduce the `Scope` operand from the builtin function name.
1345 SPIRV::Scope::Scope ScopeArg =
1347 .EndsWith("device", SPIRV::Scope::Scope::Device)
1348 .EndsWith("work_group", SPIRV::Scope::Scope::Workgroup)
1349 .EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup);
1350 Register ScopeReg = buildConstantIntReg32(ScopeArg, MIRBuilder, GR);
1351
1352 MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)
1353 .addDef(ResultReg)
1354 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1355 .addUse(ScopeReg);
1356
1357 return true;
1358}
1359
1360// These queries ask for a single size_t result for a given dimension index, e.g
1361// size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to
1362// these values are all vec3 types, so we need to extract the correct index or
1363// return defaultVal (0 or 1 depending on the query). We also handle extending
1364// or tuncating in case size_t does not match the expected result type's
1365// bitwidth.
1366//
1367// For a constant index >= 3 we generate:
1368// %res = OpConstant %SizeT 0
1369//
1370// For other indices we generate:
1371// %g = OpVariable %ptr_V3_SizeT Input
1372// OpDecorate %g BuiltIn XXX
1373// OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"
1374// OpDecorate %g Constant
1375// %loadedVec = OpLoad %V3_SizeT %g
1376//
1377// Then, if the index is constant < 3, we generate:
1378// %res = OpCompositeExtract %SizeT %loadedVec idx
1379// If the index is dynamic, we generate:
1380// %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx
1381// %cmp = OpULessThan %bool %idx %const_3
1382// %res = OpSelect %SizeT %cmp %tmp %const_0
1383//
1384// If the bitwidth of %res does not match the expected return type, we add an
1385// extend or truncate.
1387 MachineIRBuilder &MIRBuilder,
1389 SPIRV::BuiltIn::BuiltIn BuiltinValue,
1390 uint64_t DefaultValue) {
1391 Register IndexRegister = Call->Arguments[0];
1392 const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();
1393 const unsigned PointerSize = GR->getPointerSize();
1394 const SPIRVType *PointerSizeType =
1395 GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);
1396 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1397 auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);
1398
1399 // Set up the final register to do truncation or extension on at the end.
1400 Register ToTruncate = Call->ReturnRegister;
1401
1402 // If the index is constant, we can statically determine if it is in range.
1403 bool IsConstantIndex =
1404 IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;
1405
1406 // If it's out of range (max dimension is 3), we can just return the constant
1407 // default value (0 or 1 depending on which query function).
1408 if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {
1409 Register DefaultReg = Call->ReturnRegister;
1410 if (PointerSize != ResultWidth) {
1411 DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1412 MRI->setRegClass(DefaultReg, &SPIRV::iIDRegClass);
1413 GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg,
1414 MIRBuilder.getMF());
1415 ToTruncate = DefaultReg;
1416 }
1417 auto NewRegister =
1418 GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
1419 MIRBuilder.buildCopy(DefaultReg, NewRegister);
1420 } else { // If it could be in range, we need to load from the given builtin.
1421 auto Vec3Ty =
1422 GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder);
1423 Register LoadedVector =
1424 buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,
1425 LLT::fixed_vector(3, PointerSize));
1426 // Set up the vreg to extract the result to (possibly a new temporary one).
1427 Register Extracted = Call->ReturnRegister;
1428 if (!IsConstantIndex || PointerSize != ResultWidth) {
1429 Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1430 MRI->setRegClass(Extracted, &SPIRV::iIDRegClass);
1431 GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());
1432 }
1433 // Use Intrinsic::spv_extractelt so dynamic vs static extraction is
1434 // handled later: extr = spv_extractelt LoadedVector, IndexRegister.
1435 MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(
1436 Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false);
1437 ExtractInst.addUse(LoadedVector).addUse(IndexRegister);
1438
1439 // If the index is dynamic, need check if it's < 3, and then use a select.
1440 if (!IsConstantIndex) {
1441 insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder,
1442 *MRI);
1443
1444 auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);
1445 auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);
1446
1447 Register CompareRegister =
1448 MRI->createGenericVirtualRegister(LLT::scalar(1));
1449 MRI->setRegClass(CompareRegister, &SPIRV::iIDRegClass);
1450 GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());
1451
1452 // Use G_ICMP to check if idxVReg < 3.
1453 MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister,
1454 GR->buildConstantInt(3, MIRBuilder, IndexType));
1455
1456 // Get constant for the default value (0 or 1 depending on which
1457 // function).
1458 Register DefaultRegister =
1459 GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);
1460
1461 // Get a register for the selection result (possibly a new temporary one).
1462 Register SelectionResult = Call->ReturnRegister;
1463 if (PointerSize != ResultWidth) {
1464 SelectionResult =
1465 MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));
1466 MRI->setRegClass(SelectionResult, &SPIRV::iIDRegClass);
1467 GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,
1468 MIRBuilder.getMF());
1469 }
1470 // Create the final G_SELECT to return the extracted value or the default.
1471 MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,
1472 DefaultRegister);
1473 ToTruncate = SelectionResult;
1474 } else {
1475 ToTruncate = Extracted;
1476 }
1477 }
1478 // Alter the result's bitwidth if it does not match the SizeT value extracted.
1479 if (PointerSize != ResultWidth)
1480 MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);
1481 return true;
1482}
1483
1485 MachineIRBuilder &MIRBuilder,
1486 SPIRVGlobalRegistry *GR) {
1487 // Lookup the builtin variable record.
1488 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1489 SPIRV::BuiltIn::BuiltIn Value =
1490 SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1491
1492 if (Value == SPIRV::BuiltIn::GlobalInvocationId)
1493 return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);
1494
1495 // Build a load instruction for the builtin variable.
1496 unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);
1497 LLT LLType;
1498 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector)
1499 LLType =
1500 LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth);
1501 else
1502 LLType = LLT::scalar(BitWidth);
1503
1504 return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,
1505 LLType, Call->ReturnRegister);
1506}
1507
1509 MachineIRBuilder &MIRBuilder,
1510 SPIRVGlobalRegistry *GR) {
1511 // Lookup the instruction opcode in the TableGen records.
1512 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1513 unsigned Opcode =
1514 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1515
1516 switch (Opcode) {
1517 case SPIRV::OpStore:
1518 return buildAtomicInitInst(Call, MIRBuilder);
1519 case SPIRV::OpAtomicLoad:
1520 return buildAtomicLoadInst(Call, MIRBuilder, GR);
1521 case SPIRV::OpAtomicStore:
1522 return buildAtomicStoreInst(Call, MIRBuilder, GR);
1523 case SPIRV::OpAtomicCompareExchange:
1524 case SPIRV::OpAtomicCompareExchangeWeak:
1525 return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder,
1526 GR);
1527 case SPIRV::OpAtomicIAdd:
1528 case SPIRV::OpAtomicISub:
1529 case SPIRV::OpAtomicOr:
1530 case SPIRV::OpAtomicXor:
1531 case SPIRV::OpAtomicAnd:
1532 case SPIRV::OpAtomicExchange:
1533 return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);
1534 case SPIRV::OpMemoryBarrier:
1535 return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);
1536 case SPIRV::OpAtomicFlagTestAndSet:
1537 case SPIRV::OpAtomicFlagClear:
1538 return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);
1539 default:
1540 if (Call->isSpirvOp())
1541 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
1542 GR->getSPIRVTypeID(Call->ReturnType));
1543 return false;
1544 }
1545}
1546
1548 MachineIRBuilder &MIRBuilder,
1549 SPIRVGlobalRegistry *GR) {
1550 // Lookup the instruction opcode in the TableGen records.
1551 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1552 unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->Name)->Opcode;
1553
1554 switch (Opcode) {
1555 case SPIRV::OpAtomicFAddEXT:
1556 case SPIRV::OpAtomicFMinEXT:
1557 case SPIRV::OpAtomicFMaxEXT:
1558 return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR);
1559 default:
1560 return false;
1561 }
1562}
1563
1565 MachineIRBuilder &MIRBuilder,
1566 SPIRVGlobalRegistry *GR) {
1567 // Lookup the instruction opcode in the TableGen records.
1568 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1569 unsigned Opcode =
1570 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1571
1572 return buildBarrierInst(Call, Opcode, MIRBuilder, GR);
1573}
1574
1576 MachineIRBuilder &MIRBuilder) {
1577 MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)
1578 .addDef(Call->ReturnRegister)
1579 .addUse(Call->Arguments[0]);
1580 return true;
1581}
1582
1584 MachineIRBuilder &MIRBuilder,
1585 SPIRVGlobalRegistry *GR) {
1586 if (Call->isSpirvOp())
1587 return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call,
1588 GR->getSPIRVTypeID(Call->ReturnType));
1589 unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode();
1590 bool IsVec = Opcode == SPIRV::OpTypeVector;
1591 // Use OpDot only in case of vector args and OpFMul in case of scalar args.
1592 MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS)
1593 .addDef(Call->ReturnRegister)
1594 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1595 .addUse(Call->Arguments[0])
1596 .addUse(Call->Arguments[1]);
1597 return true;
1598}
1599
1601 MachineIRBuilder &MIRBuilder,
1602 SPIRVGlobalRegistry *GR) {
1603 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1604 SPIRV::BuiltIn::BuiltIn Value =
1605 SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;
1606
1607 // For now, we only support a single Wave intrinsic with a single return type.
1608 assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt);
1609 LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType));
1610
1612 MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister,
1613 /* isConst= */ false, /* hasLinkageTy= */ false);
1614}
1615
1616// We expect a builtin
1617// Name(ptr sret([RetType]) %result, Type %operand1, Type %operand1)
1618// where %result is a pointer to where the result of the builtin execution
1619// is to be stored, and generate the following instructions:
1620// Res = Opcode RetType Operand1 Operand1
1621// OpStore RetVariable Res
1623 MachineIRBuilder &MIRBuilder,
1624 SPIRVGlobalRegistry *GR) {
1625 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1626 unsigned Opcode =
1627 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1628
1629 Register SRetReg = Call->Arguments[0];
1630 SPIRVType *PtrRetType = GR->getSPIRVTypeForVReg(SRetReg);
1631 SPIRVType *RetType = GR->getPointeeType(PtrRetType);
1632 if (!RetType)
1633 report_fatal_error("The first parameter must be a pointer");
1634 if (RetType->getOpcode() != SPIRV::OpTypeStruct)
1635 report_fatal_error("Expected struct type result for the arithmetic with "
1636 "overflow builtins");
1637
1638 SPIRVType *OpType1 = GR->getSPIRVTypeForVReg(Call->Arguments[1]);
1639 SPIRVType *OpType2 = GR->getSPIRVTypeForVReg(Call->Arguments[2]);
1640 if (!OpType1 || !OpType2 || OpType1 != OpType2)
1641 report_fatal_error("Operands must have the same type");
1642 if (OpType1->getOpcode() == SPIRV::OpTypeVector)
1643 switch (Opcode) {
1644 case SPIRV::OpIAddCarryS:
1645 Opcode = SPIRV::OpIAddCarryV;
1646 break;
1647 case SPIRV::OpISubBorrowS:
1648 Opcode = SPIRV::OpISubBorrowV;
1649 break;
1650 }
1651
1652 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1653 Register ResReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
1654 if (const TargetRegisterClass *DstRC =
1655 MRI->getRegClassOrNull(Call->Arguments[1])) {
1656 MRI->setRegClass(ResReg, DstRC);
1657 MRI->setType(ResReg, MRI->getType(Call->Arguments[1]));
1658 } else {
1659 MRI->setType(ResReg, LLT::scalar(64));
1660 }
1661 GR->assignSPIRVTypeToVReg(RetType, ResReg, MIRBuilder.getMF());
1662 MIRBuilder.buildInstr(Opcode)
1663 .addDef(ResReg)
1664 .addUse(GR->getSPIRVTypeID(RetType))
1665 .addUse(Call->Arguments[1])
1666 .addUse(Call->Arguments[2]);
1667 MIRBuilder.buildInstr(SPIRV::OpStore).addUse(SRetReg).addUse(ResReg);
1668 return true;
1669}
1670
1672 MachineIRBuilder &MIRBuilder,
1673 SPIRVGlobalRegistry *GR) {
1674 // Lookup the builtin record.
1675 SPIRV::BuiltIn::BuiltIn Value =
1676 SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value;
1677 uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize ||
1678 Value == SPIRV::BuiltIn::WorkgroupSize ||
1679 Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);
1680 return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0);
1681}
1682
1684 MachineIRBuilder &MIRBuilder,
1685 SPIRVGlobalRegistry *GR) {
1686 // Lookup the image size query component number in the TableGen records.
1687 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1688 uint32_t Component =
1689 SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component;
1690 // Query result may either be a vector or a scalar. If return type is not a
1691 // vector, expect only a single size component. Otherwise get the number of
1692 // expected components.
1693 SPIRVType *RetTy = Call->ReturnType;
1694 unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector
1695 ? RetTy->getOperand(2).getImm()
1696 : 1;
1697 // Get the actual number of query result/size components.
1698 SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
1699 unsigned NumActualRetComponents = getNumSizeComponents(ImgType);
1700 Register QueryResult = Call->ReturnRegister;
1701 SPIRVType *QueryResultType = Call->ReturnType;
1702 if (NumExpectedRetComponents != NumActualRetComponents) {
1703 QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(
1704 LLT::fixed_vector(NumActualRetComponents, 32));
1705 MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::vIDRegClass);
1706 SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
1707 QueryResultType = GR->getOrCreateSPIRVVectorType(
1708 IntTy, NumActualRetComponents, MIRBuilder);
1709 GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());
1710 }
1711 bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;
1712 unsigned Opcode =
1713 IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;
1714 auto MIB = MIRBuilder.buildInstr(Opcode)
1715 .addDef(QueryResult)
1716 .addUse(GR->getSPIRVTypeID(QueryResultType))
1717 .addUse(Call->Arguments[0]);
1718 if (!IsDimBuf)
1719 MIB.addUse(buildConstantIntReg32(0, MIRBuilder, GR)); // Lod id.
1720 if (NumExpectedRetComponents == NumActualRetComponents)
1721 return true;
1722 if (NumExpectedRetComponents == 1) {
1723 // Only 1 component is expected, build OpCompositeExtract instruction.
1724 unsigned ExtractedComposite =
1725 Component == 3 ? NumActualRetComponents - 1 : Component;
1726 assert(ExtractedComposite < NumActualRetComponents &&
1727 "Invalid composite index!");
1728 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
1729 SPIRVType *NewType = nullptr;
1730 if (QueryResultType->getOpcode() == SPIRV::OpTypeVector) {
1731 Register NewTypeReg = QueryResultType->getOperand(1).getReg();
1732 if (TypeReg != NewTypeReg &&
1733 (NewType = GR->getSPIRVTypeForVReg(NewTypeReg)) != nullptr)
1734 TypeReg = NewTypeReg;
1735 }
1736 MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1737 .addDef(Call->ReturnRegister)
1738 .addUse(TypeReg)
1739 .addUse(QueryResult)
1740 .addImm(ExtractedComposite);
1741 if (NewType != nullptr)
1742 insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
1743 MIRBuilder.getMF().getRegInfo());
1744 } else {
1745 // More than 1 component is expected, fill a new vector.
1746 auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)
1747 .addDef(Call->ReturnRegister)
1748 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1749 .addUse(QueryResult)
1750 .addUse(QueryResult);
1751 for (unsigned i = 0; i < NumExpectedRetComponents; ++i)
1752 MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);
1753 }
1754 return true;
1755}
1756
1758 MachineIRBuilder &MIRBuilder,
1759 SPIRVGlobalRegistry *GR) {
1760 assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&
1761 "Image samples query result must be of int type!");
1762
1763 // Lookup the instruction opcode in the TableGen records.
1764 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1765 unsigned Opcode =
1766 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1767
1768 Register Image = Call->Arguments[0];
1769 SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(
1770 GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());
1771 (void)ImageDimensionality;
1772
1773 switch (Opcode) {
1774 case SPIRV::OpImageQuerySamples:
1775 assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&
1776 "Image must be of 2D dimensionality");
1777 break;
1778 case SPIRV::OpImageQueryLevels:
1779 assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||
1780 ImageDimensionality == SPIRV::Dim::DIM_2D ||
1781 ImageDimensionality == SPIRV::Dim::DIM_3D ||
1782 ImageDimensionality == SPIRV::Dim::DIM_Cube) &&
1783 "Image must be of 1D/2D/3D/Cube dimensionality");
1784 break;
1785 }
1786
1787 MIRBuilder.buildInstr(Opcode)
1788 .addDef(Call->ReturnRegister)
1789 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1790 .addUse(Image);
1791 return true;
1792}
1793
1794// TODO: Move to TableGen.
1795static SPIRV::SamplerAddressingMode::SamplerAddressingMode
1797 switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {
1798 case SPIRV::CLK_ADDRESS_CLAMP:
1799 return SPIRV::SamplerAddressingMode::Clamp;
1800 case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:
1801 return SPIRV::SamplerAddressingMode::ClampToEdge;
1802 case SPIRV::CLK_ADDRESS_REPEAT:
1803 return SPIRV::SamplerAddressingMode::Repeat;
1804 case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:
1805 return SPIRV::SamplerAddressingMode::RepeatMirrored;
1806 case SPIRV::CLK_ADDRESS_NONE:
1807 return SPIRV::SamplerAddressingMode::None;
1808 default:
1809 report_fatal_error("Unknown CL address mode");
1810 }
1811}
1812
1813static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {
1814 return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;
1815}
1816
1817static SPIRV::SamplerFilterMode::SamplerFilterMode
1819 if (Bitmask & SPIRV::CLK_FILTER_LINEAR)
1820 return SPIRV::SamplerFilterMode::Linear;
1821 if (Bitmask & SPIRV::CLK_FILTER_NEAREST)
1822 return SPIRV::SamplerFilterMode::Nearest;
1823 return SPIRV::SamplerFilterMode::Nearest;
1824}
1825
1826static bool generateReadImageInst(const StringRef DemangledCall,
1827 const SPIRV::IncomingCall *Call,
1828 MachineIRBuilder &MIRBuilder,
1829 SPIRVGlobalRegistry *GR) {
1830 Register Image = Call->Arguments[0];
1831 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1832 bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler");
1833 bool HasMsaa = DemangledCall.contains_insensitive("msaa");
1834 if (HasOclSampler) {
1835 Register Sampler = Call->Arguments[1];
1836
1837 if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&
1838 getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {
1839 uint64_t SamplerMask = getIConstVal(Sampler, MRI);
1840 Sampler = GR->buildConstantSampler(
1842 getSamplerParamFromBitmask(SamplerMask),
1843 getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder,
1844 GR->getSPIRVTypeForVReg(Sampler));
1845 }
1846 SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1847 SPIRVType *SampledImageType =
1848 GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1849 Register SampledImage = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
1850
1851 MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1852 .addDef(SampledImage)
1853 .addUse(GR->getSPIRVTypeID(SampledImageType))
1854 .addUse(Image)
1855 .addUse(Sampler);
1856
1858 MIRBuilder);
1859
1860 if (Call->ReturnType->getOpcode() != SPIRV::OpTypeVector) {
1861 SPIRVType *TempType =
1862 GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder);
1863 Register TempRegister =
1864 MRI->createGenericVirtualRegister(GR->getRegType(TempType));
1865 MRI->setRegClass(TempRegister, GR->getRegClass(TempType));
1866 GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());
1867 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1868 .addDef(TempRegister)
1869 .addUse(GR->getSPIRVTypeID(TempType))
1870 .addUse(SampledImage)
1871 .addUse(Call->Arguments[2]) // Coordinate.
1872 .addImm(SPIRV::ImageOperand::Lod)
1873 .addUse(Lod);
1874 MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)
1875 .addDef(Call->ReturnRegister)
1876 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1877 .addUse(TempRegister)
1878 .addImm(0);
1879 } else {
1880 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1881 .addDef(Call->ReturnRegister)
1882 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1883 .addUse(SampledImage)
1884 .addUse(Call->Arguments[2]) // Coordinate.
1885 .addImm(SPIRV::ImageOperand::Lod)
1886 .addUse(Lod);
1887 }
1888 } else if (HasMsaa) {
1889 MIRBuilder.buildInstr(SPIRV::OpImageRead)
1890 .addDef(Call->ReturnRegister)
1891 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1892 .addUse(Image)
1893 .addUse(Call->Arguments[1]) // Coordinate.
1894 .addImm(SPIRV::ImageOperand::Sample)
1895 .addUse(Call->Arguments[2]);
1896 } else {
1897 MIRBuilder.buildInstr(SPIRV::OpImageRead)
1898 .addDef(Call->ReturnRegister)
1899 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
1900 .addUse(Image)
1901 .addUse(Call->Arguments[1]); // Coordinate.
1902 }
1903 return true;
1904}
1905
1907 MachineIRBuilder &MIRBuilder,
1908 SPIRVGlobalRegistry *GR) {
1909 MIRBuilder.buildInstr(SPIRV::OpImageWrite)
1910 .addUse(Call->Arguments[0]) // Image.
1911 .addUse(Call->Arguments[1]) // Coordinate.
1912 .addUse(Call->Arguments[2]); // Texel.
1913 return true;
1914}
1915
1916static bool generateSampleImageInst(const StringRef DemangledCall,
1917 const SPIRV::IncomingCall *Call,
1918 MachineIRBuilder &MIRBuilder,
1919 SPIRVGlobalRegistry *GR) {
1920 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
1921 if (Call->Builtin->Name.contains_insensitive(
1922 "__translate_sampler_initializer")) {
1923 // Build sampler literal.
1924 uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI);
1925 Register Sampler = GR->buildConstantSampler(
1926 Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),
1928 getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType);
1929 return Sampler.isValid();
1930 } else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) {
1931 // Create OpSampledImage.
1932 Register Image = Call->Arguments[0];
1933 SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);
1934 SPIRVType *SampledImageType =
1935 GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);
1936 Register SampledImage =
1937 Call->ReturnRegister.isValid()
1938 ? Call->ReturnRegister
1939 : MRI->createVirtualRegister(&SPIRV::iIDRegClass);
1940 MIRBuilder.buildInstr(SPIRV::OpSampledImage)
1941 .addDef(SampledImage)
1942 .addUse(GR->getSPIRVTypeID(SampledImageType))
1943 .addUse(Image)
1944 .addUse(Call->Arguments[1]); // Sampler.
1945 return true;
1946 } else if (Call->Builtin->Name.contains_insensitive(
1947 "__spirv_ImageSampleExplicitLod")) {
1948 // Sample an image using an explicit level of detail.
1949 std::string ReturnType = DemangledCall.str();
1950 if (DemangledCall.contains("_R")) {
1951 ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);
1952 ReturnType = ReturnType.substr(0, ReturnType.find('('));
1953 }
1954 SPIRVType *Type =
1955 Call->ReturnType
1956 ? Call->ReturnType
1957 : GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder);
1958 if (!Type) {
1959 std::string DiagMsg =
1960 "Unable to recognize SPIRV type name: " + ReturnType;
1961 report_fatal_error(DiagMsg.c_str());
1962 }
1963 MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)
1964 .addDef(Call->ReturnRegister)
1966 .addUse(Call->Arguments[0]) // Image.
1967 .addUse(Call->Arguments[1]) // Coordinate.
1968 .addImm(SPIRV::ImageOperand::Lod)
1969 .addUse(Call->Arguments[3]);
1970 return true;
1971 }
1972 return false;
1973}
1974
1976 MachineIRBuilder &MIRBuilder) {
1977 MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],
1978 Call->Arguments[1], Call->Arguments[2]);
1979 return true;
1980}
1981
1983 MachineIRBuilder &MIRBuilder,
1984 SPIRVGlobalRegistry *GR) {
1985 return buildOpFromWrapper(MIRBuilder, SPIRV::OpCompositeConstruct, Call,
1986 GR->getSPIRVTypeID(Call->ReturnType));
1987}
1988
1990 MachineIRBuilder &MIRBuilder,
1991 SPIRVGlobalRegistry *GR) {
1992 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
1993 unsigned Opcode =
1994 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
1995 bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR &&
1996 Opcode != SPIRV::OpCooperativeMatrixStoreCheckedINTEL &&
1997 Opcode != SPIRV::OpCooperativeMatrixPrefetchINTEL;
1998 unsigned ArgSz = Call->Arguments.size();
1999 unsigned LiteralIdx = 0;
2000 switch (Opcode) {
2001 // Memory operand is optional and is literal.
2002 case SPIRV::OpCooperativeMatrixLoadKHR:
2003 LiteralIdx = ArgSz > 3 ? 3 : 0;
2004 break;
2005 case SPIRV::OpCooperativeMatrixStoreKHR:
2006 LiteralIdx = ArgSz > 4 ? 4 : 0;
2007 break;
2008 case SPIRV::OpCooperativeMatrixLoadCheckedINTEL:
2009 LiteralIdx = ArgSz > 7 ? 7 : 0;
2010 break;
2011 case SPIRV::OpCooperativeMatrixStoreCheckedINTEL:
2012 LiteralIdx = ArgSz > 8 ? 8 : 0;
2013 break;
2014 // Cooperative Matrix Operands operand is optional and is literal.
2015 case SPIRV::OpCooperativeMatrixMulAddKHR:
2016 LiteralIdx = ArgSz > 3 ? 3 : 0;
2017 break;
2018 };
2019
2021 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2022 if (Opcode == SPIRV::OpCooperativeMatrixPrefetchINTEL) {
2023 const uint32_t CacheLevel = getConstFromIntrinsic(Call->Arguments[3], MRI);
2024 auto MIB = MIRBuilder.buildInstr(SPIRV::OpCooperativeMatrixPrefetchINTEL)
2025 .addUse(Call->Arguments[0]) // pointer
2026 .addUse(Call->Arguments[1]) // rows
2027 .addUse(Call->Arguments[2]) // columns
2028 .addImm(CacheLevel) // cache level
2029 .addUse(Call->Arguments[4]); // memory layout
2030 if (ArgSz > 5)
2031 MIB.addUse(Call->Arguments[5]); // stride
2032 if (ArgSz > 6) {
2033 const uint32_t MemOp = getConstFromIntrinsic(Call->Arguments[6], MRI);
2034 MIB.addImm(MemOp); // memory operand
2035 }
2036 return true;
2037 }
2038 if (LiteralIdx > 0)
2039 ImmArgs.push_back(getConstFromIntrinsic(Call->Arguments[LiteralIdx], MRI));
2040 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2041 if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) {
2042 SPIRVType *CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
2043 if (!CoopMatrType)
2044 report_fatal_error("Can't find a register's type definition");
2045 MIRBuilder.buildInstr(Opcode)
2046 .addDef(Call->ReturnRegister)
2047 .addUse(TypeReg)
2048 .addUse(CoopMatrType->getOperand(0).getReg());
2049 return true;
2050 }
2051 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2052 IsSet ? TypeReg : Register(0), ImmArgs);
2053}
2054
2056 MachineIRBuilder &MIRBuilder,
2057 SPIRVGlobalRegistry *GR) {
2058 // Lookup the instruction opcode in the TableGen records.
2059 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2060 unsigned Opcode =
2061 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2062 const MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2063
2064 switch (Opcode) {
2065 case SPIRV::OpSpecConstant: {
2066 // Build the SpecID decoration.
2067 unsigned SpecId =
2068 static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));
2069 buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,
2070 {SpecId});
2071 // Determine the constant MI.
2072 Register ConstRegister = Call->Arguments[1];
2073 const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);
2074 assert(Const &&
2075 (Const->getOpcode() == TargetOpcode::G_CONSTANT ||
2076 Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&
2077 "Argument should be either an int or floating-point constant");
2078 // Determine the opcode and built the OpSpec MI.
2079 const MachineOperand &ConstOperand = Const->getOperand(1);
2080 if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {
2081 assert(ConstOperand.isCImm() && "Int constant operand is expected");
2082 Opcode = ConstOperand.getCImm()->getValue().getZExtValue()
2083 ? SPIRV::OpSpecConstantTrue
2084 : SPIRV::OpSpecConstantFalse;
2085 }
2086 auto MIB = MIRBuilder.buildInstr(Opcode)
2087 .addDef(Call->ReturnRegister)
2088 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2089
2090 if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {
2091 if (Const->getOpcode() == TargetOpcode::G_CONSTANT)
2092 addNumImm(ConstOperand.getCImm()->getValue(), MIB);
2093 else
2094 addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);
2095 }
2096 return true;
2097 }
2098 case SPIRV::OpSpecConstantComposite: {
2099 auto MIB = MIRBuilder.buildInstr(Opcode)
2100 .addDef(Call->ReturnRegister)
2101 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2102 for (unsigned i = 0; i < Call->Arguments.size(); i++)
2103 MIB.addUse(Call->Arguments[i]);
2104 return true;
2105 }
2106 default:
2107 return false;
2108 }
2109}
2110
2111static bool buildNDRange(const SPIRV::IncomingCall *Call,
2112 MachineIRBuilder &MIRBuilder,
2113 SPIRVGlobalRegistry *GR) {
2114 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2115 SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);
2116 assert(PtrType->getOpcode() == SPIRV::OpTypePointer &&
2117 PtrType->getOperand(2).isReg());
2118 Register TypeReg = PtrType->getOperand(2).getReg();
2120 MachineFunction &MF = MIRBuilder.getMF();
2121 Register TmpReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2122 GR->assignSPIRVTypeToVReg(StructType, TmpReg, MF);
2123 // Skip the first arg, it's the destination pointer. OpBuildNDRange takes
2124 // three other arguments, so pass zero constant on absence.
2125 unsigned NumArgs = Call->Arguments.size();
2126 assert(NumArgs >= 2);
2127 Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2];
2128 Register LocalWorkSize =
2129 NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3];
2130 Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1];
2131 if (NumArgs < 4) {
2132 Register Const;
2133 SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize);
2134 if (SpvTy->getOpcode() == SPIRV::OpTypePointer) {
2135 MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize);
2136 assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) &&
2137 DefInstr->getOperand(3).isReg());
2138 Register GWSPtr = DefInstr->getOperand(3).getReg();
2139 // TODO: Maybe simplify generation of the type of the fields.
2140 unsigned Size = Call->Builtin->Name == "ndrange_3D" ? 3 : 2;
2141 unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32;
2143 Type *FieldTy = ArrayType::get(BaseTy, Size);
2144 SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder);
2145 GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2146 GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize, MF);
2147 MIRBuilder.buildInstr(SPIRV::OpLoad)
2148 .addDef(GlobalWorkSize)
2149 .addUse(GR->getSPIRVTypeID(SpvFieldTy))
2150 .addUse(GWSPtr);
2151 const SPIRVSubtarget &ST =
2152 cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
2153 Const = GR->getOrCreateConstIntArray(0, Size, *MIRBuilder.getInsertPt(),
2154 SpvFieldTy, *ST.getInstrInfo());
2155 } else {
2156 Const = GR->buildConstantInt(0, MIRBuilder, SpvTy);
2157 }
2158 if (!LocalWorkSize.isValid())
2159 LocalWorkSize = Const;
2160 if (!GlobalWorkOffset.isValid())
2161 GlobalWorkOffset = Const;
2162 }
2163 assert(LocalWorkSize.isValid() && GlobalWorkOffset.isValid());
2164 MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)
2165 .addDef(TmpReg)
2166 .addUse(TypeReg)
2167 .addUse(GlobalWorkSize)
2168 .addUse(LocalWorkSize)
2169 .addUse(GlobalWorkOffset);
2170 return MIRBuilder.buildInstr(SPIRV::OpStore)
2171 .addUse(Call->Arguments[0])
2172 .addUse(TmpReg);
2173}
2174
2175// TODO: maybe move to the global register.
2176static SPIRVType *
2178 SPIRVGlobalRegistry *GR) {
2179 LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
2180 Type *OpaqueType = StructType::getTypeByName(Context, "spirv.DeviceEvent");
2181 if (!OpaqueType)
2182 OpaqueType = StructType::getTypeByName(Context, "opencl.clk_event_t");
2183 if (!OpaqueType)
2184 OpaqueType = StructType::create(Context, "spirv.DeviceEvent");
2185 unsigned SC0 = storageClassToAddressSpace(SPIRV::StorageClass::Function);
2186 unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2187 Type *PtrType = PointerType::get(PointerType::get(OpaqueType, SC0), SC1);
2188 return GR->getOrCreateSPIRVType(PtrType, MIRBuilder);
2189}
2190
2192 MachineIRBuilder &MIRBuilder,
2193 SPIRVGlobalRegistry *GR) {
2194 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2195 const DataLayout &DL = MIRBuilder.getDataLayout();
2196 bool IsSpirvOp = Call->isSpirvOp();
2197 bool HasEvents = Call->Builtin->Name.contains("events") || IsSpirvOp;
2198 const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
2199
2200 // Make vararg instructions before OpEnqueueKernel.
2201 // Local sizes arguments: Sizes of block invoke arguments. Clang generates
2202 // local size operands as an array, so we need to unpack them.
2203 SmallVector<Register, 16> LocalSizes;
2204 if (Call->Builtin->Name.contains("_varargs") || IsSpirvOp) {
2205 const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6;
2206 Register GepReg = Call->Arguments[LocalSizeArrayIdx];
2207 MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg);
2208 assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) &&
2209 GepMI->getOperand(3).isReg());
2210 Register ArrayReg = GepMI->getOperand(3).getReg();
2211 MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg);
2212 const Type *LocalSizeTy = getMachineInstrType(ArrayMI);
2213 assert(LocalSizeTy && "Local size type is expected");
2214 const uint64_t LocalSizeNum =
2215 cast<ArrayType>(LocalSizeTy)->getNumElements();
2216 unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
2217 const LLT LLType = LLT::pointer(SC, GR->getPointerSize());
2218 const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType(
2219 Int32Ty, MIRBuilder, SPIRV::StorageClass::Function);
2220 for (unsigned I = 0; I < LocalSizeNum; ++I) {
2221 Register Reg = MRI->createVirtualRegister(&SPIRV::pIDRegClass);
2222 MRI->setType(Reg, LLType);
2223 GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF());
2224 auto GEPInst = MIRBuilder.buildIntrinsic(
2225 Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false);
2226 GEPInst
2227 .addImm(GepMI->getOperand(2).getImm()) // In bound.
2228 .addUse(ArrayMI->getOperand(0).getReg()) // Alloca.
2229 .addUse(buildConstantIntReg32(0, MIRBuilder, GR)) // Indices.
2230 .addUse(buildConstantIntReg32(I, MIRBuilder, GR));
2231 LocalSizes.push_back(Reg);
2232 }
2233 }
2234
2235 // SPIRV OpEnqueueKernel instruction has 10+ arguments.
2236 auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)
2237 .addDef(Call->ReturnRegister)
2238 .addUse(GR->getSPIRVTypeID(Int32Ty));
2239
2240 // Copy all arguments before block invoke function pointer.
2241 const unsigned BlockFIdx = HasEvents ? 6 : 3;
2242 for (unsigned i = 0; i < BlockFIdx; i++)
2243 MIB.addUse(Call->Arguments[i]);
2244
2245 // If there are no event arguments in the original call, add dummy ones.
2246 if (!HasEvents) {
2247 MIB.addUse(buildConstantIntReg32(0, MIRBuilder, GR)); // Dummy num events.
2248 Register NullPtr = GR->getOrCreateConstNullPtr(
2249 MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR));
2250 MIB.addUse(NullPtr); // Dummy wait events.
2251 MIB.addUse(NullPtr); // Dummy ret event.
2252 }
2253
2254 MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI);
2255 assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
2256 // Invoke: Pointer to invoke function.
2257 MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal());
2258
2259 Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1];
2260 // Param: Pointer to block literal.
2261 MIB.addUse(BlockLiteralReg);
2262
2263 Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
2264 // TODO: these numbers should be obtained from block literal structure.
2265 // Param Size: Size of block literal structure.
2266 MIB.addUse(buildConstantIntReg32(DL.getTypeStoreSize(PType), MIRBuilder, GR));
2267 // Param Aligment: Aligment of block literal structure.
2268 MIB.addUse(buildConstantIntReg32(DL.getPrefTypeAlign(PType).value(),
2269 MIRBuilder, GR));
2270
2271 for (unsigned i = 0; i < LocalSizes.size(); i++)
2272 MIB.addUse(LocalSizes[i]);
2273 return true;
2274}
2275
2277 MachineIRBuilder &MIRBuilder,
2278 SPIRVGlobalRegistry *GR) {
2279 // Lookup the instruction opcode in the TableGen records.
2280 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2281 unsigned Opcode =
2282 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2283
2284 switch (Opcode) {
2285 case SPIRV::OpRetainEvent:
2286 case SPIRV::OpReleaseEvent:
2287 return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);
2288 case SPIRV::OpCreateUserEvent:
2289 case SPIRV::OpGetDefaultQueue:
2290 return MIRBuilder.buildInstr(Opcode)
2291 .addDef(Call->ReturnRegister)
2292 .addUse(GR->getSPIRVTypeID(Call->ReturnType));
2293 case SPIRV::OpIsValidEvent:
2294 return MIRBuilder.buildInstr(Opcode)
2295 .addDef(Call->ReturnRegister)
2296 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2297 .addUse(Call->Arguments[0]);
2298 case SPIRV::OpSetUserEventStatus:
2299 return MIRBuilder.buildInstr(Opcode)
2300 .addUse(Call->Arguments[0])
2301 .addUse(Call->Arguments[1]);
2302 case SPIRV::OpCaptureEventProfilingInfo:
2303 return MIRBuilder.buildInstr(Opcode)
2304 .addUse(Call->Arguments[0])
2305 .addUse(Call->Arguments[1])
2306 .addUse(Call->Arguments[2]);
2307 case SPIRV::OpBuildNDRange:
2308 return buildNDRange(Call, MIRBuilder, GR);
2309 case SPIRV::OpEnqueueKernel:
2310 return buildEnqueueKernel(Call, MIRBuilder, GR);
2311 default:
2312 return false;
2313 }
2314}
2315
2317 MachineIRBuilder &MIRBuilder,
2318 SPIRVGlobalRegistry *GR) {
2319 // Lookup the instruction opcode in the TableGen records.
2320 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2321 unsigned Opcode =
2322 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2323
2324 bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;
2325 Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2326 if (Call->isSpirvOp())
2327 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2328 IsSet ? TypeReg : Register(0));
2329
2330 auto Scope = buildConstantIntReg32(SPIRV::Scope::Workgroup, MIRBuilder, GR);
2331
2332 switch (Opcode) {
2333 case SPIRV::OpGroupAsyncCopy: {
2334 SPIRVType *NewType =
2335 Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent
2336 ? nullptr
2337 : GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder);
2338 Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);
2339 unsigned NumArgs = Call->Arguments.size();
2340 Register EventReg = Call->Arguments[NumArgs - 1];
2341 bool Res = MIRBuilder.buildInstr(Opcode)
2342 .addDef(Call->ReturnRegister)
2343 .addUse(TypeReg)
2344 .addUse(Scope)
2345 .addUse(Call->Arguments[0])
2346 .addUse(Call->Arguments[1])
2347 .addUse(Call->Arguments[2])
2348 .addUse(Call->Arguments.size() > 4
2349 ? Call->Arguments[3]
2350 : buildConstantIntReg32(1, MIRBuilder, GR))
2351 .addUse(EventReg);
2352 if (NewType != nullptr)
2353 insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,
2354 MIRBuilder.getMF().getRegInfo());
2355 return Res;
2356 }
2357 case SPIRV::OpGroupWaitEvents:
2358 return MIRBuilder.buildInstr(Opcode)
2359 .addUse(Scope)
2360 .addUse(Call->Arguments[0])
2361 .addUse(Call->Arguments[1]);
2362 default:
2363 return false;
2364 }
2365}
2366
2367static bool generateConvertInst(const StringRef DemangledCall,
2368 const SPIRV::IncomingCall *Call,
2369 MachineIRBuilder &MIRBuilder,
2370 SPIRVGlobalRegistry *GR) {
2371 // Lookup the conversion builtin in the TableGen records.
2372 const SPIRV::ConvertBuiltin *Builtin =
2373 SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set);
2374
2375 if (!Builtin && Call->isSpirvOp()) {
2376 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2377 unsigned Opcode =
2378 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2379 return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2380 GR->getSPIRVTypeID(Call->ReturnType));
2381 }
2382
2383 if (Builtin->IsSaturated)
2384 buildOpDecorate(Call->ReturnRegister, MIRBuilder,
2385 SPIRV::Decoration::SaturatedConversion, {});
2386 if (Builtin->IsRounded)
2387 buildOpDecorate(Call->ReturnRegister, MIRBuilder,
2388 SPIRV::Decoration::FPRoundingMode,
2389 {(unsigned)Builtin->RoundingMode});
2390
2391 std::string NeedExtMsg; // no errors if empty
2392 bool IsRightComponentsNumber = true; // check if input/output accepts vectors
2393 unsigned Opcode = SPIRV::OpNop;
2394 if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {
2395 // Int -> ...
2396 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2397 // Int -> Int
2398 if (Builtin->IsSaturated)
2399 Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS
2400 : SPIRV::OpSatConvertSToU;
2401 else
2402 Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert
2403 : SPIRV::OpSConvert;
2404 } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
2405 SPIRV::OpTypeFloat)) {
2406 // Int -> Float
2407 if (Builtin->IsBfloat16) {
2408 const auto *ST = static_cast<const SPIRVSubtarget *>(
2409 &MIRBuilder.getMF().getSubtarget());
2410 if (!ST->canUseExtension(
2411 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
2412 NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
2413 IsRightComponentsNumber =
2414 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
2415 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
2416 Opcode = SPIRV::OpConvertBF16ToFINTEL;
2417 } else {
2418 bool IsSourceSigned =
2419 DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';
2420 Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;
2421 }
2422 }
2423 } else if (GR->isScalarOrVectorOfType(Call->Arguments[0],
2424 SPIRV::OpTypeFloat)) {
2425 // Float -> ...
2426 if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {
2427 // Float -> Int
2428 if (Builtin->IsBfloat16) {
2429 const auto *ST = static_cast<const SPIRVSubtarget *>(
2430 &MIRBuilder.getMF().getSubtarget());
2431 if (!ST->canUseExtension(
2432 SPIRV::Extension::SPV_INTEL_bfloat16_conversion))
2433 NeedExtMsg = "SPV_INTEL_bfloat16_conversion";
2434 IsRightComponentsNumber =
2435 GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==
2436 GR->getScalarOrVectorComponentCount(Call->ReturnRegister);
2437 Opcode = SPIRV::OpConvertFToBF16INTEL;
2438 } else {
2439 Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS
2440 : SPIRV::OpConvertFToU;
2441 }
2442 } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,
2443 SPIRV::OpTypeFloat)) {
2444 // Float -> Float
2445 Opcode = SPIRV::OpFConvert;
2446 }
2447 }
2448
2449 if (!NeedExtMsg.empty()) {
2450 std::string DiagMsg = std::string(Builtin->Name) +
2451 ": the builtin requires the following SPIR-V "
2452 "extension: " +
2453 NeedExtMsg;
2454 report_fatal_error(DiagMsg.c_str(), false);
2455 }
2456 if (!IsRightComponentsNumber) {
2457 std::string DiagMsg =
2458 std::string(Builtin->Name) +
2459 ": result and argument must have the same number of components";
2460 report_fatal_error(DiagMsg.c_str(), false);
2461 }
2462 assert(Opcode != SPIRV::OpNop &&
2463 "Conversion between the types not implemented!");
2464
2465 MIRBuilder.buildInstr(Opcode)
2466 .addDef(Call->ReturnRegister)
2467 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2468 .addUse(Call->Arguments[0]);
2469 return true;
2470}
2471
2473 MachineIRBuilder &MIRBuilder,
2474 SPIRVGlobalRegistry *GR) {
2475 // Lookup the vector load/store builtin in the TableGen records.
2476 const SPIRV::VectorLoadStoreBuiltin *Builtin =
2477 SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2478 Call->Builtin->Set);
2479 // Build extended instruction.
2480 auto MIB =
2481 MIRBuilder.buildInstr(SPIRV::OpExtInst)
2482 .addDef(Call->ReturnRegister)
2483 .addUse(GR->getSPIRVTypeID(Call->ReturnType))
2484 .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
2485 .addImm(Builtin->Number);
2486 for (auto Argument : Call->Arguments)
2487 MIB.addUse(Argument);
2488 if (Builtin->Name.contains("load") && Builtin->ElementCount > 1)
2489 MIB.addImm(Builtin->ElementCount);
2490
2491 // Rounding mode should be passed as a last argument in the MI for builtins
2492 // like "vstorea_halfn_r".
2493 if (Builtin->IsRounded)
2494 MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));
2495 return true;
2496}
2497
2499 MachineIRBuilder &MIRBuilder,
2500 SPIRVGlobalRegistry *GR) {
2501 // Lookup the instruction opcode in the TableGen records.
2502 const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
2503 unsigned Opcode =
2504 SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2505 bool IsLoad = Opcode == SPIRV::OpLoad;
2506 // Build the instruction.
2507 auto MIB = MIRBuilder.buildInstr(Opcode);
2508 if (IsLoad) {
2509 MIB.addDef(Call->ReturnRegister);
2510 MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));
2511 }
2512 // Add a pointer to the value to load/store.
2513 MIB.addUse(Call->Arguments[0]);
2514 MachineRegisterInfo *MRI = MIRBuilder.getMRI();
2515 // Add a value to store.
2516 if (!IsLoad)
2517 MIB.addUse(Call->Arguments[1]);
2518 // Add optional memory attributes and an alignment.
2519 unsigned NumArgs = Call->Arguments.size();
2520 if ((IsLoad && NumArgs >= 2) || NumArgs >= 3)
2521 MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI));
2522 if ((IsLoad && NumArgs >= 3) || NumArgs >= 4)
2523 MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI));
2524 return true;
2525}
2526
2527namespace SPIRV {
2528// Try to find a builtin function attributes by a demangled function name and
2529// return a tuple <builtin group, op code, ext instruction number>, or a special
2530// tuple value <-1, 0, 0> if the builtin function is not found.
2531// Not all builtin functions are supported, only those with a ready-to-use op
2532// code or instruction number defined in TableGen.
2533// TODO: consider a major rework of mapping demangled calls into a builtin
2534// functions to unify search and decrease number of individual cases.
2535std::tuple<int, unsigned, unsigned>
2536mapBuiltinToOpcode(const StringRef DemangledCall,
2537 SPIRV::InstructionSet::InstructionSet Set) {
2538 Register Reg;
2540 std::unique_ptr<const IncomingCall> Call =
2541 lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args);
2542 if (!Call)
2543 return std::make_tuple(-1, 0, 0);
2544
2545 switch (Call->Builtin->Group) {
2546 case SPIRV::Relational:
2547 case SPIRV::Atomic:
2548 case SPIRV::Barrier:
2549 case SPIRV::CastToPtr:
2550 case SPIRV::ImageMiscQuery:
2551 case SPIRV::SpecConstant:
2552 case SPIRV::Enqueue:
2553 case SPIRV::AsyncCopy:
2554 case SPIRV::LoadStore:
2555 case SPIRV::CoopMatr:
2556 if (const auto *R =
2557 SPIRV::lookupNativeBuiltin(Call->Builtin->Name, Call->Builtin->Set))
2558 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2559 break;
2560 case SPIRV::Extended:
2561 if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->Name,
2562 Call->Builtin->Set))
2563 return std::make_tuple(Call->Builtin->Group, 0, R->Number);
2564 break;
2565 case SPIRV::VectorLoadStore:
2566 if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,
2567 Call->Builtin->Set))
2568 return std::make_tuple(SPIRV::Extended, 0, R->Number);
2569 break;
2570 case SPIRV::Group:
2571 if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->Name))
2572 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2573 break;
2574 case SPIRV::AtomicFloating:
2575 if (const auto *R = SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->Name))
2576 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2577 break;
2578 case SPIRV::IntelSubgroups:
2579 if (const auto *R = SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->Name))
2580 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2581 break;
2582 case SPIRV::GroupUniform:
2583 if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->Name))
2584 return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);
2585 break;
2586 case SPIRV::WriteImage:
2587 return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0);
2588 case SPIRV::Select:
2589 return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0);
2590 case SPIRV::Construct:
2591 return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct,
2592 0);
2593 case SPIRV::KernelClock:
2594 return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0);
2595 default:
2596 return std::make_tuple(-1, 0, 0);
2597 }
2598 return std::make_tuple(-1, 0, 0);
2599}
2600
2601std::optional<bool> lowerBuiltin(const StringRef DemangledCall,
2602 SPIRV::InstructionSet::InstructionSet Set,
2603 MachineIRBuilder &MIRBuilder,
2604 const Register OrigRet, const Type *OrigRetTy,
2605 const SmallVectorImpl<Register> &Args,
2606 SPIRVGlobalRegistry *GR) {
2607 LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");
2608
2609 // Lookup the builtin in the TableGen records.
2610 SPIRVType *SpvType = GR->getSPIRVTypeForVReg(OrigRet);
2611 assert(SpvType && "Inconsistent return register: expected valid type info");
2612 std::unique_ptr<const IncomingCall> Call =
2613 lookupBuiltin(DemangledCall, Set, OrigRet, SpvType, Args);
2614
2615 if (!Call) {
2616 LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");
2617 return std::nullopt;
2618 }
2619
2620 // TODO: check if the provided args meet the builtin requirments.
2621 assert(Args.size() >= Call->Builtin->MinNumArgs &&
2622 "Too few arguments to generate the builtin");
2623 if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs)
2624 LLVM_DEBUG(dbgs() << "More arguments provided than required!\n");
2625
2626 // Match the builtin with implementation based on the grouping.
2627 switch (Call->Builtin->Group) {
2628 case SPIRV::Extended:
2629 return generateExtInst(Call.get(), MIRBuilder, GR);
2630 case SPIRV::Relational:
2631 return generateRelationalInst(Call.get(), MIRBuilder, GR);
2632 case SPIRV::Group:
2633 return generateGroupInst(Call.get(), MIRBuilder, GR);
2634 case SPIRV::Variable:
2635 return generateBuiltinVar(Call.get(), MIRBuilder, GR);
2636 case SPIRV::Atomic:
2637 return generateAtomicInst(Call.get(), MIRBuilder, GR);
2638 case SPIRV::AtomicFloating:
2639 return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);
2640 case SPIRV::Barrier:
2641 return generateBarrierInst(Call.get(), MIRBuilder, GR);
2642 case SPIRV::CastToPtr:
2643 return generateCastToPtrInst(Call.get(), MIRBuilder);
2644 case SPIRV::Dot:
2645 return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);
2646 case SPIRV::Wave:
2647 return generateWaveInst(Call.get(), MIRBuilder, GR);
2648 case SPIRV::ICarryBorrow:
2649 return generateICarryBorrowInst(Call.get(), MIRBuilder, GR);
2650 case SPIRV::GetQuery:
2651 return generateGetQueryInst(Call.get(), MIRBuilder, GR);
2652 case SPIRV::ImageSizeQuery:
2653 return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);
2654 case SPIRV::ImageMiscQuery:
2655 return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);
2656 case SPIRV::ReadImage:
2657 return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2658 case SPIRV::WriteImage:
2659 return generateWriteImageInst(Call.get(), MIRBuilder, GR);
2660 case SPIRV::SampleImage:
2661 return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);
2662 case SPIRV::Select:
2663 return generateSelectInst(Call.get(), MIRBuilder);
2664 case SPIRV::Construct:
2665 return generateConstructInst(Call.get(), MIRBuilder, GR);
2666 case SPIRV::SpecConstant:
2667 return generateSpecConstantInst(Call.get(), MIRBuilder, GR);
2668 case SPIRV::Enqueue:
2669 return generateEnqueueInst(Call.get(), MIRBuilder, GR);
2670 case SPIRV::AsyncCopy:
2671 return generateAsyncCopy(Call.get(), MIRBuilder, GR);
2672 case SPIRV::Convert:
2673 return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);
2674 case SPIRV::VectorLoadStore:
2675 return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);
2676 case SPIRV::LoadStore:
2677 return generateLoadStoreInst(Call.get(), MIRBuilder, GR);
2678 case SPIRV::IntelSubgroups:
2679 return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR);
2680 case SPIRV::GroupUniform:
2681 return generateGroupUniformInst(Call.get(), MIRBuilder, GR);
2682 case SPIRV::KernelClock:
2683 return generateKernelClockInst(Call.get(), MIRBuilder, GR);
2684 case SPIRV::CoopMatr:
2685 return generateCoopMatrInst(Call.get(), MIRBuilder, GR);
2686 }
2687 return false;
2688}
2689
2691 // Parse strings representing OpenCL builtin types.
2692 if (hasBuiltinTypePrefix(TypeStr)) {
2693 // OpenCL builtin types in demangled call strings have the following format:
2694 // e.g. ocl_image2d_ro
2695 [[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_");
2696 assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix");
2697
2698 // Check if this is pointer to a builtin type and not just pointer
2699 // representing a builtin type. In case it is a pointer to builtin type,
2700 // this will require additional handling in the method calling
2701 // parseBuiltinCallArgumentBaseType(...) as this function only retrieves the
2702 // base types.
2703 if (TypeStr.ends_with("*"))
2704 TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));
2705
2706 return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",
2707 Ctx);
2708 }
2709
2710 // Parse type name in either "typeN" or "type vector[N]" format, where
2711 // N is the number of elements of the vector.
2712 Type *BaseType;
2713 unsigned VecElts = 0;
2714
2715 BaseType = parseBasicTypeName(TypeStr, Ctx);
2716 if (!BaseType)
2717 // Unable to recognize SPIRV type name.
2718 return nullptr;
2719
2720 // Handle "typeN*" or "type vector[N]*".
2721 TypeStr.consume_back("*");
2722
2723 if (TypeStr.consume_front(" vector["))
2724 TypeStr = TypeStr.substr(0, TypeStr.find(']'));
2725
2726 TypeStr.getAsInteger(10, VecElts);
2727 if (VecElts > 0)
2729 BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);
2730
2731 return BaseType;
2732}
2733
2735 const StringRef DemangledCall, LLVMContext &Ctx) {
2736 auto Pos1 = DemangledCall.find('(');
2737 if (Pos1 == StringRef::npos)
2738 return false;
2739 auto Pos2 = DemangledCall.find(')');
2740 if (Pos2 == StringRef::npos || Pos1 > Pos2)
2741 return false;
2742 DemangledCall.slice(Pos1 + 1, Pos2)
2743 .split(BuiltinArgsTypeStrs, ',', -1, false);
2744 return true;
2745}
2746
2748 unsigned ArgIdx, LLVMContext &Ctx) {
2749 SmallVector<StringRef, 10> BuiltinArgsTypeStrs;
2750 parseBuiltinTypeStr(BuiltinArgsTypeStrs, DemangledCall, Ctx);
2751 if (ArgIdx >= BuiltinArgsTypeStrs.size())
2752 return nullptr;
2753 StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim();
2754 return parseBuiltinCallArgumentType(TypeStr, Ctx);
2755}
2756
2760};
2761
2762#define GET_BuiltinTypes_DECL
2763#define GET_BuiltinTypes_IMPL
2764
2768};
2769
2770#define GET_OpenCLTypes_DECL
2771#define GET_OpenCLTypes_IMPL
2772
2773#include "SPIRVGenTables.inc"
2774} // namespace SPIRV
2775
2776//===----------------------------------------------------------------------===//
2777// Misc functions for parsing builtin types.
2778//===----------------------------------------------------------------------===//
2779
2781 if (Name.starts_with("void"))
2782 return Type::getVoidTy(Context);
2783 else if (Name.starts_with("int") || Name.starts_with("uint"))
2784 return Type::getInt32Ty(Context);
2785 else if (Name.starts_with("float"))
2786 return Type::getFloatTy(Context);
2787 else if (Name.starts_with("half"))
2788 return Type::getHalfTy(Context);
2789 report_fatal_error("Unable to recognize type!");
2790}
2791
2792//===----------------------------------------------------------------------===//
2793// Implementation functions for builtin types.
2794//===----------------------------------------------------------------------===//
2795
2797 const SPIRV::BuiltinType *TypeRecord,
2798 MachineIRBuilder &MIRBuilder,
2799 SPIRVGlobalRegistry *GR) {
2800 unsigned Opcode = TypeRecord->Opcode;
2801 // Create or get an existing type from GlobalRegistry.
2802 return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode);
2803}
2804
2806 SPIRVGlobalRegistry *GR) {
2807 // Create or get an existing type from GlobalRegistry.
2808 return GR->getOrCreateOpTypeSampler(MIRBuilder);
2809}
2810
2811static SPIRVType *getPipeType(const TargetExtType *ExtensionType,
2812 MachineIRBuilder &MIRBuilder,
2813 SPIRVGlobalRegistry *GR) {
2814 assert(ExtensionType->getNumIntParameters() == 1 &&
2815 "Invalid number of parameters for SPIR-V pipe builtin!");
2816 // Create or get an existing type from GlobalRegistry.
2817 return GR->getOrCreateOpTypePipe(MIRBuilder,
2818 SPIRV::AccessQualifier::AccessQualifier(
2819 ExtensionType->getIntParameter(0)));
2820}
2821
2822static SPIRVType *getCoopMatrType(const TargetExtType *ExtensionType,
2823 MachineIRBuilder &MIRBuilder,
2824 SPIRVGlobalRegistry *GR) {
2825 assert(ExtensionType->getNumIntParameters() == 4 &&
2826 "Invalid number of parameters for SPIR-V coop matrices builtin!");
2827 assert(ExtensionType->getNumTypeParameters() == 1 &&
2828 "SPIR-V coop matrices builtin type must have a type parameter!");
2829 const SPIRVType *ElemType =
2830 GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
2831 // Create or get an existing type from GlobalRegistry.
2832 return GR->getOrCreateOpTypeCoopMatr(
2833 MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0),
2834 ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
2835 ExtensionType->getIntParameter(3));
2836}
2837
2838static SPIRVType *
2839getImageType(const TargetExtType *ExtensionType,
2840 const SPIRV::AccessQualifier::AccessQualifier Qualifier,
2841 MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
2842 assert(ExtensionType->getNumTypeParameters() == 1 &&
2843 "SPIR-V image builtin type must have sampled type parameter!");
2844 const SPIRVType *SampledType =
2845 GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);
2846 assert((ExtensionType->getNumIntParameters() == 7 ||
2847 ExtensionType->getNumIntParameters() == 6) &&
2848 "Invalid number of parameters for SPIR-V image builtin!");
2849
2850 SPIRV::AccessQualifier::AccessQualifier accessQualifier =
2851 SPIRV::AccessQualifier::None;
2852 if (ExtensionType->getNumIntParameters() == 7) {
2853 accessQualifier = Qualifier == SPIRV::AccessQualifier::WriteOnly
2854 ? SPIRV::AccessQualifier::WriteOnly
2855 : SPIRV::AccessQualifier::AccessQualifier(
2856 ExtensionType->getIntParameter(6));
2857 }
2858
2859 // Create or get an existing type from GlobalRegistry.
2860 return GR->getOrCreateOpTypeImage(
2861 MIRBuilder, SampledType,
2862 SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)),
2863 ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),
2864 ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4),
2865 SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)),
2866 accessQualifier);
2867}
2868
2870 MachineIRBuilder &MIRBuilder,
2871 SPIRVGlobalRegistry *GR) {
2872 SPIRVType *OpaqueImageType = getImageType(
2873 OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder, GR);
2874 // Create or get an existing type from GlobalRegistry.
2875 return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
2876}
2877
2878namespace SPIRV {
2880 LLVMContext &Context) {
2881 StringRef NameWithParameters = TypeName;
2882
2883 // Pointers-to-opaque-structs representing OpenCL types are first translated
2884 // to equivalent SPIR-V types. OpenCL builtin type names should have the
2885 // following format: e.g. %opencl.event_t
2886 if (NameWithParameters.starts_with("opencl.")) {
2887 const SPIRV::OpenCLType *OCLTypeRecord =
2888 SPIRV::lookupOpenCLType(NameWithParameters);
2889 if (!OCLTypeRecord)
2890 report_fatal_error("Missing TableGen record for OpenCL type: " +
2891 NameWithParameters);
2892 NameWithParameters = OCLTypeRecord->SpirvTypeLiteral;
2893 // Continue with the SPIR-V builtin type...
2894 }
2895
2896 // Names of the opaque structs representing a SPIR-V builtins without
2897 // parameters should have the following format: e.g. %spirv.Event
2898 assert(NameWithParameters.starts_with("spirv.") &&
2899 "Unknown builtin opaque type!");
2900
2901 // Parameterized SPIR-V builtins names follow this format:
2902 // e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._0
2903 if (!NameWithParameters.contains('_'))
2904 return TargetExtType::get(Context, NameWithParameters);
2905
2906 SmallVector<StringRef> Parameters;
2907 unsigned BaseNameLength = NameWithParameters.find('_') - 1;
2908 SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_");
2909
2910 SmallVector<Type *, 1> TypeParameters;
2911 bool HasTypeParameter = !isDigit(Parameters[0][0]);
2912 if (HasTypeParameter)
2913 TypeParameters.push_back(parseTypeString(Parameters[0], Context));
2914 SmallVector<unsigned> IntParameters;
2915 for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) {
2916 unsigned IntParameter = 0;
2917 bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter);
2918 (void)ValidLiteral;
2919 assert(ValidLiteral &&
2920 "Invalid format of SPIR-V builtin parameter literal!");
2921 IntParameters.push_back(IntParameter);
2922 }
2923 return TargetExtType::get(Context,
2924 NameWithParameters.substr(0, BaseNameLength),
2925 TypeParameters, IntParameters);
2926}
2927
2929 SPIRV::AccessQualifier::AccessQualifier AccessQual,
2930 MachineIRBuilder &MIRBuilder,
2931 SPIRVGlobalRegistry *GR) {
2932 // In LLVM IR, SPIR-V and OpenCL builtin types are represented as either
2933 // target(...) target extension types or pointers-to-opaque-structs. The
2934 // approach relying on structs is deprecated and works only in the non-opaque
2935 // pointer mode (-opaque-pointers=0).
2936 // In order to maintain compatibility with LLVM IR generated by older versions
2937 // of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are
2938 // "translated" to target extension types. This translation is temporary and
2939 // will be removed in the future release of LLVM.
2940 const TargetExtType *BuiltinType = dyn_cast<TargetExtType>(OpaqueType);
2941 if (!BuiltinType)
2943 OpaqueType->getStructName().str(), MIRBuilder.getContext());
2944
2945 unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
2946
2947 const StringRef Name = BuiltinType->getName();
2948 LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
2949
2950 // Lookup the demangled builtin type in the TableGen records.
2951 const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
2952 if (!TypeRecord)
2953 report_fatal_error("Missing TableGen record for builtin type: " + Name);
2954
2955 // "Lower" the BuiltinType into TargetType. The following get<...>Type methods
2956 // use the implementation details from TableGen records or TargetExtType
2957 // parameters to either create a new OpType<...> machine instruction or get an
2958 // existing equivalent SPIRVType from GlobalRegistry.
2959 SPIRVType *TargetType;
2960 switch (TypeRecord->Opcode) {
2961 case SPIRV::OpTypeImage:
2962 TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
2963 break;
2964 case SPIRV::OpTypePipe:
2965 TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
2966 break;
2967 case SPIRV::OpTypeDeviceEvent:
2968 TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
2969 break;
2970 case SPIRV::OpTypeSampler:
2971 TargetType = getSamplerType(MIRBuilder, GR);
2972 break;
2973 case SPIRV::OpTypeSampledImage:
2974 TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
2975 break;
2976 case SPIRV::OpTypeCooperativeMatrixKHR:
2977 TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
2978 break;
2979 default:
2980 TargetType =
2981 getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
2982 break;
2983 }
2984
2985 // Emit OpName instruction if a new OpType<...> instruction was added
2986 // (equivalent type was not found in GlobalRegistry).
2987 if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
2988 buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder);
2989
2990 return TargetType;
2991}
2992} // namespace SPIRV
2993} // namespace llvm
unsigned const MachineRegisterInfo * MRI
MachineInstrBuilder MachineInstrBuilder & DefMI
AMDGPU Lower Kernel Arguments
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
return RetTy
#define LLVM_DEBUG(...)
Definition: Debug.h:106
std::string Name
uint64_t Size
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition: MD5.cpp:58
unsigned Reg
static bool isDigit(const char C)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
spirv structurize SPIRV
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.
APInt bitcastToAPInt() const
Definition: APFloat.h:1346
static APFloat getZero(const fltSemantics &Sem, bool Negative=false)
Factory for Positive and Negative Zero.
Definition: APFloat.h:1076
static APInt getAllOnes(unsigned numBits)
Return an APInt of a specified width with all bits set.
Definition: APInt.h:234
uint64_t getZExtValue() const
Get zero extended value.
Definition: APInt.h:1520
This class represents an incoming formal argument to a Function.
Definition: Argument.h:31
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
static ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
@ ICMP_ULT
unsigned less than
Definition: InstrTypes.h:698
@ ICMP_EQ
equal
Definition: InstrTypes.h:694
@ ICMP_NE
not equal
Definition: InstrTypes.h:695
const APFloat & getValueAPF() const
Definition: Constants.h:314
const APInt & getValue() const
Return the constant as an APInt value reference.
Definition: Constants.h:148
A parsed version of the target data layout string in and methods for querying it.
Definition: DataLayout.h:63
Tagged union holding either a T or a Error.
Definition: Error.h:481
Class to represent fixed width SIMD vectors.
Definition: DerivedTypes.h:563
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition: Function.cpp:369
static IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition: Type.cpp:311
static constexpr LLT vector(ElementCount EC, unsigned ScalarSizeInBits)
Get a low-level vector of some number of elements and element width.
Definition: LowLevelType.h:64
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
Definition: LowLevelType.h:42
static constexpr LLT pointer(unsigned AddressSpace, unsigned SizeInBits)
Get a low-level pointer in the given address space.
Definition: LowLevelType.h:57
static constexpr LLT fixed_vector(unsigned NumElements, unsigned ScalarSizeInBits)
Get a low-level fixed-width vector of some number of elements and element width.
Definition: LowLevelType.h:100
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:67
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
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 buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
MachineFunction & getMF()
Getter for the function 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
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addUse(Register RegNo, unsigned Flags=0, unsigned SubReg=0) const
Add a virtual register use operand.
const MachineInstrBuilder & addDef(Register RegNo, unsigned Flags=0, unsigned SubReg=0) const
Add a virtual register definition operand.
Representation of each machine instruction.
Definition: MachineInstr.h:69
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
Definition: MachineInstr.h:575
const MachineOperand & getOperand(unsigned i) const
Definition: MachineInstr.h:585
MachineOperand class - Representation of each machine instruction operand.
const GlobalValue * getGlobal() const
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,...
Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
void setType(Register VReg, LLT Ty)
Set the low-level type of VReg to Ty.
void setRegClass(Register Reg, const TargetRegisterClass *RC)
setRegClass - Set the register class of the specified virtual register.
Register createGenericVirtualRegister(LLT Ty, StringRef Name="")
Create and return a new generic virtual register with low-level type Ty.
unsigned getNumVirtRegs() const
getNumVirtRegs - Return the number of virtual registers created.
static PointerType * get(Type *ElementType, unsigned AddressSpace)
This constructs a pointer to an object of the specified type in a numbered address space.
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
constexpr bool isValid() const
Definition: Register.h:116
SPIRVType * getOrCreateOpTypePipe(MachineIRBuilder &MIRBuilder, SPIRV::AccessQualifier::AccessQualifier AccQual)
SPIRVType * getSPIRVTypeForVReg(Register VReg, const MachineFunction *MF=nullptr) const
void assignSPIRVTypeToVReg(SPIRVType *Type, Register VReg, const MachineFunction &MF)
SPIRVType * getOrCreateSPIRVBoolType(MachineIRBuilder &MIRBuilder)
Register getOrCreateConsIntVector(uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVType *SpvType, bool EmitIR=true)
const Type * getTypeForSPIRVType(const SPIRVType *Ty) const
Register buildConstantSampler(Register Res, unsigned AddrMode, unsigned Param, unsigned FilerMode, MachineIRBuilder &MIRBuilder, SPIRVType *SpvType)
unsigned getScalarOrVectorComponentCount(Register VReg) const
SPIRVType * getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled, SPIRV::ImageFormat::ImageFormat ImageFormat, SPIRV::AccessQualifier::AccessQualifier AccQual)
SPIRVType * getOrCreateOpTypeByOpcode(const Type *Ty, MachineIRBuilder &MIRBuilder, unsigned Opcode)
Register buildConstantFP(APFloat Val, MachineIRBuilder &MIRBuilder, SPIRVType *SpvType=nullptr)
SPIRVType * getPointeeType(SPIRVType *PtrType)
Register getSPIRVTypeID(const SPIRVType *SpirvType) const
SPIRVType * getOrCreateSPIRVType(const Type *Type, MachineIRBuilder &MIRBuilder, SPIRV::AccessQualifier::AccessQualifier AQ=SPIRV::AccessQualifier::ReadWrite, bool EmitIR=true)
bool isScalarOfType(Register VReg, unsigned TypeOpcode) const
Register buildGlobalVariable(Register Reg, SPIRVType *BaseType, StringRef Name, const GlobalValue *GV, SPIRV::StorageClass::StorageClass Storage, const MachineInstr *Init, bool IsConst, bool HasLinkageTy, SPIRV::LinkageType::LinkageType LinkageType, MachineIRBuilder &MIRBuilder, bool IsInstSelector)
SPIRVType * getOrCreateOpTypeSampledImage(SPIRVType *ImageType, MachineIRBuilder &MIRBuilder)
SPIRVType * getOrCreateSPIRVTypeByName(StringRef TypeStr, MachineIRBuilder &MIRBuilder, SPIRV::StorageClass::StorageClass SC=SPIRV::StorageClass::Function, SPIRV::AccessQualifier::AccessQualifier AQ=SPIRV::AccessQualifier::ReadWrite)
const TargetRegisterClass * getRegClass(SPIRVType *SpvType) const
bool isScalarOrVectorOfType(Register VReg, unsigned TypeOpcode) const
Register getOrCreateConstIntArray(uint64_t Val, size_t Num, MachineInstr &I, SPIRVType *SpvType, const SPIRVInstrInfo &TII)
SPIRVType * getOrCreateOpTypeDeviceEvent(MachineIRBuilder &MIRBuilder)
SPIRVType * getOrCreateSPIRVPointerType(SPIRVType *BaseType, MachineIRBuilder &MIRBuilder, SPIRV::StorageClass::StorageClass SClass=SPIRV::StorageClass::Function)
SPIRVType * getOrCreateOpTypeCoopMatr(MachineIRBuilder &MIRBuilder, const TargetExtType *ExtensionType, const SPIRVType *ElemType, uint32_t Scope, uint32_t Rows, uint32_t Columns, uint32_t Use)
SPIRVType * getOrCreateSPIRVVectorType(SPIRVType *BaseType, unsigned NumElements, MachineIRBuilder &MIRBuilder)
SPIRVType * getOrCreateSPIRVIntegerType(unsigned BitWidth, MachineIRBuilder &MIRBuilder)
LLT getRegType(SPIRVType *SpvType) const
SPIRV::StorageClass::StorageClass getPointerStorageClass(Register VReg) const
SPIRVType * getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder)
Register getOrCreateConstNullPtr(MachineIRBuilder &MIRBuilder, SPIRVType *SpvType)
unsigned getScalarOrVectorBitWidth(const SPIRVType *Type) const
Register buildConstantInt(uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVType *SpvType, bool EmitIR=true, bool ZeroAsNull=true)
size_t size() const
Definition: SmallVector.h:78
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:573
void push_back(const T &Elt)
Definition: SmallVector.h:413
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition: StringRef.h:700
bool consume_back(StringRef Suffix)
Returns true if this StringRef has the given suffix and removes that suffix.
Definition: StringRef.h:655
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition: StringRef.h:470
std::string str() const
str - Get the contents as an std::string.
Definition: StringRef.h:229
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:571
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:265
bool contains_insensitive(StringRef Other) const
Return true if the given string is a substring of *this, and false otherwise.
Definition: StringRef.h:436
StringRef slice(size_t Start, size_t End) const
Return a reference to the substring from [Start, End).
Definition: StringRef.h:684
bool contains(StringRef Other) const
Return true if the given string is a substring of *this, and false otherwise.
Definition: StringRef.h:424
bool consume_front(StringRef Prefix)
Returns true if this StringRef has the given prefix and removes that prefix.
Definition: StringRef.h:635
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:377
size_t rfind(char C, size_t From=npos) const
Search for the last character C in the string.
Definition: StringRef.h:347
size_t find(char C, size_t From=0) const
Search for the first character C in the string.
Definition: StringRef.h:297
bool ends_with(StringRef Suffix) const
Check if this string ends with the given Suffix.
Definition: StringRef.h:277
static constexpr size_t npos
Definition: StringRef.h:53
A switch()-like statement whose cases are string literals.
Definition: StringSwitch.h:44
StringSwitch & EndsWith(StringLiteral S, T Value)
Definition: StringSwitch.h:76
Class to represent struct types.
Definition: DerivedTypes.h:218
static StructType * getTypeByName(LLVMContext &C, StringRef Name)
Return the type with the specified name, or null if there is none by that name.
Definition: Type.cpp:731
static StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition: Type.cpp:612
Class to represent target extensions types, which are generally unintrospectable from target-independ...
Definition: DerivedTypes.h:744
unsigned getNumIntParameters() const
Definition: DerivedTypes.h:802
static 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:895
Type * getTypeParameter(unsigned i) const
Definition: DerivedTypes.h:792
unsigned getNumTypeParameters() const
Definition: DerivedTypes.h:793
unsigned getIntParameter(unsigned i) const
Definition: DerivedTypes.h:801
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
static Type * getHalfTy(LLVMContext &C)
StringRef getStructName() const
static Type * getVoidTy(LLVMContext &C)
static IntegerType * getInt8Ty(LLVMContext &C)
static IntegerType * getInt32Ty(LLVMContext &C)
static Type * getFloatTy(LLVMContext &C)
LLVM Value Representation.
Definition: Value.h:74
Value(Type *Ty, unsigned scid)
Definition: Value.cpp:53
static VectorType * get(Type *ElementType, ElementCount EC)
This static method is the primary way to construct an VectorType.
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition: ilist_node.h:353
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:883
std::string lookupBuiltinNameHelper(StringRef DemangledCall, FPDecorationId *DecorationId)
Parses the name part of the demangled builtin call.
Type * parseBuiltinCallArgumentType(StringRef TypeStr, LLVMContext &Ctx)
bool parseBuiltinTypeStr(SmallVector< StringRef, 10 > &BuiltinArgsTypeStrs, const StringRef DemangledCall, LLVMContext &Ctx)
std::tuple< int, unsigned, unsigned > mapBuiltinToOpcode(const StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set)
Helper function for finding a builtin function attributes by a demangled function name.
Type * parseBuiltinCallArgumentBaseType(const StringRef DemangledCall, unsigned ArgIdx, LLVMContext &Ctx)
Parses the provided ArgIdx argument base type in the DemangledCall skeleton.
TargetExtType * parseBuiltinTypeNameToTargetExtType(std::string TypeName, LLVMContext &Context)
Translates a string representing a SPIR-V or OpenCL builtin type to a TargetExtType that can be furth...
std::optional< bool > lowerBuiltin(const StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set, MachineIRBuilder &MIRBuilder, const Register OrigRet, const Type *OrigRetTy, const SmallVectorImpl< Register > &Args, SPIRVGlobalRegistry *GR)
SPIRVType * lowerBuiltinType(const Type *OpaqueType, SPIRV::AccessQualifier::AccessQualifier AccessQual, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
StorageClass
Definition: XCOFF.h:170
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
void buildOpName(Register Target, const StringRef &Name, MachineIRBuilder &MIRBuilder)
Definition: SPIRVUtils.cpp:103
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 Register buildBuiltinVariableLoad(MachineIRBuilder &MIRBuilder, SPIRVType *VariableType, SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType, Register Reg=Register(0), bool isConst=true, bool hasLinkageTy=true)
Helper function for building a load instruction for loading a builtin global variable of BuiltinValue...
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.
void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB)
Definition: SPIRVUtils.cpp:83
static const Type * getBlockStructType(Register ParamReg, MachineRegisterInfo *MRI)
static bool generateGroupInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
FPDecorationId demangledPostfixToDecorationId(const std::string &S)
Definition: SPIRVUtils.h:405
static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim)
Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy, SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB, MachineRegisterInfo &MRI)
Helper external function for inserting ASSIGN_TYPE instuction between Reg and its definition,...
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:403
static std::tuple< Register, SPIRVType * > buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType, SPIRVGlobalRegistry *GR)
Helper function building either a resulting scalar or vector bool register depending on the expected ...
static unsigned getNumSizeComponents(SPIRVType *imgType)
Helper function for obtaining the number of size components.
uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI)
Definition: SPIRVUtils.cpp:326
static Register buildConstantIntReg32(uint64_t Val, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVType * getSampledImageType(const TargetExtType *OpaqueType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
SPIRV::MemorySemantics::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass::StorageClass SC)
Definition: SPIRVUtils.cpp:245
constexpr unsigned storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC)
Definition: SPIRVUtils.h:161
static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateSampleImageInst(const StringRef DemangledCall, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateBarrierInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVType * getCoopMatrType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateKernelClockInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static void setRegClassIfNull(Register Reg, MachineRegisterInfo *MRI, SPIRVGlobalRegistry *GR)
static bool generateGroupUniformInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateWaveInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVType * getImageType(const TargetExtType *ExtensionType, const SPIRV::AccessQualifier::AccessQualifier Qualifier, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
void buildOpDecorate(Register Reg, MachineIRBuilder &MIRBuilder, SPIRV::Decoration::Decoration Dec, const std::vector< uint32_t > &DecArgs, StringRef StrImm)
Definition: SPIRVUtils.cpp:130
Register createVirtualRegister(SPIRVType *SpvType, SPIRVGlobalRegistry *GR, MachineRegisterInfo *MRI, const MachineFunction &MF)
Definition: SPIRVUtils.cpp:748
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 SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope)
static SPIRVType * getSamplerType(MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR, LLT LowLevelType, Register DestinationReg=Register(0))
Helper function for building a load instruction loading into the DestinationReg.
static bool generateEnqueueInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:167
static bool buildSelectInst(MachineIRBuilder &MIRBuilder, Register ReturnRegister, Register SourceRegister, const SPIRVType *ReturnType, SPIRVGlobalRegistry *GR)
Helper function for building either a vector or scalar select instruction depending on the expected R...
static const Type * getMachineInstrType(MachineInstr *MI)
static SPIRV::SamplerAddressingMode::SamplerAddressingMode getSamplerAddressingModeFromBitmask(unsigned Bitmask)
static bool generateAtomicInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateConvertInst(const StringRef DemangledCall, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static Register buildMemSemanticsReg(Register SemanticsRegister, Register PtrRegister, unsigned &Semantics, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI)
static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateSelectInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder)
static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building an atomic load instruction.
static bool generateIntelSubgroupsInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVType * getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Type * parseBasicTypeName(StringRef &TypeName, LLVMContext &Ctx)
Definition: SPIRVUtils.cpp:459
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 std::unique_ptr< const SPIRV::IncomingCall > lookupBuiltin(StringRef DemangledCall, SPIRV::InstructionSet::InstructionSet Set, Register ReturnRegister, const SPIRVType *ReturnType, const SmallVectorImpl< Register > &Arguments)
Looks up the demangled builtin call in the SPIRVBuiltins.td records using the provided DemangledCall ...
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)
Definition: SPIRVUtils.cpp:307
constexpr unsigned BitWidth
Definition: BitmaskEnum.h:217
const MachineInstr SPIRVType
static bool generateReadImageInst(const StringRef DemangledCall, const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
bool hasBuiltinTypePrefix(StringRef Name)
Definition: SPIRVUtils.cpp:429
static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Type * getMDOperandAsType(const MDNode *N, unsigned I)
Definition: SPIRVUtils.cpp:338
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 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 SPIRVType * getPipeType(const TargetExtType *ExtensionType, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static Type * parseTypeString(const StringRef Name, LLVMContext &Context)
bool isSpvIntrinsic(const MachineInstr &MI, Intrinsic::ID IntrinsicID)
Definition: SPIRVUtils.cpp:332
static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder)
static bool generateAtomicFloatingInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool generateExtInst(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static bool buildNDRange(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static SPIRVType * getNonParameterizedType(const TargetExtType *ExtensionType, const SPIRV::BuiltinType *TypeRecord, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static MachineInstr * getBlockStructInstr(Register ParamReg, MachineRegisterInfo *MRI)
static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode, const SPIRV::IncomingCall *Call, Register TypeReg, ArrayRef< uint32_t > ImmArgs={})
static unsigned getSamplerParamFromBitmask(unsigned Bitmask)
static bool buildAtomicCompareExchangeInst(const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin, unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
Helper function for building an atomic compare-exchange instruction.
std::string getLinkStringForBuiltIn(SPIRV::BuiltIn::BuiltIn BuiltInValue)
static bool generateBuiltinVar(const SPIRV::IncomingCall *Call, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR)
static const fltSemantics & IEEEsingle() LLVM_READNONE
Definition: APFloat.cpp:265
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
This class contains a discriminated union of information about pointers in memory operands,...
FPRoundingMode::FPRoundingMode RoundingMode
InstructionSet::InstructionSet Set
InstructionSet::InstructionSet Set
InstructionSet::InstructionSet Set
BuiltIn::BuiltIn Value
InstructionSet::InstructionSet Set
const SmallVectorImpl< Register > & Arguments
const std::string BuiltinName
const SPIRVType * ReturnType
const Register ReturnRegister
const DemangledBuiltin * Builtin
IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin, const Register ReturnRegister, const SPIRVType *ReturnType, const SmallVectorImpl< Register > &Arguments)
InstructionSet::InstructionSet Set
InstructionSet::InstructionSet Set
FPRoundingMode::FPRoundingMode RoundingMode