181ad6265SDimitry Andric //===- SPIRVModuleAnalysis.cpp - analysis of global instrs & regs - C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // The analysis collects instructions that should be output at the module level 1081ad6265SDimitry Andric // and performs the global register numbering. 1181ad6265SDimitry Andric // 1281ad6265SDimitry Andric // The results of this analysis are used in AsmPrinter to rename registers 1381ad6265SDimitry Andric // globally and to output required instructions at the module level. 1481ad6265SDimitry Andric // 1581ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1681ad6265SDimitry Andric 1781ad6265SDimitry Andric #include "SPIRVModuleAnalysis.h" 185f757f3fSDimitry Andric #include "MCTargetDesc/SPIRVBaseInfo.h" 195f757f3fSDimitry Andric #include "MCTargetDesc/SPIRVMCTargetDesc.h" 2081ad6265SDimitry Andric #include "SPIRV.h" 2181ad6265SDimitry Andric #include "SPIRVSubtarget.h" 2281ad6265SDimitry Andric #include "SPIRVTargetMachine.h" 2381ad6265SDimitry Andric #include "SPIRVUtils.h" 2481ad6265SDimitry Andric #include "TargetInfo/SPIRVTargetInfo.h" 25bdd1243dSDimitry Andric #include "llvm/ADT/STLExtras.h" 2681ad6265SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 2781ad6265SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 2881ad6265SDimitry Andric 2981ad6265SDimitry Andric using namespace llvm; 3081ad6265SDimitry Andric 3181ad6265SDimitry Andric #define DEBUG_TYPE "spirv-module-analysis" 3281ad6265SDimitry Andric 33753f127fSDimitry Andric static cl::opt<bool> 34753f127fSDimitry Andric SPVDumpDeps("spv-dump-deps", 35753f127fSDimitry Andric cl::desc("Dump MIR with SPIR-V dependencies info"), 36753f127fSDimitry Andric cl::Optional, cl::init(false)); 37753f127fSDimitry Andric 38*0fca6ea1SDimitry Andric static cl::list<SPIRV::Capability::Capability> 39*0fca6ea1SDimitry Andric AvoidCapabilities("avoid-spirv-capabilities", 40*0fca6ea1SDimitry Andric cl::desc("SPIR-V capabilities to avoid if there are " 41*0fca6ea1SDimitry Andric "other options enabling a feature"), 42*0fca6ea1SDimitry Andric cl::ZeroOrMore, cl::Hidden, 43*0fca6ea1SDimitry Andric cl::values(clEnumValN(SPIRV::Capability::Shader, "Shader", 44*0fca6ea1SDimitry Andric "SPIR-V Shader capability"))); 45*0fca6ea1SDimitry Andric // Use sets instead of cl::list to check "if contains" condition 46*0fca6ea1SDimitry Andric struct AvoidCapabilitiesSet { 47*0fca6ea1SDimitry Andric SmallSet<SPIRV::Capability::Capability, 4> S; 48*0fca6ea1SDimitry Andric AvoidCapabilitiesSet() { 49*0fca6ea1SDimitry Andric for (auto Cap : AvoidCapabilities) 50*0fca6ea1SDimitry Andric S.insert(Cap); 51*0fca6ea1SDimitry Andric } 52*0fca6ea1SDimitry Andric }; 53*0fca6ea1SDimitry Andric 5481ad6265SDimitry Andric char llvm::SPIRVModuleAnalysis::ID = 0; 5581ad6265SDimitry Andric 5681ad6265SDimitry Andric namespace llvm { 5781ad6265SDimitry Andric void initializeSPIRVModuleAnalysisPass(PassRegistry &); 5881ad6265SDimitry Andric } // namespace llvm 5981ad6265SDimitry Andric 6081ad6265SDimitry Andric INITIALIZE_PASS(SPIRVModuleAnalysis, DEBUG_TYPE, "SPIRV module analysis", true, 6181ad6265SDimitry Andric true) 6281ad6265SDimitry Andric 6381ad6265SDimitry Andric // Retrieve an unsigned from an MDNode with a list of them as operands. 6481ad6265SDimitry Andric static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex, 6581ad6265SDimitry Andric unsigned DefaultVal = 0) { 6681ad6265SDimitry Andric if (MdNode && OpIndex < MdNode->getNumOperands()) { 6781ad6265SDimitry Andric const auto &Op = MdNode->getOperand(OpIndex); 6881ad6265SDimitry Andric return mdconst::extract<ConstantInt>(Op)->getZExtValue(); 6981ad6265SDimitry Andric } 7081ad6265SDimitry Andric return DefaultVal; 7181ad6265SDimitry Andric } 7281ad6265SDimitry Andric 73bdd1243dSDimitry Andric static SPIRV::Requirements 74bdd1243dSDimitry Andric getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category, 75bdd1243dSDimitry Andric unsigned i, const SPIRVSubtarget &ST, 76bdd1243dSDimitry Andric SPIRV::RequirementHandler &Reqs) { 77*0fca6ea1SDimitry Andric static AvoidCapabilitiesSet 78*0fca6ea1SDimitry Andric AvoidCaps; // contains capabilities to avoid if there is another option 79*0fca6ea1SDimitry Andric 80*0fca6ea1SDimitry Andric VersionTuple ReqMinVer = getSymbolicOperandMinVersion(Category, i); 81*0fca6ea1SDimitry Andric VersionTuple ReqMaxVer = getSymbolicOperandMaxVersion(Category, i); 82*0fca6ea1SDimitry Andric VersionTuple SPIRVVersion = ST.getSPIRVVersion(); 83*0fca6ea1SDimitry Andric bool MinVerOK = SPIRVVersion.empty() || SPIRVVersion >= ReqMinVer; 84*0fca6ea1SDimitry Andric bool MaxVerOK = 85*0fca6ea1SDimitry Andric ReqMaxVer.empty() || SPIRVVersion.empty() || SPIRVVersion <= ReqMaxVer; 86bdd1243dSDimitry Andric CapabilityList ReqCaps = getSymbolicOperandCapabilities(Category, i); 87bdd1243dSDimitry Andric ExtensionList ReqExts = getSymbolicOperandExtensions(Category, i); 88bdd1243dSDimitry Andric if (ReqCaps.empty()) { 89bdd1243dSDimitry Andric if (ReqExts.empty()) { 90bdd1243dSDimitry Andric if (MinVerOK && MaxVerOK) 91bdd1243dSDimitry Andric return {true, {}, {}, ReqMinVer, ReqMaxVer}; 92*0fca6ea1SDimitry Andric return {false, {}, {}, VersionTuple(), VersionTuple()}; 93bdd1243dSDimitry Andric } 94bdd1243dSDimitry Andric } else if (MinVerOK && MaxVerOK) { 95*0fca6ea1SDimitry Andric if (ReqCaps.size() == 1) { 96*0fca6ea1SDimitry Andric auto Cap = ReqCaps[0]; 97bdd1243dSDimitry Andric if (Reqs.isCapabilityAvailable(Cap)) 98*0fca6ea1SDimitry Andric return {true, {Cap}, ReqExts, ReqMinVer, ReqMaxVer}; 99*0fca6ea1SDimitry Andric } else { 100*0fca6ea1SDimitry Andric // By SPIR-V specification: "If an instruction, enumerant, or other 101*0fca6ea1SDimitry Andric // feature specifies multiple enabling capabilities, only one such 102*0fca6ea1SDimitry Andric // capability needs to be declared to use the feature." However, one 103*0fca6ea1SDimitry Andric // capability may be preferred over another. We use command line 104*0fca6ea1SDimitry Andric // argument(s) and AvoidCapabilities to avoid selection of certain 105*0fca6ea1SDimitry Andric // capabilities if there are other options. 106*0fca6ea1SDimitry Andric CapabilityList UseCaps; 107*0fca6ea1SDimitry Andric for (auto Cap : ReqCaps) 108*0fca6ea1SDimitry Andric if (Reqs.isCapabilityAvailable(Cap)) 109*0fca6ea1SDimitry Andric UseCaps.push_back(Cap); 110*0fca6ea1SDimitry Andric for (size_t i = 0, Sz = UseCaps.size(); i < Sz; ++i) { 111*0fca6ea1SDimitry Andric auto Cap = UseCaps[i]; 112*0fca6ea1SDimitry Andric if (i == Sz - 1 || !AvoidCaps.S.contains(Cap)) 113*0fca6ea1SDimitry Andric return {true, {Cap}, ReqExts, ReqMinVer, ReqMaxVer}; 114*0fca6ea1SDimitry Andric } 115bdd1243dSDimitry Andric } 116bdd1243dSDimitry Andric } 117bdd1243dSDimitry Andric // If there are no capabilities, or we can't satisfy the version or 118bdd1243dSDimitry Andric // capability requirements, use the list of extensions (if the subtarget 119bdd1243dSDimitry Andric // can handle them all). 120bdd1243dSDimitry Andric if (llvm::all_of(ReqExts, [&ST](const SPIRV::Extension::Extension &Ext) { 121bdd1243dSDimitry Andric return ST.canUseExtension(Ext); 122bdd1243dSDimitry Andric })) { 123*0fca6ea1SDimitry Andric return {true, 124*0fca6ea1SDimitry Andric {}, 125*0fca6ea1SDimitry Andric ReqExts, 126*0fca6ea1SDimitry Andric VersionTuple(), 127*0fca6ea1SDimitry Andric VersionTuple()}; // TODO: add versions to extensions. 128bdd1243dSDimitry Andric } 129*0fca6ea1SDimitry Andric return {false, {}, {}, VersionTuple(), VersionTuple()}; 130bdd1243dSDimitry Andric } 131bdd1243dSDimitry Andric 13281ad6265SDimitry Andric void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { 13381ad6265SDimitry Andric MAI.MaxID = 0; 13481ad6265SDimitry Andric for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++) 13581ad6265SDimitry Andric MAI.MS[i].clear(); 13681ad6265SDimitry Andric MAI.RegisterAliasTable.clear(); 13781ad6265SDimitry Andric MAI.InstrsToDelete.clear(); 138bdd1243dSDimitry Andric MAI.FuncMap.clear(); 13981ad6265SDimitry Andric MAI.GlobalVarList.clear(); 140fcaf7f86SDimitry Andric MAI.ExtInstSetMap.clear(); 141bdd1243dSDimitry Andric MAI.Reqs.clear(); 142bdd1243dSDimitry Andric MAI.Reqs.initAvailableCapabilities(*ST); 14381ad6265SDimitry Andric 14481ad6265SDimitry Andric // TODO: determine memory model and source language from the configuratoin. 145fcaf7f86SDimitry Andric if (auto MemModel = M.getNamedMetadata("spirv.MemoryModel")) { 146fcaf7f86SDimitry Andric auto MemMD = MemModel->getOperand(0); 147bdd1243dSDimitry Andric MAI.Addr = static_cast<SPIRV::AddressingModel::AddressingModel>( 148bdd1243dSDimitry Andric getMetadataUInt(MemMD, 0)); 149bdd1243dSDimitry Andric MAI.Mem = 150bdd1243dSDimitry Andric static_cast<SPIRV::MemoryModel::MemoryModel>(getMetadataUInt(MemMD, 1)); 151fcaf7f86SDimitry Andric } else { 1525f757f3fSDimitry Andric // TODO: Add support for VulkanMemoryModel. 1535f757f3fSDimitry Andric MAI.Mem = ST->isOpenCLEnv() ? SPIRV::MemoryModel::OpenCL 1545f757f3fSDimitry Andric : SPIRV::MemoryModel::GLSL450; 1555f757f3fSDimitry Andric if (MAI.Mem == SPIRV::MemoryModel::OpenCL) { 15681ad6265SDimitry Andric unsigned PtrSize = ST->getPointerSize(); 15781ad6265SDimitry Andric MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32 15881ad6265SDimitry Andric : PtrSize == 64 ? SPIRV::AddressingModel::Physical64 15981ad6265SDimitry Andric : SPIRV::AddressingModel::Logical; 1605f757f3fSDimitry Andric } else { 1615f757f3fSDimitry Andric // TODO: Add support for PhysicalStorageBufferAddress. 1625f757f3fSDimitry Andric MAI.Addr = SPIRV::AddressingModel::Logical; 1635f757f3fSDimitry Andric } 164fcaf7f86SDimitry Andric } 16581ad6265SDimitry Andric // Get the OpenCL version number from metadata. 16681ad6265SDimitry Andric // TODO: support other source languages. 16781ad6265SDimitry Andric if (auto VerNode = M.getNamedMetadata("opencl.ocl.version")) { 168fcaf7f86SDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_C; 169fcaf7f86SDimitry Andric // Construct version literal in accordance with SPIRV-LLVM-Translator. 170fcaf7f86SDimitry Andric // TODO: support multiple OCL version metadata. 171fcaf7f86SDimitry Andric assert(VerNode->getNumOperands() > 0 && "Invalid SPIR"); 17281ad6265SDimitry Andric auto VersionMD = VerNode->getOperand(0); 17381ad6265SDimitry Andric unsigned MajorNum = getMetadataUInt(VersionMD, 0, 2); 17481ad6265SDimitry Andric unsigned MinorNum = getMetadataUInt(VersionMD, 1); 17581ad6265SDimitry Andric unsigned RevNum = getMetadataUInt(VersionMD, 2); 176*0fca6ea1SDimitry Andric // Prevent Major part of OpenCL version to be 0 177*0fca6ea1SDimitry Andric MAI.SrcLangVersion = 178*0fca6ea1SDimitry Andric (std::max(1U, MajorNum) * 100 + MinorNum) * 1000 + RevNum; 179*0fca6ea1SDimitry Andric } else { 180*0fca6ea1SDimitry Andric // If there is no information about OpenCL version we are forced to generate 181*0fca6ea1SDimitry Andric // OpenCL 1.0 by default for the OpenCL environment to avoid puzzling 182*0fca6ea1SDimitry Andric // run-times with Unknown/0.0 version output. For a reference, LLVM-SPIRV 183*0fca6ea1SDimitry Andric // Translator avoids potential issues with run-times in a similar manner. 184*0fca6ea1SDimitry Andric if (ST->isOpenCLEnv()) { 185*0fca6ea1SDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_CPP; 186*0fca6ea1SDimitry Andric MAI.SrcLangVersion = 100000; 187fcaf7f86SDimitry Andric } else { 188fcaf7f86SDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::Unknown; 189fcaf7f86SDimitry Andric MAI.SrcLangVersion = 0; 190fcaf7f86SDimitry Andric } 191*0fca6ea1SDimitry Andric } 192fcaf7f86SDimitry Andric 193fcaf7f86SDimitry Andric if (auto ExtNode = M.getNamedMetadata("opencl.used.extensions")) { 194fcaf7f86SDimitry Andric for (unsigned I = 0, E = ExtNode->getNumOperands(); I != E; ++I) { 195fcaf7f86SDimitry Andric MDNode *MD = ExtNode->getOperand(I); 196fcaf7f86SDimitry Andric if (!MD || MD->getNumOperands() == 0) 197fcaf7f86SDimitry Andric continue; 198fcaf7f86SDimitry Andric for (unsigned J = 0, N = MD->getNumOperands(); J != N; ++J) 199fcaf7f86SDimitry Andric MAI.SrcExt.insert(cast<MDString>(MD->getOperand(J))->getString()); 20081ad6265SDimitry Andric } 20181ad6265SDimitry Andric } 20281ad6265SDimitry Andric 203bdd1243dSDimitry Andric // Update required capabilities for this memory model, addressing model and 204bdd1243dSDimitry Andric // source language. 205bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand, 206bdd1243dSDimitry Andric MAI.Mem, *ST); 207bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::SourceLanguageOperand, 208bdd1243dSDimitry Andric MAI.SrcLang, *ST); 209bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand, 210bdd1243dSDimitry Andric MAI.Addr, *ST); 211bdd1243dSDimitry Andric 2125f757f3fSDimitry Andric if (ST->isOpenCLEnv()) { 213fcaf7f86SDimitry Andric // TODO: check if it's required by default. 2145f757f3fSDimitry Andric MAI.ExtInstSetMap[static_cast<unsigned>( 2155f757f3fSDimitry Andric SPIRV::InstructionSet::OpenCL_std)] = 216fcaf7f86SDimitry Andric Register::index2VirtReg(MAI.getNextID()); 21781ad6265SDimitry Andric } 2185f757f3fSDimitry Andric } 21981ad6265SDimitry Andric 220753f127fSDimitry Andric // Collect MI which defines the register in the given machine function. 221753f127fSDimitry Andric static void collectDefInstr(Register Reg, const MachineFunction *MF, 222753f127fSDimitry Andric SPIRV::ModuleAnalysisInfo *MAI, 223753f127fSDimitry Andric SPIRV::ModuleSectionType MSType, 224753f127fSDimitry Andric bool DoInsert = true) { 225753f127fSDimitry Andric assert(MAI->hasRegisterAlias(MF, Reg) && "Cannot find register alias"); 226753f127fSDimitry Andric MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(Reg); 227753f127fSDimitry Andric assert(MI && "There should be an instruction that defines the register"); 228753f127fSDimitry Andric MAI->setSkipEmission(MI); 229753f127fSDimitry Andric if (DoInsert) 230753f127fSDimitry Andric MAI->MS[MSType].push_back(MI); 231753f127fSDimitry Andric } 232753f127fSDimitry Andric 233753f127fSDimitry Andric void SPIRVModuleAnalysis::collectGlobalEntities( 234753f127fSDimitry Andric const std::vector<SPIRV::DTSortableEntry *> &DepsGraph, 235753f127fSDimitry Andric SPIRV::ModuleSectionType MSType, 236753f127fSDimitry Andric std::function<bool(const SPIRV::DTSortableEntry *)> Pred, 237fcaf7f86SDimitry Andric bool UsePreOrder = false) { 238753f127fSDimitry Andric DenseSet<const SPIRV::DTSortableEntry *> Visited; 239753f127fSDimitry Andric for (const auto *E : DepsGraph) { 240753f127fSDimitry Andric std::function<void(const SPIRV::DTSortableEntry *)> RecHoistUtil; 241753f127fSDimitry Andric // NOTE: here we prefer recursive approach over iterative because 242753f127fSDimitry Andric // we don't expect depchains long enough to cause SO. 243753f127fSDimitry Andric RecHoistUtil = [MSType, UsePreOrder, &Visited, &Pred, 244753f127fSDimitry Andric &RecHoistUtil](const SPIRV::DTSortableEntry *E) { 245753f127fSDimitry Andric if (Visited.count(E) || !Pred(E)) 246753f127fSDimitry Andric return; 247753f127fSDimitry Andric Visited.insert(E); 248753f127fSDimitry Andric 249753f127fSDimitry Andric // Traversing deps graph in post-order allows us to get rid of 250753f127fSDimitry Andric // register aliases preprocessing. 251753f127fSDimitry Andric // But pre-order is required for correct processing of function 252753f127fSDimitry Andric // declaration and arguments processing. 253753f127fSDimitry Andric if (!UsePreOrder) 254753f127fSDimitry Andric for (auto *S : E->getDeps()) 255753f127fSDimitry Andric RecHoistUtil(S); 256753f127fSDimitry Andric 257753f127fSDimitry Andric Register GlobalReg = Register::index2VirtReg(MAI.getNextID()); 258753f127fSDimitry Andric bool IsFirst = true; 259753f127fSDimitry Andric for (auto &U : *E) { 260753f127fSDimitry Andric const MachineFunction *MF = U.first; 261753f127fSDimitry Andric Register Reg = U.second; 262753f127fSDimitry Andric MAI.setRegisterAlias(MF, Reg, GlobalReg); 263753f127fSDimitry Andric if (!MF->getRegInfo().getUniqueVRegDef(Reg)) 264753f127fSDimitry Andric continue; 265753f127fSDimitry Andric collectDefInstr(Reg, MF, &MAI, MSType, IsFirst); 266753f127fSDimitry Andric IsFirst = false; 267753f127fSDimitry Andric if (E->getIsGV()) 268753f127fSDimitry Andric MAI.GlobalVarList.push_back(MF->getRegInfo().getUniqueVRegDef(Reg)); 269753f127fSDimitry Andric } 270753f127fSDimitry Andric 271753f127fSDimitry Andric if (UsePreOrder) 272753f127fSDimitry Andric for (auto *S : E->getDeps()) 273753f127fSDimitry Andric RecHoistUtil(S); 274753f127fSDimitry Andric }; 275753f127fSDimitry Andric RecHoistUtil(E); 276753f127fSDimitry Andric } 277753f127fSDimitry Andric } 278753f127fSDimitry Andric 279753f127fSDimitry Andric // The function initializes global register alias table for types, consts, 280753f127fSDimitry Andric // global vars and func decls and collects these instruction for output 281753f127fSDimitry Andric // at module level. Also it collects explicit OpExtension/OpCapability 282753f127fSDimitry Andric // instructions. 283753f127fSDimitry Andric void SPIRVModuleAnalysis::processDefInstrs(const Module &M) { 284753f127fSDimitry Andric std::vector<SPIRV::DTSortableEntry *> DepsGraph; 285753f127fSDimitry Andric 286753f127fSDimitry Andric GR->buildDepsGraph(DepsGraph, SPVDumpDeps ? MMI : nullptr); 287753f127fSDimitry Andric 288753f127fSDimitry Andric collectGlobalEntities( 289753f127fSDimitry Andric DepsGraph, SPIRV::MB_TypeConstVars, 290fcaf7f86SDimitry Andric [](const SPIRV::DTSortableEntry *E) { return !E->getIsFunc(); }); 291753f127fSDimitry Andric 292bdd1243dSDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 293bdd1243dSDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 294bdd1243dSDimitry Andric if (!MF) 295bdd1243dSDimitry Andric continue; 296bdd1243dSDimitry Andric // Iterate through and collect OpExtension/OpCapability instructions. 297bdd1243dSDimitry Andric for (MachineBasicBlock &MBB : *MF) { 298bdd1243dSDimitry Andric for (MachineInstr &MI : MBB) { 299bdd1243dSDimitry Andric if (MI.getOpcode() == SPIRV::OpExtension) { 300bdd1243dSDimitry Andric // Here, OpExtension just has a single enum operand, not a string. 301bdd1243dSDimitry Andric auto Ext = SPIRV::Extension::Extension(MI.getOperand(0).getImm()); 302bdd1243dSDimitry Andric MAI.Reqs.addExtension(Ext); 303bdd1243dSDimitry Andric MAI.setSkipEmission(&MI); 304bdd1243dSDimitry Andric } else if (MI.getOpcode() == SPIRV::OpCapability) { 305bdd1243dSDimitry Andric auto Cap = SPIRV::Capability::Capability(MI.getOperand(0).getImm()); 306bdd1243dSDimitry Andric MAI.Reqs.addCapability(Cap); 307bdd1243dSDimitry Andric MAI.setSkipEmission(&MI); 308bdd1243dSDimitry Andric } 309bdd1243dSDimitry Andric } 310bdd1243dSDimitry Andric } 311bdd1243dSDimitry Andric } 312bdd1243dSDimitry Andric 313753f127fSDimitry Andric collectGlobalEntities( 314753f127fSDimitry Andric DepsGraph, SPIRV::MB_ExtFuncDecls, 315753f127fSDimitry Andric [](const SPIRV::DTSortableEntry *E) { return E->getIsFunc(); }, true); 316753f127fSDimitry Andric } 317753f127fSDimitry Andric 318bdd1243dSDimitry Andric // Look for IDs declared with Import linkage, and map the corresponding function 31981ad6265SDimitry Andric // to the register defining that variable (which will usually be the result of 32081ad6265SDimitry Andric // an OpFunction). This lets us call externally imported functions using 32181ad6265SDimitry Andric // the correct ID registers. 32281ad6265SDimitry Andric void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI, 323bdd1243dSDimitry Andric const Function *F) { 32481ad6265SDimitry Andric if (MI.getOpcode() == SPIRV::OpDecorate) { 32581ad6265SDimitry Andric // If it's got Import linkage. 32681ad6265SDimitry Andric auto Dec = MI.getOperand(1).getImm(); 32781ad6265SDimitry Andric if (Dec == static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) { 32881ad6265SDimitry Andric auto Lnk = MI.getOperand(MI.getNumOperands() - 1).getImm(); 32981ad6265SDimitry Andric if (Lnk == static_cast<unsigned>(SPIRV::LinkageType::Import)) { 33081ad6265SDimitry Andric // Map imported function name to function ID register. 331bdd1243dSDimitry Andric const Function *ImportedFunc = 332bdd1243dSDimitry Andric F->getParent()->getFunction(getStringImm(MI, 2)); 33381ad6265SDimitry Andric Register Target = MI.getOperand(0).getReg(); 334bdd1243dSDimitry Andric MAI.FuncMap[ImportedFunc] = MAI.getRegisterAlias(MI.getMF(), Target); 33581ad6265SDimitry Andric } 33681ad6265SDimitry Andric } 33781ad6265SDimitry Andric } else if (MI.getOpcode() == SPIRV::OpFunction) { 33881ad6265SDimitry Andric // Record all internal OpFunction declarations. 33981ad6265SDimitry Andric Register Reg = MI.defs().begin()->getReg(); 34081ad6265SDimitry Andric Register GlobalReg = MAI.getRegisterAlias(MI.getMF(), Reg); 34181ad6265SDimitry Andric assert(GlobalReg.isValid()); 342bdd1243dSDimitry Andric MAI.FuncMap[F] = GlobalReg; 34381ad6265SDimitry Andric } 34481ad6265SDimitry Andric } 34581ad6265SDimitry Andric 346*0fca6ea1SDimitry Andric // References to a function via function pointers generate virtual 347*0fca6ea1SDimitry Andric // registers without a definition. We are able to resolve this 348*0fca6ea1SDimitry Andric // reference using Globar Register info into an OpFunction instruction 349*0fca6ea1SDimitry Andric // and replace dummy operands by the corresponding global register references. 350*0fca6ea1SDimitry Andric void SPIRVModuleAnalysis::collectFuncPtrs() { 351*0fca6ea1SDimitry Andric for (auto &MI : MAI.MS[SPIRV::MB_TypeConstVars]) 352*0fca6ea1SDimitry Andric if (MI->getOpcode() == SPIRV::OpConstantFunctionPointerINTEL) 353*0fca6ea1SDimitry Andric collectFuncPtrs(MI); 354*0fca6ea1SDimitry Andric } 355*0fca6ea1SDimitry Andric 356*0fca6ea1SDimitry Andric void SPIRVModuleAnalysis::collectFuncPtrs(MachineInstr *MI) { 357*0fca6ea1SDimitry Andric const MachineOperand *FunUse = &MI->getOperand(2); 358*0fca6ea1SDimitry Andric if (const MachineOperand *FunDef = GR->getFunctionDefinitionByUse(FunUse)) { 359*0fca6ea1SDimitry Andric const MachineInstr *FunDefMI = FunDef->getParent(); 360*0fca6ea1SDimitry Andric assert(FunDefMI->getOpcode() == SPIRV::OpFunction && 361*0fca6ea1SDimitry Andric "Constant function pointer must refer to function definition"); 362*0fca6ea1SDimitry Andric Register FunDefReg = FunDef->getReg(); 363*0fca6ea1SDimitry Andric Register GlobalFunDefReg = 364*0fca6ea1SDimitry Andric MAI.getRegisterAlias(FunDefMI->getMF(), FunDefReg); 365*0fca6ea1SDimitry Andric assert(GlobalFunDefReg.isValid() && 366*0fca6ea1SDimitry Andric "Function definition must refer to a global register"); 367*0fca6ea1SDimitry Andric Register FunPtrReg = FunUse->getReg(); 368*0fca6ea1SDimitry Andric MAI.setRegisterAlias(MI->getMF(), FunPtrReg, GlobalFunDefReg); 369*0fca6ea1SDimitry Andric } 370*0fca6ea1SDimitry Andric } 371*0fca6ea1SDimitry Andric 3727a6dacacSDimitry Andric using InstrSignature = SmallVector<size_t>; 3737a6dacacSDimitry Andric using InstrTraces = std::set<InstrSignature>; 3747a6dacacSDimitry Andric 3757a6dacacSDimitry Andric // Returns a representation of an instruction as a vector of MachineOperand 3767a6dacacSDimitry Andric // hash values, see llvm::hash_value(const MachineOperand &MO) for details. 3777a6dacacSDimitry Andric // This creates a signature of the instruction with the same content 3787a6dacacSDimitry Andric // that MachineOperand::isIdenticalTo uses for comparison. 3797a6dacacSDimitry Andric static InstrSignature instrToSignature(MachineInstr &MI, 3807a6dacacSDimitry Andric SPIRV::ModuleAnalysisInfo &MAI) { 3817a6dacacSDimitry Andric InstrSignature Signature; 3827a6dacacSDimitry Andric for (unsigned i = 0; i < MI.getNumOperands(); ++i) { 3837a6dacacSDimitry Andric const MachineOperand &MO = MI.getOperand(i); 3847a6dacacSDimitry Andric size_t h; 3857a6dacacSDimitry Andric if (MO.isReg()) { 3867a6dacacSDimitry Andric Register RegAlias = MAI.getRegisterAlias(MI.getMF(), MO.getReg()); 3877a6dacacSDimitry Andric // mimic llvm::hash_value(const MachineOperand &MO) 3887a6dacacSDimitry Andric h = hash_combine(MO.getType(), (unsigned)RegAlias, MO.getSubReg(), 3897a6dacacSDimitry Andric MO.isDef()); 3907a6dacacSDimitry Andric } else { 3917a6dacacSDimitry Andric h = hash_value(MO); 3927a6dacacSDimitry Andric } 3937a6dacacSDimitry Andric Signature.push_back(h); 3947a6dacacSDimitry Andric } 3957a6dacacSDimitry Andric return Signature; 3967a6dacacSDimitry Andric } 3977a6dacacSDimitry Andric 39881ad6265SDimitry Andric // Collect the given instruction in the specified MS. We assume global register 39981ad6265SDimitry Andric // numbering has already occurred by this point. We can directly compare reg 40081ad6265SDimitry Andric // arguments when detecting duplicates. 40181ad6265SDimitry Andric static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI, 4027a6dacacSDimitry Andric SPIRV::ModuleSectionType MSType, InstrTraces &IS, 403fcaf7f86SDimitry Andric bool Append = true) { 40481ad6265SDimitry Andric MAI.setSkipEmission(&MI); 4057a6dacacSDimitry Andric InstrSignature MISign = instrToSignature(MI, MAI); 4067a6dacacSDimitry Andric auto FoundMI = IS.insert(MISign); 4077a6dacacSDimitry Andric if (!FoundMI.second) 4087a6dacacSDimitry Andric return; // insert failed, so we found a duplicate; don't add it to MAI.MS 40981ad6265SDimitry Andric // No duplicates, so add it. 410fcaf7f86SDimitry Andric if (Append) 41181ad6265SDimitry Andric MAI.MS[MSType].push_back(&MI); 412fcaf7f86SDimitry Andric else 413fcaf7f86SDimitry Andric MAI.MS[MSType].insert(MAI.MS[MSType].begin(), &MI); 41481ad6265SDimitry Andric } 41581ad6265SDimitry Andric 41681ad6265SDimitry Andric // Some global instructions make reference to function-local ID regs, so cannot 41781ad6265SDimitry Andric // be correctly collected until these registers are globally numbered. 41881ad6265SDimitry Andric void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) { 4197a6dacacSDimitry Andric InstrTraces IS; 42081ad6265SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 42181ad6265SDimitry Andric if ((*F).isDeclaration()) 42281ad6265SDimitry Andric continue; 42381ad6265SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 42481ad6265SDimitry Andric assert(MF); 42581ad6265SDimitry Andric for (MachineBasicBlock &MBB : *MF) 42681ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 42781ad6265SDimitry Andric if (MAI.getSkipEmission(&MI)) 42881ad6265SDimitry Andric continue; 42981ad6265SDimitry Andric const unsigned OpCode = MI.getOpcode(); 43081ad6265SDimitry Andric if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) { 4317a6dacacSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames, IS); 43281ad6265SDimitry Andric } else if (OpCode == SPIRV::OpEntryPoint) { 4337a6dacacSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints, IS); 43481ad6265SDimitry Andric } else if (TII->isDecorationInstr(MI)) { 4357a6dacacSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_Annotations, IS); 436bdd1243dSDimitry Andric collectFuncNames(MI, &*F); 437fcaf7f86SDimitry Andric } else if (TII->isConstantInstr(MI)) { 438fcaf7f86SDimitry Andric // Now OpSpecConstant*s are not in DT, 439fcaf7f86SDimitry Andric // but they need to be collected anyway. 4407a6dacacSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_TypeConstVars, IS); 44181ad6265SDimitry Andric } else if (OpCode == SPIRV::OpFunction) { 442bdd1243dSDimitry Andric collectFuncNames(MI, &*F); 443fcaf7f86SDimitry Andric } else if (OpCode == SPIRV::OpTypeForwardPointer) { 4447a6dacacSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_TypeConstVars, IS, false); 44581ad6265SDimitry Andric } 44681ad6265SDimitry Andric } 44781ad6265SDimitry Andric } 44881ad6265SDimitry Andric } 44981ad6265SDimitry Andric 45081ad6265SDimitry Andric // Number registers in all functions globally from 0 onwards and store 451fcaf7f86SDimitry Andric // the result in global register alias table. Some registers are already 452fcaf7f86SDimitry Andric // numbered in collectGlobalEntities. 45381ad6265SDimitry Andric void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) { 45481ad6265SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 45581ad6265SDimitry Andric if ((*F).isDeclaration()) 45681ad6265SDimitry Andric continue; 45781ad6265SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 45881ad6265SDimitry Andric assert(MF); 45981ad6265SDimitry Andric for (MachineBasicBlock &MBB : *MF) { 46081ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 46181ad6265SDimitry Andric for (MachineOperand &Op : MI.operands()) { 46281ad6265SDimitry Andric if (!Op.isReg()) 46381ad6265SDimitry Andric continue; 46481ad6265SDimitry Andric Register Reg = Op.getReg(); 46581ad6265SDimitry Andric if (MAI.hasRegisterAlias(MF, Reg)) 46681ad6265SDimitry Andric continue; 46781ad6265SDimitry Andric Register NewReg = Register::index2VirtReg(MAI.getNextID()); 46881ad6265SDimitry Andric MAI.setRegisterAlias(MF, Reg, NewReg); 46981ad6265SDimitry Andric } 470fcaf7f86SDimitry Andric if (MI.getOpcode() != SPIRV::OpExtInst) 471fcaf7f86SDimitry Andric continue; 472fcaf7f86SDimitry Andric auto Set = MI.getOperand(2).getImm(); 473cb14a3feSDimitry Andric if (!MAI.ExtInstSetMap.contains(Set)) 474fcaf7f86SDimitry Andric MAI.ExtInstSetMap[Set] = Register::index2VirtReg(MAI.getNextID()); 47581ad6265SDimitry Andric } 47681ad6265SDimitry Andric } 47781ad6265SDimitry Andric } 47881ad6265SDimitry Andric } 47981ad6265SDimitry Andric 480bdd1243dSDimitry Andric // RequirementHandler implementations. 481bdd1243dSDimitry Andric void SPIRV::RequirementHandler::getAndAddRequirements( 482bdd1243dSDimitry Andric SPIRV::OperandCategory::OperandCategory Category, uint32_t i, 483bdd1243dSDimitry Andric const SPIRVSubtarget &ST) { 484bdd1243dSDimitry Andric addRequirements(getSymbolicOperandRequirements(Category, i, ST, *this)); 485bdd1243dSDimitry Andric } 486bdd1243dSDimitry Andric 487*0fca6ea1SDimitry Andric void SPIRV::RequirementHandler::recursiveAddCapabilities( 488bdd1243dSDimitry Andric const CapabilityList &ToPrune) { 489bdd1243dSDimitry Andric for (const auto &Cap : ToPrune) { 490bdd1243dSDimitry Andric AllCaps.insert(Cap); 491bdd1243dSDimitry Andric CapabilityList ImplicitDecls = 492bdd1243dSDimitry Andric getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap); 493*0fca6ea1SDimitry Andric recursiveAddCapabilities(ImplicitDecls); 494bdd1243dSDimitry Andric } 495bdd1243dSDimitry Andric } 496bdd1243dSDimitry Andric 497bdd1243dSDimitry Andric void SPIRV::RequirementHandler::addCapabilities(const CapabilityList &ToAdd) { 498bdd1243dSDimitry Andric for (const auto &Cap : ToAdd) { 499bdd1243dSDimitry Andric bool IsNewlyInserted = AllCaps.insert(Cap).second; 500bdd1243dSDimitry Andric if (!IsNewlyInserted) // Don't re-add if it's already been declared. 501bdd1243dSDimitry Andric continue; 502bdd1243dSDimitry Andric CapabilityList ImplicitDecls = 503bdd1243dSDimitry Andric getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap); 504*0fca6ea1SDimitry Andric recursiveAddCapabilities(ImplicitDecls); 505bdd1243dSDimitry Andric MinimalCaps.push_back(Cap); 506bdd1243dSDimitry Andric } 507bdd1243dSDimitry Andric } 508bdd1243dSDimitry Andric 509bdd1243dSDimitry Andric void SPIRV::RequirementHandler::addRequirements( 510bdd1243dSDimitry Andric const SPIRV::Requirements &Req) { 511bdd1243dSDimitry Andric if (!Req.IsSatisfiable) 512bdd1243dSDimitry Andric report_fatal_error("Adding SPIR-V requirements this target can't satisfy."); 513bdd1243dSDimitry Andric 514bdd1243dSDimitry Andric if (Req.Cap.has_value()) 515bdd1243dSDimitry Andric addCapabilities({Req.Cap.value()}); 516bdd1243dSDimitry Andric 517bdd1243dSDimitry Andric addExtensions(Req.Exts); 518bdd1243dSDimitry Andric 519*0fca6ea1SDimitry Andric if (!Req.MinVer.empty()) { 520*0fca6ea1SDimitry Andric if (!MaxVersion.empty() && Req.MinVer > MaxVersion) { 521bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Conflicting version requirements: >= " << Req.MinVer 522bdd1243dSDimitry Andric << " and <= " << MaxVersion << "\n"); 523bdd1243dSDimitry Andric report_fatal_error("Adding SPIR-V requirements that can't be satisfied."); 524bdd1243dSDimitry Andric } 525bdd1243dSDimitry Andric 526*0fca6ea1SDimitry Andric if (MinVersion.empty() || Req.MinVer > MinVersion) 527bdd1243dSDimitry Andric MinVersion = Req.MinVer; 528bdd1243dSDimitry Andric } 529bdd1243dSDimitry Andric 530*0fca6ea1SDimitry Andric if (!Req.MaxVer.empty()) { 531*0fca6ea1SDimitry Andric if (!MinVersion.empty() && Req.MaxVer < MinVersion) { 532bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Conflicting version requirements: <= " << Req.MaxVer 533bdd1243dSDimitry Andric << " and >= " << MinVersion << "\n"); 534bdd1243dSDimitry Andric report_fatal_error("Adding SPIR-V requirements that can't be satisfied."); 535bdd1243dSDimitry Andric } 536bdd1243dSDimitry Andric 537*0fca6ea1SDimitry Andric if (MaxVersion.empty() || Req.MaxVer < MaxVersion) 538bdd1243dSDimitry Andric MaxVersion = Req.MaxVer; 539bdd1243dSDimitry Andric } 540bdd1243dSDimitry Andric } 541bdd1243dSDimitry Andric 542bdd1243dSDimitry Andric void SPIRV::RequirementHandler::checkSatisfiable( 543bdd1243dSDimitry Andric const SPIRVSubtarget &ST) const { 544bdd1243dSDimitry Andric // Report as many errors as possible before aborting the compilation. 545bdd1243dSDimitry Andric bool IsSatisfiable = true; 546bdd1243dSDimitry Andric auto TargetVer = ST.getSPIRVVersion(); 547bdd1243dSDimitry Andric 548*0fca6ea1SDimitry Andric if (!MaxVersion.empty() && !TargetVer.empty() && MaxVersion < TargetVer) { 549bdd1243dSDimitry Andric LLVM_DEBUG( 550bdd1243dSDimitry Andric dbgs() << "Target SPIR-V version too high for required features\n" 551bdd1243dSDimitry Andric << "Required max version: " << MaxVersion << " target version " 552bdd1243dSDimitry Andric << TargetVer << "\n"); 553bdd1243dSDimitry Andric IsSatisfiable = false; 554bdd1243dSDimitry Andric } 555bdd1243dSDimitry Andric 556*0fca6ea1SDimitry Andric if (!MinVersion.empty() && !TargetVer.empty() && MinVersion > TargetVer) { 557bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Target SPIR-V version too low for required features\n" 558bdd1243dSDimitry Andric << "Required min version: " << MinVersion 559bdd1243dSDimitry Andric << " target version " << TargetVer << "\n"); 560bdd1243dSDimitry Andric IsSatisfiable = false; 561bdd1243dSDimitry Andric } 562bdd1243dSDimitry Andric 563*0fca6ea1SDimitry Andric if (!MinVersion.empty() && !MaxVersion.empty() && MinVersion > MaxVersion) { 564bdd1243dSDimitry Andric LLVM_DEBUG( 565bdd1243dSDimitry Andric dbgs() 566bdd1243dSDimitry Andric << "Version is too low for some features and too high for others.\n" 567bdd1243dSDimitry Andric << "Required SPIR-V min version: " << MinVersion 568bdd1243dSDimitry Andric << " required SPIR-V max version " << MaxVersion << "\n"); 569bdd1243dSDimitry Andric IsSatisfiable = false; 570bdd1243dSDimitry Andric } 571bdd1243dSDimitry Andric 572bdd1243dSDimitry Andric for (auto Cap : MinimalCaps) { 573bdd1243dSDimitry Andric if (AvailableCaps.contains(Cap)) 574bdd1243dSDimitry Andric continue; 575bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "Capability not supported: " 576bdd1243dSDimitry Andric << getSymbolicOperandMnemonic( 577bdd1243dSDimitry Andric OperandCategory::CapabilityOperand, Cap) 578bdd1243dSDimitry Andric << "\n"); 579bdd1243dSDimitry Andric IsSatisfiable = false; 580bdd1243dSDimitry Andric } 581bdd1243dSDimitry Andric 582bdd1243dSDimitry Andric for (auto Ext : AllExtensions) { 583bdd1243dSDimitry Andric if (ST.canUseExtension(Ext)) 584bdd1243dSDimitry Andric continue; 5855f757f3fSDimitry Andric LLVM_DEBUG(dbgs() << "Extension not supported: " 586bdd1243dSDimitry Andric << getSymbolicOperandMnemonic( 587bdd1243dSDimitry Andric OperandCategory::ExtensionOperand, Ext) 588bdd1243dSDimitry Andric << "\n"); 589bdd1243dSDimitry Andric IsSatisfiable = false; 590bdd1243dSDimitry Andric } 591bdd1243dSDimitry Andric 592bdd1243dSDimitry Andric if (!IsSatisfiable) 593bdd1243dSDimitry Andric report_fatal_error("Unable to meet SPIR-V requirements for this target."); 594bdd1243dSDimitry Andric } 595bdd1243dSDimitry Andric 596bdd1243dSDimitry Andric // Add the given capabilities and all their implicitly defined capabilities too. 597bdd1243dSDimitry Andric void SPIRV::RequirementHandler::addAvailableCaps(const CapabilityList &ToAdd) { 598bdd1243dSDimitry Andric for (const auto Cap : ToAdd) 599bdd1243dSDimitry Andric if (AvailableCaps.insert(Cap).second) 600bdd1243dSDimitry Andric addAvailableCaps(getSymbolicOperandCapabilities( 601bdd1243dSDimitry Andric SPIRV::OperandCategory::CapabilityOperand, Cap)); 602bdd1243dSDimitry Andric } 603bdd1243dSDimitry Andric 6045f757f3fSDimitry Andric void SPIRV::RequirementHandler::removeCapabilityIf( 6055f757f3fSDimitry Andric const Capability::Capability ToRemove, 6065f757f3fSDimitry Andric const Capability::Capability IfPresent) { 6075f757f3fSDimitry Andric if (AllCaps.contains(IfPresent)) 6085f757f3fSDimitry Andric AllCaps.erase(ToRemove); 6095f757f3fSDimitry Andric } 6105f757f3fSDimitry Andric 611bdd1243dSDimitry Andric namespace llvm { 612bdd1243dSDimitry Andric namespace SPIRV { 613bdd1243dSDimitry Andric void RequirementHandler::initAvailableCapabilities(const SPIRVSubtarget &ST) { 6145f757f3fSDimitry Andric if (ST.isOpenCLEnv()) { 6155f757f3fSDimitry Andric initAvailableCapabilitiesForOpenCL(ST); 616bdd1243dSDimitry Andric return; 6175f757f3fSDimitry Andric } 6185f757f3fSDimitry Andric 6195f757f3fSDimitry Andric if (ST.isVulkanEnv()) { 6205f757f3fSDimitry Andric initAvailableCapabilitiesForVulkan(ST); 6215f757f3fSDimitry Andric return; 6225f757f3fSDimitry Andric } 6235f757f3fSDimitry Andric 6245f757f3fSDimitry Andric report_fatal_error("Unimplemented environment for SPIR-V generation."); 6255f757f3fSDimitry Andric } 6265f757f3fSDimitry Andric 6275f757f3fSDimitry Andric void RequirementHandler::initAvailableCapabilitiesForOpenCL( 6285f757f3fSDimitry Andric const SPIRVSubtarget &ST) { 629bdd1243dSDimitry Andric // Add the min requirements for different OpenCL and SPIR-V versions. 630bdd1243dSDimitry Andric addAvailableCaps({Capability::Addresses, Capability::Float16Buffer, 631bdd1243dSDimitry Andric Capability::Int16, Capability::Int8, Capability::Kernel, 632bdd1243dSDimitry Andric Capability::Linkage, Capability::Vector16, 633bdd1243dSDimitry Andric Capability::Groups, Capability::GenericPointer, 634bdd1243dSDimitry Andric Capability::Shader}); 635bdd1243dSDimitry Andric if (ST.hasOpenCLFullProfile()) 636bdd1243dSDimitry Andric addAvailableCaps({Capability::Int64, Capability::Int64Atomics}); 637bdd1243dSDimitry Andric if (ST.hasOpenCLImageSupport()) { 638bdd1243dSDimitry Andric addAvailableCaps({Capability::ImageBasic, Capability::LiteralSampler, 639bdd1243dSDimitry Andric Capability::Image1D, Capability::SampledBuffer, 640bdd1243dSDimitry Andric Capability::ImageBuffer}); 641*0fca6ea1SDimitry Andric if (ST.isAtLeastOpenCLVer(VersionTuple(2, 0))) 642bdd1243dSDimitry Andric addAvailableCaps({Capability::ImageReadWrite}); 643bdd1243dSDimitry Andric } 644*0fca6ea1SDimitry Andric if (ST.isAtLeastSPIRVVer(VersionTuple(1, 1)) && 645*0fca6ea1SDimitry Andric ST.isAtLeastOpenCLVer(VersionTuple(2, 2))) 646bdd1243dSDimitry Andric addAvailableCaps({Capability::SubgroupDispatch, Capability::PipeStorage}); 647*0fca6ea1SDimitry Andric if (ST.isAtLeastSPIRVVer(VersionTuple(1, 3))) 648bdd1243dSDimitry Andric addAvailableCaps({Capability::GroupNonUniform, 649bdd1243dSDimitry Andric Capability::GroupNonUniformVote, 650bdd1243dSDimitry Andric Capability::GroupNonUniformArithmetic, 651bdd1243dSDimitry Andric Capability::GroupNonUniformBallot, 652bdd1243dSDimitry Andric Capability::GroupNonUniformClustered, 653bdd1243dSDimitry Andric Capability::GroupNonUniformShuffle, 654bdd1243dSDimitry Andric Capability::GroupNonUniformShuffleRelative}); 655*0fca6ea1SDimitry Andric if (ST.isAtLeastSPIRVVer(VersionTuple(1, 4))) 656bdd1243dSDimitry Andric addAvailableCaps({Capability::DenormPreserve, Capability::DenormFlushToZero, 657bdd1243dSDimitry Andric Capability::SignedZeroInfNanPreserve, 658bdd1243dSDimitry Andric Capability::RoundingModeRTE, 659bdd1243dSDimitry Andric Capability::RoundingModeRTZ}); 660bdd1243dSDimitry Andric // TODO: verify if this needs some checks. 661bdd1243dSDimitry Andric addAvailableCaps({Capability::Float16, Capability::Float64}); 662bdd1243dSDimitry Andric 6635f757f3fSDimitry Andric // Add capabilities enabled by extensions. 6645f757f3fSDimitry Andric for (auto Extension : ST.getAllAvailableExtensions()) { 6655f757f3fSDimitry Andric CapabilityList EnabledCapabilities = 6665f757f3fSDimitry Andric getCapabilitiesEnabledByExtension(Extension); 6675f757f3fSDimitry Andric addAvailableCaps(EnabledCapabilities); 6685f757f3fSDimitry Andric } 6695f757f3fSDimitry Andric 670bdd1243dSDimitry Andric // TODO: add OpenCL extensions. 671bdd1243dSDimitry Andric } 6725f757f3fSDimitry Andric 6735f757f3fSDimitry Andric void RequirementHandler::initAvailableCapabilitiesForVulkan( 6745f757f3fSDimitry Andric const SPIRVSubtarget &ST) { 6755f757f3fSDimitry Andric addAvailableCaps({Capability::Shader, Capability::Linkage}); 6765f757f3fSDimitry Andric 6777a6dacacSDimitry Andric // Provided by all supported Vulkan versions. 6787a6dacacSDimitry Andric addAvailableCaps({Capability::Int16, Capability::Int64, Capability::Float16, 679*0fca6ea1SDimitry Andric Capability::Float64, Capability::GroupNonUniform}); 6805f757f3fSDimitry Andric } 6815f757f3fSDimitry Andric 682bdd1243dSDimitry Andric } // namespace SPIRV 683bdd1243dSDimitry Andric } // namespace llvm 684bdd1243dSDimitry Andric 685bdd1243dSDimitry Andric // Add the required capabilities from a decoration instruction (including 686bdd1243dSDimitry Andric // BuiltIns). 687bdd1243dSDimitry Andric static void addOpDecorateReqs(const MachineInstr &MI, unsigned DecIndex, 688bdd1243dSDimitry Andric SPIRV::RequirementHandler &Reqs, 689bdd1243dSDimitry Andric const SPIRVSubtarget &ST) { 690bdd1243dSDimitry Andric int64_t DecOp = MI.getOperand(DecIndex).getImm(); 691bdd1243dSDimitry Andric auto Dec = static_cast<SPIRV::Decoration::Decoration>(DecOp); 692bdd1243dSDimitry Andric Reqs.addRequirements(getSymbolicOperandRequirements( 693bdd1243dSDimitry Andric SPIRV::OperandCategory::DecorationOperand, Dec, ST, Reqs)); 694bdd1243dSDimitry Andric 695bdd1243dSDimitry Andric if (Dec == SPIRV::Decoration::BuiltIn) { 696bdd1243dSDimitry Andric int64_t BuiltInOp = MI.getOperand(DecIndex + 1).getImm(); 697bdd1243dSDimitry Andric auto BuiltIn = static_cast<SPIRV::BuiltIn::BuiltIn>(BuiltInOp); 698bdd1243dSDimitry Andric Reqs.addRequirements(getSymbolicOperandRequirements( 699bdd1243dSDimitry Andric SPIRV::OperandCategory::BuiltInOperand, BuiltIn, ST, Reqs)); 700*0fca6ea1SDimitry Andric } else if (Dec == SPIRV::Decoration::LinkageAttributes) { 701*0fca6ea1SDimitry Andric int64_t LinkageOp = MI.getOperand(MI.getNumOperands() - 1).getImm(); 702*0fca6ea1SDimitry Andric SPIRV::LinkageType::LinkageType LnkType = 703*0fca6ea1SDimitry Andric static_cast<SPIRV::LinkageType::LinkageType>(LinkageOp); 704*0fca6ea1SDimitry Andric if (LnkType == SPIRV::LinkageType::LinkOnceODR) 705*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_linkonce_odr); 706*0fca6ea1SDimitry Andric } else if (Dec == SPIRV::Decoration::CacheControlLoadINTEL || 707*0fca6ea1SDimitry Andric Dec == SPIRV::Decoration::CacheControlStoreINTEL) { 708*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_cache_controls); 709*0fca6ea1SDimitry Andric } else if (Dec == SPIRV::Decoration::HostAccessINTEL) { 710*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_global_variable_host_access); 711*0fca6ea1SDimitry Andric } else if (Dec == SPIRV::Decoration::InitModeINTEL || 712*0fca6ea1SDimitry Andric Dec == SPIRV::Decoration::ImplementInRegisterMapINTEL) { 713*0fca6ea1SDimitry Andric Reqs.addExtension( 714*0fca6ea1SDimitry Andric SPIRV::Extension::SPV_INTEL_global_variable_fpga_decorations); 715bdd1243dSDimitry Andric } 716bdd1243dSDimitry Andric } 717bdd1243dSDimitry Andric 718bdd1243dSDimitry Andric // Add requirements for image handling. 719bdd1243dSDimitry Andric static void addOpTypeImageReqs(const MachineInstr &MI, 720bdd1243dSDimitry Andric SPIRV::RequirementHandler &Reqs, 721bdd1243dSDimitry Andric const SPIRVSubtarget &ST) { 722bdd1243dSDimitry Andric assert(MI.getNumOperands() >= 8 && "Insufficient operands for OpTypeImage"); 723bdd1243dSDimitry Andric // The operand indices used here are based on the OpTypeImage layout, which 724bdd1243dSDimitry Andric // the MachineInstr follows as well. 725bdd1243dSDimitry Andric int64_t ImgFormatOp = MI.getOperand(7).getImm(); 726bdd1243dSDimitry Andric auto ImgFormat = static_cast<SPIRV::ImageFormat::ImageFormat>(ImgFormatOp); 727bdd1243dSDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::ImageFormatOperand, 728bdd1243dSDimitry Andric ImgFormat, ST); 729bdd1243dSDimitry Andric 730bdd1243dSDimitry Andric bool IsArrayed = MI.getOperand(4).getImm() == 1; 731bdd1243dSDimitry Andric bool IsMultisampled = MI.getOperand(5).getImm() == 1; 732bdd1243dSDimitry Andric bool NoSampler = MI.getOperand(6).getImm() == 2; 733bdd1243dSDimitry Andric // Add dimension requirements. 734bdd1243dSDimitry Andric assert(MI.getOperand(2).isImm()); 735bdd1243dSDimitry Andric switch (MI.getOperand(2).getImm()) { 736bdd1243dSDimitry Andric case SPIRV::Dim::DIM_1D: 737bdd1243dSDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::Image1D 738bdd1243dSDimitry Andric : SPIRV::Capability::Sampled1D); 739bdd1243dSDimitry Andric break; 740bdd1243dSDimitry Andric case SPIRV::Dim::DIM_2D: 741bdd1243dSDimitry Andric if (IsMultisampled && NoSampler) 742bdd1243dSDimitry Andric Reqs.addRequirements(SPIRV::Capability::ImageMSArray); 743bdd1243dSDimitry Andric break; 744bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Cube: 745bdd1243dSDimitry Andric Reqs.addRequirements(SPIRV::Capability::Shader); 746bdd1243dSDimitry Andric if (IsArrayed) 747bdd1243dSDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageCubeArray 748bdd1243dSDimitry Andric : SPIRV::Capability::SampledCubeArray); 749bdd1243dSDimitry Andric break; 750bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Rect: 751bdd1243dSDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageRect 752bdd1243dSDimitry Andric : SPIRV::Capability::SampledRect); 753bdd1243dSDimitry Andric break; 754bdd1243dSDimitry Andric case SPIRV::Dim::DIM_Buffer: 755bdd1243dSDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageBuffer 756bdd1243dSDimitry Andric : SPIRV::Capability::SampledBuffer); 757bdd1243dSDimitry Andric break; 758bdd1243dSDimitry Andric case SPIRV::Dim::DIM_SubpassData: 759bdd1243dSDimitry Andric Reqs.addRequirements(SPIRV::Capability::InputAttachment); 760bdd1243dSDimitry Andric break; 761bdd1243dSDimitry Andric } 762bdd1243dSDimitry Andric 763bdd1243dSDimitry Andric // Has optional access qualifier. 764bdd1243dSDimitry Andric // TODO: check if it's OpenCL's kernel. 765bdd1243dSDimitry Andric if (MI.getNumOperands() > 8 && 766bdd1243dSDimitry Andric MI.getOperand(8).getImm() == SPIRV::AccessQualifier::ReadWrite) 767bdd1243dSDimitry Andric Reqs.addRequirements(SPIRV::Capability::ImageReadWrite); 768bdd1243dSDimitry Andric else 769bdd1243dSDimitry Andric Reqs.addRequirements(SPIRV::Capability::ImageBasic); 770bdd1243dSDimitry Andric } 771bdd1243dSDimitry Andric 772*0fca6ea1SDimitry Andric // Add requirements for handling atomic float instructions 773*0fca6ea1SDimitry Andric #define ATOM_FLT_REQ_EXT_MSG(ExtName) \ 774*0fca6ea1SDimitry Andric "The atomic float instruction requires the following SPIR-V " \ 775*0fca6ea1SDimitry Andric "extension: SPV_EXT_shader_atomic_float" ExtName 776*0fca6ea1SDimitry Andric static void AddAtomicFloatRequirements(const MachineInstr &MI, 777*0fca6ea1SDimitry Andric SPIRV::RequirementHandler &Reqs, 778*0fca6ea1SDimitry Andric const SPIRVSubtarget &ST) { 779*0fca6ea1SDimitry Andric assert(MI.getOperand(1).isReg() && 780*0fca6ea1SDimitry Andric "Expect register operand in atomic float instruction"); 781*0fca6ea1SDimitry Andric Register TypeReg = MI.getOperand(1).getReg(); 782*0fca6ea1SDimitry Andric SPIRVType *TypeDef = MI.getMF()->getRegInfo().getVRegDef(TypeReg); 783*0fca6ea1SDimitry Andric if (TypeDef->getOpcode() != SPIRV::OpTypeFloat) 784*0fca6ea1SDimitry Andric report_fatal_error("Result type of an atomic float instruction must be a " 785*0fca6ea1SDimitry Andric "floating-point type scalar"); 786*0fca6ea1SDimitry Andric 787*0fca6ea1SDimitry Andric unsigned BitWidth = TypeDef->getOperand(1).getImm(); 788*0fca6ea1SDimitry Andric unsigned Op = MI.getOpcode(); 789*0fca6ea1SDimitry Andric if (Op == SPIRV::OpAtomicFAddEXT) { 790*0fca6ea1SDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float_add)) 791*0fca6ea1SDimitry Andric report_fatal_error(ATOM_FLT_REQ_EXT_MSG("_add"), false); 792*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float_add); 793*0fca6ea1SDimitry Andric switch (BitWidth) { 794*0fca6ea1SDimitry Andric case 16: 795*0fca6ea1SDimitry Andric if (!ST.canUseExtension( 796*0fca6ea1SDimitry Andric SPIRV::Extension::SPV_EXT_shader_atomic_float16_add)) 797*0fca6ea1SDimitry Andric report_fatal_error(ATOM_FLT_REQ_EXT_MSG("16_add"), false); 798*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float16_add); 799*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat16AddEXT); 800*0fca6ea1SDimitry Andric break; 801*0fca6ea1SDimitry Andric case 32: 802*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat32AddEXT); 803*0fca6ea1SDimitry Andric break; 804*0fca6ea1SDimitry Andric case 64: 805*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat64AddEXT); 806*0fca6ea1SDimitry Andric break; 807*0fca6ea1SDimitry Andric default: 808*0fca6ea1SDimitry Andric report_fatal_error( 809*0fca6ea1SDimitry Andric "Unexpected floating-point type width in atomic float instruction"); 810*0fca6ea1SDimitry Andric } 811*0fca6ea1SDimitry Andric } else { 812*0fca6ea1SDimitry Andric if (!ST.canUseExtension( 813*0fca6ea1SDimitry Andric SPIRV::Extension::SPV_EXT_shader_atomic_float_min_max)) 814*0fca6ea1SDimitry Andric report_fatal_error(ATOM_FLT_REQ_EXT_MSG("_min_max"), false); 815*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float_min_max); 816*0fca6ea1SDimitry Andric switch (BitWidth) { 817*0fca6ea1SDimitry Andric case 16: 818*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat16MinMaxEXT); 819*0fca6ea1SDimitry Andric break; 820*0fca6ea1SDimitry Andric case 32: 821*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat32MinMaxEXT); 822*0fca6ea1SDimitry Andric break; 823*0fca6ea1SDimitry Andric case 64: 824*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat64MinMaxEXT); 825*0fca6ea1SDimitry Andric break; 826*0fca6ea1SDimitry Andric default: 827*0fca6ea1SDimitry Andric report_fatal_error( 828*0fca6ea1SDimitry Andric "Unexpected floating-point type width in atomic float instruction"); 829*0fca6ea1SDimitry Andric } 830*0fca6ea1SDimitry Andric } 831*0fca6ea1SDimitry Andric } 832*0fca6ea1SDimitry Andric 833bdd1243dSDimitry Andric void addInstrRequirements(const MachineInstr &MI, 834bdd1243dSDimitry Andric SPIRV::RequirementHandler &Reqs, 835bdd1243dSDimitry Andric const SPIRVSubtarget &ST) { 836bdd1243dSDimitry Andric switch (MI.getOpcode()) { 837bdd1243dSDimitry Andric case SPIRV::OpMemoryModel: { 838bdd1243dSDimitry Andric int64_t Addr = MI.getOperand(0).getImm(); 839bdd1243dSDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand, 840bdd1243dSDimitry Andric Addr, ST); 841bdd1243dSDimitry Andric int64_t Mem = MI.getOperand(1).getImm(); 842bdd1243dSDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand, Mem, 843bdd1243dSDimitry Andric ST); 844bdd1243dSDimitry Andric break; 845bdd1243dSDimitry Andric } 846bdd1243dSDimitry Andric case SPIRV::OpEntryPoint: { 847bdd1243dSDimitry Andric int64_t Exe = MI.getOperand(0).getImm(); 848bdd1243dSDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModelOperand, 849bdd1243dSDimitry Andric Exe, ST); 850bdd1243dSDimitry Andric break; 851bdd1243dSDimitry Andric } 852bdd1243dSDimitry Andric case SPIRV::OpExecutionMode: 853bdd1243dSDimitry Andric case SPIRV::OpExecutionModeId: { 854bdd1243dSDimitry Andric int64_t Exe = MI.getOperand(1).getImm(); 855bdd1243dSDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModeOperand, 856bdd1243dSDimitry Andric Exe, ST); 857bdd1243dSDimitry Andric break; 858bdd1243dSDimitry Andric } 859bdd1243dSDimitry Andric case SPIRV::OpTypeMatrix: 860bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Matrix); 861bdd1243dSDimitry Andric break; 862bdd1243dSDimitry Andric case SPIRV::OpTypeInt: { 863bdd1243dSDimitry Andric unsigned BitWidth = MI.getOperand(1).getImm(); 864bdd1243dSDimitry Andric if (BitWidth == 64) 865bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Int64); 866bdd1243dSDimitry Andric else if (BitWidth == 16) 867bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Int16); 868bdd1243dSDimitry Andric else if (BitWidth == 8) 869bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Int8); 870bdd1243dSDimitry Andric break; 871bdd1243dSDimitry Andric } 872bdd1243dSDimitry Andric case SPIRV::OpTypeFloat: { 873bdd1243dSDimitry Andric unsigned BitWidth = MI.getOperand(1).getImm(); 874bdd1243dSDimitry Andric if (BitWidth == 64) 875bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Float64); 876bdd1243dSDimitry Andric else if (BitWidth == 16) 877bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Float16); 878bdd1243dSDimitry Andric break; 879bdd1243dSDimitry Andric } 880bdd1243dSDimitry Andric case SPIRV::OpTypeVector: { 881bdd1243dSDimitry Andric unsigned NumComponents = MI.getOperand(2).getImm(); 882bdd1243dSDimitry Andric if (NumComponents == 8 || NumComponents == 16) 883bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Vector16); 884bdd1243dSDimitry Andric break; 885bdd1243dSDimitry Andric } 886bdd1243dSDimitry Andric case SPIRV::OpTypePointer: { 887bdd1243dSDimitry Andric auto SC = MI.getOperand(1).getImm(); 888bdd1243dSDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::StorageClassOperand, SC, 889bdd1243dSDimitry Andric ST); 8907a6dacacSDimitry Andric // If it's a type of pointer to float16 targeting OpenCL, add Float16Buffer 8917a6dacacSDimitry Andric // capability. 8927a6dacacSDimitry Andric if (!ST.isOpenCLEnv()) 8937a6dacacSDimitry Andric break; 894bdd1243dSDimitry Andric assert(MI.getOperand(2).isReg()); 895bdd1243dSDimitry Andric const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo(); 896bdd1243dSDimitry Andric SPIRVType *TypeDef = MRI.getVRegDef(MI.getOperand(2).getReg()); 897bdd1243dSDimitry Andric if (TypeDef->getOpcode() == SPIRV::OpTypeFloat && 898bdd1243dSDimitry Andric TypeDef->getOperand(1).getImm() == 16) 899bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Float16Buffer); 900bdd1243dSDimitry Andric break; 901bdd1243dSDimitry Andric } 902bdd1243dSDimitry Andric case SPIRV::OpBitReverse: 9035f757f3fSDimitry Andric case SPIRV::OpBitFieldInsert: 9045f757f3fSDimitry Andric case SPIRV::OpBitFieldSExtract: 9055f757f3fSDimitry Andric case SPIRV::OpBitFieldUExtract: 9065f757f3fSDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions)) { 9075f757f3fSDimitry Andric Reqs.addCapability(SPIRV::Capability::Shader); 9085f757f3fSDimitry Andric break; 9095f757f3fSDimitry Andric } 9105f757f3fSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_bit_instructions); 9115f757f3fSDimitry Andric Reqs.addCapability(SPIRV::Capability::BitInstructions); 9125f757f3fSDimitry Andric break; 913bdd1243dSDimitry Andric case SPIRV::OpTypeRuntimeArray: 914bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Shader); 915bdd1243dSDimitry Andric break; 916bdd1243dSDimitry Andric case SPIRV::OpTypeOpaque: 917bdd1243dSDimitry Andric case SPIRV::OpTypeEvent: 918bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Kernel); 919bdd1243dSDimitry Andric break; 920bdd1243dSDimitry Andric case SPIRV::OpTypePipe: 921bdd1243dSDimitry Andric case SPIRV::OpTypeReserveId: 922bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Pipes); 923bdd1243dSDimitry Andric break; 924bdd1243dSDimitry Andric case SPIRV::OpTypeDeviceEvent: 925bdd1243dSDimitry Andric case SPIRV::OpTypeQueue: 926bdd1243dSDimitry Andric case SPIRV::OpBuildNDRange: 927bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::DeviceEnqueue); 928bdd1243dSDimitry Andric break; 929bdd1243dSDimitry Andric case SPIRV::OpDecorate: 930bdd1243dSDimitry Andric case SPIRV::OpDecorateId: 931bdd1243dSDimitry Andric case SPIRV::OpDecorateString: 932bdd1243dSDimitry Andric addOpDecorateReqs(MI, 1, Reqs, ST); 933bdd1243dSDimitry Andric break; 934bdd1243dSDimitry Andric case SPIRV::OpMemberDecorate: 935bdd1243dSDimitry Andric case SPIRV::OpMemberDecorateString: 936bdd1243dSDimitry Andric addOpDecorateReqs(MI, 2, Reqs, ST); 937bdd1243dSDimitry Andric break; 938bdd1243dSDimitry Andric case SPIRV::OpInBoundsPtrAccessChain: 939bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Addresses); 940bdd1243dSDimitry Andric break; 941bdd1243dSDimitry Andric case SPIRV::OpConstantSampler: 942bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::LiteralSampler); 943bdd1243dSDimitry Andric break; 944bdd1243dSDimitry Andric case SPIRV::OpTypeImage: 945bdd1243dSDimitry Andric addOpTypeImageReqs(MI, Reqs, ST); 946bdd1243dSDimitry Andric break; 947bdd1243dSDimitry Andric case SPIRV::OpTypeSampler: 948bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::ImageBasic); 949bdd1243dSDimitry Andric break; 950bdd1243dSDimitry Andric case SPIRV::OpTypeForwardPointer: 951bdd1243dSDimitry Andric // TODO: check if it's OpenCL's kernel. 952bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Addresses); 953bdd1243dSDimitry Andric break; 954bdd1243dSDimitry Andric case SPIRV::OpAtomicFlagTestAndSet: 955bdd1243dSDimitry Andric case SPIRV::OpAtomicLoad: 956bdd1243dSDimitry Andric case SPIRV::OpAtomicStore: 957bdd1243dSDimitry Andric case SPIRV::OpAtomicExchange: 958bdd1243dSDimitry Andric case SPIRV::OpAtomicCompareExchange: 959bdd1243dSDimitry Andric case SPIRV::OpAtomicIIncrement: 960bdd1243dSDimitry Andric case SPIRV::OpAtomicIDecrement: 961bdd1243dSDimitry Andric case SPIRV::OpAtomicIAdd: 962bdd1243dSDimitry Andric case SPIRV::OpAtomicISub: 963bdd1243dSDimitry Andric case SPIRV::OpAtomicUMin: 964bdd1243dSDimitry Andric case SPIRV::OpAtomicUMax: 965bdd1243dSDimitry Andric case SPIRV::OpAtomicSMin: 966bdd1243dSDimitry Andric case SPIRV::OpAtomicSMax: 967bdd1243dSDimitry Andric case SPIRV::OpAtomicAnd: 968bdd1243dSDimitry Andric case SPIRV::OpAtomicOr: 969bdd1243dSDimitry Andric case SPIRV::OpAtomicXor: { 970bdd1243dSDimitry Andric const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo(); 971bdd1243dSDimitry Andric const MachineInstr *InstrPtr = &MI; 972bdd1243dSDimitry Andric if (MI.getOpcode() == SPIRV::OpAtomicStore) { 973bdd1243dSDimitry Andric assert(MI.getOperand(3).isReg()); 974bdd1243dSDimitry Andric InstrPtr = MRI.getVRegDef(MI.getOperand(3).getReg()); 975bdd1243dSDimitry Andric assert(InstrPtr && "Unexpected type instruction for OpAtomicStore"); 976bdd1243dSDimitry Andric } 977bdd1243dSDimitry Andric assert(InstrPtr->getOperand(1).isReg() && "Unexpected operand in atomic"); 978bdd1243dSDimitry Andric Register TypeReg = InstrPtr->getOperand(1).getReg(); 979bdd1243dSDimitry Andric SPIRVType *TypeDef = MRI.getVRegDef(TypeReg); 980bdd1243dSDimitry Andric if (TypeDef->getOpcode() == SPIRV::OpTypeInt) { 981bdd1243dSDimitry Andric unsigned BitWidth = TypeDef->getOperand(1).getImm(); 982bdd1243dSDimitry Andric if (BitWidth == 64) 983bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Int64Atomics); 984bdd1243dSDimitry Andric } 985bdd1243dSDimitry Andric break; 986bdd1243dSDimitry Andric } 987bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformIAdd: 988bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformFAdd: 989bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformIMul: 990bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformFMul: 991bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformSMin: 992bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformUMin: 993bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformFMin: 994bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformSMax: 995bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformUMax: 996bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformFMax: 997bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBitwiseAnd: 998bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBitwiseOr: 999bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBitwiseXor: 1000bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformLogicalAnd: 1001bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformLogicalOr: 1002bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformLogicalXor: { 1003bdd1243dSDimitry Andric assert(MI.getOperand(3).isImm()); 1004bdd1243dSDimitry Andric int64_t GroupOp = MI.getOperand(3).getImm(); 1005bdd1243dSDimitry Andric switch (GroupOp) { 1006bdd1243dSDimitry Andric case SPIRV::GroupOperation::Reduce: 1007bdd1243dSDimitry Andric case SPIRV::GroupOperation::InclusiveScan: 1008bdd1243dSDimitry Andric case SPIRV::GroupOperation::ExclusiveScan: 1009bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Kernel); 1010bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformArithmetic); 1011bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot); 1012bdd1243dSDimitry Andric break; 1013bdd1243dSDimitry Andric case SPIRV::GroupOperation::ClusteredReduce: 1014bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformClustered); 1015bdd1243dSDimitry Andric break; 1016bdd1243dSDimitry Andric case SPIRV::GroupOperation::PartitionedReduceNV: 1017bdd1243dSDimitry Andric case SPIRV::GroupOperation::PartitionedInclusiveScanNV: 1018bdd1243dSDimitry Andric case SPIRV::GroupOperation::PartitionedExclusiveScanNV: 1019bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformPartitionedNV); 1020bdd1243dSDimitry Andric break; 1021bdd1243dSDimitry Andric } 1022bdd1243dSDimitry Andric break; 1023bdd1243dSDimitry Andric } 1024bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformShuffle: 1025bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformShuffleXor: 1026bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffle); 1027bdd1243dSDimitry Andric break; 1028bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformShuffleUp: 1029bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformShuffleDown: 1030bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffleRelative); 1031bdd1243dSDimitry Andric break; 1032bdd1243dSDimitry Andric case SPIRV::OpGroupAll: 1033bdd1243dSDimitry Andric case SPIRV::OpGroupAny: 1034bdd1243dSDimitry Andric case SPIRV::OpGroupBroadcast: 1035bdd1243dSDimitry Andric case SPIRV::OpGroupIAdd: 1036bdd1243dSDimitry Andric case SPIRV::OpGroupFAdd: 1037bdd1243dSDimitry Andric case SPIRV::OpGroupFMin: 1038bdd1243dSDimitry Andric case SPIRV::OpGroupUMin: 1039bdd1243dSDimitry Andric case SPIRV::OpGroupSMin: 1040bdd1243dSDimitry Andric case SPIRV::OpGroupFMax: 1041bdd1243dSDimitry Andric case SPIRV::OpGroupUMax: 1042bdd1243dSDimitry Andric case SPIRV::OpGroupSMax: 1043bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::Groups); 1044bdd1243dSDimitry Andric break; 1045bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformElect: 1046bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniform); 1047bdd1243dSDimitry Andric break; 1048bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformAll: 1049bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformAny: 1050bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformAllEqual: 1051bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformVote); 1052bdd1243dSDimitry Andric break; 1053bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBroadcast: 1054bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBroadcastFirst: 1055bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBallot: 1056bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformInverseBallot: 1057bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBallotBitExtract: 1058bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBallotBitCount: 1059bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBallotFindLSB: 1060bdd1243dSDimitry Andric case SPIRV::OpGroupNonUniformBallotFindMSB: 1061bdd1243dSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot); 1062bdd1243dSDimitry Andric break; 1063*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupShuffleINTEL: 1064*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupShuffleDownINTEL: 1065*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupShuffleUpINTEL: 1066*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupShuffleXorINTEL: 1067*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) { 1068*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_subgroups); 1069*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::SubgroupShuffleINTEL); 1070*0fca6ea1SDimitry Andric } 1071*0fca6ea1SDimitry Andric break; 1072*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupBlockReadINTEL: 1073*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupBlockWriteINTEL: 1074*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) { 1075*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_subgroups); 1076*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::SubgroupBufferBlockIOINTEL); 1077*0fca6ea1SDimitry Andric } 1078*0fca6ea1SDimitry Andric break; 1079*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupImageBlockReadINTEL: 1080*0fca6ea1SDimitry Andric case SPIRV::OpSubgroupImageBlockWriteINTEL: 1081*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) { 1082*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_subgroups); 1083*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::SubgroupImageBlockIOINTEL); 1084*0fca6ea1SDimitry Andric } 1085*0fca6ea1SDimitry Andric break; 10865f757f3fSDimitry Andric case SPIRV::OpAssumeTrueKHR: 10875f757f3fSDimitry Andric case SPIRV::OpExpectKHR: 10885f757f3fSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume)) { 10895f757f3fSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_expect_assume); 10905f757f3fSDimitry Andric Reqs.addCapability(SPIRV::Capability::ExpectAssumeKHR); 10915f757f3fSDimitry Andric } 10925f757f3fSDimitry Andric break; 1093*0fca6ea1SDimitry Andric case SPIRV::OpPtrCastToCrossWorkgroupINTEL: 1094*0fca6ea1SDimitry Andric case SPIRV::OpCrossWorkgroupCastToPtrINTEL: 1095*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_usm_storage_classes)) { 1096*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_usm_storage_classes); 1097*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::USMStorageClassesINTEL); 1098*0fca6ea1SDimitry Andric } 1099*0fca6ea1SDimitry Andric break; 1100*0fca6ea1SDimitry Andric case SPIRV::OpConstantFunctionPointerINTEL: 1101*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)) { 1102*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_function_pointers); 1103*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::FunctionPointersINTEL); 1104*0fca6ea1SDimitry Andric } 1105*0fca6ea1SDimitry Andric break; 1106*0fca6ea1SDimitry Andric case SPIRV::OpGroupNonUniformRotateKHR: 1107*0fca6ea1SDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_subgroup_rotate)) 1108*0fca6ea1SDimitry Andric report_fatal_error("OpGroupNonUniformRotateKHR instruction requires the " 1109*0fca6ea1SDimitry Andric "following SPIR-V extension: SPV_KHR_subgroup_rotate", 1110*0fca6ea1SDimitry Andric false); 1111*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_subgroup_rotate); 1112*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformRotateKHR); 1113*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniform); 1114*0fca6ea1SDimitry Andric break; 1115*0fca6ea1SDimitry Andric case SPIRV::OpGroupIMulKHR: 1116*0fca6ea1SDimitry Andric case SPIRV::OpGroupFMulKHR: 1117*0fca6ea1SDimitry Andric case SPIRV::OpGroupBitwiseAndKHR: 1118*0fca6ea1SDimitry Andric case SPIRV::OpGroupBitwiseOrKHR: 1119*0fca6ea1SDimitry Andric case SPIRV::OpGroupBitwiseXorKHR: 1120*0fca6ea1SDimitry Andric case SPIRV::OpGroupLogicalAndKHR: 1121*0fca6ea1SDimitry Andric case SPIRV::OpGroupLogicalOrKHR: 1122*0fca6ea1SDimitry Andric case SPIRV::OpGroupLogicalXorKHR: 1123*0fca6ea1SDimitry Andric if (ST.canUseExtension( 1124*0fca6ea1SDimitry Andric SPIRV::Extension::SPV_KHR_uniform_group_instructions)) { 1125*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_uniform_group_instructions); 1126*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupUniformArithmeticKHR); 1127*0fca6ea1SDimitry Andric } 1128*0fca6ea1SDimitry Andric break; 1129*0fca6ea1SDimitry Andric case SPIRV::OpReadClockKHR: 1130*0fca6ea1SDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock)) 1131*0fca6ea1SDimitry Andric report_fatal_error("OpReadClockKHR instruction requires the " 1132*0fca6ea1SDimitry Andric "following SPIR-V extension: SPV_KHR_shader_clock", 1133*0fca6ea1SDimitry Andric false); 1134*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_shader_clock); 1135*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::ShaderClockKHR); 1136*0fca6ea1SDimitry Andric break; 1137*0fca6ea1SDimitry Andric case SPIRV::OpFunctionPointerCallINTEL: 1138*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)) { 1139*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_function_pointers); 1140*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::FunctionPointersINTEL); 1141*0fca6ea1SDimitry Andric } 1142*0fca6ea1SDimitry Andric break; 1143*0fca6ea1SDimitry Andric case SPIRV::OpAtomicFAddEXT: 1144*0fca6ea1SDimitry Andric case SPIRV::OpAtomicFMinEXT: 1145*0fca6ea1SDimitry Andric case SPIRV::OpAtomicFMaxEXT: 1146*0fca6ea1SDimitry Andric AddAtomicFloatRequirements(MI, Reqs, ST); 1147*0fca6ea1SDimitry Andric break; 1148*0fca6ea1SDimitry Andric case SPIRV::OpConvertBF16ToFINTEL: 1149*0fca6ea1SDimitry Andric case SPIRV::OpConvertFToBF16INTEL: 1150*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_bfloat16_conversion)) { 1151*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_bfloat16_conversion); 1152*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::BFloat16ConversionINTEL); 1153*0fca6ea1SDimitry Andric } 1154*0fca6ea1SDimitry Andric break; 1155*0fca6ea1SDimitry Andric case SPIRV::OpVariableLengthArrayINTEL: 1156*0fca6ea1SDimitry Andric case SPIRV::OpSaveMemoryINTEL: 1157*0fca6ea1SDimitry Andric case SPIRV::OpRestoreMemoryINTEL: 1158*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_variable_length_array)) { 1159*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_variable_length_array); 1160*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::VariableLengthArrayINTEL); 1161*0fca6ea1SDimitry Andric } 1162*0fca6ea1SDimitry Andric break; 1163*0fca6ea1SDimitry Andric case SPIRV::OpAsmTargetINTEL: 1164*0fca6ea1SDimitry Andric case SPIRV::OpAsmINTEL: 1165*0fca6ea1SDimitry Andric case SPIRV::OpAsmCallINTEL: 1166*0fca6ea1SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_inline_assembly)) { 1167*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_inline_assembly); 1168*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::AsmINTEL); 1169*0fca6ea1SDimitry Andric } 1170*0fca6ea1SDimitry Andric break; 1171*0fca6ea1SDimitry Andric case SPIRV::OpTypeCooperativeMatrixKHR: 1172*0fca6ea1SDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_cooperative_matrix)) 1173*0fca6ea1SDimitry Andric report_fatal_error( 1174*0fca6ea1SDimitry Andric "OpTypeCooperativeMatrixKHR type requires the " 1175*0fca6ea1SDimitry Andric "following SPIR-V extension: SPV_KHR_cooperative_matrix", 1176*0fca6ea1SDimitry Andric false); 1177*0fca6ea1SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_cooperative_matrix); 1178*0fca6ea1SDimitry Andric Reqs.addCapability(SPIRV::Capability::CooperativeMatrixKHR); 1179*0fca6ea1SDimitry Andric break; 1180bdd1243dSDimitry Andric default: 1181bdd1243dSDimitry Andric break; 1182bdd1243dSDimitry Andric } 11835f757f3fSDimitry Andric 11845f757f3fSDimitry Andric // If we require capability Shader, then we can remove the requirement for 11855f757f3fSDimitry Andric // the BitInstructions capability, since Shader is a superset capability 11865f757f3fSDimitry Andric // of BitInstructions. 11875f757f3fSDimitry Andric Reqs.removeCapabilityIf(SPIRV::Capability::BitInstructions, 11885f757f3fSDimitry Andric SPIRV::Capability::Shader); 1189bdd1243dSDimitry Andric } 1190bdd1243dSDimitry Andric 1191bdd1243dSDimitry Andric static void collectReqs(const Module &M, SPIRV::ModuleAnalysisInfo &MAI, 1192bdd1243dSDimitry Andric MachineModuleInfo *MMI, const SPIRVSubtarget &ST) { 1193bdd1243dSDimitry Andric // Collect requirements for existing instructions. 1194bdd1243dSDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 1195bdd1243dSDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 1196bdd1243dSDimitry Andric if (!MF) 1197bdd1243dSDimitry Andric continue; 1198bdd1243dSDimitry Andric for (const MachineBasicBlock &MBB : *MF) 1199bdd1243dSDimitry Andric for (const MachineInstr &MI : MBB) 1200bdd1243dSDimitry Andric addInstrRequirements(MI, MAI.Reqs, ST); 1201bdd1243dSDimitry Andric } 1202bdd1243dSDimitry Andric // Collect requirements for OpExecutionMode instructions. 1203bdd1243dSDimitry Andric auto Node = M.getNamedMetadata("spirv.ExecutionMode"); 1204bdd1243dSDimitry Andric if (Node) { 1205*0fca6ea1SDimitry Andric // SPV_KHR_float_controls is not available until v1.4 1206*0fca6ea1SDimitry Andric bool RequireFloatControls = false, 1207*0fca6ea1SDimitry Andric VerLower14 = !ST.isAtLeastSPIRVVer(VersionTuple(1, 4)); 1208bdd1243dSDimitry Andric for (unsigned i = 0; i < Node->getNumOperands(); i++) { 1209bdd1243dSDimitry Andric MDNode *MDN = cast<MDNode>(Node->getOperand(i)); 1210bdd1243dSDimitry Andric const MDOperand &MDOp = MDN->getOperand(1); 1211bdd1243dSDimitry Andric if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) { 1212bdd1243dSDimitry Andric Constant *C = CMeta->getValue(); 1213bdd1243dSDimitry Andric if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) { 1214bdd1243dSDimitry Andric auto EM = Const->getZExtValue(); 1215bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements( 1216bdd1243dSDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand, EM, ST); 1217*0fca6ea1SDimitry Andric // add SPV_KHR_float_controls if the version is too low 1218*0fca6ea1SDimitry Andric switch (EM) { 1219*0fca6ea1SDimitry Andric case SPIRV::ExecutionMode::DenormPreserve: 1220*0fca6ea1SDimitry Andric case SPIRV::ExecutionMode::DenormFlushToZero: 1221*0fca6ea1SDimitry Andric case SPIRV::ExecutionMode::SignedZeroInfNanPreserve: 1222*0fca6ea1SDimitry Andric case SPIRV::ExecutionMode::RoundingModeRTE: 1223*0fca6ea1SDimitry Andric case SPIRV::ExecutionMode::RoundingModeRTZ: 1224*0fca6ea1SDimitry Andric RequireFloatControls = VerLower14; 1225*0fca6ea1SDimitry Andric break; 1226bdd1243dSDimitry Andric } 1227bdd1243dSDimitry Andric } 1228bdd1243dSDimitry Andric } 1229bdd1243dSDimitry Andric } 1230*0fca6ea1SDimitry Andric if (RequireFloatControls && 1231*0fca6ea1SDimitry Andric ST.canUseExtension(SPIRV::Extension::SPV_KHR_float_controls)) 1232*0fca6ea1SDimitry Andric MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_float_controls); 1233*0fca6ea1SDimitry Andric } 1234bdd1243dSDimitry Andric for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) { 1235bdd1243dSDimitry Andric const Function &F = *FI; 1236bdd1243dSDimitry Andric if (F.isDeclaration()) 1237bdd1243dSDimitry Andric continue; 1238bdd1243dSDimitry Andric if (F.getMetadata("reqd_work_group_size")) 1239bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements( 1240bdd1243dSDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand, 1241bdd1243dSDimitry Andric SPIRV::ExecutionMode::LocalSize, ST); 12425f757f3fSDimitry Andric if (F.getFnAttribute("hlsl.numthreads").isValid()) { 12435f757f3fSDimitry Andric MAI.Reqs.getAndAddRequirements( 12445f757f3fSDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand, 12455f757f3fSDimitry Andric SPIRV::ExecutionMode::LocalSize, ST); 12465f757f3fSDimitry Andric } 1247bdd1243dSDimitry Andric if (F.getMetadata("work_group_size_hint")) 1248bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements( 1249bdd1243dSDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand, 1250bdd1243dSDimitry Andric SPIRV::ExecutionMode::LocalSizeHint, ST); 1251bdd1243dSDimitry Andric if (F.getMetadata("intel_reqd_sub_group_size")) 1252bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements( 1253bdd1243dSDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand, 1254bdd1243dSDimitry Andric SPIRV::ExecutionMode::SubgroupSize, ST); 1255bdd1243dSDimitry Andric if (F.getMetadata("vec_type_hint")) 1256bdd1243dSDimitry Andric MAI.Reqs.getAndAddRequirements( 1257bdd1243dSDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand, 1258bdd1243dSDimitry Andric SPIRV::ExecutionMode::VecTypeHint, ST); 12595f757f3fSDimitry Andric 12605f757f3fSDimitry Andric if (F.hasOptNone() && 12615f757f3fSDimitry Andric ST.canUseExtension(SPIRV::Extension::SPV_INTEL_optnone)) { 12625f757f3fSDimitry Andric // Output OpCapability OptNoneINTEL. 12635f757f3fSDimitry Andric MAI.Reqs.addExtension(SPIRV::Extension::SPV_INTEL_optnone); 12645f757f3fSDimitry Andric MAI.Reqs.addCapability(SPIRV::Capability::OptNoneINTEL); 12655f757f3fSDimitry Andric } 1266bdd1243dSDimitry Andric } 1267bdd1243dSDimitry Andric } 1268bdd1243dSDimitry Andric 1269bdd1243dSDimitry Andric static unsigned getFastMathFlags(const MachineInstr &I) { 1270bdd1243dSDimitry Andric unsigned Flags = SPIRV::FPFastMathMode::None; 1271bdd1243dSDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmNoNans)) 1272bdd1243dSDimitry Andric Flags |= SPIRV::FPFastMathMode::NotNaN; 1273bdd1243dSDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmNoInfs)) 1274bdd1243dSDimitry Andric Flags |= SPIRV::FPFastMathMode::NotInf; 1275bdd1243dSDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmNsz)) 1276bdd1243dSDimitry Andric Flags |= SPIRV::FPFastMathMode::NSZ; 1277bdd1243dSDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmArcp)) 1278bdd1243dSDimitry Andric Flags |= SPIRV::FPFastMathMode::AllowRecip; 1279bdd1243dSDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmReassoc)) 1280bdd1243dSDimitry Andric Flags |= SPIRV::FPFastMathMode::Fast; 1281bdd1243dSDimitry Andric return Flags; 1282bdd1243dSDimitry Andric } 1283bdd1243dSDimitry Andric 1284bdd1243dSDimitry Andric static void handleMIFlagDecoration(MachineInstr &I, const SPIRVSubtarget &ST, 1285bdd1243dSDimitry Andric const SPIRVInstrInfo &TII, 1286bdd1243dSDimitry Andric SPIRV::RequirementHandler &Reqs) { 1287bdd1243dSDimitry Andric if (I.getFlag(MachineInstr::MIFlag::NoSWrap) && TII.canUseNSW(I) && 1288bdd1243dSDimitry Andric getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand, 1289bdd1243dSDimitry Andric SPIRV::Decoration::NoSignedWrap, ST, Reqs) 1290bdd1243dSDimitry Andric .IsSatisfiable) { 1291bdd1243dSDimitry Andric buildOpDecorate(I.getOperand(0).getReg(), I, TII, 1292bdd1243dSDimitry Andric SPIRV::Decoration::NoSignedWrap, {}); 1293bdd1243dSDimitry Andric } 1294bdd1243dSDimitry Andric if (I.getFlag(MachineInstr::MIFlag::NoUWrap) && TII.canUseNUW(I) && 1295bdd1243dSDimitry Andric getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand, 1296bdd1243dSDimitry Andric SPIRV::Decoration::NoUnsignedWrap, ST, 1297bdd1243dSDimitry Andric Reqs) 1298bdd1243dSDimitry Andric .IsSatisfiable) { 1299bdd1243dSDimitry Andric buildOpDecorate(I.getOperand(0).getReg(), I, TII, 1300bdd1243dSDimitry Andric SPIRV::Decoration::NoUnsignedWrap, {}); 1301bdd1243dSDimitry Andric } 1302bdd1243dSDimitry Andric if (!TII.canUseFastMathFlags(I)) 1303bdd1243dSDimitry Andric return; 1304bdd1243dSDimitry Andric unsigned FMFlags = getFastMathFlags(I); 1305bdd1243dSDimitry Andric if (FMFlags == SPIRV::FPFastMathMode::None) 1306bdd1243dSDimitry Andric return; 1307bdd1243dSDimitry Andric Register DstReg = I.getOperand(0).getReg(); 1308bdd1243dSDimitry Andric buildOpDecorate(DstReg, I, TII, SPIRV::Decoration::FPFastMathMode, {FMFlags}); 1309bdd1243dSDimitry Andric } 1310bdd1243dSDimitry Andric 1311bdd1243dSDimitry Andric // Walk all functions and add decorations related to MI flags. 1312bdd1243dSDimitry Andric static void addDecorations(const Module &M, const SPIRVInstrInfo &TII, 1313bdd1243dSDimitry Andric MachineModuleInfo *MMI, const SPIRVSubtarget &ST, 1314bdd1243dSDimitry Andric SPIRV::ModuleAnalysisInfo &MAI) { 1315bdd1243dSDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 1316bdd1243dSDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 1317bdd1243dSDimitry Andric if (!MF) 1318bdd1243dSDimitry Andric continue; 1319bdd1243dSDimitry Andric for (auto &MBB : *MF) 1320bdd1243dSDimitry Andric for (auto &MI : MBB) 1321bdd1243dSDimitry Andric handleMIFlagDecoration(MI, ST, TII, MAI.Reqs); 1322bdd1243dSDimitry Andric } 1323bdd1243dSDimitry Andric } 1324bdd1243dSDimitry Andric 132581ad6265SDimitry Andric struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI; 132681ad6265SDimitry Andric 132781ad6265SDimitry Andric void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const { 132881ad6265SDimitry Andric AU.addRequired<TargetPassConfig>(); 132981ad6265SDimitry Andric AU.addRequired<MachineModuleInfoWrapperPass>(); 133081ad6265SDimitry Andric } 133181ad6265SDimitry Andric 133281ad6265SDimitry Andric bool SPIRVModuleAnalysis::runOnModule(Module &M) { 133381ad6265SDimitry Andric SPIRVTargetMachine &TM = 133481ad6265SDimitry Andric getAnalysis<TargetPassConfig>().getTM<SPIRVTargetMachine>(); 133581ad6265SDimitry Andric ST = TM.getSubtargetImpl(); 133681ad6265SDimitry Andric GR = ST->getSPIRVGlobalRegistry(); 133781ad6265SDimitry Andric TII = ST->getInstrInfo(); 133881ad6265SDimitry Andric 133981ad6265SDimitry Andric MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI(); 134081ad6265SDimitry Andric 134181ad6265SDimitry Andric setBaseInfo(M); 134281ad6265SDimitry Andric 1343bdd1243dSDimitry Andric addDecorations(M, *TII, MMI, *ST, MAI); 1344bdd1243dSDimitry Andric 1345bdd1243dSDimitry Andric collectReqs(M, MAI, MMI, *ST); 1346bdd1243dSDimitry Andric 1347fcaf7f86SDimitry Andric // Process type/const/global var/func decl instructions, number their 134881ad6265SDimitry Andric // destination registers from 0 to N, collect Extensions and Capabilities. 1349753f127fSDimitry Andric processDefInstrs(M); 135081ad6265SDimitry Andric 135181ad6265SDimitry Andric // Number rest of registers from N+1 onwards. 135281ad6265SDimitry Andric numberRegistersGlobally(M); 135381ad6265SDimitry Andric 1354*0fca6ea1SDimitry Andric // Update references to OpFunction instructions to use Global Registers 1355*0fca6ea1SDimitry Andric if (GR->hasConstFunPtr()) 1356*0fca6ea1SDimitry Andric collectFuncPtrs(); 1357*0fca6ea1SDimitry Andric 135881ad6265SDimitry Andric // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions. 135981ad6265SDimitry Andric processOtherInstrs(M); 136081ad6265SDimitry Andric 1361bdd1243dSDimitry Andric // If there are no entry points, we need the Linkage capability. 1362bdd1243dSDimitry Andric if (MAI.MS[SPIRV::MB_EntryPoints].empty()) 1363bdd1243dSDimitry Andric MAI.Reqs.addCapability(SPIRV::Capability::Linkage); 1364bdd1243dSDimitry Andric 1365*0fca6ea1SDimitry Andric // Set maximum ID used. 1366*0fca6ea1SDimitry Andric GR->setBound(MAI.MaxID); 1367*0fca6ea1SDimitry Andric 136881ad6265SDimitry Andric return false; 136981ad6265SDimitry Andric } 1370