1bdd1243dSDimitry Andric //===- SPIRVBuiltins.cpp - SPIR-V Built-in Functions ------------*- C++ -*-===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric // 9bdd1243dSDimitry Andric // This file implements lowering builtin function calls and types using their 10bdd1243dSDimitry Andric // demangled names and TableGen records. 11bdd1243dSDimitry Andric // 12bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 13bdd1243dSDimitry Andric 14bdd1243dSDimitry Andric #include "SPIRVBuiltins.h" 15bdd1243dSDimitry Andric #include "SPIRV.h" 16*0fca6ea1SDimitry Andric #include "SPIRVSubtarget.h" 17bdd1243dSDimitry Andric #include "SPIRVUtils.h" 1806c3fb27SDimitry Andric #include "llvm/ADT/StringExtras.h" 19bdd1243dSDimitry Andric #include "llvm/Analysis/ValueTracking.h" 20bdd1243dSDimitry Andric #include "llvm/IR/IntrinsicsSPIRV.h" 21bdd1243dSDimitry Andric #include <string> 22bdd1243dSDimitry Andric #include <tuple> 23bdd1243dSDimitry Andric 24bdd1243dSDimitry Andric #define DEBUG_TYPE "spirv-builtins" 25bdd1243dSDimitry Andric 26bdd1243dSDimitry Andric namespace llvm { 27bdd1243dSDimitry Andric namespace SPIRV { 28bdd1243dSDimitry Andric #define GET_BuiltinGroup_DECL 29bdd1243dSDimitry Andric #include "SPIRVGenTables.inc" 30bdd1243dSDimitry Andric 31bdd1243dSDimitry Andric struct DemangledBuiltin { 32bdd1243dSDimitry Andric StringRef Name; 33bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 34bdd1243dSDimitry Andric BuiltinGroup Group; 35bdd1243dSDimitry Andric uint8_t MinNumArgs; 36bdd1243dSDimitry Andric uint8_t MaxNumArgs; 37bdd1243dSDimitry Andric }; 38bdd1243dSDimitry Andric 39bdd1243dSDimitry Andric #define GET_DemangledBuiltins_DECL 40bdd1243dSDimitry Andric #define GET_DemangledBuiltins_IMPL 41bdd1243dSDimitry Andric 42bdd1243dSDimitry Andric struct IncomingCall { 43bdd1243dSDimitry Andric const std::string BuiltinName; 44bdd1243dSDimitry Andric const DemangledBuiltin *Builtin; 45bdd1243dSDimitry Andric 46bdd1243dSDimitry Andric const Register ReturnRegister; 47bdd1243dSDimitry Andric const SPIRVType *ReturnType; 48bdd1243dSDimitry Andric const SmallVectorImpl<Register> &Arguments; 49bdd1243dSDimitry Andric 50bdd1243dSDimitry Andric IncomingCall(const std::string BuiltinName, const DemangledBuiltin *Builtin, 51bdd1243dSDimitry Andric const Register ReturnRegister, const SPIRVType *ReturnType, 52bdd1243dSDimitry Andric const SmallVectorImpl<Register> &Arguments) 53bdd1243dSDimitry Andric : BuiltinName(BuiltinName), Builtin(Builtin), 54bdd1243dSDimitry Andric ReturnRegister(ReturnRegister), ReturnType(ReturnType), 55bdd1243dSDimitry Andric Arguments(Arguments) {} 56*0fca6ea1SDimitry Andric 57*0fca6ea1SDimitry Andric bool isSpirvOp() const { return BuiltinName.rfind("__spirv_", 0) == 0; } 58bdd1243dSDimitry Andric }; 59bdd1243dSDimitry Andric 60bdd1243dSDimitry Andric struct NativeBuiltin { 61bdd1243dSDimitry Andric StringRef Name; 62bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 63bdd1243dSDimitry Andric uint32_t Opcode; 64bdd1243dSDimitry Andric }; 65bdd1243dSDimitry Andric 66bdd1243dSDimitry Andric #define GET_NativeBuiltins_DECL 67bdd1243dSDimitry Andric #define GET_NativeBuiltins_IMPL 68bdd1243dSDimitry Andric 69bdd1243dSDimitry Andric struct GroupBuiltin { 70bdd1243dSDimitry Andric StringRef Name; 71bdd1243dSDimitry Andric uint32_t Opcode; 72bdd1243dSDimitry Andric uint32_t GroupOperation; 73bdd1243dSDimitry Andric bool IsElect; 74bdd1243dSDimitry Andric bool IsAllOrAny; 75bdd1243dSDimitry Andric bool IsAllEqual; 76bdd1243dSDimitry Andric bool IsBallot; 77bdd1243dSDimitry Andric bool IsInverseBallot; 78bdd1243dSDimitry Andric bool IsBallotBitExtract; 79bdd1243dSDimitry Andric bool IsBallotFindBit; 80bdd1243dSDimitry Andric bool IsLogical; 81bdd1243dSDimitry Andric bool NoGroupOperation; 82bdd1243dSDimitry Andric bool HasBoolArg; 83bdd1243dSDimitry Andric }; 84bdd1243dSDimitry Andric 85bdd1243dSDimitry Andric #define GET_GroupBuiltins_DECL 86bdd1243dSDimitry Andric #define GET_GroupBuiltins_IMPL 87bdd1243dSDimitry Andric 88*0fca6ea1SDimitry Andric struct IntelSubgroupsBuiltin { 89*0fca6ea1SDimitry Andric StringRef Name; 90*0fca6ea1SDimitry Andric uint32_t Opcode; 91*0fca6ea1SDimitry Andric bool IsBlock; 92*0fca6ea1SDimitry Andric bool IsWrite; 93*0fca6ea1SDimitry Andric }; 94*0fca6ea1SDimitry Andric 95*0fca6ea1SDimitry Andric #define GET_IntelSubgroupsBuiltins_DECL 96*0fca6ea1SDimitry Andric #define GET_IntelSubgroupsBuiltins_IMPL 97*0fca6ea1SDimitry Andric 98*0fca6ea1SDimitry Andric struct AtomicFloatingBuiltin { 99*0fca6ea1SDimitry Andric StringRef Name; 100*0fca6ea1SDimitry Andric uint32_t Opcode; 101*0fca6ea1SDimitry Andric }; 102*0fca6ea1SDimitry Andric 103*0fca6ea1SDimitry Andric #define GET_AtomicFloatingBuiltins_DECL 104*0fca6ea1SDimitry Andric #define GET_AtomicFloatingBuiltins_IMPL 105*0fca6ea1SDimitry Andric struct GroupUniformBuiltin { 106*0fca6ea1SDimitry Andric StringRef Name; 107*0fca6ea1SDimitry Andric uint32_t Opcode; 108*0fca6ea1SDimitry Andric bool IsLogical; 109*0fca6ea1SDimitry Andric }; 110*0fca6ea1SDimitry Andric 111*0fca6ea1SDimitry Andric #define GET_GroupUniformBuiltins_DECL 112*0fca6ea1SDimitry Andric #define GET_GroupUniformBuiltins_IMPL 113*0fca6ea1SDimitry Andric 114bdd1243dSDimitry Andric struct GetBuiltin { 115bdd1243dSDimitry Andric StringRef Name; 116bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 117bdd1243dSDimitry Andric BuiltIn::BuiltIn Value; 118bdd1243dSDimitry Andric }; 119bdd1243dSDimitry Andric 120bdd1243dSDimitry Andric using namespace BuiltIn; 121bdd1243dSDimitry Andric #define GET_GetBuiltins_DECL 122bdd1243dSDimitry Andric #define GET_GetBuiltins_IMPL 123bdd1243dSDimitry Andric 124bdd1243dSDimitry Andric struct ImageQueryBuiltin { 125bdd1243dSDimitry Andric StringRef Name; 126bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 127bdd1243dSDimitry Andric uint32_t Component; 128bdd1243dSDimitry Andric }; 129bdd1243dSDimitry Andric 130bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_DECL 131bdd1243dSDimitry Andric #define GET_ImageQueryBuiltins_IMPL 132bdd1243dSDimitry Andric 133bdd1243dSDimitry Andric struct ConvertBuiltin { 134bdd1243dSDimitry Andric StringRef Name; 135bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 136bdd1243dSDimitry Andric bool IsDestinationSigned; 137bdd1243dSDimitry Andric bool IsSaturated; 138bdd1243dSDimitry Andric bool IsRounded; 139*0fca6ea1SDimitry Andric bool IsBfloat16; 140bdd1243dSDimitry Andric FPRoundingMode::FPRoundingMode RoundingMode; 141bdd1243dSDimitry Andric }; 142bdd1243dSDimitry Andric 143bdd1243dSDimitry Andric struct VectorLoadStoreBuiltin { 144bdd1243dSDimitry Andric StringRef Name; 145bdd1243dSDimitry Andric InstructionSet::InstructionSet Set; 146bdd1243dSDimitry Andric uint32_t Number; 147*0fca6ea1SDimitry Andric uint32_t ElementCount; 148bdd1243dSDimitry Andric bool IsRounded; 149bdd1243dSDimitry Andric FPRoundingMode::FPRoundingMode RoundingMode; 150bdd1243dSDimitry Andric }; 151bdd1243dSDimitry Andric 152bdd1243dSDimitry Andric using namespace FPRoundingMode; 153bdd1243dSDimitry Andric #define GET_ConvertBuiltins_DECL 154bdd1243dSDimitry Andric #define GET_ConvertBuiltins_IMPL 155bdd1243dSDimitry Andric 156bdd1243dSDimitry Andric using namespace InstructionSet; 157bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_DECL 158bdd1243dSDimitry Andric #define GET_VectorLoadStoreBuiltins_IMPL 159bdd1243dSDimitry Andric 160bdd1243dSDimitry Andric #define GET_CLMemoryScope_DECL 161bdd1243dSDimitry Andric #define GET_CLSamplerAddressingMode_DECL 162bdd1243dSDimitry Andric #define GET_CLMemoryFenceFlags_DECL 163bdd1243dSDimitry Andric #define GET_ExtendedBuiltins_DECL 164bdd1243dSDimitry Andric #include "SPIRVGenTables.inc" 165bdd1243dSDimitry Andric } // namespace SPIRV 166bdd1243dSDimitry Andric 167bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 168bdd1243dSDimitry Andric // Misc functions for looking up builtins and veryfying requirements using 169bdd1243dSDimitry Andric // TableGen records 170bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 171bdd1243dSDimitry Andric 172*0fca6ea1SDimitry Andric namespace SPIRV { 173*0fca6ea1SDimitry Andric /// Parses the name part of the demangled builtin call. 174*0fca6ea1SDimitry Andric std::string lookupBuiltinNameHelper(StringRef DemangledCall) { 175*0fca6ea1SDimitry Andric const static std::string PassPrefix = "(anonymous namespace)::"; 176*0fca6ea1SDimitry Andric std::string BuiltinName; 177*0fca6ea1SDimitry Andric // Itanium Demangler result may have "(anonymous namespace)::" prefix 178*0fca6ea1SDimitry Andric if (DemangledCall.starts_with(PassPrefix.c_str())) 179*0fca6ea1SDimitry Andric BuiltinName = DemangledCall.substr(PassPrefix.length()); 180*0fca6ea1SDimitry Andric else 181*0fca6ea1SDimitry Andric BuiltinName = DemangledCall; 182bdd1243dSDimitry Andric // Extract the builtin function name and types of arguments from the call 183bdd1243dSDimitry Andric // skeleton. 184*0fca6ea1SDimitry Andric BuiltinName = BuiltinName.substr(0, BuiltinName.find('(')); 185*0fca6ea1SDimitry Andric 186*0fca6ea1SDimitry Andric // Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR 187*0fca6ea1SDimitry Andric if (BuiltinName.rfind("__spirv_ocl_", 0) == 0) 188*0fca6ea1SDimitry Andric BuiltinName = BuiltinName.substr(12); 189bdd1243dSDimitry Andric 190bdd1243dSDimitry Andric // Check if the extracted name contains type information between angle 191bdd1243dSDimitry Andric // brackets. If so, the builtin is an instantiated template - needs to have 192bdd1243dSDimitry Andric // the information after angle brackets and return type removed. 193bdd1243dSDimitry Andric if (BuiltinName.find('<') && BuiltinName.back() == '>') { 194bdd1243dSDimitry Andric BuiltinName = BuiltinName.substr(0, BuiltinName.find('<')); 1955f757f3fSDimitry Andric BuiltinName = BuiltinName.substr(BuiltinName.find_last_of(' ') + 1); 196bdd1243dSDimitry Andric } 197bdd1243dSDimitry Andric 198bdd1243dSDimitry Andric // Check if the extracted name begins with "__spirv_ImageSampleExplicitLod" 199bdd1243dSDimitry Andric // contains return type information at the end "_R<type>", if so extract the 200bdd1243dSDimitry Andric // plain builtin name without the type information. 201bdd1243dSDimitry Andric if (StringRef(BuiltinName).contains("__spirv_ImageSampleExplicitLod") && 202bdd1243dSDimitry Andric StringRef(BuiltinName).contains("_R")) { 203bdd1243dSDimitry Andric BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R")); 204bdd1243dSDimitry Andric } 205bdd1243dSDimitry Andric 206*0fca6ea1SDimitry Andric return BuiltinName; 207*0fca6ea1SDimitry Andric } 208*0fca6ea1SDimitry Andric } // namespace SPIRV 209*0fca6ea1SDimitry Andric 210*0fca6ea1SDimitry Andric /// Looks up the demangled builtin call in the SPIRVBuiltins.td records using 211*0fca6ea1SDimitry Andric /// the provided \p DemangledCall and specified \p Set. 212*0fca6ea1SDimitry Andric /// 213*0fca6ea1SDimitry Andric /// The lookup follows the following algorithm, returning the first successful 214*0fca6ea1SDimitry Andric /// match: 215*0fca6ea1SDimitry Andric /// 1. Search with the plain demangled name (expecting a 1:1 match). 216*0fca6ea1SDimitry Andric /// 2. Search with the prefix before or suffix after the demangled name 217*0fca6ea1SDimitry Andric /// signyfying the type of the first argument. 218*0fca6ea1SDimitry Andric /// 219*0fca6ea1SDimitry Andric /// \returns Wrapper around the demangled call and found builtin definition. 220*0fca6ea1SDimitry Andric static std::unique_ptr<const SPIRV::IncomingCall> 221*0fca6ea1SDimitry Andric lookupBuiltin(StringRef DemangledCall, 222*0fca6ea1SDimitry Andric SPIRV::InstructionSet::InstructionSet Set, 223*0fca6ea1SDimitry Andric Register ReturnRegister, const SPIRVType *ReturnType, 224*0fca6ea1SDimitry Andric const SmallVectorImpl<Register> &Arguments) { 225*0fca6ea1SDimitry Andric std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall); 226*0fca6ea1SDimitry Andric 227bdd1243dSDimitry Andric SmallVector<StringRef, 10> BuiltinArgumentTypes; 228bdd1243dSDimitry Andric StringRef BuiltinArgs = 229bdd1243dSDimitry Andric DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')')); 230bdd1243dSDimitry Andric BuiltinArgs.split(BuiltinArgumentTypes, ',', -1, false); 231bdd1243dSDimitry Andric 232bdd1243dSDimitry Andric // Look up the builtin in the defined set. Start with the plain demangled 233bdd1243dSDimitry Andric // name, expecting a 1:1 match in the defined builtin set. 234bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin; 235bdd1243dSDimitry Andric if ((Builtin = SPIRV::lookupBuiltin(BuiltinName, Set))) 236bdd1243dSDimitry Andric return std::make_unique<SPIRV::IncomingCall>( 237bdd1243dSDimitry Andric BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments); 238bdd1243dSDimitry Andric 239bdd1243dSDimitry Andric // If the initial look up was unsuccessful and the demangled call takes at 240bdd1243dSDimitry Andric // least 1 argument, add a prefix or suffix signifying the type of the first 241bdd1243dSDimitry Andric // argument and repeat the search. 242bdd1243dSDimitry Andric if (BuiltinArgumentTypes.size() >= 1) { 243bdd1243dSDimitry Andric char FirstArgumentType = BuiltinArgumentTypes[0][0]; 244bdd1243dSDimitry Andric // Prefix to be added to the builtin's name for lookup. 245bdd1243dSDimitry Andric // For example, OpenCL "abs" taking an unsigned value has a prefix "u_". 246bdd1243dSDimitry Andric std::string Prefix; 247bdd1243dSDimitry Andric 248bdd1243dSDimitry Andric switch (FirstArgumentType) { 249bdd1243dSDimitry Andric // Unsigned: 250bdd1243dSDimitry Andric case 'u': 251bdd1243dSDimitry Andric if (Set == SPIRV::InstructionSet::OpenCL_std) 252bdd1243dSDimitry Andric Prefix = "u_"; 253bdd1243dSDimitry Andric else if (Set == SPIRV::InstructionSet::GLSL_std_450) 254bdd1243dSDimitry Andric Prefix = "u"; 255bdd1243dSDimitry Andric break; 256bdd1243dSDimitry Andric // Signed: 257bdd1243dSDimitry Andric case 'c': 258bdd1243dSDimitry Andric case 's': 259bdd1243dSDimitry Andric case 'i': 260bdd1243dSDimitry Andric case 'l': 261bdd1243dSDimitry Andric if (Set == SPIRV::InstructionSet::OpenCL_std) 262bdd1243dSDimitry Andric Prefix = "s_"; 263bdd1243dSDimitry Andric else if (Set == SPIRV::InstructionSet::GLSL_std_450) 264bdd1243dSDimitry Andric Prefix = "s"; 265bdd1243dSDimitry Andric break; 266bdd1243dSDimitry Andric // Floating-point: 267bdd1243dSDimitry Andric case 'f': 268bdd1243dSDimitry Andric case 'd': 269bdd1243dSDimitry Andric case 'h': 270bdd1243dSDimitry Andric if (Set == SPIRV::InstructionSet::OpenCL_std || 271bdd1243dSDimitry Andric Set == SPIRV::InstructionSet::GLSL_std_450) 272bdd1243dSDimitry Andric Prefix = "f"; 273bdd1243dSDimitry Andric break; 274bdd1243dSDimitry Andric } 275bdd1243dSDimitry Andric 276bdd1243dSDimitry Andric // If argument-type name prefix was added, look up the builtin again. 277bdd1243dSDimitry Andric if (!Prefix.empty() && 278bdd1243dSDimitry Andric (Builtin = SPIRV::lookupBuiltin(Prefix + BuiltinName, Set))) 279bdd1243dSDimitry Andric return std::make_unique<SPIRV::IncomingCall>( 280bdd1243dSDimitry Andric BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments); 281bdd1243dSDimitry Andric 282bdd1243dSDimitry Andric // If lookup with a prefix failed, find a suffix to be added to the 283bdd1243dSDimitry Andric // builtin's name for lookup. For example, OpenCL "group_reduce_max" taking 284bdd1243dSDimitry Andric // an unsigned value has a suffix "u". 285bdd1243dSDimitry Andric std::string Suffix; 286bdd1243dSDimitry Andric 287bdd1243dSDimitry Andric switch (FirstArgumentType) { 288bdd1243dSDimitry Andric // Unsigned: 289bdd1243dSDimitry Andric case 'u': 290bdd1243dSDimitry Andric Suffix = "u"; 291bdd1243dSDimitry Andric break; 292bdd1243dSDimitry Andric // Signed: 293bdd1243dSDimitry Andric case 'c': 294bdd1243dSDimitry Andric case 's': 295bdd1243dSDimitry Andric case 'i': 296bdd1243dSDimitry Andric case 'l': 297bdd1243dSDimitry Andric Suffix = "s"; 298bdd1243dSDimitry Andric break; 299bdd1243dSDimitry Andric // Floating-point: 300bdd1243dSDimitry Andric case 'f': 301bdd1243dSDimitry Andric case 'd': 302bdd1243dSDimitry Andric case 'h': 303bdd1243dSDimitry Andric Suffix = "f"; 304bdd1243dSDimitry Andric break; 305bdd1243dSDimitry Andric } 306bdd1243dSDimitry Andric 307bdd1243dSDimitry Andric // If argument-type name suffix was added, look up the builtin again. 308bdd1243dSDimitry Andric if (!Suffix.empty() && 309bdd1243dSDimitry Andric (Builtin = SPIRV::lookupBuiltin(BuiltinName + Suffix, Set))) 310bdd1243dSDimitry Andric return std::make_unique<SPIRV::IncomingCall>( 311bdd1243dSDimitry Andric BuiltinName, Builtin, ReturnRegister, ReturnType, Arguments); 312bdd1243dSDimitry Andric } 313bdd1243dSDimitry Andric 314bdd1243dSDimitry Andric // No builtin with such name was found in the set. 315bdd1243dSDimitry Andric return nullptr; 316bdd1243dSDimitry Andric } 317bdd1243dSDimitry Andric 318*0fca6ea1SDimitry Andric static MachineInstr *getBlockStructInstr(Register ParamReg, 319*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI) { 320*0fca6ea1SDimitry Andric // We expect the following sequence of instructions: 321*0fca6ea1SDimitry Andric // %0:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.alloca) 322*0fca6ea1SDimitry Andric // or = G_GLOBAL_VALUE @block_literal_global 323*0fca6ea1SDimitry Andric // %1:_(pN) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.bitcast), %0 324*0fca6ea1SDimitry Andric // %2:_(p4) = G_ADDRSPACE_CAST %1:_(pN) 325*0fca6ea1SDimitry Andric MachineInstr *MI = MRI->getUniqueVRegDef(ParamReg); 326*0fca6ea1SDimitry Andric assert(MI->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST && 327*0fca6ea1SDimitry Andric MI->getOperand(1).isReg()); 328*0fca6ea1SDimitry Andric Register BitcastReg = MI->getOperand(1).getReg(); 329*0fca6ea1SDimitry Andric MachineInstr *BitcastMI = MRI->getUniqueVRegDef(BitcastReg); 330*0fca6ea1SDimitry Andric assert(isSpvIntrinsic(*BitcastMI, Intrinsic::spv_bitcast) && 331*0fca6ea1SDimitry Andric BitcastMI->getOperand(2).isReg()); 332*0fca6ea1SDimitry Andric Register ValueReg = BitcastMI->getOperand(2).getReg(); 333*0fca6ea1SDimitry Andric MachineInstr *ValueMI = MRI->getUniqueVRegDef(ValueReg); 334*0fca6ea1SDimitry Andric return ValueMI; 335*0fca6ea1SDimitry Andric } 336*0fca6ea1SDimitry Andric 337*0fca6ea1SDimitry Andric // Return an integer constant corresponding to the given register and 338*0fca6ea1SDimitry Andric // defined in spv_track_constant. 339*0fca6ea1SDimitry Andric // TODO: maybe unify with prelegalizer pass. 340*0fca6ea1SDimitry Andric static unsigned getConstFromIntrinsic(Register Reg, MachineRegisterInfo *MRI) { 341*0fca6ea1SDimitry Andric MachineInstr *DefMI = MRI->getUniqueVRegDef(Reg); 342*0fca6ea1SDimitry Andric assert(isSpvIntrinsic(*DefMI, Intrinsic::spv_track_constant) && 343*0fca6ea1SDimitry Andric DefMI->getOperand(2).isReg()); 344*0fca6ea1SDimitry Andric MachineInstr *DefMI2 = MRI->getUniqueVRegDef(DefMI->getOperand(2).getReg()); 345*0fca6ea1SDimitry Andric assert(DefMI2->getOpcode() == TargetOpcode::G_CONSTANT && 346*0fca6ea1SDimitry Andric DefMI2->getOperand(1).isCImm()); 347*0fca6ea1SDimitry Andric return DefMI2->getOperand(1).getCImm()->getValue().getZExtValue(); 348*0fca6ea1SDimitry Andric } 349*0fca6ea1SDimitry Andric 350*0fca6ea1SDimitry Andric // Return type of the instruction result from spv_assign_type intrinsic. 351*0fca6ea1SDimitry Andric // TODO: maybe unify with prelegalizer pass. 352*0fca6ea1SDimitry Andric static const Type *getMachineInstrType(MachineInstr *MI) { 353*0fca6ea1SDimitry Andric MachineInstr *NextMI = MI->getNextNode(); 354*0fca6ea1SDimitry Andric if (!NextMI) 355*0fca6ea1SDimitry Andric return nullptr; 356*0fca6ea1SDimitry Andric if (isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_name)) 357*0fca6ea1SDimitry Andric if ((NextMI = NextMI->getNextNode()) == nullptr) 358*0fca6ea1SDimitry Andric return nullptr; 359*0fca6ea1SDimitry Andric Register ValueReg = MI->getOperand(0).getReg(); 360*0fca6ea1SDimitry Andric if ((!isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_type) && 361*0fca6ea1SDimitry Andric !isSpvIntrinsic(*NextMI, Intrinsic::spv_assign_ptr_type)) || 362*0fca6ea1SDimitry Andric NextMI->getOperand(1).getReg() != ValueReg) 363*0fca6ea1SDimitry Andric return nullptr; 364*0fca6ea1SDimitry Andric Type *Ty = getMDOperandAsType(NextMI->getOperand(2).getMetadata(), 0); 365*0fca6ea1SDimitry Andric assert(Ty && "Type is expected"); 366*0fca6ea1SDimitry Andric return Ty; 367*0fca6ea1SDimitry Andric } 368*0fca6ea1SDimitry Andric 369*0fca6ea1SDimitry Andric static const Type *getBlockStructType(Register ParamReg, 370*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI) { 371*0fca6ea1SDimitry Andric // In principle, this information should be passed to us from Clang via 372*0fca6ea1SDimitry Andric // an elementtype attribute. However, said attribute requires that 373*0fca6ea1SDimitry Andric // the function call be an intrinsic, which is not. Instead, we rely on being 374*0fca6ea1SDimitry Andric // able to trace this to the declaration of a variable: OpenCL C specification 375*0fca6ea1SDimitry Andric // section 6.12.5 should guarantee that we can do this. 376*0fca6ea1SDimitry Andric MachineInstr *MI = getBlockStructInstr(ParamReg, MRI); 377*0fca6ea1SDimitry Andric if (MI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE) 378*0fca6ea1SDimitry Andric return MI->getOperand(1).getGlobal()->getType(); 379*0fca6ea1SDimitry Andric assert(isSpvIntrinsic(*MI, Intrinsic::spv_alloca) && 380*0fca6ea1SDimitry Andric "Blocks in OpenCL C must be traceable to allocation site"); 381*0fca6ea1SDimitry Andric return getMachineInstrType(MI); 382*0fca6ea1SDimitry Andric } 383*0fca6ea1SDimitry Andric 384bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 385bdd1243dSDimitry Andric // Helper functions for building misc instructions 386bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 387bdd1243dSDimitry Andric 388bdd1243dSDimitry Andric /// Helper function building either a resulting scalar or vector bool register 389bdd1243dSDimitry Andric /// depending on the expected \p ResultType. 390bdd1243dSDimitry Andric /// 391bdd1243dSDimitry Andric /// \returns Tuple of the resulting register and its type. 392bdd1243dSDimitry Andric static std::tuple<Register, SPIRVType *> 393bdd1243dSDimitry Andric buildBoolRegister(MachineIRBuilder &MIRBuilder, const SPIRVType *ResultType, 394bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 395bdd1243dSDimitry Andric LLT Type; 396bdd1243dSDimitry Andric SPIRVType *BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder); 397bdd1243dSDimitry Andric 398bdd1243dSDimitry Andric if (ResultType->getOpcode() == SPIRV::OpTypeVector) { 399bdd1243dSDimitry Andric unsigned VectorElements = ResultType->getOperand(2).getImm(); 400bdd1243dSDimitry Andric BoolType = 401bdd1243dSDimitry Andric GR->getOrCreateSPIRVVectorType(BoolType, VectorElements, MIRBuilder); 402bdd1243dSDimitry Andric const FixedVectorType *LLVMVectorType = 403bdd1243dSDimitry Andric cast<FixedVectorType>(GR->getTypeForSPIRVType(BoolType)); 404bdd1243dSDimitry Andric Type = LLT::vector(LLVMVectorType->getElementCount(), 1); 405bdd1243dSDimitry Andric } else { 406bdd1243dSDimitry Andric Type = LLT::scalar(1); 407bdd1243dSDimitry Andric } 408bdd1243dSDimitry Andric 409bdd1243dSDimitry Andric Register ResultRegister = 410bdd1243dSDimitry Andric MIRBuilder.getMRI()->createGenericVirtualRegister(Type); 41106c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(ResultRegister, &SPIRV::IDRegClass); 412bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(BoolType, ResultRegister, MIRBuilder.getMF()); 413bdd1243dSDimitry Andric return std::make_tuple(ResultRegister, BoolType); 414bdd1243dSDimitry Andric } 415bdd1243dSDimitry Andric 416bdd1243dSDimitry Andric /// Helper function for building either a vector or scalar select instruction 417bdd1243dSDimitry Andric /// depending on the expected \p ResultType. 418bdd1243dSDimitry Andric static bool buildSelectInst(MachineIRBuilder &MIRBuilder, 419bdd1243dSDimitry Andric Register ReturnRegister, Register SourceRegister, 420bdd1243dSDimitry Andric const SPIRVType *ReturnType, 421bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 422bdd1243dSDimitry Andric Register TrueConst, FalseConst; 423bdd1243dSDimitry Andric 424bdd1243dSDimitry Andric if (ReturnType->getOpcode() == SPIRV::OpTypeVector) { 425bdd1243dSDimitry Andric unsigned Bits = GR->getScalarOrVectorBitWidth(ReturnType); 42606c3fb27SDimitry Andric uint64_t AllOnes = APInt::getAllOnes(Bits).getZExtValue(); 427bdd1243dSDimitry Andric TrueConst = GR->getOrCreateConsIntVector(AllOnes, MIRBuilder, ReturnType); 428bdd1243dSDimitry Andric FalseConst = GR->getOrCreateConsIntVector(0, MIRBuilder, ReturnType); 429bdd1243dSDimitry Andric } else { 430bdd1243dSDimitry Andric TrueConst = GR->buildConstantInt(1, MIRBuilder, ReturnType); 431bdd1243dSDimitry Andric FalseConst = GR->buildConstantInt(0, MIRBuilder, ReturnType); 432bdd1243dSDimitry Andric } 433bdd1243dSDimitry Andric return MIRBuilder.buildSelect(ReturnRegister, SourceRegister, TrueConst, 434bdd1243dSDimitry Andric FalseConst); 435bdd1243dSDimitry Andric } 436bdd1243dSDimitry Andric 437bdd1243dSDimitry Andric /// Helper function for building a load instruction loading into the 438bdd1243dSDimitry Andric /// \p DestinationReg. 439bdd1243dSDimitry Andric static Register buildLoadInst(SPIRVType *BaseType, Register PtrRegister, 440bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 441bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, LLT LowLevelType, 442bdd1243dSDimitry Andric Register DestinationReg = Register(0)) { 443bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 444bdd1243dSDimitry Andric if (!DestinationReg.isValid()) { 445bdd1243dSDimitry Andric DestinationReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 446bdd1243dSDimitry Andric MRI->setType(DestinationReg, LLT::scalar(32)); 447bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(BaseType, DestinationReg, MIRBuilder.getMF()); 448bdd1243dSDimitry Andric } 449bdd1243dSDimitry Andric // TODO: consider using correct address space and alignment (p0 is canonical 450bdd1243dSDimitry Andric // type for selection though). 451bdd1243dSDimitry Andric MachinePointerInfo PtrInfo = MachinePointerInfo(); 452bdd1243dSDimitry Andric MIRBuilder.buildLoad(DestinationReg, PtrRegister, PtrInfo, Align()); 453bdd1243dSDimitry Andric return DestinationReg; 454bdd1243dSDimitry Andric } 455bdd1243dSDimitry Andric 456bdd1243dSDimitry Andric /// Helper function for building a load instruction for loading a builtin global 457bdd1243dSDimitry Andric /// variable of \p BuiltinValue value. 458*0fca6ea1SDimitry Andric static Register buildBuiltinVariableLoad( 459*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, SPIRVType *VariableType, 460*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR, SPIRV::BuiltIn::BuiltIn BuiltinValue, LLT LLType, 461*0fca6ea1SDimitry Andric Register Reg = Register(0), bool isConst = true, bool hasLinkageTy = true) { 462bdd1243dSDimitry Andric Register NewRegister = 463bdd1243dSDimitry Andric MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::IDRegClass); 464bdd1243dSDimitry Andric MIRBuilder.getMRI()->setType(NewRegister, 465bdd1243dSDimitry Andric LLT::pointer(0, GR->getPointerSize())); 466bdd1243dSDimitry Andric SPIRVType *PtrType = GR->getOrCreateSPIRVPointerType( 467bdd1243dSDimitry Andric VariableType, MIRBuilder, SPIRV::StorageClass::Input); 468bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF()); 469bdd1243dSDimitry Andric 470bdd1243dSDimitry Andric // Set up the global OpVariable with the necessary builtin decorations. 471bdd1243dSDimitry Andric Register Variable = GR->buildGlobalVariable( 472bdd1243dSDimitry Andric NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr, 473*0fca6ea1SDimitry Andric SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst, 474*0fca6ea1SDimitry Andric /* HasLinkageTy */ hasLinkageTy, SPIRV::LinkageType::Import, MIRBuilder, 475*0fca6ea1SDimitry Andric false); 476bdd1243dSDimitry Andric 477bdd1243dSDimitry Andric // Load the value from the global variable. 478bdd1243dSDimitry Andric Register LoadedRegister = 479bdd1243dSDimitry Andric buildLoadInst(VariableType, Variable, MIRBuilder, GR, LLType, Reg); 480bdd1243dSDimitry Andric MIRBuilder.getMRI()->setType(LoadedRegister, LLType); 481bdd1243dSDimitry Andric return LoadedRegister; 482bdd1243dSDimitry Andric } 483bdd1243dSDimitry Andric 484bdd1243dSDimitry Andric /// Helper external function for inserting ASSIGN_TYPE instuction between \p Reg 485bdd1243dSDimitry Andric /// and its definition, set the new register as a destination of the definition, 486bdd1243dSDimitry Andric /// assign SPIRVType to both registers. If SpirvTy is provided, use it as 487bdd1243dSDimitry Andric /// SPIRVType in ASSIGN_TYPE, otherwise create it from \p Ty. Defined in 488bdd1243dSDimitry Andric /// SPIRVPreLegalizer.cpp. 489bdd1243dSDimitry Andric extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy, 490bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 491bdd1243dSDimitry Andric MachineIRBuilder &MIB, 492bdd1243dSDimitry Andric MachineRegisterInfo &MRI); 493bdd1243dSDimitry Andric 494bdd1243dSDimitry Andric // TODO: Move to TableGen. 495bdd1243dSDimitry Andric static SPIRV::MemorySemantics::MemorySemantics 496bdd1243dSDimitry Andric getSPIRVMemSemantics(std::memory_order MemOrder) { 497bdd1243dSDimitry Andric switch (MemOrder) { 498bdd1243dSDimitry Andric case std::memory_order::memory_order_relaxed: 499bdd1243dSDimitry Andric return SPIRV::MemorySemantics::None; 500bdd1243dSDimitry Andric case std::memory_order::memory_order_acquire: 501bdd1243dSDimitry Andric return SPIRV::MemorySemantics::Acquire; 502bdd1243dSDimitry Andric case std::memory_order::memory_order_release: 503bdd1243dSDimitry Andric return SPIRV::MemorySemantics::Release; 504bdd1243dSDimitry Andric case std::memory_order::memory_order_acq_rel: 505bdd1243dSDimitry Andric return SPIRV::MemorySemantics::AcquireRelease; 506bdd1243dSDimitry Andric case std::memory_order::memory_order_seq_cst: 507bdd1243dSDimitry Andric return SPIRV::MemorySemantics::SequentiallyConsistent; 508bdd1243dSDimitry Andric default: 509*0fca6ea1SDimitry Andric report_fatal_error("Unknown CL memory scope"); 510bdd1243dSDimitry Andric } 511bdd1243dSDimitry Andric } 512bdd1243dSDimitry Andric 513bdd1243dSDimitry Andric static SPIRV::Scope::Scope getSPIRVScope(SPIRV::CLMemoryScope ClScope) { 514bdd1243dSDimitry Andric switch (ClScope) { 515bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_work_item: 516bdd1243dSDimitry Andric return SPIRV::Scope::Invocation; 517bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_work_group: 518bdd1243dSDimitry Andric return SPIRV::Scope::Workgroup; 519bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_device: 520bdd1243dSDimitry Andric return SPIRV::Scope::Device; 521bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_all_svm_devices: 522bdd1243dSDimitry Andric return SPIRV::Scope::CrossDevice; 523bdd1243dSDimitry Andric case SPIRV::CLMemoryScope::memory_scope_sub_group: 524bdd1243dSDimitry Andric return SPIRV::Scope::Subgroup; 525bdd1243dSDimitry Andric } 526*0fca6ea1SDimitry Andric report_fatal_error("Unknown CL memory scope"); 527bdd1243dSDimitry Andric } 528bdd1243dSDimitry Andric 529bdd1243dSDimitry Andric static Register buildConstantIntReg(uint64_t Val, MachineIRBuilder &MIRBuilder, 530bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 531bdd1243dSDimitry Andric unsigned BitWidth = 32) { 532bdd1243dSDimitry Andric SPIRVType *IntType = GR->getOrCreateSPIRVIntegerType(BitWidth, MIRBuilder); 533bdd1243dSDimitry Andric return GR->buildConstantInt(Val, MIRBuilder, IntType); 534bdd1243dSDimitry Andric } 535bdd1243dSDimitry Andric 536bdd1243dSDimitry Andric static Register buildScopeReg(Register CLScopeRegister, 53706c3fb27SDimitry Andric SPIRV::Scope::Scope Scope, 538bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 539bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 54006c3fb27SDimitry Andric MachineRegisterInfo *MRI) { 54106c3fb27SDimitry Andric if (CLScopeRegister.isValid()) { 542bdd1243dSDimitry Andric auto CLScope = 543bdd1243dSDimitry Andric static_cast<SPIRV::CLMemoryScope>(getIConstVal(CLScopeRegister, MRI)); 54406c3fb27SDimitry Andric Scope = getSPIRVScope(CLScope); 545bdd1243dSDimitry Andric 54606c3fb27SDimitry Andric if (CLScope == static_cast<unsigned>(Scope)) { 54706c3fb27SDimitry Andric MRI->setRegClass(CLScopeRegister, &SPIRV::IDRegClass); 548bdd1243dSDimitry Andric return CLScopeRegister; 54906c3fb27SDimitry Andric } 55006c3fb27SDimitry Andric } 551bdd1243dSDimitry Andric return buildConstantIntReg(Scope, MIRBuilder, GR); 552bdd1243dSDimitry Andric } 553bdd1243dSDimitry Andric 554bdd1243dSDimitry Andric static Register buildMemSemanticsReg(Register SemanticsRegister, 55506c3fb27SDimitry Andric Register PtrRegister, unsigned &Semantics, 55606c3fb27SDimitry Andric MachineIRBuilder &MIRBuilder, 557bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 55806c3fb27SDimitry Andric if (SemanticsRegister.isValid()) { 55906c3fb27SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 560bdd1243dSDimitry Andric std::memory_order Order = 561bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(SemanticsRegister, MRI)); 56206c3fb27SDimitry Andric Semantics = 563bdd1243dSDimitry Andric getSPIRVMemSemantics(Order) | 564bdd1243dSDimitry Andric getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister)); 565bdd1243dSDimitry Andric 56606c3fb27SDimitry Andric if (Order == Semantics) { 56706c3fb27SDimitry Andric MRI->setRegClass(SemanticsRegister, &SPIRV::IDRegClass); 568bdd1243dSDimitry Andric return SemanticsRegister; 56906c3fb27SDimitry Andric } 57006c3fb27SDimitry Andric } 57106c3fb27SDimitry Andric return buildConstantIntReg(Semantics, MIRBuilder, GR); 572bdd1243dSDimitry Andric } 573bdd1243dSDimitry Andric 574*0fca6ea1SDimitry Andric static bool buildOpFromWrapper(MachineIRBuilder &MIRBuilder, unsigned Opcode, 575*0fca6ea1SDimitry Andric const SPIRV::IncomingCall *Call, 576*0fca6ea1SDimitry Andric Register TypeReg, 577*0fca6ea1SDimitry Andric ArrayRef<uint32_t> ImmArgs = {}) { 578*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 579*0fca6ea1SDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode); 580*0fca6ea1SDimitry Andric if (TypeReg.isValid()) 581*0fca6ea1SDimitry Andric MIB.addDef(Call->ReturnRegister).addUse(TypeReg); 582*0fca6ea1SDimitry Andric unsigned Sz = Call->Arguments.size() - ImmArgs.size(); 583*0fca6ea1SDimitry Andric for (unsigned i = 0; i < Sz; ++i) { 584*0fca6ea1SDimitry Andric Register ArgReg = Call->Arguments[i]; 585*0fca6ea1SDimitry Andric if (!MRI->getRegClassOrNull(ArgReg)) 586*0fca6ea1SDimitry Andric MRI->setRegClass(ArgReg, &SPIRV::IDRegClass); 587*0fca6ea1SDimitry Andric MIB.addUse(ArgReg); 588*0fca6ea1SDimitry Andric } 589*0fca6ea1SDimitry Andric for (uint32_t ImmArg : ImmArgs) 590*0fca6ea1SDimitry Andric MIB.addImm(ImmArg); 591*0fca6ea1SDimitry Andric return true; 592*0fca6ea1SDimitry Andric } 593*0fca6ea1SDimitry Andric 594bdd1243dSDimitry Andric /// Helper function for translating atomic init to OpStore. 595bdd1243dSDimitry Andric static bool buildAtomicInitInst(const SPIRV::IncomingCall *Call, 596bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder) { 597*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 598*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, SPIRV::OpStore, Call, Register(0)); 599*0fca6ea1SDimitry Andric 600bdd1243dSDimitry Andric assert(Call->Arguments.size() == 2 && 601bdd1243dSDimitry Andric "Need 2 arguments for atomic init translation"); 60206c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 60306c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 604bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpStore) 605bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 606bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 607bdd1243dSDimitry Andric return true; 608bdd1243dSDimitry Andric } 609bdd1243dSDimitry Andric 610bdd1243dSDimitry Andric /// Helper function for building an atomic load instruction. 611bdd1243dSDimitry Andric static bool buildAtomicLoadInst(const SPIRV::IncomingCall *Call, 612bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 613bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 614*0fca6ea1SDimitry Andric Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType); 615*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 616*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicLoad, Call, TypeReg); 617*0fca6ea1SDimitry Andric 618bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 61906c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass); 620bdd1243dSDimitry Andric // TODO: if true insert call to __translate_ocl_memory_sccope before 621bdd1243dSDimitry Andric // OpAtomicLoad and the function implementation. We can use Translator's 622bdd1243dSDimitry Andric // output for transcoding/atomic_explicit_arguments.cl as an example. 623bdd1243dSDimitry Andric Register ScopeRegister; 62406c3fb27SDimitry Andric if (Call->Arguments.size() > 1) { 625bdd1243dSDimitry Andric ScopeRegister = Call->Arguments[1]; 62606c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(ScopeRegister, &SPIRV::IDRegClass); 62706c3fb27SDimitry Andric } else 628bdd1243dSDimitry Andric ScopeRegister = buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR); 629bdd1243dSDimitry Andric 630bdd1243dSDimitry Andric Register MemSemanticsReg; 631bdd1243dSDimitry Andric if (Call->Arguments.size() > 2) { 632bdd1243dSDimitry Andric // TODO: Insert call to __translate_ocl_memory_order before OpAtomicLoad. 633bdd1243dSDimitry Andric MemSemanticsReg = Call->Arguments[2]; 63406c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass); 635bdd1243dSDimitry Andric } else { 636bdd1243dSDimitry Andric int Semantics = 637bdd1243dSDimitry Andric SPIRV::MemorySemantics::SequentiallyConsistent | 638bdd1243dSDimitry Andric getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister)); 639bdd1243dSDimitry Andric MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR); 640bdd1243dSDimitry Andric } 641bdd1243dSDimitry Andric 642bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpAtomicLoad) 643bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 644*0fca6ea1SDimitry Andric .addUse(TypeReg) 645bdd1243dSDimitry Andric .addUse(PtrRegister) 646bdd1243dSDimitry Andric .addUse(ScopeRegister) 647bdd1243dSDimitry Andric .addUse(MemSemanticsReg); 648bdd1243dSDimitry Andric return true; 649bdd1243dSDimitry Andric } 650bdd1243dSDimitry Andric 651bdd1243dSDimitry Andric /// Helper function for building an atomic store instruction. 652bdd1243dSDimitry Andric static bool buildAtomicStoreInst(const SPIRV::IncomingCall *Call, 653bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 654bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 655*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 656*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, SPIRV::OpAtomicStore, Call, Register(0)); 657*0fca6ea1SDimitry Andric 658bdd1243dSDimitry Andric Register ScopeRegister = 659bdd1243dSDimitry Andric buildConstantIntReg(SPIRV::Scope::Device, MIRBuilder, GR); 660bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 66106c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(PtrRegister, &SPIRV::IDRegClass); 662bdd1243dSDimitry Andric int Semantics = 663bdd1243dSDimitry Andric SPIRV::MemorySemantics::SequentiallyConsistent | 664bdd1243dSDimitry Andric getMemSemanticsForStorageClass(GR->getPointerStorageClass(PtrRegister)); 665bdd1243dSDimitry Andric Register MemSemanticsReg = buildConstantIntReg(Semantics, MIRBuilder, GR); 66606c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 667bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpAtomicStore) 668bdd1243dSDimitry Andric .addUse(PtrRegister) 669bdd1243dSDimitry Andric .addUse(ScopeRegister) 670bdd1243dSDimitry Andric .addUse(MemSemanticsReg) 671bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 672bdd1243dSDimitry Andric return true; 673bdd1243dSDimitry Andric } 674bdd1243dSDimitry Andric 675bdd1243dSDimitry Andric /// Helper function for building an atomic compare-exchange instruction. 676*0fca6ea1SDimitry Andric static bool buildAtomicCompareExchangeInst( 677*0fca6ea1SDimitry Andric const SPIRV::IncomingCall *Call, const SPIRV::DemangledBuiltin *Builtin, 678*0fca6ea1SDimitry Andric unsigned Opcode, MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) { 679*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 680*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, 681*0fca6ea1SDimitry Andric GR->getSPIRVTypeID(Call->ReturnType)); 682*0fca6ea1SDimitry Andric 683bdd1243dSDimitry Andric bool IsCmpxchg = Call->Builtin->Name.contains("cmpxchg"); 684bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 685bdd1243dSDimitry Andric 686bdd1243dSDimitry Andric Register ObjectPtr = Call->Arguments[0]; // Pointer (volatile A *object.) 687bdd1243dSDimitry Andric Register ExpectedArg = Call->Arguments[1]; // Comparator (C* expected). 688bdd1243dSDimitry Andric Register Desired = Call->Arguments[2]; // Value (C Desired). 68906c3fb27SDimitry Andric MRI->setRegClass(ObjectPtr, &SPIRV::IDRegClass); 69006c3fb27SDimitry Andric MRI->setRegClass(ExpectedArg, &SPIRV::IDRegClass); 69106c3fb27SDimitry Andric MRI->setRegClass(Desired, &SPIRV::IDRegClass); 692bdd1243dSDimitry Andric SPIRVType *SpvDesiredTy = GR->getSPIRVTypeForVReg(Desired); 693bdd1243dSDimitry Andric LLT DesiredLLT = MRI->getType(Desired); 694bdd1243dSDimitry Andric 695bdd1243dSDimitry Andric assert(GR->getSPIRVTypeForVReg(ObjectPtr)->getOpcode() == 696bdd1243dSDimitry Andric SPIRV::OpTypePointer); 697bdd1243dSDimitry Andric unsigned ExpectedType = GR->getSPIRVTypeForVReg(ExpectedArg)->getOpcode(); 698*0fca6ea1SDimitry Andric (void)ExpectedType; 699bdd1243dSDimitry Andric assert(IsCmpxchg ? ExpectedType == SPIRV::OpTypeInt 700bdd1243dSDimitry Andric : ExpectedType == SPIRV::OpTypePointer); 701bdd1243dSDimitry Andric assert(GR->isScalarOfType(Desired, SPIRV::OpTypeInt)); 702bdd1243dSDimitry Andric 703bdd1243dSDimitry Andric SPIRVType *SpvObjectPtrTy = GR->getSPIRVTypeForVReg(ObjectPtr); 704bdd1243dSDimitry Andric assert(SpvObjectPtrTy->getOperand(2).isReg() && "SPIRV type is expected"); 705bdd1243dSDimitry Andric auto StorageClass = static_cast<SPIRV::StorageClass::StorageClass>( 706bdd1243dSDimitry Andric SpvObjectPtrTy->getOperand(1).getImm()); 707bdd1243dSDimitry Andric auto MemSemStorage = getMemSemanticsForStorageClass(StorageClass); 708bdd1243dSDimitry Andric 709bdd1243dSDimitry Andric Register MemSemEqualReg; 710bdd1243dSDimitry Andric Register MemSemUnequalReg; 711bdd1243dSDimitry Andric uint64_t MemSemEqual = 712bdd1243dSDimitry Andric IsCmpxchg 713bdd1243dSDimitry Andric ? SPIRV::MemorySemantics::None 714bdd1243dSDimitry Andric : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage; 715bdd1243dSDimitry Andric uint64_t MemSemUnequal = 716bdd1243dSDimitry Andric IsCmpxchg 717bdd1243dSDimitry Andric ? SPIRV::MemorySemantics::None 718bdd1243dSDimitry Andric : SPIRV::MemorySemantics::SequentiallyConsistent | MemSemStorage; 719bdd1243dSDimitry Andric if (Call->Arguments.size() >= 4) { 720bdd1243dSDimitry Andric assert(Call->Arguments.size() >= 5 && 721bdd1243dSDimitry Andric "Need 5+ args for explicit atomic cmpxchg"); 722bdd1243dSDimitry Andric auto MemOrdEq = 723bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(Call->Arguments[3], MRI)); 724bdd1243dSDimitry Andric auto MemOrdNeq = 725bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(Call->Arguments[4], MRI)); 726bdd1243dSDimitry Andric MemSemEqual = getSPIRVMemSemantics(MemOrdEq) | MemSemStorage; 727bdd1243dSDimitry Andric MemSemUnequal = getSPIRVMemSemantics(MemOrdNeq) | MemSemStorage; 728bdd1243dSDimitry Andric if (MemOrdEq == MemSemEqual) 729bdd1243dSDimitry Andric MemSemEqualReg = Call->Arguments[3]; 730bdd1243dSDimitry Andric if (MemOrdNeq == MemSemEqual) 731bdd1243dSDimitry Andric MemSemUnequalReg = Call->Arguments[4]; 73206c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass); 73306c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[4], &SPIRV::IDRegClass); 734bdd1243dSDimitry Andric } 735bdd1243dSDimitry Andric if (!MemSemEqualReg.isValid()) 736bdd1243dSDimitry Andric MemSemEqualReg = buildConstantIntReg(MemSemEqual, MIRBuilder, GR); 737bdd1243dSDimitry Andric if (!MemSemUnequalReg.isValid()) 738bdd1243dSDimitry Andric MemSemUnequalReg = buildConstantIntReg(MemSemUnequal, MIRBuilder, GR); 739bdd1243dSDimitry Andric 740bdd1243dSDimitry Andric Register ScopeReg; 741bdd1243dSDimitry Andric auto Scope = IsCmpxchg ? SPIRV::Scope::Workgroup : SPIRV::Scope::Device; 742bdd1243dSDimitry Andric if (Call->Arguments.size() >= 6) { 743bdd1243dSDimitry Andric assert(Call->Arguments.size() == 6 && 744bdd1243dSDimitry Andric "Extra args for explicit atomic cmpxchg"); 745bdd1243dSDimitry Andric auto ClScope = static_cast<SPIRV::CLMemoryScope>( 746bdd1243dSDimitry Andric getIConstVal(Call->Arguments[5], MRI)); 747bdd1243dSDimitry Andric Scope = getSPIRVScope(ClScope); 748bdd1243dSDimitry Andric if (ClScope == static_cast<unsigned>(Scope)) 749bdd1243dSDimitry Andric ScopeReg = Call->Arguments[5]; 75006c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[5], &SPIRV::IDRegClass); 751bdd1243dSDimitry Andric } 752bdd1243dSDimitry Andric if (!ScopeReg.isValid()) 753bdd1243dSDimitry Andric ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR); 754bdd1243dSDimitry Andric 755bdd1243dSDimitry Andric Register Expected = IsCmpxchg 756bdd1243dSDimitry Andric ? ExpectedArg 757bdd1243dSDimitry Andric : buildLoadInst(SpvDesiredTy, ExpectedArg, MIRBuilder, 758bdd1243dSDimitry Andric GR, LLT::scalar(32)); 759bdd1243dSDimitry Andric MRI->setType(Expected, DesiredLLT); 760bdd1243dSDimitry Andric Register Tmp = !IsCmpxchg ? MRI->createGenericVirtualRegister(DesiredLLT) 761bdd1243dSDimitry Andric : Call->ReturnRegister; 76206c3fb27SDimitry Andric if (!MRI->getRegClassOrNull(Tmp)) 76306c3fb27SDimitry Andric MRI->setRegClass(Tmp, &SPIRV::IDRegClass); 764bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(SpvDesiredTy, Tmp, MIRBuilder.getMF()); 765bdd1243dSDimitry Andric 766bdd1243dSDimitry Andric SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder); 767bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 768bdd1243dSDimitry Andric .addDef(Tmp) 769bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(IntTy)) 770bdd1243dSDimitry Andric .addUse(ObjectPtr) 771bdd1243dSDimitry Andric .addUse(ScopeReg) 772bdd1243dSDimitry Andric .addUse(MemSemEqualReg) 773bdd1243dSDimitry Andric .addUse(MemSemUnequalReg) 774bdd1243dSDimitry Andric .addUse(Desired) 775bdd1243dSDimitry Andric .addUse(Expected); 776bdd1243dSDimitry Andric if (!IsCmpxchg) { 777bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpStore).addUse(ExpectedArg).addUse(Tmp); 778bdd1243dSDimitry Andric MIRBuilder.buildICmp(CmpInst::ICMP_EQ, Call->ReturnRegister, Tmp, Expected); 779bdd1243dSDimitry Andric } 780bdd1243dSDimitry Andric return true; 781bdd1243dSDimitry Andric } 782bdd1243dSDimitry Andric 783*0fca6ea1SDimitry Andric /// Helper function for building atomic instructions. 784bdd1243dSDimitry Andric static bool buildAtomicRMWInst(const SPIRV::IncomingCall *Call, unsigned Opcode, 785bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 786bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 787*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 788*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, 789*0fca6ea1SDimitry Andric GR->getSPIRVTypeID(Call->ReturnType)); 790*0fca6ea1SDimitry Andric 79106c3fb27SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 79206c3fb27SDimitry Andric Register ScopeRegister = 79306c3fb27SDimitry Andric Call->Arguments.size() >= 4 ? Call->Arguments[3] : Register(); 794bdd1243dSDimitry Andric 79506c3fb27SDimitry Andric assert(Call->Arguments.size() <= 4 && 796bdd1243dSDimitry Andric "Too many args for explicit atomic RMW"); 79706c3fb27SDimitry Andric ScopeRegister = buildScopeReg(ScopeRegister, SPIRV::Scope::Workgroup, 79806c3fb27SDimitry Andric MIRBuilder, GR, MRI); 799bdd1243dSDimitry Andric 800bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 801bdd1243dSDimitry Andric unsigned Semantics = SPIRV::MemorySemantics::None; 80206c3fb27SDimitry Andric MRI->setRegClass(PtrRegister, &SPIRV::IDRegClass); 80306c3fb27SDimitry Andric Register MemSemanticsReg = 80406c3fb27SDimitry Andric Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register(); 80506c3fb27SDimitry Andric MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister, 80606c3fb27SDimitry Andric Semantics, MIRBuilder, GR); 80706c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 808*0fca6ea1SDimitry Andric Register ValueReg = Call->Arguments[1]; 809*0fca6ea1SDimitry Andric Register ValueTypeReg = GR->getSPIRVTypeID(Call->ReturnType); 810*0fca6ea1SDimitry Andric // support cl_ext_float_atomics 811*0fca6ea1SDimitry Andric if (Call->ReturnType->getOpcode() == SPIRV::OpTypeFloat) { 812*0fca6ea1SDimitry Andric if (Opcode == SPIRV::OpAtomicIAdd) { 813*0fca6ea1SDimitry Andric Opcode = SPIRV::OpAtomicFAddEXT; 814*0fca6ea1SDimitry Andric } else if (Opcode == SPIRV::OpAtomicISub) { 815*0fca6ea1SDimitry Andric // Translate OpAtomicISub applied to a floating type argument to 816*0fca6ea1SDimitry Andric // OpAtomicFAddEXT with the negative value operand 817*0fca6ea1SDimitry Andric Opcode = SPIRV::OpAtomicFAddEXT; 818*0fca6ea1SDimitry Andric Register NegValueReg = 819*0fca6ea1SDimitry Andric MRI->createGenericVirtualRegister(MRI->getType(ValueReg)); 820*0fca6ea1SDimitry Andric MRI->setRegClass(NegValueReg, &SPIRV::IDRegClass); 821*0fca6ea1SDimitry Andric GR->assignSPIRVTypeToVReg(Call->ReturnType, NegValueReg, 822*0fca6ea1SDimitry Andric MIRBuilder.getMF()); 823*0fca6ea1SDimitry Andric MIRBuilder.buildInstr(TargetOpcode::G_FNEG) 824*0fca6ea1SDimitry Andric .addDef(NegValueReg) 825*0fca6ea1SDimitry Andric .addUse(ValueReg); 826*0fca6ea1SDimitry Andric insertAssignInstr(NegValueReg, nullptr, Call->ReturnType, GR, MIRBuilder, 827*0fca6ea1SDimitry Andric MIRBuilder.getMF().getRegInfo()); 828*0fca6ea1SDimitry Andric ValueReg = NegValueReg; 829*0fca6ea1SDimitry Andric } 830*0fca6ea1SDimitry Andric } 831bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 832bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 833*0fca6ea1SDimitry Andric .addUse(ValueTypeReg) 834bdd1243dSDimitry Andric .addUse(PtrRegister) 835bdd1243dSDimitry Andric .addUse(ScopeRegister) 836bdd1243dSDimitry Andric .addUse(MemSemanticsReg) 837*0fca6ea1SDimitry Andric .addUse(ValueReg); 838*0fca6ea1SDimitry Andric return true; 839*0fca6ea1SDimitry Andric } 840*0fca6ea1SDimitry Andric 841*0fca6ea1SDimitry Andric /// Helper function for building an atomic floating-type instruction. 842*0fca6ea1SDimitry Andric static bool buildAtomicFloatingRMWInst(const SPIRV::IncomingCall *Call, 843*0fca6ea1SDimitry Andric unsigned Opcode, 844*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 845*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 846*0fca6ea1SDimitry Andric assert(Call->Arguments.size() == 4 && 847*0fca6ea1SDimitry Andric "Wrong number of atomic floating-type builtin"); 848*0fca6ea1SDimitry Andric 849*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 850*0fca6ea1SDimitry Andric 851*0fca6ea1SDimitry Andric Register PtrReg = Call->Arguments[0]; 852*0fca6ea1SDimitry Andric MRI->setRegClass(PtrReg, &SPIRV::IDRegClass); 853*0fca6ea1SDimitry Andric 854*0fca6ea1SDimitry Andric Register ScopeReg = Call->Arguments[1]; 855*0fca6ea1SDimitry Andric MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass); 856*0fca6ea1SDimitry Andric 857*0fca6ea1SDimitry Andric Register MemSemanticsReg = Call->Arguments[2]; 858*0fca6ea1SDimitry Andric MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass); 859*0fca6ea1SDimitry Andric 860*0fca6ea1SDimitry Andric Register ValueReg = Call->Arguments[3]; 861*0fca6ea1SDimitry Andric MRI->setRegClass(ValueReg, &SPIRV::IDRegClass); 862*0fca6ea1SDimitry Andric 863*0fca6ea1SDimitry Andric MIRBuilder.buildInstr(Opcode) 864*0fca6ea1SDimitry Andric .addDef(Call->ReturnRegister) 865*0fca6ea1SDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 866*0fca6ea1SDimitry Andric .addUse(PtrReg) 867*0fca6ea1SDimitry Andric .addUse(ScopeReg) 868*0fca6ea1SDimitry Andric .addUse(MemSemanticsReg) 869*0fca6ea1SDimitry Andric .addUse(ValueReg); 870bdd1243dSDimitry Andric return true; 871bdd1243dSDimitry Andric } 872bdd1243dSDimitry Andric 873bdd1243dSDimitry Andric /// Helper function for building atomic flag instructions (e.g. 874bdd1243dSDimitry Andric /// OpAtomicFlagTestAndSet). 875bdd1243dSDimitry Andric static bool buildAtomicFlagInst(const SPIRV::IncomingCall *Call, 876bdd1243dSDimitry Andric unsigned Opcode, MachineIRBuilder &MIRBuilder, 877bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 878*0fca6ea1SDimitry Andric bool IsSet = Opcode == SPIRV::OpAtomicFlagTestAndSet; 879*0fca6ea1SDimitry Andric Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType); 880*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 881*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, 882*0fca6ea1SDimitry Andric IsSet ? TypeReg : Register(0)); 883*0fca6ea1SDimitry Andric 88406c3fb27SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 885bdd1243dSDimitry Andric Register PtrRegister = Call->Arguments[0]; 886bdd1243dSDimitry Andric unsigned Semantics = SPIRV::MemorySemantics::SequentiallyConsistent; 88706c3fb27SDimitry Andric Register MemSemanticsReg = 88806c3fb27SDimitry Andric Call->Arguments.size() >= 2 ? Call->Arguments[1] : Register(); 88906c3fb27SDimitry Andric MemSemanticsReg = buildMemSemanticsReg(MemSemanticsReg, PtrRegister, 89006c3fb27SDimitry Andric Semantics, MIRBuilder, GR); 891bdd1243dSDimitry Andric 892bdd1243dSDimitry Andric assert((Opcode != SPIRV::OpAtomicFlagClear || 893bdd1243dSDimitry Andric (Semantics != SPIRV::MemorySemantics::Acquire && 894bdd1243dSDimitry Andric Semantics != SPIRV::MemorySemantics::AcquireRelease)) && 895bdd1243dSDimitry Andric "Invalid memory order argument!"); 896bdd1243dSDimitry Andric 89706c3fb27SDimitry Andric Register ScopeRegister = 89806c3fb27SDimitry Andric Call->Arguments.size() >= 3 ? Call->Arguments[2] : Register(); 89906c3fb27SDimitry Andric ScopeRegister = 90006c3fb27SDimitry Andric buildScopeReg(ScopeRegister, SPIRV::Scope::Device, MIRBuilder, GR, MRI); 901bdd1243dSDimitry Andric 902bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode); 903*0fca6ea1SDimitry Andric if (IsSet) 904*0fca6ea1SDimitry Andric MIB.addDef(Call->ReturnRegister).addUse(TypeReg); 905bdd1243dSDimitry Andric 906bdd1243dSDimitry Andric MIB.addUse(PtrRegister).addUse(ScopeRegister).addUse(MemSemanticsReg); 907bdd1243dSDimitry Andric return true; 908bdd1243dSDimitry Andric } 909bdd1243dSDimitry Andric 910bdd1243dSDimitry Andric /// Helper function for building barriers, i.e., memory/control ordering 911bdd1243dSDimitry Andric /// operations. 912bdd1243dSDimitry Andric static bool buildBarrierInst(const SPIRV::IncomingCall *Call, unsigned Opcode, 913bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 914bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 915*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 916*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, Register(0)); 917*0fca6ea1SDimitry Andric 91806c3fb27SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 919bdd1243dSDimitry Andric unsigned MemFlags = getIConstVal(Call->Arguments[0], MRI); 920bdd1243dSDimitry Andric unsigned MemSemantics = SPIRV::MemorySemantics::None; 921bdd1243dSDimitry Andric 922bdd1243dSDimitry Andric if (MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) 923bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::WorkgroupMemory; 924bdd1243dSDimitry Andric 925bdd1243dSDimitry Andric if (MemFlags & SPIRV::CLK_GLOBAL_MEM_FENCE) 926bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::CrossWorkgroupMemory; 927bdd1243dSDimitry Andric 928bdd1243dSDimitry Andric if (MemFlags & SPIRV::CLK_IMAGE_MEM_FENCE) 929bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::ImageMemory; 930bdd1243dSDimitry Andric 931bdd1243dSDimitry Andric if (Opcode == SPIRV::OpMemoryBarrier) { 932bdd1243dSDimitry Andric std::memory_order MemOrder = 933bdd1243dSDimitry Andric static_cast<std::memory_order>(getIConstVal(Call->Arguments[1], MRI)); 934bdd1243dSDimitry Andric MemSemantics = getSPIRVMemSemantics(MemOrder) | MemSemantics; 935bdd1243dSDimitry Andric } else { 936bdd1243dSDimitry Andric MemSemantics |= SPIRV::MemorySemantics::SequentiallyConsistent; 937bdd1243dSDimitry Andric } 938bdd1243dSDimitry Andric 939bdd1243dSDimitry Andric Register MemSemanticsReg; 94006c3fb27SDimitry Andric if (MemFlags == MemSemantics) { 941bdd1243dSDimitry Andric MemSemanticsReg = Call->Arguments[0]; 94206c3fb27SDimitry Andric MRI->setRegClass(MemSemanticsReg, &SPIRV::IDRegClass); 94306c3fb27SDimitry Andric } else 944bdd1243dSDimitry Andric MemSemanticsReg = buildConstantIntReg(MemSemantics, MIRBuilder, GR); 945bdd1243dSDimitry Andric 946bdd1243dSDimitry Andric Register ScopeReg; 947bdd1243dSDimitry Andric SPIRV::Scope::Scope Scope = SPIRV::Scope::Workgroup; 948bdd1243dSDimitry Andric SPIRV::Scope::Scope MemScope = Scope; 949bdd1243dSDimitry Andric if (Call->Arguments.size() >= 2) { 950bdd1243dSDimitry Andric assert( 951bdd1243dSDimitry Andric ((Opcode != SPIRV::OpMemoryBarrier && Call->Arguments.size() == 2) || 952bdd1243dSDimitry Andric (Opcode == SPIRV::OpMemoryBarrier && Call->Arguments.size() == 3)) && 953bdd1243dSDimitry Andric "Extra args for explicitly scoped barrier"); 954bdd1243dSDimitry Andric Register ScopeArg = (Opcode == SPIRV::OpMemoryBarrier) ? Call->Arguments[2] 955bdd1243dSDimitry Andric : Call->Arguments[1]; 956bdd1243dSDimitry Andric SPIRV::CLMemoryScope CLScope = 957bdd1243dSDimitry Andric static_cast<SPIRV::CLMemoryScope>(getIConstVal(ScopeArg, MRI)); 958bdd1243dSDimitry Andric MemScope = getSPIRVScope(CLScope); 959bdd1243dSDimitry Andric if (!(MemFlags & SPIRV::CLK_LOCAL_MEM_FENCE) || 960bdd1243dSDimitry Andric (Opcode == SPIRV::OpMemoryBarrier)) 961bdd1243dSDimitry Andric Scope = MemScope; 962bdd1243dSDimitry Andric 96306c3fb27SDimitry Andric if (CLScope == static_cast<unsigned>(Scope)) { 964bdd1243dSDimitry Andric ScopeReg = Call->Arguments[1]; 96506c3fb27SDimitry Andric MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass); 96606c3fb27SDimitry Andric } 967bdd1243dSDimitry Andric } 968bdd1243dSDimitry Andric 969bdd1243dSDimitry Andric if (!ScopeReg.isValid()) 970bdd1243dSDimitry Andric ScopeReg = buildConstantIntReg(Scope, MIRBuilder, GR); 971bdd1243dSDimitry Andric 972bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode).addUse(ScopeReg); 973bdd1243dSDimitry Andric if (Opcode != SPIRV::OpMemoryBarrier) 974bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(MemScope, MIRBuilder, GR)); 975bdd1243dSDimitry Andric MIB.addUse(MemSemanticsReg); 976bdd1243dSDimitry Andric return true; 977bdd1243dSDimitry Andric } 978bdd1243dSDimitry Andric 979bdd1243dSDimitry Andric static unsigned getNumComponentsForDim(SPIRV::Dim::Dim dim) { 980bdd1243dSDimitry Andric switch (dim) { 981bdd1243dSDimitry Andric case SPIRV::Dim::DIM_1D: 982bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Buffer: 983bdd1243dSDimitry Andric return 1; 984bdd1243dSDimitry Andric case SPIRV::Dim::DIM_2D: 985bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Cube: 986bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Rect: 987bdd1243dSDimitry Andric return 2; 988bdd1243dSDimitry Andric case SPIRV::Dim::DIM_3D: 989bdd1243dSDimitry Andric return 3; 990bdd1243dSDimitry Andric default: 991*0fca6ea1SDimitry Andric report_fatal_error("Cannot get num components for given Dim"); 992bdd1243dSDimitry Andric } 993bdd1243dSDimitry Andric } 994bdd1243dSDimitry Andric 995bdd1243dSDimitry Andric /// Helper function for obtaining the number of size components. 996bdd1243dSDimitry Andric static unsigned getNumSizeComponents(SPIRVType *imgType) { 997bdd1243dSDimitry Andric assert(imgType->getOpcode() == SPIRV::OpTypeImage); 998bdd1243dSDimitry Andric auto dim = static_cast<SPIRV::Dim::Dim>(imgType->getOperand(2).getImm()); 999bdd1243dSDimitry Andric unsigned numComps = getNumComponentsForDim(dim); 1000bdd1243dSDimitry Andric bool arrayed = imgType->getOperand(4).getImm() == 1; 1001bdd1243dSDimitry Andric return arrayed ? numComps + 1 : numComps; 1002bdd1243dSDimitry Andric } 1003bdd1243dSDimitry Andric 1004bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 1005bdd1243dSDimitry Andric // Implementation functions for each builtin group 1006bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 1007bdd1243dSDimitry Andric 1008bdd1243dSDimitry Andric static bool generateExtInst(const SPIRV::IncomingCall *Call, 1009bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1010bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1011bdd1243dSDimitry Andric // Lookup the extended instruction number in the TableGen records. 1012bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1013bdd1243dSDimitry Andric uint32_t Number = 1014bdd1243dSDimitry Andric SPIRV::lookupExtendedBuiltin(Builtin->Name, Builtin->Set)->Number; 1015bdd1243dSDimitry Andric 1016bdd1243dSDimitry Andric // Build extended instruction. 1017bdd1243dSDimitry Andric auto MIB = 1018bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpExtInst) 1019bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1020bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1021bdd1243dSDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std)) 1022bdd1243dSDimitry Andric .addImm(Number); 1023bdd1243dSDimitry Andric 1024bdd1243dSDimitry Andric for (auto Argument : Call->Arguments) 1025bdd1243dSDimitry Andric MIB.addUse(Argument); 1026bdd1243dSDimitry Andric return true; 1027bdd1243dSDimitry Andric } 1028bdd1243dSDimitry Andric 1029bdd1243dSDimitry Andric static bool generateRelationalInst(const SPIRV::IncomingCall *Call, 1030bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1031bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1032bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1033bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1034bdd1243dSDimitry Andric unsigned Opcode = 1035bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1036bdd1243dSDimitry Andric 1037bdd1243dSDimitry Andric Register CompareRegister; 1038bdd1243dSDimitry Andric SPIRVType *RelationType; 1039bdd1243dSDimitry Andric std::tie(CompareRegister, RelationType) = 1040bdd1243dSDimitry Andric buildBoolRegister(MIRBuilder, Call->ReturnType, GR); 1041bdd1243dSDimitry Andric 1042bdd1243dSDimitry Andric // Build relational instruction. 1043bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 1044bdd1243dSDimitry Andric .addDef(CompareRegister) 1045bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(RelationType)); 1046bdd1243dSDimitry Andric 1047bdd1243dSDimitry Andric for (auto Argument : Call->Arguments) 1048bdd1243dSDimitry Andric MIB.addUse(Argument); 1049bdd1243dSDimitry Andric 1050bdd1243dSDimitry Andric // Build select instruction. 1051bdd1243dSDimitry Andric return buildSelectInst(MIRBuilder, Call->ReturnRegister, CompareRegister, 1052bdd1243dSDimitry Andric Call->ReturnType, GR); 1053bdd1243dSDimitry Andric } 1054bdd1243dSDimitry Andric 1055bdd1243dSDimitry Andric static bool generateGroupInst(const SPIRV::IncomingCall *Call, 1056bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1057bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1058bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1059bdd1243dSDimitry Andric const SPIRV::GroupBuiltin *GroupBuiltin = 1060bdd1243dSDimitry Andric SPIRV::lookupGroupBuiltin(Builtin->Name); 1061*0fca6ea1SDimitry Andric 106206c3fb27SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1063*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) { 1064*0fca6ea1SDimitry Andric if (GroupBuiltin->NoGroupOperation) 1065*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, GroupBuiltin->Opcode, Call, 1066*0fca6ea1SDimitry Andric GR->getSPIRVTypeID(Call->ReturnType)); 1067*0fca6ea1SDimitry Andric 1068*0fca6ea1SDimitry Andric // Group Operation is a literal 1069*0fca6ea1SDimitry Andric Register GroupOpReg = Call->Arguments[1]; 1070*0fca6ea1SDimitry Andric const MachineInstr *MI = getDefInstrMaybeConstant(GroupOpReg, MRI); 1071*0fca6ea1SDimitry Andric if (!MI || MI->getOpcode() != TargetOpcode::G_CONSTANT) 1072*0fca6ea1SDimitry Andric report_fatal_error( 1073*0fca6ea1SDimitry Andric "Group Operation parameter must be an integer constant"); 1074*0fca6ea1SDimitry Andric uint64_t GrpOp = MI->getOperand(1).getCImm()->getValue().getZExtValue(); 1075*0fca6ea1SDimitry Andric Register ScopeReg = Call->Arguments[0]; 1076*0fca6ea1SDimitry Andric if (!MRI->getRegClassOrNull(ScopeReg)) 1077*0fca6ea1SDimitry Andric MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass); 1078*0fca6ea1SDimitry Andric auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode) 1079*0fca6ea1SDimitry Andric .addDef(Call->ReturnRegister) 1080*0fca6ea1SDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1081*0fca6ea1SDimitry Andric .addUse(ScopeReg) 1082*0fca6ea1SDimitry Andric .addImm(GrpOp); 1083*0fca6ea1SDimitry Andric for (unsigned i = 2; i < Call->Arguments.size(); ++i) { 1084*0fca6ea1SDimitry Andric Register ArgReg = Call->Arguments[i]; 1085*0fca6ea1SDimitry Andric if (!MRI->getRegClassOrNull(ArgReg)) 1086*0fca6ea1SDimitry Andric MRI->setRegClass(ArgReg, &SPIRV::IDRegClass); 1087*0fca6ea1SDimitry Andric MIB.addUse(ArgReg); 1088*0fca6ea1SDimitry Andric } 1089*0fca6ea1SDimitry Andric return true; 1090*0fca6ea1SDimitry Andric } 1091*0fca6ea1SDimitry Andric 1092bdd1243dSDimitry Andric Register Arg0; 1093bdd1243dSDimitry Andric if (GroupBuiltin->HasBoolArg) { 1094bdd1243dSDimitry Andric Register ConstRegister = Call->Arguments[0]; 1095bdd1243dSDimitry Andric auto ArgInstruction = getDefInstrMaybeConstant(ConstRegister, MRI); 1096*0fca6ea1SDimitry Andric (void)ArgInstruction; 1097bdd1243dSDimitry Andric // TODO: support non-constant bool values. 1098bdd1243dSDimitry Andric assert(ArgInstruction->getOpcode() == TargetOpcode::G_CONSTANT && 1099bdd1243dSDimitry Andric "Only constant bool value args are supported"); 1100bdd1243dSDimitry Andric if (GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode() != 1101bdd1243dSDimitry Andric SPIRV::OpTypeBool) 1102bdd1243dSDimitry Andric Arg0 = GR->buildConstantInt(getIConstVal(ConstRegister, MRI), MIRBuilder, 1103bdd1243dSDimitry Andric GR->getOrCreateSPIRVBoolType(MIRBuilder)); 1104bdd1243dSDimitry Andric } 1105bdd1243dSDimitry Andric 1106bdd1243dSDimitry Andric Register GroupResultRegister = Call->ReturnRegister; 1107bdd1243dSDimitry Andric SPIRVType *GroupResultType = Call->ReturnType; 1108bdd1243dSDimitry Andric 1109bdd1243dSDimitry Andric // TODO: maybe we need to check whether the result type is already boolean 1110bdd1243dSDimitry Andric // and in this case do not insert select instruction. 1111bdd1243dSDimitry Andric const bool HasBoolReturnTy = 1112bdd1243dSDimitry Andric GroupBuiltin->IsElect || GroupBuiltin->IsAllOrAny || 1113bdd1243dSDimitry Andric GroupBuiltin->IsAllEqual || GroupBuiltin->IsLogical || 1114bdd1243dSDimitry Andric GroupBuiltin->IsInverseBallot || GroupBuiltin->IsBallotBitExtract; 1115bdd1243dSDimitry Andric 1116bdd1243dSDimitry Andric if (HasBoolReturnTy) 1117bdd1243dSDimitry Andric std::tie(GroupResultRegister, GroupResultType) = 1118bdd1243dSDimitry Andric buildBoolRegister(MIRBuilder, Call->ReturnType, GR); 1119bdd1243dSDimitry Andric 11205f757f3fSDimitry Andric auto Scope = Builtin->Name.starts_with("sub_group") ? SPIRV::Scope::Subgroup 1121bdd1243dSDimitry Andric : SPIRV::Scope::Workgroup; 1122bdd1243dSDimitry Andric Register ScopeRegister = buildConstantIntReg(Scope, MIRBuilder, GR); 1123bdd1243dSDimitry Andric 1124bdd1243dSDimitry Andric // Build work/sub group instruction. 1125bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(GroupBuiltin->Opcode) 1126bdd1243dSDimitry Andric .addDef(GroupResultRegister) 1127bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(GroupResultType)) 1128bdd1243dSDimitry Andric .addUse(ScopeRegister); 1129bdd1243dSDimitry Andric 1130bdd1243dSDimitry Andric if (!GroupBuiltin->NoGroupOperation) 1131bdd1243dSDimitry Andric MIB.addImm(GroupBuiltin->GroupOperation); 1132bdd1243dSDimitry Andric if (Call->Arguments.size() > 0) { 1133bdd1243dSDimitry Andric MIB.addUse(Arg0.isValid() ? Arg0 : Call->Arguments[0]); 113406c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 113506c3fb27SDimitry Andric for (unsigned i = 1; i < Call->Arguments.size(); i++) { 1136bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[i]); 113706c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass); 113806c3fb27SDimitry Andric } 1139bdd1243dSDimitry Andric } 1140bdd1243dSDimitry Andric 1141bdd1243dSDimitry Andric // Build select instruction. 1142bdd1243dSDimitry Andric if (HasBoolReturnTy) 1143bdd1243dSDimitry Andric buildSelectInst(MIRBuilder, Call->ReturnRegister, GroupResultRegister, 1144bdd1243dSDimitry Andric Call->ReturnType, GR); 1145bdd1243dSDimitry Andric return true; 1146bdd1243dSDimitry Andric } 1147bdd1243dSDimitry Andric 1148*0fca6ea1SDimitry Andric static bool generateIntelSubgroupsInst(const SPIRV::IncomingCall *Call, 1149*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 1150*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 1151*0fca6ea1SDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1152*0fca6ea1SDimitry Andric MachineFunction &MF = MIRBuilder.getMF(); 1153*0fca6ea1SDimitry Andric const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget()); 1154*0fca6ea1SDimitry Andric if (!ST->canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) { 1155*0fca6ea1SDimitry Andric std::string DiagMsg = std::string(Builtin->Name) + 1156*0fca6ea1SDimitry Andric ": the builtin requires the following SPIR-V " 1157*0fca6ea1SDimitry Andric "extension: SPV_INTEL_subgroups"; 1158*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str(), false); 1159*0fca6ea1SDimitry Andric } 1160*0fca6ea1SDimitry Andric const SPIRV::IntelSubgroupsBuiltin *IntelSubgroups = 1161*0fca6ea1SDimitry Andric SPIRV::lookupIntelSubgroupsBuiltin(Builtin->Name); 1162*0fca6ea1SDimitry Andric 1163*0fca6ea1SDimitry Andric uint32_t OpCode = IntelSubgroups->Opcode; 1164*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) { 1165*0fca6ea1SDimitry Andric bool IsSet = OpCode != SPIRV::OpSubgroupBlockWriteINTEL && 1166*0fca6ea1SDimitry Andric OpCode != SPIRV::OpSubgroupImageBlockWriteINTEL; 1167*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, OpCode, Call, 1168*0fca6ea1SDimitry Andric IsSet ? GR->getSPIRVTypeID(Call->ReturnType) 1169*0fca6ea1SDimitry Andric : Register(0)); 1170*0fca6ea1SDimitry Andric } 1171*0fca6ea1SDimitry Andric 1172*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1173*0fca6ea1SDimitry Andric if (IntelSubgroups->IsBlock) { 1174*0fca6ea1SDimitry Andric // Minimal number or arguments set in TableGen records is 1 1175*0fca6ea1SDimitry Andric if (SPIRVType *Arg0Type = GR->getSPIRVTypeForVReg(Call->Arguments[0])) { 1176*0fca6ea1SDimitry Andric if (Arg0Type->getOpcode() == SPIRV::OpTypeImage) { 1177*0fca6ea1SDimitry Andric // TODO: add required validation from the specification: 1178*0fca6ea1SDimitry Andric // "'Image' must be an object whose type is OpTypeImage with a 'Sampled' 1179*0fca6ea1SDimitry Andric // operand of 0 or 2. If the 'Sampled' operand is 2, then some 1180*0fca6ea1SDimitry Andric // dimensions require a capability." 1181*0fca6ea1SDimitry Andric switch (OpCode) { 1182*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupBlockReadINTEL: 1183*0fca6ea1SDimitry Andric OpCode = SPIRV::OpSubgroupImageBlockReadINTEL; 1184*0fca6ea1SDimitry Andric break; 1185*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupBlockWriteINTEL: 1186*0fca6ea1SDimitry Andric OpCode = SPIRV::OpSubgroupImageBlockWriteINTEL; 1187*0fca6ea1SDimitry Andric break; 1188*0fca6ea1SDimitry Andric } 1189*0fca6ea1SDimitry Andric } 1190*0fca6ea1SDimitry Andric } 1191*0fca6ea1SDimitry Andric } 1192*0fca6ea1SDimitry Andric 1193*0fca6ea1SDimitry Andric // TODO: opaque pointers types should be eventually resolved in such a way 1194*0fca6ea1SDimitry Andric // that validation of block read is enabled with respect to the following 1195*0fca6ea1SDimitry Andric // specification requirement: 1196*0fca6ea1SDimitry Andric // "'Result Type' may be a scalar or vector type, and its component type must 1197*0fca6ea1SDimitry Andric // be equal to the type pointed to by 'Ptr'." 1198*0fca6ea1SDimitry Andric // For example, function parameter type should not be default i8 pointer, but 1199*0fca6ea1SDimitry Andric // depend on the result type of the instruction where it is used as a pointer 1200*0fca6ea1SDimitry Andric // argument of OpSubgroupBlockReadINTEL 1201*0fca6ea1SDimitry Andric 1202*0fca6ea1SDimitry Andric // Build Intel subgroups instruction 1203*0fca6ea1SDimitry Andric MachineInstrBuilder MIB = 1204*0fca6ea1SDimitry Andric IntelSubgroups->IsWrite 1205*0fca6ea1SDimitry Andric ? MIRBuilder.buildInstr(OpCode) 1206*0fca6ea1SDimitry Andric : MIRBuilder.buildInstr(OpCode) 1207*0fca6ea1SDimitry Andric .addDef(Call->ReturnRegister) 1208*0fca6ea1SDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 1209*0fca6ea1SDimitry Andric for (size_t i = 0; i < Call->Arguments.size(); ++i) { 1210*0fca6ea1SDimitry Andric MIB.addUse(Call->Arguments[i]); 1211*0fca6ea1SDimitry Andric MRI->setRegClass(Call->Arguments[i], &SPIRV::IDRegClass); 1212*0fca6ea1SDimitry Andric } 1213*0fca6ea1SDimitry Andric 1214*0fca6ea1SDimitry Andric return true; 1215*0fca6ea1SDimitry Andric } 1216*0fca6ea1SDimitry Andric 1217*0fca6ea1SDimitry Andric static bool generateGroupUniformInst(const SPIRV::IncomingCall *Call, 1218*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 1219*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 1220*0fca6ea1SDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1221*0fca6ea1SDimitry Andric MachineFunction &MF = MIRBuilder.getMF(); 1222*0fca6ea1SDimitry Andric const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget()); 1223*0fca6ea1SDimitry Andric if (!ST->canUseExtension( 1224*0fca6ea1SDimitry Andric SPIRV::Extension::SPV_KHR_uniform_group_instructions)) { 1225*0fca6ea1SDimitry Andric std::string DiagMsg = std::string(Builtin->Name) + 1226*0fca6ea1SDimitry Andric ": the builtin requires the following SPIR-V " 1227*0fca6ea1SDimitry Andric "extension: SPV_KHR_uniform_group_instructions"; 1228*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str(), false); 1229*0fca6ea1SDimitry Andric } 1230*0fca6ea1SDimitry Andric const SPIRV::GroupUniformBuiltin *GroupUniform = 1231*0fca6ea1SDimitry Andric SPIRV::lookupGroupUniformBuiltin(Builtin->Name); 1232*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1233*0fca6ea1SDimitry Andric 1234*0fca6ea1SDimitry Andric Register GroupResultReg = Call->ReturnRegister; 1235*0fca6ea1SDimitry Andric MRI->setRegClass(GroupResultReg, &SPIRV::IDRegClass); 1236*0fca6ea1SDimitry Andric 1237*0fca6ea1SDimitry Andric // Scope 1238*0fca6ea1SDimitry Andric Register ScopeReg = Call->Arguments[0]; 1239*0fca6ea1SDimitry Andric MRI->setRegClass(ScopeReg, &SPIRV::IDRegClass); 1240*0fca6ea1SDimitry Andric 1241*0fca6ea1SDimitry Andric // Group Operation 1242*0fca6ea1SDimitry Andric Register ConstGroupOpReg = Call->Arguments[1]; 1243*0fca6ea1SDimitry Andric const MachineInstr *Const = getDefInstrMaybeConstant(ConstGroupOpReg, MRI); 1244*0fca6ea1SDimitry Andric if (!Const || Const->getOpcode() != TargetOpcode::G_CONSTANT) 1245*0fca6ea1SDimitry Andric report_fatal_error( 1246*0fca6ea1SDimitry Andric "expect a constant group operation for a uniform group instruction", 1247*0fca6ea1SDimitry Andric false); 1248*0fca6ea1SDimitry Andric const MachineOperand &ConstOperand = Const->getOperand(1); 1249*0fca6ea1SDimitry Andric if (!ConstOperand.isCImm()) 1250*0fca6ea1SDimitry Andric report_fatal_error("uniform group instructions: group operation must be an " 1251*0fca6ea1SDimitry Andric "integer constant", 1252*0fca6ea1SDimitry Andric false); 1253*0fca6ea1SDimitry Andric 1254*0fca6ea1SDimitry Andric // Value 1255*0fca6ea1SDimitry Andric Register ValueReg = Call->Arguments[2]; 1256*0fca6ea1SDimitry Andric MRI->setRegClass(ValueReg, &SPIRV::IDRegClass); 1257*0fca6ea1SDimitry Andric 1258*0fca6ea1SDimitry Andric auto MIB = MIRBuilder.buildInstr(GroupUniform->Opcode) 1259*0fca6ea1SDimitry Andric .addDef(GroupResultReg) 1260*0fca6ea1SDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1261*0fca6ea1SDimitry Andric .addUse(ScopeReg); 1262*0fca6ea1SDimitry Andric addNumImm(ConstOperand.getCImm()->getValue(), MIB); 1263*0fca6ea1SDimitry Andric MIB.addUse(ValueReg); 1264*0fca6ea1SDimitry Andric 1265*0fca6ea1SDimitry Andric return true; 1266*0fca6ea1SDimitry Andric } 1267*0fca6ea1SDimitry Andric 1268*0fca6ea1SDimitry Andric static bool generateKernelClockInst(const SPIRV::IncomingCall *Call, 1269*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 1270*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 1271*0fca6ea1SDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1272*0fca6ea1SDimitry Andric MachineFunction &MF = MIRBuilder.getMF(); 1273*0fca6ea1SDimitry Andric const auto *ST = static_cast<const SPIRVSubtarget *>(&MF.getSubtarget()); 1274*0fca6ea1SDimitry Andric if (!ST->canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) { 1275*0fca6ea1SDimitry Andric std::string DiagMsg = std::string(Builtin->Name) + 1276*0fca6ea1SDimitry Andric ": the builtin requires the following SPIR-V " 1277*0fca6ea1SDimitry Andric "extension: SPV_KHR_shader_clock"; 1278*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str(), false); 1279*0fca6ea1SDimitry Andric } 1280*0fca6ea1SDimitry Andric 1281*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1282*0fca6ea1SDimitry Andric Register ResultReg = Call->ReturnRegister; 1283*0fca6ea1SDimitry Andric MRI->setRegClass(ResultReg, &SPIRV::IDRegClass); 1284*0fca6ea1SDimitry Andric 1285*0fca6ea1SDimitry Andric // Deduce the `Scope` operand from the builtin function name. 1286*0fca6ea1SDimitry Andric SPIRV::Scope::Scope ScopeArg = 1287*0fca6ea1SDimitry Andric StringSwitch<SPIRV::Scope::Scope>(Builtin->Name) 1288*0fca6ea1SDimitry Andric .EndsWith("device", SPIRV::Scope::Scope::Device) 1289*0fca6ea1SDimitry Andric .EndsWith("work_group", SPIRV::Scope::Scope::Workgroup) 1290*0fca6ea1SDimitry Andric .EndsWith("sub_group", SPIRV::Scope::Scope::Subgroup); 1291*0fca6ea1SDimitry Andric Register ScopeReg = buildConstantIntReg(ScopeArg, MIRBuilder, GR); 1292*0fca6ea1SDimitry Andric 1293*0fca6ea1SDimitry Andric MIRBuilder.buildInstr(SPIRV::OpReadClockKHR) 1294*0fca6ea1SDimitry Andric .addDef(ResultReg) 1295*0fca6ea1SDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1296*0fca6ea1SDimitry Andric .addUse(ScopeReg); 1297*0fca6ea1SDimitry Andric 1298*0fca6ea1SDimitry Andric return true; 1299*0fca6ea1SDimitry Andric } 1300*0fca6ea1SDimitry Andric 1301bdd1243dSDimitry Andric // These queries ask for a single size_t result for a given dimension index, e.g 1302bdd1243dSDimitry Andric // size_t get_global_id(uint dimindex). In SPIR-V, the builtins corresonding to 1303bdd1243dSDimitry Andric // these values are all vec3 types, so we need to extract the correct index or 1304bdd1243dSDimitry Andric // return defaultVal (0 or 1 depending on the query). We also handle extending 1305bdd1243dSDimitry Andric // or tuncating in case size_t does not match the expected result type's 1306bdd1243dSDimitry Andric // bitwidth. 1307bdd1243dSDimitry Andric // 1308bdd1243dSDimitry Andric // For a constant index >= 3 we generate: 1309bdd1243dSDimitry Andric // %res = OpConstant %SizeT 0 1310bdd1243dSDimitry Andric // 1311bdd1243dSDimitry Andric // For other indices we generate: 1312bdd1243dSDimitry Andric // %g = OpVariable %ptr_V3_SizeT Input 1313bdd1243dSDimitry Andric // OpDecorate %g BuiltIn XXX 1314bdd1243dSDimitry Andric // OpDecorate %g LinkageAttributes "__spirv_BuiltInXXX" 1315bdd1243dSDimitry Andric // OpDecorate %g Constant 1316bdd1243dSDimitry Andric // %loadedVec = OpLoad %V3_SizeT %g 1317bdd1243dSDimitry Andric // 1318bdd1243dSDimitry Andric // Then, if the index is constant < 3, we generate: 1319bdd1243dSDimitry Andric // %res = OpCompositeExtract %SizeT %loadedVec idx 1320bdd1243dSDimitry Andric // If the index is dynamic, we generate: 1321bdd1243dSDimitry Andric // %tmp = OpVectorExtractDynamic %SizeT %loadedVec %idx 1322bdd1243dSDimitry Andric // %cmp = OpULessThan %bool %idx %const_3 1323bdd1243dSDimitry Andric // %res = OpSelect %SizeT %cmp %tmp %const_0 1324bdd1243dSDimitry Andric // 1325bdd1243dSDimitry Andric // If the bitwidth of %res does not match the expected return type, we add an 1326bdd1243dSDimitry Andric // extend or truncate. 1327bdd1243dSDimitry Andric static bool genWorkgroupQuery(const SPIRV::IncomingCall *Call, 1328bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1329bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR, 1330bdd1243dSDimitry Andric SPIRV::BuiltIn::BuiltIn BuiltinValue, 1331bdd1243dSDimitry Andric uint64_t DefaultValue) { 1332bdd1243dSDimitry Andric Register IndexRegister = Call->Arguments[0]; 1333bdd1243dSDimitry Andric const unsigned ResultWidth = Call->ReturnType->getOperand(1).getImm(); 1334bdd1243dSDimitry Andric const unsigned PointerSize = GR->getPointerSize(); 1335bdd1243dSDimitry Andric const SPIRVType *PointerSizeType = 1336bdd1243dSDimitry Andric GR->getOrCreateSPIRVIntegerType(PointerSize, MIRBuilder); 1337bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1338bdd1243dSDimitry Andric auto IndexInstruction = getDefInstrMaybeConstant(IndexRegister, MRI); 1339bdd1243dSDimitry Andric 1340bdd1243dSDimitry Andric // Set up the final register to do truncation or extension on at the end. 1341bdd1243dSDimitry Andric Register ToTruncate = Call->ReturnRegister; 1342bdd1243dSDimitry Andric 1343bdd1243dSDimitry Andric // If the index is constant, we can statically determine if it is in range. 1344bdd1243dSDimitry Andric bool IsConstantIndex = 1345bdd1243dSDimitry Andric IndexInstruction->getOpcode() == TargetOpcode::G_CONSTANT; 1346bdd1243dSDimitry Andric 1347bdd1243dSDimitry Andric // If it's out of range (max dimension is 3), we can just return the constant 1348bdd1243dSDimitry Andric // default value (0 or 1 depending on which query function). 1349bdd1243dSDimitry Andric if (IsConstantIndex && getIConstVal(IndexRegister, MRI) >= 3) { 135006c3fb27SDimitry Andric Register DefaultReg = Call->ReturnRegister; 1351bdd1243dSDimitry Andric if (PointerSize != ResultWidth) { 135206c3fb27SDimitry Andric DefaultReg = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize)); 135306c3fb27SDimitry Andric MRI->setRegClass(DefaultReg, &SPIRV::IDRegClass); 135406c3fb27SDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeType, DefaultReg, 1355bdd1243dSDimitry Andric MIRBuilder.getMF()); 135606c3fb27SDimitry Andric ToTruncate = DefaultReg; 1357bdd1243dSDimitry Andric } 1358bdd1243dSDimitry Andric auto NewRegister = 1359bdd1243dSDimitry Andric GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType); 136006c3fb27SDimitry Andric MIRBuilder.buildCopy(DefaultReg, NewRegister); 1361bdd1243dSDimitry Andric } else { // If it could be in range, we need to load from the given builtin. 1362bdd1243dSDimitry Andric auto Vec3Ty = 1363bdd1243dSDimitry Andric GR->getOrCreateSPIRVVectorType(PointerSizeType, 3, MIRBuilder); 1364bdd1243dSDimitry Andric Register LoadedVector = 1365bdd1243dSDimitry Andric buildBuiltinVariableLoad(MIRBuilder, Vec3Ty, GR, BuiltinValue, 1366bdd1243dSDimitry Andric LLT::fixed_vector(3, PointerSize)); 1367bdd1243dSDimitry Andric // Set up the vreg to extract the result to (possibly a new temporary one). 1368bdd1243dSDimitry Andric Register Extracted = Call->ReturnRegister; 1369bdd1243dSDimitry Andric if (!IsConstantIndex || PointerSize != ResultWidth) { 1370bdd1243dSDimitry Andric Extracted = MRI->createGenericVirtualRegister(LLT::scalar(PointerSize)); 137106c3fb27SDimitry Andric MRI->setRegClass(Extracted, &SPIRV::IDRegClass); 1372bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeType, Extracted, MIRBuilder.getMF()); 1373bdd1243dSDimitry Andric } 1374bdd1243dSDimitry Andric // Use Intrinsic::spv_extractelt so dynamic vs static extraction is 1375bdd1243dSDimitry Andric // handled later: extr = spv_extractelt LoadedVector, IndexRegister. 1376bdd1243dSDimitry Andric MachineInstrBuilder ExtractInst = MIRBuilder.buildIntrinsic( 13775f757f3fSDimitry Andric Intrinsic::spv_extractelt, ArrayRef<Register>{Extracted}, true, false); 1378bdd1243dSDimitry Andric ExtractInst.addUse(LoadedVector).addUse(IndexRegister); 1379bdd1243dSDimitry Andric 1380bdd1243dSDimitry Andric // If the index is dynamic, need check if it's < 3, and then use a select. 1381bdd1243dSDimitry Andric if (!IsConstantIndex) { 1382bdd1243dSDimitry Andric insertAssignInstr(Extracted, nullptr, PointerSizeType, GR, MIRBuilder, 1383bdd1243dSDimitry Andric *MRI); 1384bdd1243dSDimitry Andric 1385bdd1243dSDimitry Andric auto IndexType = GR->getSPIRVTypeForVReg(IndexRegister); 1386bdd1243dSDimitry Andric auto BoolType = GR->getOrCreateSPIRVBoolType(MIRBuilder); 1387bdd1243dSDimitry Andric 1388bdd1243dSDimitry Andric Register CompareRegister = 1389bdd1243dSDimitry Andric MRI->createGenericVirtualRegister(LLT::scalar(1)); 139006c3fb27SDimitry Andric MRI->setRegClass(CompareRegister, &SPIRV::IDRegClass); 1391bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(BoolType, CompareRegister, MIRBuilder.getMF()); 1392bdd1243dSDimitry Andric 1393bdd1243dSDimitry Andric // Use G_ICMP to check if idxVReg < 3. 1394bdd1243dSDimitry Andric MIRBuilder.buildICmp(CmpInst::ICMP_ULT, CompareRegister, IndexRegister, 1395bdd1243dSDimitry Andric GR->buildConstantInt(3, MIRBuilder, IndexType)); 1396bdd1243dSDimitry Andric 1397bdd1243dSDimitry Andric // Get constant for the default value (0 or 1 depending on which 1398bdd1243dSDimitry Andric // function). 1399bdd1243dSDimitry Andric Register DefaultRegister = 1400bdd1243dSDimitry Andric GR->buildConstantInt(DefaultValue, MIRBuilder, PointerSizeType); 1401bdd1243dSDimitry Andric 1402bdd1243dSDimitry Andric // Get a register for the selection result (possibly a new temporary one). 1403bdd1243dSDimitry Andric Register SelectionResult = Call->ReturnRegister; 1404bdd1243dSDimitry Andric if (PointerSize != ResultWidth) { 1405bdd1243dSDimitry Andric SelectionResult = 1406bdd1243dSDimitry Andric MRI->createGenericVirtualRegister(LLT::scalar(PointerSize)); 140706c3fb27SDimitry Andric MRI->setRegClass(SelectionResult, &SPIRV::IDRegClass); 1408bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeType, SelectionResult, 1409bdd1243dSDimitry Andric MIRBuilder.getMF()); 1410bdd1243dSDimitry Andric } 1411bdd1243dSDimitry Andric // Create the final G_SELECT to return the extracted value or the default. 1412bdd1243dSDimitry Andric MIRBuilder.buildSelect(SelectionResult, CompareRegister, Extracted, 1413bdd1243dSDimitry Andric DefaultRegister); 1414bdd1243dSDimitry Andric ToTruncate = SelectionResult; 1415bdd1243dSDimitry Andric } else { 1416bdd1243dSDimitry Andric ToTruncate = Extracted; 1417bdd1243dSDimitry Andric } 1418bdd1243dSDimitry Andric } 1419bdd1243dSDimitry Andric // Alter the result's bitwidth if it does not match the SizeT value extracted. 1420bdd1243dSDimitry Andric if (PointerSize != ResultWidth) 1421bdd1243dSDimitry Andric MIRBuilder.buildZExtOrTrunc(Call->ReturnRegister, ToTruncate); 1422bdd1243dSDimitry Andric return true; 1423bdd1243dSDimitry Andric } 1424bdd1243dSDimitry Andric 1425bdd1243dSDimitry Andric static bool generateBuiltinVar(const SPIRV::IncomingCall *Call, 1426bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1427bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1428bdd1243dSDimitry Andric // Lookup the builtin variable record. 1429bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1430bdd1243dSDimitry Andric SPIRV::BuiltIn::BuiltIn Value = 1431bdd1243dSDimitry Andric SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value; 1432bdd1243dSDimitry Andric 1433bdd1243dSDimitry Andric if (Value == SPIRV::BuiltIn::GlobalInvocationId) 1434bdd1243dSDimitry Andric return genWorkgroupQuery(Call, MIRBuilder, GR, Value, 0); 1435bdd1243dSDimitry Andric 1436bdd1243dSDimitry Andric // Build a load instruction for the builtin variable. 1437bdd1243dSDimitry Andric unsigned BitWidth = GR->getScalarOrVectorBitWidth(Call->ReturnType); 1438bdd1243dSDimitry Andric LLT LLType; 1439bdd1243dSDimitry Andric if (Call->ReturnType->getOpcode() == SPIRV::OpTypeVector) 1440bdd1243dSDimitry Andric LLType = 1441bdd1243dSDimitry Andric LLT::fixed_vector(Call->ReturnType->getOperand(2).getImm(), BitWidth); 1442bdd1243dSDimitry Andric else 1443bdd1243dSDimitry Andric LLType = LLT::scalar(BitWidth); 1444bdd1243dSDimitry Andric 1445bdd1243dSDimitry Andric return buildBuiltinVariableLoad(MIRBuilder, Call->ReturnType, GR, Value, 1446bdd1243dSDimitry Andric LLType, Call->ReturnRegister); 1447bdd1243dSDimitry Andric } 1448bdd1243dSDimitry Andric 1449bdd1243dSDimitry Andric static bool generateAtomicInst(const SPIRV::IncomingCall *Call, 1450bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1451bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1452bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1453bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1454bdd1243dSDimitry Andric unsigned Opcode = 1455bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1456bdd1243dSDimitry Andric 1457bdd1243dSDimitry Andric switch (Opcode) { 1458bdd1243dSDimitry Andric case SPIRV::OpStore: 1459bdd1243dSDimitry Andric return buildAtomicInitInst(Call, MIRBuilder); 1460bdd1243dSDimitry Andric case SPIRV::OpAtomicLoad: 1461bdd1243dSDimitry Andric return buildAtomicLoadInst(Call, MIRBuilder, GR); 1462bdd1243dSDimitry Andric case SPIRV::OpAtomicStore: 1463bdd1243dSDimitry Andric return buildAtomicStoreInst(Call, MIRBuilder, GR); 1464bdd1243dSDimitry Andric case SPIRV::OpAtomicCompareExchange: 1465bdd1243dSDimitry Andric case SPIRV::OpAtomicCompareExchangeWeak: 1466*0fca6ea1SDimitry Andric return buildAtomicCompareExchangeInst(Call, Builtin, Opcode, MIRBuilder, 1467*0fca6ea1SDimitry Andric GR); 1468bdd1243dSDimitry Andric case SPIRV::OpAtomicIAdd: 1469bdd1243dSDimitry Andric case SPIRV::OpAtomicISub: 1470bdd1243dSDimitry Andric case SPIRV::OpAtomicOr: 1471bdd1243dSDimitry Andric case SPIRV::OpAtomicXor: 1472bdd1243dSDimitry Andric case SPIRV::OpAtomicAnd: 1473bdd1243dSDimitry Andric case SPIRV::OpAtomicExchange: 1474bdd1243dSDimitry Andric return buildAtomicRMWInst(Call, Opcode, MIRBuilder, GR); 1475bdd1243dSDimitry Andric case SPIRV::OpMemoryBarrier: 1476bdd1243dSDimitry Andric return buildBarrierInst(Call, SPIRV::OpMemoryBarrier, MIRBuilder, GR); 1477bdd1243dSDimitry Andric case SPIRV::OpAtomicFlagTestAndSet: 1478bdd1243dSDimitry Andric case SPIRV::OpAtomicFlagClear: 1479bdd1243dSDimitry Andric return buildAtomicFlagInst(Call, Opcode, MIRBuilder, GR); 1480bdd1243dSDimitry Andric default: 1481*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 1482*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, 1483*0fca6ea1SDimitry Andric GR->getSPIRVTypeID(Call->ReturnType)); 1484*0fca6ea1SDimitry Andric return false; 1485*0fca6ea1SDimitry Andric } 1486*0fca6ea1SDimitry Andric } 1487*0fca6ea1SDimitry Andric 1488*0fca6ea1SDimitry Andric static bool generateAtomicFloatingInst(const SPIRV::IncomingCall *Call, 1489*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 1490*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 1491*0fca6ea1SDimitry Andric // Lookup the instruction opcode in the TableGen records. 1492*0fca6ea1SDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1493*0fca6ea1SDimitry Andric unsigned Opcode = SPIRV::lookupAtomicFloatingBuiltin(Builtin->Name)->Opcode; 1494*0fca6ea1SDimitry Andric 1495*0fca6ea1SDimitry Andric switch (Opcode) { 1496*0fca6ea1SDimitry Andric case SPIRV::OpAtomicFAddEXT: 1497*0fca6ea1SDimitry Andric case SPIRV::OpAtomicFMinEXT: 1498*0fca6ea1SDimitry Andric case SPIRV::OpAtomicFMaxEXT: 1499*0fca6ea1SDimitry Andric return buildAtomicFloatingRMWInst(Call, Opcode, MIRBuilder, GR); 1500*0fca6ea1SDimitry Andric default: 1501bdd1243dSDimitry Andric return false; 1502bdd1243dSDimitry Andric } 1503bdd1243dSDimitry Andric } 1504bdd1243dSDimitry Andric 1505bdd1243dSDimitry Andric static bool generateBarrierInst(const SPIRV::IncomingCall *Call, 1506bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1507bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1508bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1509bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1510bdd1243dSDimitry Andric unsigned Opcode = 1511bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1512bdd1243dSDimitry Andric 1513bdd1243dSDimitry Andric return buildBarrierInst(Call, Opcode, MIRBuilder, GR); 1514bdd1243dSDimitry Andric } 1515bdd1243dSDimitry Andric 1516*0fca6ea1SDimitry Andric static bool generateCastToPtrInst(const SPIRV::IncomingCall *Call, 1517*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder) { 1518*0fca6ea1SDimitry Andric MIRBuilder.buildInstr(TargetOpcode::G_ADDRSPACE_CAST) 1519*0fca6ea1SDimitry Andric .addDef(Call->ReturnRegister) 1520*0fca6ea1SDimitry Andric .addUse(Call->Arguments[0]); 1521*0fca6ea1SDimitry Andric return true; 1522*0fca6ea1SDimitry Andric } 1523*0fca6ea1SDimitry Andric 1524bdd1243dSDimitry Andric static bool generateDotOrFMulInst(const SPIRV::IncomingCall *Call, 1525bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1526bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1527*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 1528*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, SPIRV::OpDot, Call, 1529*0fca6ea1SDimitry Andric GR->getSPIRVTypeID(Call->ReturnType)); 1530bdd1243dSDimitry Andric unsigned Opcode = GR->getSPIRVTypeForVReg(Call->Arguments[0])->getOpcode(); 1531bdd1243dSDimitry Andric bool IsVec = Opcode == SPIRV::OpTypeVector; 1532bdd1243dSDimitry Andric // Use OpDot only in case of vector args and OpFMul in case of scalar args. 1533bdd1243dSDimitry Andric MIRBuilder.buildInstr(IsVec ? SPIRV::OpDot : SPIRV::OpFMulS) 1534bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1535bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1536bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 1537bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 1538bdd1243dSDimitry Andric return true; 1539bdd1243dSDimitry Andric } 1540bdd1243dSDimitry Andric 1541*0fca6ea1SDimitry Andric static bool generateWaveInst(const SPIRV::IncomingCall *Call, 1542*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 1543*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 1544*0fca6ea1SDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1545*0fca6ea1SDimitry Andric SPIRV::BuiltIn::BuiltIn Value = 1546*0fca6ea1SDimitry Andric SPIRV::lookupGetBuiltin(Builtin->Name, Builtin->Set)->Value; 1547*0fca6ea1SDimitry Andric 1548*0fca6ea1SDimitry Andric // For now, we only support a single Wave intrinsic with a single return type. 1549*0fca6ea1SDimitry Andric assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt); 1550*0fca6ea1SDimitry Andric LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(Call->ReturnType)); 1551*0fca6ea1SDimitry Andric 1552*0fca6ea1SDimitry Andric return buildBuiltinVariableLoad( 1553*0fca6ea1SDimitry Andric MIRBuilder, Call->ReturnType, GR, Value, LLType, Call->ReturnRegister, 1554*0fca6ea1SDimitry Andric /* isConst= */ false, /* hasLinkageTy= */ false); 1555*0fca6ea1SDimitry Andric } 1556*0fca6ea1SDimitry Andric 1557bdd1243dSDimitry Andric static bool generateGetQueryInst(const SPIRV::IncomingCall *Call, 1558bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1559bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1560bdd1243dSDimitry Andric // Lookup the builtin record. 1561bdd1243dSDimitry Andric SPIRV::BuiltIn::BuiltIn Value = 1562bdd1243dSDimitry Andric SPIRV::lookupGetBuiltin(Call->Builtin->Name, Call->Builtin->Set)->Value; 1563bdd1243dSDimitry Andric uint64_t IsDefault = (Value == SPIRV::BuiltIn::GlobalSize || 1564bdd1243dSDimitry Andric Value == SPIRV::BuiltIn::WorkgroupSize || 1565bdd1243dSDimitry Andric Value == SPIRV::BuiltIn::EnqueuedWorkgroupSize); 1566bdd1243dSDimitry Andric return genWorkgroupQuery(Call, MIRBuilder, GR, Value, IsDefault ? 1 : 0); 1567bdd1243dSDimitry Andric } 1568bdd1243dSDimitry Andric 1569bdd1243dSDimitry Andric static bool generateImageSizeQueryInst(const SPIRV::IncomingCall *Call, 1570bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1571bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1572bdd1243dSDimitry Andric // Lookup the image size query component number in the TableGen records. 1573bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1574bdd1243dSDimitry Andric uint32_t Component = 1575bdd1243dSDimitry Andric SPIRV::lookupImageQueryBuiltin(Builtin->Name, Builtin->Set)->Component; 1576bdd1243dSDimitry Andric // Query result may either be a vector or a scalar. If return type is not a 1577bdd1243dSDimitry Andric // vector, expect only a single size component. Otherwise get the number of 1578bdd1243dSDimitry Andric // expected components. 1579bdd1243dSDimitry Andric SPIRVType *RetTy = Call->ReturnType; 1580bdd1243dSDimitry Andric unsigned NumExpectedRetComponents = RetTy->getOpcode() == SPIRV::OpTypeVector 1581bdd1243dSDimitry Andric ? RetTy->getOperand(2).getImm() 1582bdd1243dSDimitry Andric : 1; 1583bdd1243dSDimitry Andric // Get the actual number of query result/size components. 1584bdd1243dSDimitry Andric SPIRVType *ImgType = GR->getSPIRVTypeForVReg(Call->Arguments[0]); 1585bdd1243dSDimitry Andric unsigned NumActualRetComponents = getNumSizeComponents(ImgType); 1586bdd1243dSDimitry Andric Register QueryResult = Call->ReturnRegister; 1587bdd1243dSDimitry Andric SPIRVType *QueryResultType = Call->ReturnType; 1588bdd1243dSDimitry Andric if (NumExpectedRetComponents != NumActualRetComponents) { 1589bdd1243dSDimitry Andric QueryResult = MIRBuilder.getMRI()->createGenericVirtualRegister( 1590bdd1243dSDimitry Andric LLT::fixed_vector(NumActualRetComponents, 32)); 159106c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(QueryResult, &SPIRV::IDRegClass); 1592bdd1243dSDimitry Andric SPIRVType *IntTy = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder); 1593bdd1243dSDimitry Andric QueryResultType = GR->getOrCreateSPIRVVectorType( 1594bdd1243dSDimitry Andric IntTy, NumActualRetComponents, MIRBuilder); 1595bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(QueryResultType, QueryResult, MIRBuilder.getMF()); 1596bdd1243dSDimitry Andric } 1597bdd1243dSDimitry Andric bool IsDimBuf = ImgType->getOperand(2).getImm() == SPIRV::Dim::DIM_Buffer; 1598bdd1243dSDimitry Andric unsigned Opcode = 1599bdd1243dSDimitry Andric IsDimBuf ? SPIRV::OpImageQuerySize : SPIRV::OpImageQuerySizeLod; 160006c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 1601bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 1602bdd1243dSDimitry Andric .addDef(QueryResult) 1603bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(QueryResultType)) 1604bdd1243dSDimitry Andric .addUse(Call->Arguments[0]); 1605bdd1243dSDimitry Andric if (!IsDimBuf) 1606bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Lod id. 1607bdd1243dSDimitry Andric if (NumExpectedRetComponents == NumActualRetComponents) 1608bdd1243dSDimitry Andric return true; 1609bdd1243dSDimitry Andric if (NumExpectedRetComponents == 1) { 1610bdd1243dSDimitry Andric // Only 1 component is expected, build OpCompositeExtract instruction. 1611bdd1243dSDimitry Andric unsigned ExtractedComposite = 1612bdd1243dSDimitry Andric Component == 3 ? NumActualRetComponents - 1 : Component; 1613bdd1243dSDimitry Andric assert(ExtractedComposite < NumActualRetComponents && 1614bdd1243dSDimitry Andric "Invalid composite index!"); 1615*0fca6ea1SDimitry Andric Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType); 1616*0fca6ea1SDimitry Andric SPIRVType *NewType = nullptr; 1617*0fca6ea1SDimitry Andric if (QueryResultType->getOpcode() == SPIRV::OpTypeVector) { 1618*0fca6ea1SDimitry Andric Register NewTypeReg = QueryResultType->getOperand(1).getReg(); 1619*0fca6ea1SDimitry Andric if (TypeReg != NewTypeReg && 1620*0fca6ea1SDimitry Andric (NewType = GR->getSPIRVTypeForVReg(NewTypeReg)) != nullptr) 1621*0fca6ea1SDimitry Andric TypeReg = NewTypeReg; 1622*0fca6ea1SDimitry Andric } 1623bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpCompositeExtract) 1624bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1625*0fca6ea1SDimitry Andric .addUse(TypeReg) 1626bdd1243dSDimitry Andric .addUse(QueryResult) 1627bdd1243dSDimitry Andric .addImm(ExtractedComposite); 1628*0fca6ea1SDimitry Andric if (NewType != nullptr) 1629*0fca6ea1SDimitry Andric insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder, 1630*0fca6ea1SDimitry Andric MIRBuilder.getMF().getRegInfo()); 1631bdd1243dSDimitry Andric } else { 1632bdd1243dSDimitry Andric // More than 1 component is expected, fill a new vector. 1633bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(SPIRV::OpVectorShuffle) 1634bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1635bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1636bdd1243dSDimitry Andric .addUse(QueryResult) 1637bdd1243dSDimitry Andric .addUse(QueryResult); 1638bdd1243dSDimitry Andric for (unsigned i = 0; i < NumExpectedRetComponents; ++i) 1639bdd1243dSDimitry Andric MIB.addImm(i < NumActualRetComponents ? i : 0xffffffff); 1640bdd1243dSDimitry Andric } 1641bdd1243dSDimitry Andric return true; 1642bdd1243dSDimitry Andric } 1643bdd1243dSDimitry Andric 1644bdd1243dSDimitry Andric static bool generateImageMiscQueryInst(const SPIRV::IncomingCall *Call, 1645bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1646bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1647bdd1243dSDimitry Andric assert(Call->ReturnType->getOpcode() == SPIRV::OpTypeInt && 1648bdd1243dSDimitry Andric "Image samples query result must be of int type!"); 1649bdd1243dSDimitry Andric 1650bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1651bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1652bdd1243dSDimitry Andric unsigned Opcode = 1653bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1654bdd1243dSDimitry Andric 1655bdd1243dSDimitry Andric Register Image = Call->Arguments[0]; 165606c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Image, &SPIRV::IDRegClass); 1657bdd1243dSDimitry Andric SPIRV::Dim::Dim ImageDimensionality = static_cast<SPIRV::Dim::Dim>( 1658bdd1243dSDimitry Andric GR->getSPIRVTypeForVReg(Image)->getOperand(2).getImm()); 1659*0fca6ea1SDimitry Andric (void)ImageDimensionality; 1660bdd1243dSDimitry Andric 1661bdd1243dSDimitry Andric switch (Opcode) { 1662bdd1243dSDimitry Andric case SPIRV::OpImageQuerySamples: 1663bdd1243dSDimitry Andric assert(ImageDimensionality == SPIRV::Dim::DIM_2D && 1664bdd1243dSDimitry Andric "Image must be of 2D dimensionality"); 1665bdd1243dSDimitry Andric break; 1666bdd1243dSDimitry Andric case SPIRV::OpImageQueryLevels: 1667bdd1243dSDimitry Andric assert((ImageDimensionality == SPIRV::Dim::DIM_1D || 1668bdd1243dSDimitry Andric ImageDimensionality == SPIRV::Dim::DIM_2D || 1669bdd1243dSDimitry Andric ImageDimensionality == SPIRV::Dim::DIM_3D || 1670bdd1243dSDimitry Andric ImageDimensionality == SPIRV::Dim::DIM_Cube) && 1671bdd1243dSDimitry Andric "Image must be of 1D/2D/3D/Cube dimensionality"); 1672bdd1243dSDimitry Andric break; 1673bdd1243dSDimitry Andric } 1674bdd1243dSDimitry Andric 1675bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 1676bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1677bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1678bdd1243dSDimitry Andric .addUse(Image); 1679bdd1243dSDimitry Andric return true; 1680bdd1243dSDimitry Andric } 1681bdd1243dSDimitry Andric 1682bdd1243dSDimitry Andric // TODO: Move to TableGen. 1683bdd1243dSDimitry Andric static SPIRV::SamplerAddressingMode::SamplerAddressingMode 1684bdd1243dSDimitry Andric getSamplerAddressingModeFromBitmask(unsigned Bitmask) { 1685bdd1243dSDimitry Andric switch (Bitmask & SPIRV::CLK_ADDRESS_MODE_MASK) { 1686bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_CLAMP: 1687bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::Clamp; 1688bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_CLAMP_TO_EDGE: 1689bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::ClampToEdge; 1690bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_REPEAT: 1691bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::Repeat; 1692bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_MIRRORED_REPEAT: 1693bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::RepeatMirrored; 1694bdd1243dSDimitry Andric case SPIRV::CLK_ADDRESS_NONE: 1695bdd1243dSDimitry Andric return SPIRV::SamplerAddressingMode::None; 1696bdd1243dSDimitry Andric default: 1697*0fca6ea1SDimitry Andric report_fatal_error("Unknown CL address mode"); 1698bdd1243dSDimitry Andric } 1699bdd1243dSDimitry Andric } 1700bdd1243dSDimitry Andric 1701bdd1243dSDimitry Andric static unsigned getSamplerParamFromBitmask(unsigned Bitmask) { 1702bdd1243dSDimitry Andric return (Bitmask & SPIRV::CLK_NORMALIZED_COORDS_TRUE) ? 1 : 0; 1703bdd1243dSDimitry Andric } 1704bdd1243dSDimitry Andric 1705bdd1243dSDimitry Andric static SPIRV::SamplerFilterMode::SamplerFilterMode 1706bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(unsigned Bitmask) { 1707bdd1243dSDimitry Andric if (Bitmask & SPIRV::CLK_FILTER_LINEAR) 1708bdd1243dSDimitry Andric return SPIRV::SamplerFilterMode::Linear; 1709bdd1243dSDimitry Andric if (Bitmask & SPIRV::CLK_FILTER_NEAREST) 1710bdd1243dSDimitry Andric return SPIRV::SamplerFilterMode::Nearest; 1711bdd1243dSDimitry Andric return SPIRV::SamplerFilterMode::Nearest; 1712bdd1243dSDimitry Andric } 1713bdd1243dSDimitry Andric 1714bdd1243dSDimitry Andric static bool generateReadImageInst(const StringRef DemangledCall, 1715bdd1243dSDimitry Andric const SPIRV::IncomingCall *Call, 1716bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1717bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1718bdd1243dSDimitry Andric Register Image = Call->Arguments[0]; 1719bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 172006c3fb27SDimitry Andric MRI->setRegClass(Image, &SPIRV::IDRegClass); 172106c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 172206c3fb27SDimitry Andric bool HasOclSampler = DemangledCall.contains_insensitive("ocl_sampler"); 172306c3fb27SDimitry Andric bool HasMsaa = DemangledCall.contains_insensitive("msaa"); 172406c3fb27SDimitry Andric if (HasOclSampler || HasMsaa) 172506c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass); 172606c3fb27SDimitry Andric if (HasOclSampler) { 1727bdd1243dSDimitry Andric Register Sampler = Call->Arguments[1]; 1728bdd1243dSDimitry Andric 1729bdd1243dSDimitry Andric if (!GR->isScalarOfType(Sampler, SPIRV::OpTypeSampler) && 1730bdd1243dSDimitry Andric getDefInstrMaybeConstant(Sampler, MRI)->getOperand(1).isCImm()) { 1731bdd1243dSDimitry Andric uint64_t SamplerMask = getIConstVal(Sampler, MRI); 1732bdd1243dSDimitry Andric Sampler = GR->buildConstantSampler( 1733bdd1243dSDimitry Andric Register(), getSamplerAddressingModeFromBitmask(SamplerMask), 1734bdd1243dSDimitry Andric getSamplerParamFromBitmask(SamplerMask), 1735bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(SamplerMask), MIRBuilder, 1736bdd1243dSDimitry Andric GR->getSPIRVTypeForVReg(Sampler)); 1737bdd1243dSDimitry Andric } 1738bdd1243dSDimitry Andric SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image); 1739bdd1243dSDimitry Andric SPIRVType *SampledImageType = 1740bdd1243dSDimitry Andric GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder); 1741bdd1243dSDimitry Andric Register SampledImage = MRI->createVirtualRegister(&SPIRV::IDRegClass); 1742bdd1243dSDimitry Andric 1743bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpSampledImage) 1744bdd1243dSDimitry Andric .addDef(SampledImage) 1745bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(SampledImageType)) 1746bdd1243dSDimitry Andric .addUse(Image) 1747bdd1243dSDimitry Andric .addUse(Sampler); 1748bdd1243dSDimitry Andric 1749bdd1243dSDimitry Andric Register Lod = GR->buildConstantFP(APFloat::getZero(APFloat::IEEEsingle()), 1750bdd1243dSDimitry Andric MIRBuilder); 1751bdd1243dSDimitry Andric SPIRVType *TempType = Call->ReturnType; 1752bdd1243dSDimitry Andric bool NeedsExtraction = false; 1753bdd1243dSDimitry Andric if (TempType->getOpcode() != SPIRV::OpTypeVector) { 1754bdd1243dSDimitry Andric TempType = 1755bdd1243dSDimitry Andric GR->getOrCreateSPIRVVectorType(Call->ReturnType, 4, MIRBuilder); 1756bdd1243dSDimitry Andric NeedsExtraction = true; 1757bdd1243dSDimitry Andric } 1758bdd1243dSDimitry Andric LLT LLType = LLT::scalar(GR->getScalarOrVectorBitWidth(TempType)); 1759bdd1243dSDimitry Andric Register TempRegister = MRI->createGenericVirtualRegister(LLType); 176006c3fb27SDimitry Andric MRI->setRegClass(TempRegister, &SPIRV::IDRegClass); 1761bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(TempType, TempRegister, MIRBuilder.getMF()); 1762bdd1243dSDimitry Andric 1763bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod) 1764bdd1243dSDimitry Andric .addDef(NeedsExtraction ? TempRegister : Call->ReturnRegister) 1765bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(TempType)) 1766bdd1243dSDimitry Andric .addUse(SampledImage) 1767bdd1243dSDimitry Andric .addUse(Call->Arguments[2]) // Coordinate. 1768bdd1243dSDimitry Andric .addImm(SPIRV::ImageOperand::Lod) 1769bdd1243dSDimitry Andric .addUse(Lod); 1770bdd1243dSDimitry Andric 1771bdd1243dSDimitry Andric if (NeedsExtraction) 1772bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpCompositeExtract) 1773bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1774bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1775bdd1243dSDimitry Andric .addUse(TempRegister) 1776bdd1243dSDimitry Andric .addImm(0); 177706c3fb27SDimitry Andric } else if (HasMsaa) { 1778bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageRead) 1779bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1780bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1781bdd1243dSDimitry Andric .addUse(Image) 1782bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) // Coordinate. 1783bdd1243dSDimitry Andric .addImm(SPIRV::ImageOperand::Sample) 1784bdd1243dSDimitry Andric .addUse(Call->Arguments[2]); 1785bdd1243dSDimitry Andric } else { 1786bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageRead) 1787bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1788bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 1789bdd1243dSDimitry Andric .addUse(Image) 1790bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); // Coordinate. 1791bdd1243dSDimitry Andric } 1792bdd1243dSDimitry Andric return true; 1793bdd1243dSDimitry Andric } 1794bdd1243dSDimitry Andric 1795bdd1243dSDimitry Andric static bool generateWriteImageInst(const SPIRV::IncomingCall *Call, 1796bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1797bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 179806c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 179906c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 180006c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass); 1801bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageWrite) 1802bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) // Image. 1803bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) // Coordinate. 1804bdd1243dSDimitry Andric .addUse(Call->Arguments[2]); // Texel. 1805bdd1243dSDimitry Andric return true; 1806bdd1243dSDimitry Andric } 1807bdd1243dSDimitry Andric 1808bdd1243dSDimitry Andric static bool generateSampleImageInst(const StringRef DemangledCall, 1809bdd1243dSDimitry Andric const SPIRV::IncomingCall *Call, 1810bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1811bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 181206c3fb27SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1813bdd1243dSDimitry Andric if (Call->Builtin->Name.contains_insensitive( 1814bdd1243dSDimitry Andric "__translate_sampler_initializer")) { 1815bdd1243dSDimitry Andric // Build sampler literal. 181606c3fb27SDimitry Andric uint64_t Bitmask = getIConstVal(Call->Arguments[0], MRI); 1817bdd1243dSDimitry Andric Register Sampler = GR->buildConstantSampler( 1818bdd1243dSDimitry Andric Call->ReturnRegister, getSamplerAddressingModeFromBitmask(Bitmask), 1819bdd1243dSDimitry Andric getSamplerParamFromBitmask(Bitmask), 1820bdd1243dSDimitry Andric getSamplerFilterModeFromBitmask(Bitmask), MIRBuilder, Call->ReturnType); 1821bdd1243dSDimitry Andric return Sampler.isValid(); 1822bdd1243dSDimitry Andric } else if (Call->Builtin->Name.contains_insensitive("__spirv_SampledImage")) { 1823bdd1243dSDimitry Andric // Create OpSampledImage. 1824bdd1243dSDimitry Andric Register Image = Call->Arguments[0]; 1825bdd1243dSDimitry Andric SPIRVType *ImageType = GR->getSPIRVTypeForVReg(Image); 1826bdd1243dSDimitry Andric SPIRVType *SampledImageType = 1827bdd1243dSDimitry Andric GR->getOrCreateOpTypeSampledImage(ImageType, MIRBuilder); 1828bdd1243dSDimitry Andric Register SampledImage = 1829bdd1243dSDimitry Andric Call->ReturnRegister.isValid() 1830bdd1243dSDimitry Andric ? Call->ReturnRegister 183106c3fb27SDimitry Andric : MRI->createVirtualRegister(&SPIRV::IDRegClass); 1832bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpSampledImage) 1833bdd1243dSDimitry Andric .addDef(SampledImage) 1834bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(SampledImageType)) 1835bdd1243dSDimitry Andric .addUse(Image) 1836bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); // Sampler. 1837bdd1243dSDimitry Andric return true; 1838bdd1243dSDimitry Andric } else if (Call->Builtin->Name.contains_insensitive( 1839bdd1243dSDimitry Andric "__spirv_ImageSampleExplicitLod")) { 1840bdd1243dSDimitry Andric // Sample an image using an explicit level of detail. 1841bdd1243dSDimitry Andric std::string ReturnType = DemangledCall.str(); 1842bdd1243dSDimitry Andric if (DemangledCall.contains("_R")) { 1843bdd1243dSDimitry Andric ReturnType = ReturnType.substr(ReturnType.find("_R") + 2); 1844bdd1243dSDimitry Andric ReturnType = ReturnType.substr(0, ReturnType.find('(')); 1845bdd1243dSDimitry Andric } 1846*0fca6ea1SDimitry Andric SPIRVType *Type = 1847*0fca6ea1SDimitry Andric Call->ReturnType 1848*0fca6ea1SDimitry Andric ? Call->ReturnType 1849*0fca6ea1SDimitry Andric : GR->getOrCreateSPIRVTypeByName(ReturnType, MIRBuilder); 1850*0fca6ea1SDimitry Andric if (!Type) { 1851*0fca6ea1SDimitry Andric std::string DiagMsg = 1852*0fca6ea1SDimitry Andric "Unable to recognize SPIRV type name: " + ReturnType; 1853*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str()); 1854*0fca6ea1SDimitry Andric } 185506c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 185606c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 185706c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[3], &SPIRV::IDRegClass); 185806c3fb27SDimitry Andric 1859bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpImageSampleExplicitLod) 1860bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1861bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Type)) 1862bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) // Image. 1863bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) // Coordinate. 1864bdd1243dSDimitry Andric .addImm(SPIRV::ImageOperand::Lod) 1865bdd1243dSDimitry Andric .addUse(Call->Arguments[3]); 1866bdd1243dSDimitry Andric return true; 1867bdd1243dSDimitry Andric } 1868bdd1243dSDimitry Andric return false; 1869bdd1243dSDimitry Andric } 1870bdd1243dSDimitry Andric 1871bdd1243dSDimitry Andric static bool generateSelectInst(const SPIRV::IncomingCall *Call, 1872bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder) { 1873bdd1243dSDimitry Andric MIRBuilder.buildSelect(Call->ReturnRegister, Call->Arguments[0], 1874bdd1243dSDimitry Andric Call->Arguments[1], Call->Arguments[2]); 1875bdd1243dSDimitry Andric return true; 1876bdd1243dSDimitry Andric } 1877bdd1243dSDimitry Andric 1878*0fca6ea1SDimitry Andric static bool generateConstructInst(const SPIRV::IncomingCall *Call, 1879*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 1880*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 1881*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, SPIRV::OpCompositeConstruct, Call, 1882*0fca6ea1SDimitry Andric GR->getSPIRVTypeID(Call->ReturnType)); 1883*0fca6ea1SDimitry Andric } 1884*0fca6ea1SDimitry Andric 1885*0fca6ea1SDimitry Andric static bool generateCoopMatrInst(const SPIRV::IncomingCall *Call, 1886*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 1887*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 1888*0fca6ea1SDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1889*0fca6ea1SDimitry Andric unsigned Opcode = 1890*0fca6ea1SDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1891*0fca6ea1SDimitry Andric bool IsSet = Opcode != SPIRV::OpCooperativeMatrixStoreKHR; 1892*0fca6ea1SDimitry Andric unsigned ArgSz = Call->Arguments.size(); 1893*0fca6ea1SDimitry Andric unsigned LiteralIdx = 0; 1894*0fca6ea1SDimitry Andric if (Opcode == SPIRV::OpCooperativeMatrixLoadKHR && ArgSz > 3) 1895*0fca6ea1SDimitry Andric LiteralIdx = 3; 1896*0fca6ea1SDimitry Andric else if (Opcode == SPIRV::OpCooperativeMatrixStoreKHR && ArgSz > 4) 1897*0fca6ea1SDimitry Andric LiteralIdx = 4; 1898*0fca6ea1SDimitry Andric SmallVector<uint32_t, 1> ImmArgs; 1899*0fca6ea1SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1900*0fca6ea1SDimitry Andric if (LiteralIdx > 0) 1901*0fca6ea1SDimitry Andric ImmArgs.push_back(getConstFromIntrinsic(Call->Arguments[LiteralIdx], MRI)); 1902*0fca6ea1SDimitry Andric Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType); 1903*0fca6ea1SDimitry Andric if (Opcode == SPIRV::OpCooperativeMatrixLengthKHR) { 1904*0fca6ea1SDimitry Andric SPIRVType *CoopMatrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]); 1905*0fca6ea1SDimitry Andric if (!CoopMatrType) 1906*0fca6ea1SDimitry Andric report_fatal_error("Can't find a register's type definition"); 1907*0fca6ea1SDimitry Andric MIRBuilder.buildInstr(Opcode) 1908*0fca6ea1SDimitry Andric .addDef(Call->ReturnRegister) 1909*0fca6ea1SDimitry Andric .addUse(TypeReg) 1910*0fca6ea1SDimitry Andric .addUse(CoopMatrType->getOperand(0).getReg()); 1911*0fca6ea1SDimitry Andric return true; 1912*0fca6ea1SDimitry Andric } 1913*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, 1914*0fca6ea1SDimitry Andric IsSet ? TypeReg : Register(0), ImmArgs); 1915*0fca6ea1SDimitry Andric } 1916*0fca6ea1SDimitry Andric 1917bdd1243dSDimitry Andric static bool generateSpecConstantInst(const SPIRV::IncomingCall *Call, 1918bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 1919bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 1920bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 1921bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 1922bdd1243dSDimitry Andric unsigned Opcode = 1923bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 1924bdd1243dSDimitry Andric const MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1925bdd1243dSDimitry Andric 1926bdd1243dSDimitry Andric switch (Opcode) { 1927bdd1243dSDimitry Andric case SPIRV::OpSpecConstant: { 1928bdd1243dSDimitry Andric // Build the SpecID decoration. 1929bdd1243dSDimitry Andric unsigned SpecId = 1930bdd1243dSDimitry Andric static_cast<unsigned>(getIConstVal(Call->Arguments[0], MRI)); 1931bdd1243dSDimitry Andric buildOpDecorate(Call->ReturnRegister, MIRBuilder, SPIRV::Decoration::SpecId, 1932bdd1243dSDimitry Andric {SpecId}); 1933bdd1243dSDimitry Andric // Determine the constant MI. 1934bdd1243dSDimitry Andric Register ConstRegister = Call->Arguments[1]; 1935bdd1243dSDimitry Andric const MachineInstr *Const = getDefInstrMaybeConstant(ConstRegister, MRI); 1936bdd1243dSDimitry Andric assert(Const && 1937bdd1243dSDimitry Andric (Const->getOpcode() == TargetOpcode::G_CONSTANT || 1938bdd1243dSDimitry Andric Const->getOpcode() == TargetOpcode::G_FCONSTANT) && 1939bdd1243dSDimitry Andric "Argument should be either an int or floating-point constant"); 1940bdd1243dSDimitry Andric // Determine the opcode and built the OpSpec MI. 1941bdd1243dSDimitry Andric const MachineOperand &ConstOperand = Const->getOperand(1); 1942bdd1243dSDimitry Andric if (Call->ReturnType->getOpcode() == SPIRV::OpTypeBool) { 1943bdd1243dSDimitry Andric assert(ConstOperand.isCImm() && "Int constant operand is expected"); 1944bdd1243dSDimitry Andric Opcode = ConstOperand.getCImm()->getValue().getZExtValue() 1945bdd1243dSDimitry Andric ? SPIRV::OpSpecConstantTrue 1946bdd1243dSDimitry Andric : SPIRV::OpSpecConstantFalse; 1947bdd1243dSDimitry Andric } 1948bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 1949bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1950bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 1951bdd1243dSDimitry Andric 1952bdd1243dSDimitry Andric if (Call->ReturnType->getOpcode() != SPIRV::OpTypeBool) { 1953bdd1243dSDimitry Andric if (Const->getOpcode() == TargetOpcode::G_CONSTANT) 1954bdd1243dSDimitry Andric addNumImm(ConstOperand.getCImm()->getValue(), MIB); 1955bdd1243dSDimitry Andric else 1956bdd1243dSDimitry Andric addNumImm(ConstOperand.getFPImm()->getValueAPF().bitcastToAPInt(), MIB); 1957bdd1243dSDimitry Andric } 1958bdd1243dSDimitry Andric return true; 1959bdd1243dSDimitry Andric } 1960bdd1243dSDimitry Andric case SPIRV::OpSpecConstantComposite: { 1961bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode) 1962bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 1963bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 1964bdd1243dSDimitry Andric for (unsigned i = 0; i < Call->Arguments.size(); i++) 1965bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[i]); 1966bdd1243dSDimitry Andric return true; 1967bdd1243dSDimitry Andric } 1968bdd1243dSDimitry Andric default: 1969bdd1243dSDimitry Andric return false; 1970bdd1243dSDimitry Andric } 1971bdd1243dSDimitry Andric } 1972bdd1243dSDimitry Andric 197306c3fb27SDimitry Andric static bool buildNDRange(const SPIRV::IncomingCall *Call, 197406c3fb27SDimitry Andric MachineIRBuilder &MIRBuilder, 197506c3fb27SDimitry Andric SPIRVGlobalRegistry *GR) { 197606c3fb27SDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 197706c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 197806c3fb27SDimitry Andric SPIRVType *PtrType = GR->getSPIRVTypeForVReg(Call->Arguments[0]); 197906c3fb27SDimitry Andric assert(PtrType->getOpcode() == SPIRV::OpTypePointer && 198006c3fb27SDimitry Andric PtrType->getOperand(2).isReg()); 198106c3fb27SDimitry Andric Register TypeReg = PtrType->getOperand(2).getReg(); 198206c3fb27SDimitry Andric SPIRVType *StructType = GR->getSPIRVTypeForVReg(TypeReg); 198306c3fb27SDimitry Andric MachineFunction &MF = MIRBuilder.getMF(); 198406c3fb27SDimitry Andric Register TmpReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 198506c3fb27SDimitry Andric GR->assignSPIRVTypeToVReg(StructType, TmpReg, MF); 198606c3fb27SDimitry Andric // Skip the first arg, it's the destination pointer. OpBuildNDRange takes 198706c3fb27SDimitry Andric // three other arguments, so pass zero constant on absence. 198806c3fb27SDimitry Andric unsigned NumArgs = Call->Arguments.size(); 198906c3fb27SDimitry Andric assert(NumArgs >= 2); 199006c3fb27SDimitry Andric Register GlobalWorkSize = Call->Arguments[NumArgs < 4 ? 1 : 2]; 199106c3fb27SDimitry Andric MRI->setRegClass(GlobalWorkSize, &SPIRV::IDRegClass); 199206c3fb27SDimitry Andric Register LocalWorkSize = 199306c3fb27SDimitry Andric NumArgs == 2 ? Register(0) : Call->Arguments[NumArgs < 4 ? 2 : 3]; 199406c3fb27SDimitry Andric if (LocalWorkSize.isValid()) 199506c3fb27SDimitry Andric MRI->setRegClass(LocalWorkSize, &SPIRV::IDRegClass); 199606c3fb27SDimitry Andric Register GlobalWorkOffset = NumArgs <= 3 ? Register(0) : Call->Arguments[1]; 199706c3fb27SDimitry Andric if (GlobalWorkOffset.isValid()) 199806c3fb27SDimitry Andric MRI->setRegClass(GlobalWorkOffset, &SPIRV::IDRegClass); 199906c3fb27SDimitry Andric if (NumArgs < 4) { 200006c3fb27SDimitry Andric Register Const; 200106c3fb27SDimitry Andric SPIRVType *SpvTy = GR->getSPIRVTypeForVReg(GlobalWorkSize); 200206c3fb27SDimitry Andric if (SpvTy->getOpcode() == SPIRV::OpTypePointer) { 200306c3fb27SDimitry Andric MachineInstr *DefInstr = MRI->getUniqueVRegDef(GlobalWorkSize); 200406c3fb27SDimitry Andric assert(DefInstr && isSpvIntrinsic(*DefInstr, Intrinsic::spv_gep) && 200506c3fb27SDimitry Andric DefInstr->getOperand(3).isReg()); 200606c3fb27SDimitry Andric Register GWSPtr = DefInstr->getOperand(3).getReg(); 200706c3fb27SDimitry Andric if (!MRI->getRegClassOrNull(GWSPtr)) 200806c3fb27SDimitry Andric MRI->setRegClass(GWSPtr, &SPIRV::IDRegClass); 200906c3fb27SDimitry Andric // TODO: Maybe simplify generation of the type of the fields. 2010*0fca6ea1SDimitry Andric unsigned Size = Call->Builtin->Name == "ndrange_3D" ? 3 : 2; 201106c3fb27SDimitry Andric unsigned BitWidth = GR->getPointerSize() == 64 ? 64 : 32; 201206c3fb27SDimitry Andric Type *BaseTy = IntegerType::get(MF.getFunction().getContext(), BitWidth); 201306c3fb27SDimitry Andric Type *FieldTy = ArrayType::get(BaseTy, Size); 201406c3fb27SDimitry Andric SPIRVType *SpvFieldTy = GR->getOrCreateSPIRVType(FieldTy, MIRBuilder); 201506c3fb27SDimitry Andric GlobalWorkSize = MRI->createVirtualRegister(&SPIRV::IDRegClass); 201606c3fb27SDimitry Andric GR->assignSPIRVTypeToVReg(SpvFieldTy, GlobalWorkSize, MF); 201706c3fb27SDimitry Andric MIRBuilder.buildInstr(SPIRV::OpLoad) 201806c3fb27SDimitry Andric .addDef(GlobalWorkSize) 201906c3fb27SDimitry Andric .addUse(GR->getSPIRVTypeID(SpvFieldTy)) 202006c3fb27SDimitry Andric .addUse(GWSPtr); 2021*0fca6ea1SDimitry Andric const SPIRVSubtarget &ST = 2022*0fca6ea1SDimitry Andric cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget()); 2023*0fca6ea1SDimitry Andric Const = GR->getOrCreateConstIntArray(0, Size, *MIRBuilder.getInsertPt(), 2024*0fca6ea1SDimitry Andric SpvFieldTy, *ST.getInstrInfo()); 202506c3fb27SDimitry Andric } else { 202606c3fb27SDimitry Andric Const = GR->buildConstantInt(0, MIRBuilder, SpvTy); 202706c3fb27SDimitry Andric } 202806c3fb27SDimitry Andric if (!LocalWorkSize.isValid()) 202906c3fb27SDimitry Andric LocalWorkSize = Const; 203006c3fb27SDimitry Andric if (!GlobalWorkOffset.isValid()) 203106c3fb27SDimitry Andric GlobalWorkOffset = Const; 203206c3fb27SDimitry Andric } 203306c3fb27SDimitry Andric assert(LocalWorkSize.isValid() && GlobalWorkOffset.isValid()); 203406c3fb27SDimitry Andric MIRBuilder.buildInstr(SPIRV::OpBuildNDRange) 203506c3fb27SDimitry Andric .addDef(TmpReg) 203606c3fb27SDimitry Andric .addUse(TypeReg) 203706c3fb27SDimitry Andric .addUse(GlobalWorkSize) 203806c3fb27SDimitry Andric .addUse(LocalWorkSize) 203906c3fb27SDimitry Andric .addUse(GlobalWorkOffset); 204006c3fb27SDimitry Andric return MIRBuilder.buildInstr(SPIRV::OpStore) 204106c3fb27SDimitry Andric .addUse(Call->Arguments[0]) 204206c3fb27SDimitry Andric .addUse(TmpReg); 204306c3fb27SDimitry Andric } 204406c3fb27SDimitry Andric 2045bdd1243dSDimitry Andric // TODO: maybe move to the global register. 2046bdd1243dSDimitry Andric static SPIRVType * 2047bdd1243dSDimitry Andric getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder, 2048bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2049bdd1243dSDimitry Andric LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext(); 2050bdd1243dSDimitry Andric Type *OpaqueType = StructType::getTypeByName(Context, "spirv.DeviceEvent"); 2051bdd1243dSDimitry Andric if (!OpaqueType) 2052bdd1243dSDimitry Andric OpaqueType = StructType::getTypeByName(Context, "opencl.clk_event_t"); 2053bdd1243dSDimitry Andric if (!OpaqueType) 2054bdd1243dSDimitry Andric OpaqueType = StructType::create(Context, "spirv.DeviceEvent"); 2055bdd1243dSDimitry Andric unsigned SC0 = storageClassToAddressSpace(SPIRV::StorageClass::Function); 2056bdd1243dSDimitry Andric unsigned SC1 = storageClassToAddressSpace(SPIRV::StorageClass::Generic); 2057bdd1243dSDimitry Andric Type *PtrType = PointerType::get(PointerType::get(OpaqueType, SC0), SC1); 2058bdd1243dSDimitry Andric return GR->getOrCreateSPIRVType(PtrType, MIRBuilder); 2059bdd1243dSDimitry Andric } 2060bdd1243dSDimitry Andric 2061bdd1243dSDimitry Andric static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call, 2062bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2063bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2064bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 2065bdd1243dSDimitry Andric const DataLayout &DL = MIRBuilder.getDataLayout(); 2066*0fca6ea1SDimitry Andric bool IsSpirvOp = Call->isSpirvOp(); 2067*0fca6ea1SDimitry Andric bool HasEvents = Call->Builtin->Name.contains("events") || IsSpirvOp; 2068bdd1243dSDimitry Andric const SPIRVType *Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder); 2069bdd1243dSDimitry Andric 2070bdd1243dSDimitry Andric // Make vararg instructions before OpEnqueueKernel. 2071bdd1243dSDimitry Andric // Local sizes arguments: Sizes of block invoke arguments. Clang generates 2072bdd1243dSDimitry Andric // local size operands as an array, so we need to unpack them. 2073bdd1243dSDimitry Andric SmallVector<Register, 16> LocalSizes; 2074*0fca6ea1SDimitry Andric if (Call->Builtin->Name.contains("_varargs") || IsSpirvOp) { 2075bdd1243dSDimitry Andric const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6; 2076bdd1243dSDimitry Andric Register GepReg = Call->Arguments[LocalSizeArrayIdx]; 2077bdd1243dSDimitry Andric MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg); 2078bdd1243dSDimitry Andric assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) && 2079bdd1243dSDimitry Andric GepMI->getOperand(3).isReg()); 2080bdd1243dSDimitry Andric Register ArrayReg = GepMI->getOperand(3).getReg(); 2081bdd1243dSDimitry Andric MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg); 2082bdd1243dSDimitry Andric const Type *LocalSizeTy = getMachineInstrType(ArrayMI); 2083bdd1243dSDimitry Andric assert(LocalSizeTy && "Local size type is expected"); 2084bdd1243dSDimitry Andric const uint64_t LocalSizeNum = 2085bdd1243dSDimitry Andric cast<ArrayType>(LocalSizeTy)->getNumElements(); 2086bdd1243dSDimitry Andric unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic); 2087bdd1243dSDimitry Andric const LLT LLType = LLT::pointer(SC, GR->getPointerSize()); 2088bdd1243dSDimitry Andric const SPIRVType *PointerSizeTy = GR->getOrCreateSPIRVPointerType( 2089bdd1243dSDimitry Andric Int32Ty, MIRBuilder, SPIRV::StorageClass::Function); 2090bdd1243dSDimitry Andric for (unsigned I = 0; I < LocalSizeNum; ++I) { 209106c3fb27SDimitry Andric Register Reg = MRI->createVirtualRegister(&SPIRV::IDRegClass); 209206c3fb27SDimitry Andric MRI->setType(Reg, LLType); 2093bdd1243dSDimitry Andric GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF()); 20945f757f3fSDimitry Andric auto GEPInst = MIRBuilder.buildIntrinsic( 20955f757f3fSDimitry Andric Intrinsic::spv_gep, ArrayRef<Register>{Reg}, true, false); 2096bdd1243dSDimitry Andric GEPInst 2097bdd1243dSDimitry Andric .addImm(GepMI->getOperand(2).getImm()) // In bound. 2098bdd1243dSDimitry Andric .addUse(ArrayMI->getOperand(0).getReg()) // Alloca. 2099bdd1243dSDimitry Andric .addUse(buildConstantIntReg(0, MIRBuilder, GR)) // Indices. 2100bdd1243dSDimitry Andric .addUse(buildConstantIntReg(I, MIRBuilder, GR)); 2101bdd1243dSDimitry Andric LocalSizes.push_back(Reg); 2102bdd1243dSDimitry Andric } 2103bdd1243dSDimitry Andric } 2104bdd1243dSDimitry Andric 2105bdd1243dSDimitry Andric // SPIRV OpEnqueueKernel instruction has 10+ arguments. 2106bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(SPIRV::OpEnqueueKernel) 2107bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 2108bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Int32Ty)); 2109bdd1243dSDimitry Andric 2110bdd1243dSDimitry Andric // Copy all arguments before block invoke function pointer. 2111bdd1243dSDimitry Andric const unsigned BlockFIdx = HasEvents ? 6 : 3; 2112bdd1243dSDimitry Andric for (unsigned i = 0; i < BlockFIdx; i++) 2113bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[i]); 2114bdd1243dSDimitry Andric 2115bdd1243dSDimitry Andric // If there are no event arguments in the original call, add dummy ones. 2116bdd1243dSDimitry Andric if (!HasEvents) { 2117bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(0, MIRBuilder, GR)); // Dummy num events. 2118bdd1243dSDimitry Andric Register NullPtr = GR->getOrCreateConstNullPtr( 2119bdd1243dSDimitry Andric MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR)); 2120bdd1243dSDimitry Andric MIB.addUse(NullPtr); // Dummy wait events. 2121bdd1243dSDimitry Andric MIB.addUse(NullPtr); // Dummy ret event. 2122bdd1243dSDimitry Andric } 2123bdd1243dSDimitry Andric 2124bdd1243dSDimitry Andric MachineInstr *BlockMI = getBlockStructInstr(Call->Arguments[BlockFIdx], MRI); 2125bdd1243dSDimitry Andric assert(BlockMI->getOpcode() == TargetOpcode::G_GLOBAL_VALUE); 2126bdd1243dSDimitry Andric // Invoke: Pointer to invoke function. 2127bdd1243dSDimitry Andric MIB.addGlobalAddress(BlockMI->getOperand(1).getGlobal()); 2128bdd1243dSDimitry Andric 2129bdd1243dSDimitry Andric Register BlockLiteralReg = Call->Arguments[BlockFIdx + 1]; 2130bdd1243dSDimitry Andric // Param: Pointer to block literal. 2131bdd1243dSDimitry Andric MIB.addUse(BlockLiteralReg); 2132bdd1243dSDimitry Andric 2133bdd1243dSDimitry Andric Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI)); 2134bdd1243dSDimitry Andric // TODO: these numbers should be obtained from block literal structure. 2135bdd1243dSDimitry Andric // Param Size: Size of block literal structure. 2136bdd1243dSDimitry Andric MIB.addUse(buildConstantIntReg(DL.getTypeStoreSize(PType), MIRBuilder, GR)); 2137bdd1243dSDimitry Andric // Param Aligment: Aligment of block literal structure. 2138bdd1243dSDimitry Andric MIB.addUse( 213906c3fb27SDimitry Andric buildConstantIntReg(DL.getPrefTypeAlign(PType).value(), MIRBuilder, GR)); 2140bdd1243dSDimitry Andric 2141bdd1243dSDimitry Andric for (unsigned i = 0; i < LocalSizes.size(); i++) 2142bdd1243dSDimitry Andric MIB.addUse(LocalSizes[i]); 2143bdd1243dSDimitry Andric return true; 2144bdd1243dSDimitry Andric } 2145bdd1243dSDimitry Andric 2146bdd1243dSDimitry Andric static bool generateEnqueueInst(const SPIRV::IncomingCall *Call, 2147bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2148bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2149bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 2150bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 2151bdd1243dSDimitry Andric unsigned Opcode = 2152bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 2153bdd1243dSDimitry Andric 2154bdd1243dSDimitry Andric switch (Opcode) { 2155bdd1243dSDimitry Andric case SPIRV::OpRetainEvent: 2156bdd1243dSDimitry Andric case SPIRV::OpReleaseEvent: 215706c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 2158bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode).addUse(Call->Arguments[0]); 2159bdd1243dSDimitry Andric case SPIRV::OpCreateUserEvent: 2160bdd1243dSDimitry Andric case SPIRV::OpGetDefaultQueue: 2161bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 2162bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 2163bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)); 2164bdd1243dSDimitry Andric case SPIRV::OpIsValidEvent: 216506c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 2166bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 2167bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 2168bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 2169bdd1243dSDimitry Andric .addUse(Call->Arguments[0]); 2170bdd1243dSDimitry Andric case SPIRV::OpSetUserEventStatus: 217106c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 217206c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 2173bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 2174bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 2175bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 2176bdd1243dSDimitry Andric case SPIRV::OpCaptureEventProfilingInfo: 217706c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 217806c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 217906c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(Call->Arguments[2], &SPIRV::IDRegClass); 2180bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 2181bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 2182bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) 2183bdd1243dSDimitry Andric .addUse(Call->Arguments[2]); 218406c3fb27SDimitry Andric case SPIRV::OpBuildNDRange: 218506c3fb27SDimitry Andric return buildNDRange(Call, MIRBuilder, GR); 2186bdd1243dSDimitry Andric case SPIRV::OpEnqueueKernel: 2187bdd1243dSDimitry Andric return buildEnqueueKernel(Call, MIRBuilder, GR); 2188bdd1243dSDimitry Andric default: 2189bdd1243dSDimitry Andric return false; 2190bdd1243dSDimitry Andric } 2191bdd1243dSDimitry Andric } 2192bdd1243dSDimitry Andric 2193bdd1243dSDimitry Andric static bool generateAsyncCopy(const SPIRV::IncomingCall *Call, 2194bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2195bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2196bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 2197bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 2198bdd1243dSDimitry Andric unsigned Opcode = 2199bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 2200*0fca6ea1SDimitry Andric 2201*0fca6ea1SDimitry Andric bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy; 2202*0fca6ea1SDimitry Andric Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType); 2203*0fca6ea1SDimitry Andric if (Call->isSpirvOp()) 2204*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, 2205*0fca6ea1SDimitry Andric IsSet ? TypeReg : Register(0)); 2206*0fca6ea1SDimitry Andric 2207bdd1243dSDimitry Andric auto Scope = buildConstantIntReg(SPIRV::Scope::Workgroup, MIRBuilder, GR); 2208bdd1243dSDimitry Andric 2209bdd1243dSDimitry Andric switch (Opcode) { 2210*0fca6ea1SDimitry Andric case SPIRV::OpGroupAsyncCopy: { 2211*0fca6ea1SDimitry Andric SPIRVType *NewType = 2212*0fca6ea1SDimitry Andric Call->ReturnType->getOpcode() == SPIRV::OpTypeEvent 2213*0fca6ea1SDimitry Andric ? nullptr 2214*0fca6ea1SDimitry Andric : GR->getOrCreateSPIRVTypeByName("spirv.Event", MIRBuilder); 2215*0fca6ea1SDimitry Andric Register TypeReg = GR->getSPIRVTypeID(NewType ? NewType : Call->ReturnType); 2216*0fca6ea1SDimitry Andric unsigned NumArgs = Call->Arguments.size(); 2217*0fca6ea1SDimitry Andric Register EventReg = Call->Arguments[NumArgs - 1]; 2218*0fca6ea1SDimitry Andric bool Res = MIRBuilder.buildInstr(Opcode) 2219bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 2220*0fca6ea1SDimitry Andric .addUse(TypeReg) 2221bdd1243dSDimitry Andric .addUse(Scope) 2222bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 2223bdd1243dSDimitry Andric .addUse(Call->Arguments[1]) 2224bdd1243dSDimitry Andric .addUse(Call->Arguments[2]) 2225*0fca6ea1SDimitry Andric .addUse(Call->Arguments.size() > 4 2226*0fca6ea1SDimitry Andric ? Call->Arguments[3] 2227*0fca6ea1SDimitry Andric : buildConstantIntReg(1, MIRBuilder, GR)) 2228*0fca6ea1SDimitry Andric .addUse(EventReg); 2229*0fca6ea1SDimitry Andric if (NewType != nullptr) 2230*0fca6ea1SDimitry Andric insertAssignInstr(Call->ReturnRegister, nullptr, NewType, GR, MIRBuilder, 2231*0fca6ea1SDimitry Andric MIRBuilder.getMF().getRegInfo()); 2232*0fca6ea1SDimitry Andric return Res; 2233*0fca6ea1SDimitry Andric } 2234bdd1243dSDimitry Andric case SPIRV::OpGroupWaitEvents: 2235bdd1243dSDimitry Andric return MIRBuilder.buildInstr(Opcode) 2236bdd1243dSDimitry Andric .addUse(Scope) 2237bdd1243dSDimitry Andric .addUse(Call->Arguments[0]) 2238bdd1243dSDimitry Andric .addUse(Call->Arguments[1]); 2239bdd1243dSDimitry Andric default: 2240bdd1243dSDimitry Andric return false; 2241bdd1243dSDimitry Andric } 2242bdd1243dSDimitry Andric } 2243bdd1243dSDimitry Andric 2244bdd1243dSDimitry Andric static bool generateConvertInst(const StringRef DemangledCall, 2245bdd1243dSDimitry Andric const SPIRV::IncomingCall *Call, 2246bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2247bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2248bdd1243dSDimitry Andric // Lookup the conversion builtin in the TableGen records. 2249bdd1243dSDimitry Andric const SPIRV::ConvertBuiltin *Builtin = 2250bdd1243dSDimitry Andric SPIRV::lookupConvertBuiltin(Call->Builtin->Name, Call->Builtin->Set); 2251bdd1243dSDimitry Andric 2252*0fca6ea1SDimitry Andric if (!Builtin && Call->isSpirvOp()) { 2253*0fca6ea1SDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 2254*0fca6ea1SDimitry Andric unsigned Opcode = 2255*0fca6ea1SDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 2256*0fca6ea1SDimitry Andric return buildOpFromWrapper(MIRBuilder, Opcode, Call, 2257*0fca6ea1SDimitry Andric GR->getSPIRVTypeID(Call->ReturnType)); 2258*0fca6ea1SDimitry Andric } 2259*0fca6ea1SDimitry Andric 2260bdd1243dSDimitry Andric if (Builtin->IsSaturated) 2261bdd1243dSDimitry Andric buildOpDecorate(Call->ReturnRegister, MIRBuilder, 2262bdd1243dSDimitry Andric SPIRV::Decoration::SaturatedConversion, {}); 2263bdd1243dSDimitry Andric if (Builtin->IsRounded) 2264bdd1243dSDimitry Andric buildOpDecorate(Call->ReturnRegister, MIRBuilder, 226506c3fb27SDimitry Andric SPIRV::Decoration::FPRoundingMode, 226606c3fb27SDimitry Andric {(unsigned)Builtin->RoundingMode}); 2267bdd1243dSDimitry Andric 2268*0fca6ea1SDimitry Andric std::string NeedExtMsg; // no errors if empty 2269*0fca6ea1SDimitry Andric bool IsRightComponentsNumber = true; // check if input/output accepts vectors 2270bdd1243dSDimitry Andric unsigned Opcode = SPIRV::OpNop; 2271bdd1243dSDimitry Andric if (GR->isScalarOrVectorOfType(Call->Arguments[0], SPIRV::OpTypeInt)) { 2272bdd1243dSDimitry Andric // Int -> ... 2273bdd1243dSDimitry Andric if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) { 2274bdd1243dSDimitry Andric // Int -> Int 2275bdd1243dSDimitry Andric if (Builtin->IsSaturated) 2276bdd1243dSDimitry Andric Opcode = Builtin->IsDestinationSigned ? SPIRV::OpSatConvertUToS 2277bdd1243dSDimitry Andric : SPIRV::OpSatConvertSToU; 2278bdd1243dSDimitry Andric else 2279bdd1243dSDimitry Andric Opcode = Builtin->IsDestinationSigned ? SPIRV::OpUConvert 2280bdd1243dSDimitry Andric : SPIRV::OpSConvert; 2281bdd1243dSDimitry Andric } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister, 2282bdd1243dSDimitry Andric SPIRV::OpTypeFloat)) { 2283bdd1243dSDimitry Andric // Int -> Float 2284*0fca6ea1SDimitry Andric if (Builtin->IsBfloat16) { 2285*0fca6ea1SDimitry Andric const auto *ST = static_cast<const SPIRVSubtarget *>( 2286*0fca6ea1SDimitry Andric &MIRBuilder.getMF().getSubtarget()); 2287*0fca6ea1SDimitry Andric if (!ST->canUseExtension( 2288*0fca6ea1SDimitry Andric SPIRV::Extension::SPV_INTEL_bfloat16_conversion)) 2289*0fca6ea1SDimitry Andric NeedExtMsg = "SPV_INTEL_bfloat16_conversion"; 2290*0fca6ea1SDimitry Andric IsRightComponentsNumber = 2291*0fca6ea1SDimitry Andric GR->getScalarOrVectorComponentCount(Call->Arguments[0]) == 2292*0fca6ea1SDimitry Andric GR->getScalarOrVectorComponentCount(Call->ReturnRegister); 2293*0fca6ea1SDimitry Andric Opcode = SPIRV::OpConvertBF16ToFINTEL; 2294*0fca6ea1SDimitry Andric } else { 2295bdd1243dSDimitry Andric bool IsSourceSigned = 2296bdd1243dSDimitry Andric DemangledCall[DemangledCall.find_first_of('(') + 1] != 'u'; 2297bdd1243dSDimitry Andric Opcode = IsSourceSigned ? SPIRV::OpConvertSToF : SPIRV::OpConvertUToF; 2298bdd1243dSDimitry Andric } 2299*0fca6ea1SDimitry Andric } 2300bdd1243dSDimitry Andric } else if (GR->isScalarOrVectorOfType(Call->Arguments[0], 2301bdd1243dSDimitry Andric SPIRV::OpTypeFloat)) { 2302bdd1243dSDimitry Andric // Float -> ... 2303*0fca6ea1SDimitry Andric if (GR->isScalarOrVectorOfType(Call->ReturnRegister, SPIRV::OpTypeInt)) { 2304bdd1243dSDimitry Andric // Float -> Int 2305*0fca6ea1SDimitry Andric if (Builtin->IsBfloat16) { 2306*0fca6ea1SDimitry Andric const auto *ST = static_cast<const SPIRVSubtarget *>( 2307*0fca6ea1SDimitry Andric &MIRBuilder.getMF().getSubtarget()); 2308*0fca6ea1SDimitry Andric if (!ST->canUseExtension( 2309*0fca6ea1SDimitry Andric SPIRV::Extension::SPV_INTEL_bfloat16_conversion)) 2310*0fca6ea1SDimitry Andric NeedExtMsg = "SPV_INTEL_bfloat16_conversion"; 2311*0fca6ea1SDimitry Andric IsRightComponentsNumber = 2312*0fca6ea1SDimitry Andric GR->getScalarOrVectorComponentCount(Call->Arguments[0]) == 2313*0fca6ea1SDimitry Andric GR->getScalarOrVectorComponentCount(Call->ReturnRegister); 2314*0fca6ea1SDimitry Andric Opcode = SPIRV::OpConvertFToBF16INTEL; 2315*0fca6ea1SDimitry Andric } else { 2316bdd1243dSDimitry Andric Opcode = Builtin->IsDestinationSigned ? SPIRV::OpConvertFToS 2317bdd1243dSDimitry Andric : SPIRV::OpConvertFToU; 2318*0fca6ea1SDimitry Andric } 2319*0fca6ea1SDimitry Andric } else if (GR->isScalarOrVectorOfType(Call->ReturnRegister, 2320*0fca6ea1SDimitry Andric SPIRV::OpTypeFloat)) { 2321bdd1243dSDimitry Andric // Float -> Float 2322bdd1243dSDimitry Andric Opcode = SPIRV::OpFConvert; 2323bdd1243dSDimitry Andric } 2324*0fca6ea1SDimitry Andric } 2325bdd1243dSDimitry Andric 2326*0fca6ea1SDimitry Andric if (!NeedExtMsg.empty()) { 2327*0fca6ea1SDimitry Andric std::string DiagMsg = std::string(Builtin->Name) + 2328*0fca6ea1SDimitry Andric ": the builtin requires the following SPIR-V " 2329*0fca6ea1SDimitry Andric "extension: " + 2330*0fca6ea1SDimitry Andric NeedExtMsg; 2331*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str(), false); 2332*0fca6ea1SDimitry Andric } 2333*0fca6ea1SDimitry Andric if (!IsRightComponentsNumber) { 2334*0fca6ea1SDimitry Andric std::string DiagMsg = 2335*0fca6ea1SDimitry Andric std::string(Builtin->Name) + 2336*0fca6ea1SDimitry Andric ": result and argument must have the same number of components"; 2337*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str(), false); 2338*0fca6ea1SDimitry Andric } 2339bdd1243dSDimitry Andric assert(Opcode != SPIRV::OpNop && 2340bdd1243dSDimitry Andric "Conversion between the types not implemented!"); 2341bdd1243dSDimitry Andric 2342bdd1243dSDimitry Andric MIRBuilder.buildInstr(Opcode) 2343bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 2344bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 2345bdd1243dSDimitry Andric .addUse(Call->Arguments[0]); 2346bdd1243dSDimitry Andric return true; 2347bdd1243dSDimitry Andric } 2348bdd1243dSDimitry Andric 2349bdd1243dSDimitry Andric static bool generateVectorLoadStoreInst(const SPIRV::IncomingCall *Call, 2350bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2351bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2352bdd1243dSDimitry Andric // Lookup the vector load/store builtin in the TableGen records. 2353bdd1243dSDimitry Andric const SPIRV::VectorLoadStoreBuiltin *Builtin = 2354bdd1243dSDimitry Andric SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name, 2355bdd1243dSDimitry Andric Call->Builtin->Set); 2356bdd1243dSDimitry Andric // Build extended instruction. 2357bdd1243dSDimitry Andric auto MIB = 2358bdd1243dSDimitry Andric MIRBuilder.buildInstr(SPIRV::OpExtInst) 2359bdd1243dSDimitry Andric .addDef(Call->ReturnRegister) 2360bdd1243dSDimitry Andric .addUse(GR->getSPIRVTypeID(Call->ReturnType)) 2361bdd1243dSDimitry Andric .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std)) 2362bdd1243dSDimitry Andric .addImm(Builtin->Number); 2363bdd1243dSDimitry Andric for (auto Argument : Call->Arguments) 2364bdd1243dSDimitry Andric MIB.addUse(Argument); 2365*0fca6ea1SDimitry Andric if (Builtin->Name.contains("load") && Builtin->ElementCount > 1) 2366*0fca6ea1SDimitry Andric MIB.addImm(Builtin->ElementCount); 2367bdd1243dSDimitry Andric 2368bdd1243dSDimitry Andric // Rounding mode should be passed as a last argument in the MI for builtins 2369bdd1243dSDimitry Andric // like "vstorea_halfn_r". 2370bdd1243dSDimitry Andric if (Builtin->IsRounded) 2371bdd1243dSDimitry Andric MIB.addImm(static_cast<uint32_t>(Builtin->RoundingMode)); 2372bdd1243dSDimitry Andric return true; 2373bdd1243dSDimitry Andric } 2374bdd1243dSDimitry Andric 2375bdd1243dSDimitry Andric static bool generateLoadStoreInst(const SPIRV::IncomingCall *Call, 2376bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2377bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2378bdd1243dSDimitry Andric // Lookup the instruction opcode in the TableGen records. 2379bdd1243dSDimitry Andric const SPIRV::DemangledBuiltin *Builtin = Call->Builtin; 2380bdd1243dSDimitry Andric unsigned Opcode = 2381bdd1243dSDimitry Andric SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode; 2382bdd1243dSDimitry Andric bool IsLoad = Opcode == SPIRV::OpLoad; 2383bdd1243dSDimitry Andric // Build the instruction. 2384bdd1243dSDimitry Andric auto MIB = MIRBuilder.buildInstr(Opcode); 2385bdd1243dSDimitry Andric if (IsLoad) { 2386bdd1243dSDimitry Andric MIB.addDef(Call->ReturnRegister); 2387bdd1243dSDimitry Andric MIB.addUse(GR->getSPIRVTypeID(Call->ReturnType)); 2388bdd1243dSDimitry Andric } 2389bdd1243dSDimitry Andric // Add a pointer to the value to load/store. 2390bdd1243dSDimitry Andric MIB.addUse(Call->Arguments[0]); 2391bdd1243dSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 239206c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[0], &SPIRV::IDRegClass); 239306c3fb27SDimitry Andric // Add a value to store. 239406c3fb27SDimitry Andric if (!IsLoad) { 239506c3fb27SDimitry Andric MIB.addUse(Call->Arguments[1]); 239606c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[1], &SPIRV::IDRegClass); 239706c3fb27SDimitry Andric } 239806c3fb27SDimitry Andric // Add optional memory attributes and an alignment. 2399bdd1243dSDimitry Andric unsigned NumArgs = Call->Arguments.size(); 240006c3fb27SDimitry Andric if ((IsLoad && NumArgs >= 2) || NumArgs >= 3) { 2401bdd1243dSDimitry Andric MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 1 : 2], MRI)); 240206c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[IsLoad ? 1 : 2], &SPIRV::IDRegClass); 240306c3fb27SDimitry Andric } 240406c3fb27SDimitry Andric if ((IsLoad && NumArgs >= 3) || NumArgs >= 4) { 2405bdd1243dSDimitry Andric MIB.addImm(getConstFromIntrinsic(Call->Arguments[IsLoad ? 2 : 3], MRI)); 240606c3fb27SDimitry Andric MRI->setRegClass(Call->Arguments[IsLoad ? 2 : 3], &SPIRV::IDRegClass); 240706c3fb27SDimitry Andric } 2408bdd1243dSDimitry Andric return true; 2409bdd1243dSDimitry Andric } 2410bdd1243dSDimitry Andric 2411bdd1243dSDimitry Andric namespace SPIRV { 2412*0fca6ea1SDimitry Andric // Try to find a builtin function attributes by a demangled function name and 2413*0fca6ea1SDimitry Andric // return a tuple <builtin group, op code, ext instruction number>, or a special 2414*0fca6ea1SDimitry Andric // tuple value <-1, 0, 0> if the builtin function is not found. 2415*0fca6ea1SDimitry Andric // Not all builtin functions are supported, only those with a ready-to-use op 2416*0fca6ea1SDimitry Andric // code or instruction number defined in TableGen. 2417*0fca6ea1SDimitry Andric // TODO: consider a major rework of mapping demangled calls into a builtin 2418*0fca6ea1SDimitry Andric // functions to unify search and decrease number of individual cases. 2419*0fca6ea1SDimitry Andric std::tuple<int, unsigned, unsigned> 2420*0fca6ea1SDimitry Andric mapBuiltinToOpcode(const StringRef DemangledCall, 2421*0fca6ea1SDimitry Andric SPIRV::InstructionSet::InstructionSet Set) { 2422*0fca6ea1SDimitry Andric Register Reg; 2423*0fca6ea1SDimitry Andric SmallVector<Register> Args; 2424*0fca6ea1SDimitry Andric std::unique_ptr<const IncomingCall> Call = 2425*0fca6ea1SDimitry Andric lookupBuiltin(DemangledCall, Set, Reg, nullptr, Args); 2426*0fca6ea1SDimitry Andric if (!Call) 2427*0fca6ea1SDimitry Andric return std::make_tuple(-1, 0, 0); 2428*0fca6ea1SDimitry Andric 2429*0fca6ea1SDimitry Andric switch (Call->Builtin->Group) { 2430*0fca6ea1SDimitry Andric case SPIRV::Relational: 2431*0fca6ea1SDimitry Andric case SPIRV::Atomic: 2432*0fca6ea1SDimitry Andric case SPIRV::Barrier: 2433*0fca6ea1SDimitry Andric case SPIRV::CastToPtr: 2434*0fca6ea1SDimitry Andric case SPIRV::ImageMiscQuery: 2435*0fca6ea1SDimitry Andric case SPIRV::SpecConstant: 2436*0fca6ea1SDimitry Andric case SPIRV::Enqueue: 2437*0fca6ea1SDimitry Andric case SPIRV::AsyncCopy: 2438*0fca6ea1SDimitry Andric case SPIRV::LoadStore: 2439*0fca6ea1SDimitry Andric case SPIRV::CoopMatr: 2440*0fca6ea1SDimitry Andric if (const auto *R = 2441*0fca6ea1SDimitry Andric SPIRV::lookupNativeBuiltin(Call->Builtin->Name, Call->Builtin->Set)) 2442*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, R->Opcode, 0); 2443*0fca6ea1SDimitry Andric break; 2444*0fca6ea1SDimitry Andric case SPIRV::Extended: 2445*0fca6ea1SDimitry Andric if (const auto *R = SPIRV::lookupExtendedBuiltin(Call->Builtin->Name, 2446*0fca6ea1SDimitry Andric Call->Builtin->Set)) 2447*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, 0, R->Number); 2448*0fca6ea1SDimitry Andric break; 2449*0fca6ea1SDimitry Andric case SPIRV::VectorLoadStore: 2450*0fca6ea1SDimitry Andric if (const auto *R = SPIRV::lookupVectorLoadStoreBuiltin(Call->Builtin->Name, 2451*0fca6ea1SDimitry Andric Call->Builtin->Set)) 2452*0fca6ea1SDimitry Andric return std::make_tuple(SPIRV::Extended, 0, R->Number); 2453*0fca6ea1SDimitry Andric break; 2454*0fca6ea1SDimitry Andric case SPIRV::Group: 2455*0fca6ea1SDimitry Andric if (const auto *R = SPIRV::lookupGroupBuiltin(Call->Builtin->Name)) 2456*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, R->Opcode, 0); 2457*0fca6ea1SDimitry Andric break; 2458*0fca6ea1SDimitry Andric case SPIRV::AtomicFloating: 2459*0fca6ea1SDimitry Andric if (const auto *R = SPIRV::lookupAtomicFloatingBuiltin(Call->Builtin->Name)) 2460*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, R->Opcode, 0); 2461*0fca6ea1SDimitry Andric break; 2462*0fca6ea1SDimitry Andric case SPIRV::IntelSubgroups: 2463*0fca6ea1SDimitry Andric if (const auto *R = SPIRV::lookupIntelSubgroupsBuiltin(Call->Builtin->Name)) 2464*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, R->Opcode, 0); 2465*0fca6ea1SDimitry Andric break; 2466*0fca6ea1SDimitry Andric case SPIRV::GroupUniform: 2467*0fca6ea1SDimitry Andric if (const auto *R = SPIRV::lookupGroupUniformBuiltin(Call->Builtin->Name)) 2468*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, R->Opcode, 0); 2469*0fca6ea1SDimitry Andric break; 2470*0fca6ea1SDimitry Andric case SPIRV::WriteImage: 2471*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, SPIRV::OpImageWrite, 0); 2472*0fca6ea1SDimitry Andric case SPIRV::Select: 2473*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, TargetOpcode::G_SELECT, 0); 2474*0fca6ea1SDimitry Andric case SPIRV::Construct: 2475*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, SPIRV::OpCompositeConstruct, 2476*0fca6ea1SDimitry Andric 0); 2477*0fca6ea1SDimitry Andric case SPIRV::KernelClock: 2478*0fca6ea1SDimitry Andric return std::make_tuple(Call->Builtin->Group, SPIRV::OpReadClockKHR, 0); 2479*0fca6ea1SDimitry Andric default: 2480*0fca6ea1SDimitry Andric return std::make_tuple(-1, 0, 0); 2481*0fca6ea1SDimitry Andric } 2482*0fca6ea1SDimitry Andric return std::make_tuple(-1, 0, 0); 2483*0fca6ea1SDimitry Andric } 2484*0fca6ea1SDimitry Andric 2485bdd1243dSDimitry Andric std::optional<bool> lowerBuiltin(const StringRef DemangledCall, 2486bdd1243dSDimitry Andric SPIRV::InstructionSet::InstructionSet Set, 2487bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2488bdd1243dSDimitry Andric const Register OrigRet, const Type *OrigRetTy, 2489bdd1243dSDimitry Andric const SmallVectorImpl<Register> &Args, 2490bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2491bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Lowering builtin call: " << DemangledCall << "\n"); 2492bdd1243dSDimitry Andric 2493bdd1243dSDimitry Andric // SPIR-V type and return register. 2494bdd1243dSDimitry Andric Register ReturnRegister = OrigRet; 2495bdd1243dSDimitry Andric SPIRVType *ReturnType = nullptr; 2496bdd1243dSDimitry Andric if (OrigRetTy && !OrigRetTy->isVoidTy()) { 2497bdd1243dSDimitry Andric ReturnType = GR->assignTypeToVReg(OrigRetTy, OrigRet, MIRBuilder); 249806c3fb27SDimitry Andric if (!MIRBuilder.getMRI()->getRegClassOrNull(ReturnRegister)) 249906c3fb27SDimitry Andric MIRBuilder.getMRI()->setRegClass(ReturnRegister, &SPIRV::IDRegClass); 2500bdd1243dSDimitry Andric } else if (OrigRetTy && OrigRetTy->isVoidTy()) { 2501bdd1243dSDimitry Andric ReturnRegister = MIRBuilder.getMRI()->createVirtualRegister(&IDRegClass); 2502bdd1243dSDimitry Andric MIRBuilder.getMRI()->setType(ReturnRegister, LLT::scalar(32)); 2503bdd1243dSDimitry Andric ReturnType = GR->assignTypeToVReg(OrigRetTy, ReturnRegister, MIRBuilder); 2504bdd1243dSDimitry Andric } 2505bdd1243dSDimitry Andric 2506bdd1243dSDimitry Andric // Lookup the builtin in the TableGen records. 2507bdd1243dSDimitry Andric std::unique_ptr<const IncomingCall> Call = 2508bdd1243dSDimitry Andric lookupBuiltin(DemangledCall, Set, ReturnRegister, ReturnType, Args); 2509bdd1243dSDimitry Andric 2510bdd1243dSDimitry Andric if (!Call) { 2511bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Builtin record was not found!\n"); 2512bdd1243dSDimitry Andric return std::nullopt; 2513bdd1243dSDimitry Andric } 2514bdd1243dSDimitry Andric 2515bdd1243dSDimitry Andric // TODO: check if the provided args meet the builtin requirments. 2516bdd1243dSDimitry Andric assert(Args.size() >= Call->Builtin->MinNumArgs && 2517bdd1243dSDimitry Andric "Too few arguments to generate the builtin"); 2518bdd1243dSDimitry Andric if (Call->Builtin->MaxNumArgs && Args.size() > Call->Builtin->MaxNumArgs) 2519bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "More arguments provided than required!\n"); 2520bdd1243dSDimitry Andric 2521bdd1243dSDimitry Andric // Match the builtin with implementation based on the grouping. 2522bdd1243dSDimitry Andric switch (Call->Builtin->Group) { 2523bdd1243dSDimitry Andric case SPIRV::Extended: 2524bdd1243dSDimitry Andric return generateExtInst(Call.get(), MIRBuilder, GR); 2525bdd1243dSDimitry Andric case SPIRV::Relational: 2526bdd1243dSDimitry Andric return generateRelationalInst(Call.get(), MIRBuilder, GR); 2527bdd1243dSDimitry Andric case SPIRV::Group: 2528bdd1243dSDimitry Andric return generateGroupInst(Call.get(), MIRBuilder, GR); 2529bdd1243dSDimitry Andric case SPIRV::Variable: 2530bdd1243dSDimitry Andric return generateBuiltinVar(Call.get(), MIRBuilder, GR); 2531bdd1243dSDimitry Andric case SPIRV::Atomic: 2532bdd1243dSDimitry Andric return generateAtomicInst(Call.get(), MIRBuilder, GR); 2533*0fca6ea1SDimitry Andric case SPIRV::AtomicFloating: 2534*0fca6ea1SDimitry Andric return generateAtomicFloatingInst(Call.get(), MIRBuilder, GR); 2535bdd1243dSDimitry Andric case SPIRV::Barrier: 2536bdd1243dSDimitry Andric return generateBarrierInst(Call.get(), MIRBuilder, GR); 2537*0fca6ea1SDimitry Andric case SPIRV::CastToPtr: 2538*0fca6ea1SDimitry Andric return generateCastToPtrInst(Call.get(), MIRBuilder); 2539bdd1243dSDimitry Andric case SPIRV::Dot: 2540bdd1243dSDimitry Andric return generateDotOrFMulInst(Call.get(), MIRBuilder, GR); 2541*0fca6ea1SDimitry Andric case SPIRV::Wave: 2542*0fca6ea1SDimitry Andric return generateWaveInst(Call.get(), MIRBuilder, GR); 2543bdd1243dSDimitry Andric case SPIRV::GetQuery: 2544bdd1243dSDimitry Andric return generateGetQueryInst(Call.get(), MIRBuilder, GR); 2545bdd1243dSDimitry Andric case SPIRV::ImageSizeQuery: 2546bdd1243dSDimitry Andric return generateImageSizeQueryInst(Call.get(), MIRBuilder, GR); 2547bdd1243dSDimitry Andric case SPIRV::ImageMiscQuery: 2548bdd1243dSDimitry Andric return generateImageMiscQueryInst(Call.get(), MIRBuilder, GR); 2549bdd1243dSDimitry Andric case SPIRV::ReadImage: 2550bdd1243dSDimitry Andric return generateReadImageInst(DemangledCall, Call.get(), MIRBuilder, GR); 2551bdd1243dSDimitry Andric case SPIRV::WriteImage: 2552bdd1243dSDimitry Andric return generateWriteImageInst(Call.get(), MIRBuilder, GR); 2553bdd1243dSDimitry Andric case SPIRV::SampleImage: 2554bdd1243dSDimitry Andric return generateSampleImageInst(DemangledCall, Call.get(), MIRBuilder, GR); 2555bdd1243dSDimitry Andric case SPIRV::Select: 2556bdd1243dSDimitry Andric return generateSelectInst(Call.get(), MIRBuilder); 2557*0fca6ea1SDimitry Andric case SPIRV::Construct: 2558*0fca6ea1SDimitry Andric return generateConstructInst(Call.get(), MIRBuilder, GR); 2559bdd1243dSDimitry Andric case SPIRV::SpecConstant: 2560bdd1243dSDimitry Andric return generateSpecConstantInst(Call.get(), MIRBuilder, GR); 2561bdd1243dSDimitry Andric case SPIRV::Enqueue: 2562bdd1243dSDimitry Andric return generateEnqueueInst(Call.get(), MIRBuilder, GR); 2563bdd1243dSDimitry Andric case SPIRV::AsyncCopy: 2564bdd1243dSDimitry Andric return generateAsyncCopy(Call.get(), MIRBuilder, GR); 2565bdd1243dSDimitry Andric case SPIRV::Convert: 2566bdd1243dSDimitry Andric return generateConvertInst(DemangledCall, Call.get(), MIRBuilder, GR); 2567bdd1243dSDimitry Andric case SPIRV::VectorLoadStore: 2568bdd1243dSDimitry Andric return generateVectorLoadStoreInst(Call.get(), MIRBuilder, GR); 2569bdd1243dSDimitry Andric case SPIRV::LoadStore: 2570bdd1243dSDimitry Andric return generateLoadStoreInst(Call.get(), MIRBuilder, GR); 2571*0fca6ea1SDimitry Andric case SPIRV::IntelSubgroups: 2572*0fca6ea1SDimitry Andric return generateIntelSubgroupsInst(Call.get(), MIRBuilder, GR); 2573*0fca6ea1SDimitry Andric case SPIRV::GroupUniform: 2574*0fca6ea1SDimitry Andric return generateGroupUniformInst(Call.get(), MIRBuilder, GR); 2575*0fca6ea1SDimitry Andric case SPIRV::KernelClock: 2576*0fca6ea1SDimitry Andric return generateKernelClockInst(Call.get(), MIRBuilder, GR); 2577*0fca6ea1SDimitry Andric case SPIRV::CoopMatr: 2578*0fca6ea1SDimitry Andric return generateCoopMatrInst(Call.get(), MIRBuilder, GR); 2579bdd1243dSDimitry Andric } 2580bdd1243dSDimitry Andric return false; 2581bdd1243dSDimitry Andric } 2582bdd1243dSDimitry Andric 2583*0fca6ea1SDimitry Andric Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall, 2584*0fca6ea1SDimitry Andric unsigned ArgIdx, LLVMContext &Ctx) { 2585*0fca6ea1SDimitry Andric SmallVector<StringRef, 10> BuiltinArgsTypeStrs; 2586*0fca6ea1SDimitry Andric StringRef BuiltinArgs = 2587*0fca6ea1SDimitry Andric DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')')); 2588*0fca6ea1SDimitry Andric BuiltinArgs.split(BuiltinArgsTypeStrs, ',', -1, false); 2589*0fca6ea1SDimitry Andric if (ArgIdx >= BuiltinArgsTypeStrs.size()) 2590*0fca6ea1SDimitry Andric return nullptr; 2591*0fca6ea1SDimitry Andric StringRef TypeStr = BuiltinArgsTypeStrs[ArgIdx].trim(); 2592*0fca6ea1SDimitry Andric 2593*0fca6ea1SDimitry Andric // Parse strings representing OpenCL builtin types. 2594*0fca6ea1SDimitry Andric if (hasBuiltinTypePrefix(TypeStr)) { 2595*0fca6ea1SDimitry Andric // OpenCL builtin types in demangled call strings have the following format: 2596*0fca6ea1SDimitry Andric // e.g. ocl_image2d_ro 2597*0fca6ea1SDimitry Andric [[maybe_unused]] bool IsOCLBuiltinType = TypeStr.consume_front("ocl_"); 2598*0fca6ea1SDimitry Andric assert(IsOCLBuiltinType && "Invalid OpenCL builtin prefix"); 2599*0fca6ea1SDimitry Andric 2600*0fca6ea1SDimitry Andric // Check if this is pointer to a builtin type and not just pointer 2601*0fca6ea1SDimitry Andric // representing a builtin type. In case it is a pointer to builtin type, 2602*0fca6ea1SDimitry Andric // this will require additional handling in the method calling 2603*0fca6ea1SDimitry Andric // parseBuiltinCallArgumentBaseType(...) as this function only retrieves the 2604*0fca6ea1SDimitry Andric // base types. 2605*0fca6ea1SDimitry Andric if (TypeStr.ends_with("*")) 2606*0fca6ea1SDimitry Andric TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *")); 2607*0fca6ea1SDimitry Andric 2608*0fca6ea1SDimitry Andric return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t", 2609*0fca6ea1SDimitry Andric Ctx); 2610*0fca6ea1SDimitry Andric } 2611*0fca6ea1SDimitry Andric 2612*0fca6ea1SDimitry Andric // Parse type name in either "typeN" or "type vector[N]" format, where 2613*0fca6ea1SDimitry Andric // N is the number of elements of the vector. 2614*0fca6ea1SDimitry Andric Type *BaseType; 2615*0fca6ea1SDimitry Andric unsigned VecElts = 0; 2616*0fca6ea1SDimitry Andric 2617*0fca6ea1SDimitry Andric BaseType = parseBasicTypeName(TypeStr, Ctx); 2618*0fca6ea1SDimitry Andric if (!BaseType) 2619*0fca6ea1SDimitry Andric // Unable to recognize SPIRV type name. 2620*0fca6ea1SDimitry Andric return nullptr; 2621*0fca6ea1SDimitry Andric 2622*0fca6ea1SDimitry Andric // Handle "typeN*" or "type vector[N]*". 2623*0fca6ea1SDimitry Andric TypeStr.consume_back("*"); 2624*0fca6ea1SDimitry Andric 2625*0fca6ea1SDimitry Andric if (TypeStr.consume_front(" vector[")) 2626*0fca6ea1SDimitry Andric TypeStr = TypeStr.substr(0, TypeStr.find(']')); 2627*0fca6ea1SDimitry Andric 2628*0fca6ea1SDimitry Andric TypeStr.getAsInteger(10, VecElts); 2629*0fca6ea1SDimitry Andric if (VecElts > 0) 2630*0fca6ea1SDimitry Andric BaseType = VectorType::get( 2631*0fca6ea1SDimitry Andric BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false); 2632*0fca6ea1SDimitry Andric 2633*0fca6ea1SDimitry Andric return BaseType; 2634*0fca6ea1SDimitry Andric } 2635*0fca6ea1SDimitry Andric 263606c3fb27SDimitry Andric struct BuiltinType { 2637bdd1243dSDimitry Andric StringRef Name; 2638bdd1243dSDimitry Andric uint32_t Opcode; 2639bdd1243dSDimitry Andric }; 2640bdd1243dSDimitry Andric 264106c3fb27SDimitry Andric #define GET_BuiltinTypes_DECL 264206c3fb27SDimitry Andric #define GET_BuiltinTypes_IMPL 2643bdd1243dSDimitry Andric 264406c3fb27SDimitry Andric struct OpenCLType { 2645bdd1243dSDimitry Andric StringRef Name; 264606c3fb27SDimitry Andric StringRef SpirvTypeLiteral; 2647bdd1243dSDimitry Andric }; 2648bdd1243dSDimitry Andric 264906c3fb27SDimitry Andric #define GET_OpenCLTypes_DECL 265006c3fb27SDimitry Andric #define GET_OpenCLTypes_IMPL 2651bdd1243dSDimitry Andric 2652bdd1243dSDimitry Andric #include "SPIRVGenTables.inc" 2653bdd1243dSDimitry Andric } // namespace SPIRV 2654bdd1243dSDimitry Andric 2655bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 265606c3fb27SDimitry Andric // Misc functions for parsing builtin types. 2657bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 2658bdd1243dSDimitry Andric 265906c3fb27SDimitry Andric static Type *parseTypeString(const StringRef Name, LLVMContext &Context) { 26605f757f3fSDimitry Andric if (Name.starts_with("void")) 266106c3fb27SDimitry Andric return Type::getVoidTy(Context); 26625f757f3fSDimitry Andric else if (Name.starts_with("int") || Name.starts_with("uint")) 266306c3fb27SDimitry Andric return Type::getInt32Ty(Context); 26645f757f3fSDimitry Andric else if (Name.starts_with("float")) 266506c3fb27SDimitry Andric return Type::getFloatTy(Context); 26665f757f3fSDimitry Andric else if (Name.starts_with("half")) 266706c3fb27SDimitry Andric return Type::getHalfTy(Context); 2668*0fca6ea1SDimitry Andric report_fatal_error("Unable to recognize type!"); 2669bdd1243dSDimitry Andric } 2670bdd1243dSDimitry Andric 2671bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 2672bdd1243dSDimitry Andric // Implementation functions for builtin types. 2673bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 2674bdd1243dSDimitry Andric 267506c3fb27SDimitry Andric static SPIRVType *getNonParameterizedType(const TargetExtType *ExtensionType, 267606c3fb27SDimitry Andric const SPIRV::BuiltinType *TypeRecord, 2677bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2678bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2679bdd1243dSDimitry Andric unsigned Opcode = TypeRecord->Opcode; 2680bdd1243dSDimitry Andric // Create or get an existing type from GlobalRegistry. 268106c3fb27SDimitry Andric return GR->getOrCreateOpTypeByOpcode(ExtensionType, MIRBuilder, Opcode); 2682bdd1243dSDimitry Andric } 2683bdd1243dSDimitry Andric 2684bdd1243dSDimitry Andric static SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder, 2685bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 2686bdd1243dSDimitry Andric // Create or get an existing type from GlobalRegistry. 2687bdd1243dSDimitry Andric return GR->getOrCreateOpTypeSampler(MIRBuilder); 2688bdd1243dSDimitry Andric } 2689bdd1243dSDimitry Andric 269006c3fb27SDimitry Andric static SPIRVType *getPipeType(const TargetExtType *ExtensionType, 2691bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2692bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 269306c3fb27SDimitry Andric assert(ExtensionType->getNumIntParameters() == 1 && 269406c3fb27SDimitry Andric "Invalid number of parameters for SPIR-V pipe builtin!"); 2695bdd1243dSDimitry Andric // Create or get an existing type from GlobalRegistry. 269606c3fb27SDimitry Andric return GR->getOrCreateOpTypePipe(MIRBuilder, 269706c3fb27SDimitry Andric SPIRV::AccessQualifier::AccessQualifier( 269806c3fb27SDimitry Andric ExtensionType->getIntParameter(0))); 2699bdd1243dSDimitry Andric } 2700bdd1243dSDimitry Andric 2701*0fca6ea1SDimitry Andric static SPIRVType *getCoopMatrType(const TargetExtType *ExtensionType, 2702*0fca6ea1SDimitry Andric MachineIRBuilder &MIRBuilder, 2703*0fca6ea1SDimitry Andric SPIRVGlobalRegistry *GR) { 2704*0fca6ea1SDimitry Andric assert(ExtensionType->getNumIntParameters() == 4 && 2705*0fca6ea1SDimitry Andric "Invalid number of parameters for SPIR-V coop matrices builtin!"); 2706*0fca6ea1SDimitry Andric assert(ExtensionType->getNumTypeParameters() == 1 && 2707*0fca6ea1SDimitry Andric "SPIR-V coop matrices builtin type must have a type parameter!"); 2708*0fca6ea1SDimitry Andric const SPIRVType *ElemType = 2709*0fca6ea1SDimitry Andric GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder); 2710*0fca6ea1SDimitry Andric // Create or get an existing type from GlobalRegistry. 2711*0fca6ea1SDimitry Andric return GR->getOrCreateOpTypeCoopMatr( 2712*0fca6ea1SDimitry Andric MIRBuilder, ExtensionType, ElemType, ExtensionType->getIntParameter(0), 2713*0fca6ea1SDimitry Andric ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2), 2714*0fca6ea1SDimitry Andric ExtensionType->getIntParameter(3)); 2715*0fca6ea1SDimitry Andric } 2716*0fca6ea1SDimitry Andric 2717bdd1243dSDimitry Andric static SPIRVType * 271806c3fb27SDimitry Andric getImageType(const TargetExtType *ExtensionType, 271906c3fb27SDimitry Andric const SPIRV::AccessQualifier::AccessQualifier Qualifier, 2720bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) { 272106c3fb27SDimitry Andric assert(ExtensionType->getNumTypeParameters() == 1 && 272206c3fb27SDimitry Andric "SPIR-V image builtin type must have sampled type parameter!"); 272306c3fb27SDimitry Andric const SPIRVType *SampledType = 272406c3fb27SDimitry Andric GR->getOrCreateSPIRVType(ExtensionType->getTypeParameter(0), MIRBuilder); 272506c3fb27SDimitry Andric assert(ExtensionType->getNumIntParameters() == 7 && 272606c3fb27SDimitry Andric "Invalid number of parameters for SPIR-V image builtin!"); 272706c3fb27SDimitry Andric // Create or get an existing type from GlobalRegistry. 2728bdd1243dSDimitry Andric return GR->getOrCreateOpTypeImage( 272906c3fb27SDimitry Andric MIRBuilder, SampledType, 273006c3fb27SDimitry Andric SPIRV::Dim::Dim(ExtensionType->getIntParameter(0)), 273106c3fb27SDimitry Andric ExtensionType->getIntParameter(1), ExtensionType->getIntParameter(2), 273206c3fb27SDimitry Andric ExtensionType->getIntParameter(3), ExtensionType->getIntParameter(4), 273306c3fb27SDimitry Andric SPIRV::ImageFormat::ImageFormat(ExtensionType->getIntParameter(5)), 273406c3fb27SDimitry Andric Qualifier == SPIRV::AccessQualifier::WriteOnly 2735bdd1243dSDimitry Andric ? SPIRV::AccessQualifier::WriteOnly 273606c3fb27SDimitry Andric : SPIRV::AccessQualifier::AccessQualifier( 273706c3fb27SDimitry Andric ExtensionType->getIntParameter(6))); 2738bdd1243dSDimitry Andric } 2739bdd1243dSDimitry Andric 274006c3fb27SDimitry Andric static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType, 2741bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2742bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 274306c3fb27SDimitry Andric SPIRVType *OpaqueImageType = getImageType( 274406c3fb27SDimitry Andric OpaqueType, SPIRV::AccessQualifier::ReadOnly, MIRBuilder, GR); 274506c3fb27SDimitry Andric // Create or get an existing type from GlobalRegistry. 274606c3fb27SDimitry Andric return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder); 2747bdd1243dSDimitry Andric } 2748bdd1243dSDimitry Andric 2749bdd1243dSDimitry Andric namespace SPIRV { 2750*0fca6ea1SDimitry Andric TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName, 2751*0fca6ea1SDimitry Andric LLVMContext &Context) { 27525f757f3fSDimitry Andric StringRef NameWithParameters = TypeName; 27535f757f3fSDimitry Andric 27545f757f3fSDimitry Andric // Pointers-to-opaque-structs representing OpenCL types are first translated 27555f757f3fSDimitry Andric // to equivalent SPIR-V types. OpenCL builtin type names should have the 27565f757f3fSDimitry Andric // following format: e.g. %opencl.event_t 27575f757f3fSDimitry Andric if (NameWithParameters.starts_with("opencl.")) { 27585f757f3fSDimitry Andric const SPIRV::OpenCLType *OCLTypeRecord = 27595f757f3fSDimitry Andric SPIRV::lookupOpenCLType(NameWithParameters); 27605f757f3fSDimitry Andric if (!OCLTypeRecord) 27615f757f3fSDimitry Andric report_fatal_error("Missing TableGen record for OpenCL type: " + 27625f757f3fSDimitry Andric NameWithParameters); 27635f757f3fSDimitry Andric NameWithParameters = OCLTypeRecord->SpirvTypeLiteral; 27645f757f3fSDimitry Andric // Continue with the SPIR-V builtin type... 27655f757f3fSDimitry Andric } 27665f757f3fSDimitry Andric 27675f757f3fSDimitry Andric // Names of the opaque structs representing a SPIR-V builtins without 27685f757f3fSDimitry Andric // parameters should have the following format: e.g. %spirv.Event 27695f757f3fSDimitry Andric assert(NameWithParameters.starts_with("spirv.") && 27705f757f3fSDimitry Andric "Unknown builtin opaque type!"); 27715f757f3fSDimitry Andric 27725f757f3fSDimitry Andric // Parameterized SPIR-V builtins names follow this format: 27735f757f3fSDimitry Andric // e.g. %spirv.Image._void_1_0_0_0_0_0_0, %spirv.Pipe._0 2774cb14a3feSDimitry Andric if (!NameWithParameters.contains('_')) 2775*0fca6ea1SDimitry Andric return TargetExtType::get(Context, NameWithParameters); 27765f757f3fSDimitry Andric 27775f757f3fSDimitry Andric SmallVector<StringRef> Parameters; 27785f757f3fSDimitry Andric unsigned BaseNameLength = NameWithParameters.find('_') - 1; 27795f757f3fSDimitry Andric SplitString(NameWithParameters.substr(BaseNameLength + 1), Parameters, "_"); 27805f757f3fSDimitry Andric 27815f757f3fSDimitry Andric SmallVector<Type *, 1> TypeParameters; 27825f757f3fSDimitry Andric bool HasTypeParameter = !isDigit(Parameters[0][0]); 27835f757f3fSDimitry Andric if (HasTypeParameter) 2784*0fca6ea1SDimitry Andric TypeParameters.push_back(parseTypeString(Parameters[0], Context)); 27855f757f3fSDimitry Andric SmallVector<unsigned> IntParameters; 27865f757f3fSDimitry Andric for (unsigned i = HasTypeParameter ? 1 : 0; i < Parameters.size(); i++) { 27875f757f3fSDimitry Andric unsigned IntParameter = 0; 27885f757f3fSDimitry Andric bool ValidLiteral = !Parameters[i].getAsInteger(10, IntParameter); 2789*0fca6ea1SDimitry Andric (void)ValidLiteral; 27905f757f3fSDimitry Andric assert(ValidLiteral && 27915f757f3fSDimitry Andric "Invalid format of SPIR-V builtin parameter literal!"); 27925f757f3fSDimitry Andric IntParameters.push_back(IntParameter); 27935f757f3fSDimitry Andric } 2794*0fca6ea1SDimitry Andric return TargetExtType::get(Context, 27955f757f3fSDimitry Andric NameWithParameters.substr(0, BaseNameLength), 27965f757f3fSDimitry Andric TypeParameters, IntParameters); 27975f757f3fSDimitry Andric } 27985f757f3fSDimitry Andric 279906c3fb27SDimitry Andric SPIRVType *lowerBuiltinType(const Type *OpaqueType, 2800bdd1243dSDimitry Andric SPIRV::AccessQualifier::AccessQualifier AccessQual, 2801bdd1243dSDimitry Andric MachineIRBuilder &MIRBuilder, 2802bdd1243dSDimitry Andric SPIRVGlobalRegistry *GR) { 280306c3fb27SDimitry Andric // In LLVM IR, SPIR-V and OpenCL builtin types are represented as either 280406c3fb27SDimitry Andric // target(...) target extension types or pointers-to-opaque-structs. The 280506c3fb27SDimitry Andric // approach relying on structs is deprecated and works only in the non-opaque 280606c3fb27SDimitry Andric // pointer mode (-opaque-pointers=0). 280706c3fb27SDimitry Andric // In order to maintain compatibility with LLVM IR generated by older versions 280806c3fb27SDimitry Andric // of Clang and LLVM/SPIR-V Translator, the pointers-to-opaque-structs are 280906c3fb27SDimitry Andric // "translated" to target extension types. This translation is temporary and 281006c3fb27SDimitry Andric // will be removed in the future release of LLVM. 281106c3fb27SDimitry Andric const TargetExtType *BuiltinType = dyn_cast<TargetExtType>(OpaqueType); 281206c3fb27SDimitry Andric if (!BuiltinType) 28135f757f3fSDimitry Andric BuiltinType = parseBuiltinTypeNameToTargetExtType( 2814*0fca6ea1SDimitry Andric OpaqueType->getStructName().str(), MIRBuilder.getContext()); 281506c3fb27SDimitry Andric 2816bdd1243dSDimitry Andric unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs(); 2817bdd1243dSDimitry Andric 281806c3fb27SDimitry Andric const StringRef Name = BuiltinType->getName(); 2819bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n"); 2820bdd1243dSDimitry Andric 2821bdd1243dSDimitry Andric // Lookup the demangled builtin type in the TableGen records. 282206c3fb27SDimitry Andric const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name); 2823bdd1243dSDimitry Andric if (!TypeRecord) 2824bdd1243dSDimitry Andric report_fatal_error("Missing TableGen record for builtin type: " + Name); 2825bdd1243dSDimitry Andric 2826bdd1243dSDimitry Andric // "Lower" the BuiltinType into TargetType. The following get<...>Type methods 282706c3fb27SDimitry Andric // use the implementation details from TableGen records or TargetExtType 282806c3fb27SDimitry Andric // parameters to either create a new OpType<...> machine instruction or get an 282906c3fb27SDimitry Andric // existing equivalent SPIRVType from GlobalRegistry. 2830bdd1243dSDimitry Andric SPIRVType *TargetType; 2831bdd1243dSDimitry Andric switch (TypeRecord->Opcode) { 2832bdd1243dSDimitry Andric case SPIRV::OpTypeImage: 283306c3fb27SDimitry Andric TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR); 2834bdd1243dSDimitry Andric break; 2835bdd1243dSDimitry Andric case SPIRV::OpTypePipe: 283606c3fb27SDimitry Andric TargetType = getPipeType(BuiltinType, MIRBuilder, GR); 2837bdd1243dSDimitry Andric break; 2838bdd1243dSDimitry Andric case SPIRV::OpTypeDeviceEvent: 2839bdd1243dSDimitry Andric TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder); 2840bdd1243dSDimitry Andric break; 2841bdd1243dSDimitry Andric case SPIRV::OpTypeSampler: 2842bdd1243dSDimitry Andric TargetType = getSamplerType(MIRBuilder, GR); 2843bdd1243dSDimitry Andric break; 2844bdd1243dSDimitry Andric case SPIRV::OpTypeSampledImage: 284506c3fb27SDimitry Andric TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR); 2846bdd1243dSDimitry Andric break; 2847*0fca6ea1SDimitry Andric case SPIRV::OpTypeCooperativeMatrixKHR: 2848*0fca6ea1SDimitry Andric TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR); 2849*0fca6ea1SDimitry Andric break; 2850bdd1243dSDimitry Andric default: 285106c3fb27SDimitry Andric TargetType = 285206c3fb27SDimitry Andric getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR); 2853bdd1243dSDimitry Andric break; 2854bdd1243dSDimitry Andric } 2855bdd1243dSDimitry Andric 2856bdd1243dSDimitry Andric // Emit OpName instruction if a new OpType<...> instruction was added 2857bdd1243dSDimitry Andric // (equivalent type was not found in GlobalRegistry). 2858bdd1243dSDimitry Andric if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs()) 285906c3fb27SDimitry Andric buildOpName(GR->getSPIRVTypeID(TargetType), Name, MIRBuilder); 2860bdd1243dSDimitry Andric 2861bdd1243dSDimitry Andric return TargetType; 2862bdd1243dSDimitry Andric } 2863bdd1243dSDimitry Andric } // namespace SPIRV 2864bdd1243dSDimitry Andric } // namespace llvm 2865