1*bdd1243dSDimitry Andric //===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===// 2*bdd1243dSDimitry Andric // 3*bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*bdd1243dSDimitry Andric // 7*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8*bdd1243dSDimitry Andric // 9*bdd1243dSDimitry Andric // This file implements lowering builtin function calls and types using their 10*bdd1243dSDimitry Andric // demangled names and TableGen records. 11*bdd1243dSDimitry Andric // 12*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 13*bdd1243dSDimitry Andric 14*bdd1243dSDimitry Andric #include "SPIRVBuiltins.h" 15*bdd1243dSDimitry Andric #include "SPIRV.h" 16*bdd1243dSDimitry Andric #include "SPIRVUtils.h" 17*bdd1243dSDimitry Andric #include "llvm/Analysis/ValueTracking.h" 18*bdd1243dSDimitry Andric #include "llvm/IR/IntrinsicsSPIRV.h" 19*bdd1243dSDimitry Andric #include <string> 20*bdd1243dSDimitry Andric #include <tuple> 21*bdd1243dSDimitry Andric 22*bdd1243dSDimitry Andric #define DEBUG_TYPE "spirv-builtins" 23*bdd1243dSDimitry Andric 24*bdd1243dSDimitry Andric namespace llvm { 25*bdd1243dSDimitry Andric namespace SPIRV { 26*bdd1243dSDimitry Andric #define GET_BuiltinGroup_DECL 27*bdd1243dSDimitry Andric #include "SPIRVGenTables.inc" 28*bdd1243dSDimitry Andric 29*bdd1243dSDimitry Andric struct DemangledBuiltin { 30*bdd1243dSDimitry Andric StringRef Name; 31*bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 32*bdd1243dSDimitry Andric BuiltinGroup Group; 33*bdd1243dSDimitry Andric uint8_t MinNumArgs; 34*bdd1243dSDimitry Andric uint8_t MaxNumArgs; 35*bdd1243dSDimitry Andric }; 36*bdd1243dSDimitry Andric 37*bdd1243dSDimitry Andric #define GET_DemangledBuiltins_DECL 38*bdd1243dSDimitry Andric #define GET_DemangledBuiltins_IMPL 39*bdd1243dSDimitry Andric 40*bdd1243dSDimitry Andric struct IncomingCall { 41*bdd1243dSDimitry Andric const std::string BuiltinName; 42*bdd1243dSDimitry Andric const DemangledBuiltin *Builtin; 43*bdd1243dSDimitry Andric 44*bdd1243dSDimitry Andric const Register ReturnRegister; 45*bdd1243dSDimitry Andric const SPIRVType *ReturnType; 46*bdd1243dSDimitry Andric const SmallVectorImpl<Register> &Arguments; 47*bdd1243dSDimitry Andric 48*bdd1243dSDimitry Andric IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin, 49*bdd1243dSDimitry Andric const Register ReturnRegister, const SPIRVType *ReturnType, 50*bdd1243dSDimitry Andric const SmallVectorImpl<Register> &Arguments) 51*bdd1243dSDimitry Andric : BuiltinName(BuiltinName), Builtin(Builtin), 52*bdd1243dSDimitry Andric ReturnRegister(ReturnRegister), ReturnType(ReturnType), 53*bdd1243dSDimitry Andric Arguments(Arguments) {} 54*bdd1243dSDimitry Andric }; 55*bdd1243dSDimitry Andric 56*bdd1243dSDimitry Andric struct NativeBuiltin { 57*bdd1243dSDimitry Andric StringRef Name; 58*bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 59*bdd1243dSDimitry Andric uint32_t Opcode; 60*bdd1243dSDimitry Andric }; 61*bdd1243dSDimitry Andric 62*bdd1243dSDimitry Andric #define GET_NativeBuiltins_DECL 63*bdd1243dSDimitry Andric #define GET_NativeBuiltins_IMPL 64*bdd1243dSDimitry Andric 65*bdd1243dSDimitry Andric struct GroupBuiltin { 66*bdd1243dSDimitry Andric StringRef Name; 67*bdd1243dSDimitry Andric uint32_t Opcode; 68*bdd1243dSDimitry Andric uint32_t GroupOperation; 69*bdd1243dSDimitry Andric bool IsElect; 70*bdd1243dSDimitry Andric bool IsAllOrAny; 71*bdd1243dSDimitry Andric bool IsAllEqual; 72*bdd1243dSDimitry Andric bool IsBallot; 73*bdd1243dSDimitry Andric bool IsInverseBallot; 74*bdd1243dSDimitry Andric bool IsBallotBitExtract; 75*bdd1243dSDimitry Andric bool IsBallotFindBit; 76*bdd1243dSDimitry Andric bool IsLogical; 77*bdd1243dSDimitry Andric bool NoGroupOperation; 78*bdd1243dSDimitry Andric bool HasBoolArg; 79*bdd1243dSDimitry Andric }; 80*bdd1243dSDimitry Andric 81*bdd1243dSDimitry Andric #define GET_GroupBuiltins_DECL 82*bdd1243dSDimitry Andric #define GET_GroupBuiltins_IMPL 83*bdd1243dSDimitry Andric 84*bdd1243dSDimitry Andric struct GetBuiltin { 85*bdd1243dSDimitry Andric StringRef Name; 86*bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 87*bdd1243dSDimitry Andric BuiltIn::BuiltIn Value; 88*bdd1243dSDimitry Andric }; 89*bdd1243dSDimitry Andric 90*bdd1243dSDimitry Andric using namespace BuiltIn; 91*bdd1243dSDimitry Andric #define GET_GetBuiltins_DECL 92*bdd1243dSDimitry Andric #define GET_GetBuiltins_IMPL 93*bdd1243dSDimitry Andric 94*bdd1243dSDimitry Andric struct ImageQueryBuiltin { 95*bdd1243dSDimitry Andric StringRef Name; 96*bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 97*bdd1243dSDimitry Andric uint32_t Component; 98*bdd1243dSDimitry Andric }; 99*bdd1243dSDimitry Andric 100*bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_DECL 101*bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_IMPL 102*bdd1243dSDimitry Andric 103*bdd1243dSDimitry Andric struct ConvertBuiltin { 104*bdd1243dSDimitry Andric StringRef Name; 105*bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 106*bdd1243dSDimitry Andric bool IsDestinationSigned; 107*bdd1243dSDimitry Andric bool IsSaturated; 108*bdd1243dSDimitry Andric bool IsRounded; 109*bdd1243dSDimitry Andric FPRoundingMode::FPRoundingMode RoundingMode; 110*bdd1243dSDimitry Andric }; 111*bdd1243dSDimitry Andric 112*bdd1243dSDimitry Andric struct VectorLoadStoreBuiltin { 113*bdd1243dSDimitry Andric StringRef Name; 114*bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 115*bdd1243dSDimitry Andric uint32_t Number; 116*bdd1243dSDimitry Andric bool IsRounded; 117*bdd1243dSDimitry Andric FPRoundingMode::FPRoundingMode RoundingMode; 118*bdd1243dSDimitry Andric }; 119*bdd1243dSDimitry Andric 120*bdd1243dSDimitry Andric using namespace FPRoundingMode; 121*bdd1243dSDimitry Andric #define GET_ConvertBuiltins_DECL 122*bdd1243dSDimitry Andric #define GET_ConvertBuiltins_IMPL 123*bdd1243dSDimitry Andric 124*bdd1243dSDimitry Andric using namespace InstructionSet; 125*bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_DECL 126*bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_IMPL 127*bdd1243dSDimitry Andric 128*bdd1243dSDimitry Andric #define GET_CLMemoryScope_DECL 129*bdd1243dSDimitry Andric #define GET_CLSamplerAddressingMode_DECL 130*bdd1243dSDimitry Andric #define GET_CLMemoryFenceFlags_DECL 131*bdd1243dSDimitry Andric #define GET_ExtendedBuiltins_DECL 132*bdd1243dSDimitry Andric #include "SPIRVGenTables.inc" 133*bdd1243dSDimitry Andric } // namespace SPIRV 134*bdd1243dSDimitry Andric 135*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 136*bdd1243dSDimitry Andric // Misc functions for looking up builtins and veryfying requirements using 137*bdd1243dSDimitry Andric // TableGen records 138*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 139*bdd1243dSDimitry Andric 140*bdd1243dSDimitry Andric /// Looks up the demangled builtin call in the SPIRVBuiltins.td records using 141*bdd1243dSDimitry Andric /// the provided \p DemangledCall and specified \p Set. 142*bdd1243dSDimitry Andric /// 143*bdd1243dSDimitry Andric /// The lookup follows the following algorithm, returning the first successful 144*bdd1243dSDimitry Andric /// match: 145*bdd1243dSDimitry Andric /// 1. Search with the plain demangled name (expecting a 1:1 match). 146*bdd1243dSDimitry Andric /// 2. Search with the prefix before or suffix after the demangled name 147*bdd1243dSDimitry Andric /// signyfying the type of the first argument. 148*bdd1243dSDimitry Andric /// 149*bdd1243dSDimitry Andric /// \returns Wrapper around the demangled call and found builtin definition. 150*bdd1243dSDimitry Andric static std::unique_ptr<const SPIRV::IncomingCall> 151*bdd1243dSDimitry Andric lookupBuiltin(StringRef DemangledCall, 152*bdd1243dSDimitry Andric SPIRV::InstructionSet::InstructionSet Set, 153*bdd1243dSDimitry Andric Register ReturnRegister, const SPIRVType *ReturnType, 154*bdd1243dSDimitry Andric const SmallVectorImpl<Register> &Arguments) { 155*bdd1243dSDimitry Andric // Extract the builtin function name and types of arguments from the call 156*bdd1243dSDimitry Andric // skeleton. 157*bdd1243dSDimitry Andric std::string BuiltinName = 158*bdd1243dSDimitry Andric DemangledCall.substr(0, DemangledCall.find('(')).str(); 159*bdd1243dSDimitry Andric 160*bdd1243dSDimitry Andric // Check if the extracted name contains type information between angle 161*bdd1243dSDimitry Andric // brackets. If so, the builtin is an instantiated template - needs to have 162*bdd1243dSDimitry Andric // the information after angle brackets and return type removed. 163*bdd1243dSDimitry Andric if (BuiltinName.find('<') && BuiltinName.back() == '>') { 164*bdd1243dSDimitry Andric BuiltinName = BuiltinName.substr(0, BuiltinName.find('<')); 165*bdd1243dSDimitry Andric BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(" ") + 1); 166*bdd1243dSDimitry Andric } 167*bdd1243dSDimitry Andric 168*bdd1243dSDimitry Andric // Check if the extracted name begins with "__spirv_ImageSampleExplicitLod" 169*bdd1243dSDimitry Andric // contains return type information at the end "_R<type>", if so extract the 170*bdd1243dSDimitry Andric // plain builtin name without the type information. 171*bdd1243dSDimitry Andric if (StringRef(BuiltinName).contains("__spirv_ImageSampleExplicitLod") && 172*bdd1243dSDimitry Andric StringRef(BuiltinName).contains("_R")) { 173*bdd1243dSDimitry Andric BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R")); 174*bdd1243dSDimitry Andric } 175*bdd1243dSDimitry Andric 176*bdd1243dSDimitry Andric SmallVector<StringRef, 10> BuiltinArgumentTypes; 177*bdd1243dSDimitry Andric StringRef BuiltinArgs = 178*bdd1243dSDimitry Andric DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')')); 179*bdd1243dSDimitry Andric BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false); 180*bdd1243dSDimitry Andric 181*bdd1243dSDimitry Andric // Look up the builtin in the defined set. Start with the plain demangled 182*bdd1243dSDimitry Andric // name, expecting a 1:1 match in the defined builtin set. 183*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin; 184*bdd1243dSDimitry Andric if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set))) 185*bdd1243dSDimitry Andric return std::make_unique<SPIRV::IncomingCall>( 186*bdd1243dSDimitry Andric BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments); 187*bdd1243dSDimitry Andric 188*bdd1243dSDimitry Andric // If the initial look up was unsuccessful and the demangled call takes at 189*bdd1243dSDimitry Andric // least 1 argument, add a prefix or suffix signifying the type of the first 190*bdd1243dSDimitry Andric // argument and repeat the search. 191*bdd1243dSDimitry Andric if (BuiltinArgumentTypes.size() >= 1) { 192*bdd1243dSDimitry Andric char FirstArgumentType = BuiltinArgumentTypes[0][0]; 193*bdd1243dSDimitry Andric // Prefix to be added to the builtin's name for lookup. 194*bdd1243dSDimitry Andric // For example, OpenCL "abs" taking an unsigned value has a prefix "u_". 195*bdd1243dSDimitry Andric std::string Prefix; 196*bdd1243dSDimitry Andric 197*bdd1243dSDimitry Andric switch (FirstArgumentType) { 198*bdd1243dSDimitry Andric // Unsigned: 199*bdd1243dSDimitry Andric case 'u': 200*bdd1243dSDimitry Andric if (Set == SPIRV::InstructionSet::OpenCL_std) 201*bdd1243dSDimitry Andric Prefix = "u_"; 202*bdd1243dSDimitry Andric else if (Set == SPIRV::InstructionSet::GLSL_std_450) 203*bdd1243dSDimitry Andric Prefix = "u"; 204*bdd1243dSDimitry Andric break; 205*bdd1243dSDimitry Andric // Signed: 206*bdd1243dSDimitry Andric case 'c': 207*bdd1243dSDimitry Andric case 's': 208*bdd1243dSDimitry Andric case 'i': 209*bdd1243dSDimitry Andric case 'l': 210*bdd1243dSDimitry Andric if (Set == SPIRV::InstructionSet::OpenCL_std) 211*bdd1243dSDimitry Andric Prefix = "s_"; 212*bdd1243dSDimitry Andric else if (Set == SPIRV::InstructionSet::GLSL_std_450) 213*bdd1243dSDimitry Andric Prefix = "s"; 214*bdd1243dSDimitry Andric break; 215*bdd1243dSDimitry Andric // Floating-point: 216*bdd1243dSDimitry Andric case 'f': 217*bdd1243dSDimitry Andric case 'd': 218*bdd1243dSDimitry Andric case 'h': 219*bdd1243dSDimitry Andric if (Set == SPIRV::InstructionSet::OpenCL_std || 220*bdd1243dSDimitry Andric Set == SPIRV::InstructionSet::GLSL_std_450) 221*bdd1243dSDimitry Andric Prefix = "f"; 222*bdd1243dSDimitry Andric break; 223*bdd1243dSDimitry Andric } 224*bdd1243dSDimitry Andric 225*bdd1243dSDimitry Andric // If argument-type name prefix was added, look up the builtin again. 226*bdd1243dSDimitry Andric if (!Prefix.empty() && 227*bdd1243dSDimitry Andric (Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set))) 228*bdd1243dSDimitry Andric return std::make_unique<SPIRV::IncomingCall>( 229*bdd1243dSDimitry Andric BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments); 230*bdd1243dSDimitry Andric 231*bdd1243dSDimitry Andric // If lookup with a prefix failed, find a suffix to be added to the 232*bdd1243dSDimitry Andric // builtin's name for lookup. For example, OpenCL "group_reduce_max" taking 233*bdd1243dSDimitry Andric // an unsigned value has a suffix "u". 234*bdd1243dSDimitry Andric std::string Suffix; 235*bdd1243dSDimitry Andric 236*bdd1243dSDimitry Andric switch (FirstArgumentType) { 237*bdd1243dSDimitry Andric // Unsigned: 238*bdd1243dSDimitry Andric case 'u': 239*bdd1243dSDimitry Andric Suffix = "u"; 240*bdd1243dSDimitry Andric break; 241*bdd1243dSDimitry Andric // Signed: 242*bdd1243dSDimitry Andric case 'c': 243*bdd1243dSDimitry Andric case 's': 244*bdd1243dSDimitry Andric case 'i': 245*bdd1243dSDimitry Andric case 'l': 246*bdd1243dSDimitry Andric Suffix = "s"; 247*bdd1243dSDimitry Andric break; 248*bdd1243dSDimitry Andric // Floating-point: 249*bdd1243dSDimitry Andric case 'f': 250*bdd1243dSDimitry Andric case 'd': 251*bdd1243dSDimitry Andric case 'h': 252*bdd1243dSDimitry Andric Suffix = "f"; 253*bdd1243dSDimitry Andric break; 254*bdd1243dSDimitry Andric } 255*bdd1243dSDimitry Andric 256*bdd1243dSDimitry Andric // If argument-type name suffix was added, look up the builtin again. 257*bdd1243dSDimitry Andric if (!Suffix.empty() && 258*bdd1243dSDimitry Andric (Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set))) 259*bdd1243dSDimitry Andric return std::make_unique<SPIRV::IncomingCall>( 260*bdd1243dSDimitry Andric BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments); 261*bdd1243dSDimitry Andric } 262*bdd1243dSDimitry Andric 263*bdd1243dSDimitry Andric // No builtin with such name was found in the set. 264*bdd1243dSDimitry Andric return nullptr; 265*bdd1243dSDimitry Andric } 266*bdd1243dSDimitry Andric 267*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 268*bdd1243dSDimitry Andric // Helper functions for building misc instructions 269*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 270*bdd1243dSDimitry Andric 271*bdd1243dSDimitry Andric /// Helper function building either a resulting scalar or vector bool register 272*bdd1243dSDimitry Andric /// depending on the expected \p ResultType. 273*bdd1243dSDimitry Andric /// 274*bdd1243dSDimitry Andric /// \returns Tuple of the resulting register and its type. 275*bdd1243dSDimitry Andric static std::tuple<Register, SPIRVType *> 276*bdd1243dSDimitry Andric buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType, 277*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 278*bdd1243dSDimitry Andric LLT Type; 279*bdd1243dSDimitry Andric SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder); 280*bdd1243dSDimitry Andric 281*bdd1243dSDimitry Andric if (ResultType->getOpcode() == SPIRV::OpTypeVector) { 282*bdd1243dSDimitry Andric unsigned VectorElements = ResultType->getOperand(2).getImm(); 283*bdd1243dSDimitry Andric BoolType = 284*bdd1243dSDimitry Andric GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder); 285*bdd1243dSDimitry Andric const FixedVectorType *LLVMVectorType = 286*bdd1243dSDimitry Andric cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType)); 287*bdd1243dSDimitry Andric Type = LLT::vector(LLVMVectorType->getElementCount(), 1); 288*bdd1243dSDimitry Andric } else { 289*bdd1243dSDimitry Andric Type = LLT::scalar(1); 290*bdd1243dSDimitry Andric } 291*bdd1243dSDimitry Andric 292*bdd1243dSDimitry Andric Register ResultRegister = 293*bdd1243dSDimitry Andric MIRBuilder.getMRI()->createGenericVirtualRegister(Type); 294*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF()); 295*bdd1243dSDimitry Andric return std::make_tuple(ResultRegister, BoolType); 296*bdd1243dSDimitry Andric } 297*bdd1243dSDimitry Andric 298*bdd1243dSDimitry Andric /// Helper function for building either a vector or scalar select instruction 299*bdd1243dSDimitry Andric /// depending on the expected \p ResultType. 300*bdd1243dSDimitry Andric static bool buildSelectInst(MachineIRBuilder &MIRBuilder, 301*bdd1243dSDimitry Andric Register ReturnRegister, Register SourceRegister, 302*bdd1243dSDimitry Andric const SPIRVType *ReturnType, 303*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 304*bdd1243dSDimitry Andric Register TrueConst, FalseConst; 305*bdd1243dSDimitry Andric 306*bdd1243dSDimitry Andric if (ReturnType->getOpcode() == SPIRV::OpTypeVector) { 307*bdd1243dSDimitry Andric unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType); 308*bdd1243dSDimitry Andric uint64_t AllOnes = APInt::getAllOnesValue(Bits).getZExtValue(); 309*bdd1243dSDimitry Andric TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType); 310*bdd1243dSDimitry Andric FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType); 311*bdd1243dSDimitry Andric } else { 312*bdd1243dSDimitry Andric TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType); 313*bdd1243dSDimitry Andric FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType); 314*bdd1243dSDimitry Andric } 315*bdd1243dSDimitry Andric return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst, 316*bdd1243dSDimitry Andric FalseConst); 317*bdd1243dSDimitry Andric } 318*bdd1243dSDimitry Andric 319*bdd1243dSDimitry Andric /// Helper function for building a load instruction loading into the 320*bdd1243dSDimitry Andric /// \p DestinationReg. 321*bdd1243dSDimitry Andric static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister, 322*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 323*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, LLT LowLevelType, 324*bdd1243dSDimitry Andric Register DestinationReg = Register(0)) { 325*bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 326*bdd1243dSDimitry Andric if (!DestinationReg.isValid()) { 327*bdd1243dSDimitry Andric DestinationReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 328*bdd1243dSDimitry Andric MRI->setType(DestinationReg, LLT::scalar(32)); 329*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(BaseType, DestinationReg, MIRBuilder.getMF()); 330*bdd1243dSDimitry Andric } 331*bdd1243dSDimitry Andric // TODO: consider using correct address space and alignment (p0 is canonical 332*bdd1243dSDimitry Andric // type for selection though). 333*bdd1243dSDimitry Andric MachinePointerInfo PtrInfo = MachinePointerInfo(); 334*bdd1243dSDimitry Andric MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align()); 335*bdd1243dSDimitry Andric return DestinationReg; 336*bdd1243dSDimitry Andric } 337*bdd1243dSDimitry Andric 338*bdd1243dSDimitry Andric /// Helper function for building a load instruction for loading a builtin global 339*bdd1243dSDimitry Andric /// variable of \p BuiltinValue value. 340*bdd1243dSDimitry Andric static Register buildBuiltinVariableLoad(MachineIRBuilder &MIRBuilder, 341*bdd1243dSDimitry Andric SPIRVType *VariableType, 342*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 343*bdd1243dSDimitry Andric SPIRV::BuiltIn::BuiltIn BuiltinValue, 344*bdd1243dSDimitry Andric LLT LLType, 345*bdd1243dSDimitry Andric Register Reg = Register(0)) { 346*bdd1243dSDimitry Andric Register NewRegister = 347*bdd1243dSDimitry Andric MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass); 348*bdd1243dSDimitry Andric MIRBuilder.getMRI()->setType(NewRegister, 349*bdd1243dSDimitry Andric LLT::pointer(0, GR->getPointerSize())); 350*bdd1243dSDimitry Andric SPIRVType *PtrType = GR->getOrCreateSPIRVPointerType( 351*bdd1243dSDimitry Andric VariableType, MIRBuilder, SPIRV::StorageClass::Input); 352*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF()); 353*bdd1243dSDimitry Andric 354*bdd1243dSDimitry Andric // Set up the global OpVariable with the necessary builtin decorations. 355*bdd1243dSDimitry Andric Register Variable = GR->buildGlobalVariable( 356*bdd1243dSDimitry Andric NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr, 357*bdd1243dSDimitry Andric SPIRV::StorageClass::Input, nullptr, true, true, 358*bdd1243dSDimitry Andric SPIRV::LinkageType::Import, MIRBuilder, false); 359*bdd1243dSDimitry Andric 360*bdd1243dSDimitry Andric // Load the value from the global variable. 361*bdd1243dSDimitry Andric Register LoadedRegister = 362*bdd1243dSDimitry Andric buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg); 363*bdd1243dSDimitry Andric MIRBuilder.getMRI()->setType(LoadedRegister, LLType); 364*bdd1243dSDimitry Andric return LoadedRegister; 365*bdd1243dSDimitry Andric } 366*bdd1243dSDimitry Andric 367*bdd1243dSDimitry Andric /// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg 368*bdd1243dSDimitry Andric /// and its definition, set the new register as a destination of the definition, 369*bdd1243dSDimitry Andric /// assign SPIRVType to both registers. If SpirvTy is provided, use it as 370*bdd1243dSDimitry Andric /// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in 371*bdd1243dSDimitry Andric /// SPIRVPreLegalizer.cpp. 372*bdd1243dSDimitry Andric extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy, 373*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 374*bdd1243dSDimitry Andric MachineIRBuilder &MIB, 375*bdd1243dSDimitry Andric MachineRegisterInfo &MRI); 376*bdd1243dSDimitry Andric 377*bdd1243dSDimitry Andric // TODO: Move to TableGen. 378*bdd1243dSDimitry Andric static SPIRV::MemorySemantics::MemorySemantics 379*bdd1243dSDimitry Andric getSPIRVMemSemantics(std::memory_order MemOrder) { 380*bdd1243dSDimitry Andric switch (MemOrder) { 381*bdd1243dSDimitry Andric case std::memory_order::memory_order_relaxed: 382*bdd1243dSDimitry Andric return SPIRV::MemorySemantics::None; 383*bdd1243dSDimitry Andric case std::memory_order::memory_order_acquire: 384*bdd1243dSDimitry Andric return SPIRV::MemorySemantics::Acquire; 385*bdd1243dSDimitry Andric case std::memory_order::memory_order_release: 386*bdd1243dSDimitry Andric return SPIRV::MemorySemantics::Release; 387*bdd1243dSDimitry Andric case std::memory_order::memory_order_acq_rel: 388*bdd1243dSDimitry Andric return SPIRV::MemorySemantics::AcquireRelease; 389*bdd1243dSDimitry Andric case std::memory_order::memory_order_seq_cst: 390*bdd1243dSDimitry Andric return SPIRV::MemorySemantics::SequentiallyConsistent; 391*bdd1243dSDimitry Andric default: 392*bdd1243dSDimitry Andric llvm_unreachable("Unknown CL memory scope"); 393*bdd1243dSDimitry Andric } 394*bdd1243dSDimitry Andric } 395*bdd1243dSDimitry Andric 396*bdd1243dSDimitry Andric static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) { 397*bdd1243dSDimitry Andric switch (ClScope) { 398*bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_work_item: 399*bdd1243dSDimitry Andric return SPIRV::Scope::Invocation; 400*bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_work_group: 401*bdd1243dSDimitry Andric return SPIRV::Scope::Workgroup; 402*bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_device: 403*bdd1243dSDimitry Andric return SPIRV::Scope::Device; 404*bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_all_svm_devices: 405*bdd1243dSDimitry Andric return SPIRV::Scope::CrossDevice; 406*bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_sub_group: 407*bdd1243dSDimitry Andric return SPIRV::Scope::Subgroup; 408*bdd1243dSDimitry Andric } 409*bdd1243dSDimitry Andric llvm_unreachable("Unknown CL memory scope"); 410*bdd1243dSDimitry Andric } 411*bdd1243dSDimitry Andric 412*bdd1243dSDimitry Andric static Register buildConstantIntReg(uint64_t Val, MachineIRBuilder &MIRBuilder, 413*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 414*bdd1243dSDimitry Andric unsigned BitWidth = 32) { 415*bdd1243dSDimitry Andric SPIRVType *IntType = GR->getOrCreateSPIRVIntegerType(BitWidth, MIRBuilder); 416*bdd1243dSDimitry Andric return GR->buildConstantInt(Val, MIRBuilder, IntType); 417*bdd1243dSDimitry Andric } 418*bdd1243dSDimitry Andric 419*bdd1243dSDimitry Andric static Register buildScopeReg(Register CLScopeRegister, 420*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 421*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 422*bdd1243dSDimitry Andric const MachineRegisterInfo *MRI) { 423*bdd1243dSDimitry Andric auto CLScope = 424*bdd1243dSDimitry Andric static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI)); 425*bdd1243dSDimitry Andric SPIRV::Scope::Scope Scope = getSPIRVScope(CLScope); 426*bdd1243dSDimitry Andric 427*bdd1243dSDimitry Andric if (CLScope == static_cast<unsigned>(Scope)) 428*bdd1243dSDimitry Andric return CLScopeRegister; 429*bdd1243dSDimitry Andric 430*bdd1243dSDimitry Andric return buildConstantIntReg(Scope, MIRBuilder, GR); 431*bdd1243dSDimitry Andric } 432*bdd1243dSDimitry Andric 433*bdd1243dSDimitry Andric static Register buildMemSemanticsReg(Register SemanticsRegister, 434*bdd1243dSDimitry Andric Register PtrRegister, 435*bdd1243dSDimitry Andric const MachineRegisterInfo *MRI, 436*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 437*bdd1243dSDimitry Andric std::memory_order Order = 438*bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI)); 439*bdd1243dSDimitry Andric unsigned Semantics = 440*bdd1243dSDimitry Andric getSPIRVMemSemantics(Order) | 441*bdd1243dSDimitry Andric getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister)); 442*bdd1243dSDimitry Andric 443*bdd1243dSDimitry Andric if (Order == Semantics) 444*bdd1243dSDimitry Andric return SemanticsRegister; 445*bdd1243dSDimitry Andric 446*bdd1243dSDimitry Andric return Register(); 447*bdd1243dSDimitry Andric } 448*bdd1243dSDimitry Andric 449*bdd1243dSDimitry Andric /// Helper function for translating atomic init to OpStore. 450*bdd1243dSDimitry Andric static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call, 451*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder) { 452*bdd1243dSDimitry Andric assert(Call->Arguments.size() == 2 && 453*bdd1243dSDimitry Andric "Need 2 arguments for atomic init translation"); 454*bdd1243dSDimitry Andric 455*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpStore) 456*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 457*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 458*bdd1243dSDimitry Andric return true; 459*bdd1243dSDimitry Andric } 460*bdd1243dSDimitry Andric 461*bdd1243dSDimitry Andric /// Helper function for building an atomic load instruction. 462*bdd1243dSDimitry Andric static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call, 463*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 464*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 465*bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 466*bdd1243dSDimitry Andric // TODO: if true insert call to __translate_ocl_memory_sccope before 467*bdd1243dSDimitry Andric // OpAtomicLoad and the function implementation. We can use Translator's 468*bdd1243dSDimitry Andric // output for transcoding/atomic_explicit_arguments.cl as an example. 469*bdd1243dSDimitry Andric Register ScopeRegister; 470*bdd1243dSDimitry Andric if (Call->Arguments.size() > 1) 471*bdd1243dSDimitry Andric ScopeRegister = Call->Arguments[1]; 472*bdd1243dSDimitry Andric else 473*bdd1243dSDimitry Andric ScopeRegister = buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR); 474*bdd1243dSDimitry Andric 475*bdd1243dSDimitry Andric Register MemSemanticsReg; 476*bdd1243dSDimitry Andric if (Call->Arguments.size() > 2) { 477*bdd1243dSDimitry Andric // TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad. 478*bdd1243dSDimitry Andric MemSemanticsReg = Call->Arguments[2]; 479*bdd1243dSDimitry Andric } else { 480*bdd1243dSDimitry Andric int Semantics = 481*bdd1243dSDimitry Andric SPIRV::MemorySemantics::SequentiallyConsistent | 482*bdd1243dSDimitry Andric getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister)); 483*bdd1243dSDimitry Andric MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR); 484*bdd1243dSDimitry Andric } 485*bdd1243dSDimitry Andric 486*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpAtomicLoad) 487*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 488*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 489*bdd1243dSDimitry Andric .addUse(PtrRegister) 490*bdd1243dSDimitry Andric .addUse(ScopeRegister) 491*bdd1243dSDimitry Andric .addUse(MemSemanticsReg); 492*bdd1243dSDimitry Andric return true; 493*bdd1243dSDimitry Andric } 494*bdd1243dSDimitry Andric 495*bdd1243dSDimitry Andric /// Helper function for building an atomic store instruction. 496*bdd1243dSDimitry Andric static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call, 497*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 498*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 499*bdd1243dSDimitry Andric Register ScopeRegister = 500*bdd1243dSDimitry Andric buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR); 501*bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 502*bdd1243dSDimitry Andric int Semantics = 503*bdd1243dSDimitry Andric SPIRV::MemorySemantics::SequentiallyConsistent | 504*bdd1243dSDimitry Andric getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister)); 505*bdd1243dSDimitry Andric Register MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR); 506*bdd1243dSDimitry Andric 507*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpAtomicStore) 508*bdd1243dSDimitry Andric .addUse(PtrRegister) 509*bdd1243dSDimitry Andric .addUse(ScopeRegister) 510*bdd1243dSDimitry Andric .addUse(MemSemanticsReg) 511*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 512*bdd1243dSDimitry Andric return true; 513*bdd1243dSDimitry Andric } 514*bdd1243dSDimitry Andric 515*bdd1243dSDimitry Andric /// Helper function for building an atomic compare-exchange instruction. 516*bdd1243dSDimitry Andric static bool buildAtomicCompareExchangeInst(const SPIRV::IncomingCall *Call, 517*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 518*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 519*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 520*bdd1243dSDimitry Andric unsigned Opcode = 521*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 522*bdd1243dSDimitry Andric bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg"); 523*bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 524*bdd1243dSDimitry Andric 525*bdd1243dSDimitry Andric Register ObjectPtr = Call->Arguments[0]; // Pointer (volatile A *object.) 526*bdd1243dSDimitry Andric Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected). 527*bdd1243dSDimitry Andric Register Desired = Call->Arguments[2]; // Value (C Desired). 528*bdd1243dSDimitry Andric SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired); 529*bdd1243dSDimitry Andric LLT DesiredLLT = MRI->getType(Desired); 530*bdd1243dSDimitry Andric 531*bdd1243dSDimitry Andric assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() == 532*bdd1243dSDimitry Andric SPIRV::OpTypePointer); 533*bdd1243dSDimitry Andric unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode(); 534*bdd1243dSDimitry Andric assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt 535*bdd1243dSDimitry Andric : ExpectedType == SPIRV::OpTypePointer); 536*bdd1243dSDimitry Andric assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt)); 537*bdd1243dSDimitry Andric 538*bdd1243dSDimitry Andric SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr); 539*bdd1243dSDimitry Andric assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected"); 540*bdd1243dSDimitry Andric auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>( 541*bdd1243dSDimitry Andric SpvObjectPtrTy->getOperand(1).getImm()); 542*bdd1243dSDimitry Andric auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass); 543*bdd1243dSDimitry Andric 544*bdd1243dSDimitry Andric Register MemSemEqualReg; 545*bdd1243dSDimitry Andric Register MemSemUnequalReg; 546*bdd1243dSDimitry Andric uint64_t MemSemEqual = 547*bdd1243dSDimitry Andric IsCmpxchg 548*bdd1243dSDimitry Andric ? SPIRV::MemorySemantics::None 549*bdd1243dSDimitry Andric : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage; 550*bdd1243dSDimitry Andric uint64_t MemSemUnequal = 551*bdd1243dSDimitry Andric IsCmpxchg 552*bdd1243dSDimitry Andric ? SPIRV::MemorySemantics::None 553*bdd1243dSDimitry Andric : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage; 554*bdd1243dSDimitry Andric if (Call->Arguments.size() >= 4) { 555*bdd1243dSDimitry Andric assert(Call->Arguments.size() >= 5 && 556*bdd1243dSDimitry Andric "Need 5+ args for explicit atomic cmpxchg"); 557*bdd1243dSDimitry Andric auto MemOrdEq = 558*bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI)); 559*bdd1243dSDimitry Andric auto MemOrdNeq = 560*bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI)); 561*bdd1243dSDimitry Andric MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage; 562*bdd1243dSDimitry Andric MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage; 563*bdd1243dSDimitry Andric if (MemOrdEq == MemSemEqual) 564*bdd1243dSDimitry Andric MemSemEqualReg = Call->Arguments[3]; 565*bdd1243dSDimitry Andric if (MemOrdNeq == MemSemEqual) 566*bdd1243dSDimitry Andric MemSemUnequalReg = Call->Arguments[4]; 567*bdd1243dSDimitry Andric } 568*bdd1243dSDimitry Andric if (!MemSemEqualReg.isValid()) 569*bdd1243dSDimitry Andric MemSemEqualReg = buildConstantIntReg(MemSemEqual, MIRBuilder, GR); 570*bdd1243dSDimitry Andric if (!MemSemUnequalReg.isValid()) 571*bdd1243dSDimitry Andric MemSemUnequalReg = buildConstantIntReg(MemSemUnequal, MIRBuilder, GR); 572*bdd1243dSDimitry Andric 573*bdd1243dSDimitry Andric Register ScopeReg; 574*bdd1243dSDimitry Andric auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device; 575*bdd1243dSDimitry Andric if (Call->Arguments.size() >= 6) { 576*bdd1243dSDimitry Andric assert(Call->Arguments.size() == 6 && 577*bdd1243dSDimitry Andric "Extra args for explicit atomic cmpxchg"); 578*bdd1243dSDimitry Andric auto ClScope = static_cast<SPIRV::CLMemoryScope>( 579*bdd1243dSDimitry Andric getIConstVal(Call->Arguments[5], MRI)); 580*bdd1243dSDimitry Andric Scope = getSPIRVScope(ClScope); 581*bdd1243dSDimitry Andric if (ClScope == static_cast<unsigned>(Scope)) 582*bdd1243dSDimitry Andric ScopeReg = Call->Arguments[5]; 583*bdd1243dSDimitry Andric } 584*bdd1243dSDimitry Andric if (!ScopeReg.isValid()) 585*bdd1243dSDimitry Andric ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR); 586*bdd1243dSDimitry Andric 587*bdd1243dSDimitry Andric Register Expected = IsCmpxchg 588*bdd1243dSDimitry Andric ? ExpectedArg 589*bdd1243dSDimitry Andric : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder, 590*bdd1243dSDimitry Andric GR, LLT::scalar(32)); 591*bdd1243dSDimitry Andric MRI->setType(Expected, DesiredLLT); 592*bdd1243dSDimitry Andric Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT) 593*bdd1243dSDimitry Andric : Call->ReturnRegister; 594*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF()); 595*bdd1243dSDimitry Andric 596*bdd1243dSDimitry Andric SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder); 597*bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 598*bdd1243dSDimitry Andric .addDef(Tmp) 599*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(IntTy)) 600*bdd1243dSDimitry Andric .addUse(ObjectPtr) 601*bdd1243dSDimitry Andric .addUse(ScopeReg) 602*bdd1243dSDimitry Andric .addUse(MemSemEqualReg) 603*bdd1243dSDimitry Andric .addUse(MemSemUnequalReg) 604*bdd1243dSDimitry Andric .addUse(Desired) 605*bdd1243dSDimitry Andric .addUse(Expected); 606*bdd1243dSDimitry Andric if (!IsCmpxchg) { 607*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp); 608*bdd1243dSDimitry Andric MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected); 609*bdd1243dSDimitry Andric } 610*bdd1243dSDimitry Andric return true; 611*bdd1243dSDimitry Andric } 612*bdd1243dSDimitry Andric 613*bdd1243dSDimitry Andric /// Helper function for building an atomic load instruction. 614*bdd1243dSDimitry Andric static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode, 615*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 616*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 617*bdd1243dSDimitry Andric const MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 618*bdd1243dSDimitry Andric SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup; 619*bdd1243dSDimitry Andric Register ScopeRegister; 620*bdd1243dSDimitry Andric 621*bdd1243dSDimitry Andric if (Call->Arguments.size() >= 4) { 622*bdd1243dSDimitry Andric assert(Call->Arguments.size() == 4 && 623*bdd1243dSDimitry Andric "Too many args for explicit atomic RMW"); 624*bdd1243dSDimitry Andric ScopeRegister = buildScopeReg(Call->Arguments[3], MIRBuilder, GR, MRI); 625*bdd1243dSDimitry Andric } 626*bdd1243dSDimitry Andric 627*bdd1243dSDimitry Andric if (!ScopeRegister.isValid()) 628*bdd1243dSDimitry Andric ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR); 629*bdd1243dSDimitry Andric 630*bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 631*bdd1243dSDimitry Andric unsigned Semantics = SPIRV::MemorySemantics::None; 632*bdd1243dSDimitry Andric Register MemSemanticsReg; 633*bdd1243dSDimitry Andric 634*bdd1243dSDimitry Andric if (Call->Arguments.size() >= 3) 635*bdd1243dSDimitry Andric MemSemanticsReg = 636*bdd1243dSDimitry Andric buildMemSemanticsReg(Call->Arguments[2], PtrRegister, MRI, GR); 637*bdd1243dSDimitry Andric 638*bdd1243dSDimitry Andric if (!MemSemanticsReg.isValid()) 639*bdd1243dSDimitry Andric MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR); 640*bdd1243dSDimitry Andric 641*bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 642*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 643*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 644*bdd1243dSDimitry Andric .addUse(PtrRegister) 645*bdd1243dSDimitry Andric .addUse(ScopeRegister) 646*bdd1243dSDimitry Andric .addUse(MemSemanticsReg) 647*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 648*bdd1243dSDimitry Andric return true; 649*bdd1243dSDimitry Andric } 650*bdd1243dSDimitry Andric 651*bdd1243dSDimitry Andric /// Helper function for building atomic flag instructions (e.g. 652*bdd1243dSDimitry Andric /// OpAtomicFlagTestAndSet). 653*bdd1243dSDimitry Andric static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call, 654*bdd1243dSDimitry Andric unsigned Opcode, MachineIRBuilder &MIRBuilder, 655*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 656*bdd1243dSDimitry Andric const MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 657*bdd1243dSDimitry Andric 658*bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 659*bdd1243dSDimitry Andric unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent; 660*bdd1243dSDimitry Andric Register MemSemanticsReg; 661*bdd1243dSDimitry Andric 662*bdd1243dSDimitry Andric if (Call->Arguments.size() >= 2) 663*bdd1243dSDimitry Andric MemSemanticsReg = 664*bdd1243dSDimitry Andric buildMemSemanticsReg(Call->Arguments[1], PtrRegister, MRI, GR); 665*bdd1243dSDimitry Andric 666*bdd1243dSDimitry Andric if (!MemSemanticsReg.isValid()) 667*bdd1243dSDimitry Andric MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR); 668*bdd1243dSDimitry Andric 669*bdd1243dSDimitry Andric assert((Opcode != SPIRV::OpAtomicFlagClear || 670*bdd1243dSDimitry Andric (Semantics != SPIRV::MemorySemantics::Acquire && 671*bdd1243dSDimitry Andric Semantics != SPIRV::MemorySemantics::AcquireRelease)) && 672*bdd1243dSDimitry Andric "Invalid memory order argument!"); 673*bdd1243dSDimitry Andric 674*bdd1243dSDimitry Andric SPIRV::Scope::Scope Scope = SPIRV::Scope::Device; 675*bdd1243dSDimitry Andric Register ScopeRegister; 676*bdd1243dSDimitry Andric 677*bdd1243dSDimitry Andric if (Call->Arguments.size() >= 3) 678*bdd1243dSDimitry Andric ScopeRegister = buildScopeReg(Call->Arguments[2], MIRBuilder, GR, MRI); 679*bdd1243dSDimitry Andric 680*bdd1243dSDimitry Andric if (!ScopeRegister.isValid()) 681*bdd1243dSDimitry Andric ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR); 682*bdd1243dSDimitry Andric 683*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode); 684*bdd1243dSDimitry Andric if (Opcode == SPIRV::OpAtomicFlagTestAndSet) 685*bdd1243dSDimitry Andric MIB.addDef(Call->ReturnRegister) 686*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 687*bdd1243dSDimitry Andric 688*bdd1243dSDimitry Andric MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg); 689*bdd1243dSDimitry Andric return true; 690*bdd1243dSDimitry Andric } 691*bdd1243dSDimitry Andric 692*bdd1243dSDimitry Andric /// Helper function for building barriers, i.e., memory/control ordering 693*bdd1243dSDimitry Andric /// operations. 694*bdd1243dSDimitry Andric static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode, 695*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 696*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 697*bdd1243dSDimitry Andric const MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 698*bdd1243dSDimitry Andric unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI); 699*bdd1243dSDimitry Andric unsigned MemSemantics = SPIRV::MemorySemantics::None; 700*bdd1243dSDimitry Andric 701*bdd1243dSDimitry Andric if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) 702*bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory; 703*bdd1243dSDimitry Andric 704*bdd1243dSDimitry Andric if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE) 705*bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory; 706*bdd1243dSDimitry Andric 707*bdd1243dSDimitry Andric if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE) 708*bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::ImageMemory; 709*bdd1243dSDimitry Andric 710*bdd1243dSDimitry Andric if (Opcode == SPIRV::OpMemoryBarrier) { 711*bdd1243dSDimitry Andric std::memory_order MemOrder = 712*bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(Call->Arguments[1], MRI)); 713*bdd1243dSDimitry Andric MemSemantics = getSPIRVMemSemantics(MemOrder) | MemSemantics; 714*bdd1243dSDimitry Andric } else { 715*bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent; 716*bdd1243dSDimitry Andric } 717*bdd1243dSDimitry Andric 718*bdd1243dSDimitry Andric Register MemSemanticsReg; 719*bdd1243dSDimitry Andric if (MemFlags == MemSemantics) 720*bdd1243dSDimitry Andric MemSemanticsReg = Call->Arguments[0]; 721*bdd1243dSDimitry Andric else 722*bdd1243dSDimitry Andric MemSemanticsReg = buildConstantIntReg(MemSemantics, MIRBuilder, GR); 723*bdd1243dSDimitry Andric 724*bdd1243dSDimitry Andric Register ScopeReg; 725*bdd1243dSDimitry Andric SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup; 726*bdd1243dSDimitry Andric SPIRV::Scope::Scope MemScope = Scope; 727*bdd1243dSDimitry Andric if (Call->Arguments.size() >= 2) { 728*bdd1243dSDimitry Andric assert( 729*bdd1243dSDimitry Andric ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) || 730*bdd1243dSDimitry Andric (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) && 731*bdd1243dSDimitry Andric "Extra args for explicitly scoped barrier"); 732*bdd1243dSDimitry Andric Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2] 733*bdd1243dSDimitry Andric : Call->Arguments[1]; 734*bdd1243dSDimitry Andric SPIRV::CLMemoryScope CLScope = 735*bdd1243dSDimitry Andric static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI)); 736*bdd1243dSDimitry Andric MemScope = getSPIRVScope(CLScope); 737*bdd1243dSDimitry Andric if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) || 738*bdd1243dSDimitry Andric (Opcode == SPIRV::OpMemoryBarrier)) 739*bdd1243dSDimitry Andric Scope = MemScope; 740*bdd1243dSDimitry Andric 741*bdd1243dSDimitry Andric if (CLScope == static_cast<unsigned>(Scope)) 742*bdd1243dSDimitry Andric ScopeReg = Call->Arguments[1]; 743*bdd1243dSDimitry Andric } 744*bdd1243dSDimitry Andric 745*bdd1243dSDimitry Andric if (!ScopeReg.isValid()) 746*bdd1243dSDimitry Andric ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR); 747*bdd1243dSDimitry Andric 748*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg); 749*bdd1243dSDimitry Andric if (Opcode != SPIRV::OpMemoryBarrier) 750*bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(MemScope, MIRBuilder, GR)); 751*bdd1243dSDimitry Andric MIB.addUse(MemSemanticsReg); 752*bdd1243dSDimitry Andric return true; 753*bdd1243dSDimitry Andric } 754*bdd1243dSDimitry Andric 755*bdd1243dSDimitry Andric static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) { 756*bdd1243dSDimitry Andric switch (dim) { 757*bdd1243dSDimitry Andric case SPIRV::Dim::DIM_1D: 758*bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Buffer: 759*bdd1243dSDimitry Andric return 1; 760*bdd1243dSDimitry Andric case SPIRV::Dim::DIM_2D: 761*bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Cube: 762*bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Rect: 763*bdd1243dSDimitry Andric return 2; 764*bdd1243dSDimitry Andric case SPIRV::Dim::DIM_3D: 765*bdd1243dSDimitry Andric return 3; 766*bdd1243dSDimitry Andric default: 767*bdd1243dSDimitry Andric llvm_unreachable("Cannot get num components for given Dim"); 768*bdd1243dSDimitry Andric } 769*bdd1243dSDimitry Andric } 770*bdd1243dSDimitry Andric 771*bdd1243dSDimitry Andric /// Helper function for obtaining the number of size components. 772*bdd1243dSDimitry Andric static unsigned getNumSizeComponents(SPIRVType *imgType) { 773*bdd1243dSDimitry Andric assert(imgType->getOpcode() == SPIRV::OpTypeImage); 774*bdd1243dSDimitry Andric auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm()); 775*bdd1243dSDimitry Andric unsigned numComps = getNumComponentsForDim(dim); 776*bdd1243dSDimitry Andric bool arrayed = imgType->getOperand(4).getImm() == 1; 777*bdd1243dSDimitry Andric return arrayed ? numComps + 1 : numComps; 778*bdd1243dSDimitry Andric } 779*bdd1243dSDimitry Andric 780*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 781*bdd1243dSDimitry Andric // Implementation functions for each builtin group 782*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 783*bdd1243dSDimitry Andric 784*bdd1243dSDimitry Andric static bool generateExtInst(const SPIRV::IncomingCall *Call, 785*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 786*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 787*bdd1243dSDimitry Andric // Lookup the extended instruction number in the TableGen records. 788*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 789*bdd1243dSDimitry Andric uint32_t Number = 790*bdd1243dSDimitry Andric SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number; 791*bdd1243dSDimitry Andric 792*bdd1243dSDimitry Andric // Build extended instruction. 793*bdd1243dSDimitry Andric auto MIB = 794*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpExtInst) 795*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 796*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 797*bdd1243dSDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std)) 798*bdd1243dSDimitry Andric .addImm(Number); 799*bdd1243dSDimitry Andric 800*bdd1243dSDimitry Andric for (auto Argument : Call->Arguments) 801*bdd1243dSDimitry Andric MIB.addUse(Argument); 802*bdd1243dSDimitry Andric return true; 803*bdd1243dSDimitry Andric } 804*bdd1243dSDimitry Andric 805*bdd1243dSDimitry Andric static bool generateRelationalInst(const SPIRV::IncomingCall *Call, 806*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 807*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 808*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 809*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 810*bdd1243dSDimitry Andric unsigned Opcode = 811*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 812*bdd1243dSDimitry Andric 813*bdd1243dSDimitry Andric Register CompareRegister; 814*bdd1243dSDimitry Andric SPIRVType *RelationType; 815*bdd1243dSDimitry Andric std::tie(CompareRegister, RelationType) = 816*bdd1243dSDimitry Andric buildBoolRegister(MIRBuilder, Call->ReturnType, GR); 817*bdd1243dSDimitry Andric 818*bdd1243dSDimitry Andric // Build relational instruction. 819*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 820*bdd1243dSDimitry Andric .addDef(CompareRegister) 821*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(RelationType)); 822*bdd1243dSDimitry Andric 823*bdd1243dSDimitry Andric for (auto Argument : Call->Arguments) 824*bdd1243dSDimitry Andric MIB.addUse(Argument); 825*bdd1243dSDimitry Andric 826*bdd1243dSDimitry Andric // Build select instruction. 827*bdd1243dSDimitry Andric return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister, 828*bdd1243dSDimitry Andric Call->ReturnType, GR); 829*bdd1243dSDimitry Andric } 830*bdd1243dSDimitry Andric 831*bdd1243dSDimitry Andric static bool generateGroupInst(const SPIRV::IncomingCall *Call, 832*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 833*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 834*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 835*bdd1243dSDimitry Andric const SPIRV::GroupBuiltin *GroupBuiltin = 836*bdd1243dSDimitry Andric SPIRV::lookupGroupBuiltin(Builtin->Name); 837*bdd1243dSDimitry Andric const MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 838*bdd1243dSDimitry Andric Register Arg0; 839*bdd1243dSDimitry Andric if (GroupBuiltin->HasBoolArg) { 840*bdd1243dSDimitry Andric Register ConstRegister = Call->Arguments[0]; 841*bdd1243dSDimitry Andric auto ArgInstruction = getDefInstrMaybeConstant(ConstRegister, MRI); 842*bdd1243dSDimitry Andric // TODO: support non-constant bool values. 843*bdd1243dSDimitry Andric assert(ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT && 844*bdd1243dSDimitry Andric "Only constant bool value args are supported"); 845*bdd1243dSDimitry Andric if (GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode() != 846*bdd1243dSDimitry Andric SPIRV::OpTypeBool) 847*bdd1243dSDimitry Andric Arg0 = GR->buildConstantInt(getIConstVal(ConstRegister, MRI), MIRBuilder, 848*bdd1243dSDimitry Andric GR->getOrCreateSPIRVBoolType(MIRBuilder)); 849*bdd1243dSDimitry Andric } 850*bdd1243dSDimitry Andric 851*bdd1243dSDimitry Andric Register GroupResultRegister = Call->ReturnRegister; 852*bdd1243dSDimitry Andric SPIRVType *GroupResultType = Call->ReturnType; 853*bdd1243dSDimitry Andric 854*bdd1243dSDimitry Andric // TODO: maybe we need to check whether the result type is already boolean 855*bdd1243dSDimitry Andric // and in this case do not insert select instruction. 856*bdd1243dSDimitry Andric const bool HasBoolReturnTy = 857*bdd1243dSDimitry Andric GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny || 858*bdd1243dSDimitry Andric GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical || 859*bdd1243dSDimitry Andric GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract; 860*bdd1243dSDimitry Andric 861*bdd1243dSDimitry Andric if (HasBoolReturnTy) 862*bdd1243dSDimitry Andric std::tie(GroupResultRegister, GroupResultType) = 863*bdd1243dSDimitry Andric buildBoolRegister(MIRBuilder, Call->ReturnType, GR); 864*bdd1243dSDimitry Andric 865*bdd1243dSDimitry Andric auto Scope = Builtin->Name.startswith("sub_group") ? SPIRV::Scope::Subgroup 866*bdd1243dSDimitry Andric : SPIRV::Scope::Workgroup; 867*bdd1243dSDimitry Andric Register ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR); 868*bdd1243dSDimitry Andric 869*bdd1243dSDimitry Andric // Build work/sub group instruction. 870*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode) 871*bdd1243dSDimitry Andric .addDef(GroupResultRegister) 872*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(GroupResultType)) 873*bdd1243dSDimitry Andric .addUse(ScopeRegister); 874*bdd1243dSDimitry Andric 875*bdd1243dSDimitry Andric if (!GroupBuiltin->NoGroupOperation) 876*bdd1243dSDimitry Andric MIB.addImm(GroupBuiltin->GroupOperation); 877*bdd1243dSDimitry Andric if (Call->Arguments.size() > 0) { 878*bdd1243dSDimitry Andric MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]); 879*bdd1243dSDimitry Andric for (unsigned i = 1; i < Call->Arguments.size(); i++) 880*bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[i]); 881*bdd1243dSDimitry Andric } 882*bdd1243dSDimitry Andric 883*bdd1243dSDimitry Andric // Build select instruction. 884*bdd1243dSDimitry Andric if (HasBoolReturnTy) 885*bdd1243dSDimitry Andric buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister, 886*bdd1243dSDimitry Andric Call->ReturnType, GR); 887*bdd1243dSDimitry Andric return true; 888*bdd1243dSDimitry Andric } 889*bdd1243dSDimitry Andric 890*bdd1243dSDimitry Andric // These queries ask for a single size_t result for a given dimension index, e.g 891*bdd1243dSDimitry Andric // size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to 892*bdd1243dSDimitry Andric // these values are all vec3 types, so we need to extract the correct index or 893*bdd1243dSDimitry Andric // return defaultVal (0 or 1 depending on the query). We also handle extending 894*bdd1243dSDimitry Andric // or tuncating in case size_t does not match the expected result type's 895*bdd1243dSDimitry Andric // bitwidth. 896*bdd1243dSDimitry Andric // 897*bdd1243dSDimitry Andric // For a constant index >= 3 we generate: 898*bdd1243dSDimitry Andric // %res = OpConstant %SizeT 0 899*bdd1243dSDimitry Andric // 900*bdd1243dSDimitry Andric // For other indices we generate: 901*bdd1243dSDimitry Andric // %g = OpVariable %ptr_V3_SizeT Input 902*bdd1243dSDimitry Andric // OpDecorate %g BuiltIn XXX 903*bdd1243dSDimitry Andric // OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX" 904*bdd1243dSDimitry Andric // OpDecorate %g Constant 905*bdd1243dSDimitry Andric // %loadedVec = OpLoad %V3_SizeT %g 906*bdd1243dSDimitry Andric // 907*bdd1243dSDimitry Andric // Then, if the index is constant < 3, we generate: 908*bdd1243dSDimitry Andric // %res = OpCompositeExtract %SizeT %loadedVec idx 909*bdd1243dSDimitry Andric // If the index is dynamic, we generate: 910*bdd1243dSDimitry Andric // %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx 911*bdd1243dSDimitry Andric // %cmp = OpULessThan %bool %idx %const_3 912*bdd1243dSDimitry Andric // %res = OpSelect %SizeT %cmp %tmp %const_0 913*bdd1243dSDimitry Andric // 914*bdd1243dSDimitry Andric // If the bitwidth of %res does not match the expected return type, we add an 915*bdd1243dSDimitry Andric // extend or truncate. 916*bdd1243dSDimitry Andric static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call, 917*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 918*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 919*bdd1243dSDimitry Andric SPIRV::BuiltIn::BuiltIn BuiltinValue, 920*bdd1243dSDimitry Andric uint64_t DefaultValue) { 921*bdd1243dSDimitry Andric Register IndexRegister = Call->Arguments[0]; 922*bdd1243dSDimitry Andric const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm(); 923*bdd1243dSDimitry Andric const unsigned PointerSize = GR->getPointerSize(); 924*bdd1243dSDimitry Andric const SPIRVType *PointerSizeType = 925*bdd1243dSDimitry Andric GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder); 926*bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 927*bdd1243dSDimitry Andric auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI); 928*bdd1243dSDimitry Andric 929*bdd1243dSDimitry Andric // Set up the final register to do truncation or extension on at the end. 930*bdd1243dSDimitry Andric Register ToTruncate = Call->ReturnRegister; 931*bdd1243dSDimitry Andric 932*bdd1243dSDimitry Andric // If the index is constant, we can statically determine if it is in range. 933*bdd1243dSDimitry Andric bool IsConstantIndex = 934*bdd1243dSDimitry Andric IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT; 935*bdd1243dSDimitry Andric 936*bdd1243dSDimitry Andric // If it's out of range (max dimension is 3), we can just return the constant 937*bdd1243dSDimitry Andric // default value (0 or 1 depending on which query function). 938*bdd1243dSDimitry Andric if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) { 939*bdd1243dSDimitry Andric Register defaultReg = Call->ReturnRegister; 940*bdd1243dSDimitry Andric if (PointerSize != ResultWidth) { 941*bdd1243dSDimitry Andric defaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize)); 942*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeType, defaultReg, 943*bdd1243dSDimitry Andric MIRBuilder.getMF()); 944*bdd1243dSDimitry Andric ToTruncate = defaultReg; 945*bdd1243dSDimitry Andric } 946*bdd1243dSDimitry Andric auto NewRegister = 947*bdd1243dSDimitry Andric GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType); 948*bdd1243dSDimitry Andric MIRBuilder.buildCopy(defaultReg, NewRegister); 949*bdd1243dSDimitry Andric } else { // If it could be in range, we need to load from the given builtin. 950*bdd1243dSDimitry Andric auto Vec3Ty = 951*bdd1243dSDimitry Andric GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder); 952*bdd1243dSDimitry Andric Register LoadedVector = 953*bdd1243dSDimitry Andric buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue, 954*bdd1243dSDimitry Andric LLT::fixed_vector(3, PointerSize)); 955*bdd1243dSDimitry Andric // Set up the vreg to extract the result to (possibly a new temporary one). 956*bdd1243dSDimitry Andric Register Extracted = Call->ReturnRegister; 957*bdd1243dSDimitry Andric if (!IsConstantIndex || PointerSize != ResultWidth) { 958*bdd1243dSDimitry Andric Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize)); 959*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF()); 960*bdd1243dSDimitry Andric } 961*bdd1243dSDimitry Andric // Use Intrinsic::spv_extractelt so dynamic vs static extraction is 962*bdd1243dSDimitry Andric // handled later: extr = spv_extractelt LoadedVector, IndexRegister. 963*bdd1243dSDimitry Andric MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic( 964*bdd1243dSDimitry Andric Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true); 965*bdd1243dSDimitry Andric ExtractInst.addUse(LoadedVector).addUse(IndexRegister); 966*bdd1243dSDimitry Andric 967*bdd1243dSDimitry Andric // If the index is dynamic, need check if it's < 3, and then use a select. 968*bdd1243dSDimitry Andric if (!IsConstantIndex) { 969*bdd1243dSDimitry Andric insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder, 970*bdd1243dSDimitry Andric *MRI); 971*bdd1243dSDimitry Andric 972*bdd1243dSDimitry Andric auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister); 973*bdd1243dSDimitry Andric auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder); 974*bdd1243dSDimitry Andric 975*bdd1243dSDimitry Andric Register CompareRegister = 976*bdd1243dSDimitry Andric MRI->createGenericVirtualRegister(LLT::scalar(1)); 977*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF()); 978*bdd1243dSDimitry Andric 979*bdd1243dSDimitry Andric // Use G_ICMP to check if idxVReg < 3. 980*bdd1243dSDimitry Andric MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister, 981*bdd1243dSDimitry Andric GR->buildConstantInt(3, MIRBuilder, IndexType)); 982*bdd1243dSDimitry Andric 983*bdd1243dSDimitry Andric // Get constant for the default value (0 or 1 depending on which 984*bdd1243dSDimitry Andric // function). 985*bdd1243dSDimitry Andric Register DefaultRegister = 986*bdd1243dSDimitry Andric GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType); 987*bdd1243dSDimitry Andric 988*bdd1243dSDimitry Andric // Get a register for the selection result (possibly a new temporary one). 989*bdd1243dSDimitry Andric Register SelectionResult = Call->ReturnRegister; 990*bdd1243dSDimitry Andric if (PointerSize != ResultWidth) { 991*bdd1243dSDimitry Andric SelectionResult = 992*bdd1243dSDimitry Andric MRI->createGenericVirtualRegister(LLT::scalar(PointerSize)); 993*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult, 994*bdd1243dSDimitry Andric MIRBuilder.getMF()); 995*bdd1243dSDimitry Andric } 996*bdd1243dSDimitry Andric // Create the final G_SELECT to return the extracted value or the default. 997*bdd1243dSDimitry Andric MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted, 998*bdd1243dSDimitry Andric DefaultRegister); 999*bdd1243dSDimitry Andric ToTruncate = SelectionResult; 1000*bdd1243dSDimitry Andric } else { 1001*bdd1243dSDimitry Andric ToTruncate = Extracted; 1002*bdd1243dSDimitry Andric } 1003*bdd1243dSDimitry Andric } 1004*bdd1243dSDimitry Andric // Alter the result's bitwidth if it does not match the SizeT value extracted. 1005*bdd1243dSDimitry Andric if (PointerSize != ResultWidth) 1006*bdd1243dSDimitry Andric MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate); 1007*bdd1243dSDimitry Andric return true; 1008*bdd1243dSDimitry Andric } 1009*bdd1243dSDimitry Andric 1010*bdd1243dSDimitry Andric static bool generateBuiltinVar(const SPIRV::IncomingCall *Call, 1011*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1012*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1013*bdd1243dSDimitry Andric // Lookup the builtin variable record. 1014*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1015*bdd1243dSDimitry Andric SPIRV::BuiltIn::BuiltIn Value = 1016*bdd1243dSDimitry Andric SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value; 1017*bdd1243dSDimitry Andric 1018*bdd1243dSDimitry Andric if (Value == SPIRV::BuiltIn::GlobalInvocationId) 1019*bdd1243dSDimitry Andric return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0); 1020*bdd1243dSDimitry Andric 1021*bdd1243dSDimitry Andric // Build a load instruction for the builtin variable. 1022*bdd1243dSDimitry Andric unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType); 1023*bdd1243dSDimitry Andric LLT LLType; 1024*bdd1243dSDimitry Andric if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector) 1025*bdd1243dSDimitry Andric LLType = 1026*bdd1243dSDimitry Andric LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth); 1027*bdd1243dSDimitry Andric else 1028*bdd1243dSDimitry Andric LLType = LLT::scalar(BitWidth); 1029*bdd1243dSDimitry Andric 1030*bdd1243dSDimitry Andric return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value, 1031*bdd1243dSDimitry Andric LLType, Call->ReturnRegister); 1032*bdd1243dSDimitry Andric } 1033*bdd1243dSDimitry Andric 1034*bdd1243dSDimitry Andric static bool generateAtomicInst(const SPIRV::IncomingCall *Call, 1035*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1036*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1037*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1038*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1039*bdd1243dSDimitry Andric unsigned Opcode = 1040*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1041*bdd1243dSDimitry Andric 1042*bdd1243dSDimitry Andric switch (Opcode) { 1043*bdd1243dSDimitry Andric case SPIRV::OpStore: 1044*bdd1243dSDimitry Andric return buildAtomicInitInst(Call, MIRBuilder); 1045*bdd1243dSDimitry Andric case SPIRV::OpAtomicLoad: 1046*bdd1243dSDimitry Andric return buildAtomicLoadInst(Call, MIRBuilder, GR); 1047*bdd1243dSDimitry Andric case SPIRV::OpAtomicStore: 1048*bdd1243dSDimitry Andric return buildAtomicStoreInst(Call, MIRBuilder, GR); 1049*bdd1243dSDimitry Andric case SPIRV::OpAtomicCompareExchange: 1050*bdd1243dSDimitry Andric case SPIRV::OpAtomicCompareExchangeWeak: 1051*bdd1243dSDimitry Andric return buildAtomicCompareExchangeInst(Call, MIRBuilder, GR); 1052*bdd1243dSDimitry Andric case SPIRV::OpAtomicIAdd: 1053*bdd1243dSDimitry Andric case SPIRV::OpAtomicISub: 1054*bdd1243dSDimitry Andric case SPIRV::OpAtomicOr: 1055*bdd1243dSDimitry Andric case SPIRV::OpAtomicXor: 1056*bdd1243dSDimitry Andric case SPIRV::OpAtomicAnd: 1057*bdd1243dSDimitry Andric case SPIRV::OpAtomicExchange: 1058*bdd1243dSDimitry Andric return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR); 1059*bdd1243dSDimitry Andric case SPIRV::OpMemoryBarrier: 1060*bdd1243dSDimitry Andric return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR); 1061*bdd1243dSDimitry Andric case SPIRV::OpAtomicFlagTestAndSet: 1062*bdd1243dSDimitry Andric case SPIRV::OpAtomicFlagClear: 1063*bdd1243dSDimitry Andric return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR); 1064*bdd1243dSDimitry Andric default: 1065*bdd1243dSDimitry Andric return false; 1066*bdd1243dSDimitry Andric } 1067*bdd1243dSDimitry Andric } 1068*bdd1243dSDimitry Andric 1069*bdd1243dSDimitry Andric static bool generateBarrierInst(const SPIRV::IncomingCall *Call, 1070*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1071*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1072*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1073*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1074*bdd1243dSDimitry Andric unsigned Opcode = 1075*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1076*bdd1243dSDimitry Andric 1077*bdd1243dSDimitry Andric return buildBarrierInst(Call, Opcode, MIRBuilder, GR); 1078*bdd1243dSDimitry Andric } 1079*bdd1243dSDimitry Andric 1080*bdd1243dSDimitry Andric static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call, 1081*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1082*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1083*bdd1243dSDimitry Andric unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode(); 1084*bdd1243dSDimitry Andric bool IsVec = Opcode == SPIRV::OpTypeVector; 1085*bdd1243dSDimitry Andric // Use OpDot only in case of vector args and OpFMul in case of scalar args. 1086*bdd1243dSDimitry Andric MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS) 1087*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1088*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1089*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 1090*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 1091*bdd1243dSDimitry Andric return true; 1092*bdd1243dSDimitry Andric } 1093*bdd1243dSDimitry Andric 1094*bdd1243dSDimitry Andric static bool generateGetQueryInst(const SPIRV::IncomingCall *Call, 1095*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1096*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1097*bdd1243dSDimitry Andric // Lookup the builtin record. 1098*bdd1243dSDimitry Andric SPIRV::BuiltIn::BuiltIn Value = 1099*bdd1243dSDimitry Andric SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value; 1100*bdd1243dSDimitry Andric uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize || 1101*bdd1243dSDimitry Andric Value == SPIRV::BuiltIn::WorkgroupSize || 1102*bdd1243dSDimitry Andric Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize); 1103*bdd1243dSDimitry Andric return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0); 1104*bdd1243dSDimitry Andric } 1105*bdd1243dSDimitry Andric 1106*bdd1243dSDimitry Andric static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call, 1107*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1108*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1109*bdd1243dSDimitry Andric // Lookup the image size query component number in the TableGen records. 1110*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1111*bdd1243dSDimitry Andric uint32_t Component = 1112*bdd1243dSDimitry Andric SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component; 1113*bdd1243dSDimitry Andric // Query result may either be a vector or a scalar. If return type is not a 1114*bdd1243dSDimitry Andric // vector, expect only a single size component. Otherwise get the number of 1115*bdd1243dSDimitry Andric // expected components. 1116*bdd1243dSDimitry Andric SPIRVType *RetTy = Call->ReturnType; 1117*bdd1243dSDimitry Andric unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector 1118*bdd1243dSDimitry Andric ? RetTy->getOperand(2).getImm() 1119*bdd1243dSDimitry Andric : 1; 1120*bdd1243dSDimitry Andric // Get the actual number of query result/size components. 1121*bdd1243dSDimitry Andric SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]); 1122*bdd1243dSDimitry Andric unsigned NumActualRetComponents = getNumSizeComponents(ImgType); 1123*bdd1243dSDimitry Andric Register QueryResult = Call->ReturnRegister; 1124*bdd1243dSDimitry Andric SPIRVType *QueryResultType = Call->ReturnType; 1125*bdd1243dSDimitry Andric if (NumExpectedRetComponents != NumActualRetComponents) { 1126*bdd1243dSDimitry Andric QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister( 1127*bdd1243dSDimitry Andric LLT::fixed_vector(NumActualRetComponents, 32)); 1128*bdd1243dSDimitry Andric SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder); 1129*bdd1243dSDimitry Andric QueryResultType = GR->getOrCreateSPIRVVectorType( 1130*bdd1243dSDimitry Andric IntTy, NumActualRetComponents, MIRBuilder); 1131*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF()); 1132*bdd1243dSDimitry Andric } 1133*bdd1243dSDimitry Andric bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer; 1134*bdd1243dSDimitry Andric unsigned Opcode = 1135*bdd1243dSDimitry Andric IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod; 1136*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 1137*bdd1243dSDimitry Andric .addDef(QueryResult) 1138*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(QueryResultType)) 1139*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]); 1140*bdd1243dSDimitry Andric if (!IsDimBuf) 1141*bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Lod id. 1142*bdd1243dSDimitry Andric if (NumExpectedRetComponents == NumActualRetComponents) 1143*bdd1243dSDimitry Andric return true; 1144*bdd1243dSDimitry Andric if (NumExpectedRetComponents == 1) { 1145*bdd1243dSDimitry Andric // Only 1 component is expected, build OpCompositeExtract instruction. 1146*bdd1243dSDimitry Andric unsigned ExtractedComposite = 1147*bdd1243dSDimitry Andric Component == 3 ? NumActualRetComponents - 1 : Component; 1148*bdd1243dSDimitry Andric assert(ExtractedComposite < NumActualRetComponents && 1149*bdd1243dSDimitry Andric "Invalid composite index!"); 1150*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpCompositeExtract) 1151*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1152*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1153*bdd1243dSDimitry Andric .addUse(QueryResult) 1154*bdd1243dSDimitry Andric .addImm(ExtractedComposite); 1155*bdd1243dSDimitry Andric } else { 1156*bdd1243dSDimitry Andric // More than 1 component is expected, fill a new vector. 1157*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle) 1158*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1159*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1160*bdd1243dSDimitry Andric .addUse(QueryResult) 1161*bdd1243dSDimitry Andric .addUse(QueryResult); 1162*bdd1243dSDimitry Andric for (unsigned i = 0; i < NumExpectedRetComponents; ++i) 1163*bdd1243dSDimitry Andric MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff); 1164*bdd1243dSDimitry Andric } 1165*bdd1243dSDimitry Andric return true; 1166*bdd1243dSDimitry Andric } 1167*bdd1243dSDimitry Andric 1168*bdd1243dSDimitry Andric static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call, 1169*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1170*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1171*bdd1243dSDimitry Andric assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt && 1172*bdd1243dSDimitry Andric "Image samples query result must be of int type!"); 1173*bdd1243dSDimitry Andric 1174*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1175*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1176*bdd1243dSDimitry Andric unsigned Opcode = 1177*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1178*bdd1243dSDimitry Andric 1179*bdd1243dSDimitry Andric Register Image = Call->Arguments[0]; 1180*bdd1243dSDimitry Andric SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>( 1181*bdd1243dSDimitry Andric GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm()); 1182*bdd1243dSDimitry Andric 1183*bdd1243dSDimitry Andric switch (Opcode) { 1184*bdd1243dSDimitry Andric case SPIRV::OpImageQuerySamples: 1185*bdd1243dSDimitry Andric assert(ImageDimensionality == SPIRV::Dim::DIM_2D && 1186*bdd1243dSDimitry Andric "Image must be of 2D dimensionality"); 1187*bdd1243dSDimitry Andric break; 1188*bdd1243dSDimitry Andric case SPIRV::OpImageQueryLevels: 1189*bdd1243dSDimitry Andric assert((ImageDimensionality == SPIRV::Dim::DIM_1D || 1190*bdd1243dSDimitry Andric ImageDimensionality == SPIRV::Dim::DIM_2D || 1191*bdd1243dSDimitry Andric ImageDimensionality == SPIRV::Dim::DIM_3D || 1192*bdd1243dSDimitry Andric ImageDimensionality == SPIRV::Dim::DIM_Cube) && 1193*bdd1243dSDimitry Andric "Image must be of 1D/2D/3D/Cube dimensionality"); 1194*bdd1243dSDimitry Andric break; 1195*bdd1243dSDimitry Andric } 1196*bdd1243dSDimitry Andric 1197*bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 1198*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1199*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1200*bdd1243dSDimitry Andric .addUse(Image); 1201*bdd1243dSDimitry Andric return true; 1202*bdd1243dSDimitry Andric } 1203*bdd1243dSDimitry Andric 1204*bdd1243dSDimitry Andric // TODO: Move to TableGen. 1205*bdd1243dSDimitry Andric static SPIRV::SamplerAddressingMode::SamplerAddressingMode 1206*bdd1243dSDimitry Andric getSamplerAddressingModeFromBitmask(unsigned Bitmask) { 1207*bdd1243dSDimitry Andric switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) { 1208*bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_CLAMP: 1209*bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::Clamp; 1210*bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE: 1211*bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::ClampToEdge; 1212*bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_REPEAT: 1213*bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::Repeat; 1214*bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT: 1215*bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::RepeatMirrored; 1216*bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_NONE: 1217*bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::None; 1218*bdd1243dSDimitry Andric default: 1219*bdd1243dSDimitry Andric llvm_unreachable("Unknown CL address mode"); 1220*bdd1243dSDimitry Andric } 1221*bdd1243dSDimitry Andric } 1222*bdd1243dSDimitry Andric 1223*bdd1243dSDimitry Andric static unsigned getSamplerParamFromBitmask(unsigned Bitmask) { 1224*bdd1243dSDimitry Andric return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0; 1225*bdd1243dSDimitry Andric } 1226*bdd1243dSDimitry Andric 1227*bdd1243dSDimitry Andric static SPIRV::SamplerFilterMode::SamplerFilterMode 1228*bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(unsigned Bitmask) { 1229*bdd1243dSDimitry Andric if (Bitmask & SPIRV::CLK_FILTER_LINEAR) 1230*bdd1243dSDimitry Andric return SPIRV::SamplerFilterMode::Linear; 1231*bdd1243dSDimitry Andric if (Bitmask & SPIRV::CLK_FILTER_NEAREST) 1232*bdd1243dSDimitry Andric return SPIRV::SamplerFilterMode::Nearest; 1233*bdd1243dSDimitry Andric return SPIRV::SamplerFilterMode::Nearest; 1234*bdd1243dSDimitry Andric } 1235*bdd1243dSDimitry Andric 1236*bdd1243dSDimitry Andric static bool generateReadImageInst(const StringRef DemangledCall, 1237*bdd1243dSDimitry Andric const SPIRV::IncomingCall *Call, 1238*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1239*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1240*bdd1243dSDimitry Andric Register Image = Call->Arguments[0]; 1241*bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1242*bdd1243dSDimitry Andric 1243*bdd1243dSDimitry Andric if (DemangledCall.contains_insensitive("ocl_sampler")) { 1244*bdd1243dSDimitry Andric Register Sampler = Call->Arguments[1]; 1245*bdd1243dSDimitry Andric 1246*bdd1243dSDimitry Andric if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) && 1247*bdd1243dSDimitry Andric getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) { 1248*bdd1243dSDimitry Andric uint64_t SamplerMask = getIConstVal(Sampler, MRI); 1249*bdd1243dSDimitry Andric Sampler = GR->buildConstantSampler( 1250*bdd1243dSDimitry Andric Register(), getSamplerAddressingModeFromBitmask(SamplerMask), 1251*bdd1243dSDimitry Andric getSamplerParamFromBitmask(SamplerMask), 1252*bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder, 1253*bdd1243dSDimitry Andric GR->getSPIRVTypeForVReg(Sampler)); 1254*bdd1243dSDimitry Andric } 1255*bdd1243dSDimitry Andric SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image); 1256*bdd1243dSDimitry Andric SPIRVType *SampledImageType = 1257*bdd1243dSDimitry Andric GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder); 1258*bdd1243dSDimitry Andric Register SampledImage = MRI->createVirtualRegister(&SPIRV::IDRegClass); 1259*bdd1243dSDimitry Andric 1260*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpSampledImage) 1261*bdd1243dSDimitry Andric .addDef(SampledImage) 1262*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(SampledImageType)) 1263*bdd1243dSDimitry Andric .addUse(Image) 1264*bdd1243dSDimitry Andric .addUse(Sampler); 1265*bdd1243dSDimitry Andric 1266*bdd1243dSDimitry Andric Register Lod = GR->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()), 1267*bdd1243dSDimitry Andric MIRBuilder); 1268*bdd1243dSDimitry Andric SPIRVType *TempType = Call->ReturnType; 1269*bdd1243dSDimitry Andric bool NeedsExtraction = false; 1270*bdd1243dSDimitry Andric if (TempType->getOpcode() != SPIRV::OpTypeVector) { 1271*bdd1243dSDimitry Andric TempType = 1272*bdd1243dSDimitry Andric GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder); 1273*bdd1243dSDimitry Andric NeedsExtraction = true; 1274*bdd1243dSDimitry Andric } 1275*bdd1243dSDimitry Andric LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(TempType)); 1276*bdd1243dSDimitry Andric Register TempRegister = MRI->createGenericVirtualRegister(LLType); 1277*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF()); 1278*bdd1243dSDimitry Andric 1279*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod) 1280*bdd1243dSDimitry Andric .addDef(NeedsExtraction ? TempRegister : Call->ReturnRegister) 1281*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(TempType)) 1282*bdd1243dSDimitry Andric .addUse(SampledImage) 1283*bdd1243dSDimitry Andric .addUse(Call->Arguments[2]) // Coordinate. 1284*bdd1243dSDimitry Andric .addImm(SPIRV::ImageOperand::Lod) 1285*bdd1243dSDimitry Andric .addUse(Lod); 1286*bdd1243dSDimitry Andric 1287*bdd1243dSDimitry Andric if (NeedsExtraction) 1288*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpCompositeExtract) 1289*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1290*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1291*bdd1243dSDimitry Andric .addUse(TempRegister) 1292*bdd1243dSDimitry Andric .addImm(0); 1293*bdd1243dSDimitry Andric } else if (DemangledCall.contains_insensitive("msaa")) { 1294*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageRead) 1295*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1296*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1297*bdd1243dSDimitry Andric .addUse(Image) 1298*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) // Coordinate. 1299*bdd1243dSDimitry Andric .addImm(SPIRV::ImageOperand::Sample) 1300*bdd1243dSDimitry Andric .addUse(Call->Arguments[2]); 1301*bdd1243dSDimitry Andric } else { 1302*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageRead) 1303*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1304*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1305*bdd1243dSDimitry Andric .addUse(Image) 1306*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); // Coordinate. 1307*bdd1243dSDimitry Andric } 1308*bdd1243dSDimitry Andric return true; 1309*bdd1243dSDimitry Andric } 1310*bdd1243dSDimitry Andric 1311*bdd1243dSDimitry Andric static bool generateWriteImageInst(const SPIRV::IncomingCall *Call, 1312*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1313*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1314*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageWrite) 1315*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) // Image. 1316*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) // Coordinate. 1317*bdd1243dSDimitry Andric .addUse(Call->Arguments[2]); // Texel. 1318*bdd1243dSDimitry Andric return true; 1319*bdd1243dSDimitry Andric } 1320*bdd1243dSDimitry Andric 1321*bdd1243dSDimitry Andric static bool generateSampleImageInst(const StringRef DemangledCall, 1322*bdd1243dSDimitry Andric const SPIRV::IncomingCall *Call, 1323*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1324*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1325*bdd1243dSDimitry Andric if (Call->Builtin->Name.contains_insensitive( 1326*bdd1243dSDimitry Andric "__translate_sampler_initializer")) { 1327*bdd1243dSDimitry Andric // Build sampler literal. 1328*bdd1243dSDimitry Andric uint64_t Bitmask = getIConstVal(Call->Arguments[0], MIRBuilder.getMRI()); 1329*bdd1243dSDimitry Andric Register Sampler = GR->buildConstantSampler( 1330*bdd1243dSDimitry Andric Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask), 1331*bdd1243dSDimitry Andric getSamplerParamFromBitmask(Bitmask), 1332*bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType); 1333*bdd1243dSDimitry Andric return Sampler.isValid(); 1334*bdd1243dSDimitry Andric } else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) { 1335*bdd1243dSDimitry Andric // Create OpSampledImage. 1336*bdd1243dSDimitry Andric Register Image = Call->Arguments[0]; 1337*bdd1243dSDimitry Andric SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image); 1338*bdd1243dSDimitry Andric SPIRVType *SampledImageType = 1339*bdd1243dSDimitry Andric GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder); 1340*bdd1243dSDimitry Andric Register SampledImage = 1341*bdd1243dSDimitry Andric Call->ReturnRegister.isValid() 1342*bdd1243dSDimitry Andric ? Call->ReturnRegister 1343*bdd1243dSDimitry Andric : MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass); 1344*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpSampledImage) 1345*bdd1243dSDimitry Andric .addDef(SampledImage) 1346*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(SampledImageType)) 1347*bdd1243dSDimitry Andric .addUse(Image) 1348*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); // Sampler. 1349*bdd1243dSDimitry Andric return true; 1350*bdd1243dSDimitry Andric } else if (Call->Builtin->Name.contains_insensitive( 1351*bdd1243dSDimitry Andric "__spirv_ImageSampleExplicitLod")) { 1352*bdd1243dSDimitry Andric // Sample an image using an explicit level of detail. 1353*bdd1243dSDimitry Andric std::string ReturnType = DemangledCall.str(); 1354*bdd1243dSDimitry Andric if (DemangledCall.contains("_R")) { 1355*bdd1243dSDimitry Andric ReturnType = ReturnType.substr(ReturnType.find("_R") + 2); 1356*bdd1243dSDimitry Andric ReturnType = ReturnType.substr(0, ReturnType.find('(')); 1357*bdd1243dSDimitry Andric } 1358*bdd1243dSDimitry Andric SPIRVType *Type = GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder); 1359*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod) 1360*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1361*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Type)) 1362*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) // Image. 1363*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) // Coordinate. 1364*bdd1243dSDimitry Andric .addImm(SPIRV::ImageOperand::Lod) 1365*bdd1243dSDimitry Andric .addUse(Call->Arguments[3]); 1366*bdd1243dSDimitry Andric return true; 1367*bdd1243dSDimitry Andric } 1368*bdd1243dSDimitry Andric return false; 1369*bdd1243dSDimitry Andric } 1370*bdd1243dSDimitry Andric 1371*bdd1243dSDimitry Andric static bool generateSelectInst(const SPIRV::IncomingCall *Call, 1372*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder) { 1373*bdd1243dSDimitry Andric MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0], 1374*bdd1243dSDimitry Andric Call->Arguments[1], Call->Arguments[2]); 1375*bdd1243dSDimitry Andric return true; 1376*bdd1243dSDimitry Andric } 1377*bdd1243dSDimitry Andric 1378*bdd1243dSDimitry Andric static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call, 1379*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1380*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1381*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1382*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1383*bdd1243dSDimitry Andric unsigned Opcode = 1384*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1385*bdd1243dSDimitry Andric const MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1386*bdd1243dSDimitry Andric 1387*bdd1243dSDimitry Andric switch (Opcode) { 1388*bdd1243dSDimitry Andric case SPIRV::OpSpecConstant: { 1389*bdd1243dSDimitry Andric // Build the SpecID decoration. 1390*bdd1243dSDimitry Andric unsigned SpecId = 1391*bdd1243dSDimitry Andric static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI)); 1392*bdd1243dSDimitry Andric buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId, 1393*bdd1243dSDimitry Andric {SpecId}); 1394*bdd1243dSDimitry Andric // Determine the constant MI. 1395*bdd1243dSDimitry Andric Register ConstRegister = Call->Arguments[1]; 1396*bdd1243dSDimitry Andric const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI); 1397*bdd1243dSDimitry Andric assert(Const && 1398*bdd1243dSDimitry Andric (Const->getOpcode() == TargetOpcode::G_CONSTANT || 1399*bdd1243dSDimitry Andric Const->getOpcode() == TargetOpcode::G_FCONSTANT) && 1400*bdd1243dSDimitry Andric "Argument should be either an int or floating-point constant"); 1401*bdd1243dSDimitry Andric // Determine the opcode and built the OpSpec MI. 1402*bdd1243dSDimitry Andric const MachineOperand &ConstOperand = Const->getOperand(1); 1403*bdd1243dSDimitry Andric if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) { 1404*bdd1243dSDimitry Andric assert(ConstOperand.isCImm() && "Int constant operand is expected"); 1405*bdd1243dSDimitry Andric Opcode = ConstOperand.getCImm()->getValue().getZExtValue() 1406*bdd1243dSDimitry Andric ? SPIRV::OpSpecConstantTrue 1407*bdd1243dSDimitry Andric : SPIRV::OpSpecConstantFalse; 1408*bdd1243dSDimitry Andric } 1409*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 1410*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1411*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 1412*bdd1243dSDimitry Andric 1413*bdd1243dSDimitry Andric if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) { 1414*bdd1243dSDimitry Andric if (Const->getOpcode() == TargetOpcode::G_CONSTANT) 1415*bdd1243dSDimitry Andric addNumImm(ConstOperand.getCImm()->getValue(), MIB); 1416*bdd1243dSDimitry Andric else 1417*bdd1243dSDimitry Andric addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB); 1418*bdd1243dSDimitry Andric } 1419*bdd1243dSDimitry Andric return true; 1420*bdd1243dSDimitry Andric } 1421*bdd1243dSDimitry Andric case SPIRV::OpSpecConstantComposite: { 1422*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 1423*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1424*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 1425*bdd1243dSDimitry Andric for (unsigned i = 0; i < Call->Arguments.size(); i++) 1426*bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[i]); 1427*bdd1243dSDimitry Andric return true; 1428*bdd1243dSDimitry Andric } 1429*bdd1243dSDimitry Andric default: 1430*bdd1243dSDimitry Andric return false; 1431*bdd1243dSDimitry Andric } 1432*bdd1243dSDimitry Andric } 1433*bdd1243dSDimitry Andric 1434*bdd1243dSDimitry Andric static MachineInstr *getBlockStructInstr(Register ParamReg, 1435*bdd1243dSDimitry Andric MachineRegisterInfo *MRI) { 1436*bdd1243dSDimitry Andric // We expect the following sequence of instructions: 1437*bdd1243dSDimitry Andric // %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca) 1438*bdd1243dSDimitry Andric // or = G_GLOBAL_VALUE @block_literal_global 1439*bdd1243dSDimitry Andric // %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0 1440*bdd1243dSDimitry Andric // %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN) 1441*bdd1243dSDimitry Andric MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg); 1442*bdd1243dSDimitry Andric assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST && 1443*bdd1243dSDimitry Andric MI->getOperand(1).isReg()); 1444*bdd1243dSDimitry Andric Register BitcastReg = MI->getOperand(1).getReg(); 1445*bdd1243dSDimitry Andric MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg); 1446*bdd1243dSDimitry Andric assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) && 1447*bdd1243dSDimitry Andric BitcastMI->getOperand(2).isReg()); 1448*bdd1243dSDimitry Andric Register ValueReg = BitcastMI->getOperand(2).getReg(); 1449*bdd1243dSDimitry Andric MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg); 1450*bdd1243dSDimitry Andric return ValueMI; 1451*bdd1243dSDimitry Andric } 1452*bdd1243dSDimitry Andric 1453*bdd1243dSDimitry Andric // Return an integer constant corresponding to the given register and 1454*bdd1243dSDimitry Andric // defined in spv_track_constant. 1455*bdd1243dSDimitry Andric // TODO: maybe unify with prelegalizer pass. 1456*bdd1243dSDimitry Andric static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) { 1457*bdd1243dSDimitry Andric MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg); 1458*bdd1243dSDimitry Andric assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) && 1459*bdd1243dSDimitry Andric DefMI->getOperand(2).isReg()); 1460*bdd1243dSDimitry Andric MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg()); 1461*bdd1243dSDimitry Andric assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT && 1462*bdd1243dSDimitry Andric DefMI2->getOperand(1).isCImm()); 1463*bdd1243dSDimitry Andric return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue(); 1464*bdd1243dSDimitry Andric } 1465*bdd1243dSDimitry Andric 1466*bdd1243dSDimitry Andric // Return type of the instruction result from spv_assign_type intrinsic. 1467*bdd1243dSDimitry Andric // TODO: maybe unify with prelegalizer pass. 1468*bdd1243dSDimitry Andric static const Type *getMachineInstrType(MachineInstr *MI) { 1469*bdd1243dSDimitry Andric MachineInstr *NextMI = MI->getNextNode(); 1470*bdd1243dSDimitry Andric if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name)) 1471*bdd1243dSDimitry Andric NextMI = NextMI->getNextNode(); 1472*bdd1243dSDimitry Andric Register ValueReg = MI->getOperand(0).getReg(); 1473*bdd1243dSDimitry Andric if (!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) || 1474*bdd1243dSDimitry Andric NextMI->getOperand(1).getReg() != ValueReg) 1475*bdd1243dSDimitry Andric return nullptr; 1476*bdd1243dSDimitry Andric Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0); 1477*bdd1243dSDimitry Andric assert(Ty && "Type is expected"); 1478*bdd1243dSDimitry Andric return getTypedPtrEltType(Ty); 1479*bdd1243dSDimitry Andric } 1480*bdd1243dSDimitry Andric 1481*bdd1243dSDimitry Andric static const Type *getBlockStructType(Register ParamReg, 1482*bdd1243dSDimitry Andric MachineRegisterInfo *MRI) { 1483*bdd1243dSDimitry Andric // In principle, this information should be passed to us from Clang via 1484*bdd1243dSDimitry Andric // an elementtype attribute. However, said attribute requires that 1485*bdd1243dSDimitry Andric // the function call be an intrinsic, which is not. Instead, we rely on being 1486*bdd1243dSDimitry Andric // able to trace this to the declaration of a variable: OpenCL C specification 1487*bdd1243dSDimitry Andric // section 6.12.5 should guarantee that we can do this. 1488*bdd1243dSDimitry Andric MachineInstr *MI = getBlockStructInstr(ParamReg, MRI); 1489*bdd1243dSDimitry Andric if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE) 1490*bdd1243dSDimitry Andric return getTypedPtrEltType(MI->getOperand(1).getGlobal()->getType()); 1491*bdd1243dSDimitry Andric assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) && 1492*bdd1243dSDimitry Andric "Blocks in OpenCL C must be traceable to allocation site"); 1493*bdd1243dSDimitry Andric return getMachineInstrType(MI); 1494*bdd1243dSDimitry Andric } 1495*bdd1243dSDimitry Andric 1496*bdd1243dSDimitry Andric // TODO: maybe move to the global register. 1497*bdd1243dSDimitry Andric static SPIRVType * 1498*bdd1243dSDimitry Andric getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder, 1499*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1500*bdd1243dSDimitry Andric LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext(); 1501*bdd1243dSDimitry Andric Type *OpaqueType = StructType::getTypeByName(Context, "spirv.DeviceEvent"); 1502*bdd1243dSDimitry Andric if (!OpaqueType) 1503*bdd1243dSDimitry Andric OpaqueType = StructType::getTypeByName(Context, "opencl.clk_event_t"); 1504*bdd1243dSDimitry Andric if (!OpaqueType) 1505*bdd1243dSDimitry Andric OpaqueType = StructType::create(Context, "spirv.DeviceEvent"); 1506*bdd1243dSDimitry Andric unsigned SC0 = storageClassToAddressSpace(SPIRV::StorageClass::Function); 1507*bdd1243dSDimitry Andric unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic); 1508*bdd1243dSDimitry Andric Type *PtrType = PointerType::get(PointerType::get(OpaqueType, SC0), SC1); 1509*bdd1243dSDimitry Andric return GR->getOrCreateSPIRVType(PtrType, MIRBuilder); 1510*bdd1243dSDimitry Andric } 1511*bdd1243dSDimitry Andric 1512*bdd1243dSDimitry Andric static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call, 1513*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1514*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1515*bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1516*bdd1243dSDimitry Andric const DataLayout &DL = MIRBuilder.getDataLayout(); 1517*bdd1243dSDimitry Andric bool HasEvents = Call->Builtin->Name.find("events") != StringRef::npos; 1518*bdd1243dSDimitry Andric const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder); 1519*bdd1243dSDimitry Andric 1520*bdd1243dSDimitry Andric // Make vararg instructions before OpEnqueueKernel. 1521*bdd1243dSDimitry Andric // Local sizes arguments: Sizes of block invoke arguments. Clang generates 1522*bdd1243dSDimitry Andric // local size operands as an array, so we need to unpack them. 1523*bdd1243dSDimitry Andric SmallVector<Register, 16> LocalSizes; 1524*bdd1243dSDimitry Andric if (Call->Builtin->Name.find("_varargs") != StringRef::npos) { 1525*bdd1243dSDimitry Andric const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6; 1526*bdd1243dSDimitry Andric Register GepReg = Call->Arguments[LocalSizeArrayIdx]; 1527*bdd1243dSDimitry Andric MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg); 1528*bdd1243dSDimitry Andric assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) && 1529*bdd1243dSDimitry Andric GepMI->getOperand(3).isReg()); 1530*bdd1243dSDimitry Andric Register ArrayReg = GepMI->getOperand(3).getReg(); 1531*bdd1243dSDimitry Andric MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg); 1532*bdd1243dSDimitry Andric const Type *LocalSizeTy = getMachineInstrType(ArrayMI); 1533*bdd1243dSDimitry Andric assert(LocalSizeTy && "Local size type is expected"); 1534*bdd1243dSDimitry Andric const uint64_t LocalSizeNum = 1535*bdd1243dSDimitry Andric cast<ArrayType>(LocalSizeTy)->getNumElements(); 1536*bdd1243dSDimitry Andric unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic); 1537*bdd1243dSDimitry Andric const LLT LLType = LLT::pointer(SC, GR->getPointerSize()); 1538*bdd1243dSDimitry Andric const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType( 1539*bdd1243dSDimitry Andric Int32Ty, MIRBuilder, SPIRV::StorageClass::Function); 1540*bdd1243dSDimitry Andric for (unsigned I = 0; I < LocalSizeNum; ++I) { 1541*bdd1243dSDimitry Andric Register Reg = 1542*bdd1243dSDimitry Andric MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass); 1543*bdd1243dSDimitry Andric MIRBuilder.getMRI()->setType(Reg, LLType); 1544*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF()); 1545*bdd1243dSDimitry Andric auto GEPInst = MIRBuilder.buildIntrinsic(Intrinsic::spv_gep, 1546*bdd1243dSDimitry Andric ArrayRef<Register>{Reg}, true); 1547*bdd1243dSDimitry Andric GEPInst 1548*bdd1243dSDimitry Andric .addImm(GepMI->getOperand(2).getImm()) // In bound. 1549*bdd1243dSDimitry Andric .addUse(ArrayMI->getOperand(0).getReg()) // Alloca. 1550*bdd1243dSDimitry Andric .addUse(buildConstantIntReg(0, MIRBuilder, GR)) // Indices. 1551*bdd1243dSDimitry Andric .addUse(buildConstantIntReg(I, MIRBuilder, GR)); 1552*bdd1243dSDimitry Andric LocalSizes.push_back(Reg); 1553*bdd1243dSDimitry Andric } 1554*bdd1243dSDimitry Andric } 1555*bdd1243dSDimitry Andric 1556*bdd1243dSDimitry Andric // SPIRV OpEnqueueKernel instruction has 10+ arguments. 1557*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel) 1558*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1559*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Int32Ty)); 1560*bdd1243dSDimitry Andric 1561*bdd1243dSDimitry Andric // Copy all arguments before block invoke function pointer. 1562*bdd1243dSDimitry Andric const unsigned BlockFIdx = HasEvents ? 6 : 3; 1563*bdd1243dSDimitry Andric for (unsigned i = 0; i < BlockFIdx; i++) 1564*bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[i]); 1565*bdd1243dSDimitry Andric 1566*bdd1243dSDimitry Andric // If there are no event arguments in the original call, add dummy ones. 1567*bdd1243dSDimitry Andric if (!HasEvents) { 1568*bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Dummy num events. 1569*bdd1243dSDimitry Andric Register NullPtr = GR->getOrCreateConstNullPtr( 1570*bdd1243dSDimitry Andric MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR)); 1571*bdd1243dSDimitry Andric MIB.addUse(NullPtr); // Dummy wait events. 1572*bdd1243dSDimitry Andric MIB.addUse(NullPtr); // Dummy ret event. 1573*bdd1243dSDimitry Andric } 1574*bdd1243dSDimitry Andric 1575*bdd1243dSDimitry Andric MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI); 1576*bdd1243dSDimitry Andric assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE); 1577*bdd1243dSDimitry Andric // Invoke: Pointer to invoke function. 1578*bdd1243dSDimitry Andric MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal()); 1579*bdd1243dSDimitry Andric 1580*bdd1243dSDimitry Andric Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1]; 1581*bdd1243dSDimitry Andric // Param: Pointer to block literal. 1582*bdd1243dSDimitry Andric MIB.addUse(BlockLiteralReg); 1583*bdd1243dSDimitry Andric 1584*bdd1243dSDimitry Andric Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI)); 1585*bdd1243dSDimitry Andric // TODO: these numbers should be obtained from block literal structure. 1586*bdd1243dSDimitry Andric // Param Size: Size of block literal structure. 1587*bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(DL.getTypeStoreSize(PType), MIRBuilder, GR)); 1588*bdd1243dSDimitry Andric // Param Aligment: Aligment of block literal structure. 1589*bdd1243dSDimitry Andric MIB.addUse( 1590*bdd1243dSDimitry Andric buildConstantIntReg(DL.getPrefTypeAlignment(PType), MIRBuilder, GR)); 1591*bdd1243dSDimitry Andric 1592*bdd1243dSDimitry Andric for (unsigned i = 0; i < LocalSizes.size(); i++) 1593*bdd1243dSDimitry Andric MIB.addUse(LocalSizes[i]); 1594*bdd1243dSDimitry Andric return true; 1595*bdd1243dSDimitry Andric } 1596*bdd1243dSDimitry Andric 1597*bdd1243dSDimitry Andric static bool generateEnqueueInst(const SPIRV::IncomingCall *Call, 1598*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1599*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1600*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1601*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1602*bdd1243dSDimitry Andric unsigned Opcode = 1603*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1604*bdd1243dSDimitry Andric 1605*bdd1243dSDimitry Andric switch (Opcode) { 1606*bdd1243dSDimitry Andric case SPIRV::OpRetainEvent: 1607*bdd1243dSDimitry Andric case SPIRV::OpReleaseEvent: 1608*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]); 1609*bdd1243dSDimitry Andric case SPIRV::OpCreateUserEvent: 1610*bdd1243dSDimitry Andric case SPIRV::OpGetDefaultQueue: 1611*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 1612*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1613*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 1614*bdd1243dSDimitry Andric case SPIRV::OpIsValidEvent: 1615*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 1616*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1617*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1618*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]); 1619*bdd1243dSDimitry Andric case SPIRV::OpSetUserEventStatus: 1620*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 1621*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 1622*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 1623*bdd1243dSDimitry Andric case SPIRV::OpCaptureEventProfilingInfo: 1624*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 1625*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 1626*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) 1627*bdd1243dSDimitry Andric .addUse(Call->Arguments[2]); 1628*bdd1243dSDimitry Andric case SPIRV::OpBuildNDRange: { 1629*bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1630*bdd1243dSDimitry Andric SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]); 1631*bdd1243dSDimitry Andric assert(PtrType->getOpcode() == SPIRV::OpTypePointer && 1632*bdd1243dSDimitry Andric PtrType->getOperand(2).isReg()); 1633*bdd1243dSDimitry Andric Register TypeReg = PtrType->getOperand(2).getReg(); 1634*bdd1243dSDimitry Andric SPIRVType *StructType = GR->getSPIRVTypeForVReg(TypeReg); 1635*bdd1243dSDimitry Andric Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 1636*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(StructType, TmpReg, MIRBuilder.getMF()); 1637*bdd1243dSDimitry Andric // Skip the first arg, it's the destination pointer. OpBuildNDRange takes 1638*bdd1243dSDimitry Andric // three other arguments, so pass zero constant on absence. 1639*bdd1243dSDimitry Andric unsigned NumArgs = Call->Arguments.size(); 1640*bdd1243dSDimitry Andric assert(NumArgs >= 2); 1641*bdd1243dSDimitry Andric Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2]; 1642*bdd1243dSDimitry Andric Register LocalWorkSize = 1643*bdd1243dSDimitry Andric NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3]; 1644*bdd1243dSDimitry Andric Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1]; 1645*bdd1243dSDimitry Andric if (NumArgs < 4) { 1646*bdd1243dSDimitry Andric Register Const; 1647*bdd1243dSDimitry Andric SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize); 1648*bdd1243dSDimitry Andric if (SpvTy->getOpcode() == SPIRV::OpTypePointer) { 1649*bdd1243dSDimitry Andric MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize); 1650*bdd1243dSDimitry Andric assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) && 1651*bdd1243dSDimitry Andric DefInstr->getOperand(3).isReg()); 1652*bdd1243dSDimitry Andric Register GWSPtr = DefInstr->getOperand(3).getReg(); 1653*bdd1243dSDimitry Andric // TODO: Maybe simplify generation of the type of the fields. 1654*bdd1243dSDimitry Andric unsigned Size = Call->Builtin->Name.equals("ndrange_3D") ? 3 : 2; 1655*bdd1243dSDimitry Andric unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32; 1656*bdd1243dSDimitry Andric Type *BaseTy = IntegerType::get( 1657*bdd1243dSDimitry Andric MIRBuilder.getMF().getFunction().getContext(), BitWidth); 1658*bdd1243dSDimitry Andric Type *FieldTy = ArrayType::get(BaseTy, Size); 1659*bdd1243dSDimitry Andric SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder); 1660*bdd1243dSDimitry Andric GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::IDRegClass); 1661*bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize, 1662*bdd1243dSDimitry Andric MIRBuilder.getMF()); 1663*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpLoad) 1664*bdd1243dSDimitry Andric .addDef(GlobalWorkSize) 1665*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(SpvFieldTy)) 1666*bdd1243dSDimitry Andric .addUse(GWSPtr); 1667*bdd1243dSDimitry Andric Const = GR->getOrCreateConsIntArray(0, MIRBuilder, SpvFieldTy); 1668*bdd1243dSDimitry Andric } else { 1669*bdd1243dSDimitry Andric Const = GR->buildConstantInt(0, MIRBuilder, SpvTy); 1670*bdd1243dSDimitry Andric } 1671*bdd1243dSDimitry Andric if (!LocalWorkSize.isValid()) 1672*bdd1243dSDimitry Andric LocalWorkSize = Const; 1673*bdd1243dSDimitry Andric if (!GlobalWorkOffset.isValid()) 1674*bdd1243dSDimitry Andric GlobalWorkOffset = Const; 1675*bdd1243dSDimitry Andric } 1676*bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 1677*bdd1243dSDimitry Andric .addDef(TmpReg) 1678*bdd1243dSDimitry Andric .addUse(TypeReg) 1679*bdd1243dSDimitry Andric .addUse(GlobalWorkSize) 1680*bdd1243dSDimitry Andric .addUse(LocalWorkSize) 1681*bdd1243dSDimitry Andric .addUse(GlobalWorkOffset); 1682*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(SPIRV::OpStore) 1683*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 1684*bdd1243dSDimitry Andric .addUse(TmpReg); 1685*bdd1243dSDimitry Andric } 1686*bdd1243dSDimitry Andric case SPIRV::OpEnqueueKernel: 1687*bdd1243dSDimitry Andric return buildEnqueueKernel(Call, MIRBuilder, GR); 1688*bdd1243dSDimitry Andric default: 1689*bdd1243dSDimitry Andric return false; 1690*bdd1243dSDimitry Andric } 1691*bdd1243dSDimitry Andric } 1692*bdd1243dSDimitry Andric 1693*bdd1243dSDimitry Andric static bool generateAsyncCopy(const SPIRV::IncomingCall *Call, 1694*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1695*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1696*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1697*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1698*bdd1243dSDimitry Andric unsigned Opcode = 1699*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1700*bdd1243dSDimitry Andric auto Scope = buildConstantIntReg(SPIRV::Scope::Workgroup, MIRBuilder, GR); 1701*bdd1243dSDimitry Andric 1702*bdd1243dSDimitry Andric switch (Opcode) { 1703*bdd1243dSDimitry Andric case SPIRV::OpGroupAsyncCopy: 1704*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 1705*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1706*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1707*bdd1243dSDimitry Andric .addUse(Scope) 1708*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 1709*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) 1710*bdd1243dSDimitry Andric .addUse(Call->Arguments[2]) 1711*bdd1243dSDimitry Andric .addUse(buildConstantIntReg(1, MIRBuilder, GR)) 1712*bdd1243dSDimitry Andric .addUse(Call->Arguments[3]); 1713*bdd1243dSDimitry Andric case SPIRV::OpGroupWaitEvents: 1714*bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 1715*bdd1243dSDimitry Andric .addUse(Scope) 1716*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 1717*bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 1718*bdd1243dSDimitry Andric default: 1719*bdd1243dSDimitry Andric return false; 1720*bdd1243dSDimitry Andric } 1721*bdd1243dSDimitry Andric } 1722*bdd1243dSDimitry Andric 1723*bdd1243dSDimitry Andric static bool generateConvertInst(const StringRef DemangledCall, 1724*bdd1243dSDimitry Andric const SPIRV::IncomingCall *Call, 1725*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1726*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1727*bdd1243dSDimitry Andric // Lookup the conversion builtin in the TableGen records. 1728*bdd1243dSDimitry Andric const SPIRV::ConvertBuiltin *Builtin = 1729*bdd1243dSDimitry Andric SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set); 1730*bdd1243dSDimitry Andric 1731*bdd1243dSDimitry Andric if (Builtin->IsSaturated) 1732*bdd1243dSDimitry Andric buildOpDecorate(Call->ReturnRegister, MIRBuilder, 1733*bdd1243dSDimitry Andric SPIRV::Decoration::SaturatedConversion, {}); 1734*bdd1243dSDimitry Andric if (Builtin->IsRounded) 1735*bdd1243dSDimitry Andric buildOpDecorate(Call->ReturnRegister, MIRBuilder, 1736*bdd1243dSDimitry Andric SPIRV::Decoration::FPRoundingMode, {Builtin->RoundingMode}); 1737*bdd1243dSDimitry Andric 1738*bdd1243dSDimitry Andric unsigned Opcode = SPIRV::OpNop; 1739*bdd1243dSDimitry Andric if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) { 1740*bdd1243dSDimitry Andric // Int -> ... 1741*bdd1243dSDimitry Andric if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) { 1742*bdd1243dSDimitry Andric // Int -> Int 1743*bdd1243dSDimitry Andric if (Builtin->IsSaturated) 1744*bdd1243dSDimitry Andric Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS 1745*bdd1243dSDimitry Andric : SPIRV::OpSatConvertSToU; 1746*bdd1243dSDimitry Andric else 1747*bdd1243dSDimitry Andric Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert 1748*bdd1243dSDimitry Andric : SPIRV::OpSConvert; 1749*bdd1243dSDimitry Andric } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister, 1750*bdd1243dSDimitry Andric SPIRV::OpTypeFloat)) { 1751*bdd1243dSDimitry Andric // Int -> Float 1752*bdd1243dSDimitry Andric bool IsSourceSigned = 1753*bdd1243dSDimitry Andric DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u'; 1754*bdd1243dSDimitry Andric Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF; 1755*bdd1243dSDimitry Andric } 1756*bdd1243dSDimitry Andric } else if (GR->isScalarOrVectorOfType(Call->Arguments[0], 1757*bdd1243dSDimitry Andric SPIRV::OpTypeFloat)) { 1758*bdd1243dSDimitry Andric // Float -> ... 1759*bdd1243dSDimitry Andric if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) 1760*bdd1243dSDimitry Andric // Float -> Int 1761*bdd1243dSDimitry Andric Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS 1762*bdd1243dSDimitry Andric : SPIRV::OpConvertFToU; 1763*bdd1243dSDimitry Andric else if (GR->isScalarOrVectorOfType(Call->ReturnRegister, 1764*bdd1243dSDimitry Andric SPIRV::OpTypeFloat)) 1765*bdd1243dSDimitry Andric // Float -> Float 1766*bdd1243dSDimitry Andric Opcode = SPIRV::OpFConvert; 1767*bdd1243dSDimitry Andric } 1768*bdd1243dSDimitry Andric 1769*bdd1243dSDimitry Andric assert(Opcode != SPIRV::OpNop && 1770*bdd1243dSDimitry Andric "Conversion between the types not implemented!"); 1771*bdd1243dSDimitry Andric 1772*bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 1773*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1774*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1775*bdd1243dSDimitry Andric .addUse(Call->Arguments[0]); 1776*bdd1243dSDimitry Andric return true; 1777*bdd1243dSDimitry Andric } 1778*bdd1243dSDimitry Andric 1779*bdd1243dSDimitry Andric static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call, 1780*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1781*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1782*bdd1243dSDimitry Andric // Lookup the vector load/store builtin in the TableGen records. 1783*bdd1243dSDimitry Andric const SPIRV::VectorLoadStoreBuiltin *Builtin = 1784*bdd1243dSDimitry Andric SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name, 1785*bdd1243dSDimitry Andric Call->Builtin->Set); 1786*bdd1243dSDimitry Andric // Build extended instruction. 1787*bdd1243dSDimitry Andric auto MIB = 1788*bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpExtInst) 1789*bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1790*bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1791*bdd1243dSDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std)) 1792*bdd1243dSDimitry Andric .addImm(Builtin->Number); 1793*bdd1243dSDimitry Andric for (auto Argument : Call->Arguments) 1794*bdd1243dSDimitry Andric MIB.addUse(Argument); 1795*bdd1243dSDimitry Andric 1796*bdd1243dSDimitry Andric // Rounding mode should be passed as a last argument in the MI for builtins 1797*bdd1243dSDimitry Andric // like "vstorea_halfn_r". 1798*bdd1243dSDimitry Andric if (Builtin->IsRounded) 1799*bdd1243dSDimitry Andric MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode)); 1800*bdd1243dSDimitry Andric return true; 1801*bdd1243dSDimitry Andric } 1802*bdd1243dSDimitry Andric 1803*bdd1243dSDimitry Andric static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call, 1804*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1805*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1806*bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1807*bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1808*bdd1243dSDimitry Andric unsigned Opcode = 1809*bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1810*bdd1243dSDimitry Andric bool IsLoad = Opcode == SPIRV::OpLoad; 1811*bdd1243dSDimitry Andric // Build the instruction. 1812*bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode); 1813*bdd1243dSDimitry Andric if (IsLoad) { 1814*bdd1243dSDimitry Andric MIB.addDef(Call->ReturnRegister); 1815*bdd1243dSDimitry Andric MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType)); 1816*bdd1243dSDimitry Andric } 1817*bdd1243dSDimitry Andric // Add a pointer to the value to load/store. 1818*bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[0]); 1819*bdd1243dSDimitry Andric // Add a value to store. 1820*bdd1243dSDimitry Andric if (!IsLoad) 1821*bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[1]); 1822*bdd1243dSDimitry Andric // Add optional memory attributes and an alignment. 1823*bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1824*bdd1243dSDimitry Andric unsigned NumArgs = Call->Arguments.size(); 1825*bdd1243dSDimitry Andric if ((IsLoad && NumArgs >= 2) || NumArgs >= 3) 1826*bdd1243dSDimitry Andric MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI)); 1827*bdd1243dSDimitry Andric if ((IsLoad && NumArgs >= 3) || NumArgs >= 4) 1828*bdd1243dSDimitry Andric MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI)); 1829*bdd1243dSDimitry Andric return true; 1830*bdd1243dSDimitry Andric } 1831*bdd1243dSDimitry Andric 1832*bdd1243dSDimitry Andric /// Lowers a builtin funtion call using the provided \p DemangledCall skeleton 1833*bdd1243dSDimitry Andric /// and external instruction \p Set. 1834*bdd1243dSDimitry Andric namespace SPIRV { 1835*bdd1243dSDimitry Andric std::optional<bool> lowerBuiltin(const StringRef DemangledCall, 1836*bdd1243dSDimitry Andric SPIRV::InstructionSet::InstructionSet Set, 1837*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1838*bdd1243dSDimitry Andric const Register OrigRet, const Type *OrigRetTy, 1839*bdd1243dSDimitry Andric const SmallVectorImpl<Register> &Args, 1840*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1841*bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n"); 1842*bdd1243dSDimitry Andric 1843*bdd1243dSDimitry Andric // SPIR-V type and return register. 1844*bdd1243dSDimitry Andric Register ReturnRegister = OrigRet; 1845*bdd1243dSDimitry Andric SPIRVType *ReturnType = nullptr; 1846*bdd1243dSDimitry Andric if (OrigRetTy && !OrigRetTy->isVoidTy()) { 1847*bdd1243dSDimitry Andric ReturnType = GR->assignTypeToVReg(OrigRetTy, OrigRet, MIRBuilder); 1848*bdd1243dSDimitry Andric } else if (OrigRetTy && OrigRetTy->isVoidTy()) { 1849*bdd1243dSDimitry Andric ReturnRegister = MIRBuilder.getMRI()->createVirtualRegister(&IDRegClass); 1850*bdd1243dSDimitry Andric MIRBuilder.getMRI()->setType(ReturnRegister, LLT::scalar(32)); 1851*bdd1243dSDimitry Andric ReturnType = GR->assignTypeToVReg(OrigRetTy, ReturnRegister, MIRBuilder); 1852*bdd1243dSDimitry Andric } 1853*bdd1243dSDimitry Andric 1854*bdd1243dSDimitry Andric // Lookup the builtin in the TableGen records. 1855*bdd1243dSDimitry Andric std::unique_ptr<const IncomingCall> Call = 1856*bdd1243dSDimitry Andric lookupBuiltin(DemangledCall, Set, ReturnRegister, ReturnType, Args); 1857*bdd1243dSDimitry Andric 1858*bdd1243dSDimitry Andric if (!Call) { 1859*bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Builtin record was not found!\n"); 1860*bdd1243dSDimitry Andric return std::nullopt; 1861*bdd1243dSDimitry Andric } 1862*bdd1243dSDimitry Andric 1863*bdd1243dSDimitry Andric // TODO: check if the provided args meet the builtin requirments. 1864*bdd1243dSDimitry Andric assert(Args.size() >= Call->Builtin->MinNumArgs && 1865*bdd1243dSDimitry Andric "Too few arguments to generate the builtin"); 1866*bdd1243dSDimitry Andric if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs) 1867*bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "More arguments provided than required!\n"); 1868*bdd1243dSDimitry Andric 1869*bdd1243dSDimitry Andric // Match the builtin with implementation based on the grouping. 1870*bdd1243dSDimitry Andric switch (Call->Builtin->Group) { 1871*bdd1243dSDimitry Andric case SPIRV::Extended: 1872*bdd1243dSDimitry Andric return generateExtInst(Call.get(), MIRBuilder, GR); 1873*bdd1243dSDimitry Andric case SPIRV::Relational: 1874*bdd1243dSDimitry Andric return generateRelationalInst(Call.get(), MIRBuilder, GR); 1875*bdd1243dSDimitry Andric case SPIRV::Group: 1876*bdd1243dSDimitry Andric return generateGroupInst(Call.get(), MIRBuilder, GR); 1877*bdd1243dSDimitry Andric case SPIRV::Variable: 1878*bdd1243dSDimitry Andric return generateBuiltinVar(Call.get(), MIRBuilder, GR); 1879*bdd1243dSDimitry Andric case SPIRV::Atomic: 1880*bdd1243dSDimitry Andric return generateAtomicInst(Call.get(), MIRBuilder, GR); 1881*bdd1243dSDimitry Andric case SPIRV::Barrier: 1882*bdd1243dSDimitry Andric return generateBarrierInst(Call.get(), MIRBuilder, GR); 1883*bdd1243dSDimitry Andric case SPIRV::Dot: 1884*bdd1243dSDimitry Andric return generateDotOrFMulInst(Call.get(), MIRBuilder, GR); 1885*bdd1243dSDimitry Andric case SPIRV::GetQuery: 1886*bdd1243dSDimitry Andric return generateGetQueryInst(Call.get(), MIRBuilder, GR); 1887*bdd1243dSDimitry Andric case SPIRV::ImageSizeQuery: 1888*bdd1243dSDimitry Andric return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR); 1889*bdd1243dSDimitry Andric case SPIRV::ImageMiscQuery: 1890*bdd1243dSDimitry Andric return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR); 1891*bdd1243dSDimitry Andric case SPIRV::ReadImage: 1892*bdd1243dSDimitry Andric return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR); 1893*bdd1243dSDimitry Andric case SPIRV::WriteImage: 1894*bdd1243dSDimitry Andric return generateWriteImageInst(Call.get(), MIRBuilder, GR); 1895*bdd1243dSDimitry Andric case SPIRV::SampleImage: 1896*bdd1243dSDimitry Andric return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR); 1897*bdd1243dSDimitry Andric case SPIRV::Select: 1898*bdd1243dSDimitry Andric return generateSelectInst(Call.get(), MIRBuilder); 1899*bdd1243dSDimitry Andric case SPIRV::SpecConstant: 1900*bdd1243dSDimitry Andric return generateSpecConstantInst(Call.get(), MIRBuilder, GR); 1901*bdd1243dSDimitry Andric case SPIRV::Enqueue: 1902*bdd1243dSDimitry Andric return generateEnqueueInst(Call.get(), MIRBuilder, GR); 1903*bdd1243dSDimitry Andric case SPIRV::AsyncCopy: 1904*bdd1243dSDimitry Andric return generateAsyncCopy(Call.get(), MIRBuilder, GR); 1905*bdd1243dSDimitry Andric case SPIRV::Convert: 1906*bdd1243dSDimitry Andric return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR); 1907*bdd1243dSDimitry Andric case SPIRV::VectorLoadStore: 1908*bdd1243dSDimitry Andric return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR); 1909*bdd1243dSDimitry Andric case SPIRV::LoadStore: 1910*bdd1243dSDimitry Andric return generateLoadStoreInst(Call.get(), MIRBuilder, GR); 1911*bdd1243dSDimitry Andric } 1912*bdd1243dSDimitry Andric return false; 1913*bdd1243dSDimitry Andric } 1914*bdd1243dSDimitry Andric 1915*bdd1243dSDimitry Andric struct DemangledType { 1916*bdd1243dSDimitry Andric StringRef Name; 1917*bdd1243dSDimitry Andric uint32_t Opcode; 1918*bdd1243dSDimitry Andric }; 1919*bdd1243dSDimitry Andric 1920*bdd1243dSDimitry Andric #define GET_DemangledTypes_DECL 1921*bdd1243dSDimitry Andric #define GET_DemangledTypes_IMPL 1922*bdd1243dSDimitry Andric 1923*bdd1243dSDimitry Andric struct ImageType { 1924*bdd1243dSDimitry Andric StringRef Name; 1925*bdd1243dSDimitry Andric StringRef SampledType; 1926*bdd1243dSDimitry Andric AccessQualifier::AccessQualifier Qualifier; 1927*bdd1243dSDimitry Andric Dim::Dim Dimensionality; 1928*bdd1243dSDimitry Andric bool Arrayed; 1929*bdd1243dSDimitry Andric bool Depth; 1930*bdd1243dSDimitry Andric bool Multisampled; 1931*bdd1243dSDimitry Andric bool Sampled; 1932*bdd1243dSDimitry Andric ImageFormat::ImageFormat Format; 1933*bdd1243dSDimitry Andric }; 1934*bdd1243dSDimitry Andric 1935*bdd1243dSDimitry Andric struct PipeType { 1936*bdd1243dSDimitry Andric StringRef Name; 1937*bdd1243dSDimitry Andric AccessQualifier::AccessQualifier Qualifier; 1938*bdd1243dSDimitry Andric }; 1939*bdd1243dSDimitry Andric 1940*bdd1243dSDimitry Andric using namespace AccessQualifier; 1941*bdd1243dSDimitry Andric using namespace Dim; 1942*bdd1243dSDimitry Andric using namespace ImageFormat; 1943*bdd1243dSDimitry Andric #define GET_ImageTypes_DECL 1944*bdd1243dSDimitry Andric #define GET_ImageTypes_IMPL 1945*bdd1243dSDimitry Andric #define GET_PipeTypes_DECL 1946*bdd1243dSDimitry Andric #define GET_PipeTypes_IMPL 1947*bdd1243dSDimitry Andric #include "SPIRVGenTables.inc" 1948*bdd1243dSDimitry Andric } // namespace SPIRV 1949*bdd1243dSDimitry Andric 1950*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 1951*bdd1243dSDimitry Andric // Misc functions for parsing builtin types and looking up implementation 1952*bdd1243dSDimitry Andric // details in TableGenerated tables. 1953*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 1954*bdd1243dSDimitry Andric 1955*bdd1243dSDimitry Andric static const SPIRV::DemangledType *findBuiltinType(StringRef Name) { 1956*bdd1243dSDimitry Andric if (Name.startswith("opencl.")) 1957*bdd1243dSDimitry Andric return SPIRV::lookupBuiltinType(Name); 1958*bdd1243dSDimitry Andric if (!Name.startswith("spirv.")) 1959*bdd1243dSDimitry Andric return nullptr; 1960*bdd1243dSDimitry Andric // Some SPIR-V builtin types have a complex list of parameters as part of 1961*bdd1243dSDimitry Andric // their name (e.g. spirv.Image._void_1_0_0_0_0_0_0). Those parameters often 1962*bdd1243dSDimitry Andric // are numeric literals which cannot be easily represented by TableGen 1963*bdd1243dSDimitry Andric // records and should be parsed instead. 1964*bdd1243dSDimitry Andric unsigned BaseTypeNameLength = 1965*bdd1243dSDimitry Andric Name.contains('_') ? Name.find('_') - 1 : Name.size(); 1966*bdd1243dSDimitry Andric return SPIRV::lookupBuiltinType(Name.substr(0, BaseTypeNameLength).str()); 1967*bdd1243dSDimitry Andric } 1968*bdd1243dSDimitry Andric 1969*bdd1243dSDimitry Andric static std::unique_ptr<const SPIRV::ImageType> 1970*bdd1243dSDimitry Andric lookupOrParseBuiltinImageType(StringRef Name) { 1971*bdd1243dSDimitry Andric if (Name.startswith("opencl.")) { 1972*bdd1243dSDimitry Andric // Lookup OpenCL builtin image type lowering details in TableGen records. 1973*bdd1243dSDimitry Andric const SPIRV::ImageType *Record = SPIRV::lookupImageType(Name); 1974*bdd1243dSDimitry Andric return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType(*Record)); 1975*bdd1243dSDimitry Andric } 1976*bdd1243dSDimitry Andric if (!Name.startswith("spirv.")) 1977*bdd1243dSDimitry Andric llvm_unreachable("Unknown builtin image type name/literal"); 1978*bdd1243dSDimitry Andric // Parse the literals of SPIR-V image builtin parameters. The name should 1979*bdd1243dSDimitry Andric // have the following format: 1980*bdd1243dSDimitry Andric // spirv.Image._Type_Dim_Depth_Arrayed_MS_Sampled_ImageFormat_AccessQualifier 1981*bdd1243dSDimitry Andric // e.g. %spirv.Image._void_1_0_0_0_0_0_0 1982*bdd1243dSDimitry Andric StringRef TypeParametersString = Name.substr(strlen("spirv.Image.")); 1983*bdd1243dSDimitry Andric SmallVector<StringRef> TypeParameters; 1984*bdd1243dSDimitry Andric SplitString(TypeParametersString, TypeParameters, "_"); 1985*bdd1243dSDimitry Andric assert(TypeParameters.size() == 8 && 1986*bdd1243dSDimitry Andric "Wrong number of literals in SPIR-V builtin image type"); 1987*bdd1243dSDimitry Andric 1988*bdd1243dSDimitry Andric StringRef SampledType = TypeParameters[0]; 1989*bdd1243dSDimitry Andric unsigned Dim, Depth, Arrayed, Multisampled, Sampled, Format, AccessQual; 1990*bdd1243dSDimitry Andric bool AreParameterLiteralsValid = 1991*bdd1243dSDimitry Andric !(TypeParameters[1].getAsInteger(10, Dim) || 1992*bdd1243dSDimitry Andric TypeParameters[2].getAsInteger(10, Depth) || 1993*bdd1243dSDimitry Andric TypeParameters[3].getAsInteger(10, Arrayed) || 1994*bdd1243dSDimitry Andric TypeParameters[4].getAsInteger(10, Multisampled) || 1995*bdd1243dSDimitry Andric TypeParameters[5].getAsInteger(10, Sampled) || 1996*bdd1243dSDimitry Andric TypeParameters[6].getAsInteger(10, Format) || 1997*bdd1243dSDimitry Andric TypeParameters[7].getAsInteger(10, AccessQual)); 1998*bdd1243dSDimitry Andric assert(AreParameterLiteralsValid && 1999*bdd1243dSDimitry Andric "Invalid format of SPIR-V image type parameter literals."); 2000*bdd1243dSDimitry Andric 2001*bdd1243dSDimitry Andric return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType{ 2002*bdd1243dSDimitry Andric Name, SampledType, SPIRV::AccessQualifier::AccessQualifier(AccessQual), 2003*bdd1243dSDimitry Andric SPIRV::Dim::Dim(Dim), static_cast<bool>(Arrayed), 2004*bdd1243dSDimitry Andric static_cast<bool>(Depth), static_cast<bool>(Multisampled), 2005*bdd1243dSDimitry Andric static_cast<bool>(Sampled), SPIRV::ImageFormat::ImageFormat(Format)}); 2006*bdd1243dSDimitry Andric } 2007*bdd1243dSDimitry Andric 2008*bdd1243dSDimitry Andric static std::unique_ptr<const SPIRV::PipeType> 2009*bdd1243dSDimitry Andric lookupOrParseBuiltinPipeType(StringRef Name) { 2010*bdd1243dSDimitry Andric if (Name.startswith("opencl.")) { 2011*bdd1243dSDimitry Andric // Lookup OpenCL builtin pipe type lowering details in TableGen records. 2012*bdd1243dSDimitry Andric const SPIRV::PipeType *Record = SPIRV::lookupPipeType(Name); 2013*bdd1243dSDimitry Andric return std::unique_ptr<SPIRV::PipeType>(new SPIRV::PipeType(*Record)); 2014*bdd1243dSDimitry Andric } 2015*bdd1243dSDimitry Andric if (!Name.startswith("spirv.")) 2016*bdd1243dSDimitry Andric llvm_unreachable("Unknown builtin pipe type name/literal"); 2017*bdd1243dSDimitry Andric // Parse the access qualifier literal in the name of the SPIR-V pipe type. 2018*bdd1243dSDimitry Andric // The name should have the following format: 2019*bdd1243dSDimitry Andric // spirv.Pipe._AccessQualifier 2020*bdd1243dSDimitry Andric // e.g. %spirv.Pipe._1 2021*bdd1243dSDimitry Andric if (Name.endswith("_0")) 2022*bdd1243dSDimitry Andric return std::unique_ptr<SPIRV::PipeType>( 2023*bdd1243dSDimitry Andric new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadOnly}); 2024*bdd1243dSDimitry Andric if (Name.endswith("_1")) 2025*bdd1243dSDimitry Andric return std::unique_ptr<SPIRV::PipeType>( 2026*bdd1243dSDimitry Andric new SPIRV::PipeType{Name, SPIRV::AccessQualifier::WriteOnly}); 2027*bdd1243dSDimitry Andric if (Name.endswith("_2")) 2028*bdd1243dSDimitry Andric return std::unique_ptr<SPIRV::PipeType>( 2029*bdd1243dSDimitry Andric new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadWrite}); 2030*bdd1243dSDimitry Andric llvm_unreachable("Unknown pipe type access qualifier literal"); 2031*bdd1243dSDimitry Andric } 2032*bdd1243dSDimitry Andric 2033*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 2034*bdd1243dSDimitry Andric // Implementation functions for builtin types. 2035*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 2036*bdd1243dSDimitry Andric 2037*bdd1243dSDimitry Andric static SPIRVType *getNonParametrizedType(const StructType *OpaqueType, 2038*bdd1243dSDimitry Andric const SPIRV::DemangledType *TypeRecord, 2039*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2040*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2041*bdd1243dSDimitry Andric unsigned Opcode = TypeRecord->Opcode; 2042*bdd1243dSDimitry Andric // Create or get an existing type from GlobalRegistry. 2043*bdd1243dSDimitry Andric return GR->getOrCreateOpTypeByOpcode(OpaqueType, MIRBuilder, Opcode); 2044*bdd1243dSDimitry Andric } 2045*bdd1243dSDimitry Andric 2046*bdd1243dSDimitry Andric static SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder, 2047*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2048*bdd1243dSDimitry Andric // Create or get an existing type from GlobalRegistry. 2049*bdd1243dSDimitry Andric return GR->getOrCreateOpTypeSampler(MIRBuilder); 2050*bdd1243dSDimitry Andric } 2051*bdd1243dSDimitry Andric 2052*bdd1243dSDimitry Andric static SPIRVType *getPipeType(const StructType *OpaqueType, 2053*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2054*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2055*bdd1243dSDimitry Andric // Lookup pipe type lowering details in TableGen records or parse the 2056*bdd1243dSDimitry Andric // name/literal for details. 2057*bdd1243dSDimitry Andric std::unique_ptr<const SPIRV::PipeType> Record = 2058*bdd1243dSDimitry Andric lookupOrParseBuiltinPipeType(OpaqueType->getName()); 2059*bdd1243dSDimitry Andric // Create or get an existing type from GlobalRegistry. 2060*bdd1243dSDimitry Andric return GR->getOrCreateOpTypePipe(MIRBuilder, Record.get()->Qualifier); 2061*bdd1243dSDimitry Andric } 2062*bdd1243dSDimitry Andric 2063*bdd1243dSDimitry Andric static SPIRVType * 2064*bdd1243dSDimitry Andric getImageType(const StructType *OpaqueType, 2065*bdd1243dSDimitry Andric SPIRV::AccessQualifier::AccessQualifier AccessQual, 2066*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) { 2067*bdd1243dSDimitry Andric // Lookup image type lowering details in TableGen records or parse the 2068*bdd1243dSDimitry Andric // name/literal for details. 2069*bdd1243dSDimitry Andric std::unique_ptr<const SPIRV::ImageType> Record = 2070*bdd1243dSDimitry Andric lookupOrParseBuiltinImageType(OpaqueType->getName()); 2071*bdd1243dSDimitry Andric 2072*bdd1243dSDimitry Andric SPIRVType *SampledType = 2073*bdd1243dSDimitry Andric GR->getOrCreateSPIRVTypeByName(Record.get()->SampledType, MIRBuilder); 2074*bdd1243dSDimitry Andric return GR->getOrCreateOpTypeImage( 2075*bdd1243dSDimitry Andric MIRBuilder, SampledType, Record.get()->Dimensionality, 2076*bdd1243dSDimitry Andric Record.get()->Depth, Record.get()->Arrayed, Record.get()->Multisampled, 2077*bdd1243dSDimitry Andric Record.get()->Sampled, Record.get()->Format, 2078*bdd1243dSDimitry Andric AccessQual == SPIRV::AccessQualifier::WriteOnly 2079*bdd1243dSDimitry Andric ? SPIRV::AccessQualifier::WriteOnly 2080*bdd1243dSDimitry Andric : Record.get()->Qualifier); 2081*bdd1243dSDimitry Andric } 2082*bdd1243dSDimitry Andric 2083*bdd1243dSDimitry Andric static SPIRVType *getSampledImageType(const StructType *OpaqueType, 2084*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2085*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2086*bdd1243dSDimitry Andric StringRef TypeParametersString = 2087*bdd1243dSDimitry Andric OpaqueType->getName().substr(strlen("spirv.SampledImage.")); 2088*bdd1243dSDimitry Andric LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext(); 2089*bdd1243dSDimitry Andric Type *ImageOpaqueType = StructType::getTypeByName( 2090*bdd1243dSDimitry Andric Context, "spirv.Image." + TypeParametersString.str()); 2091*bdd1243dSDimitry Andric SPIRVType *TargetImageType = 2092*bdd1243dSDimitry Andric GR->getOrCreateSPIRVType(ImageOpaqueType, MIRBuilder); 2093*bdd1243dSDimitry Andric return GR->getOrCreateOpTypeSampledImage(TargetImageType, MIRBuilder); 2094*bdd1243dSDimitry Andric } 2095*bdd1243dSDimitry Andric 2096*bdd1243dSDimitry Andric namespace SPIRV { 2097*bdd1243dSDimitry Andric SPIRVType *lowerBuiltinType(const StructType *OpaqueType, 2098*bdd1243dSDimitry Andric SPIRV::AccessQualifier::AccessQualifier AccessQual, 2099*bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2100*bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2101*bdd1243dSDimitry Andric assert(OpaqueType->hasName() && 2102*bdd1243dSDimitry Andric "Structs representing builtin types must have a parsable name"); 2103*bdd1243dSDimitry Andric unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs(); 2104*bdd1243dSDimitry Andric 2105*bdd1243dSDimitry Andric const StringRef Name = OpaqueType->getName(); 2106*bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n"); 2107*bdd1243dSDimitry Andric 2108*bdd1243dSDimitry Andric // Lookup the demangled builtin type in the TableGen records. 2109*bdd1243dSDimitry Andric const SPIRV::DemangledType *TypeRecord = findBuiltinType(Name); 2110*bdd1243dSDimitry Andric if (!TypeRecord) 2111*bdd1243dSDimitry Andric report_fatal_error("Missing TableGen record for builtin type: " + Name); 2112*bdd1243dSDimitry Andric 2113*bdd1243dSDimitry Andric // "Lower" the BuiltinType into TargetType. The following get<...>Type methods 2114*bdd1243dSDimitry Andric // use the implementation details from TableGen records to either create a new 2115*bdd1243dSDimitry Andric // OpType<...> machine instruction or get an existing equivalent SPIRVType 2116*bdd1243dSDimitry Andric // from GlobalRegistry. 2117*bdd1243dSDimitry Andric SPIRVType *TargetType; 2118*bdd1243dSDimitry Andric switch (TypeRecord->Opcode) { 2119*bdd1243dSDimitry Andric case SPIRV::OpTypeImage: 2120*bdd1243dSDimitry Andric TargetType = getImageType(OpaqueType, AccessQual, MIRBuilder, GR); 2121*bdd1243dSDimitry Andric break; 2122*bdd1243dSDimitry Andric case SPIRV::OpTypePipe: 2123*bdd1243dSDimitry Andric TargetType = getPipeType(OpaqueType, MIRBuilder, GR); 2124*bdd1243dSDimitry Andric break; 2125*bdd1243dSDimitry Andric case SPIRV::OpTypeDeviceEvent: 2126*bdd1243dSDimitry Andric TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder); 2127*bdd1243dSDimitry Andric break; 2128*bdd1243dSDimitry Andric case SPIRV::OpTypeSampler: 2129*bdd1243dSDimitry Andric TargetType = getSamplerType(MIRBuilder, GR); 2130*bdd1243dSDimitry Andric break; 2131*bdd1243dSDimitry Andric case SPIRV::OpTypeSampledImage: 2132*bdd1243dSDimitry Andric TargetType = getSampledImageType(OpaqueType, MIRBuilder, GR); 2133*bdd1243dSDimitry Andric break; 2134*bdd1243dSDimitry Andric default: 2135*bdd1243dSDimitry Andric TargetType = getNonParametrizedType(OpaqueType, TypeRecord, MIRBuilder, GR); 2136*bdd1243dSDimitry Andric break; 2137*bdd1243dSDimitry Andric } 2138*bdd1243dSDimitry Andric 2139*bdd1243dSDimitry Andric // Emit OpName instruction if a new OpType<...> instruction was added 2140*bdd1243dSDimitry Andric // (equivalent type was not found in GlobalRegistry). 2141*bdd1243dSDimitry Andric if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs()) 2142*bdd1243dSDimitry Andric buildOpName(GR->getSPIRVTypeID(TargetType), OpaqueType->getName(), 2143*bdd1243dSDimitry Andric MIRBuilder); 2144*bdd1243dSDimitry Andric 2145*bdd1243dSDimitry Andric return TargetType; 2146*bdd1243dSDimitry Andric } 2147*bdd1243dSDimitry Andric } // namespace SPIRV 2148*bdd1243dSDimitry Andric } // namespace llvm 2149