Path: blob/main/contrib/llvm-project/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
35294 views
//===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements lowering builtin function calls and types using their9// demangled names and TableGen records.10//11//===----------------------------------------------------------------------===//1213#include "SPIRVBuiltins.h"14#include "SPIRV.h"15#include "SPIRVSubtarget.h"16#include "SPIRVUtils.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/Analysis/ValueTracking.h"19#include "llvm/IR/IntrinsicsSPIRV.h"20#include <string>21#include <tuple>2223#define DEBUG_TYPE "spirv-builtins"2425namespace llvm {26namespace SPIRV {27#define GET_BuiltinGroup_DECL28#include "SPIRVGenTables.inc"2930struct DemangledBuiltin {31StringRef Name;32InstructionSet::InstructionSet Set;33BuiltinGroup Group;34uint8_t MinNumArgs;35uint8_t MaxNumArgs;36};3738#define GET_DemangledBuiltins_DECL39#define GET_DemangledBuiltins_IMPL4041struct IncomingCall {42const std::string BuiltinName;43const DemangledBuiltin *Builtin;4445const Register ReturnRegister;46const SPIRVType *ReturnType;47const SmallVectorImpl<Register> &Arguments;4849IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin,50const Register ReturnRegister, const SPIRVType *ReturnType,51const SmallVectorImpl<Register> &Arguments)52: BuiltinName(BuiltinName), Builtin(Builtin),53ReturnRegister(ReturnRegister), ReturnType(ReturnType),54Arguments(Arguments) {}5556bool isSpirvOp() const { return BuiltinName.rfind("__spirv_", 0) == 0; }57};5859struct NativeBuiltin {60StringRef Name;61InstructionSet::InstructionSet Set;62uint32_t Opcode;63};6465#define GET_NativeBuiltins_DECL66#define GET_NativeBuiltins_IMPL6768struct GroupBuiltin {69StringRef Name;70uint32_t Opcode;71uint32_t GroupOperation;72bool IsElect;73bool IsAllOrAny;74bool IsAllEqual;75bool IsBallot;76bool IsInverseBallot;77bool IsBallotBitExtract;78bool IsBallotFindBit;79bool IsLogical;80bool NoGroupOperation;81bool HasBoolArg;82};8384#define GET_GroupBuiltins_DECL85#define GET_GroupBuiltins_IMPL8687struct IntelSubgroupsBuiltin {88StringRef Name;89uint32_t Opcode;90bool IsBlock;91bool IsWrite;92};9394#define GET_IntelSubgroupsBuiltins_DECL95#define GET_IntelSubgroupsBuiltins_IMPL9697struct AtomicFloatingBuiltin {98StringRef Name;99uint32_t Opcode;100};101102#define GET_AtomicFloatingBuiltins_DECL103#define GET_AtomicFloatingBuiltins_IMPL104struct GroupUniformBuiltin {105StringRef Name;106uint32_t Opcode;107bool IsLogical;108};109110#define GET_GroupUniformBuiltins_DECL111#define GET_GroupUniformBuiltins_IMPL112113struct GetBuiltin {114StringRef Name;115InstructionSet::InstructionSet Set;116BuiltIn::BuiltIn Value;117};118119using namespace BuiltIn;120#define GET_GetBuiltins_DECL121#define GET_GetBuiltins_IMPL122123struct ImageQueryBuiltin {124StringRef Name;125InstructionSet::InstructionSet Set;126uint32_t Component;127};128129#define GET_ImageQueryBuiltins_DECL130#define GET_ImageQueryBuiltins_IMPL131132struct ConvertBuiltin {133StringRef Name;134InstructionSet::InstructionSet Set;135bool IsDestinationSigned;136bool IsSaturated;137bool IsRounded;138bool IsBfloat16;139FPRoundingMode::FPRoundingMode RoundingMode;140};141142struct VectorLoadStoreBuiltin {143StringRef Name;144InstructionSet::InstructionSet Set;145uint32_t Number;146uint32_t ElementCount;147bool IsRounded;148FPRoundingMode::FPRoundingMode RoundingMode;149};150151using namespace FPRoundingMode;152#define GET_ConvertBuiltins_DECL153#define GET_ConvertBuiltins_IMPL154155using namespace InstructionSet;156#define GET_VectorLoadStoreBuiltins_DECL157#define GET_VectorLoadStoreBuiltins_IMPL158159#define GET_CLMemoryScope_DECL160#define GET_CLSamplerAddressingMode_DECL161#define GET_CLMemoryFenceFlags_DECL162#define GET_ExtendedBuiltins_DECL163#include "SPIRVGenTables.inc"164} // namespace SPIRV165166//===----------------------------------------------------------------------===//167// Misc functions for looking up builtins and veryfying requirements using168// TableGen records169//===----------------------------------------------------------------------===//170171namespace SPIRV {172/// Parses the name part of the demangled builtin call.173std::string lookupBuiltinNameHelper(StringRef DemangledCall) {174const static std::string PassPrefix = "(anonymous namespace)::";175std::string BuiltinName;176// Itanium Demangler result may have "(anonymous namespace)::" prefix177if (DemangledCall.starts_with(PassPrefix.c_str()))178BuiltinName = DemangledCall.substr(PassPrefix.length());179else180BuiltinName = DemangledCall;181// Extract the builtin function name and types of arguments from the call182// skeleton.183BuiltinName = BuiltinName.substr(0, BuiltinName.find('('));184185// Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR186if (BuiltinName.rfind("__spirv_ocl_", 0) == 0)187BuiltinName = BuiltinName.substr(12);188189// Check if the extracted name contains type information between angle190// brackets. If so, the builtin is an instantiated template - needs to have191// the information after angle brackets and return type removed.192if (BuiltinName.find('<') && BuiltinName.back() == '>') {193BuiltinName = BuiltinName.substr(0, BuiltinName.find('<'));194BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(' ') + 1);195}196197// Check if the extracted name begins with "__spirv_ImageSampleExplicitLod"198// contains return type information at the end "_R<type>", if so extract the199// plain builtin name without the type information.200if (StringRef(BuiltinName).contains("__spirv_ImageSampleExplicitLod") &&201StringRef(BuiltinName).contains("_R")) {202BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R"));203}204205return BuiltinName;206}207} // namespace SPIRV208209/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using210/// the provided \p DemangledCall and specified \p Set.211///212/// The lookup follows the following algorithm, returning the first successful213/// match:214/// 1. Search with the plain demangled name (expecting a 1:1 match).215/// 2. Search with the prefix before or suffix after the demangled name216/// signyfying the type of the first argument.217///218/// \returns Wrapper around the demangled call and found builtin definition.219static std::unique_ptr<const SPIRV::IncomingCall>220lookupBuiltin(StringRef DemangledCall,221SPIRV::InstructionSet::InstructionSet Set,222Register ReturnRegister, const SPIRVType *ReturnType,223const SmallVectorImpl<Register> &Arguments) {224std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall);225226SmallVector<StringRef, 10> BuiltinArgumentTypes;227StringRef BuiltinArgs =228DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));229BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false);230231// Look up the builtin in the defined set. Start with the plain demangled232// name, expecting a 1:1 match in the defined builtin set.233const SPIRV::DemangledBuiltin *Builtin;234if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set)))235return std::make_unique<SPIRV::IncomingCall>(236BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);237238// If the initial look up was unsuccessful and the demangled call takes at239// least 1 argument, add a prefix or suffix signifying the type of the first240// argument and repeat the search.241if (BuiltinArgumentTypes.size() >= 1) {242char FirstArgumentType = BuiltinArgumentTypes[0][0];243// Prefix to be added to the builtin's name for lookup.244// For example, OpenCL "abs" taking an unsigned value has a prefix "u_".245std::string Prefix;246247switch (FirstArgumentType) {248// Unsigned:249case 'u':250if (Set == SPIRV::InstructionSet::OpenCL_std)251Prefix = "u_";252else if (Set == SPIRV::InstructionSet::GLSL_std_450)253Prefix = "u";254break;255// Signed:256case 'c':257case 's':258case 'i':259case 'l':260if (Set == SPIRV::InstructionSet::OpenCL_std)261Prefix = "s_";262else if (Set == SPIRV::InstructionSet::GLSL_std_450)263Prefix = "s";264break;265// Floating-point:266case 'f':267case 'd':268case 'h':269if (Set == SPIRV::InstructionSet::OpenCL_std ||270Set == SPIRV::InstructionSet::GLSL_std_450)271Prefix = "f";272break;273}274275// If argument-type name prefix was added, look up the builtin again.276if (!Prefix.empty() &&277(Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set)))278return std::make_unique<SPIRV::IncomingCall>(279BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);280281// If lookup with a prefix failed, find a suffix to be added to the282// builtin's name for lookup. For example, OpenCL "group_reduce_max" taking283// an unsigned value has a suffix "u".284std::string Suffix;285286switch (FirstArgumentType) {287// Unsigned:288case 'u':289Suffix = "u";290break;291// Signed:292case 'c':293case 's':294case 'i':295case 'l':296Suffix = "s";297break;298// Floating-point:299case 'f':300case 'd':301case 'h':302Suffix = "f";303break;304}305306// If argument-type name suffix was added, look up the builtin again.307if (!Suffix.empty() &&308(Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set)))309return std::make_unique<SPIRV::IncomingCall>(310BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments);311}312313// No builtin with such name was found in the set.314return nullptr;315}316317static MachineInstr *getBlockStructInstr(Register ParamReg,318MachineRegisterInfo *MRI) {319// We expect the following sequence of instructions:320// %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca)321// or = G_GLOBAL_VALUE @block_literal_global322// %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0323// %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN)324MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg);325assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST &&326MI->getOperand(1).isReg());327Register BitcastReg = MI->getOperand(1).getReg();328MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg);329assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) &&330BitcastMI->getOperand(2).isReg());331Register ValueReg = BitcastMI->getOperand(2).getReg();332MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg);333return ValueMI;334}335336// Return an integer constant corresponding to the given register and337// defined in spv_track_constant.338// TODO: maybe unify with prelegalizer pass.339static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) {340MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg);341assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) &&342DefMI->getOperand(2).isReg());343MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg());344assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT &&345DefMI2->getOperand(1).isCImm());346return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue();347}348349// Return type of the instruction result from spv_assign_type intrinsic.350// TODO: maybe unify with prelegalizer pass.351static const Type *getMachineInstrType(MachineInstr *MI) {352MachineInstr *NextMI = MI->getNextNode();353if (!NextMI)354return nullptr;355if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name))356if ((NextMI = NextMI->getNextNode()) == nullptr)357return nullptr;358Register ValueReg = MI->getOperand(0).getReg();359if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) &&360!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) ||361NextMI->getOperand(1).getReg() != ValueReg)362return nullptr;363Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0);364assert(Ty && "Type is expected");365return Ty;366}367368static const Type *getBlockStructType(Register ParamReg,369MachineRegisterInfo *MRI) {370// In principle, this information should be passed to us from Clang via371// an elementtype attribute. However, said attribute requires that372// the function call be an intrinsic, which is not. Instead, we rely on being373// able to trace this to the declaration of a variable: OpenCL C specification374// section 6.12.5 should guarantee that we can do this.375MachineInstr *MI = getBlockStructInstr(ParamReg, MRI);376if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE)377return MI->getOperand(1).getGlobal()->getType();378assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) &&379"Blocks in OpenCL C must be traceable to allocation site");380return getMachineInstrType(MI);381}382383//===----------------------------------------------------------------------===//384// Helper functions for building misc instructions385//===----------------------------------------------------------------------===//386387/// Helper function building either a resulting scalar or vector bool register388/// depending on the expected \p ResultType.389///390/// \returns Tuple of the resulting register and its type.391static std::tuple<Register, SPIRVType *>392buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType,393SPIRVGlobalRegistry *GR) {394LLT Type;395SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);396397if (ResultType->getOpcode() == SPIRV::OpTypeVector) {398unsigned VectorElements = ResultType->getOperand(2).getImm();399BoolType =400GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder);401const FixedVectorType *LLVMVectorType =402cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType));403Type = LLT::vector(LLVMVectorType->getElementCount(), 1);404} else {405Type = LLT::scalar(1);406}407408Register ResultRegister =409MIRBuilder.getMRI()->createGenericVirtualRegister(Type);410MIRBuilder.getMRI()->setRegClass(ResultRegister, &SPIRV::IDRegClass);411GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF());412return std::make_tuple(ResultRegister, BoolType);413}414415/// Helper function for building either a vector or scalar select instruction416/// depending on the expected \p ResultType.417static bool buildSelectInst(MachineIRBuilder &MIRBuilder,418Register ReturnRegister, Register SourceRegister,419const SPIRVType *ReturnType,420SPIRVGlobalRegistry *GR) {421Register TrueConst, FalseConst;422423if (ReturnType->getOpcode() == SPIRV::OpTypeVector) {424unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType);425uint64_t AllOnes = APInt::getAllOnes(Bits).getZExtValue();426TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType);427FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType);428} else {429TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType);430FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType);431}432return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst,433FalseConst);434}435436/// Helper function for building a load instruction loading into the437/// \p DestinationReg.438static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister,439MachineIRBuilder &MIRBuilder,440SPIRVGlobalRegistry *GR, LLT LowLevelType,441Register DestinationReg = Register(0)) {442MachineRegisterInfo *MRI = MIRBuilder.getMRI();443if (!DestinationReg.isValid()) {444DestinationReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);445MRI->setType(DestinationReg, LLT::scalar(32));446GR->assignSPIRVTypeToVReg(BaseType, DestinationReg, MIRBuilder.getMF());447}448// TODO: consider using correct address space and alignment (p0 is canonical449// type for selection though).450MachinePointerInfo PtrInfo = MachinePointerInfo();451MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align());452return DestinationReg;453}454455/// Helper function for building a load instruction for loading a builtin global456/// variable of \p BuiltinValue value.457static Register buildBuiltinVariableLoad(458MachineIRBuilder &MIRBuilder, SPIRVType *VariableType,459SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType,460Register Reg = Register(0), bool isConst = true, bool hasLinkageTy = true) {461Register NewRegister =462MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass);463MIRBuilder.getMRI()->setType(NewRegister,464LLT::pointer(0, GR->getPointerSize()));465SPIRVType *PtrType = GR->getOrCreateSPIRVPointerType(466VariableType, MIRBuilder, SPIRV::StorageClass::Input);467GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());468469// Set up the global OpVariable with the necessary builtin decorations.470Register Variable = GR->buildGlobalVariable(471NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,472SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst,473/* HasLinkageTy */ hasLinkageTy, SPIRV::LinkageType::Import, MIRBuilder,474false);475476// Load the value from the global variable.477Register LoadedRegister =478buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg);479MIRBuilder.getMRI()->setType(LoadedRegister, LLType);480return LoadedRegister;481}482483/// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg484/// and its definition, set the new register as a destination of the definition,485/// assign SPIRVType to both registers. If SpirvTy is provided, use it as486/// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in487/// SPIRVPreLegalizer.cpp.488extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,489SPIRVGlobalRegistry *GR,490MachineIRBuilder &MIB,491MachineRegisterInfo &MRI);492493// TODO: Move to TableGen.494static SPIRV::MemorySemantics::MemorySemantics495getSPIRVMemSemantics(std::memory_order MemOrder) {496switch (MemOrder) {497case std::memory_order::memory_order_relaxed:498return SPIRV::MemorySemantics::None;499case std::memory_order::memory_order_acquire:500return SPIRV::MemorySemantics::Acquire;501case std::memory_order::memory_order_release:502return SPIRV::MemorySemantics::Release;503case std::memory_order::memory_order_acq_rel:504return SPIRV::MemorySemantics::AcquireRelease;505case std::memory_order::memory_order_seq_cst:506return SPIRV::MemorySemantics::SequentiallyConsistent;507default:508report_fatal_error("Unknown CL memory scope");509}510}511512static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) {513switch (ClScope) {514case SPIRV::CLMemoryScope::memory_scope_work_item:515return SPIRV::Scope::Invocation;516case SPIRV::CLMemoryScope::memory_scope_work_group:517return SPIRV::Scope::Workgroup;518case SPIRV::CLMemoryScope::memory_scope_device:519return SPIRV::Scope::Device;520case SPIRV::CLMemoryScope::memory_scope_all_svm_devices:521return SPIRV::Scope::CrossDevice;522case SPIRV::CLMemoryScope::memory_scope_sub_group:523return SPIRV::Scope::Subgroup;524}525report_fatal_error("Unknown CL memory scope");526}527528static Register buildConstantIntReg(uint64_t Val, MachineIRBuilder &MIRBuilder,529SPIRVGlobalRegistry *GR,530unsigned BitWidth = 32) {531SPIRVType *IntType = GR->getOrCreateSPIRVIntegerType(BitWidth, MIRBuilder);532return GR->buildConstantInt(Val, MIRBuilder, IntType);533}534535static Register buildScopeReg(Register CLScopeRegister,536SPIRV::Scope::Scope Scope,537MachineIRBuilder &MIRBuilder,538SPIRVGlobalRegistry *GR,539MachineRegisterInfo *MRI) {540if (CLScopeRegister.isValid()) {541auto CLScope =542static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI));543Scope = getSPIRVScope(CLScope);544545if (CLScope == static_cast<unsigned>(Scope)) {546MRI->setRegClass(CLScopeRegister, &SPIRV::IDRegClass);547return CLScopeRegister;548}549}550return buildConstantIntReg(Scope, MIRBuilder, GR);551}552553static Register buildMemSemanticsReg(Register SemanticsRegister,554Register PtrRegister, unsigned &Semantics,555MachineIRBuilder &MIRBuilder,556SPIRVGlobalRegistry *GR) {557if (SemanticsRegister.isValid()) {558MachineRegisterInfo *MRI = MIRBuilder.getMRI();559std::memory_order Order =560static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI));561Semantics =562getSPIRVMemSemantics(Order) |563getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));564565if (Order == Semantics) {566MRI->setRegClass(SemanticsRegister, &SPIRV::IDRegClass);567return SemanticsRegister;568}569}570return buildConstantIntReg(Semantics, MIRBuilder, GR);571}572573static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode,574const SPIRV::IncomingCall *Call,575Register TypeReg,576ArrayRef<uint32_t> ImmArgs = {}) {577MachineRegisterInfo *MRI = MIRBuilder.getMRI();578auto MIB = MIRBuilder.buildInstr(Opcode);579if (TypeReg.isValid())580MIB.addDef(Call->ReturnRegister).addUse(TypeReg);581unsigned Sz = Call->Arguments.size() - ImmArgs.size();582for (unsigned i = 0; i < Sz; ++i) {583Register ArgReg = Call->Arguments[i];584if (!MRI->getRegClassOrNull(ArgReg))585MRI->setRegClass(ArgReg, &SPIRV::IDRegClass);586MIB.addUse(ArgReg);587}588for (uint32_t ImmArg : ImmArgs)589MIB.addImm(ImmArg);590return true;591}592593/// Helper function for translating atomic init to OpStore.594static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call,595MachineIRBuilder &MIRBuilder) {596if (Call->isSpirvOp())597return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0));598599assert(Call->Arguments.size() == 2 &&600"Need 2 arguments for atomic init translation");601MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);602MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);603MIRBuilder.buildInstr(SPIRV::OpStore)604.addUse(Call->Arguments[0])605.addUse(Call->Arguments[1]);606return true;607}608609/// Helper function for building an atomic load instruction.610static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call,611MachineIRBuilder &MIRBuilder,612SPIRVGlobalRegistry *GR) {613Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);614if (Call->isSpirvOp())615return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg);616617Register PtrRegister = Call->Arguments[0];618MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass);619// TODO: if true insert call to __translate_ocl_memory_sccope before620// OpAtomicLoad and the function implementation. We can use Translator's621// output for transcoding/atomic_explicit_arguments.cl as an example.622Register ScopeRegister;623if (Call->Arguments.size() > 1) {624ScopeRegister = Call->Arguments[1];625MIRBuilder.getMRI()->setRegClass(ScopeRegister, &SPIRV::IDRegClass);626} else627ScopeRegister = buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);628629Register MemSemanticsReg;630if (Call->Arguments.size() > 2) {631// TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad.632MemSemanticsReg = Call->Arguments[2];633MIRBuilder.getMRI()->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);634} else {635int Semantics =636SPIRV::MemorySemantics::SequentiallyConsistent |637getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));638MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);639}640641MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)642.addDef(Call->ReturnRegister)643.addUse(TypeReg)644.addUse(PtrRegister)645.addUse(ScopeRegister)646.addUse(MemSemanticsReg);647return true;648}649650/// Helper function for building an atomic store instruction.651static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call,652MachineIRBuilder &MIRBuilder,653SPIRVGlobalRegistry *GR) {654if (Call->isSpirvOp())655return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call, Register(0));656657Register ScopeRegister =658buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR);659Register PtrRegister = Call->Arguments[0];660MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass);661int Semantics =662SPIRV::MemorySemantics::SequentiallyConsistent |663getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister));664Register MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR);665MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);666MIRBuilder.buildInstr(SPIRV::OpAtomicStore)667.addUse(PtrRegister)668.addUse(ScopeRegister)669.addUse(MemSemanticsReg)670.addUse(Call->Arguments[1]);671return true;672}673674/// Helper function for building an atomic compare-exchange instruction.675static bool buildAtomicCompareExchangeInst(676const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin,677unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {678if (Call->isSpirvOp())679return buildOpFromWrapper(MIRBuilder, Opcode, Call,680GR->getSPIRVTypeID(Call->ReturnType));681682bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg");683MachineRegisterInfo *MRI = MIRBuilder.getMRI();684685Register ObjectPtr = Call->Arguments[0]; // Pointer (volatile A *object.)686Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected).687Register Desired = Call->Arguments[2]; // Value (C Desired).688MRI->setRegClass(ObjectPtr, &SPIRV::IDRegClass);689MRI->setRegClass(ExpectedArg, &SPIRV::IDRegClass);690MRI->setRegClass(Desired, &SPIRV::IDRegClass);691SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired);692LLT DesiredLLT = MRI->getType(Desired);693694assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() ==695SPIRV::OpTypePointer);696unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode();697(void)ExpectedType;698assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt699: ExpectedType == SPIRV::OpTypePointer);700assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt));701702SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr);703assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected");704auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>(705SpvObjectPtrTy->getOperand(1).getImm());706auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass);707708Register MemSemEqualReg;709Register MemSemUnequalReg;710uint64_t MemSemEqual =711IsCmpxchg712? SPIRV::MemorySemantics::None713: SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;714uint64_t MemSemUnequal =715IsCmpxchg716? SPIRV::MemorySemantics::None717: SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage;718if (Call->Arguments.size() >= 4) {719assert(Call->Arguments.size() >= 5 &&720"Need 5+ args for explicit atomic cmpxchg");721auto MemOrdEq =722static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI));723auto MemOrdNeq =724static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI));725MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage;726MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage;727if (MemOrdEq == MemSemEqual)728MemSemEqualReg = Call->Arguments[3];729if (MemOrdNeq == MemSemEqual)730MemSemUnequalReg = Call->Arguments[4];731MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass);732MRI->setRegClass(Call->Arguments[4], &SPIRV::IDRegClass);733}734if (!MemSemEqualReg.isValid())735MemSemEqualReg = buildConstantIntReg(MemSemEqual, MIRBuilder, GR);736if (!MemSemUnequalReg.isValid())737MemSemUnequalReg = buildConstantIntReg(MemSemUnequal, MIRBuilder, GR);738739Register ScopeReg;740auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device;741if (Call->Arguments.size() >= 6) {742assert(Call->Arguments.size() == 6 &&743"Extra args for explicit atomic cmpxchg");744auto ClScope = static_cast<SPIRV::CLMemoryScope>(745getIConstVal(Call->Arguments[5], MRI));746Scope = getSPIRVScope(ClScope);747if (ClScope == static_cast<unsigned>(Scope))748ScopeReg = Call->Arguments[5];749MRI->setRegClass(Call->Arguments[5], &SPIRV::IDRegClass);750}751if (!ScopeReg.isValid())752ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);753754Register Expected = IsCmpxchg755? ExpectedArg756: buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder,757GR, LLT::scalar(32));758MRI->setType(Expected, DesiredLLT);759Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT)760: Call->ReturnRegister;761if (!MRI->getRegClassOrNull(Tmp))762MRI->setRegClass(Tmp, &SPIRV::IDRegClass);763GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF());764765SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);766MIRBuilder.buildInstr(Opcode)767.addDef(Tmp)768.addUse(GR->getSPIRVTypeID(IntTy))769.addUse(ObjectPtr)770.addUse(ScopeReg)771.addUse(MemSemEqualReg)772.addUse(MemSemUnequalReg)773.addUse(Desired)774.addUse(Expected);775if (!IsCmpxchg) {776MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp);777MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected);778}779return true;780}781782/// Helper function for building atomic instructions.783static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode,784MachineIRBuilder &MIRBuilder,785SPIRVGlobalRegistry *GR) {786if (Call->isSpirvOp())787return buildOpFromWrapper(MIRBuilder, Opcode, Call,788GR->getSPIRVTypeID(Call->ReturnType));789790MachineRegisterInfo *MRI = MIRBuilder.getMRI();791Register ScopeRegister =792Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register();793794assert(Call->Arguments.size() <= 4 &&795"Too many args for explicit atomic RMW");796ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup,797MIRBuilder, GR, MRI);798799Register PtrRegister = Call->Arguments[0];800unsigned Semantics = SPIRV::MemorySemantics::None;801MRI->setRegClass(PtrRegister, &SPIRV::IDRegClass);802Register MemSemanticsReg =803Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();804MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,805Semantics, MIRBuilder, GR);806MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);807Register ValueReg = Call->Arguments[1];808Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType);809// support cl_ext_float_atomics810if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) {811if (Opcode == SPIRV::OpAtomicIAdd) {812Opcode = SPIRV::OpAtomicFAddEXT;813} else if (Opcode == SPIRV::OpAtomicISub) {814// Translate OpAtomicISub applied to a floating type argument to815// OpAtomicFAddEXT with the negative value operand816Opcode = SPIRV::OpAtomicFAddEXT;817Register NegValueReg =818MRI->createGenericVirtualRegister(MRI->getType(ValueReg));819MRI->setRegClass(NegValueReg, &SPIRV::IDRegClass);820GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg,821MIRBuilder.getMF());822MIRBuilder.buildInstr(TargetOpcode::G_FNEG)823.addDef(NegValueReg)824.addUse(ValueReg);825insertAssignInstr(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder,826MIRBuilder.getMF().getRegInfo());827ValueReg = NegValueReg;828}829}830MIRBuilder.buildInstr(Opcode)831.addDef(Call->ReturnRegister)832.addUse(ValueTypeReg)833.addUse(PtrRegister)834.addUse(ScopeRegister)835.addUse(MemSemanticsReg)836.addUse(ValueReg);837return true;838}839840/// Helper function for building an atomic floating-type instruction.841static bool buildAtomicFloatingRMWInst(const SPIRV::IncomingCall *Call,842unsigned Opcode,843MachineIRBuilder &MIRBuilder,844SPIRVGlobalRegistry *GR) {845assert(Call->Arguments.size() == 4 &&846"Wrong number of atomic floating-type builtin");847848MachineRegisterInfo *MRI = MIRBuilder.getMRI();849850Register PtrReg = Call->Arguments[0];851MRI->setRegClass(PtrReg, &SPIRV::IDRegClass);852853Register ScopeReg = Call->Arguments[1];854MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);855856Register MemSemanticsReg = Call->Arguments[2];857MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);858859Register ValueReg = Call->Arguments[3];860MRI->setRegClass(ValueReg, &SPIRV::IDRegClass);861862MIRBuilder.buildInstr(Opcode)863.addDef(Call->ReturnRegister)864.addUse(GR->getSPIRVTypeID(Call->ReturnType))865.addUse(PtrReg)866.addUse(ScopeReg)867.addUse(MemSemanticsReg)868.addUse(ValueReg);869return true;870}871872/// Helper function for building atomic flag instructions (e.g.873/// OpAtomicFlagTestAndSet).874static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call,875unsigned Opcode, MachineIRBuilder &MIRBuilder,876SPIRVGlobalRegistry *GR) {877bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet;878Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);879if (Call->isSpirvOp())880return buildOpFromWrapper(MIRBuilder, Opcode, Call,881IsSet ? TypeReg : Register(0));882883MachineRegisterInfo *MRI = MIRBuilder.getMRI();884Register PtrRegister = Call->Arguments[0];885unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent;886Register MemSemanticsReg =887Call->Arguments.size() >= 2 ? Call->Arguments[1] : Register();888MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister,889Semantics, MIRBuilder, GR);890891assert((Opcode != SPIRV::OpAtomicFlagClear ||892(Semantics != SPIRV::MemorySemantics::Acquire &&893Semantics != SPIRV::MemorySemantics::AcquireRelease)) &&894"Invalid memory order argument!");895896Register ScopeRegister =897Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register();898ScopeRegister =899buildScopeReg(ScopeRegister, SPIRV::Scope::Device, MIRBuilder, GR, MRI);900901auto MIB = MIRBuilder.buildInstr(Opcode);902if (IsSet)903MIB.addDef(Call->ReturnRegister).addUse(TypeReg);904905MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg);906return true;907}908909/// Helper function for building barriers, i.e., memory/control ordering910/// operations.911static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode,912MachineIRBuilder &MIRBuilder,913SPIRVGlobalRegistry *GR) {914if (Call->isSpirvOp())915return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0));916917MachineRegisterInfo *MRI = MIRBuilder.getMRI();918unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI);919unsigned MemSemantics = SPIRV::MemorySemantics::None;920921if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE)922MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory;923924if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE)925MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory;926927if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE)928MemSemantics |= SPIRV::MemorySemantics::ImageMemory;929930if (Opcode == SPIRV::OpMemoryBarrier) {931std::memory_order MemOrder =932static_cast<std::memory_order>(getIConstVal(Call->Arguments[1], MRI));933MemSemantics = getSPIRVMemSemantics(MemOrder) | MemSemantics;934} else {935MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent;936}937938Register MemSemanticsReg;939if (MemFlags == MemSemantics) {940MemSemanticsReg = Call->Arguments[0];941MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass);942} else943MemSemanticsReg = buildConstantIntReg(MemSemantics, MIRBuilder, GR);944945Register ScopeReg;946SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup;947SPIRV::Scope::Scope MemScope = Scope;948if (Call->Arguments.size() >= 2) {949assert(950((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) ||951(Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) &&952"Extra args for explicitly scoped barrier");953Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2]954: Call->Arguments[1];955SPIRV::CLMemoryScope CLScope =956static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI));957MemScope = getSPIRVScope(CLScope);958if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) ||959(Opcode == SPIRV::OpMemoryBarrier))960Scope = MemScope;961962if (CLScope == static_cast<unsigned>(Scope)) {963ScopeReg = Call->Arguments[1];964MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);965}966}967968if (!ScopeReg.isValid())969ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR);970971auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg);972if (Opcode != SPIRV::OpMemoryBarrier)973MIB.addUse(buildConstantIntReg(MemScope, MIRBuilder, GR));974MIB.addUse(MemSemanticsReg);975return true;976}977978static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) {979switch (dim) {980case SPIRV::Dim::DIM_1D:981case SPIRV::Dim::DIM_Buffer:982return 1;983case SPIRV::Dim::DIM_2D:984case SPIRV::Dim::DIM_Cube:985case SPIRV::Dim::DIM_Rect:986return 2;987case SPIRV::Dim::DIM_3D:988return 3;989default:990report_fatal_error("Cannot get num components for given Dim");991}992}993994/// Helper function for obtaining the number of size components.995static unsigned getNumSizeComponents(SPIRVType *imgType) {996assert(imgType->getOpcode() == SPIRV::OpTypeImage);997auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm());998unsigned numComps = getNumComponentsForDim(dim);999bool arrayed = imgType->getOperand(4).getImm() == 1;1000return arrayed ? numComps + 1 : numComps;1001}10021003//===----------------------------------------------------------------------===//1004// Implementation functions for each builtin group1005//===----------------------------------------------------------------------===//10061007static bool generateExtInst(const SPIRV::IncomingCall *Call,1008MachineIRBuilder &MIRBuilder,1009SPIRVGlobalRegistry *GR) {1010// Lookup the extended instruction number in the TableGen records.1011const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1012uint32_t Number =1013SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number;10141015// Build extended instruction.1016auto MIB =1017MIRBuilder.buildInstr(SPIRV::OpExtInst)1018.addDef(Call->ReturnRegister)1019.addUse(GR->getSPIRVTypeID(Call->ReturnType))1020.addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))1021.addImm(Number);10221023for (auto Argument : Call->Arguments)1024MIB.addUse(Argument);1025return true;1026}10271028static bool generateRelationalInst(const SPIRV::IncomingCall *Call,1029MachineIRBuilder &MIRBuilder,1030SPIRVGlobalRegistry *GR) {1031// Lookup the instruction opcode in the TableGen records.1032const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1033unsigned Opcode =1034SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;10351036Register CompareRegister;1037SPIRVType *RelationType;1038std::tie(CompareRegister, RelationType) =1039buildBoolRegister(MIRBuilder, Call->ReturnType, GR);10401041// Build relational instruction.1042auto MIB = MIRBuilder.buildInstr(Opcode)1043.addDef(CompareRegister)1044.addUse(GR->getSPIRVTypeID(RelationType));10451046for (auto Argument : Call->Arguments)1047MIB.addUse(Argument);10481049// Build select instruction.1050return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister,1051Call->ReturnType, GR);1052}10531054static bool generateGroupInst(const SPIRV::IncomingCall *Call,1055MachineIRBuilder &MIRBuilder,1056SPIRVGlobalRegistry *GR) {1057const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1058const SPIRV::GroupBuiltin *GroupBuiltin =1059SPIRV::lookupGroupBuiltin(Builtin->Name);10601061MachineRegisterInfo *MRI = MIRBuilder.getMRI();1062if (Call->isSpirvOp()) {1063if (GroupBuiltin->NoGroupOperation)1064return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call,1065GR->getSPIRVTypeID(Call->ReturnType));10661067// Group Operation is a literal1068Register GroupOpReg = Call->Arguments[1];1069const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI);1070if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT)1071report_fatal_error(1072"Group Operation parameter must be an integer constant");1073uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue();1074Register ScopeReg = Call->Arguments[0];1075if (!MRI->getRegClassOrNull(ScopeReg))1076MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);1077auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)1078.addDef(Call->ReturnRegister)1079.addUse(GR->getSPIRVTypeID(Call->ReturnType))1080.addUse(ScopeReg)1081.addImm(GrpOp);1082for (unsigned i = 2; i < Call->Arguments.size(); ++i) {1083Register ArgReg = Call->Arguments[i];1084if (!MRI->getRegClassOrNull(ArgReg))1085MRI->setRegClass(ArgReg, &SPIRV::IDRegClass);1086MIB.addUse(ArgReg);1087}1088return true;1089}10901091Register Arg0;1092if (GroupBuiltin->HasBoolArg) {1093Register ConstRegister = Call->Arguments[0];1094auto ArgInstruction = getDefInstrMaybeConstant(ConstRegister, MRI);1095(void)ArgInstruction;1096// TODO: support non-constant bool values.1097assert(ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT &&1098"Only constant bool value args are supported");1099if (GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode() !=1100SPIRV::OpTypeBool)1101Arg0 = GR->buildConstantInt(getIConstVal(ConstRegister, MRI), MIRBuilder,1102GR->getOrCreateSPIRVBoolType(MIRBuilder));1103}11041105Register GroupResultRegister = Call->ReturnRegister;1106SPIRVType *GroupResultType = Call->ReturnType;11071108// TODO: maybe we need to check whether the result type is already boolean1109// and in this case do not insert select instruction.1110const bool HasBoolReturnTy =1111GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny ||1112GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical ||1113GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract;11141115if (HasBoolReturnTy)1116std::tie(GroupResultRegister, GroupResultType) =1117buildBoolRegister(MIRBuilder, Call->ReturnType, GR);11181119auto Scope = Builtin->Name.starts_with("sub_group") ? SPIRV::Scope::Subgroup1120: SPIRV::Scope::Workgroup;1121Register ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR);11221123// Build work/sub group instruction.1124auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode)1125.addDef(GroupResultRegister)1126.addUse(GR->getSPIRVTypeID(GroupResultType))1127.addUse(ScopeRegister);11281129if (!GroupBuiltin->NoGroupOperation)1130MIB.addImm(GroupBuiltin->GroupOperation);1131if (Call->Arguments.size() > 0) {1132MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]);1133MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);1134for (unsigned i = 1; i < Call->Arguments.size(); i++) {1135MIB.addUse(Call->Arguments[i]);1136MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass);1137}1138}11391140// Build select instruction.1141if (HasBoolReturnTy)1142buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister,1143Call->ReturnType, GR);1144return true;1145}11461147static bool generateIntelSubgroupsInst(const SPIRV::IncomingCall *Call,1148MachineIRBuilder &MIRBuilder,1149SPIRVGlobalRegistry *GR) {1150const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1151MachineFunction &MF = MIRBuilder.getMF();1152const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());1153if (!ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {1154std::string DiagMsg = std::string(Builtin->Name) +1155": the builtin requires the following SPIR-V "1156"extension: SPV_INTEL_subgroups";1157report_fatal_error(DiagMsg.c_str(), false);1158}1159const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups =1160SPIRV::lookupIntelSubgroupsBuiltin(Builtin->Name);11611162uint32_t OpCode = IntelSubgroups->Opcode;1163if (Call->isSpirvOp()) {1164bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL &&1165OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL;1166return buildOpFromWrapper(MIRBuilder, OpCode, Call,1167IsSet ? GR->getSPIRVTypeID(Call->ReturnType)1168: Register(0));1169}11701171MachineRegisterInfo *MRI = MIRBuilder.getMRI();1172if (IntelSubgroups->IsBlock) {1173// Minimal number or arguments set in TableGen records is 11174if (SPIRVType *Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) {1175if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) {1176// TODO: add required validation from the specification:1177// "'Image' must be an object whose type is OpTypeImage with a 'Sampled'1178// operand of 0 or 2. If the 'Sampled' operand is 2, then some1179// dimensions require a capability."1180switch (OpCode) {1181case SPIRV::OpSubgroupBlockReadINTEL:1182OpCode = SPIRV::OpSubgroupImageBlockReadINTEL;1183break;1184case SPIRV::OpSubgroupBlockWriteINTEL:1185OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL;1186break;1187}1188}1189}1190}11911192// TODO: opaque pointers types should be eventually resolved in such a way1193// that validation of block read is enabled with respect to the following1194// specification requirement:1195// "'Result Type' may be a scalar or vector type, and its component type must1196// be equal to the type pointed to by 'Ptr'."1197// For example, function parameter type should not be default i8 pointer, but1198// depend on the result type of the instruction where it is used as a pointer1199// argument of OpSubgroupBlockReadINTEL12001201// Build Intel subgroups instruction1202MachineInstrBuilder MIB =1203IntelSubgroups->IsWrite1204? MIRBuilder.buildInstr(OpCode)1205: MIRBuilder.buildInstr(OpCode)1206.addDef(Call->ReturnRegister)1207.addUse(GR->getSPIRVTypeID(Call->ReturnType));1208for (size_t i = 0; i < Call->Arguments.size(); ++i) {1209MIB.addUse(Call->Arguments[i]);1210MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass);1211}12121213return true;1214}12151216static bool generateGroupUniformInst(const SPIRV::IncomingCall *Call,1217MachineIRBuilder &MIRBuilder,1218SPIRVGlobalRegistry *GR) {1219const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1220MachineFunction &MF = MIRBuilder.getMF();1221const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());1222if (!ST->canUseExtension(1223SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {1224std::string DiagMsg = std::string(Builtin->Name) +1225": the builtin requires the following SPIR-V "1226"extension: SPV_KHR_uniform_group_instructions";1227report_fatal_error(DiagMsg.c_str(), false);1228}1229const SPIRV::GroupUniformBuiltin *GroupUniform =1230SPIRV::lookupGroupUniformBuiltin(Builtin->Name);1231MachineRegisterInfo *MRI = MIRBuilder.getMRI();12321233Register GroupResultReg = Call->ReturnRegister;1234MRI->setRegClass(GroupResultReg, &SPIRV::IDRegClass);12351236// Scope1237Register ScopeReg = Call->Arguments[0];1238MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass);12391240// Group Operation1241Register ConstGroupOpReg = Call->Arguments[1];1242const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI);1243if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT)1244report_fatal_error(1245"expect a constant group operation for a uniform group instruction",1246false);1247const MachineOperand &ConstOperand = Const->getOperand(1);1248if (!ConstOperand.isCImm())1249report_fatal_error("uniform group instructions: group operation must be an "1250"integer constant",1251false);12521253// Value1254Register ValueReg = Call->Arguments[2];1255MRI->setRegClass(ValueReg, &SPIRV::IDRegClass);12561257auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode)1258.addDef(GroupResultReg)1259.addUse(GR->getSPIRVTypeID(Call->ReturnType))1260.addUse(ScopeReg);1261addNumImm(ConstOperand.getCImm()->getValue(), MIB);1262MIB.addUse(ValueReg);12631264return true;1265}12661267static bool generateKernelClockInst(const SPIRV::IncomingCall *Call,1268MachineIRBuilder &MIRBuilder,1269SPIRVGlobalRegistry *GR) {1270const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1271MachineFunction &MF = MIRBuilder.getMF();1272const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget());1273if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) {1274std::string DiagMsg = std::string(Builtin->Name) +1275": the builtin requires the following SPIR-V "1276"extension: SPV_KHR_shader_clock";1277report_fatal_error(DiagMsg.c_str(), false);1278}12791280MachineRegisterInfo *MRI = MIRBuilder.getMRI();1281Register ResultReg = Call->ReturnRegister;1282MRI->setRegClass(ResultReg, &SPIRV::IDRegClass);12831284// Deduce the `Scope` operand from the builtin function name.1285SPIRV::Scope::Scope ScopeArg =1286StringSwitch<SPIRV::Scope::Scope>(Builtin->Name)1287.EndsWith("device", SPIRV::Scope::Scope::Device)1288.EndsWith("work_group", SPIRV::Scope::Scope::Workgroup)1289.EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup);1290Register ScopeReg = buildConstantIntReg(ScopeArg, MIRBuilder, GR);12911292MIRBuilder.buildInstr(SPIRV::OpReadClockKHR)1293.addDef(ResultReg)1294.addUse(GR->getSPIRVTypeID(Call->ReturnType))1295.addUse(ScopeReg);12961297return true;1298}12991300// These queries ask for a single size_t result for a given dimension index, e.g1301// size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to1302// these values are all vec3 types, so we need to extract the correct index or1303// return defaultVal (0 or 1 depending on the query). We also handle extending1304// or tuncating in case size_t does not match the expected result type's1305// bitwidth.1306//1307// For a constant index >= 3 we generate:1308// %res = OpConstant %SizeT 01309//1310// For other indices we generate:1311// %g = OpVariable %ptr_V3_SizeT Input1312// OpDecorate %g BuiltIn XXX1313// OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX"1314// OpDecorate %g Constant1315// %loadedVec = OpLoad %V3_SizeT %g1316//1317// Then, if the index is constant < 3, we generate:1318// %res = OpCompositeExtract %SizeT %loadedVec idx1319// If the index is dynamic, we generate:1320// %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx1321// %cmp = OpULessThan %bool %idx %const_31322// %res = OpSelect %SizeT %cmp %tmp %const_01323//1324// If the bitwidth of %res does not match the expected return type, we add an1325// extend or truncate.1326static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call,1327MachineIRBuilder &MIRBuilder,1328SPIRVGlobalRegistry *GR,1329SPIRV::BuiltIn::BuiltIn BuiltinValue,1330uint64_t DefaultValue) {1331Register IndexRegister = Call->Arguments[0];1332const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm();1333const unsigned PointerSize = GR->getPointerSize();1334const SPIRVType *PointerSizeType =1335GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder);1336MachineRegisterInfo *MRI = MIRBuilder.getMRI();1337auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI);13381339// Set up the final register to do truncation or extension on at the end.1340Register ToTruncate = Call->ReturnRegister;13411342// If the index is constant, we can statically determine if it is in range.1343bool IsConstantIndex =1344IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT;13451346// If it's out of range (max dimension is 3), we can just return the constant1347// default value (0 or 1 depending on which query function).1348if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) {1349Register DefaultReg = Call->ReturnRegister;1350if (PointerSize != ResultWidth) {1351DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));1352MRI->setRegClass(DefaultReg, &SPIRV::IDRegClass);1353GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg,1354MIRBuilder.getMF());1355ToTruncate = DefaultReg;1356}1357auto NewRegister =1358GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);1359MIRBuilder.buildCopy(DefaultReg, NewRegister);1360} else { // If it could be in range, we need to load from the given builtin.1361auto Vec3Ty =1362GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder);1363Register LoadedVector =1364buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue,1365LLT::fixed_vector(3, PointerSize));1366// Set up the vreg to extract the result to (possibly a new temporary one).1367Register Extracted = Call->ReturnRegister;1368if (!IsConstantIndex || PointerSize != ResultWidth) {1369Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));1370MRI->setRegClass(Extracted, &SPIRV::IDRegClass);1371GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF());1372}1373// Use Intrinsic::spv_extractelt so dynamic vs static extraction is1374// handled later: extr = spv_extractelt LoadedVector, IndexRegister.1375MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic(1376Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false);1377ExtractInst.addUse(LoadedVector).addUse(IndexRegister);13781379// If the index is dynamic, need check if it's < 3, and then use a select.1380if (!IsConstantIndex) {1381insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder,1382*MRI);13831384auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister);1385auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder);13861387Register CompareRegister =1388MRI->createGenericVirtualRegister(LLT::scalar(1));1389MRI->setRegClass(CompareRegister, &SPIRV::IDRegClass);1390GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF());13911392// Use G_ICMP to check if idxVReg < 3.1393MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister,1394GR->buildConstantInt(3, MIRBuilder, IndexType));13951396// Get constant for the default value (0 or 1 depending on which1397// function).1398Register DefaultRegister =1399GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType);14001401// Get a register for the selection result (possibly a new temporary one).1402Register SelectionResult = Call->ReturnRegister;1403if (PointerSize != ResultWidth) {1404SelectionResult =1405MRI->createGenericVirtualRegister(LLT::scalar(PointerSize));1406MRI->setRegClass(SelectionResult, &SPIRV::IDRegClass);1407GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult,1408MIRBuilder.getMF());1409}1410// Create the final G_SELECT to return the extracted value or the default.1411MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted,1412DefaultRegister);1413ToTruncate = SelectionResult;1414} else {1415ToTruncate = Extracted;1416}1417}1418// Alter the result's bitwidth if it does not match the SizeT value extracted.1419if (PointerSize != ResultWidth)1420MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate);1421return true;1422}14231424static bool generateBuiltinVar(const SPIRV::IncomingCall *Call,1425MachineIRBuilder &MIRBuilder,1426SPIRVGlobalRegistry *GR) {1427// Lookup the builtin variable record.1428const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1429SPIRV::BuiltIn::BuiltIn Value =1430SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;14311432if (Value == SPIRV::BuiltIn::GlobalInvocationId)1433return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0);14341435// Build a load instruction for the builtin variable.1436unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType);1437LLT LLType;1438if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector)1439LLType =1440LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth);1441else1442LLType = LLT::scalar(BitWidth);14431444return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value,1445LLType, Call->ReturnRegister);1446}14471448static bool generateAtomicInst(const SPIRV::IncomingCall *Call,1449MachineIRBuilder &MIRBuilder,1450SPIRVGlobalRegistry *GR) {1451// Lookup the instruction opcode in the TableGen records.1452const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1453unsigned Opcode =1454SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;14551456switch (Opcode) {1457case SPIRV::OpStore:1458return buildAtomicInitInst(Call, MIRBuilder);1459case SPIRV::OpAtomicLoad:1460return buildAtomicLoadInst(Call, MIRBuilder, GR);1461case SPIRV::OpAtomicStore:1462return buildAtomicStoreInst(Call, MIRBuilder, GR);1463case SPIRV::OpAtomicCompareExchange:1464case SPIRV::OpAtomicCompareExchangeWeak:1465return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder,1466GR);1467case SPIRV::OpAtomicIAdd:1468case SPIRV::OpAtomicISub:1469case SPIRV::OpAtomicOr:1470case SPIRV::OpAtomicXor:1471case SPIRV::OpAtomicAnd:1472case SPIRV::OpAtomicExchange:1473return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR);1474case SPIRV::OpMemoryBarrier:1475return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR);1476case SPIRV::OpAtomicFlagTestAndSet:1477case SPIRV::OpAtomicFlagClear:1478return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR);1479default:1480if (Call->isSpirvOp())1481return buildOpFromWrapper(MIRBuilder, Opcode, Call,1482GR->getSPIRVTypeID(Call->ReturnType));1483return false;1484}1485}14861487static bool generateAtomicFloatingInst(const SPIRV::IncomingCall *Call,1488MachineIRBuilder &MIRBuilder,1489SPIRVGlobalRegistry *GR) {1490// Lookup the instruction opcode in the TableGen records.1491const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1492unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->Name)->Opcode;14931494switch (Opcode) {1495case SPIRV::OpAtomicFAddEXT:1496case SPIRV::OpAtomicFMinEXT:1497case SPIRV::OpAtomicFMaxEXT:1498return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR);1499default:1500return false;1501}1502}15031504static bool generateBarrierInst(const SPIRV::IncomingCall *Call,1505MachineIRBuilder &MIRBuilder,1506SPIRVGlobalRegistry *GR) {1507// Lookup the instruction opcode in the TableGen records.1508const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1509unsigned Opcode =1510SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;15111512return buildBarrierInst(Call, Opcode, MIRBuilder, GR);1513}15141515static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call,1516MachineIRBuilder &MIRBuilder) {1517MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST)1518.addDef(Call->ReturnRegister)1519.addUse(Call->Arguments[0]);1520return true;1521}15221523static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call,1524MachineIRBuilder &MIRBuilder,1525SPIRVGlobalRegistry *GR) {1526if (Call->isSpirvOp())1527return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call,1528GR->getSPIRVTypeID(Call->ReturnType));1529unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode();1530bool IsVec = Opcode == SPIRV::OpTypeVector;1531// Use OpDot only in case of vector args and OpFMul in case of scalar args.1532MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS)1533.addDef(Call->ReturnRegister)1534.addUse(GR->getSPIRVTypeID(Call->ReturnType))1535.addUse(Call->Arguments[0])1536.addUse(Call->Arguments[1]);1537return true;1538}15391540static bool generateWaveInst(const SPIRV::IncomingCall *Call,1541MachineIRBuilder &MIRBuilder,1542SPIRVGlobalRegistry *GR) {1543const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1544SPIRV::BuiltIn::BuiltIn Value =1545SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value;15461547// For now, we only support a single Wave intrinsic with a single return type.1548assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt);1549LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType));15501551return buildBuiltinVariableLoad(1552MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister,1553/* isConst= */ false, /* hasLinkageTy= */ false);1554}15551556static bool generateGetQueryInst(const SPIRV::IncomingCall *Call,1557MachineIRBuilder &MIRBuilder,1558SPIRVGlobalRegistry *GR) {1559// Lookup the builtin record.1560SPIRV::BuiltIn::BuiltIn Value =1561SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value;1562uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize ||1563Value == SPIRV::BuiltIn::WorkgroupSize ||1564Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize);1565return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0);1566}15671568static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call,1569MachineIRBuilder &MIRBuilder,1570SPIRVGlobalRegistry *GR) {1571// Lookup the image size query component number in the TableGen records.1572const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1573uint32_t Component =1574SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component;1575// Query result may either be a vector or a scalar. If return type is not a1576// vector, expect only a single size component. Otherwise get the number of1577// expected components.1578SPIRVType *RetTy = Call->ReturnType;1579unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector1580? RetTy->getOperand(2).getImm()1581: 1;1582// Get the actual number of query result/size components.1583SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);1584unsigned NumActualRetComponents = getNumSizeComponents(ImgType);1585Register QueryResult = Call->ReturnRegister;1586SPIRVType *QueryResultType = Call->ReturnType;1587if (NumExpectedRetComponents != NumActualRetComponents) {1588QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister(1589LLT::fixed_vector(NumActualRetComponents, 32));1590MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::IDRegClass);1591SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);1592QueryResultType = GR->getOrCreateSPIRVVectorType(1593IntTy, NumActualRetComponents, MIRBuilder);1594GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF());1595}1596bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer;1597unsigned Opcode =1598IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod;1599MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);1600auto MIB = MIRBuilder.buildInstr(Opcode)1601.addDef(QueryResult)1602.addUse(GR->getSPIRVTypeID(QueryResultType))1603.addUse(Call->Arguments[0]);1604if (!IsDimBuf)1605MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Lod id.1606if (NumExpectedRetComponents == NumActualRetComponents)1607return true;1608if (NumExpectedRetComponents == 1) {1609// Only 1 component is expected, build OpCompositeExtract instruction.1610unsigned ExtractedComposite =1611Component == 3 ? NumActualRetComponents - 1 : Component;1612assert(ExtractedComposite < NumActualRetComponents &&1613"Invalid composite index!");1614Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);1615SPIRVType *NewType = nullptr;1616if (QueryResultType->getOpcode() == SPIRV::OpTypeVector) {1617Register NewTypeReg = QueryResultType->getOperand(1).getReg();1618if (TypeReg != NewTypeReg &&1619(NewType = GR->getSPIRVTypeForVReg(NewTypeReg)) != nullptr)1620TypeReg = NewTypeReg;1621}1622MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)1623.addDef(Call->ReturnRegister)1624.addUse(TypeReg)1625.addUse(QueryResult)1626.addImm(ExtractedComposite);1627if (NewType != nullptr)1628insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,1629MIRBuilder.getMF().getRegInfo());1630} else {1631// More than 1 component is expected, fill a new vector.1632auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle)1633.addDef(Call->ReturnRegister)1634.addUse(GR->getSPIRVTypeID(Call->ReturnType))1635.addUse(QueryResult)1636.addUse(QueryResult);1637for (unsigned i = 0; i < NumExpectedRetComponents; ++i)1638MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff);1639}1640return true;1641}16421643static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call,1644MachineIRBuilder &MIRBuilder,1645SPIRVGlobalRegistry *GR) {1646assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt &&1647"Image samples query result must be of int type!");16481649// Lookup the instruction opcode in the TableGen records.1650const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1651unsigned Opcode =1652SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;16531654Register Image = Call->Arguments[0];1655MIRBuilder.getMRI()->setRegClass(Image, &SPIRV::IDRegClass);1656SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>(1657GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm());1658(void)ImageDimensionality;16591660switch (Opcode) {1661case SPIRV::OpImageQuerySamples:1662assert(ImageDimensionality == SPIRV::Dim::DIM_2D &&1663"Image must be of 2D dimensionality");1664break;1665case SPIRV::OpImageQueryLevels:1666assert((ImageDimensionality == SPIRV::Dim::DIM_1D ||1667ImageDimensionality == SPIRV::Dim::DIM_2D ||1668ImageDimensionality == SPIRV::Dim::DIM_3D ||1669ImageDimensionality == SPIRV::Dim::DIM_Cube) &&1670"Image must be of 1D/2D/3D/Cube dimensionality");1671break;1672}16731674MIRBuilder.buildInstr(Opcode)1675.addDef(Call->ReturnRegister)1676.addUse(GR->getSPIRVTypeID(Call->ReturnType))1677.addUse(Image);1678return true;1679}16801681// TODO: Move to TableGen.1682static SPIRV::SamplerAddressingMode::SamplerAddressingMode1683getSamplerAddressingModeFromBitmask(unsigned Bitmask) {1684switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) {1685case SPIRV::CLK_ADDRESS_CLAMP:1686return SPIRV::SamplerAddressingMode::Clamp;1687case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE:1688return SPIRV::SamplerAddressingMode::ClampToEdge;1689case SPIRV::CLK_ADDRESS_REPEAT:1690return SPIRV::SamplerAddressingMode::Repeat;1691case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT:1692return SPIRV::SamplerAddressingMode::RepeatMirrored;1693case SPIRV::CLK_ADDRESS_NONE:1694return SPIRV::SamplerAddressingMode::None;1695default:1696report_fatal_error("Unknown CL address mode");1697}1698}16991700static unsigned getSamplerParamFromBitmask(unsigned Bitmask) {1701return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0;1702}17031704static SPIRV::SamplerFilterMode::SamplerFilterMode1705getSamplerFilterModeFromBitmask(unsigned Bitmask) {1706if (Bitmask & SPIRV::CLK_FILTER_LINEAR)1707return SPIRV::SamplerFilterMode::Linear;1708if (Bitmask & SPIRV::CLK_FILTER_NEAREST)1709return SPIRV::SamplerFilterMode::Nearest;1710return SPIRV::SamplerFilterMode::Nearest;1711}17121713static bool generateReadImageInst(const StringRef DemangledCall,1714const SPIRV::IncomingCall *Call,1715MachineIRBuilder &MIRBuilder,1716SPIRVGlobalRegistry *GR) {1717Register Image = Call->Arguments[0];1718MachineRegisterInfo *MRI = MIRBuilder.getMRI();1719MRI->setRegClass(Image, &SPIRV::IDRegClass);1720MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);1721bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler");1722bool HasMsaa = DemangledCall.contains_insensitive("msaa");1723if (HasOclSampler || HasMsaa)1724MRI->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);1725if (HasOclSampler) {1726Register Sampler = Call->Arguments[1];17271728if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) &&1729getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) {1730uint64_t SamplerMask = getIConstVal(Sampler, MRI);1731Sampler = GR->buildConstantSampler(1732Register(), getSamplerAddressingModeFromBitmask(SamplerMask),1733getSamplerParamFromBitmask(SamplerMask),1734getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder,1735GR->getSPIRVTypeForVReg(Sampler));1736}1737SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);1738SPIRVType *SampledImageType =1739GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);1740Register SampledImage = MRI->createVirtualRegister(&SPIRV::IDRegClass);17411742MIRBuilder.buildInstr(SPIRV::OpSampledImage)1743.addDef(SampledImage)1744.addUse(GR->getSPIRVTypeID(SampledImageType))1745.addUse(Image)1746.addUse(Sampler);17471748Register Lod = GR->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()),1749MIRBuilder);1750SPIRVType *TempType = Call->ReturnType;1751bool NeedsExtraction = false;1752if (TempType->getOpcode() != SPIRV::OpTypeVector) {1753TempType =1754GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder);1755NeedsExtraction = true;1756}1757LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(TempType));1758Register TempRegister = MRI->createGenericVirtualRegister(LLType);1759MRI->setRegClass(TempRegister, &SPIRV::IDRegClass);1760GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF());17611762MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)1763.addDef(NeedsExtraction ? TempRegister : Call->ReturnRegister)1764.addUse(GR->getSPIRVTypeID(TempType))1765.addUse(SampledImage)1766.addUse(Call->Arguments[2]) // Coordinate.1767.addImm(SPIRV::ImageOperand::Lod)1768.addUse(Lod);17691770if (NeedsExtraction)1771MIRBuilder.buildInstr(SPIRV::OpCompositeExtract)1772.addDef(Call->ReturnRegister)1773.addUse(GR->getSPIRVTypeID(Call->ReturnType))1774.addUse(TempRegister)1775.addImm(0);1776} else if (HasMsaa) {1777MIRBuilder.buildInstr(SPIRV::OpImageRead)1778.addDef(Call->ReturnRegister)1779.addUse(GR->getSPIRVTypeID(Call->ReturnType))1780.addUse(Image)1781.addUse(Call->Arguments[1]) // Coordinate.1782.addImm(SPIRV::ImageOperand::Sample)1783.addUse(Call->Arguments[2]);1784} else {1785MIRBuilder.buildInstr(SPIRV::OpImageRead)1786.addDef(Call->ReturnRegister)1787.addUse(GR->getSPIRVTypeID(Call->ReturnType))1788.addUse(Image)1789.addUse(Call->Arguments[1]); // Coordinate.1790}1791return true;1792}17931794static bool generateWriteImageInst(const SPIRV::IncomingCall *Call,1795MachineIRBuilder &MIRBuilder,1796SPIRVGlobalRegistry *GR) {1797MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);1798MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);1799MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);1800MIRBuilder.buildInstr(SPIRV::OpImageWrite)1801.addUse(Call->Arguments[0]) // Image.1802.addUse(Call->Arguments[1]) // Coordinate.1803.addUse(Call->Arguments[2]); // Texel.1804return true;1805}18061807static bool generateSampleImageInst(const StringRef DemangledCall,1808const SPIRV::IncomingCall *Call,1809MachineIRBuilder &MIRBuilder,1810SPIRVGlobalRegistry *GR) {1811MachineRegisterInfo *MRI = MIRBuilder.getMRI();1812if (Call->Builtin->Name.contains_insensitive(1813"__translate_sampler_initializer")) {1814// Build sampler literal.1815uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI);1816Register Sampler = GR->buildConstantSampler(1817Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask),1818getSamplerParamFromBitmask(Bitmask),1819getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType);1820return Sampler.isValid();1821} else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) {1822// Create OpSampledImage.1823Register Image = Call->Arguments[0];1824SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image);1825SPIRVType *SampledImageType =1826GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder);1827Register SampledImage =1828Call->ReturnRegister.isValid()1829? Call->ReturnRegister1830: MRI->createVirtualRegister(&SPIRV::IDRegClass);1831MIRBuilder.buildInstr(SPIRV::OpSampledImage)1832.addDef(SampledImage)1833.addUse(GR->getSPIRVTypeID(SampledImageType))1834.addUse(Image)1835.addUse(Call->Arguments[1]); // Sampler.1836return true;1837} else if (Call->Builtin->Name.contains_insensitive(1838"__spirv_ImageSampleExplicitLod")) {1839// Sample an image using an explicit level of detail.1840std::string ReturnType = DemangledCall.str();1841if (DemangledCall.contains("_R")) {1842ReturnType = ReturnType.substr(ReturnType.find("_R") + 2);1843ReturnType = ReturnType.substr(0, ReturnType.find('('));1844}1845SPIRVType *Type =1846Call->ReturnType1847? Call->ReturnType1848: GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder);1849if (!Type) {1850std::string DiagMsg =1851"Unable to recognize SPIRV type name: " + ReturnType;1852report_fatal_error(DiagMsg.c_str());1853}1854MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);1855MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);1856MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass);18571858MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod)1859.addDef(Call->ReturnRegister)1860.addUse(GR->getSPIRVTypeID(Type))1861.addUse(Call->Arguments[0]) // Image.1862.addUse(Call->Arguments[1]) // Coordinate.1863.addImm(SPIRV::ImageOperand::Lod)1864.addUse(Call->Arguments[3]);1865return true;1866}1867return false;1868}18691870static bool generateSelectInst(const SPIRV::IncomingCall *Call,1871MachineIRBuilder &MIRBuilder) {1872MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0],1873Call->Arguments[1], Call->Arguments[2]);1874return true;1875}18761877static bool generateConstructInst(const SPIRV::IncomingCall *Call,1878MachineIRBuilder &MIRBuilder,1879SPIRVGlobalRegistry *GR) {1880return buildOpFromWrapper(MIRBuilder, SPIRV::OpCompositeConstruct, Call,1881GR->getSPIRVTypeID(Call->ReturnType));1882}18831884static bool generateCoopMatrInst(const SPIRV::IncomingCall *Call,1885MachineIRBuilder &MIRBuilder,1886SPIRVGlobalRegistry *GR) {1887const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1888unsigned Opcode =1889SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;1890bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR;1891unsigned ArgSz = Call->Arguments.size();1892unsigned LiteralIdx = 0;1893if (Opcode == SPIRV::OpCooperativeMatrixLoadKHR && ArgSz > 3)1894LiteralIdx = 3;1895else if (Opcode == SPIRV::OpCooperativeMatrixStoreKHR && ArgSz > 4)1896LiteralIdx = 4;1897SmallVector<uint32_t, 1> ImmArgs;1898MachineRegisterInfo *MRI = MIRBuilder.getMRI();1899if (LiteralIdx > 0)1900ImmArgs.push_back(getConstFromIntrinsic(Call->Arguments[LiteralIdx], MRI));1901Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);1902if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) {1903SPIRVType *CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);1904if (!CoopMatrType)1905report_fatal_error("Can't find a register's type definition");1906MIRBuilder.buildInstr(Opcode)1907.addDef(Call->ReturnRegister)1908.addUse(TypeReg)1909.addUse(CoopMatrType->getOperand(0).getReg());1910return true;1911}1912return buildOpFromWrapper(MIRBuilder, Opcode, Call,1913IsSet ? TypeReg : Register(0), ImmArgs);1914}19151916static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call,1917MachineIRBuilder &MIRBuilder,1918SPIRVGlobalRegistry *GR) {1919// Lookup the instruction opcode in the TableGen records.1920const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;1921unsigned Opcode =1922SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;1923const MachineRegisterInfo *MRI = MIRBuilder.getMRI();19241925switch (Opcode) {1926case SPIRV::OpSpecConstant: {1927// Build the SpecID decoration.1928unsigned SpecId =1929static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI));1930buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId,1931{SpecId});1932// Determine the constant MI.1933Register ConstRegister = Call->Arguments[1];1934const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI);1935assert(Const &&1936(Const->getOpcode() == TargetOpcode::G_CONSTANT ||1937Const->getOpcode() == TargetOpcode::G_FCONSTANT) &&1938"Argument should be either an int or floating-point constant");1939// Determine the opcode and built the OpSpec MI.1940const MachineOperand &ConstOperand = Const->getOperand(1);1941if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) {1942assert(ConstOperand.isCImm() && "Int constant operand is expected");1943Opcode = ConstOperand.getCImm()->getValue().getZExtValue()1944? SPIRV::OpSpecConstantTrue1945: SPIRV::OpSpecConstantFalse;1946}1947auto MIB = MIRBuilder.buildInstr(Opcode)1948.addDef(Call->ReturnRegister)1949.addUse(GR->getSPIRVTypeID(Call->ReturnType));19501951if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) {1952if (Const->getOpcode() == TargetOpcode::G_CONSTANT)1953addNumImm(ConstOperand.getCImm()->getValue(), MIB);1954else1955addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB);1956}1957return true;1958}1959case SPIRV::OpSpecConstantComposite: {1960auto MIB = MIRBuilder.buildInstr(Opcode)1961.addDef(Call->ReturnRegister)1962.addUse(GR->getSPIRVTypeID(Call->ReturnType));1963for (unsigned i = 0; i < Call->Arguments.size(); i++)1964MIB.addUse(Call->Arguments[i]);1965return true;1966}1967default:1968return false;1969}1970}19711972static bool buildNDRange(const SPIRV::IncomingCall *Call,1973MachineIRBuilder &MIRBuilder,1974SPIRVGlobalRegistry *GR) {1975MachineRegisterInfo *MRI = MIRBuilder.getMRI();1976MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);1977SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]);1978assert(PtrType->getOpcode() == SPIRV::OpTypePointer &&1979PtrType->getOperand(2).isReg());1980Register TypeReg = PtrType->getOperand(2).getReg();1981SPIRVType *StructType = GR->getSPIRVTypeForVReg(TypeReg);1982MachineFunction &MF = MIRBuilder.getMF();1983Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);1984GR->assignSPIRVTypeToVReg(StructType, TmpReg, MF);1985// Skip the first arg, it's the destination pointer. OpBuildNDRange takes1986// three other arguments, so pass zero constant on absence.1987unsigned NumArgs = Call->Arguments.size();1988assert(NumArgs >= 2);1989Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2];1990MRI->setRegClass(GlobalWorkSize, &SPIRV::IDRegClass);1991Register LocalWorkSize =1992NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3];1993if (LocalWorkSize.isValid())1994MRI->setRegClass(LocalWorkSize, &SPIRV::IDRegClass);1995Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1];1996if (GlobalWorkOffset.isValid())1997MRI->setRegClass(GlobalWorkOffset, &SPIRV::IDRegClass);1998if (NumArgs < 4) {1999Register Const;2000SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize);2001if (SpvTy->getOpcode() == SPIRV::OpTypePointer) {2002MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize);2003assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) &&2004DefInstr->getOperand(3).isReg());2005Register GWSPtr = DefInstr->getOperand(3).getReg();2006if (!MRI->getRegClassOrNull(GWSPtr))2007MRI->setRegClass(GWSPtr, &SPIRV::IDRegClass);2008// TODO: Maybe simplify generation of the type of the fields.2009unsigned Size = Call->Builtin->Name == "ndrange_3D" ? 3 : 2;2010unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32;2011Type *BaseTy = IntegerType::get(MF.getFunction().getContext(), BitWidth);2012Type *FieldTy = ArrayType::get(BaseTy, Size);2013SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder);2014GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::IDRegClass);2015GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize, MF);2016MIRBuilder.buildInstr(SPIRV::OpLoad)2017.addDef(GlobalWorkSize)2018.addUse(GR->getSPIRVTypeID(SpvFieldTy))2019.addUse(GWSPtr);2020const SPIRVSubtarget &ST =2021cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());2022Const = GR->getOrCreateConstIntArray(0, Size, *MIRBuilder.getInsertPt(),2023SpvFieldTy, *ST.getInstrInfo());2024} else {2025Const = GR->buildConstantInt(0, MIRBuilder, SpvTy);2026}2027if (!LocalWorkSize.isValid())2028LocalWorkSize = Const;2029if (!GlobalWorkOffset.isValid())2030GlobalWorkOffset = Const;2031}2032assert(LocalWorkSize.isValid() && GlobalWorkOffset.isValid());2033MIRBuilder.buildInstr(SPIRV::OpBuildNDRange)2034.addDef(TmpReg)2035.addUse(TypeReg)2036.addUse(GlobalWorkSize)2037.addUse(LocalWorkSize)2038.addUse(GlobalWorkOffset);2039return MIRBuilder.buildInstr(SPIRV::OpStore)2040.addUse(Call->Arguments[0])2041.addUse(TmpReg);2042}20432044// TODO: maybe move to the global register.2045static SPIRVType *2046getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder,2047SPIRVGlobalRegistry *GR) {2048LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();2049Type *OpaqueType = StructType::getTypeByName(Context, "spirv.DeviceEvent");2050if (!OpaqueType)2051OpaqueType = StructType::getTypeByName(Context, "opencl.clk_event_t");2052if (!OpaqueType)2053OpaqueType = StructType::create(Context, "spirv.DeviceEvent");2054unsigned SC0 = storageClassToAddressSpace(SPIRV::StorageClass::Function);2055unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic);2056Type *PtrType = PointerType::get(PointerType::get(OpaqueType, SC0), SC1);2057return GR->getOrCreateSPIRVType(PtrType, MIRBuilder);2058}20592060static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call,2061MachineIRBuilder &MIRBuilder,2062SPIRVGlobalRegistry *GR) {2063MachineRegisterInfo *MRI = MIRBuilder.getMRI();2064const DataLayout &DL = MIRBuilder.getDataLayout();2065bool IsSpirvOp = Call->isSpirvOp();2066bool HasEvents = Call->Builtin->Name.contains("events") || IsSpirvOp;2067const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);20682069// Make vararg instructions before OpEnqueueKernel.2070// Local sizes arguments: Sizes of block invoke arguments. Clang generates2071// local size operands as an array, so we need to unpack them.2072SmallVector<Register, 16> LocalSizes;2073if (Call->Builtin->Name.contains("_varargs") || IsSpirvOp) {2074const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6;2075Register GepReg = Call->Arguments[LocalSizeArrayIdx];2076MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg);2077assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) &&2078GepMI->getOperand(3).isReg());2079Register ArrayReg = GepMI->getOperand(3).getReg();2080MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg);2081const Type *LocalSizeTy = getMachineInstrType(ArrayMI);2082assert(LocalSizeTy && "Local size type is expected");2083const uint64_t LocalSizeNum =2084cast<ArrayType>(LocalSizeTy)->getNumElements();2085unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic);2086const LLT LLType = LLT::pointer(SC, GR->getPointerSize());2087const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType(2088Int32Ty, MIRBuilder, SPIRV::StorageClass::Function);2089for (unsigned I = 0; I < LocalSizeNum; ++I) {2090Register Reg = MRI->createVirtualRegister(&SPIRV::IDRegClass);2091MRI->setType(Reg, LLType);2092GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF());2093auto GEPInst = MIRBuilder.buildIntrinsic(2094Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false);2095GEPInst2096.addImm(GepMI->getOperand(2).getImm()) // In bound.2097.addUse(ArrayMI->getOperand(0).getReg()) // Alloca.2098.addUse(buildConstantIntReg(0, MIRBuilder, GR)) // Indices.2099.addUse(buildConstantIntReg(I, MIRBuilder, GR));2100LocalSizes.push_back(Reg);2101}2102}21032104// SPIRV OpEnqueueKernel instruction has 10+ arguments.2105auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel)2106.addDef(Call->ReturnRegister)2107.addUse(GR->getSPIRVTypeID(Int32Ty));21082109// Copy all arguments before block invoke function pointer.2110const unsigned BlockFIdx = HasEvents ? 6 : 3;2111for (unsigned i = 0; i < BlockFIdx; i++)2112MIB.addUse(Call->Arguments[i]);21132114// If there are no event arguments in the original call, add dummy ones.2115if (!HasEvents) {2116MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Dummy num events.2117Register NullPtr = GR->getOrCreateConstNullPtr(2118MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR));2119MIB.addUse(NullPtr); // Dummy wait events.2120MIB.addUse(NullPtr); // Dummy ret event.2121}21222123MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI);2124assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE);2125// Invoke: Pointer to invoke function.2126MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal());21272128Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1];2129// Param: Pointer to block literal.2130MIB.addUse(BlockLiteralReg);21312132Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));2133// TODO: these numbers should be obtained from block literal structure.2134// Param Size: Size of block literal structure.2135MIB.addUse(buildConstantIntReg(DL.getTypeStoreSize(PType), MIRBuilder, GR));2136// Param Aligment: Aligment of block literal structure.2137MIB.addUse(2138buildConstantIntReg(DL.getPrefTypeAlign(PType).value(), MIRBuilder, GR));21392140for (unsigned i = 0; i < LocalSizes.size(); i++)2141MIB.addUse(LocalSizes[i]);2142return true;2143}21442145static bool generateEnqueueInst(const SPIRV::IncomingCall *Call,2146MachineIRBuilder &MIRBuilder,2147SPIRVGlobalRegistry *GR) {2148// Lookup the instruction opcode in the TableGen records.2149const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;2150unsigned Opcode =2151SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;21522153switch (Opcode) {2154case SPIRV::OpRetainEvent:2155case SPIRV::OpReleaseEvent:2156MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);2157return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]);2158case SPIRV::OpCreateUserEvent:2159case SPIRV::OpGetDefaultQueue:2160return MIRBuilder.buildInstr(Opcode)2161.addDef(Call->ReturnRegister)2162.addUse(GR->getSPIRVTypeID(Call->ReturnType));2163case SPIRV::OpIsValidEvent:2164MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);2165return MIRBuilder.buildInstr(Opcode)2166.addDef(Call->ReturnRegister)2167.addUse(GR->getSPIRVTypeID(Call->ReturnType))2168.addUse(Call->Arguments[0]);2169case SPIRV::OpSetUserEventStatus:2170MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);2171MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);2172return MIRBuilder.buildInstr(Opcode)2173.addUse(Call->Arguments[0])2174.addUse(Call->Arguments[1]);2175case SPIRV::OpCaptureEventProfilingInfo:2176MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);2177MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);2178MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass);2179return MIRBuilder.buildInstr(Opcode)2180.addUse(Call->Arguments[0])2181.addUse(Call->Arguments[1])2182.addUse(Call->Arguments[2]);2183case SPIRV::OpBuildNDRange:2184return buildNDRange(Call, MIRBuilder, GR);2185case SPIRV::OpEnqueueKernel:2186return buildEnqueueKernel(Call, MIRBuilder, GR);2187default:2188return false;2189}2190}21912192static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,2193MachineIRBuilder &MIRBuilder,2194SPIRVGlobalRegistry *GR) {2195// Lookup the instruction opcode in the TableGen records.2196const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;2197unsigned Opcode =2198SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;21992200bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;2201Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);2202if (Call->isSpirvOp())2203return buildOpFromWrapper(MIRBuilder, Opcode, Call,2204IsSet ? TypeReg : Register(0));22052206auto Scope = buildConstantIntReg(SPIRV::Scope::Workgroup, MIRBuilder, GR);22072208switch (Opcode) {2209case SPIRV::OpGroupAsyncCopy: {2210SPIRVType *NewType =2211Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent2212? nullptr2213: GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder);2214Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType);2215unsigned NumArgs = Call->Arguments.size();2216Register EventReg = Call->Arguments[NumArgs - 1];2217bool Res = MIRBuilder.buildInstr(Opcode)2218.addDef(Call->ReturnRegister)2219.addUse(TypeReg)2220.addUse(Scope)2221.addUse(Call->Arguments[0])2222.addUse(Call->Arguments[1])2223.addUse(Call->Arguments[2])2224.addUse(Call->Arguments.size() > 42225? Call->Arguments[3]2226: buildConstantIntReg(1, MIRBuilder, GR))2227.addUse(EventReg);2228if (NewType != nullptr)2229insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder,2230MIRBuilder.getMF().getRegInfo());2231return Res;2232}2233case SPIRV::OpGroupWaitEvents:2234return MIRBuilder.buildInstr(Opcode)2235.addUse(Scope)2236.addUse(Call->Arguments[0])2237.addUse(Call->Arguments[1]);2238default:2239return false;2240}2241}22422243static bool generateConvertInst(const StringRef DemangledCall,2244const SPIRV::IncomingCall *Call,2245MachineIRBuilder &MIRBuilder,2246SPIRVGlobalRegistry *GR) {2247// Lookup the conversion builtin in the TableGen records.2248const SPIRV::ConvertBuiltin *Builtin =2249SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set);22502251if (!Builtin && Call->isSpirvOp()) {2252const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;2253unsigned Opcode =2254SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;2255return buildOpFromWrapper(MIRBuilder, Opcode, Call,2256GR->getSPIRVTypeID(Call->ReturnType));2257}22582259if (Builtin->IsSaturated)2260buildOpDecorate(Call->ReturnRegister, MIRBuilder,2261SPIRV::Decoration::SaturatedConversion, {});2262if (Builtin->IsRounded)2263buildOpDecorate(Call->ReturnRegister, MIRBuilder,2264SPIRV::Decoration::FPRoundingMode,2265{(unsigned)Builtin->RoundingMode});22662267std::string NeedExtMsg; // no errors if empty2268bool IsRightComponentsNumber = true; // check if input/output accepts vectors2269unsigned Opcode = SPIRV::OpNop;2270if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) {2271// Int -> ...2272if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {2273// Int -> Int2274if (Builtin->IsSaturated)2275Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS2276: SPIRV::OpSatConvertSToU;2277else2278Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert2279: SPIRV::OpSConvert;2280} else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,2281SPIRV::OpTypeFloat)) {2282// Int -> Float2283if (Builtin->IsBfloat16) {2284const auto *ST = static_cast<const SPIRVSubtarget *>(2285&MIRBuilder.getMF().getSubtarget());2286if (!ST->canUseExtension(2287SPIRV::Extension::SPV_INTEL_bfloat16_conversion))2288NeedExtMsg = "SPV_INTEL_bfloat16_conversion";2289IsRightComponentsNumber =2290GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==2291GR->getScalarOrVectorComponentCount(Call->ReturnRegister);2292Opcode = SPIRV::OpConvertBF16ToFINTEL;2293} else {2294bool IsSourceSigned =2295DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u';2296Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF;2297}2298}2299} else if (GR->isScalarOrVectorOfType(Call->Arguments[0],2300SPIRV::OpTypeFloat)) {2301// Float -> ...2302if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) {2303// Float -> Int2304if (Builtin->IsBfloat16) {2305const auto *ST = static_cast<const SPIRVSubtarget *>(2306&MIRBuilder.getMF().getSubtarget());2307if (!ST->canUseExtension(2308SPIRV::Extension::SPV_INTEL_bfloat16_conversion))2309NeedExtMsg = "SPV_INTEL_bfloat16_conversion";2310IsRightComponentsNumber =2311GR->getScalarOrVectorComponentCount(Call->Arguments[0]) ==2312GR->getScalarOrVectorComponentCount(Call->ReturnRegister);2313Opcode = SPIRV::OpConvertFToBF16INTEL;2314} else {2315Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS2316: SPIRV::OpConvertFToU;2317}2318} else if (GR->isScalarOrVectorOfType(Call->ReturnRegister,2319SPIRV::OpTypeFloat)) {2320// Float -> Float2321Opcode = SPIRV::OpFConvert;2322}2323}23242325if (!NeedExtMsg.empty()) {2326std::string DiagMsg = std::string(Builtin->Name) +2327": the builtin requires the following SPIR-V "2328"extension: " +2329NeedExtMsg;2330report_fatal_error(DiagMsg.c_str(), false);2331}2332if (!IsRightComponentsNumber) {2333std::string DiagMsg =2334std::string(Builtin->Name) +2335": result and argument must have the same number of components";2336report_fatal_error(DiagMsg.c_str(), false);2337}2338assert(Opcode != SPIRV::OpNop &&2339"Conversion between the types not implemented!");23402341MIRBuilder.buildInstr(Opcode)2342.addDef(Call->ReturnRegister)2343.addUse(GR->getSPIRVTypeID(Call->ReturnType))2344.addUse(Call->Arguments[0]);2345return true;2346}23472348static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call,2349MachineIRBuilder &MIRBuilder,2350SPIRVGlobalRegistry *GR) {2351// Lookup the vector load/store builtin in the TableGen records.2352const SPIRV::VectorLoadStoreBuiltin *Builtin =2353SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,2354Call->Builtin->Set);2355// Build extended instruction.2356auto MIB =2357MIRBuilder.buildInstr(SPIRV::OpExtInst)2358.addDef(Call->ReturnRegister)2359.addUse(GR->getSPIRVTypeID(Call->ReturnType))2360.addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))2361.addImm(Builtin->Number);2362for (auto Argument : Call->Arguments)2363MIB.addUse(Argument);2364if (Builtin->Name.contains("load") && Builtin->ElementCount > 1)2365MIB.addImm(Builtin->ElementCount);23662367// Rounding mode should be passed as a last argument in the MI for builtins2368// like "vstorea_halfn_r".2369if (Builtin->IsRounded)2370MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode));2371return true;2372}23732374static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call,2375MachineIRBuilder &MIRBuilder,2376SPIRVGlobalRegistry *GR) {2377// Lookup the instruction opcode in the TableGen records.2378const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;2379unsigned Opcode =2380SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;2381bool IsLoad = Opcode == SPIRV::OpLoad;2382// Build the instruction.2383auto MIB = MIRBuilder.buildInstr(Opcode);2384if (IsLoad) {2385MIB.addDef(Call->ReturnRegister);2386MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType));2387}2388// Add a pointer to the value to load/store.2389MIB.addUse(Call->Arguments[0]);2390MachineRegisterInfo *MRI = MIRBuilder.getMRI();2391MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass);2392// Add a value to store.2393if (!IsLoad) {2394MIB.addUse(Call->Arguments[1]);2395MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass);2396}2397// Add optional memory attributes and an alignment.2398unsigned NumArgs = Call->Arguments.size();2399if ((IsLoad && NumArgs >= 2) || NumArgs >= 3) {2400MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI));2401MRI->setRegClass(Call->Arguments[IsLoad ? 1 : 2], &SPIRV::IDRegClass);2402}2403if ((IsLoad && NumArgs >= 3) || NumArgs >= 4) {2404MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI));2405MRI->setRegClass(Call->Arguments[IsLoad ? 2 : 3], &SPIRV::IDRegClass);2406}2407return true;2408}24092410namespace SPIRV {2411// Try to find a builtin function attributes by a demangled function name and2412// return a tuple <builtin group, op code, ext instruction number>, or a special2413// tuple value <-1, 0, 0> if the builtin function is not found.2414// Not all builtin functions are supported, only those with a ready-to-use op2415// code or instruction number defined in TableGen.2416// TODO: consider a major rework of mapping demangled calls into a builtin2417// functions to unify search and decrease number of individual cases.2418std::tuple<int, unsigned, unsigned>2419mapBuiltinToOpcode(const StringRef DemangledCall,2420SPIRV::InstructionSet::InstructionSet Set) {2421Register Reg;2422SmallVector<Register> Args;2423std::unique_ptr<const IncomingCall> Call =2424lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args);2425if (!Call)2426return std::make_tuple(-1, 0, 0);24272428switch (Call->Builtin->Group) {2429case SPIRV::Relational:2430case SPIRV::Atomic:2431case SPIRV::Barrier:2432case SPIRV::CastToPtr:2433case SPIRV::ImageMiscQuery:2434case SPIRV::SpecConstant:2435case SPIRV::Enqueue:2436case SPIRV::AsyncCopy:2437case SPIRV::LoadStore:2438case SPIRV::CoopMatr:2439if (const auto *R =2440SPIRV::lookupNativeBuiltin(Call->Builtin->Name, Call->Builtin->Set))2441return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);2442break;2443case SPIRV::Extended:2444if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->Name,2445Call->Builtin->Set))2446return std::make_tuple(Call->Builtin->Group, 0, R->Number);2447break;2448case SPIRV::VectorLoadStore:2449if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name,2450Call->Builtin->Set))2451return std::make_tuple(SPIRV::Extended, 0, R->Number);2452break;2453case SPIRV::Group:2454if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->Name))2455return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);2456break;2457case SPIRV::AtomicFloating:2458if (const auto *R = SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->Name))2459return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);2460break;2461case SPIRV::IntelSubgroups:2462if (const auto *R = SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->Name))2463return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);2464break;2465case SPIRV::GroupUniform:2466if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->Name))2467return std::make_tuple(Call->Builtin->Group, R->Opcode, 0);2468break;2469case SPIRV::WriteImage:2470return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0);2471case SPIRV::Select:2472return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0);2473case SPIRV::Construct:2474return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct,24750);2476case SPIRV::KernelClock:2477return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0);2478default:2479return std::make_tuple(-1, 0, 0);2480}2481return std::make_tuple(-1, 0, 0);2482}24832484std::optional<bool> lowerBuiltin(const StringRef DemangledCall,2485SPIRV::InstructionSet::InstructionSet Set,2486MachineIRBuilder &MIRBuilder,2487const Register OrigRet, const Type *OrigRetTy,2488const SmallVectorImpl<Register> &Args,2489SPIRVGlobalRegistry *GR) {2490LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n");24912492// SPIR-V type and return register.2493Register ReturnRegister = OrigRet;2494SPIRVType *ReturnType = nullptr;2495if (OrigRetTy && !OrigRetTy->isVoidTy()) {2496ReturnType = GR->assignTypeToVReg(OrigRetTy, OrigRet, MIRBuilder);2497if (!MIRBuilder.getMRI()->getRegClassOrNull(ReturnRegister))2498MIRBuilder.getMRI()->setRegClass(ReturnRegister, &SPIRV::IDRegClass);2499} else if (OrigRetTy && OrigRetTy->isVoidTy()) {2500ReturnRegister = MIRBuilder.getMRI()->createVirtualRegister(&IDRegClass);2501MIRBuilder.getMRI()->setType(ReturnRegister, LLT::scalar(32));2502ReturnType = GR->assignTypeToVReg(OrigRetTy, ReturnRegister, MIRBuilder);2503}25042505// Lookup the builtin in the TableGen records.2506std::unique_ptr<const IncomingCall> Call =2507lookupBuiltin(DemangledCall, Set, ReturnRegister, ReturnType, Args);25082509if (!Call) {2510LLVM_DEBUG(dbgs() << "Builtin record was not found!\n");2511return std::nullopt;2512}25132514// TODO: check if the provided args meet the builtin requirments.2515assert(Args.size() >= Call->Builtin->MinNumArgs &&2516"Too few arguments to generate the builtin");2517if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs)2518LLVM_DEBUG(dbgs() << "More arguments provided than required!\n");25192520// Match the builtin with implementation based on the grouping.2521switch (Call->Builtin->Group) {2522case SPIRV::Extended:2523return generateExtInst(Call.get(), MIRBuilder, GR);2524case SPIRV::Relational:2525return generateRelationalInst(Call.get(), MIRBuilder, GR);2526case SPIRV::Group:2527return generateGroupInst(Call.get(), MIRBuilder, GR);2528case SPIRV::Variable:2529return generateBuiltinVar(Call.get(), MIRBuilder, GR);2530case SPIRV::Atomic:2531return generateAtomicInst(Call.get(), MIRBuilder, GR);2532case SPIRV::AtomicFloating:2533return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR);2534case SPIRV::Barrier:2535return generateBarrierInst(Call.get(), MIRBuilder, GR);2536case SPIRV::CastToPtr:2537return generateCastToPtrInst(Call.get(), MIRBuilder);2538case SPIRV::Dot:2539return generateDotOrFMulInst(Call.get(), MIRBuilder, GR);2540case SPIRV::Wave:2541return generateWaveInst(Call.get(), MIRBuilder, GR);2542case SPIRV::GetQuery:2543return generateGetQueryInst(Call.get(), MIRBuilder, GR);2544case SPIRV::ImageSizeQuery:2545return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR);2546case SPIRV::ImageMiscQuery:2547return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR);2548case SPIRV::ReadImage:2549return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR);2550case SPIRV::WriteImage:2551return generateWriteImageInst(Call.get(), MIRBuilder, GR);2552case SPIRV::SampleImage:2553return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR);2554case SPIRV::Select:2555return generateSelectInst(Call.get(), MIRBuilder);2556case SPIRV::Construct:2557return generateConstructInst(Call.get(), MIRBuilder, GR);2558case SPIRV::SpecConstant:2559return generateSpecConstantInst(Call.get(), MIRBuilder, GR);2560case SPIRV::Enqueue:2561return generateEnqueueInst(Call.get(), MIRBuilder, GR);2562case SPIRV::AsyncCopy:2563return generateAsyncCopy(Call.get(), MIRBuilder, GR);2564case SPIRV::Convert:2565return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR);2566case SPIRV::VectorLoadStore:2567return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR);2568case SPIRV::LoadStore:2569return generateLoadStoreInst(Call.get(), MIRBuilder, GR);2570case SPIRV::IntelSubgroups:2571return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR);2572case SPIRV::GroupUniform:2573return generateGroupUniformInst(Call.get(), MIRBuilder, GR);2574case SPIRV::KernelClock:2575return generateKernelClockInst(Call.get(), MIRBuilder, GR);2576case SPIRV::CoopMatr:2577return generateCoopMatrInst(Call.get(), MIRBuilder, GR);2578}2579return false;2580}25812582Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall,2583unsigned ArgIdx, LLVMContext &Ctx) {2584SmallVector<StringRef, 10> BuiltinArgsTypeStrs;2585StringRef BuiltinArgs =2586DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));2587BuiltinArgs.split(BuiltinArgsTypeStrs, ',', -1, false);2588if (ArgIdx >= BuiltinArgsTypeStrs.size())2589return nullptr;2590StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim();25912592// Parse strings representing OpenCL builtin types.2593if (hasBuiltinTypePrefix(TypeStr)) {2594// OpenCL builtin types in demangled call strings have the following format:2595// e.g. ocl_image2d_ro2596[[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_");2597assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix");25982599// Check if this is pointer to a builtin type and not just pointer2600// representing a builtin type. In case it is a pointer to builtin type,2601// this will require additional handling in the method calling2602// parseBuiltinCallArgumentBaseType(...) as this function only retrieves the2603// base types.2604if (TypeStr.ends_with("*"))2605TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));26062607return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",2608Ctx);2609}26102611// Parse type name in either "typeN" or "type vector[N]" format, where2612// N is the number of elements of the vector.2613Type *BaseType;2614unsigned VecElts = 0;26152616BaseType = parseBasicTypeName(TypeStr, Ctx);2617if (!BaseType)2618// Unable to recognize SPIRV type name.2619return nullptr;26202621// Handle "typeN*" or "type vector[N]*".2622TypeStr.consume_back("*");26232624if (TypeStr.consume_front(" vector["))2625TypeStr = TypeStr.substr(0, TypeStr.find(']'));26262627TypeStr.getAsInteger(10, VecElts);2628if (VecElts > 0)2629BaseType = VectorType::get(2630BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);26312632return BaseType;2633}26342635struct BuiltinType {2636StringRef Name;2637uint32_t Opcode;2638};26392640#define GET_BuiltinTypes_DECL2641#define GET_BuiltinTypes_IMPL26422643struct OpenCLType {2644StringRef Name;2645StringRef SpirvTypeLiteral;2646};26472648#define GET_OpenCLTypes_DECL2649#define GET_OpenCLTypes_IMPL26502651#include "SPIRVGenTables.inc"2652} // namespace SPIRV26532654//===----------------------------------------------------------------------===//2655// Misc functions for parsing builtin types.2656//===----------------------------------------------------------------------===//26572658static Type *parseTypeString(const StringRef Name, LLVMContext &Context) {2659if (Name.starts_with("void"))2660return Type::getVoidTy(Context);2661else if (Name.starts_with("int") || Name.starts_with("uint"))2662return Type::getInt32Ty(Context);2663else if (Name.starts_with("float"))2664return Type::getFloatTy(Context);2665else if (Name.starts_with("half"))2666return Type::getHalfTy(Context);2667report_fatal_error("Unable to recognize type!");2668}26692670//===----------------------------------------------------------------------===//2671// Implementation functions for builtin types.2672//===----------------------------------------------------------------------===//26732674static SPIRVType *getNonParameterizedType(const TargetExtType *ExtensionType,2675const SPIRV::BuiltinType *TypeRecord,2676MachineIRBuilder &MIRBuilder,2677SPIRVGlobalRegistry *GR) {2678unsigned Opcode = TypeRecord->Opcode;2679// Create or get an existing type from GlobalRegistry.2680return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode);2681}26822683static SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder,2684SPIRVGlobalRegistry *GR) {2685// Create or get an existing type from GlobalRegistry.2686return GR->getOrCreateOpTypeSampler(MIRBuilder);2687}26882689static SPIRVType *getPipeType(const TargetExtType *ExtensionType,2690MachineIRBuilder &MIRBuilder,2691SPIRVGlobalRegistry *GR) {2692assert(ExtensionType->getNumIntParameters() == 1 &&2693"Invalid number of parameters for SPIR-V pipe builtin!");2694// Create or get an existing type from GlobalRegistry.2695return GR->getOrCreateOpTypePipe(MIRBuilder,2696SPIRV::AccessQualifier::AccessQualifier(2697ExtensionType->getIntParameter(0)));2698}26992700static SPIRVType *getCoopMatrType(const TargetExtType *ExtensionType,2701MachineIRBuilder &MIRBuilder,2702SPIRVGlobalRegistry *GR) {2703assert(ExtensionType->getNumIntParameters() == 4 &&2704"Invalid number of parameters for SPIR-V coop matrices builtin!");2705assert(ExtensionType->getNumTypeParameters() == 1 &&2706"SPIR-V coop matrices builtin type must have a type parameter!");2707const SPIRVType *ElemType =2708GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);2709// Create or get an existing type from GlobalRegistry.2710return GR->getOrCreateOpTypeCoopMatr(2711MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0),2712ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),2713ExtensionType->getIntParameter(3));2714}27152716static SPIRVType *2717getImageType(const TargetExtType *ExtensionType,2718const SPIRV::AccessQualifier::AccessQualifier Qualifier,2719MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {2720assert(ExtensionType->getNumTypeParameters() == 1 &&2721"SPIR-V image builtin type must have sampled type parameter!");2722const SPIRVType *SampledType =2723GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder);2724assert(ExtensionType->getNumIntParameters() == 7 &&2725"Invalid number of parameters for SPIR-V image builtin!");2726// Create or get an existing type from GlobalRegistry.2727return GR->getOrCreateOpTypeImage(2728MIRBuilder, SampledType,2729SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)),2730ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2),2731ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4),2732SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)),2733Qualifier == SPIRV::AccessQualifier::WriteOnly2734? SPIRV::AccessQualifier::WriteOnly2735: SPIRV::AccessQualifier::AccessQualifier(2736ExtensionType->getIntParameter(6)));2737}27382739static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType,2740MachineIRBuilder &MIRBuilder,2741SPIRVGlobalRegistry *GR) {2742SPIRVType *OpaqueImageType = getImageType(2743OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder, GR);2744// Create or get an existing type from GlobalRegistry.2745return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);2746}27472748namespace SPIRV {2749TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,2750LLVMContext &Context) {2751StringRef NameWithParameters = TypeName;27522753// Pointers-to-opaque-structs representing OpenCL types are first translated2754// to equivalent SPIR-V types. OpenCL builtin type names should have the2755// following format: e.g. %opencl.event_t2756if (NameWithParameters.starts_with("opencl.")) {2757const SPIRV::OpenCLType *OCLTypeRecord =2758SPIRV::lookupOpenCLType(NameWithParameters);2759if (!OCLTypeRecord)2760report_fatal_error("Missing TableGen record for OpenCL type: " +2761NameWithParameters);2762NameWithParameters = OCLTypeRecord->SpirvTypeLiteral;2763// Continue with the SPIR-V builtin type...2764}27652766// Names of the opaque structs representing a SPIR-V builtins without2767// parameters should have the following format: e.g. %spirv.Event2768assert(NameWithParameters.starts_with("spirv.") &&2769"Unknown builtin opaque type!");27702771// Parameterized SPIR-V builtins names follow this format:2772// e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._02773if (!NameWithParameters.contains('_'))2774return TargetExtType::get(Context, NameWithParameters);27752776SmallVector<StringRef> Parameters;2777unsigned BaseNameLength = NameWithParameters.find('_') - 1;2778SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_");27792780SmallVector<Type *, 1> TypeParameters;2781bool HasTypeParameter = !isDigit(Parameters[0][0]);2782if (HasTypeParameter)2783TypeParameters.push_back(parseTypeString(Parameters[0], Context));2784SmallVector<unsigned> IntParameters;2785for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) {2786unsigned IntParameter = 0;2787bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter);2788(void)ValidLiteral;2789assert(ValidLiteral &&2790"Invalid format of SPIR-V builtin parameter literal!");2791IntParameters.push_back(IntParameter);2792}2793return TargetExtType::get(Context,2794NameWithParameters.substr(0, BaseNameLength),2795TypeParameters, IntParameters);2796}27972798SPIRVType *lowerBuiltinType(const Type *OpaqueType,2799SPIRV::AccessQualifier::AccessQualifier AccessQual,2800MachineIRBuilder &MIRBuilder,2801SPIRVGlobalRegistry *GR) {2802// In LLVM IR, SPIR-V and OpenCL builtin types are represented as either2803// target(...) target extension types or pointers-to-opaque-structs. The2804// approach relying on structs is deprecated and works only in the non-opaque2805// pointer mode (-opaque-pointers=0).2806// In order to maintain compatibility with LLVM IR generated by older versions2807// of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are2808// "translated" to target extension types. This translation is temporary and2809// will be removed in the future release of LLVM.2810const TargetExtType *BuiltinType = dyn_cast<TargetExtType>(OpaqueType);2811if (!BuiltinType)2812BuiltinType = parseBuiltinTypeNameToTargetExtType(2813OpaqueType->getStructName().str(), MIRBuilder.getContext());28142815unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();28162817const StringRef Name = BuiltinType->getName();2818LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");28192820// Lookup the demangled builtin type in the TableGen records.2821const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);2822if (!TypeRecord)2823report_fatal_error("Missing TableGen record for builtin type: " + Name);28242825// "Lower" the BuiltinType into TargetType. The following get<...>Type methods2826// use the implementation details from TableGen records or TargetExtType2827// parameters to either create a new OpType<...> machine instruction or get an2828// existing equivalent SPIRVType from GlobalRegistry.2829SPIRVType *TargetType;2830switch (TypeRecord->Opcode) {2831case SPIRV::OpTypeImage:2832TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);2833break;2834case SPIRV::OpTypePipe:2835TargetType = getPipeType(BuiltinType, MIRBuilder, GR);2836break;2837case SPIRV::OpTypeDeviceEvent:2838TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);2839break;2840case SPIRV::OpTypeSampler:2841TargetType = getSamplerType(MIRBuilder, GR);2842break;2843case SPIRV::OpTypeSampledImage:2844TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);2845break;2846case SPIRV::OpTypeCooperativeMatrixKHR:2847TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);2848break;2849default:2850TargetType =2851getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);2852break;2853}28542855// Emit OpName instruction if a new OpType<...> instruction was added2856// (equivalent type was not found in GlobalRegistry).2857if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())2858buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder);28592860return TargetType;2861}2862} // namespace SPIRV2863} // namespace llvm286428652866