1145449b1SDimitry Andric //===- SPIRVModuleAnalysis.cpp - analysis of global instrs & regs - C++ -*-===//
2145449b1SDimitry Andric //
3145449b1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4145449b1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5145449b1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6145449b1SDimitry Andric //
7145449b1SDimitry Andric //===----------------------------------------------------------------------===//
8145449b1SDimitry Andric //
9145449b1SDimitry Andric // The analysis collects instructions that should be output at the module level
10145449b1SDimitry Andric // and performs the global register numbering.
11145449b1SDimitry Andric //
12145449b1SDimitry Andric // The results of this analysis are used in AsmPrinter to rename registers
13145449b1SDimitry Andric // globally and to output required instructions at the module level.
14145449b1SDimitry Andric //
15145449b1SDimitry Andric //===----------------------------------------------------------------------===//
16145449b1SDimitry Andric
17145449b1SDimitry Andric #include "SPIRVModuleAnalysis.h"
18b1c73532SDimitry Andric #include "MCTargetDesc/SPIRVBaseInfo.h"
19b1c73532SDimitry Andric #include "MCTargetDesc/SPIRVMCTargetDesc.h"
20145449b1SDimitry Andric #include "SPIRV.h"
21145449b1SDimitry Andric #include "SPIRVSubtarget.h"
22145449b1SDimitry Andric #include "SPIRVTargetMachine.h"
23145449b1SDimitry Andric #include "SPIRVUtils.h"
24145449b1SDimitry Andric #include "TargetInfo/SPIRVTargetInfo.h"
25e3b55780SDimitry Andric #include "llvm/ADT/STLExtras.h"
26145449b1SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h"
27145449b1SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
28145449b1SDimitry Andric
29145449b1SDimitry Andric using namespace llvm;
30145449b1SDimitry Andric
31145449b1SDimitry Andric #define DEBUG_TYPE "spirv-module-analysis"
32145449b1SDimitry Andric
331f917f69SDimitry Andric static cl::opt<bool>
341f917f69SDimitry Andric SPVDumpDeps("spv-dump-deps",
351f917f69SDimitry Andric cl::desc("Dump MIR with SPIR-V dependencies info"),
361f917f69SDimitry Andric cl::Optional, cl::init(false));
371f917f69SDimitry Andric
38ac9a064cSDimitry Andric static cl::list<SPIRV::Capability::Capability>
39ac9a064cSDimitry Andric AvoidCapabilities("avoid-spirv-capabilities",
40ac9a064cSDimitry Andric cl::desc("SPIR-V capabilities to avoid if there are "
41ac9a064cSDimitry Andric "other options enabling a feature"),
42ac9a064cSDimitry Andric cl::ZeroOrMore, cl::Hidden,
43ac9a064cSDimitry Andric cl::values(clEnumValN(SPIRV::Capability::Shader, "Shader",
44ac9a064cSDimitry Andric "SPIR-V Shader capability")));
45ac9a064cSDimitry Andric // Use sets instead of cl::list to check "if contains" condition
46ac9a064cSDimitry Andric struct AvoidCapabilitiesSet {
47ac9a064cSDimitry Andric SmallSet<SPIRV::Capability::Capability, 4> S;
AvoidCapabilitiesSetAvoidCapabilitiesSet48ac9a064cSDimitry Andric AvoidCapabilitiesSet() {
49ac9a064cSDimitry Andric for (auto Cap : AvoidCapabilities)
50ac9a064cSDimitry Andric S.insert(Cap);
51ac9a064cSDimitry Andric }
52ac9a064cSDimitry Andric };
53ac9a064cSDimitry Andric
54145449b1SDimitry Andric char llvm::SPIRVModuleAnalysis::ID = 0;
55145449b1SDimitry Andric
56145449b1SDimitry Andric namespace llvm {
57145449b1SDimitry Andric void initializeSPIRVModuleAnalysisPass(PassRegistry &);
58145449b1SDimitry Andric } // namespace llvm
59145449b1SDimitry Andric
60145449b1SDimitry Andric INITIALIZE_PASS(SPIRVModuleAnalysis, DEBUG_TYPE, "SPIRV module analysis", true,
61145449b1SDimitry Andric true)
62145449b1SDimitry Andric
63145449b1SDimitry Andric // Retrieve an unsigned from an MDNode with a list of them as operands.
getMetadataUInt(MDNode * MdNode,unsigned OpIndex,unsigned DefaultVal=0)64145449b1SDimitry Andric static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex,
65145449b1SDimitry Andric unsigned DefaultVal = 0) {
66145449b1SDimitry Andric if (MdNode && OpIndex < MdNode->getNumOperands()) {
67145449b1SDimitry Andric const auto &Op = MdNode->getOperand(OpIndex);
68145449b1SDimitry Andric return mdconst::extract<ConstantInt>(Op)->getZExtValue();
69145449b1SDimitry Andric }
70145449b1SDimitry Andric return DefaultVal;
71145449b1SDimitry Andric }
72145449b1SDimitry Andric
73e3b55780SDimitry Andric static SPIRV::Requirements
getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,unsigned i,const SPIRVSubtarget & ST,SPIRV::RequirementHandler & Reqs)74e3b55780SDimitry Andric getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
75e3b55780SDimitry Andric unsigned i, const SPIRVSubtarget &ST,
76e3b55780SDimitry Andric SPIRV::RequirementHandler &Reqs) {
77ac9a064cSDimitry Andric static AvoidCapabilitiesSet
78ac9a064cSDimitry Andric AvoidCaps; // contains capabilities to avoid if there is another option
79ac9a064cSDimitry Andric
80ac9a064cSDimitry Andric VersionTuple ReqMinVer = getSymbolicOperandMinVersion(Category, i);
81ac9a064cSDimitry Andric VersionTuple ReqMaxVer = getSymbolicOperandMaxVersion(Category, i);
82ac9a064cSDimitry Andric VersionTuple SPIRVVersion = ST.getSPIRVVersion();
83ac9a064cSDimitry Andric bool MinVerOK = SPIRVVersion.empty() || SPIRVVersion >= ReqMinVer;
84ac9a064cSDimitry Andric bool MaxVerOK =
85ac9a064cSDimitry Andric ReqMaxVer.empty() || SPIRVVersion.empty() || SPIRVVersion <= ReqMaxVer;
86e3b55780SDimitry Andric CapabilityList ReqCaps = getSymbolicOperandCapabilities(Category, i);
87e3b55780SDimitry Andric ExtensionList ReqExts = getSymbolicOperandExtensions(Category, i);
88e3b55780SDimitry Andric if (ReqCaps.empty()) {
89e3b55780SDimitry Andric if (ReqExts.empty()) {
90e3b55780SDimitry Andric if (MinVerOK && MaxVerOK)
91e3b55780SDimitry Andric return {true, {}, {}, ReqMinVer, ReqMaxVer};
92ac9a064cSDimitry Andric return {false, {}, {}, VersionTuple(), VersionTuple()};
93e3b55780SDimitry Andric }
94e3b55780SDimitry Andric } else if (MinVerOK && MaxVerOK) {
95ac9a064cSDimitry Andric if (ReqCaps.size() == 1) {
96ac9a064cSDimitry Andric auto Cap = ReqCaps[0];
97e3b55780SDimitry Andric if (Reqs.isCapabilityAvailable(Cap))
98ac9a064cSDimitry Andric return {true, {Cap}, ReqExts, ReqMinVer, ReqMaxVer};
99ac9a064cSDimitry Andric } else {
100ac9a064cSDimitry Andric // By SPIR-V specification: "If an instruction, enumerant, or other
101ac9a064cSDimitry Andric // feature specifies multiple enabling capabilities, only one such
102ac9a064cSDimitry Andric // capability needs to be declared to use the feature." However, one
103ac9a064cSDimitry Andric // capability may be preferred over another. We use command line
104ac9a064cSDimitry Andric // argument(s) and AvoidCapabilities to avoid selection of certain
105ac9a064cSDimitry Andric // capabilities if there are other options.
106ac9a064cSDimitry Andric CapabilityList UseCaps;
107ac9a064cSDimitry Andric for (auto Cap : ReqCaps)
108ac9a064cSDimitry Andric if (Reqs.isCapabilityAvailable(Cap))
109ac9a064cSDimitry Andric UseCaps.push_back(Cap);
110ac9a064cSDimitry Andric for (size_t i = 0, Sz = UseCaps.size(); i < Sz; ++i) {
111ac9a064cSDimitry Andric auto Cap = UseCaps[i];
112ac9a064cSDimitry Andric if (i == Sz - 1 || !AvoidCaps.S.contains(Cap))
113ac9a064cSDimitry Andric return {true, {Cap}, ReqExts, ReqMinVer, ReqMaxVer};
114ac9a064cSDimitry Andric }
115e3b55780SDimitry Andric }
116e3b55780SDimitry Andric }
117e3b55780SDimitry Andric // If there are no capabilities, or we can't satisfy the version or
118e3b55780SDimitry Andric // capability requirements, use the list of extensions (if the subtarget
119e3b55780SDimitry Andric // can handle them all).
120e3b55780SDimitry Andric if (llvm::all_of(ReqExts, [&ST](const SPIRV::Extension::Extension &Ext) {
121e3b55780SDimitry Andric return ST.canUseExtension(Ext);
122e3b55780SDimitry Andric })) {
123ac9a064cSDimitry Andric return {true,
124ac9a064cSDimitry Andric {},
125ac9a064cSDimitry Andric ReqExts,
126ac9a064cSDimitry Andric VersionTuple(),
127ac9a064cSDimitry Andric VersionTuple()}; // TODO: add versions to extensions.
128e3b55780SDimitry Andric }
129ac9a064cSDimitry Andric return {false, {}, {}, VersionTuple(), VersionTuple()};
130e3b55780SDimitry Andric }
131e3b55780SDimitry Andric
setBaseInfo(const Module & M)132145449b1SDimitry Andric void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
133145449b1SDimitry Andric MAI.MaxID = 0;
134145449b1SDimitry Andric for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++)
135145449b1SDimitry Andric MAI.MS[i].clear();
136145449b1SDimitry Andric MAI.RegisterAliasTable.clear();
137145449b1SDimitry Andric MAI.InstrsToDelete.clear();
138e3b55780SDimitry Andric MAI.FuncMap.clear();
139145449b1SDimitry Andric MAI.GlobalVarList.clear();
1404b4fe385SDimitry Andric MAI.ExtInstSetMap.clear();
141e3b55780SDimitry Andric MAI.Reqs.clear();
142e3b55780SDimitry Andric MAI.Reqs.initAvailableCapabilities(*ST);
143145449b1SDimitry Andric
144145449b1SDimitry Andric // TODO: determine memory model and source language from the configuratoin.
1454b4fe385SDimitry Andric if (auto MemModel = M.getNamedMetadata("spirv.MemoryModel")) {
1464b4fe385SDimitry Andric auto MemMD = MemModel->getOperand(0);
147e3b55780SDimitry Andric MAI.Addr = static_cast<SPIRV::AddressingModel::AddressingModel>(
148e3b55780SDimitry Andric getMetadataUInt(MemMD, 0));
149e3b55780SDimitry Andric MAI.Mem =
150e3b55780SDimitry Andric static_cast<SPIRV::MemoryModel::MemoryModel>(getMetadataUInt(MemMD, 1));
1514b4fe385SDimitry Andric } else {
152b1c73532SDimitry Andric // TODO: Add support for VulkanMemoryModel.
153b1c73532SDimitry Andric MAI.Mem = ST->isOpenCLEnv() ? SPIRV::MemoryModel::OpenCL
154b1c73532SDimitry Andric : SPIRV::MemoryModel::GLSL450;
155b1c73532SDimitry Andric if (MAI.Mem == SPIRV::MemoryModel::OpenCL) {
156145449b1SDimitry Andric unsigned PtrSize = ST->getPointerSize();
157145449b1SDimitry Andric MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32
158145449b1SDimitry Andric : PtrSize == 64 ? SPIRV::AddressingModel::Physical64
159145449b1SDimitry Andric : SPIRV::AddressingModel::Logical;
160b1c73532SDimitry Andric } else {
161b1c73532SDimitry Andric // TODO: Add support for PhysicalStorageBufferAddress.
162b1c73532SDimitry Andric MAI.Addr = SPIRV::AddressingModel::Logical;
163b1c73532SDimitry Andric }
1644b4fe385SDimitry Andric }
165145449b1SDimitry Andric // Get the OpenCL version number from metadata.
166145449b1SDimitry Andric // TODO: support other source languages.
167145449b1SDimitry Andric if (auto VerNode = M.getNamedMetadata("opencl.ocl.version")) {
1684b4fe385SDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_C;
1694b4fe385SDimitry Andric // Construct version literal in accordance with SPIRV-LLVM-Translator.
1704b4fe385SDimitry Andric // TODO: support multiple OCL version metadata.
1714b4fe385SDimitry Andric assert(VerNode->getNumOperands() > 0 && "Invalid SPIR");
172145449b1SDimitry Andric auto VersionMD = VerNode->getOperand(0);
173145449b1SDimitry Andric unsigned MajorNum = getMetadataUInt(VersionMD, 0, 2);
174145449b1SDimitry Andric unsigned MinorNum = getMetadataUInt(VersionMD, 1);
175145449b1SDimitry Andric unsigned RevNum = getMetadataUInt(VersionMD, 2);
176ac9a064cSDimitry Andric // Prevent Major part of OpenCL version to be 0
177ac9a064cSDimitry Andric MAI.SrcLangVersion =
178ac9a064cSDimitry Andric (std::max(1U, MajorNum) * 100 + MinorNum) * 1000 + RevNum;
179ac9a064cSDimitry Andric } else {
180ac9a064cSDimitry Andric // If there is no information about OpenCL version we are forced to generate
181ac9a064cSDimitry Andric // OpenCL 1.0 by default for the OpenCL environment to avoid puzzling
182ac9a064cSDimitry Andric // run-times with Unknown/0.0 version output. For a reference, LLVM-SPIRV
183ac9a064cSDimitry Andric // Translator avoids potential issues with run-times in a similar manner.
184ac9a064cSDimitry Andric if (ST->isOpenCLEnv()) {
185ac9a064cSDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_CPP;
186ac9a064cSDimitry Andric MAI.SrcLangVersion = 100000;
1874b4fe385SDimitry Andric } else {
1884b4fe385SDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::Unknown;
1894b4fe385SDimitry Andric MAI.SrcLangVersion = 0;
1904b4fe385SDimitry Andric }
191ac9a064cSDimitry Andric }
1924b4fe385SDimitry Andric
1934b4fe385SDimitry Andric if (auto ExtNode = M.getNamedMetadata("opencl.used.extensions")) {
1944b4fe385SDimitry Andric for (unsigned I = 0, E = ExtNode->getNumOperands(); I != E; ++I) {
1954b4fe385SDimitry Andric MDNode *MD = ExtNode->getOperand(I);
1964b4fe385SDimitry Andric if (!MD || MD->getNumOperands() == 0)
1974b4fe385SDimitry Andric continue;
1984b4fe385SDimitry Andric for (unsigned J = 0, N = MD->getNumOperands(); J != N; ++J)
1994b4fe385SDimitry Andric MAI.SrcExt.insert(cast<MDString>(MD->getOperand(J))->getString());
200145449b1SDimitry Andric }
201145449b1SDimitry Andric }
202145449b1SDimitry Andric
203e3b55780SDimitry Andric // Update required capabilities for this memory model, addressing model and
204e3b55780SDimitry Andric // source language.
205e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand,
206e3b55780SDimitry Andric MAI.Mem, *ST);
207e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::SourceLanguageOperand,
208e3b55780SDimitry Andric MAI.SrcLang, *ST);
209e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
210e3b55780SDimitry Andric MAI.Addr, *ST);
211e3b55780SDimitry Andric
212b1c73532SDimitry Andric if (ST->isOpenCLEnv()) {
2134b4fe385SDimitry Andric // TODO: check if it's required by default.
214b1c73532SDimitry Andric MAI.ExtInstSetMap[static_cast<unsigned>(
215b1c73532SDimitry Andric SPIRV::InstructionSet::OpenCL_std)] =
2164b4fe385SDimitry Andric Register::index2VirtReg(MAI.getNextID());
217145449b1SDimitry Andric }
218b1c73532SDimitry Andric }
219145449b1SDimitry Andric
2201f917f69SDimitry 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)2211f917f69SDimitry Andric static void collectDefInstr(Register Reg, const MachineFunction *MF,
2221f917f69SDimitry Andric SPIRV::ModuleAnalysisInfo *MAI,
2231f917f69SDimitry Andric SPIRV::ModuleSectionType MSType,
2241f917f69SDimitry Andric bool DoInsert = true) {
2251f917f69SDimitry Andric assert(MAI->hasRegisterAlias(MF, Reg) && "Cannot find register alias");
2261f917f69SDimitry Andric MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(Reg);
2271f917f69SDimitry Andric assert(MI && "There should be an instruction that defines the register");
2281f917f69SDimitry Andric MAI->setSkipEmission(MI);
2291f917f69SDimitry Andric if (DoInsert)
2301f917f69SDimitry Andric MAI->MS[MSType].push_back(MI);
2311f917f69SDimitry Andric }
2321f917f69SDimitry Andric
collectGlobalEntities(const std::vector<SPIRV::DTSortableEntry * > & DepsGraph,SPIRV::ModuleSectionType MSType,std::function<bool (const SPIRV::DTSortableEntry *)> Pred,bool UsePreOrder=false)2331f917f69SDimitry Andric void SPIRVModuleAnalysis::collectGlobalEntities(
2341f917f69SDimitry Andric const std::vector<SPIRV::DTSortableEntry *> &DepsGraph,
2351f917f69SDimitry Andric SPIRV::ModuleSectionType MSType,
2361f917f69SDimitry Andric std::function<bool(const SPIRV::DTSortableEntry *)> Pred,
2374b4fe385SDimitry Andric bool UsePreOrder = false) {
2381f917f69SDimitry Andric DenseSet<const SPIRV::DTSortableEntry *> Visited;
2391f917f69SDimitry Andric for (const auto *E : DepsGraph) {
2401f917f69SDimitry Andric std::function<void(const SPIRV::DTSortableEntry *)> RecHoistUtil;
2411f917f69SDimitry Andric // NOTE: here we prefer recursive approach over iterative because
2421f917f69SDimitry Andric // we don't expect depchains long enough to cause SO.
2431f917f69SDimitry Andric RecHoistUtil = [MSType, UsePreOrder, &Visited, &Pred,
2441f917f69SDimitry Andric &RecHoistUtil](const SPIRV::DTSortableEntry *E) {
2451f917f69SDimitry Andric if (Visited.count(E) || !Pred(E))
2461f917f69SDimitry Andric return;
2471f917f69SDimitry Andric Visited.insert(E);
2481f917f69SDimitry Andric
2491f917f69SDimitry Andric // Traversing deps graph in post-order allows us to get rid of
2501f917f69SDimitry Andric // register aliases preprocessing.
2511f917f69SDimitry Andric // But pre-order is required for correct processing of function
2521f917f69SDimitry Andric // declaration and arguments processing.
2531f917f69SDimitry Andric if (!UsePreOrder)
2541f917f69SDimitry Andric for (auto *S : E->getDeps())
2551f917f69SDimitry Andric RecHoistUtil(S);
2561f917f69SDimitry Andric
2571f917f69SDimitry Andric Register GlobalReg = Register::index2VirtReg(MAI.getNextID());
2581f917f69SDimitry Andric bool IsFirst = true;
2591f917f69SDimitry Andric for (auto &U : *E) {
2601f917f69SDimitry Andric const MachineFunction *MF = U.first;
2611f917f69SDimitry Andric Register Reg = U.second;
2621f917f69SDimitry Andric MAI.setRegisterAlias(MF, Reg, GlobalReg);
2631f917f69SDimitry Andric if (!MF->getRegInfo().getUniqueVRegDef(Reg))
2641f917f69SDimitry Andric continue;
2651f917f69SDimitry Andric collectDefInstr(Reg, MF, &MAI, MSType, IsFirst);
2661f917f69SDimitry Andric IsFirst = false;
2671f917f69SDimitry Andric if (E->getIsGV())
2681f917f69SDimitry Andric MAI.GlobalVarList.push_back(MF->getRegInfo().getUniqueVRegDef(Reg));
2691f917f69SDimitry Andric }
2701f917f69SDimitry Andric
2711f917f69SDimitry Andric if (UsePreOrder)
2721f917f69SDimitry Andric for (auto *S : E->getDeps())
2731f917f69SDimitry Andric RecHoistUtil(S);
2741f917f69SDimitry Andric };
2751f917f69SDimitry Andric RecHoistUtil(E);
2761f917f69SDimitry Andric }
2771f917f69SDimitry Andric }
2781f917f69SDimitry Andric
2791f917f69SDimitry Andric // The function initializes global register alias table for types, consts,
2801f917f69SDimitry Andric // global vars and func decls and collects these instruction for output
2811f917f69SDimitry Andric // at module level. Also it collects explicit OpExtension/OpCapability
2821f917f69SDimitry Andric // instructions.
processDefInstrs(const Module & M)2831f917f69SDimitry Andric void SPIRVModuleAnalysis::processDefInstrs(const Module &M) {
2841f917f69SDimitry Andric std::vector<SPIRV::DTSortableEntry *> DepsGraph;
2851f917f69SDimitry Andric
2861f917f69SDimitry Andric GR->buildDepsGraph(DepsGraph, SPVDumpDeps ? MMI : nullptr);
2871f917f69SDimitry Andric
2881f917f69SDimitry Andric collectGlobalEntities(
2891f917f69SDimitry Andric DepsGraph, SPIRV::MB_TypeConstVars,
2904b4fe385SDimitry Andric [](const SPIRV::DTSortableEntry *E) { return !E->getIsFunc(); });
2911f917f69SDimitry Andric
292e3b55780SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) {
293e3b55780SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F);
294e3b55780SDimitry Andric if (!MF)
295e3b55780SDimitry Andric continue;
296e3b55780SDimitry Andric // Iterate through and collect OpExtension/OpCapability instructions.
297e3b55780SDimitry Andric for (MachineBasicBlock &MBB : *MF) {
298e3b55780SDimitry Andric for (MachineInstr &MI : MBB) {
299e3b55780SDimitry Andric if (MI.getOpcode() == SPIRV::OpExtension) {
300e3b55780SDimitry Andric // Here, OpExtension just has a single enum operand, not a string.
301e3b55780SDimitry Andric auto Ext = SPIRV::Extension::Extension(MI.getOperand(0).getImm());
302e3b55780SDimitry Andric MAI.Reqs.addExtension(Ext);
303e3b55780SDimitry Andric MAI.setSkipEmission(&MI);
304e3b55780SDimitry Andric } else if (MI.getOpcode() == SPIRV::OpCapability) {
305e3b55780SDimitry Andric auto Cap = SPIRV::Capability::Capability(MI.getOperand(0).getImm());
306e3b55780SDimitry Andric MAI.Reqs.addCapability(Cap);
307e3b55780SDimitry Andric MAI.setSkipEmission(&MI);
308e3b55780SDimitry Andric }
309e3b55780SDimitry Andric }
310e3b55780SDimitry Andric }
311e3b55780SDimitry Andric }
312e3b55780SDimitry Andric
3131f917f69SDimitry Andric collectGlobalEntities(
3141f917f69SDimitry Andric DepsGraph, SPIRV::MB_ExtFuncDecls,
3151f917f69SDimitry Andric [](const SPIRV::DTSortableEntry *E) { return E->getIsFunc(); }, true);
3161f917f69SDimitry Andric }
3171f917f69SDimitry Andric
318e3b55780SDimitry Andric // Look for IDs declared with Import linkage, and map the corresponding function
319145449b1SDimitry Andric // to the register defining that variable (which will usually be the result of
320145449b1SDimitry Andric // an OpFunction). This lets us call externally imported functions using
321145449b1SDimitry Andric // the correct ID registers.
collectFuncNames(MachineInstr & MI,const Function * F)322145449b1SDimitry Andric void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI,
323e3b55780SDimitry Andric const Function *F) {
324145449b1SDimitry Andric if (MI.getOpcode() == SPIRV::OpDecorate) {
325145449b1SDimitry Andric // If it's got Import linkage.
326145449b1SDimitry Andric auto Dec = MI.getOperand(1).getImm();
327145449b1SDimitry Andric if (Dec == static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) {
328145449b1SDimitry Andric auto Lnk = MI.getOperand(MI.getNumOperands() - 1).getImm();
329145449b1SDimitry Andric if (Lnk == static_cast<unsigned>(SPIRV::LinkageType::Import)) {
330145449b1SDimitry Andric // Map imported function name to function ID register.
331e3b55780SDimitry Andric const Function *ImportedFunc =
332e3b55780SDimitry Andric F->getParent()->getFunction(getStringImm(MI, 2));
333145449b1SDimitry Andric Register Target = MI.getOperand(0).getReg();
334e3b55780SDimitry Andric MAI.FuncMap[ImportedFunc] = MAI.getRegisterAlias(MI.getMF(), Target);
335145449b1SDimitry Andric }
336145449b1SDimitry Andric }
337145449b1SDimitry Andric } else if (MI.getOpcode() == SPIRV::OpFunction) {
338145449b1SDimitry Andric // Record all internal OpFunction declarations.
339145449b1SDimitry Andric Register Reg = MI.defs().begin()->getReg();
340145449b1SDimitry Andric Register GlobalReg = MAI.getRegisterAlias(MI.getMF(), Reg);
341145449b1SDimitry Andric assert(GlobalReg.isValid());
342e3b55780SDimitry Andric MAI.FuncMap[F] = GlobalReg;
343145449b1SDimitry Andric }
344145449b1SDimitry Andric }
345145449b1SDimitry Andric
346ac9a064cSDimitry Andric // References to a function via function pointers generate virtual
347ac9a064cSDimitry Andric // registers without a definition. We are able to resolve this
348ac9a064cSDimitry Andric // reference using Globar Register info into an OpFunction instruction
349ac9a064cSDimitry Andric // and replace dummy operands by the corresponding global register references.
collectFuncPtrs()350ac9a064cSDimitry Andric void SPIRVModuleAnalysis::collectFuncPtrs() {
351ac9a064cSDimitry Andric for (auto &MI : MAI.MS[SPIRV::MB_TypeConstVars])
352ac9a064cSDimitry Andric if (MI->getOpcode() == SPIRV::OpConstantFunctionPointerINTEL)
353ac9a064cSDimitry Andric collectFuncPtrs(MI);
354ac9a064cSDimitry Andric }
355ac9a064cSDimitry Andric
collectFuncPtrs(MachineInstr * MI)356ac9a064cSDimitry Andric void SPIRVModuleAnalysis::collectFuncPtrs(MachineInstr *MI) {
357ac9a064cSDimitry Andric const MachineOperand *FunUse = &MI->getOperand(2);
358ac9a064cSDimitry Andric if (const MachineOperand *FunDef = GR->getFunctionDefinitionByUse(FunUse)) {
359ac9a064cSDimitry Andric const MachineInstr *FunDefMI = FunDef->getParent();
360ac9a064cSDimitry Andric assert(FunDefMI->getOpcode() == SPIRV::OpFunction &&
361ac9a064cSDimitry Andric "Constant function pointer must refer to function definition");
362ac9a064cSDimitry Andric Register FunDefReg = FunDef->getReg();
363ac9a064cSDimitry Andric Register GlobalFunDefReg =
364ac9a064cSDimitry Andric MAI.getRegisterAlias(FunDefMI->getMF(), FunDefReg);
365ac9a064cSDimitry Andric assert(GlobalFunDefReg.isValid() &&
366ac9a064cSDimitry Andric "Function definition must refer to a global register");
367ac9a064cSDimitry Andric Register FunPtrReg = FunUse->getReg();
368ac9a064cSDimitry Andric MAI.setRegisterAlias(MI->getMF(), FunPtrReg, GlobalFunDefReg);
369ac9a064cSDimitry Andric }
370ac9a064cSDimitry Andric }
371ac9a064cSDimitry Andric
3724df029ccSDimitry Andric using InstrSignature = SmallVector<size_t>;
3734df029ccSDimitry Andric using InstrTraces = std::set<InstrSignature>;
3744df029ccSDimitry Andric
3754df029ccSDimitry Andric // Returns a representation of an instruction as a vector of MachineOperand
3764df029ccSDimitry Andric // hash values, see llvm::hash_value(const MachineOperand &MO) for details.
3774df029ccSDimitry Andric // This creates a signature of the instruction with the same content
3784df029ccSDimitry Andric // that MachineOperand::isIdenticalTo uses for comparison.
instrToSignature(MachineInstr & MI,SPIRV::ModuleAnalysisInfo & MAI)3794df029ccSDimitry Andric static InstrSignature instrToSignature(MachineInstr &MI,
3804df029ccSDimitry Andric SPIRV::ModuleAnalysisInfo &MAI) {
3814df029ccSDimitry Andric InstrSignature Signature;
3824df029ccSDimitry Andric for (unsigned i = 0; i < MI.getNumOperands(); ++i) {
3834df029ccSDimitry Andric const MachineOperand &MO = MI.getOperand(i);
3844df029ccSDimitry Andric size_t h;
3854df029ccSDimitry Andric if (MO.isReg()) {
3864df029ccSDimitry Andric Register RegAlias = MAI.getRegisterAlias(MI.getMF(), MO.getReg());
3874df029ccSDimitry Andric // mimic llvm::hash_value(const MachineOperand &MO)
3884df029ccSDimitry Andric h = hash_combine(MO.getType(), (unsigned)RegAlias, MO.getSubReg(),
3894df029ccSDimitry Andric MO.isDef());
3904df029ccSDimitry Andric } else {
3914df029ccSDimitry Andric h = hash_value(MO);
3924df029ccSDimitry Andric }
3934df029ccSDimitry Andric Signature.push_back(h);
3944df029ccSDimitry Andric }
3954df029ccSDimitry Andric return Signature;
3964df029ccSDimitry Andric }
3974df029ccSDimitry Andric
398145449b1SDimitry Andric // Collect the given instruction in the specified MS. We assume global register
399145449b1SDimitry Andric // numbering has already occurred by this point. We can directly compare reg
400145449b1SDimitry Andric // arguments when detecting duplicates.
collectOtherInstr(MachineInstr & MI,SPIRV::ModuleAnalysisInfo & MAI,SPIRV::ModuleSectionType MSType,InstrTraces & IS,bool Append=true)401145449b1SDimitry Andric static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI,
4024df029ccSDimitry Andric SPIRV::ModuleSectionType MSType, InstrTraces &IS,
4034b4fe385SDimitry Andric bool Append = true) {
404145449b1SDimitry Andric MAI.setSkipEmission(&MI);
4054df029ccSDimitry Andric InstrSignature MISign = instrToSignature(MI, MAI);
4064df029ccSDimitry Andric auto FoundMI = IS.insert(MISign);
4074df029ccSDimitry Andric if (!FoundMI.second)
4084df029ccSDimitry Andric return; // insert failed, so we found a duplicate; don't add it to MAI.MS
409145449b1SDimitry Andric // No duplicates, so add it.
4104b4fe385SDimitry Andric if (Append)
411145449b1SDimitry Andric MAI.MS[MSType].push_back(&MI);
4124b4fe385SDimitry Andric else
4134b4fe385SDimitry Andric MAI.MS[MSType].insert(MAI.MS[MSType].begin(), &MI);
414145449b1SDimitry Andric }
415145449b1SDimitry Andric
416145449b1SDimitry Andric // Some global instructions make reference to function-local ID regs, so cannot
417145449b1SDimitry Andric // be correctly collected until these registers are globally numbered.
processOtherInstrs(const Module & M)418145449b1SDimitry Andric void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
4194df029ccSDimitry Andric InstrTraces IS;
420145449b1SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) {
421145449b1SDimitry Andric if ((*F).isDeclaration())
422145449b1SDimitry Andric continue;
423145449b1SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F);
424145449b1SDimitry Andric assert(MF);
425145449b1SDimitry Andric for (MachineBasicBlock &MBB : *MF)
426145449b1SDimitry Andric for (MachineInstr &MI : MBB) {
427145449b1SDimitry Andric if (MAI.getSkipEmission(&MI))
428145449b1SDimitry Andric continue;
429145449b1SDimitry Andric const unsigned OpCode = MI.getOpcode();
430145449b1SDimitry Andric if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
4314df029ccSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames, IS);
432145449b1SDimitry Andric } else if (OpCode == SPIRV::OpEntryPoint) {
4334df029ccSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints, IS);
434145449b1SDimitry Andric } else if (TII->isDecorationInstr(MI)) {
4354df029ccSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_Annotations, IS);
436e3b55780SDimitry Andric collectFuncNames(MI, &*F);
4374b4fe385SDimitry Andric } else if (TII->isConstantInstr(MI)) {
4384b4fe385SDimitry Andric // Now OpSpecConstant*s are not in DT,
4394b4fe385SDimitry Andric // but they need to be collected anyway.
4404df029ccSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_TypeConstVars, IS);
441145449b1SDimitry Andric } else if (OpCode == SPIRV::OpFunction) {
442e3b55780SDimitry Andric collectFuncNames(MI, &*F);
4434b4fe385SDimitry Andric } else if (OpCode == SPIRV::OpTypeForwardPointer) {
4444df029ccSDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_TypeConstVars, IS, false);
445145449b1SDimitry Andric }
446145449b1SDimitry Andric }
447145449b1SDimitry Andric }
448145449b1SDimitry Andric }
449145449b1SDimitry Andric
450145449b1SDimitry Andric // Number registers in all functions globally from 0 onwards and store
4514b4fe385SDimitry Andric // the result in global register alias table. Some registers are already
4524b4fe385SDimitry Andric // numbered in collectGlobalEntities.
numberRegistersGlobally(const Module & M)453145449b1SDimitry Andric void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) {
454145449b1SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) {
455145449b1SDimitry Andric if ((*F).isDeclaration())
456145449b1SDimitry Andric continue;
457145449b1SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F);
458145449b1SDimitry Andric assert(MF);
459145449b1SDimitry Andric for (MachineBasicBlock &MBB : *MF) {
460145449b1SDimitry Andric for (MachineInstr &MI : MBB) {
461145449b1SDimitry Andric for (MachineOperand &Op : MI.operands()) {
462145449b1SDimitry Andric if (!Op.isReg())
463145449b1SDimitry Andric continue;
464145449b1SDimitry Andric Register Reg = Op.getReg();
465145449b1SDimitry Andric if (MAI.hasRegisterAlias(MF, Reg))
466145449b1SDimitry Andric continue;
467145449b1SDimitry Andric Register NewReg = Register::index2VirtReg(MAI.getNextID());
468145449b1SDimitry Andric MAI.setRegisterAlias(MF, Reg, NewReg);
469145449b1SDimitry Andric }
4704b4fe385SDimitry Andric if (MI.getOpcode() != SPIRV::OpExtInst)
4714b4fe385SDimitry Andric continue;
4724b4fe385SDimitry Andric auto Set = MI.getOperand(2).getImm();
47399aabd70SDimitry Andric if (!MAI.ExtInstSetMap.contains(Set))
4744b4fe385SDimitry Andric MAI.ExtInstSetMap[Set] = Register::index2VirtReg(MAI.getNextID());
475145449b1SDimitry Andric }
476145449b1SDimitry Andric }
477145449b1SDimitry Andric }
478145449b1SDimitry Andric }
479145449b1SDimitry Andric
480e3b55780SDimitry Andric // RequirementHandler implementations.
getAndAddRequirements(SPIRV::OperandCategory::OperandCategory Category,uint32_t i,const SPIRVSubtarget & ST)481e3b55780SDimitry Andric void SPIRV::RequirementHandler::getAndAddRequirements(
482e3b55780SDimitry Andric SPIRV::OperandCategory::OperandCategory Category, uint32_t i,
483e3b55780SDimitry Andric const SPIRVSubtarget &ST) {
484e3b55780SDimitry Andric addRequirements(getSymbolicOperandRequirements(Category, i, ST, *this));
485e3b55780SDimitry Andric }
486e3b55780SDimitry Andric
recursiveAddCapabilities(const CapabilityList & ToPrune)487ac9a064cSDimitry Andric void SPIRV::RequirementHandler::recursiveAddCapabilities(
488e3b55780SDimitry Andric const CapabilityList &ToPrune) {
489e3b55780SDimitry Andric for (const auto &Cap : ToPrune) {
490e3b55780SDimitry Andric AllCaps.insert(Cap);
491e3b55780SDimitry Andric CapabilityList ImplicitDecls =
492e3b55780SDimitry Andric getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
493ac9a064cSDimitry Andric recursiveAddCapabilities(ImplicitDecls);
494e3b55780SDimitry Andric }
495e3b55780SDimitry Andric }
496e3b55780SDimitry Andric
addCapabilities(const CapabilityList & ToAdd)497e3b55780SDimitry Andric void SPIRV::RequirementHandler::addCapabilities(const CapabilityList &ToAdd) {
498e3b55780SDimitry Andric for (const auto &Cap : ToAdd) {
499e3b55780SDimitry Andric bool IsNewlyInserted = AllCaps.insert(Cap).second;
500e3b55780SDimitry Andric if (!IsNewlyInserted) // Don't re-add if it's already been declared.
501e3b55780SDimitry Andric continue;
502e3b55780SDimitry Andric CapabilityList ImplicitDecls =
503e3b55780SDimitry Andric getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
504ac9a064cSDimitry Andric recursiveAddCapabilities(ImplicitDecls);
505e3b55780SDimitry Andric MinimalCaps.push_back(Cap);
506e3b55780SDimitry Andric }
507e3b55780SDimitry Andric }
508e3b55780SDimitry Andric
addRequirements(const SPIRV::Requirements & Req)509e3b55780SDimitry Andric void SPIRV::RequirementHandler::addRequirements(
510e3b55780SDimitry Andric const SPIRV::Requirements &Req) {
511e3b55780SDimitry Andric if (!Req.IsSatisfiable)
512e3b55780SDimitry Andric report_fatal_error("Adding SPIR-V requirements this target can't satisfy.");
513e3b55780SDimitry Andric
514e3b55780SDimitry Andric if (Req.Cap.has_value())
515e3b55780SDimitry Andric addCapabilities({Req.Cap.value()});
516e3b55780SDimitry Andric
517e3b55780SDimitry Andric addExtensions(Req.Exts);
518e3b55780SDimitry Andric
519ac9a064cSDimitry Andric if (!Req.MinVer.empty()) {
520ac9a064cSDimitry Andric if (!MaxVersion.empty() && Req.MinVer > MaxVersion) {
521e3b55780SDimitry Andric LLVM_DEBUG(dbgs() << "Conflicting version requirements: >= " << Req.MinVer
522e3b55780SDimitry Andric << " and <= " << MaxVersion << "\n");
523e3b55780SDimitry Andric report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
524e3b55780SDimitry Andric }
525e3b55780SDimitry Andric
526ac9a064cSDimitry Andric if (MinVersion.empty() || Req.MinVer > MinVersion)
527e3b55780SDimitry Andric MinVersion = Req.MinVer;
528e3b55780SDimitry Andric }
529e3b55780SDimitry Andric
530ac9a064cSDimitry Andric if (!Req.MaxVer.empty()) {
531ac9a064cSDimitry Andric if (!MinVersion.empty() && Req.MaxVer < MinVersion) {
532e3b55780SDimitry Andric LLVM_DEBUG(dbgs() << "Conflicting version requirements: <= " << Req.MaxVer
533e3b55780SDimitry Andric << " and >= " << MinVersion << "\n");
534e3b55780SDimitry Andric report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
535e3b55780SDimitry Andric }
536e3b55780SDimitry Andric
537ac9a064cSDimitry Andric if (MaxVersion.empty() || Req.MaxVer < MaxVersion)
538e3b55780SDimitry Andric MaxVersion = Req.MaxVer;
539e3b55780SDimitry Andric }
540e3b55780SDimitry Andric }
541e3b55780SDimitry Andric
checkSatisfiable(const SPIRVSubtarget & ST) const542e3b55780SDimitry Andric void SPIRV::RequirementHandler::checkSatisfiable(
543e3b55780SDimitry Andric const SPIRVSubtarget &ST) const {
544e3b55780SDimitry Andric // Report as many errors as possible before aborting the compilation.
545e3b55780SDimitry Andric bool IsSatisfiable = true;
546e3b55780SDimitry Andric auto TargetVer = ST.getSPIRVVersion();
547e3b55780SDimitry Andric
548ac9a064cSDimitry Andric if (!MaxVersion.empty() && !TargetVer.empty() && MaxVersion < TargetVer) {
549e3b55780SDimitry Andric LLVM_DEBUG(
550e3b55780SDimitry Andric dbgs() << "Target SPIR-V version too high for required features\n"
551e3b55780SDimitry Andric << "Required max version: " << MaxVersion << " target version "
552e3b55780SDimitry Andric << TargetVer << "\n");
553e3b55780SDimitry Andric IsSatisfiable = false;
554e3b55780SDimitry Andric }
555e3b55780SDimitry Andric
556ac9a064cSDimitry Andric if (!MinVersion.empty() && !TargetVer.empty() && MinVersion > TargetVer) {
557e3b55780SDimitry Andric LLVM_DEBUG(dbgs() << "Target SPIR-V version too low for required features\n"
558e3b55780SDimitry Andric << "Required min version: " << MinVersion
559e3b55780SDimitry Andric << " target version " << TargetVer << "\n");
560e3b55780SDimitry Andric IsSatisfiable = false;
561e3b55780SDimitry Andric }
562e3b55780SDimitry Andric
563ac9a064cSDimitry Andric if (!MinVersion.empty() && !MaxVersion.empty() && MinVersion > MaxVersion) {
564e3b55780SDimitry Andric LLVM_DEBUG(
565e3b55780SDimitry Andric dbgs()
566e3b55780SDimitry Andric << "Version is too low for some features and too high for others.\n"
567e3b55780SDimitry Andric << "Required SPIR-V min version: " << MinVersion
568e3b55780SDimitry Andric << " required SPIR-V max version " << MaxVersion << "\n");
569e3b55780SDimitry Andric IsSatisfiable = false;
570e3b55780SDimitry Andric }
571e3b55780SDimitry Andric
572e3b55780SDimitry Andric for (auto Cap : MinimalCaps) {
573e3b55780SDimitry Andric if (AvailableCaps.contains(Cap))
574e3b55780SDimitry Andric continue;
575e3b55780SDimitry Andric LLVM_DEBUG(dbgs() << "Capability not supported: "
576e3b55780SDimitry Andric << getSymbolicOperandMnemonic(
577e3b55780SDimitry Andric OperandCategory::CapabilityOperand, Cap)
578e3b55780SDimitry Andric << "\n");
579e3b55780SDimitry Andric IsSatisfiable = false;
580e3b55780SDimitry Andric }
581e3b55780SDimitry Andric
582e3b55780SDimitry Andric for (auto Ext : AllExtensions) {
583e3b55780SDimitry Andric if (ST.canUseExtension(Ext))
584e3b55780SDimitry Andric continue;
585b1c73532SDimitry Andric LLVM_DEBUG(dbgs() << "Extension not supported: "
586e3b55780SDimitry Andric << getSymbolicOperandMnemonic(
587e3b55780SDimitry Andric OperandCategory::ExtensionOperand, Ext)
588e3b55780SDimitry Andric << "\n");
589e3b55780SDimitry Andric IsSatisfiable = false;
590e3b55780SDimitry Andric }
591e3b55780SDimitry Andric
592e3b55780SDimitry Andric if (!IsSatisfiable)
593e3b55780SDimitry Andric report_fatal_error("Unable to meet SPIR-V requirements for this target.");
594e3b55780SDimitry Andric }
595e3b55780SDimitry Andric
596e3b55780SDimitry Andric // Add the given capabilities and all their implicitly defined capabilities too.
addAvailableCaps(const CapabilityList & ToAdd)597e3b55780SDimitry Andric void SPIRV::RequirementHandler::addAvailableCaps(const CapabilityList &ToAdd) {
598e3b55780SDimitry Andric for (const auto Cap : ToAdd)
599e3b55780SDimitry Andric if (AvailableCaps.insert(Cap).second)
600e3b55780SDimitry Andric addAvailableCaps(getSymbolicOperandCapabilities(
601e3b55780SDimitry Andric SPIRV::OperandCategory::CapabilityOperand, Cap));
602e3b55780SDimitry Andric }
603e3b55780SDimitry Andric
removeCapabilityIf(const Capability::Capability ToRemove,const Capability::Capability IfPresent)604b1c73532SDimitry Andric void SPIRV::RequirementHandler::removeCapabilityIf(
605b1c73532SDimitry Andric const Capability::Capability ToRemove,
606b1c73532SDimitry Andric const Capability::Capability IfPresent) {
607b1c73532SDimitry Andric if (AllCaps.contains(IfPresent))
608b1c73532SDimitry Andric AllCaps.erase(ToRemove);
609b1c73532SDimitry Andric }
610b1c73532SDimitry Andric
611e3b55780SDimitry Andric namespace llvm {
612e3b55780SDimitry Andric namespace SPIRV {
initAvailableCapabilities(const SPIRVSubtarget & ST)613e3b55780SDimitry Andric void RequirementHandler::initAvailableCapabilities(const SPIRVSubtarget &ST) {
614b1c73532SDimitry Andric if (ST.isOpenCLEnv()) {
615b1c73532SDimitry Andric initAvailableCapabilitiesForOpenCL(ST);
616e3b55780SDimitry Andric return;
617b1c73532SDimitry Andric }
618b1c73532SDimitry Andric
619b1c73532SDimitry Andric if (ST.isVulkanEnv()) {
620b1c73532SDimitry Andric initAvailableCapabilitiesForVulkan(ST);
621b1c73532SDimitry Andric return;
622b1c73532SDimitry Andric }
623b1c73532SDimitry Andric
624b1c73532SDimitry Andric report_fatal_error("Unimplemented environment for SPIR-V generation.");
625b1c73532SDimitry Andric }
626b1c73532SDimitry Andric
initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget & ST)627b1c73532SDimitry Andric void RequirementHandler::initAvailableCapabilitiesForOpenCL(
628b1c73532SDimitry Andric const SPIRVSubtarget &ST) {
629e3b55780SDimitry Andric // Add the min requirements for different OpenCL and SPIR-V versions.
630e3b55780SDimitry Andric addAvailableCaps({Capability::Addresses, Capability::Float16Buffer,
631e3b55780SDimitry Andric Capability::Int16, Capability::Int8, Capability::Kernel,
632e3b55780SDimitry Andric Capability::Linkage, Capability::Vector16,
633e3b55780SDimitry Andric Capability::Groups, Capability::GenericPointer,
634e3b55780SDimitry Andric Capability::Shader});
635e3b55780SDimitry Andric if (ST.hasOpenCLFullProfile())
636e3b55780SDimitry Andric addAvailableCaps({Capability::Int64, Capability::Int64Atomics});
637e3b55780SDimitry Andric if (ST.hasOpenCLImageSupport()) {
638e3b55780SDimitry Andric addAvailableCaps({Capability::ImageBasic, Capability::LiteralSampler,
639e3b55780SDimitry Andric Capability::Image1D, Capability::SampledBuffer,
640e3b55780SDimitry Andric Capability::ImageBuffer});
641ac9a064cSDimitry Andric if (ST.isAtLeastOpenCLVer(VersionTuple(2, 0)))
642e3b55780SDimitry Andric addAvailableCaps({Capability::ImageReadWrite});
643e3b55780SDimitry Andric }
644ac9a064cSDimitry Andric if (ST.isAtLeastSPIRVVer(VersionTuple(1, 1)) &&
645ac9a064cSDimitry Andric ST.isAtLeastOpenCLVer(VersionTuple(2, 2)))
646e3b55780SDimitry Andric addAvailableCaps({Capability::SubgroupDispatch, Capability::PipeStorage});
647ac9a064cSDimitry Andric if (ST.isAtLeastSPIRVVer(VersionTuple(1, 3)))
648e3b55780SDimitry Andric addAvailableCaps({Capability::GroupNonUniform,
649e3b55780SDimitry Andric Capability::GroupNonUniformVote,
650e3b55780SDimitry Andric Capability::GroupNonUniformArithmetic,
651e3b55780SDimitry Andric Capability::GroupNonUniformBallot,
652e3b55780SDimitry Andric Capability::GroupNonUniformClustered,
653e3b55780SDimitry Andric Capability::GroupNonUniformShuffle,
654e3b55780SDimitry Andric Capability::GroupNonUniformShuffleRelative});
655ac9a064cSDimitry Andric if (ST.isAtLeastSPIRVVer(VersionTuple(1, 4)))
656e3b55780SDimitry Andric addAvailableCaps({Capability::DenormPreserve, Capability::DenormFlushToZero,
657e3b55780SDimitry Andric Capability::SignedZeroInfNanPreserve,
658e3b55780SDimitry Andric Capability::RoundingModeRTE,
659e3b55780SDimitry Andric Capability::RoundingModeRTZ});
660e3b55780SDimitry Andric // TODO: verify if this needs some checks.
661e3b55780SDimitry Andric addAvailableCaps({Capability::Float16, Capability::Float64});
662e3b55780SDimitry Andric
663b1c73532SDimitry Andric // Add capabilities enabled by extensions.
664b1c73532SDimitry Andric for (auto Extension : ST.getAllAvailableExtensions()) {
665b1c73532SDimitry Andric CapabilityList EnabledCapabilities =
666b1c73532SDimitry Andric getCapabilitiesEnabledByExtension(Extension);
667b1c73532SDimitry Andric addAvailableCaps(EnabledCapabilities);
668b1c73532SDimitry Andric }
669b1c73532SDimitry Andric
670e3b55780SDimitry Andric // TODO: add OpenCL extensions.
671e3b55780SDimitry Andric }
672b1c73532SDimitry Andric
initAvailableCapabilitiesForVulkan(const SPIRVSubtarget & ST)673b1c73532SDimitry Andric void RequirementHandler::initAvailableCapabilitiesForVulkan(
674b1c73532SDimitry Andric const SPIRVSubtarget &ST) {
675b1c73532SDimitry Andric addAvailableCaps({Capability::Shader, Capability::Linkage});
676b1c73532SDimitry Andric
6774df029ccSDimitry Andric // Provided by all supported Vulkan versions.
6784df029ccSDimitry Andric addAvailableCaps({Capability::Int16, Capability::Int64, Capability::Float16,
679ac9a064cSDimitry Andric Capability::Float64, Capability::GroupNonUniform});
680b1c73532SDimitry Andric }
681b1c73532SDimitry Andric
682e3b55780SDimitry Andric } // namespace SPIRV
683e3b55780SDimitry Andric } // namespace llvm
684e3b55780SDimitry Andric
685e3b55780SDimitry Andric // Add the required capabilities from a decoration instruction (including
686e3b55780SDimitry Andric // BuiltIns).
addOpDecorateReqs(const MachineInstr & MI,unsigned DecIndex,SPIRV::RequirementHandler & Reqs,const SPIRVSubtarget & ST)687e3b55780SDimitry Andric static void addOpDecorateReqs(const MachineInstr &MI, unsigned DecIndex,
688e3b55780SDimitry Andric SPIRV::RequirementHandler &Reqs,
689e3b55780SDimitry Andric const SPIRVSubtarget &ST) {
690e3b55780SDimitry Andric int64_t DecOp = MI.getOperand(DecIndex).getImm();
691e3b55780SDimitry Andric auto Dec = static_cast<SPIRV::Decoration::Decoration>(DecOp);
692e3b55780SDimitry Andric Reqs.addRequirements(getSymbolicOperandRequirements(
693e3b55780SDimitry Andric SPIRV::OperandCategory::DecorationOperand, Dec, ST, Reqs));
694e3b55780SDimitry Andric
695e3b55780SDimitry Andric if (Dec == SPIRV::Decoration::BuiltIn) {
696e3b55780SDimitry Andric int64_t BuiltInOp = MI.getOperand(DecIndex + 1).getImm();
697e3b55780SDimitry Andric auto BuiltIn = static_cast<SPIRV::BuiltIn::BuiltIn>(BuiltInOp);
698e3b55780SDimitry Andric Reqs.addRequirements(getSymbolicOperandRequirements(
699e3b55780SDimitry Andric SPIRV::OperandCategory::BuiltInOperand, BuiltIn, ST, Reqs));
700ac9a064cSDimitry Andric } else if (Dec == SPIRV::Decoration::LinkageAttributes) {
701ac9a064cSDimitry Andric int64_t LinkageOp = MI.getOperand(MI.getNumOperands() - 1).getImm();
702ac9a064cSDimitry Andric SPIRV::LinkageType::LinkageType LnkType =
703ac9a064cSDimitry Andric static_cast<SPIRV::LinkageType::LinkageType>(LinkageOp);
704ac9a064cSDimitry Andric if (LnkType == SPIRV::LinkageType::LinkOnceODR)
705ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_linkonce_odr);
706ac9a064cSDimitry Andric } else if (Dec == SPIRV::Decoration::CacheControlLoadINTEL ||
707ac9a064cSDimitry Andric Dec == SPIRV::Decoration::CacheControlStoreINTEL) {
708ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_cache_controls);
709ac9a064cSDimitry Andric } else if (Dec == SPIRV::Decoration::HostAccessINTEL) {
710ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_global_variable_host_access);
711ac9a064cSDimitry Andric } else if (Dec == SPIRV::Decoration::InitModeINTEL ||
712ac9a064cSDimitry Andric Dec == SPIRV::Decoration::ImplementInRegisterMapINTEL) {
713ac9a064cSDimitry Andric Reqs.addExtension(
714ac9a064cSDimitry Andric SPIRV::Extension::SPV_INTEL_global_variable_fpga_decorations);
715e3b55780SDimitry Andric }
716e3b55780SDimitry Andric }
717e3b55780SDimitry Andric
718e3b55780SDimitry Andric // Add requirements for image handling.
addOpTypeImageReqs(const MachineInstr & MI,SPIRV::RequirementHandler & Reqs,const SPIRVSubtarget & ST)719e3b55780SDimitry Andric static void addOpTypeImageReqs(const MachineInstr &MI,
720e3b55780SDimitry Andric SPIRV::RequirementHandler &Reqs,
721e3b55780SDimitry Andric const SPIRVSubtarget &ST) {
722e3b55780SDimitry Andric assert(MI.getNumOperands() >= 8 && "Insufficient operands for OpTypeImage");
723e3b55780SDimitry Andric // The operand indices used here are based on the OpTypeImage layout, which
724e3b55780SDimitry Andric // the MachineInstr follows as well.
725e3b55780SDimitry Andric int64_t ImgFormatOp = MI.getOperand(7).getImm();
726e3b55780SDimitry Andric auto ImgFormat = static_cast<SPIRV::ImageFormat::ImageFormat>(ImgFormatOp);
727e3b55780SDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::ImageFormatOperand,
728e3b55780SDimitry Andric ImgFormat, ST);
729e3b55780SDimitry Andric
730e3b55780SDimitry Andric bool IsArrayed = MI.getOperand(4).getImm() == 1;
731e3b55780SDimitry Andric bool IsMultisampled = MI.getOperand(5).getImm() == 1;
732e3b55780SDimitry Andric bool NoSampler = MI.getOperand(6).getImm() == 2;
733e3b55780SDimitry Andric // Add dimension requirements.
734e3b55780SDimitry Andric assert(MI.getOperand(2).isImm());
735e3b55780SDimitry Andric switch (MI.getOperand(2).getImm()) {
736e3b55780SDimitry Andric case SPIRV::Dim::DIM_1D:
737e3b55780SDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::Image1D
738e3b55780SDimitry Andric : SPIRV::Capability::Sampled1D);
739e3b55780SDimitry Andric break;
740e3b55780SDimitry Andric case SPIRV::Dim::DIM_2D:
741e3b55780SDimitry Andric if (IsMultisampled && NoSampler)
742e3b55780SDimitry Andric Reqs.addRequirements(SPIRV::Capability::ImageMSArray);
743e3b55780SDimitry Andric break;
744e3b55780SDimitry Andric case SPIRV::Dim::DIM_Cube:
745e3b55780SDimitry Andric Reqs.addRequirements(SPIRV::Capability::Shader);
746e3b55780SDimitry Andric if (IsArrayed)
747e3b55780SDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageCubeArray
748e3b55780SDimitry Andric : SPIRV::Capability::SampledCubeArray);
749e3b55780SDimitry Andric break;
750e3b55780SDimitry Andric case SPIRV::Dim::DIM_Rect:
751e3b55780SDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageRect
752e3b55780SDimitry Andric : SPIRV::Capability::SampledRect);
753e3b55780SDimitry Andric break;
754e3b55780SDimitry Andric case SPIRV::Dim::DIM_Buffer:
755e3b55780SDimitry Andric Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageBuffer
756e3b55780SDimitry Andric : SPIRV::Capability::SampledBuffer);
757e3b55780SDimitry Andric break;
758e3b55780SDimitry Andric case SPIRV::Dim::DIM_SubpassData:
759e3b55780SDimitry Andric Reqs.addRequirements(SPIRV::Capability::InputAttachment);
760e3b55780SDimitry Andric break;
761e3b55780SDimitry Andric }
762e3b55780SDimitry Andric
763e3b55780SDimitry Andric // Has optional access qualifier.
764e3b55780SDimitry Andric // TODO: check if it's OpenCL's kernel.
765e3b55780SDimitry Andric if (MI.getNumOperands() > 8 &&
766e3b55780SDimitry Andric MI.getOperand(8).getImm() == SPIRV::AccessQualifier::ReadWrite)
767e3b55780SDimitry Andric Reqs.addRequirements(SPIRV::Capability::ImageReadWrite);
768e3b55780SDimitry Andric else
769e3b55780SDimitry Andric Reqs.addRequirements(SPIRV::Capability::ImageBasic);
770e3b55780SDimitry Andric }
771e3b55780SDimitry Andric
772ac9a064cSDimitry Andric // Add requirements for handling atomic float instructions
773ac9a064cSDimitry Andric #define ATOM_FLT_REQ_EXT_MSG(ExtName) \
774ac9a064cSDimitry Andric "The atomic float instruction requires the following SPIR-V " \
775ac9a064cSDimitry Andric "extension: SPV_EXT_shader_atomic_float" ExtName
AddAtomicFloatRequirements(const MachineInstr & MI,SPIRV::RequirementHandler & Reqs,const SPIRVSubtarget & ST)776ac9a064cSDimitry Andric static void AddAtomicFloatRequirements(const MachineInstr &MI,
777ac9a064cSDimitry Andric SPIRV::RequirementHandler &Reqs,
778ac9a064cSDimitry Andric const SPIRVSubtarget &ST) {
779ac9a064cSDimitry Andric assert(MI.getOperand(1).isReg() &&
780ac9a064cSDimitry Andric "Expect register operand in atomic float instruction");
781ac9a064cSDimitry Andric Register TypeReg = MI.getOperand(1).getReg();
782ac9a064cSDimitry Andric SPIRVType *TypeDef = MI.getMF()->getRegInfo().getVRegDef(TypeReg);
783ac9a064cSDimitry Andric if (TypeDef->getOpcode() != SPIRV::OpTypeFloat)
784ac9a064cSDimitry Andric report_fatal_error("Result type of an atomic float instruction must be a "
785ac9a064cSDimitry Andric "floating-point type scalar");
786ac9a064cSDimitry Andric
787ac9a064cSDimitry Andric unsigned BitWidth = TypeDef->getOperand(1).getImm();
788ac9a064cSDimitry Andric unsigned Op = MI.getOpcode();
789ac9a064cSDimitry Andric if (Op == SPIRV::OpAtomicFAddEXT) {
790ac9a064cSDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float_add))
791ac9a064cSDimitry Andric report_fatal_error(ATOM_FLT_REQ_EXT_MSG("_add"), false);
792ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float_add);
793ac9a064cSDimitry Andric switch (BitWidth) {
794ac9a064cSDimitry Andric case 16:
795ac9a064cSDimitry Andric if (!ST.canUseExtension(
796ac9a064cSDimitry Andric SPIRV::Extension::SPV_EXT_shader_atomic_float16_add))
797ac9a064cSDimitry Andric report_fatal_error(ATOM_FLT_REQ_EXT_MSG("16_add"), false);
798ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float16_add);
799ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat16AddEXT);
800ac9a064cSDimitry Andric break;
801ac9a064cSDimitry Andric case 32:
802ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat32AddEXT);
803ac9a064cSDimitry Andric break;
804ac9a064cSDimitry Andric case 64:
805ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat64AddEXT);
806ac9a064cSDimitry Andric break;
807ac9a064cSDimitry Andric default:
808ac9a064cSDimitry Andric report_fatal_error(
809ac9a064cSDimitry Andric "Unexpected floating-point type width in atomic float instruction");
810ac9a064cSDimitry Andric }
811ac9a064cSDimitry Andric } else {
812ac9a064cSDimitry Andric if (!ST.canUseExtension(
813ac9a064cSDimitry Andric SPIRV::Extension::SPV_EXT_shader_atomic_float_min_max))
814ac9a064cSDimitry Andric report_fatal_error(ATOM_FLT_REQ_EXT_MSG("_min_max"), false);
815ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_EXT_shader_atomic_float_min_max);
816ac9a064cSDimitry Andric switch (BitWidth) {
817ac9a064cSDimitry Andric case 16:
818ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat16MinMaxEXT);
819ac9a064cSDimitry Andric break;
820ac9a064cSDimitry Andric case 32:
821ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat32MinMaxEXT);
822ac9a064cSDimitry Andric break;
823ac9a064cSDimitry Andric case 64:
824ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::AtomicFloat64MinMaxEXT);
825ac9a064cSDimitry Andric break;
826ac9a064cSDimitry Andric default:
827ac9a064cSDimitry Andric report_fatal_error(
828ac9a064cSDimitry Andric "Unexpected floating-point type width in atomic float instruction");
829ac9a064cSDimitry Andric }
830ac9a064cSDimitry Andric }
831ac9a064cSDimitry Andric }
832ac9a064cSDimitry Andric
addInstrRequirements(const MachineInstr & MI,SPIRV::RequirementHandler & Reqs,const SPIRVSubtarget & ST)833e3b55780SDimitry Andric void addInstrRequirements(const MachineInstr &MI,
834e3b55780SDimitry Andric SPIRV::RequirementHandler &Reqs,
835e3b55780SDimitry Andric const SPIRVSubtarget &ST) {
836e3b55780SDimitry Andric switch (MI.getOpcode()) {
837e3b55780SDimitry Andric case SPIRV::OpMemoryModel: {
838e3b55780SDimitry Andric int64_t Addr = MI.getOperand(0).getImm();
839e3b55780SDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
840e3b55780SDimitry Andric Addr, ST);
841e3b55780SDimitry Andric int64_t Mem = MI.getOperand(1).getImm();
842e3b55780SDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand, Mem,
843e3b55780SDimitry Andric ST);
844e3b55780SDimitry Andric break;
845e3b55780SDimitry Andric }
846e3b55780SDimitry Andric case SPIRV::OpEntryPoint: {
847e3b55780SDimitry Andric int64_t Exe = MI.getOperand(0).getImm();
848e3b55780SDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModelOperand,
849e3b55780SDimitry Andric Exe, ST);
850e3b55780SDimitry Andric break;
851e3b55780SDimitry Andric }
852e3b55780SDimitry Andric case SPIRV::OpExecutionMode:
853e3b55780SDimitry Andric case SPIRV::OpExecutionModeId: {
854e3b55780SDimitry Andric int64_t Exe = MI.getOperand(1).getImm();
855e3b55780SDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModeOperand,
856e3b55780SDimitry Andric Exe, ST);
857e3b55780SDimitry Andric break;
858e3b55780SDimitry Andric }
859e3b55780SDimitry Andric case SPIRV::OpTypeMatrix:
860e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Matrix);
861e3b55780SDimitry Andric break;
862e3b55780SDimitry Andric case SPIRV::OpTypeInt: {
863e3b55780SDimitry Andric unsigned BitWidth = MI.getOperand(1).getImm();
864e3b55780SDimitry Andric if (BitWidth == 64)
865e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Int64);
866e3b55780SDimitry Andric else if (BitWidth == 16)
867e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Int16);
868e3b55780SDimitry Andric else if (BitWidth == 8)
869e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Int8);
870e3b55780SDimitry Andric break;
871e3b55780SDimitry Andric }
872e3b55780SDimitry Andric case SPIRV::OpTypeFloat: {
873e3b55780SDimitry Andric unsigned BitWidth = MI.getOperand(1).getImm();
874e3b55780SDimitry Andric if (BitWidth == 64)
875e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Float64);
876e3b55780SDimitry Andric else if (BitWidth == 16)
877e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Float16);
878e3b55780SDimitry Andric break;
879e3b55780SDimitry Andric }
880e3b55780SDimitry Andric case SPIRV::OpTypeVector: {
881e3b55780SDimitry Andric unsigned NumComponents = MI.getOperand(2).getImm();
882e3b55780SDimitry Andric if (NumComponents == 8 || NumComponents == 16)
883e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Vector16);
884e3b55780SDimitry Andric break;
885e3b55780SDimitry Andric }
886e3b55780SDimitry Andric case SPIRV::OpTypePointer: {
887e3b55780SDimitry Andric auto SC = MI.getOperand(1).getImm();
888e3b55780SDimitry Andric Reqs.getAndAddRequirements(SPIRV::OperandCategory::StorageClassOperand, SC,
889e3b55780SDimitry Andric ST);
8904df029ccSDimitry Andric // If it's a type of pointer to float16 targeting OpenCL, add Float16Buffer
8914df029ccSDimitry Andric // capability.
8924df029ccSDimitry Andric if (!ST.isOpenCLEnv())
8934df029ccSDimitry Andric break;
894e3b55780SDimitry Andric assert(MI.getOperand(2).isReg());
895e3b55780SDimitry Andric const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
896e3b55780SDimitry Andric SPIRVType *TypeDef = MRI.getVRegDef(MI.getOperand(2).getReg());
897e3b55780SDimitry Andric if (TypeDef->getOpcode() == SPIRV::OpTypeFloat &&
898e3b55780SDimitry Andric TypeDef->getOperand(1).getImm() == 16)
899e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Float16Buffer);
900e3b55780SDimitry Andric break;
901e3b55780SDimitry Andric }
902e3b55780SDimitry Andric case SPIRV::OpBitReverse:
903b1c73532SDimitry Andric case SPIRV::OpBitFieldInsert:
904b1c73532SDimitry Andric case SPIRV::OpBitFieldSExtract:
905b1c73532SDimitry Andric case SPIRV::OpBitFieldUExtract:
906b1c73532SDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_bit_instructions)) {
907b1c73532SDimitry Andric Reqs.addCapability(SPIRV::Capability::Shader);
908b1c73532SDimitry Andric break;
909b1c73532SDimitry Andric }
910b1c73532SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_bit_instructions);
911b1c73532SDimitry Andric Reqs.addCapability(SPIRV::Capability::BitInstructions);
912b1c73532SDimitry Andric break;
913e3b55780SDimitry Andric case SPIRV::OpTypeRuntimeArray:
914e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Shader);
915e3b55780SDimitry Andric break;
916e3b55780SDimitry Andric case SPIRV::OpTypeOpaque:
917e3b55780SDimitry Andric case SPIRV::OpTypeEvent:
918e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Kernel);
919e3b55780SDimitry Andric break;
920e3b55780SDimitry Andric case SPIRV::OpTypePipe:
921e3b55780SDimitry Andric case SPIRV::OpTypeReserveId:
922e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Pipes);
923e3b55780SDimitry Andric break;
924e3b55780SDimitry Andric case SPIRV::OpTypeDeviceEvent:
925e3b55780SDimitry Andric case SPIRV::OpTypeQueue:
926e3b55780SDimitry Andric case SPIRV::OpBuildNDRange:
927e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::DeviceEnqueue);
928e3b55780SDimitry Andric break;
929e3b55780SDimitry Andric case SPIRV::OpDecorate:
930e3b55780SDimitry Andric case SPIRV::OpDecorateId:
931e3b55780SDimitry Andric case SPIRV::OpDecorateString:
932e3b55780SDimitry Andric addOpDecorateReqs(MI, 1, Reqs, ST);
933e3b55780SDimitry Andric break;
934e3b55780SDimitry Andric case SPIRV::OpMemberDecorate:
935e3b55780SDimitry Andric case SPIRV::OpMemberDecorateString:
936e3b55780SDimitry Andric addOpDecorateReqs(MI, 2, Reqs, ST);
937e3b55780SDimitry Andric break;
938e3b55780SDimitry Andric case SPIRV::OpInBoundsPtrAccessChain:
939e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Addresses);
940e3b55780SDimitry Andric break;
941e3b55780SDimitry Andric case SPIRV::OpConstantSampler:
942e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::LiteralSampler);
943e3b55780SDimitry Andric break;
944e3b55780SDimitry Andric case SPIRV::OpTypeImage:
945e3b55780SDimitry Andric addOpTypeImageReqs(MI, Reqs, ST);
946e3b55780SDimitry Andric break;
947e3b55780SDimitry Andric case SPIRV::OpTypeSampler:
948e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::ImageBasic);
949e3b55780SDimitry Andric break;
950e3b55780SDimitry Andric case SPIRV::OpTypeForwardPointer:
951e3b55780SDimitry Andric // TODO: check if it's OpenCL's kernel.
952e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Addresses);
953e3b55780SDimitry Andric break;
954e3b55780SDimitry Andric case SPIRV::OpAtomicFlagTestAndSet:
955e3b55780SDimitry Andric case SPIRV::OpAtomicLoad:
956e3b55780SDimitry Andric case SPIRV::OpAtomicStore:
957e3b55780SDimitry Andric case SPIRV::OpAtomicExchange:
958e3b55780SDimitry Andric case SPIRV::OpAtomicCompareExchange:
959e3b55780SDimitry Andric case SPIRV::OpAtomicIIncrement:
960e3b55780SDimitry Andric case SPIRV::OpAtomicIDecrement:
961e3b55780SDimitry Andric case SPIRV::OpAtomicIAdd:
962e3b55780SDimitry Andric case SPIRV::OpAtomicISub:
963e3b55780SDimitry Andric case SPIRV::OpAtomicUMin:
964e3b55780SDimitry Andric case SPIRV::OpAtomicUMax:
965e3b55780SDimitry Andric case SPIRV::OpAtomicSMin:
966e3b55780SDimitry Andric case SPIRV::OpAtomicSMax:
967e3b55780SDimitry Andric case SPIRV::OpAtomicAnd:
968e3b55780SDimitry Andric case SPIRV::OpAtomicOr:
969e3b55780SDimitry Andric case SPIRV::OpAtomicXor: {
970e3b55780SDimitry Andric const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
971e3b55780SDimitry Andric const MachineInstr *InstrPtr = &MI;
972e3b55780SDimitry Andric if (MI.getOpcode() == SPIRV::OpAtomicStore) {
973e3b55780SDimitry Andric assert(MI.getOperand(3).isReg());
974e3b55780SDimitry Andric InstrPtr = MRI.getVRegDef(MI.getOperand(3).getReg());
975e3b55780SDimitry Andric assert(InstrPtr && "Unexpected type instruction for OpAtomicStore");
976e3b55780SDimitry Andric }
977e3b55780SDimitry Andric assert(InstrPtr->getOperand(1).isReg() && "Unexpected operand in atomic");
978e3b55780SDimitry Andric Register TypeReg = InstrPtr->getOperand(1).getReg();
979e3b55780SDimitry Andric SPIRVType *TypeDef = MRI.getVRegDef(TypeReg);
980e3b55780SDimitry Andric if (TypeDef->getOpcode() == SPIRV::OpTypeInt) {
981e3b55780SDimitry Andric unsigned BitWidth = TypeDef->getOperand(1).getImm();
982e3b55780SDimitry Andric if (BitWidth == 64)
983e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Int64Atomics);
984e3b55780SDimitry Andric }
985e3b55780SDimitry Andric break;
986e3b55780SDimitry Andric }
987e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformIAdd:
988e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformFAdd:
989e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformIMul:
990e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformFMul:
991e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformSMin:
992e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformUMin:
993e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformFMin:
994e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformSMax:
995e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformUMax:
996e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformFMax:
997e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBitwiseAnd:
998e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBitwiseOr:
999e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBitwiseXor:
1000e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformLogicalAnd:
1001e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformLogicalOr:
1002e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformLogicalXor: {
1003e3b55780SDimitry Andric assert(MI.getOperand(3).isImm());
1004e3b55780SDimitry Andric int64_t GroupOp = MI.getOperand(3).getImm();
1005e3b55780SDimitry Andric switch (GroupOp) {
1006e3b55780SDimitry Andric case SPIRV::GroupOperation::Reduce:
1007e3b55780SDimitry Andric case SPIRV::GroupOperation::InclusiveScan:
1008e3b55780SDimitry Andric case SPIRV::GroupOperation::ExclusiveScan:
1009e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Kernel);
1010e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformArithmetic);
1011e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot);
1012e3b55780SDimitry Andric break;
1013e3b55780SDimitry Andric case SPIRV::GroupOperation::ClusteredReduce:
1014e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformClustered);
1015e3b55780SDimitry Andric break;
1016e3b55780SDimitry Andric case SPIRV::GroupOperation::PartitionedReduceNV:
1017e3b55780SDimitry Andric case SPIRV::GroupOperation::PartitionedInclusiveScanNV:
1018e3b55780SDimitry Andric case SPIRV::GroupOperation::PartitionedExclusiveScanNV:
1019e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformPartitionedNV);
1020e3b55780SDimitry Andric break;
1021e3b55780SDimitry Andric }
1022e3b55780SDimitry Andric break;
1023e3b55780SDimitry Andric }
1024e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformShuffle:
1025e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformShuffleXor:
1026e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffle);
1027e3b55780SDimitry Andric break;
1028e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformShuffleUp:
1029e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformShuffleDown:
1030e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffleRelative);
1031e3b55780SDimitry Andric break;
1032e3b55780SDimitry Andric case SPIRV::OpGroupAll:
1033e3b55780SDimitry Andric case SPIRV::OpGroupAny:
1034e3b55780SDimitry Andric case SPIRV::OpGroupBroadcast:
1035e3b55780SDimitry Andric case SPIRV::OpGroupIAdd:
1036e3b55780SDimitry Andric case SPIRV::OpGroupFAdd:
1037e3b55780SDimitry Andric case SPIRV::OpGroupFMin:
1038e3b55780SDimitry Andric case SPIRV::OpGroupUMin:
1039e3b55780SDimitry Andric case SPIRV::OpGroupSMin:
1040e3b55780SDimitry Andric case SPIRV::OpGroupFMax:
1041e3b55780SDimitry Andric case SPIRV::OpGroupUMax:
1042e3b55780SDimitry Andric case SPIRV::OpGroupSMax:
1043e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::Groups);
1044e3b55780SDimitry Andric break;
1045e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformElect:
1046e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniform);
1047e3b55780SDimitry Andric break;
1048e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformAll:
1049e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformAny:
1050e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformAllEqual:
1051e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformVote);
1052e3b55780SDimitry Andric break;
1053e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBroadcast:
1054e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBroadcastFirst:
1055e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBallot:
1056e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformInverseBallot:
1057e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBallotBitExtract:
1058e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBallotBitCount:
1059e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBallotFindLSB:
1060e3b55780SDimitry Andric case SPIRV::OpGroupNonUniformBallotFindMSB:
1061e3b55780SDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot);
1062e3b55780SDimitry Andric break;
1063ac9a064cSDimitry Andric case SPIRV::OpSubgroupShuffleINTEL:
1064ac9a064cSDimitry Andric case SPIRV::OpSubgroupShuffleDownINTEL:
1065ac9a064cSDimitry Andric case SPIRV::OpSubgroupShuffleUpINTEL:
1066ac9a064cSDimitry Andric case SPIRV::OpSubgroupShuffleXorINTEL:
1067ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1068ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_subgroups);
1069ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::SubgroupShuffleINTEL);
1070ac9a064cSDimitry Andric }
1071ac9a064cSDimitry Andric break;
1072ac9a064cSDimitry Andric case SPIRV::OpSubgroupBlockReadINTEL:
1073ac9a064cSDimitry Andric case SPIRV::OpSubgroupBlockWriteINTEL:
1074ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1075ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_subgroups);
1076ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::SubgroupBufferBlockIOINTEL);
1077ac9a064cSDimitry Andric }
1078ac9a064cSDimitry Andric break;
1079ac9a064cSDimitry Andric case SPIRV::OpSubgroupImageBlockReadINTEL:
1080ac9a064cSDimitry Andric case SPIRV::OpSubgroupImageBlockWriteINTEL:
1081ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_subgroups)) {
1082ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_subgroups);
1083ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::SubgroupImageBlockIOINTEL);
1084ac9a064cSDimitry Andric }
1085ac9a064cSDimitry Andric break;
1086b1c73532SDimitry Andric case SPIRV::OpAssumeTrueKHR:
1087b1c73532SDimitry Andric case SPIRV::OpExpectKHR:
1088b1c73532SDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_KHR_expect_assume)) {
1089b1c73532SDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_expect_assume);
1090b1c73532SDimitry Andric Reqs.addCapability(SPIRV::Capability::ExpectAssumeKHR);
1091b1c73532SDimitry Andric }
1092b1c73532SDimitry Andric break;
1093ac9a064cSDimitry Andric case SPIRV::OpPtrCastToCrossWorkgroupINTEL:
1094ac9a064cSDimitry Andric case SPIRV::OpCrossWorkgroupCastToPtrINTEL:
1095ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_usm_storage_classes)) {
1096ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_usm_storage_classes);
1097ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::USMStorageClassesINTEL);
1098ac9a064cSDimitry Andric }
1099ac9a064cSDimitry Andric break;
1100ac9a064cSDimitry Andric case SPIRV::OpConstantFunctionPointerINTEL:
1101ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)) {
1102ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
1103ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::FunctionPointersINTEL);
1104ac9a064cSDimitry Andric }
1105ac9a064cSDimitry Andric break;
1106ac9a064cSDimitry Andric case SPIRV::OpGroupNonUniformRotateKHR:
1107ac9a064cSDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_subgroup_rotate))
1108ac9a064cSDimitry Andric report_fatal_error("OpGroupNonUniformRotateKHR instruction requires the "
1109ac9a064cSDimitry Andric "following SPIR-V extension: SPV_KHR_subgroup_rotate",
1110ac9a064cSDimitry Andric false);
1111ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_subgroup_rotate);
1112ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniformRotateKHR);
1113ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupNonUniform);
1114ac9a064cSDimitry Andric break;
1115ac9a064cSDimitry Andric case SPIRV::OpGroupIMulKHR:
1116ac9a064cSDimitry Andric case SPIRV::OpGroupFMulKHR:
1117ac9a064cSDimitry Andric case SPIRV::OpGroupBitwiseAndKHR:
1118ac9a064cSDimitry Andric case SPIRV::OpGroupBitwiseOrKHR:
1119ac9a064cSDimitry Andric case SPIRV::OpGroupBitwiseXorKHR:
1120ac9a064cSDimitry Andric case SPIRV::OpGroupLogicalAndKHR:
1121ac9a064cSDimitry Andric case SPIRV::OpGroupLogicalOrKHR:
1122ac9a064cSDimitry Andric case SPIRV::OpGroupLogicalXorKHR:
1123ac9a064cSDimitry Andric if (ST.canUseExtension(
1124ac9a064cSDimitry Andric SPIRV::Extension::SPV_KHR_uniform_group_instructions)) {
1125ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_uniform_group_instructions);
1126ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::GroupUniformArithmeticKHR);
1127ac9a064cSDimitry Andric }
1128ac9a064cSDimitry Andric break;
1129ac9a064cSDimitry Andric case SPIRV::OpReadClockKHR:
1130ac9a064cSDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_shader_clock))
1131ac9a064cSDimitry Andric report_fatal_error("OpReadClockKHR instruction requires the "
1132ac9a064cSDimitry Andric "following SPIR-V extension: SPV_KHR_shader_clock",
1133ac9a064cSDimitry Andric false);
1134ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_shader_clock);
1135ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::ShaderClockKHR);
1136ac9a064cSDimitry Andric break;
1137ac9a064cSDimitry Andric case SPIRV::OpFunctionPointerCallINTEL:
1138ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)) {
1139ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
1140ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::FunctionPointersINTEL);
1141ac9a064cSDimitry Andric }
1142ac9a064cSDimitry Andric break;
1143ac9a064cSDimitry Andric case SPIRV::OpAtomicFAddEXT:
1144ac9a064cSDimitry Andric case SPIRV::OpAtomicFMinEXT:
1145ac9a064cSDimitry Andric case SPIRV::OpAtomicFMaxEXT:
1146ac9a064cSDimitry Andric AddAtomicFloatRequirements(MI, Reqs, ST);
1147ac9a064cSDimitry Andric break;
1148ac9a064cSDimitry Andric case SPIRV::OpConvertBF16ToFINTEL:
1149ac9a064cSDimitry Andric case SPIRV::OpConvertFToBF16INTEL:
1150ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_bfloat16_conversion)) {
1151ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_bfloat16_conversion);
1152ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::BFloat16ConversionINTEL);
1153ac9a064cSDimitry Andric }
1154ac9a064cSDimitry Andric break;
1155ac9a064cSDimitry Andric case SPIRV::OpVariableLengthArrayINTEL:
1156ac9a064cSDimitry Andric case SPIRV::OpSaveMemoryINTEL:
1157ac9a064cSDimitry Andric case SPIRV::OpRestoreMemoryINTEL:
1158ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_variable_length_array)) {
1159ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_variable_length_array);
1160ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::VariableLengthArrayINTEL);
1161ac9a064cSDimitry Andric }
1162ac9a064cSDimitry Andric break;
1163ac9a064cSDimitry Andric case SPIRV::OpAsmTargetINTEL:
1164ac9a064cSDimitry Andric case SPIRV::OpAsmINTEL:
1165ac9a064cSDimitry Andric case SPIRV::OpAsmCallINTEL:
1166ac9a064cSDimitry Andric if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_inline_assembly)) {
1167ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_INTEL_inline_assembly);
1168ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::AsmINTEL);
1169ac9a064cSDimitry Andric }
1170ac9a064cSDimitry Andric break;
1171ac9a064cSDimitry Andric case SPIRV::OpTypeCooperativeMatrixKHR:
1172ac9a064cSDimitry Andric if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_cooperative_matrix))
1173ac9a064cSDimitry Andric report_fatal_error(
1174ac9a064cSDimitry Andric "OpTypeCooperativeMatrixKHR type requires the "
1175ac9a064cSDimitry Andric "following SPIR-V extension: SPV_KHR_cooperative_matrix",
1176ac9a064cSDimitry Andric false);
1177ac9a064cSDimitry Andric Reqs.addExtension(SPIRV::Extension::SPV_KHR_cooperative_matrix);
1178ac9a064cSDimitry Andric Reqs.addCapability(SPIRV::Capability::CooperativeMatrixKHR);
1179ac9a064cSDimitry Andric break;
1180e3b55780SDimitry Andric default:
1181e3b55780SDimitry Andric break;
1182e3b55780SDimitry Andric }
1183b1c73532SDimitry Andric
1184b1c73532SDimitry Andric // If we require capability Shader, then we can remove the requirement for
1185b1c73532SDimitry Andric // the BitInstructions capability, since Shader is a superset capability
1186b1c73532SDimitry Andric // of BitInstructions.
1187b1c73532SDimitry Andric Reqs.removeCapabilityIf(SPIRV::Capability::BitInstructions,
1188b1c73532SDimitry Andric SPIRV::Capability::Shader);
1189e3b55780SDimitry Andric }
1190e3b55780SDimitry Andric
collectReqs(const Module & M,SPIRV::ModuleAnalysisInfo & MAI,MachineModuleInfo * MMI,const SPIRVSubtarget & ST)1191e3b55780SDimitry Andric static void collectReqs(const Module &M, SPIRV::ModuleAnalysisInfo &MAI,
1192e3b55780SDimitry Andric MachineModuleInfo *MMI, const SPIRVSubtarget &ST) {
1193e3b55780SDimitry Andric // Collect requirements for existing instructions.
1194e3b55780SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) {
1195e3b55780SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F);
1196e3b55780SDimitry Andric if (!MF)
1197e3b55780SDimitry Andric continue;
1198e3b55780SDimitry Andric for (const MachineBasicBlock &MBB : *MF)
1199e3b55780SDimitry Andric for (const MachineInstr &MI : MBB)
1200e3b55780SDimitry Andric addInstrRequirements(MI, MAI.Reqs, ST);
1201e3b55780SDimitry Andric }
1202e3b55780SDimitry Andric // Collect requirements for OpExecutionMode instructions.
1203e3b55780SDimitry Andric auto Node = M.getNamedMetadata("spirv.ExecutionMode");
1204e3b55780SDimitry Andric if (Node) {
1205ac9a064cSDimitry Andric // SPV_KHR_float_controls is not available until v1.4
1206ac9a064cSDimitry Andric bool RequireFloatControls = false,
1207ac9a064cSDimitry Andric VerLower14 = !ST.isAtLeastSPIRVVer(VersionTuple(1, 4));
1208e3b55780SDimitry Andric for (unsigned i = 0; i < Node->getNumOperands(); i++) {
1209e3b55780SDimitry Andric MDNode *MDN = cast<MDNode>(Node->getOperand(i));
1210e3b55780SDimitry Andric const MDOperand &MDOp = MDN->getOperand(1);
1211e3b55780SDimitry Andric if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) {
1212e3b55780SDimitry Andric Constant *C = CMeta->getValue();
1213e3b55780SDimitry Andric if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) {
1214e3b55780SDimitry Andric auto EM = Const->getZExtValue();
1215e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(
1216e3b55780SDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand, EM, ST);
1217ac9a064cSDimitry Andric // add SPV_KHR_float_controls if the version is too low
1218ac9a064cSDimitry Andric switch (EM) {
1219ac9a064cSDimitry Andric case SPIRV::ExecutionMode::DenormPreserve:
1220ac9a064cSDimitry Andric case SPIRV::ExecutionMode::DenormFlushToZero:
1221ac9a064cSDimitry Andric case SPIRV::ExecutionMode::SignedZeroInfNanPreserve:
1222ac9a064cSDimitry Andric case SPIRV::ExecutionMode::RoundingModeRTE:
1223ac9a064cSDimitry Andric case SPIRV::ExecutionMode::RoundingModeRTZ:
1224ac9a064cSDimitry Andric RequireFloatControls = VerLower14;
1225ac9a064cSDimitry Andric break;
1226e3b55780SDimitry Andric }
1227e3b55780SDimitry Andric }
1228e3b55780SDimitry Andric }
1229e3b55780SDimitry Andric }
1230ac9a064cSDimitry Andric if (RequireFloatControls &&
1231ac9a064cSDimitry Andric ST.canUseExtension(SPIRV::Extension::SPV_KHR_float_controls))
1232ac9a064cSDimitry Andric MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_float_controls);
1233ac9a064cSDimitry Andric }
1234e3b55780SDimitry Andric for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) {
1235e3b55780SDimitry Andric const Function &F = *FI;
1236e3b55780SDimitry Andric if (F.isDeclaration())
1237e3b55780SDimitry Andric continue;
1238e3b55780SDimitry Andric if (F.getMetadata("reqd_work_group_size"))
1239e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(
1240e3b55780SDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand,
1241e3b55780SDimitry Andric SPIRV::ExecutionMode::LocalSize, ST);
1242b1c73532SDimitry Andric if (F.getFnAttribute("hlsl.numthreads").isValid()) {
1243b1c73532SDimitry Andric MAI.Reqs.getAndAddRequirements(
1244b1c73532SDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand,
1245b1c73532SDimitry Andric SPIRV::ExecutionMode::LocalSize, ST);
1246b1c73532SDimitry Andric }
1247e3b55780SDimitry Andric if (F.getMetadata("work_group_size_hint"))
1248e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(
1249e3b55780SDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand,
1250e3b55780SDimitry Andric SPIRV::ExecutionMode::LocalSizeHint, ST);
1251e3b55780SDimitry Andric if (F.getMetadata("intel_reqd_sub_group_size"))
1252e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(
1253e3b55780SDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand,
1254e3b55780SDimitry Andric SPIRV::ExecutionMode::SubgroupSize, ST);
1255e3b55780SDimitry Andric if (F.getMetadata("vec_type_hint"))
1256e3b55780SDimitry Andric MAI.Reqs.getAndAddRequirements(
1257e3b55780SDimitry Andric SPIRV::OperandCategory::ExecutionModeOperand,
1258e3b55780SDimitry Andric SPIRV::ExecutionMode::VecTypeHint, ST);
1259b1c73532SDimitry Andric
1260b1c73532SDimitry Andric if (F.hasOptNone() &&
1261b1c73532SDimitry Andric ST.canUseExtension(SPIRV::Extension::SPV_INTEL_optnone)) {
1262b1c73532SDimitry Andric // Output OpCapability OptNoneINTEL.
1263b1c73532SDimitry Andric MAI.Reqs.addExtension(SPIRV::Extension::SPV_INTEL_optnone);
1264b1c73532SDimitry Andric MAI.Reqs.addCapability(SPIRV::Capability::OptNoneINTEL);
1265b1c73532SDimitry Andric }
1266e3b55780SDimitry Andric }
1267e3b55780SDimitry Andric }
1268e3b55780SDimitry Andric
getFastMathFlags(const MachineInstr & I)1269e3b55780SDimitry Andric static unsigned getFastMathFlags(const MachineInstr &I) {
1270e3b55780SDimitry Andric unsigned Flags = SPIRV::FPFastMathMode::None;
1271e3b55780SDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmNoNans))
1272e3b55780SDimitry Andric Flags |= SPIRV::FPFastMathMode::NotNaN;
1273e3b55780SDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmNoInfs))
1274e3b55780SDimitry Andric Flags |= SPIRV::FPFastMathMode::NotInf;
1275e3b55780SDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmNsz))
1276e3b55780SDimitry Andric Flags |= SPIRV::FPFastMathMode::NSZ;
1277e3b55780SDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmArcp))
1278e3b55780SDimitry Andric Flags |= SPIRV::FPFastMathMode::AllowRecip;
1279e3b55780SDimitry Andric if (I.getFlag(MachineInstr::MIFlag::FmReassoc))
1280e3b55780SDimitry Andric Flags |= SPIRV::FPFastMathMode::Fast;
1281e3b55780SDimitry Andric return Flags;
1282e3b55780SDimitry Andric }
1283e3b55780SDimitry Andric
handleMIFlagDecoration(MachineInstr & I,const SPIRVSubtarget & ST,const SPIRVInstrInfo & TII,SPIRV::RequirementHandler & Reqs)1284e3b55780SDimitry Andric static void handleMIFlagDecoration(MachineInstr &I, const SPIRVSubtarget &ST,
1285e3b55780SDimitry Andric const SPIRVInstrInfo &TII,
1286e3b55780SDimitry Andric SPIRV::RequirementHandler &Reqs) {
1287e3b55780SDimitry Andric if (I.getFlag(MachineInstr::MIFlag::NoSWrap) && TII.canUseNSW(I) &&
1288e3b55780SDimitry Andric getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand,
1289e3b55780SDimitry Andric SPIRV::Decoration::NoSignedWrap, ST, Reqs)
1290e3b55780SDimitry Andric .IsSatisfiable) {
1291e3b55780SDimitry Andric buildOpDecorate(I.getOperand(0).getReg(), I, TII,
1292e3b55780SDimitry Andric SPIRV::Decoration::NoSignedWrap, {});
1293e3b55780SDimitry Andric }
1294e3b55780SDimitry Andric if (I.getFlag(MachineInstr::MIFlag::NoUWrap) && TII.canUseNUW(I) &&
1295e3b55780SDimitry Andric getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand,
1296e3b55780SDimitry Andric SPIRV::Decoration::NoUnsignedWrap, ST,
1297e3b55780SDimitry Andric Reqs)
1298e3b55780SDimitry Andric .IsSatisfiable) {
1299e3b55780SDimitry Andric buildOpDecorate(I.getOperand(0).getReg(), I, TII,
1300e3b55780SDimitry Andric SPIRV::Decoration::NoUnsignedWrap, {});
1301e3b55780SDimitry Andric }
1302e3b55780SDimitry Andric if (!TII.canUseFastMathFlags(I))
1303e3b55780SDimitry Andric return;
1304e3b55780SDimitry Andric unsigned FMFlags = getFastMathFlags(I);
1305e3b55780SDimitry Andric if (FMFlags == SPIRV::FPFastMathMode::None)
1306e3b55780SDimitry Andric return;
1307e3b55780SDimitry Andric Register DstReg = I.getOperand(0).getReg();
1308e3b55780SDimitry Andric buildOpDecorate(DstReg, I, TII, SPIRV::Decoration::FPFastMathMode, {FMFlags});
1309e3b55780SDimitry Andric }
1310e3b55780SDimitry Andric
1311e3b55780SDimitry 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)1312e3b55780SDimitry Andric static void addDecorations(const Module &M, const SPIRVInstrInfo &TII,
1313e3b55780SDimitry Andric MachineModuleInfo *MMI, const SPIRVSubtarget &ST,
1314e3b55780SDimitry Andric SPIRV::ModuleAnalysisInfo &MAI) {
1315e3b55780SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) {
1316e3b55780SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F);
1317e3b55780SDimitry Andric if (!MF)
1318e3b55780SDimitry Andric continue;
1319e3b55780SDimitry Andric for (auto &MBB : *MF)
1320e3b55780SDimitry Andric for (auto &MI : MBB)
1321e3b55780SDimitry Andric handleMIFlagDecoration(MI, ST, TII, MAI.Reqs);
1322e3b55780SDimitry Andric }
1323e3b55780SDimitry Andric }
1324e3b55780SDimitry Andric
1325145449b1SDimitry Andric struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI;
1326145449b1SDimitry Andric
getAnalysisUsage(AnalysisUsage & AU) const1327145449b1SDimitry Andric void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
1328145449b1SDimitry Andric AU.addRequired<TargetPassConfig>();
1329145449b1SDimitry Andric AU.addRequired<MachineModuleInfoWrapperPass>();
1330145449b1SDimitry Andric }
1331145449b1SDimitry Andric
runOnModule(Module & M)1332145449b1SDimitry Andric bool SPIRVModuleAnalysis::runOnModule(Module &M) {
1333145449b1SDimitry Andric SPIRVTargetMachine &TM =
1334145449b1SDimitry Andric getAnalysis<TargetPassConfig>().getTM<SPIRVTargetMachine>();
1335145449b1SDimitry Andric ST = TM.getSubtargetImpl();
1336145449b1SDimitry Andric GR = ST->getSPIRVGlobalRegistry();
1337145449b1SDimitry Andric TII = ST->getInstrInfo();
1338145449b1SDimitry Andric
1339145449b1SDimitry Andric MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
1340145449b1SDimitry Andric
1341145449b1SDimitry Andric setBaseInfo(M);
1342145449b1SDimitry Andric
1343e3b55780SDimitry Andric addDecorations(M, *TII, MMI, *ST, MAI);
1344e3b55780SDimitry Andric
1345e3b55780SDimitry Andric collectReqs(M, MAI, MMI, *ST);
1346e3b55780SDimitry Andric
13474b4fe385SDimitry Andric // Process type/const/global var/func decl instructions, number their
1348145449b1SDimitry Andric // destination registers from 0 to N, collect Extensions and Capabilities.
13491f917f69SDimitry Andric processDefInstrs(M);
1350145449b1SDimitry Andric
1351145449b1SDimitry Andric // Number rest of registers from N+1 onwards.
1352145449b1SDimitry Andric numberRegistersGlobally(M);
1353145449b1SDimitry Andric
1354ac9a064cSDimitry Andric // Update references to OpFunction instructions to use Global Registers
1355ac9a064cSDimitry Andric if (GR->hasConstFunPtr())
1356ac9a064cSDimitry Andric collectFuncPtrs();
1357ac9a064cSDimitry Andric
1358145449b1SDimitry Andric // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions.
1359145449b1SDimitry Andric processOtherInstrs(M);
1360145449b1SDimitry Andric
1361e3b55780SDimitry Andric // If there are no entry points, we need the Linkage capability.
1362e3b55780SDimitry Andric if (MAI.MS[SPIRV::MB_EntryPoints].empty())
1363e3b55780SDimitry Andric MAI.Reqs.addCapability(SPIRV::Capability::Linkage);
1364e3b55780SDimitry Andric
1365ac9a064cSDimitry Andric // Set maximum ID used.
1366ac9a064cSDimitry Andric GR->setBound(MAI.MaxID);
1367ac9a064cSDimitry Andric
1368145449b1SDimitry Andric return false;
1369145449b1SDimitry Andric }
1370