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 
3881ad6265SDimitry Andric char llvm::SPIRVModuleAnalysis::ID = 0;
3981ad6265SDimitry Andric 
4081ad6265SDimitry Andric namespace llvm {
4181ad6265SDimitry Andric void initializeSPIRVModuleAnalysisPass(PassRegistry &);
4281ad6265SDimitry Andric } // namespace llvm
4381ad6265SDimitry Andric 
4481ad6265SDimitry Andric INITIALIZE_PASS(SPIRVModuleAnalysis, DEBUG_TYPE, "SPIRV module analysis", true,
4581ad6265SDimitry Andric                 true)
4681ad6265SDimitry Andric 
4781ad6265SDimitry Andric // Retrieve an unsigned from an MDNode with a list of them as operands.
getMetadataUInt(MDNode * MdNode,unsigned OpIndex,unsigned DefaultVal=0)4881ad6265SDimitry Andric static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex,
4981ad6265SDimitry Andric                                 unsigned DefaultVal = 0) {
5081ad6265SDimitry Andric   if (MdNode && OpIndex < MdNode->getNumOperands()) {
5181ad6265SDimitry Andric     const auto &Op = MdNode->getOperand(OpIndex);
5281ad6265SDimitry Andric     return mdconst::extract<ConstantInt>(Op)->getZExtValue();
5381ad6265SDimitry Andric   }
5481ad6265SDimitry Andric   return DefaultVal;
5581ad6265SDimitry Andric }
5681ad6265SDimitry Andric 
57bdd1243dSDimitry Andric static SPIRV::Requirements
getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,unsigned i,const SPIRVSubtarget & ST,SPIRV::RequirementHandler & Reqs)58bdd1243dSDimitry Andric getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
59bdd1243dSDimitry Andric                                unsigned i, const SPIRVSubtarget &ST,
60bdd1243dSDimitry Andric                                SPIRV::RequirementHandler &Reqs) {
61bdd1243dSDimitry Andric   unsigned ReqMinVer = getSymbolicOperandMinVersion(Category, i);
62bdd1243dSDimitry Andric   unsigned ReqMaxVer = getSymbolicOperandMaxVersion(Category, i);
63bdd1243dSDimitry Andric   unsigned TargetVer = ST.getSPIRVVersion();
64bdd1243dSDimitry Andric   bool MinVerOK = !ReqMinVer || !TargetVer || TargetVer >= ReqMinVer;
65bdd1243dSDimitry Andric   bool MaxVerOK = !ReqMaxVer || !TargetVer || TargetVer <= ReqMaxVer;
66bdd1243dSDimitry Andric   CapabilityList ReqCaps = getSymbolicOperandCapabilities(Category, i);
67bdd1243dSDimitry Andric   ExtensionList ReqExts = getSymbolicOperandExtensions(Category, i);
68bdd1243dSDimitry Andric   if (ReqCaps.empty()) {
69bdd1243dSDimitry Andric     if (ReqExts.empty()) {
70bdd1243dSDimitry Andric       if (MinVerOK && MaxVerOK)
71bdd1243dSDimitry Andric         return {true, {}, {}, ReqMinVer, ReqMaxVer};
72bdd1243dSDimitry Andric       return {false, {}, {}, 0, 0};
73bdd1243dSDimitry Andric     }
74bdd1243dSDimitry Andric   } else if (MinVerOK && MaxVerOK) {
75bdd1243dSDimitry Andric     for (auto Cap : ReqCaps) { // Only need 1 of the capabilities to work.
76bdd1243dSDimitry Andric       if (Reqs.isCapabilityAvailable(Cap))
77bdd1243dSDimitry Andric         return {true, {Cap}, {}, ReqMinVer, ReqMaxVer};
78bdd1243dSDimitry Andric     }
79bdd1243dSDimitry Andric   }
80bdd1243dSDimitry Andric   // If there are no capabilities, or we can't satisfy the version or
81bdd1243dSDimitry Andric   // capability requirements, use the list of extensions (if the subtarget
82bdd1243dSDimitry Andric   // can handle them all).
83bdd1243dSDimitry Andric   if (llvm::all_of(ReqExts, [&ST](const SPIRV::Extension::Extension &Ext) {
84bdd1243dSDimitry Andric         return ST.canUseExtension(Ext);
85bdd1243dSDimitry Andric       })) {
86bdd1243dSDimitry Andric     return {true, {}, ReqExts, 0, 0}; // TODO: add versions to extensions.
87bdd1243dSDimitry Andric   }
88bdd1243dSDimitry Andric   return {false, {}, {}, 0, 0};
89bdd1243dSDimitry Andric }
90bdd1243dSDimitry Andric 
setBaseInfo(const Module & M)9181ad6265SDimitry Andric void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
9281ad6265SDimitry Andric   MAI.MaxID = 0;
9381ad6265SDimitry Andric   for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++)
9481ad6265SDimitry Andric     MAI.MS[i].clear();
9581ad6265SDimitry Andric   MAI.RegisterAliasTable.clear();
9681ad6265SDimitry Andric   MAI.InstrsToDelete.clear();
97bdd1243dSDimitry Andric   MAI.FuncMap.clear();
9881ad6265SDimitry Andric   MAI.GlobalVarList.clear();
99fcaf7f86SDimitry Andric   MAI.ExtInstSetMap.clear();
100bdd1243dSDimitry Andric   MAI.Reqs.clear();
101bdd1243dSDimitry Andric   MAI.Reqs.initAvailableCapabilities(*ST);
10281ad6265SDimitry Andric 
10381ad6265SDimitry Andric   // TODO: determine memory model and source language from the configuratoin.
104fcaf7f86SDimitry Andric   if (auto MemModel = M.getNamedMetadata("spirv.MemoryModel")) {
105fcaf7f86SDimitry Andric     auto MemMD = MemModel->getOperand(0);
106bdd1243dSDimitry Andric     MAI.Addr = static_cast<SPIRV::AddressingModel::AddressingModel>(
107bdd1243dSDimitry Andric         getMetadataUInt(MemMD, 0));
108bdd1243dSDimitry Andric     MAI.Mem =
109bdd1243dSDimitry Andric         static_cast<SPIRV::MemoryModel::MemoryModel>(getMetadataUInt(MemMD, 1));
110fcaf7f86SDimitry Andric   } else {
1115f757f3fSDimitry Andric     // TODO: Add support for VulkanMemoryModel.
1125f757f3fSDimitry Andric     MAI.Mem = ST->isOpenCLEnv() ? SPIRV::MemoryModel::OpenCL
1135f757f3fSDimitry Andric                                 : SPIRV::MemoryModel::GLSL450;
1145f757f3fSDimitry Andric     if (MAI.Mem == SPIRV::MemoryModel::OpenCL) {
11581ad6265SDimitry Andric       unsigned PtrSize = ST->getPointerSize();
11681ad6265SDimitry Andric       MAI.Addr = PtrSize == 32   ? SPIRV::AddressingModel::Physical32
11781ad6265SDimitry Andric                  : PtrSize == 64 ? SPIRV::AddressingModel::Physical64
11881ad6265SDimitry Andric                                  : SPIRV::AddressingModel::Logical;
1195f757f3fSDimitry Andric     } else {
1205f757f3fSDimitry Andric       // TODO: Add support for PhysicalStorageBufferAddress.
1215f757f3fSDimitry Andric       MAI.Addr = SPIRV::AddressingModel::Logical;
1225f757f3fSDimitry Andric     }
123fcaf7f86SDimitry Andric   }
12481ad6265SDimitry Andric   // Get the OpenCL version number from metadata.
12581ad6265SDimitry Andric   // TODO: support other source languages.
12681ad6265SDimitry Andric   if (auto VerNode = M.getNamedMetadata("opencl.ocl.version")) {
127fcaf7f86SDimitry Andric     MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_C;
128fcaf7f86SDimitry Andric     // Construct version literal in accordance with SPIRV-LLVM-Translator.
129fcaf7f86SDimitry Andric     // TODO: support multiple OCL version metadata.
130fcaf7f86SDimitry Andric     assert(VerNode->getNumOperands() > 0 && "Invalid SPIR");
13181ad6265SDimitry Andric     auto VersionMD = VerNode->getOperand(0);
13281ad6265SDimitry Andric     unsigned MajorNum = getMetadataUInt(VersionMD, 0, 2);
13381ad6265SDimitry Andric     unsigned MinorNum = getMetadataUInt(VersionMD, 1);
13481ad6265SDimitry Andric     unsigned RevNum = getMetadataUInt(VersionMD, 2);
135fcaf7f86SDimitry Andric     MAI.SrcLangVersion = (MajorNum * 100 + MinorNum) * 1000 + RevNum;
136fcaf7f86SDimitry Andric   } else {
137fcaf7f86SDimitry Andric     MAI.SrcLang = SPIRV::SourceLanguage::Unknown;
138fcaf7f86SDimitry Andric     MAI.SrcLangVersion = 0;
139fcaf7f86SDimitry Andric   }
140fcaf7f86SDimitry Andric 
141fcaf7f86SDimitry Andric   if (auto ExtNode = M.getNamedMetadata("opencl.used.extensions")) {
142fcaf7f86SDimitry Andric     for (unsigned I = 0, E = ExtNode->getNumOperands(); I != E; ++I) {
143fcaf7f86SDimitry Andric       MDNode *MD = ExtNode->getOperand(I);
144fcaf7f86SDimitry Andric       if (!MD || MD->getNumOperands() == 0)
145fcaf7f86SDimitry Andric         continue;
146fcaf7f86SDimitry Andric       for (unsigned J = 0, N = MD->getNumOperands(); J != N; ++J)
147fcaf7f86SDimitry Andric         MAI.SrcExt.insert(cast<MDString>(MD->getOperand(J))->getString());
14881ad6265SDimitry Andric     }
14981ad6265SDimitry Andric   }
15081ad6265SDimitry Andric 
151bdd1243dSDimitry Andric   // Update required capabilities for this memory model, addressing model and
152bdd1243dSDimitry Andric   // source language.
153bdd1243dSDimitry Andric   MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand,
154bdd1243dSDimitry Andric                                  MAI.Mem, *ST);
155bdd1243dSDimitry Andric   MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::SourceLanguageOperand,
156bdd1243dSDimitry Andric                                  MAI.SrcLang, *ST);
157bdd1243dSDimitry Andric   MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
158bdd1243dSDimitry Andric                                  MAI.Addr, *ST);
159bdd1243dSDimitry Andric 
1605f757f3fSDimitry Andric   if (ST->isOpenCLEnv()) {
161fcaf7f86SDimitry Andric     // TODO: check if it's required by default.
1625f757f3fSDimitry Andric     MAI.ExtInstSetMap[static_cast<unsigned>(
1635f757f3fSDimitry Andric         SPIRV::InstructionSet::OpenCL_std)] =
164fcaf7f86SDimitry Andric         Register::index2VirtReg(MAI.getNextID());
16581ad6265SDimitry Andric   }
1665f757f3fSDimitry Andric }
16781ad6265SDimitry Andric 
168753f127fSDimitry Andric // Collect MI which defines the register in the given machine function.
collectDefInstr(Register Reg,const MachineFunction * MF,SPIRV::ModuleAnalysisInfo * MAI,SPIRV::ModuleSectionType MSType,bool DoInsert=true)169753f127fSDimitry Andric static void collectDefInstr(Register Reg, const MachineFunction *MF,
170753f127fSDimitry Andric                             SPIRV::ModuleAnalysisInfo *MAI,
171753f127fSDimitry Andric                             SPIRV::ModuleSectionType MSType,
172753f127fSDimitry Andric                             bool DoInsert = true) {
173753f127fSDimitry Andric   assert(MAI->hasRegisterAlias(MF, Reg) && "Cannot find register alias");
174753f127fSDimitry Andric   MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(Reg);
175753f127fSDimitry Andric   assert(MI && "There should be an instruction that defines the register");
176753f127fSDimitry Andric   MAI->setSkipEmission(MI);
177753f127fSDimitry Andric   if (DoInsert)
178753f127fSDimitry Andric     MAI->MS[MSType].push_back(MI);
179753f127fSDimitry Andric }
180753f127fSDimitry Andric 
collectGlobalEntities(const std::vector<SPIRV::DTSortableEntry * > & DepsGraph,SPIRV::ModuleSectionType MSType,std::function<bool (const SPIRV::DTSortableEntry *)> Pred,bool UsePreOrder=false)181753f127fSDimitry Andric void SPIRVModuleAnalysis::collectGlobalEntities(
182753f127fSDimitry Andric     const std::vector<SPIRV::DTSortableEntry *> &DepsGraph,
183753f127fSDimitry Andric     SPIRV::ModuleSectionType MSType,
184753f127fSDimitry Andric     std::function<bool(const SPIRV::DTSortableEntry *)> Pred,
185fcaf7f86SDimitry Andric     bool UsePreOrder = false) {
186753f127fSDimitry Andric   DenseSet<const SPIRV::DTSortableEntry *> Visited;
187753f127fSDimitry Andric   for (const auto *E : DepsGraph) {
188753f127fSDimitry Andric     std::function<void(const SPIRV::DTSortableEntry *)> RecHoistUtil;
189753f127fSDimitry Andric     // NOTE: here we prefer recursive approach over iterative because
190753f127fSDimitry Andric     // we don't expect depchains long enough to cause SO.
191753f127fSDimitry Andric     RecHoistUtil = [MSType, UsePreOrder, &Visited, &Pred,
192753f127fSDimitry Andric                     &RecHoistUtil](const SPIRV::DTSortableEntry *E) {
193753f127fSDimitry Andric       if (Visited.count(E) || !Pred(E))
194753f127fSDimitry Andric         return;
195753f127fSDimitry Andric       Visited.insert(E);
196753f127fSDimitry Andric 
197753f127fSDimitry Andric       // Traversing deps graph in post-order allows us to get rid of
198753f127fSDimitry Andric       // register aliases preprocessing.
199753f127fSDimitry Andric       // But pre-order is required for correct processing of function
200753f127fSDimitry Andric       // declaration and arguments processing.
201753f127fSDimitry Andric       if (!UsePreOrder)
202753f127fSDimitry Andric         for (auto *S : E->getDeps())
203753f127fSDimitry Andric           RecHoistUtil(S);
204753f127fSDimitry Andric 
205753f127fSDimitry Andric       Register GlobalReg = Register::index2VirtReg(MAI.getNextID());
206753f127fSDimitry Andric       bool IsFirst = true;
207753f127fSDimitry Andric       for (auto &U : *E) {
208753f127fSDimitry Andric         const MachineFunction *MF = U.first;
209753f127fSDimitry Andric         Register Reg = U.second;
210753f127fSDimitry Andric         MAI.setRegisterAlias(MF, Reg, GlobalReg);
211753f127fSDimitry Andric         if (!MF->getRegInfo().getUniqueVRegDef(Reg))
212753f127fSDimitry Andric           continue;
213753f127fSDimitry Andric         collectDefInstr(Reg, MF, &MAI, MSType, IsFirst);
214753f127fSDimitry Andric         IsFirst = false;
215753f127fSDimitry Andric         if (E->getIsGV())
216753f127fSDimitry Andric           MAI.GlobalVarList.push_back(MF->getRegInfo().getUniqueVRegDef(Reg));
217753f127fSDimitry Andric       }
218753f127fSDimitry Andric 
219753f127fSDimitry Andric       if (UsePreOrder)
220753f127fSDimitry Andric         for (auto *S : E->getDeps())
221753f127fSDimitry Andric           RecHoistUtil(S);
222753f127fSDimitry Andric     };
223753f127fSDimitry Andric     RecHoistUtil(E);
224753f127fSDimitry Andric   }
225753f127fSDimitry Andric }
226753f127fSDimitry Andric 
227753f127fSDimitry Andric // The function initializes global register alias table for types, consts,
228753f127fSDimitry Andric // global vars and func decls and collects these instruction for output
229753f127fSDimitry Andric // at module level. Also it collects explicit OpExtension/OpCapability
230753f127fSDimitry Andric // instructions.
processDefInstrs(const Module & M)231753f127fSDimitry Andric void SPIRVModuleAnalysis::processDefInstrs(const Module &M) {
232753f127fSDimitry Andric   std::vector<SPIRV::DTSortableEntry *> DepsGraph;
233753f127fSDimitry Andric 
234753f127fSDimitry Andric   GR->buildDepsGraph(DepsGraph, SPVDumpDeps ? MMI : nullptr);
235753f127fSDimitry Andric 
236753f127fSDimitry Andric   collectGlobalEntities(
237753f127fSDimitry Andric       DepsGraph, SPIRV::MB_TypeConstVars,
238fcaf7f86SDimitry Andric       [](const SPIRV::DTSortableEntry *E) { return !E->getIsFunc(); });
239753f127fSDimitry Andric 
240bdd1243dSDimitry Andric   for (auto F = M.begin(), E = M.end(); F != E; ++F) {
241bdd1243dSDimitry Andric     MachineFunction *MF = MMI->getMachineFunction(*F);
242bdd1243dSDimitry Andric     if (!MF)
243bdd1243dSDimitry Andric       continue;
244bdd1243dSDimitry Andric     // Iterate through and collect OpExtension/OpCapability instructions.
245bdd1243dSDimitry Andric     for (MachineBasicBlock &MBB : *MF) {
246bdd1243dSDimitry Andric       for (MachineInstr &MI : MBB) {
247bdd1243dSDimitry Andric         if (MI.getOpcode() == SPIRV::OpExtension) {
248bdd1243dSDimitry Andric           // Here, OpExtension just has a single enum operand, not a string.
249bdd1243dSDimitry Andric           auto Ext = SPIRV::Extension::Extension(MI.getOperand(0).getImm());
250bdd1243dSDimitry Andric           MAI.Reqs.addExtension(Ext);
251bdd1243dSDimitry Andric           MAI.setSkipEmission(&MI);
252bdd1243dSDimitry Andric         } else if (MI.getOpcode() == SPIRV::OpCapability) {
253bdd1243dSDimitry Andric           auto Cap = SPIRV::Capability::Capability(MI.getOperand(0).getImm());
254bdd1243dSDimitry Andric           MAI.Reqs.addCapability(Cap);
255bdd1243dSDimitry Andric           MAI.setSkipEmission(&MI);
256bdd1243dSDimitry Andric         }
257bdd1243dSDimitry Andric       }
258bdd1243dSDimitry Andric     }
259bdd1243dSDimitry Andric   }
260bdd1243dSDimitry Andric 
261753f127fSDimitry Andric   collectGlobalEntities(
262753f127fSDimitry Andric       DepsGraph, SPIRV::MB_ExtFuncDecls,
263753f127fSDimitry Andric       [](const SPIRV::DTSortableEntry *E) { return E->getIsFunc(); }, true);
264753f127fSDimitry Andric }
265753f127fSDimitry Andric 
266bdd1243dSDimitry Andric // Look for IDs declared with Import linkage, and map the corresponding function
26781ad6265SDimitry Andric // to the register defining that variable (which will usually be the result of
26881ad6265SDimitry Andric // an OpFunction). This lets us call externally imported functions using
26981ad6265SDimitry Andric // the correct ID registers.
collectFuncNames(MachineInstr & MI,const Function * F)27081ad6265SDimitry Andric void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI,
271bdd1243dSDimitry Andric                                            const Function *F) {
27281ad6265SDimitry Andric   if (MI.getOpcode() == SPIRV::OpDecorate) {
27381ad6265SDimitry Andric     // If it's got Import linkage.
27481ad6265SDimitry Andric     auto Dec = MI.getOperand(1).getImm();
27581ad6265SDimitry Andric     if (Dec == static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) {
27681ad6265SDimitry Andric       auto Lnk = MI.getOperand(MI.getNumOperands() - 1).getImm();
27781ad6265SDimitry Andric       if (Lnk == static_cast<unsigned>(SPIRV::LinkageType::Import)) {
27881ad6265SDimitry Andric         // Map imported function name to function ID register.
279bdd1243dSDimitry Andric         const Function *ImportedFunc =
280bdd1243dSDimitry Andric             F->getParent()->getFunction(getStringImm(MI, 2));
28181ad6265SDimitry Andric         Register Target = MI.getOperand(0).getReg();
282bdd1243dSDimitry Andric         MAI.FuncMap[ImportedFunc] = MAI.getRegisterAlias(MI.getMF(), Target);
28381ad6265SDimitry Andric       }
28481ad6265SDimitry Andric     }
28581ad6265SDimitry Andric   } else if (MI.getOpcode() == SPIRV::OpFunction) {
28681ad6265SDimitry Andric     // Record all internal OpFunction declarations.
28781ad6265SDimitry Andric     Register Reg = MI.defs().begin()->getReg();
28881ad6265SDimitry Andric     Register GlobalReg = MAI.getRegisterAlias(MI.getMF(), Reg);
28981ad6265SDimitry Andric     assert(GlobalReg.isValid());
290bdd1243dSDimitry Andric     MAI.FuncMap[F] = GlobalReg;
29181ad6265SDimitry Andric   }
29281ad6265SDimitry Andric }
29381ad6265SDimitry Andric 
294*7a6dacacSDimitry Andric using InstrSignature = SmallVector<size_t>;
295*7a6dacacSDimitry Andric using InstrTraces = std::set<InstrSignature>;
296*7a6dacacSDimitry Andric 
297*7a6dacacSDimitry Andric // Returns a representation of an instruction as a vector of MachineOperand
298*7a6dacacSDimitry Andric // hash values, see llvm::hash_value(const MachineOperand &MO) for details.
299*7a6dacacSDimitry Andric // This creates a signature of the instruction with the same content
300*7a6dacacSDimitry Andric // that MachineOperand::isIdenticalTo uses for comparison.
instrToSignature(MachineInstr & MI,SPIRV::ModuleAnalysisInfo & MAI)301*7a6dacacSDimitry Andric static InstrSignature instrToSignature(MachineInstr &MI,
302*7a6dacacSDimitry Andric                                        SPIRV::ModuleAnalysisInfo &MAI) {
303*7a6dacacSDimitry Andric   InstrSignature Signature;
304*7a6dacacSDimitry Andric   for (unsigned i = 0; i < MI.getNumOperands(); ++i) {
305*7a6dacacSDimitry Andric     const MachineOperand &MO = MI.getOperand(i);
306*7a6dacacSDimitry Andric     size_t h;
307*7a6dacacSDimitry Andric     if (MO.isReg()) {
308*7a6dacacSDimitry Andric       Register RegAlias = MAI.getRegisterAlias(MI.getMF(), MO.getReg());
309*7a6dacacSDimitry Andric       // mimic llvm::hash_value(const MachineOperand &MO)
310*7a6dacacSDimitry Andric       h = hash_combine(MO.getType(), (unsigned)RegAlias, MO.getSubReg(),
311*7a6dacacSDimitry Andric                        MO.isDef());
312*7a6dacacSDimitry Andric     } else {
313*7a6dacacSDimitry Andric       h = hash_value(MO);
314*7a6dacacSDimitry Andric     }
315*7a6dacacSDimitry Andric     Signature.push_back(h);
316*7a6dacacSDimitry Andric   }
317*7a6dacacSDimitry Andric   return Signature;
318*7a6dacacSDimitry Andric }
319*7a6dacacSDimitry Andric 
32081ad6265SDimitry Andric // Collect the given instruction in the specified MS. We assume global register
32181ad6265SDimitry Andric // numbering has already occurred by this point. We can directly compare reg
32281ad6265SDimitry Andric // arguments when detecting duplicates.
collectOtherInstr(MachineInstr & MI,SPIRV::ModuleAnalysisInfo & MAI,SPIRV::ModuleSectionType MSType,InstrTraces & IS,bool Append=true)32381ad6265SDimitry Andric static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI,
324*7a6dacacSDimitry Andric                               SPIRV::ModuleSectionType MSType, InstrTraces &IS,
325fcaf7f86SDimitry Andric                               bool Append = true) {
32681ad6265SDimitry Andric   MAI.setSkipEmission(&MI);
327*7a6dacacSDimitry Andric   InstrSignature MISign = instrToSignature(MI, MAI);
328*7a6dacacSDimitry Andric   auto FoundMI = IS.insert(MISign);
329*7a6dacacSDimitry Andric   if (!FoundMI.second)
330*7a6dacacSDimitry Andric     return; // insert failed, so we found a duplicate; don't add it to MAI.MS
33181ad6265SDimitry Andric   // No duplicates, so add it.
332fcaf7f86SDimitry Andric   if (Append)
33381ad6265SDimitry Andric     MAI.MS[MSType].push_back(&MI);
334fcaf7f86SDimitry Andric   else
335fcaf7f86SDimitry Andric     MAI.MS[MSType].insert(MAI.MS[MSType].begin(), &MI);
33681ad6265SDimitry Andric }
33781ad6265SDimitry Andric 
33881ad6265SDimitry Andric // Some global instructions make reference to function-local ID regs, so cannot
33981ad6265SDimitry Andric // be correctly collected until these registers are globally numbered.
processOtherInstrs(const Module & M)34081ad6265SDimitry Andric void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
341*7a6dacacSDimitry Andric   InstrTraces IS;
34281ad6265SDimitry Andric   for (auto F = M.begin(), E = M.end(); F != E; ++F) {
34381ad6265SDimitry Andric     if ((*F).isDeclaration())
34481ad6265SDimitry Andric       continue;
34581ad6265SDimitry Andric     MachineFunction *MF = MMI->getMachineFunction(*F);
34681ad6265SDimitry Andric     assert(MF);
34781ad6265SDimitry Andric     for (MachineBasicBlock &MBB : *MF)
34881ad6265SDimitry Andric       for (MachineInstr &MI : MBB) {
34981ad6265SDimitry Andric         if (MAI.getSkipEmission(&MI))
35081ad6265SDimitry Andric           continue;
35181ad6265SDimitry Andric         const unsigned OpCode = MI.getOpcode();
35281ad6265SDimitry Andric         if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
353*7a6dacacSDimitry Andric           collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames, IS);
35481ad6265SDimitry Andric         } else if (OpCode == SPIRV::OpEntryPoint) {
355*7a6dacacSDimitry Andric           collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints, IS);
35681ad6265SDimitry Andric         } else if (TII->isDecorationInstr(MI)) {
357*7a6dacacSDimitry Andric           collectOtherInstr(MI, MAI, SPIRV::MB_Annotations, IS);
358bdd1243dSDimitry Andric           collectFuncNames(MI, &*F);
359fcaf7f86SDimitry Andric         } else if (TII->isConstantInstr(MI)) {
360fcaf7f86SDimitry Andric           // Now OpSpecConstant*s are not in DT,
361fcaf7f86SDimitry Andric           // but they need to be collected anyway.
362*7a6dacacSDimitry Andric           collectOtherInstr(MI, MAI, SPIRV::MB_TypeConstVars, IS);
36381ad6265SDimitry Andric         } else if (OpCode == SPIRV::OpFunction) {
364bdd1243dSDimitry Andric           collectFuncNames(MI, &*F);
365fcaf7f86SDimitry Andric         } else if (OpCode == SPIRV::OpTypeForwardPointer) {
366*7a6dacacSDimitry Andric           collectOtherInstr(MI, MAI, SPIRV::MB_TypeConstVars, IS, false);
36781ad6265SDimitry Andric         }
36881ad6265SDimitry Andric       }
36981ad6265SDimitry Andric   }
37081ad6265SDimitry Andric }
37181ad6265SDimitry Andric 
37281ad6265SDimitry Andric // Number registers in all functions globally from 0 onwards and store
373fcaf7f86SDimitry Andric // the result in global register alias table. Some registers are already
374fcaf7f86SDimitry Andric // numbered in collectGlobalEntities.
numberRegistersGlobally(const Module & M)37581ad6265SDimitry Andric void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) {
37681ad6265SDimitry Andric   for (auto F = M.begin(), E = M.end(); F != E; ++F) {
37781ad6265SDimitry Andric     if ((*F).isDeclaration())
37881ad6265SDimitry Andric       continue;
37981ad6265SDimitry Andric     MachineFunction *MF = MMI->getMachineFunction(*F);
38081ad6265SDimitry Andric     assert(MF);
38181ad6265SDimitry Andric     for (MachineBasicBlock &MBB : *MF) {
38281ad6265SDimitry Andric       for (MachineInstr &MI : MBB) {
38381ad6265SDimitry Andric         for (MachineOperand &Op : MI.operands()) {
38481ad6265SDimitry Andric           if (!Op.isReg())
38581ad6265SDimitry Andric             continue;
38681ad6265SDimitry Andric           Register Reg = Op.getReg();
38781ad6265SDimitry Andric           if (MAI.hasRegisterAlias(MF, Reg))
38881ad6265SDimitry Andric             continue;
38981ad6265SDimitry Andric           Register NewReg = Register::index2VirtReg(MAI.getNextID());
39081ad6265SDimitry Andric           MAI.setRegisterAlias(MF, Reg, NewReg);
39181ad6265SDimitry Andric         }
392fcaf7f86SDimitry Andric         if (MI.getOpcode() != SPIRV::OpExtInst)
393fcaf7f86SDimitry Andric           continue;
394fcaf7f86SDimitry Andric         auto Set = MI.getOperand(2).getImm();
395cb14a3feSDimitry Andric         if (!MAI.ExtInstSetMap.contains(Set))
396fcaf7f86SDimitry Andric           MAI.ExtInstSetMap[Set] = Register::index2VirtReg(MAI.getNextID());
39781ad6265SDimitry Andric       }
39881ad6265SDimitry Andric     }
39981ad6265SDimitry Andric   }
40081ad6265SDimitry Andric }
40181ad6265SDimitry Andric 
402bdd1243dSDimitry Andric // RequirementHandler implementations.
getAndAddRequirements(SPIRV::OperandCategory::OperandCategory Category,uint32_t i,const SPIRVSubtarget & ST)403bdd1243dSDimitry Andric void SPIRV::RequirementHandler::getAndAddRequirements(
404bdd1243dSDimitry Andric     SPIRV::OperandCategory::OperandCategory Category, uint32_t i,
405bdd1243dSDimitry Andric     const SPIRVSubtarget &ST) {
406bdd1243dSDimitry Andric   addRequirements(getSymbolicOperandRequirements(Category, i, ST, *this));
407bdd1243dSDimitry Andric }
408bdd1243dSDimitry Andric 
pruneCapabilities(const CapabilityList & ToPrune)409bdd1243dSDimitry Andric void SPIRV::RequirementHandler::pruneCapabilities(
410bdd1243dSDimitry Andric     const CapabilityList &ToPrune) {
411bdd1243dSDimitry Andric   for (const auto &Cap : ToPrune) {
412bdd1243dSDimitry Andric     AllCaps.insert(Cap);
413*7a6dacacSDimitry Andric     auto FoundIndex = llvm::find(MinimalCaps, Cap);
414bdd1243dSDimitry Andric     if (FoundIndex != MinimalCaps.end())
415bdd1243dSDimitry Andric       MinimalCaps.erase(FoundIndex);
416bdd1243dSDimitry Andric     CapabilityList ImplicitDecls =
417bdd1243dSDimitry Andric         getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
418bdd1243dSDimitry Andric     pruneCapabilities(ImplicitDecls);
419bdd1243dSDimitry Andric   }
420bdd1243dSDimitry Andric }
421bdd1243dSDimitry Andric 
addCapabilities(const CapabilityList & ToAdd)422bdd1243dSDimitry Andric void SPIRV::RequirementHandler::addCapabilities(const CapabilityList &ToAdd) {
423bdd1243dSDimitry Andric   for (const auto &Cap : ToAdd) {
424bdd1243dSDimitry Andric     bool IsNewlyInserted = AllCaps.insert(Cap).second;
425bdd1243dSDimitry Andric     if (!IsNewlyInserted) // Don't re-add if it's already been declared.
426bdd1243dSDimitry Andric       continue;
427bdd1243dSDimitry Andric     CapabilityList ImplicitDecls =
428bdd1243dSDimitry Andric         getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
429bdd1243dSDimitry Andric     pruneCapabilities(ImplicitDecls);
430bdd1243dSDimitry Andric     MinimalCaps.push_back(Cap);
431bdd1243dSDimitry Andric   }
432bdd1243dSDimitry Andric }
433bdd1243dSDimitry Andric 
addRequirements(const SPIRV::Requirements & Req)434bdd1243dSDimitry Andric void SPIRV::RequirementHandler::addRequirements(
435bdd1243dSDimitry Andric     const SPIRV::Requirements &Req) {
436bdd1243dSDimitry Andric   if (!Req.IsSatisfiable)
437bdd1243dSDimitry Andric     report_fatal_error("Adding SPIR-V requirements this target can't satisfy.");
438bdd1243dSDimitry Andric 
439bdd1243dSDimitry Andric   if (Req.Cap.has_value())
440bdd1243dSDimitry Andric     addCapabilities({Req.Cap.value()});
441bdd1243dSDimitry Andric 
442bdd1243dSDimitry Andric   addExtensions(Req.Exts);
443bdd1243dSDimitry Andric 
444bdd1243dSDimitry Andric   if (Req.MinVer) {
445bdd1243dSDimitry Andric     if (MaxVersion && Req.MinVer > MaxVersion) {
446bdd1243dSDimitry Andric       LLVM_DEBUG(dbgs() << "Conflicting version requirements: >= " << Req.MinVer
447bdd1243dSDimitry Andric                         << " and <= " << MaxVersion << "\n");
448bdd1243dSDimitry Andric       report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
449bdd1243dSDimitry Andric     }
450bdd1243dSDimitry Andric 
451bdd1243dSDimitry Andric     if (MinVersion == 0 || Req.MinVer > MinVersion)
452bdd1243dSDimitry Andric       MinVersion = Req.MinVer;
453bdd1243dSDimitry Andric   }
454bdd1243dSDimitry Andric 
455bdd1243dSDimitry Andric   if (Req.MaxVer) {
456bdd1243dSDimitry Andric     if (MinVersion && Req.MaxVer < MinVersion) {
457bdd1243dSDimitry Andric       LLVM_DEBUG(dbgs() << "Conflicting version requirements: <= " << Req.MaxVer
458bdd1243dSDimitry Andric                         << " and >= " << MinVersion << "\n");
459bdd1243dSDimitry Andric       report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
460bdd1243dSDimitry Andric     }
461bdd1243dSDimitry Andric 
462bdd1243dSDimitry Andric     if (MaxVersion == 0 || Req.MaxVer < MaxVersion)
463bdd1243dSDimitry Andric       MaxVersion = Req.MaxVer;
464bdd1243dSDimitry Andric   }
465bdd1243dSDimitry Andric }
466bdd1243dSDimitry Andric 
checkSatisfiable(const SPIRVSubtarget & ST) const467bdd1243dSDimitry Andric void SPIRV::RequirementHandler::checkSatisfiable(
468bdd1243dSDimitry Andric     const SPIRVSubtarget &ST) const {
469bdd1243dSDimitry Andric   // Report as many errors as possible before aborting the compilation.
470bdd1243dSDimitry Andric   bool IsSatisfiable = true;
471bdd1243dSDimitry Andric   auto TargetVer = ST.getSPIRVVersion();
472bdd1243dSDimitry Andric 
473bdd1243dSDimitry Andric   if (MaxVersion && TargetVer && MaxVersion < TargetVer) {
474bdd1243dSDimitry Andric     LLVM_DEBUG(
475bdd1243dSDimitry Andric         dbgs() << "Target SPIR-V version too high for required features\n"
476bdd1243dSDimitry Andric                << "Required max version: " << MaxVersion << " target version "
477bdd1243dSDimitry Andric                << TargetVer << "\n");
478bdd1243dSDimitry Andric     IsSatisfiable = false;
479bdd1243dSDimitry Andric   }
480bdd1243dSDimitry Andric 
481bdd1243dSDimitry Andric   if (MinVersion && TargetVer && MinVersion > TargetVer) {
482bdd1243dSDimitry Andric     LLVM_DEBUG(dbgs() << "Target SPIR-V version too low for required features\n"
483bdd1243dSDimitry Andric                       << "Required min version: " << MinVersion
484bdd1243dSDimitry Andric                       << " target version " << TargetVer << "\n");
485bdd1243dSDimitry Andric     IsSatisfiable = false;
486bdd1243dSDimitry Andric   }
487bdd1243dSDimitry Andric 
488bdd1243dSDimitry Andric   if (MinVersion && MaxVersion && MinVersion > MaxVersion) {
489bdd1243dSDimitry Andric     LLVM_DEBUG(
490bdd1243dSDimitry Andric         dbgs()
491bdd1243dSDimitry Andric         << "Version is too low for some features and too high for others.\n"
492bdd1243dSDimitry Andric         << "Required SPIR-V min version: " << MinVersion
493bdd1243dSDimitry Andric         << " required SPIR-V max version " << MaxVersion << "\n");
494bdd1243dSDimitry Andric     IsSatisfiable = false;
495bdd1243dSDimitry Andric   }
496bdd1243dSDimitry Andric 
497bdd1243dSDimitry Andric   for (auto Cap : MinimalCaps) {
498bdd1243dSDimitry Andric     if (AvailableCaps.contains(Cap))
499bdd1243dSDimitry Andric       continue;
500bdd1243dSDimitry Andric     LLVM_DEBUG(dbgs() << "Capability not supported: "
501bdd1243dSDimitry Andric                       << getSymbolicOperandMnemonic(
502bdd1243dSDimitry Andric                              OperandCategory::CapabilityOperand, Cap)
503bdd1243dSDimitry Andric                       << "\n");
504bdd1243dSDimitry Andric     IsSatisfiable = false;
505bdd1243dSDimitry Andric   }
506bdd1243dSDimitry Andric 
507bdd1243dSDimitry Andric   for (auto Ext : AllExtensions) {
508bdd1243dSDimitry Andric     if (ST.canUseExtension(Ext))
509bdd1243dSDimitry Andric       continue;
5105f757f3fSDimitry Andric     LLVM_DEBUG(dbgs() << "Extension not supported: "
511bdd1243dSDimitry Andric                       << getSymbolicOperandMnemonic(
512bdd1243dSDimitry Andric                              OperandCategory::ExtensionOperand, Ext)
513bdd1243dSDimitry Andric                       << "\n");
514bdd1243dSDimitry Andric     IsSatisfiable = false;
515bdd1243dSDimitry Andric   }
516bdd1243dSDimitry Andric 
517bdd1243dSDimitry Andric   if (!IsSatisfiable)
518bdd1243dSDimitry Andric     report_fatal_error("Unable to meet SPIR-V requirements for this target.");
519bdd1243dSDimitry Andric }
520bdd1243dSDimitry Andric 
521bdd1243dSDimitry Andric // Add the given capabilities and all their implicitly defined capabilities too.
addAvailableCaps(const CapabilityList & ToAdd)522bdd1243dSDimitry Andric void SPIRV::RequirementHandler::addAvailableCaps(const CapabilityList &ToAdd) {
523bdd1243dSDimitry Andric   for (const auto Cap : ToAdd)
524bdd1243dSDimitry Andric     if (AvailableCaps.insert(Cap).second)
525bdd1243dSDimitry Andric       addAvailableCaps(getSymbolicOperandCapabilities(
526bdd1243dSDimitry Andric           SPIRV::OperandCategory::CapabilityOperand, Cap));
527bdd1243dSDimitry Andric }
528bdd1243dSDimitry Andric 
removeCapabilityIf(const Capability::Capability ToRemove,const Capability::Capability IfPresent)5295f757f3fSDimitry Andric void SPIRV::RequirementHandler::removeCapabilityIf(
5305f757f3fSDimitry Andric     const Capability::Capability ToRemove,
5315f757f3fSDimitry Andric     const Capability::Capability IfPresent) {
5325f757f3fSDimitry Andric   if (AllCaps.contains(IfPresent))
5335f757f3fSDimitry Andric     AllCaps.erase(ToRemove);
5345f757f3fSDimitry Andric }
5355f757f3fSDimitry Andric 
536bdd1243dSDimitry Andric namespace llvm {
537bdd1243dSDimitry Andric namespace SPIRV {
initAvailableCapabilities(const SPIRVSubtarget & ST)538bdd1243dSDimitry Andric void RequirementHandler::initAvailableCapabilities(const SPIRVSubtarget &ST) {
5395f757f3fSDimitry Andric   if (ST.isOpenCLEnv()) {
5405f757f3fSDimitry Andric     initAvailableCapabilitiesForOpenCL(ST);
541bdd1243dSDimitry Andric     return;
5425f757f3fSDimitry Andric   }
5435f757f3fSDimitry Andric 
5445f757f3fSDimitry Andric   if (ST.isVulkanEnv()) {
5455f757f3fSDimitry Andric     initAvailableCapabilitiesForVulkan(ST);
5465f757f3fSDimitry Andric     return;
5475f757f3fSDimitry Andric   }
5485f757f3fSDimitry Andric 
5495f757f3fSDimitry Andric   report_fatal_error("Unimplemented environment for SPIR-V generation.");
5505f757f3fSDimitry Andric }
5515f757f3fSDimitry Andric 
initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget & ST)5525f757f3fSDimitry Andric void RequirementHandler::initAvailableCapabilitiesForOpenCL(
5535f757f3fSDimitry Andric     const SPIRVSubtarget &ST) {
554bdd1243dSDimitry Andric   // Add the min requirements for different OpenCL and SPIR-V versions.
555bdd1243dSDimitry Andric   addAvailableCaps({Capability::Addresses, Capability::Float16Buffer,
556bdd1243dSDimitry Andric                     Capability::Int16, Capability::Int8, Capability::Kernel,
557bdd1243dSDimitry Andric                     Capability::Linkage, Capability::Vector16,
558bdd1243dSDimitry Andric                     Capability::Groups, Capability::GenericPointer,
559bdd1243dSDimitry Andric                     Capability::Shader});
560bdd1243dSDimitry Andric   if (ST.hasOpenCLFullProfile())
561bdd1243dSDimitry Andric     addAvailableCaps({Capability::Int64, Capability::Int64Atomics});
562bdd1243dSDimitry Andric   if (ST.hasOpenCLImageSupport()) {
563bdd1243dSDimitry Andric     addAvailableCaps({Capability::ImageBasic, Capability::LiteralSampler,
564bdd1243dSDimitry Andric                       Capability::Image1D, Capability::SampledBuffer,
565bdd1243dSDimitry Andric                       Capability::ImageBuffer});
566bdd1243dSDimitry Andric     if (ST.isAtLeastOpenCLVer(20))
567bdd1243dSDimitry Andric       addAvailableCaps({Capability::ImageReadWrite});
568bdd1243dSDimitry Andric   }
569bdd1243dSDimitry Andric   if (ST.isAtLeastSPIRVVer(11) && ST.isAtLeastOpenCLVer(22))
570bdd1243dSDimitry Andric     addAvailableCaps({Capability::SubgroupDispatch, Capability::PipeStorage});
571bdd1243dSDimitry Andric   if (ST.isAtLeastSPIRVVer(13))
572bdd1243dSDimitry Andric     addAvailableCaps({Capability::GroupNonUniform,
573bdd1243dSDimitry Andric                       Capability::GroupNonUniformVote,
574bdd1243dSDimitry Andric                       Capability::GroupNonUniformArithmetic,
575bdd1243dSDimitry Andric                       Capability::GroupNonUniformBallot,
576bdd1243dSDimitry Andric                       Capability::GroupNonUniformClustered,
577bdd1243dSDimitry Andric                       Capability::GroupNonUniformShuffle,
578bdd1243dSDimitry Andric                       Capability::GroupNonUniformShuffleRelative});
579bdd1243dSDimitry Andric   if (ST.isAtLeastSPIRVVer(14))
580bdd1243dSDimitry Andric     addAvailableCaps({Capability::DenormPreserve, Capability::DenormFlushToZero,
581bdd1243dSDimitry Andric                       Capability::SignedZeroInfNanPreserve,
582bdd1243dSDimitry Andric                       Capability::RoundingModeRTE,
583bdd1243dSDimitry Andric                       Capability::RoundingModeRTZ});
584bdd1243dSDimitry Andric   // TODO: verify if this needs some checks.
585bdd1243dSDimitry Andric   addAvailableCaps({Capability::Float16, Capability::Float64});
586bdd1243dSDimitry Andric 
5875f757f3fSDimitry Andric   // Add capabilities enabled by extensions.
5885f757f3fSDimitry Andric   for (auto Extension : ST.getAllAvailableExtensions()) {
5895f757f3fSDimitry Andric     CapabilityList EnabledCapabilities =
5905f757f3fSDimitry Andric         getCapabilitiesEnabledByExtension(Extension);
5915f757f3fSDimitry Andric     addAvailableCaps(EnabledCapabilities);
5925f757f3fSDimitry Andric   }
5935f757f3fSDimitry Andric 
594bdd1243dSDimitry Andric   // TODO: add OpenCL extensions.
595bdd1243dSDimitry Andric }
5965f757f3fSDimitry Andric 
initAvailableCapabilitiesForVulkan(const SPIRVSubtarget & ST)5975f757f3fSDimitry Andric void RequirementHandler::initAvailableCapabilitiesForVulkan(
5985f757f3fSDimitry Andric     const SPIRVSubtarget &ST) {
5995f757f3fSDimitry Andric   addAvailableCaps({Capability::Shader, Capability::Linkage});
6005f757f3fSDimitry Andric 
601*7a6dacacSDimitry Andric   // Provided by all supported Vulkan versions.
602*7a6dacacSDimitry Andric   addAvailableCaps({Capability::Int16, Capability::Int64, Capability::Float16,
603*7a6dacacSDimitry Andric                     Capability::Float64});
6045f757f3fSDimitry Andric }
6055f757f3fSDimitry Andric 
606bdd1243dSDimitry Andric } // namespace SPIRV
607bdd1243dSDimitry Andric } // namespace llvm
608bdd1243dSDimitry Andric 
609bdd1243dSDimitry Andric // Add the required capabilities from a decoration instruction (including
610bdd1243dSDimitry Andric // BuiltIns).
addOpDecorateReqs(const MachineInstr & MI,unsigned DecIndex,SPIRV::RequirementHandler & Reqs,const SPIRVSubtarget & ST)611bdd1243dSDimitry Andric static void addOpDecorateReqs(const MachineInstr &MI, unsigned DecIndex,
612bdd1243dSDimitry Andric                               SPIRV::RequirementHandler &Reqs,
613bdd1243dSDimitry Andric                               const SPIRVSubtarget &ST) {
614bdd1243dSDimitry Andric   int64_t DecOp = MI.getOperand(DecIndex).getImm();
615bdd1243dSDimitry Andric   auto Dec = static_cast<SPIRV::Decoration::Decoration>(DecOp);
616bdd1243dSDimitry Andric   Reqs.addRequirements(getSymbolicOperandRequirements(
617bdd1243dSDimitry Andric       SPIRV::OperandCategory::DecorationOperand, Dec, ST, Reqs));
618bdd1243dSDimitry Andric 
619bdd1243dSDimitry Andric   if (Dec == SPIRV::Decoration::BuiltIn) {
620bdd1243dSDimitry Andric     int64_t BuiltInOp = MI.getOperand(DecIndex + 1).getImm();
621bdd1243dSDimitry Andric     auto BuiltIn = static_cast<SPIRV::BuiltIn::BuiltIn>(BuiltInOp);
622bdd1243dSDimitry Andric     Reqs.addRequirements(getSymbolicOperandRequirements(
623bdd1243dSDimitry Andric         SPIRV::OperandCategory::BuiltInOperand, BuiltIn, ST, Reqs));
624bdd1243dSDimitry Andric   }
625bdd1243dSDimitry Andric }
626bdd1243dSDimitry Andric 
627bdd1243dSDimitry Andric // Add requirements for image handling.
addOpTypeImageReqs(const MachineInstr & MI,SPIRV::RequirementHandler & Reqs,const SPIRVSubtarget & ST)628bdd1243dSDimitry Andric static void addOpTypeImageReqs(const MachineInstr &MI,
629bdd1243dSDimitry Andric                                SPIRV::RequirementHandler &Reqs,
630bdd1243dSDimitry Andric                                const SPIRVSubtarget &ST) {
631bdd1243dSDimitry Andric   assert(MI.getNumOperands() >= 8 && "Insufficient operands for OpTypeImage");
632bdd1243dSDimitry Andric   // The operand indices used here are based on the OpTypeImage layout, which
633bdd1243dSDimitry Andric   // the MachineInstr follows as well.
634bdd1243dSDimitry Andric   int64_t ImgFormatOp = MI.getOperand(7).getImm();
635bdd1243dSDimitry Andric   auto ImgFormat = static_cast<SPIRV::ImageFormat::ImageFormat>(ImgFormatOp);
636bdd1243dSDimitry Andric   Reqs.getAndAddRequirements(SPIRV::OperandCategory::ImageFormatOperand,
637bdd1243dSDimitry Andric                              ImgFormat, ST);
638bdd1243dSDimitry Andric 
639bdd1243dSDimitry Andric   bool IsArrayed = MI.getOperand(4).getImm() == 1;
640bdd1243dSDimitry Andric   bool IsMultisampled = MI.getOperand(5).getImm() == 1;
641bdd1243dSDimitry Andric   bool NoSampler = MI.getOperand(6).getImm() == 2;
642bdd1243dSDimitry Andric   // Add dimension requirements.
643bdd1243dSDimitry Andric   assert(MI.getOperand(2).isImm());
644bdd1243dSDimitry Andric   switch (MI.getOperand(2).getImm()) {
645bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_1D:
646bdd1243dSDimitry Andric     Reqs.addRequirements(NoSampler ? SPIRV::Capability::Image1D
647bdd1243dSDimitry Andric                                    : SPIRV::Capability::Sampled1D);
648bdd1243dSDimitry Andric     break;
649bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_2D:
650bdd1243dSDimitry Andric     if (IsMultisampled && NoSampler)
651bdd1243dSDimitry Andric       Reqs.addRequirements(SPIRV::Capability::ImageMSArray);
652bdd1243dSDimitry Andric     break;
653bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Cube:
654bdd1243dSDimitry Andric     Reqs.addRequirements(SPIRV::Capability::Shader);
655bdd1243dSDimitry Andric     if (IsArrayed)
656bdd1243dSDimitry Andric       Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageCubeArray
657bdd1243dSDimitry Andric                                      : SPIRV::Capability::SampledCubeArray);
658bdd1243dSDimitry Andric     break;
659bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Rect:
660bdd1243dSDimitry Andric     Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageRect
661bdd1243dSDimitry Andric                                    : SPIRV::Capability::SampledRect);
662bdd1243dSDimitry Andric     break;
663bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_Buffer:
664bdd1243dSDimitry Andric     Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageBuffer
665bdd1243dSDimitry Andric                                    : SPIRV::Capability::SampledBuffer);
666bdd1243dSDimitry Andric     break;
667bdd1243dSDimitry Andric   case SPIRV::Dim::DIM_SubpassData:
668bdd1243dSDimitry Andric     Reqs.addRequirements(SPIRV::Capability::InputAttachment);
669bdd1243dSDimitry Andric     break;
670bdd1243dSDimitry Andric   }
671bdd1243dSDimitry Andric 
672bdd1243dSDimitry Andric   // Has optional access qualifier.
673bdd1243dSDimitry Andric   // TODO: check if it's OpenCL's kernel.
674bdd1243dSDimitry Andric   if (MI.getNumOperands() > 8 &&
675bdd1243dSDimitry Andric       MI.getOperand(8).getImm() == SPIRV::AccessQualifier::ReadWrite)
676bdd1243dSDimitry Andric     Reqs.addRequirements(SPIRV::Capability::ImageReadWrite);
677bdd1243dSDimitry Andric   else
678bdd1243dSDimitry Andric     Reqs.addRequirements(SPIRV::Capability::ImageBasic);
679bdd1243dSDimitry Andric }
680bdd1243dSDimitry Andric 
addInstrRequirements(const MachineInstr & MI,SPIRV::RequirementHandler & Reqs,const SPIRVSubtarget & ST)681bdd1243dSDimitry Andric void addInstrRequirements(const MachineInstr &MI,
682bdd1243dSDimitry Andric                           SPIRV::RequirementHandler &Reqs,
683bdd1243dSDimitry Andric                           const SPIRVSubtarget &ST) {
684bdd1243dSDimitry Andric   switch (MI.getOpcode()) {
685bdd1243dSDimitry Andric   case SPIRV::OpMemoryModel: {
686bdd1243dSDimitry Andric     int64_t Addr = MI.getOperand(0).getImm();
687bdd1243dSDimitry Andric     Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
688bdd1243dSDimitry Andric                                Addr, ST);
689bdd1243dSDimitry Andric     int64_t Mem = MI.getOperand(1).getImm();
690bdd1243dSDimitry Andric     Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand, Mem,
691bdd1243dSDimitry Andric                                ST);
692bdd1243dSDimitry Andric     break;
693bdd1243dSDimitry Andric   }
694bdd1243dSDimitry Andric   case SPIRV::OpEntryPoint: {
695bdd1243dSDimitry Andric     int64_t Exe = MI.getOperand(0).getImm();
696bdd1243dSDimitry Andric     Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModelOperand,
697bdd1243dSDimitry Andric                                Exe, ST);
698bdd1243dSDimitry Andric     break;
699bdd1243dSDimitry Andric   }
700bdd1243dSDimitry Andric   case SPIRV::OpExecutionMode:
701bdd1243dSDimitry Andric   case SPIRV::OpExecutionModeId: {
702bdd1243dSDimitry Andric     int64_t Exe = MI.getOperand(1).getImm();
703bdd1243dSDimitry Andric     Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModeOperand,
704bdd1243dSDimitry Andric                                Exe, ST);
705bdd1243dSDimitry Andric     break;
706bdd1243dSDimitry Andric   }
707bdd1243dSDimitry Andric   case SPIRV::OpTypeMatrix:
708bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::Matrix);
709bdd1243dSDimitry Andric     break;
710bdd1243dSDimitry Andric   case SPIRV::OpTypeInt: {
711bdd1243dSDimitry Andric     unsigned BitWidth = MI.getOperand(1).getImm();
712bdd1243dSDimitry Andric     if (BitWidth == 64)
713bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Int64);
714bdd1243dSDimitry Andric     else if (BitWidth == 16)
715bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Int16);
716bdd1243dSDimitry Andric     else if (BitWidth == 8)
717bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Int8);
718bdd1243dSDimitry Andric     break;
719bdd1243dSDimitry Andric   }
720bdd1243dSDimitry Andric   case SPIRV::OpTypeFloat: {
721bdd1243dSDimitry Andric     unsigned BitWidth = MI.getOperand(1).getImm();
722bdd1243dSDimitry Andric     if (BitWidth == 64)
723bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Float64);
724bdd1243dSDimitry Andric     else if (BitWidth == 16)
725bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Float16);
726bdd1243dSDimitry Andric     break;
727bdd1243dSDimitry Andric   }
728bdd1243dSDimitry Andric   case SPIRV::OpTypeVector: {
729bdd1243dSDimitry Andric     unsigned NumComponents = MI.getOperand(2).getImm();
730bdd1243dSDimitry Andric     if (NumComponents == 8 || NumComponents == 16)
731bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Vector16);
732bdd1243dSDimitry Andric     break;
733bdd1243dSDimitry Andric   }
734bdd1243dSDimitry Andric   case SPIRV::OpTypePointer: {
735bdd1243dSDimitry Andric     auto SC = MI.getOperand(1).getImm();
736bdd1243dSDimitry Andric     Reqs.getAndAddRequirements(SPIRV::OperandCategory::StorageClassOperand, SC,
737bdd1243dSDimitry Andric                                ST);
738*7a6dacacSDimitry Andric     // If it's a type of pointer to float16 targeting OpenCL, add Float16Buffer
739*7a6dacacSDimitry Andric     // capability.
740*7a6dacacSDimitry Andric     if (!ST.isOpenCLEnv())
741*7a6dacacSDimitry Andric       break;
742bdd1243dSDimitry Andric     assert(MI.getOperand(2).isReg());
743bdd1243dSDimitry Andric     const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
744bdd1243dSDimitry Andric     SPIRVType *TypeDef = MRI.getVRegDef(MI.getOperand(2).getReg());
745bdd1243dSDimitry Andric     if (TypeDef->getOpcode() == SPIRV::OpTypeFloat &&
746bdd1243dSDimitry Andric         TypeDef->getOperand(1).getImm() == 16)
747bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Float16Buffer);
748bdd1243dSDimitry Andric     break;
749bdd1243dSDimitry Andric   }
750bdd1243dSDimitry Andric   case SPIRV::OpBitReverse:
7515f757f3fSDimitry Andric   case SPIRV::OpBitFieldInsert:
7525f757f3fSDimitry Andric   case SPIRV::OpBitFieldSExtract:
7535f757f3fSDimitry Andric   case SPIRV::OpBitFieldUExtract:
7545f757f3fSDimitry Andric     if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions)) {
7555f757f3fSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Shader);
7565f757f3fSDimitry Andric       break;
7575f757f3fSDimitry Andric     }
7585f757f3fSDimitry Andric     Reqs.addExtension(SPIRV::Extension::SPV_KHR_bit_instructions);
7595f757f3fSDimitry Andric     Reqs.addCapability(SPIRV::Capability::BitInstructions);
7605f757f3fSDimitry Andric     break;
761bdd1243dSDimitry Andric   case SPIRV::OpTypeRuntimeArray:
762bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::Shader);
763bdd1243dSDimitry Andric     break;
764bdd1243dSDimitry Andric   case SPIRV::OpTypeOpaque:
765bdd1243dSDimitry Andric   case SPIRV::OpTypeEvent:
766bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::Kernel);
767bdd1243dSDimitry Andric     break;
768bdd1243dSDimitry Andric   case SPIRV::OpTypePipe:
769bdd1243dSDimitry Andric   case SPIRV::OpTypeReserveId:
770bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::Pipes);
771bdd1243dSDimitry Andric     break;
772bdd1243dSDimitry Andric   case SPIRV::OpTypeDeviceEvent:
773bdd1243dSDimitry Andric   case SPIRV::OpTypeQueue:
774bdd1243dSDimitry Andric   case SPIRV::OpBuildNDRange:
775bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::DeviceEnqueue);
776bdd1243dSDimitry Andric     break;
777bdd1243dSDimitry Andric   case SPIRV::OpDecorate:
778bdd1243dSDimitry Andric   case SPIRV::OpDecorateId:
779bdd1243dSDimitry Andric   case SPIRV::OpDecorateString:
780bdd1243dSDimitry Andric     addOpDecorateReqs(MI, 1, Reqs, ST);
781bdd1243dSDimitry Andric     break;
782bdd1243dSDimitry Andric   case SPIRV::OpMemberDecorate:
783bdd1243dSDimitry Andric   case SPIRV::OpMemberDecorateString:
784bdd1243dSDimitry Andric     addOpDecorateReqs(MI, 2, Reqs, ST);
785bdd1243dSDimitry Andric     break;
786bdd1243dSDimitry Andric   case SPIRV::OpInBoundsPtrAccessChain:
787bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::Addresses);
788bdd1243dSDimitry Andric     break;
789bdd1243dSDimitry Andric   case SPIRV::OpConstantSampler:
790bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::LiteralSampler);
791bdd1243dSDimitry Andric     break;
792bdd1243dSDimitry Andric   case SPIRV::OpTypeImage:
793bdd1243dSDimitry Andric     addOpTypeImageReqs(MI, Reqs, ST);
794bdd1243dSDimitry Andric     break;
795bdd1243dSDimitry Andric   case SPIRV::OpTypeSampler:
796bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::ImageBasic);
797bdd1243dSDimitry Andric     break;
798bdd1243dSDimitry Andric   case SPIRV::OpTypeForwardPointer:
799bdd1243dSDimitry Andric     // TODO: check if it's OpenCL's kernel.
800bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::Addresses);
801bdd1243dSDimitry Andric     break;
802bdd1243dSDimitry Andric   case SPIRV::OpAtomicFlagTestAndSet:
803bdd1243dSDimitry Andric   case SPIRV::OpAtomicLoad:
804bdd1243dSDimitry Andric   case SPIRV::OpAtomicStore:
805bdd1243dSDimitry Andric   case SPIRV::OpAtomicExchange:
806bdd1243dSDimitry Andric   case SPIRV::OpAtomicCompareExchange:
807bdd1243dSDimitry Andric   case SPIRV::OpAtomicIIncrement:
808bdd1243dSDimitry Andric   case SPIRV::OpAtomicIDecrement:
809bdd1243dSDimitry Andric   case SPIRV::OpAtomicIAdd:
810bdd1243dSDimitry Andric   case SPIRV::OpAtomicISub:
811bdd1243dSDimitry Andric   case SPIRV::OpAtomicUMin:
812bdd1243dSDimitry Andric   case SPIRV::OpAtomicUMax:
813bdd1243dSDimitry Andric   case SPIRV::OpAtomicSMin:
814bdd1243dSDimitry Andric   case SPIRV::OpAtomicSMax:
815bdd1243dSDimitry Andric   case SPIRV::OpAtomicAnd:
816bdd1243dSDimitry Andric   case SPIRV::OpAtomicOr:
817bdd1243dSDimitry Andric   case SPIRV::OpAtomicXor: {
818bdd1243dSDimitry Andric     const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
819bdd1243dSDimitry Andric     const MachineInstr *InstrPtr = &MI;
820bdd1243dSDimitry Andric     if (MI.getOpcode() == SPIRV::OpAtomicStore) {
821bdd1243dSDimitry Andric       assert(MI.getOperand(3).isReg());
822bdd1243dSDimitry Andric       InstrPtr = MRI.getVRegDef(MI.getOperand(3).getReg());
823bdd1243dSDimitry Andric       assert(InstrPtr && "Unexpected type instruction for OpAtomicStore");
824bdd1243dSDimitry Andric     }
825bdd1243dSDimitry Andric     assert(InstrPtr->getOperand(1).isReg() && "Unexpected operand in atomic");
826bdd1243dSDimitry Andric     Register TypeReg = InstrPtr->getOperand(1).getReg();
827bdd1243dSDimitry Andric     SPIRVType *TypeDef = MRI.getVRegDef(TypeReg);
828bdd1243dSDimitry Andric     if (TypeDef->getOpcode() == SPIRV::OpTypeInt) {
829bdd1243dSDimitry Andric       unsigned BitWidth = TypeDef->getOperand(1).getImm();
830bdd1243dSDimitry Andric       if (BitWidth == 64)
831bdd1243dSDimitry Andric         Reqs.addCapability(SPIRV::Capability::Int64Atomics);
832bdd1243dSDimitry Andric     }
833bdd1243dSDimitry Andric     break;
834bdd1243dSDimitry Andric   }
835bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformIAdd:
836bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformFAdd:
837bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformIMul:
838bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformFMul:
839bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformSMin:
840bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformUMin:
841bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformFMin:
842bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformSMax:
843bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformUMax:
844bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformFMax:
845bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBitwiseAnd:
846bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBitwiseOr:
847bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBitwiseXor:
848bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformLogicalAnd:
849bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformLogicalOr:
850bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformLogicalXor: {
851bdd1243dSDimitry Andric     assert(MI.getOperand(3).isImm());
852bdd1243dSDimitry Andric     int64_t GroupOp = MI.getOperand(3).getImm();
853bdd1243dSDimitry Andric     switch (GroupOp) {
854bdd1243dSDimitry Andric     case SPIRV::GroupOperation::Reduce:
855bdd1243dSDimitry Andric     case SPIRV::GroupOperation::InclusiveScan:
856bdd1243dSDimitry Andric     case SPIRV::GroupOperation::ExclusiveScan:
857bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::Kernel);
858bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::GroupNonUniformArithmetic);
859bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot);
860bdd1243dSDimitry Andric       break;
861bdd1243dSDimitry Andric     case SPIRV::GroupOperation::ClusteredReduce:
862bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::GroupNonUniformClustered);
863bdd1243dSDimitry Andric       break;
864bdd1243dSDimitry Andric     case SPIRV::GroupOperation::PartitionedReduceNV:
865bdd1243dSDimitry Andric     case SPIRV::GroupOperation::PartitionedInclusiveScanNV:
866bdd1243dSDimitry Andric     case SPIRV::GroupOperation::PartitionedExclusiveScanNV:
867bdd1243dSDimitry Andric       Reqs.addCapability(SPIRV::Capability::GroupNonUniformPartitionedNV);
868bdd1243dSDimitry Andric       break;
869bdd1243dSDimitry Andric     }
870bdd1243dSDimitry Andric     break;
871bdd1243dSDimitry Andric   }
872bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformShuffle:
873bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformShuffleXor:
874bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffle);
875bdd1243dSDimitry Andric     break;
876bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformShuffleUp:
877bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformShuffleDown:
878bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffleRelative);
879bdd1243dSDimitry Andric     break;
880bdd1243dSDimitry Andric   case SPIRV::OpGroupAll:
881bdd1243dSDimitry Andric   case SPIRV::OpGroupAny:
882bdd1243dSDimitry Andric   case SPIRV::OpGroupBroadcast:
883bdd1243dSDimitry Andric   case SPIRV::OpGroupIAdd:
884bdd1243dSDimitry Andric   case SPIRV::OpGroupFAdd:
885bdd1243dSDimitry Andric   case SPIRV::OpGroupFMin:
886bdd1243dSDimitry Andric   case SPIRV::OpGroupUMin:
887bdd1243dSDimitry Andric   case SPIRV::OpGroupSMin:
888bdd1243dSDimitry Andric   case SPIRV::OpGroupFMax:
889bdd1243dSDimitry Andric   case SPIRV::OpGroupUMax:
890bdd1243dSDimitry Andric   case SPIRV::OpGroupSMax:
891bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::Groups);
892bdd1243dSDimitry Andric     break;
893bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformElect:
894bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::GroupNonUniform);
895bdd1243dSDimitry Andric     break;
896bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformAll:
897bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformAny:
898bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformAllEqual:
899bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::GroupNonUniformVote);
900bdd1243dSDimitry Andric     break;
901bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBroadcast:
902bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBroadcastFirst:
903bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBallot:
904bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformInverseBallot:
905bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBallotBitExtract:
906bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBallotBitCount:
907bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBallotFindLSB:
908bdd1243dSDimitry Andric   case SPIRV::OpGroupNonUniformBallotFindMSB:
909bdd1243dSDimitry Andric     Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot);
910bdd1243dSDimitry Andric     break;
9115f757f3fSDimitry Andric   case SPIRV::OpAssumeTrueKHR:
9125f757f3fSDimitry Andric   case SPIRV::OpExpectKHR:
9135f757f3fSDimitry Andric     if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume)) {
9145f757f3fSDimitry Andric       Reqs.addExtension(SPIRV::Extension::SPV_KHR_expect_assume);
9155f757f3fSDimitry Andric       Reqs.addCapability(SPIRV::Capability::ExpectAssumeKHR);
9165f757f3fSDimitry Andric     }
9175f757f3fSDimitry Andric     break;
918bdd1243dSDimitry Andric   default:
919bdd1243dSDimitry Andric     break;
920bdd1243dSDimitry Andric   }
9215f757f3fSDimitry Andric 
9225f757f3fSDimitry Andric   // If we require capability Shader, then we can remove the requirement for
9235f757f3fSDimitry Andric   // the BitInstructions capability, since Shader is a superset capability
9245f757f3fSDimitry Andric   // of BitInstructions.
9255f757f3fSDimitry Andric   Reqs.removeCapabilityIf(SPIRV::Capability::BitInstructions,
9265f757f3fSDimitry Andric                           SPIRV::Capability::Shader);
927bdd1243dSDimitry Andric }
928bdd1243dSDimitry Andric 
collectReqs(const Module & M,SPIRV::ModuleAnalysisInfo & MAI,MachineModuleInfo * MMI,const SPIRVSubtarget & ST)929bdd1243dSDimitry Andric static void collectReqs(const Module &M, SPIRV::ModuleAnalysisInfo &MAI,
930bdd1243dSDimitry Andric                         MachineModuleInfo *MMI, const SPIRVSubtarget &ST) {
931bdd1243dSDimitry Andric   // Collect requirements for existing instructions.
932bdd1243dSDimitry Andric   for (auto F = M.begin(), E = M.end(); F != E; ++F) {
933bdd1243dSDimitry Andric     MachineFunction *MF = MMI->getMachineFunction(*F);
934bdd1243dSDimitry Andric     if (!MF)
935bdd1243dSDimitry Andric       continue;
936bdd1243dSDimitry Andric     for (const MachineBasicBlock &MBB : *MF)
937bdd1243dSDimitry Andric       for (const MachineInstr &MI : MBB)
938bdd1243dSDimitry Andric         addInstrRequirements(MI, MAI.Reqs, ST);
939bdd1243dSDimitry Andric   }
940bdd1243dSDimitry Andric   // Collect requirements for OpExecutionMode instructions.
941bdd1243dSDimitry Andric   auto Node = M.getNamedMetadata("spirv.ExecutionMode");
942bdd1243dSDimitry Andric   if (Node) {
943bdd1243dSDimitry Andric     for (unsigned i = 0; i < Node->getNumOperands(); i++) {
944bdd1243dSDimitry Andric       MDNode *MDN = cast<MDNode>(Node->getOperand(i));
945bdd1243dSDimitry Andric       const MDOperand &MDOp = MDN->getOperand(1);
946bdd1243dSDimitry Andric       if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) {
947bdd1243dSDimitry Andric         Constant *C = CMeta->getValue();
948bdd1243dSDimitry Andric         if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) {
949bdd1243dSDimitry Andric           auto EM = Const->getZExtValue();
950bdd1243dSDimitry Andric           MAI.Reqs.getAndAddRequirements(
951bdd1243dSDimitry Andric               SPIRV::OperandCategory::ExecutionModeOperand, EM, ST);
952bdd1243dSDimitry Andric         }
953bdd1243dSDimitry Andric       }
954bdd1243dSDimitry Andric     }
955bdd1243dSDimitry Andric   }
956bdd1243dSDimitry Andric   for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) {
957bdd1243dSDimitry Andric     const Function &F = *FI;
958bdd1243dSDimitry Andric     if (F.isDeclaration())
959bdd1243dSDimitry Andric       continue;
960bdd1243dSDimitry Andric     if (F.getMetadata("reqd_work_group_size"))
961bdd1243dSDimitry Andric       MAI.Reqs.getAndAddRequirements(
962bdd1243dSDimitry Andric           SPIRV::OperandCategory::ExecutionModeOperand,
963bdd1243dSDimitry Andric           SPIRV::ExecutionMode::LocalSize, ST);
9645f757f3fSDimitry Andric     if (F.getFnAttribute("hlsl.numthreads").isValid()) {
9655f757f3fSDimitry Andric       MAI.Reqs.getAndAddRequirements(
9665f757f3fSDimitry Andric           SPIRV::OperandCategory::ExecutionModeOperand,
9675f757f3fSDimitry Andric           SPIRV::ExecutionMode::LocalSize, ST);
9685f757f3fSDimitry Andric     }
969bdd1243dSDimitry Andric     if (F.getMetadata("work_group_size_hint"))
970bdd1243dSDimitry Andric       MAI.Reqs.getAndAddRequirements(
971bdd1243dSDimitry Andric           SPIRV::OperandCategory::ExecutionModeOperand,
972bdd1243dSDimitry Andric           SPIRV::ExecutionMode::LocalSizeHint, ST);
973bdd1243dSDimitry Andric     if (F.getMetadata("intel_reqd_sub_group_size"))
974bdd1243dSDimitry Andric       MAI.Reqs.getAndAddRequirements(
975bdd1243dSDimitry Andric           SPIRV::OperandCategory::ExecutionModeOperand,
976bdd1243dSDimitry Andric           SPIRV::ExecutionMode::SubgroupSize, ST);
977bdd1243dSDimitry Andric     if (F.getMetadata("vec_type_hint"))
978bdd1243dSDimitry Andric       MAI.Reqs.getAndAddRequirements(
979bdd1243dSDimitry Andric           SPIRV::OperandCategory::ExecutionModeOperand,
980bdd1243dSDimitry Andric           SPIRV::ExecutionMode::VecTypeHint, ST);
9815f757f3fSDimitry Andric 
9825f757f3fSDimitry Andric     if (F.hasOptNone() &&
9835f757f3fSDimitry Andric         ST.canUseExtension(SPIRV::Extension::SPV_INTEL_optnone)) {
9845f757f3fSDimitry Andric       // Output OpCapability OptNoneINTEL.
9855f757f3fSDimitry Andric       MAI.Reqs.addExtension(SPIRV::Extension::SPV_INTEL_optnone);
9865f757f3fSDimitry Andric       MAI.Reqs.addCapability(SPIRV::Capability::OptNoneINTEL);
9875f757f3fSDimitry Andric     }
988bdd1243dSDimitry Andric   }
989bdd1243dSDimitry Andric }
990bdd1243dSDimitry Andric 
getFastMathFlags(const MachineInstr & I)991bdd1243dSDimitry Andric static unsigned getFastMathFlags(const MachineInstr &I) {
992bdd1243dSDimitry Andric   unsigned Flags = SPIRV::FPFastMathMode::None;
993bdd1243dSDimitry Andric   if (I.getFlag(MachineInstr::MIFlag::FmNoNans))
994bdd1243dSDimitry Andric     Flags |= SPIRV::FPFastMathMode::NotNaN;
995bdd1243dSDimitry Andric   if (I.getFlag(MachineInstr::MIFlag::FmNoInfs))
996bdd1243dSDimitry Andric     Flags |= SPIRV::FPFastMathMode::NotInf;
997bdd1243dSDimitry Andric   if (I.getFlag(MachineInstr::MIFlag::FmNsz))
998bdd1243dSDimitry Andric     Flags |= SPIRV::FPFastMathMode::NSZ;
999bdd1243dSDimitry Andric   if (I.getFlag(MachineInstr::MIFlag::FmArcp))
1000bdd1243dSDimitry Andric     Flags |= SPIRV::FPFastMathMode::AllowRecip;
1001bdd1243dSDimitry Andric   if (I.getFlag(MachineInstr::MIFlag::FmReassoc))
1002bdd1243dSDimitry Andric     Flags |= SPIRV::FPFastMathMode::Fast;
1003bdd1243dSDimitry Andric   return Flags;
1004bdd1243dSDimitry Andric }
1005bdd1243dSDimitry Andric 
handleMIFlagDecoration(MachineInstr & I,const SPIRVSubtarget & ST,const SPIRVInstrInfo & TII,SPIRV::RequirementHandler & Reqs)1006bdd1243dSDimitry Andric static void handleMIFlagDecoration(MachineInstr &I, const SPIRVSubtarget &ST,
1007bdd1243dSDimitry Andric                                    const SPIRVInstrInfo &TII,
1008bdd1243dSDimitry Andric                                    SPIRV::RequirementHandler &Reqs) {
1009bdd1243dSDimitry Andric   if (I.getFlag(MachineInstr::MIFlag::NoSWrap) && TII.canUseNSW(I) &&
1010bdd1243dSDimitry Andric       getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand,
1011bdd1243dSDimitry Andric                                      SPIRV::Decoration::NoSignedWrap, ST, Reqs)
1012bdd1243dSDimitry Andric           .IsSatisfiable) {
1013bdd1243dSDimitry Andric     buildOpDecorate(I.getOperand(0).getReg(), I, TII,
1014bdd1243dSDimitry Andric                     SPIRV::Decoration::NoSignedWrap, {});
1015bdd1243dSDimitry Andric   }
1016bdd1243dSDimitry Andric   if (I.getFlag(MachineInstr::MIFlag::NoUWrap) && TII.canUseNUW(I) &&
1017bdd1243dSDimitry Andric       getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand,
1018bdd1243dSDimitry Andric                                      SPIRV::Decoration::NoUnsignedWrap, ST,
1019bdd1243dSDimitry Andric                                      Reqs)
1020bdd1243dSDimitry Andric           .IsSatisfiable) {
1021bdd1243dSDimitry Andric     buildOpDecorate(I.getOperand(0).getReg(), I, TII,
1022bdd1243dSDimitry Andric                     SPIRV::Decoration::NoUnsignedWrap, {});
1023bdd1243dSDimitry Andric   }
1024bdd1243dSDimitry Andric   if (!TII.canUseFastMathFlags(I))
1025bdd1243dSDimitry Andric     return;
1026bdd1243dSDimitry Andric   unsigned FMFlags = getFastMathFlags(I);
1027bdd1243dSDimitry Andric   if (FMFlags == SPIRV::FPFastMathMode::None)
1028bdd1243dSDimitry Andric     return;
1029bdd1243dSDimitry Andric   Register DstReg = I.getOperand(0).getReg();
1030bdd1243dSDimitry Andric   buildOpDecorate(DstReg, I, TII, SPIRV::Decoration::FPFastMathMode, {FMFlags});
1031bdd1243dSDimitry Andric }
1032bdd1243dSDimitry Andric 
1033bdd1243dSDimitry Andric // Walk all functions and add decorations related to MI flags.
addDecorations(const Module & M,const SPIRVInstrInfo & TII,MachineModuleInfo * MMI,const SPIRVSubtarget & ST,SPIRV::ModuleAnalysisInfo & MAI)1034bdd1243dSDimitry Andric static void addDecorations(const Module &M, const SPIRVInstrInfo &TII,
1035bdd1243dSDimitry Andric                            MachineModuleInfo *MMI, const SPIRVSubtarget &ST,
1036bdd1243dSDimitry Andric                            SPIRV::ModuleAnalysisInfo &MAI) {
1037bdd1243dSDimitry Andric   for (auto F = M.begin(), E = M.end(); F != E; ++F) {
1038bdd1243dSDimitry Andric     MachineFunction *MF = MMI->getMachineFunction(*F);
1039bdd1243dSDimitry Andric     if (!MF)
1040bdd1243dSDimitry Andric       continue;
1041bdd1243dSDimitry Andric     for (auto &MBB : *MF)
1042bdd1243dSDimitry Andric       for (auto &MI : MBB)
1043bdd1243dSDimitry Andric         handleMIFlagDecoration(MI, ST, TII, MAI.Reqs);
1044bdd1243dSDimitry Andric   }
1045bdd1243dSDimitry Andric }
1046bdd1243dSDimitry Andric 
104781ad6265SDimitry Andric struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI;
104881ad6265SDimitry Andric 
getAnalysisUsage(AnalysisUsage & AU) const104981ad6265SDimitry Andric void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
105081ad6265SDimitry Andric   AU.addRequired<TargetPassConfig>();
105181ad6265SDimitry Andric   AU.addRequired<MachineModuleInfoWrapperPass>();
105281ad6265SDimitry Andric }
105381ad6265SDimitry Andric 
runOnModule(Module & M)105481ad6265SDimitry Andric bool SPIRVModuleAnalysis::runOnModule(Module &M) {
105581ad6265SDimitry Andric   SPIRVTargetMachine &TM =
105681ad6265SDimitry Andric       getAnalysis<TargetPassConfig>().getTM<SPIRVTargetMachine>();
105781ad6265SDimitry Andric   ST = TM.getSubtargetImpl();
105881ad6265SDimitry Andric   GR = ST->getSPIRVGlobalRegistry();
105981ad6265SDimitry Andric   TII = ST->getInstrInfo();
106081ad6265SDimitry Andric 
106181ad6265SDimitry Andric   MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
106281ad6265SDimitry Andric 
106381ad6265SDimitry Andric   setBaseInfo(M);
106481ad6265SDimitry Andric 
1065bdd1243dSDimitry Andric   addDecorations(M, *TII, MMI, *ST, MAI);
1066bdd1243dSDimitry Andric 
1067bdd1243dSDimitry Andric   collectReqs(M, MAI, MMI, *ST);
1068bdd1243dSDimitry Andric 
1069fcaf7f86SDimitry Andric   // Process type/const/global var/func decl instructions, number their
107081ad6265SDimitry Andric   // destination registers from 0 to N, collect Extensions and Capabilities.
1071753f127fSDimitry Andric   processDefInstrs(M);
107281ad6265SDimitry Andric 
107381ad6265SDimitry Andric   // Number rest of registers from N+1 onwards.
107481ad6265SDimitry Andric   numberRegistersGlobally(M);
107581ad6265SDimitry Andric 
107681ad6265SDimitry Andric   // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions.
107781ad6265SDimitry Andric   processOtherInstrs(M);
107881ad6265SDimitry Andric 
1079bdd1243dSDimitry Andric   // If there are no entry points, we need the Linkage capability.
1080bdd1243dSDimitry Andric   if (MAI.MS[SPIRV::MB_EntryPoints].empty())
1081bdd1243dSDimitry Andric     MAI.Reqs.addCapability(SPIRV::Capability::Linkage);
1082bdd1243dSDimitry Andric 
108381ad6265SDimitry Andric   return false;
108481ad6265SDimitry Andric }
1085