xref: /llvm-project/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h (revision 83c1d003118a2cb8136fe49e2ec43958c93d9d6b)
1 //===- SPIRVModuleAnalysis.h - analysis of global instrs & regs -*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // The analysis collects instructions that should be output at the module level
10 // and performs the global register numbering.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
15 #define LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
16 
17 #include "MCTargetDesc/SPIRVBaseInfo.h"
18 #include "SPIRVGlobalRegistry.h"
19 #include "SPIRVUtils.h"
20 #include "llvm/ADT/DenseMap.h"
21 #include "llvm/ADT/SmallSet.h"
22 #include "llvm/ADT/SmallVector.h"
23 
24 namespace llvm {
25 class SPIRVSubtarget;
26 class MachineFunction;
27 class MachineModuleInfo;
28 
29 namespace SPIRV {
30 // The enum contains logical module sections for the instruction collection.
31 enum ModuleSectionType {
32   //  MB_Capabilities, MB_Extensions, MB_ExtInstImports, MB_MemoryModel,
33   MB_EntryPoints, // All OpEntryPoint instructions (if any).
34   //  MB_ExecutionModes, MB_DebugSourceAndStrings,
35   MB_DebugNames,           // All OpName and OpMemberName intrs.
36   MB_DebugStrings,         // All OpString intrs.
37   MB_DebugModuleProcessed, // All OpModuleProcessed instructions.
38   MB_Annotations,          // OpDecorate, OpMemberDecorate etc.
39   MB_TypeConstVars,        // OpTypeXXX, OpConstantXXX, and global OpVariables.
40   MB_NonSemanticGlobalDI,  // OpExtInst with e.g. DebugSource, DebugTypeBasic.
41   MB_ExtFuncDecls,         // OpFunction etc. to declare for external funcs.
42   NUM_MODULE_SECTIONS      // Total number of sections requiring basic blocks.
43 };
44 
45 struct Requirements {
46   const bool IsSatisfiable;
47   const std::optional<Capability::Capability> Cap;
48   const ExtensionList Exts;
49   const VersionTuple MinVer; // 0 if no min version is required.
50   const VersionTuple MaxVer; // 0 if no max version is required.
51 
52   Requirements(bool IsSatisfiable = false,
53                std::optional<Capability::Capability> Cap = {},
54                ExtensionList Exts = {}, VersionTuple MinVer = VersionTuple(),
55                VersionTuple MaxVer = VersionTuple())
56       : IsSatisfiable(IsSatisfiable), Cap(Cap), Exts(Exts), MinVer(MinVer),
57         MaxVer(MaxVer) {}
58   Requirements(Capability::Capability Cap) : Requirements(true, {Cap}) {}
59 };
60 
61 struct RequirementHandler {
62 private:
63   CapabilityList MinimalCaps;
64 
65   // AllCaps and AvailableCaps are related but different. AllCaps is a subset of
66   // AvailableCaps. AvailableCaps is the complete set of capabilities that are
67   // available to the current target. AllCaps is the set of capabilities that
68   // are required by the current module.
69   SmallSet<Capability::Capability, 8> AllCaps;
70   DenseSet<unsigned> AvailableCaps;
71 
72   SmallSet<Extension::Extension, 4> AllExtensions;
73   VersionTuple MinVersion; // 0 if no min version is defined.
74   VersionTuple MaxVersion; // 0 if no max version is defined.
75   // Add capabilities to AllCaps, recursing through their implicitly declared
76   // capabilities too.
77   void recursiveAddCapabilities(const CapabilityList &ToPrune);
78 
79   void initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget &ST);
80   void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST);
81 
82 public:
83   RequirementHandler() {}
84   void clear() {
85     MinimalCaps.clear();
86     AllCaps.clear();
87     AvailableCaps.clear();
88     AllExtensions.clear();
89     MinVersion = VersionTuple();
90     MaxVersion = VersionTuple();
91   }
92   const CapabilityList &getMinimalCapabilities() const { return MinimalCaps; }
93   const SmallSet<Extension::Extension, 4> &getExtensions() const {
94     return AllExtensions;
95   }
96   // Add a list of capabilities, ensuring AllCaps captures all the implicitly
97   // declared capabilities, and MinimalCaps has the minimal set of required
98   // capabilities (so all implicitly declared ones are removed).
99   void addCapabilities(const CapabilityList &ToAdd);
100   void addCapability(Capability::Capability ToAdd) { addCapabilities({ToAdd}); }
101   void addExtensions(const ExtensionList &ToAdd) {
102     AllExtensions.insert(ToAdd.begin(), ToAdd.end());
103   }
104   void addExtension(Extension::Extension ToAdd) { AllExtensions.insert(ToAdd); }
105   // Add the given requirements to the lists. If constraints conflict, or these
106   // requirements cannot be satisfied, then abort the compilation.
107   void addRequirements(const Requirements &Req);
108   // Get requirement and add it to the list.
109   void getAndAddRequirements(SPIRV::OperandCategory::OperandCategory Category,
110                              uint32_t i, const SPIRVSubtarget &ST);
111   // Check if all the requirements can be satisfied for the given subtarget, and
112   // if not abort compilation.
113   void checkSatisfiable(const SPIRVSubtarget &ST) const;
114   void initAvailableCapabilities(const SPIRVSubtarget &ST);
115   // Add the given capabilities to available and all their implicitly defined
116   // capabilities too.
117   void addAvailableCaps(const CapabilityList &ToAdd);
118   bool isCapabilityAvailable(Capability::Capability Cap) const {
119     return AvailableCaps.contains(Cap);
120   }
121 
122   // Remove capability ToRemove, but only if IfPresent is present.
123   void removeCapabilityIf(const Capability::Capability ToRemove,
124                           const Capability::Capability IfPresent);
125 };
126 
127 using InstrList = SmallVector<const MachineInstr *>;
128 // Maps a local register to the corresponding global alias.
129 using LocalToGlobalRegTable = std::map<Register, Register>;
130 using RegisterAliasMapTy =
131     std::map<const MachineFunction *, LocalToGlobalRegTable>;
132 
133 // The struct contains results of the module analysis and methods
134 // to access them.
135 struct ModuleAnalysisInfo {
136   RequirementHandler Reqs;
137   MemoryModel::MemoryModel Mem;
138   AddressingModel::AddressingModel Addr;
139   SourceLanguage::SourceLanguage SrcLang;
140   unsigned SrcLangVersion;
141   StringSet<> SrcExt;
142   // Maps ExtInstSet to corresponding ID register.
143   DenseMap<unsigned, Register> ExtInstSetMap;
144   // Contains the list of all global OpVariables in the module.
145   SmallVector<const MachineInstr *, 4> GlobalVarList;
146   // Maps functions to corresponding function ID registers.
147   DenseMap<const Function *, Register> FuncMap;
148   // The set contains machine instructions which are necessary
149   // for correct MIR but will not be emitted in function bodies.
150   DenseSet<const MachineInstr *> InstrsToDelete;
151   // The table contains global aliases of local registers for each machine
152   // function. The aliases are used to substitute local registers during
153   // code emission.
154   RegisterAliasMapTy RegisterAliasTable;
155   // The counter holds the maximum ID we have in the module.
156   unsigned MaxID;
157   // The array contains lists of MIs for each module section.
158   InstrList MS[NUM_MODULE_SECTIONS];
159   // The table maps MBB number to SPIR-V unique ID register.
160   DenseMap<std::pair<const MachineFunction *, int>, Register> BBNumToRegMap;
161 
162   Register getFuncReg(const Function *F) {
163     assert(F && "Function is null");
164     auto FuncPtrRegPair = FuncMap.find(F);
165     return FuncPtrRegPair == FuncMap.end() ? Register(0)
166                                            : FuncPtrRegPair->second;
167   }
168   Register getExtInstSetReg(unsigned SetNum) { return ExtInstSetMap[SetNum]; }
169   InstrList &getMSInstrs(unsigned MSType) { return MS[MSType]; }
170   void setSkipEmission(const MachineInstr *MI) { InstrsToDelete.insert(MI); }
171   bool getSkipEmission(const MachineInstr *MI) {
172     return InstrsToDelete.contains(MI);
173   }
174   void setRegisterAlias(const MachineFunction *MF, Register Reg,
175                         Register AliasReg) {
176     RegisterAliasTable[MF][Reg] = AliasReg;
177   }
178   Register getRegisterAlias(const MachineFunction *MF, Register Reg) {
179     auto RI = RegisterAliasTable[MF].find(Reg);
180     if (RI == RegisterAliasTable[MF].end()) {
181       return Register(0);
182     }
183     return RegisterAliasTable[MF][Reg];
184   }
185   bool hasRegisterAlias(const MachineFunction *MF, Register Reg) {
186     return RegisterAliasTable.find(MF) != RegisterAliasTable.end() &&
187            RegisterAliasTable[MF].find(Reg) != RegisterAliasTable[MF].end();
188   }
189   unsigned getNextID() { return MaxID++; }
190   bool hasMBBRegister(const MachineBasicBlock &MBB) {
191     auto Key = std::make_pair(MBB.getParent(), MBB.getNumber());
192     return BBNumToRegMap.contains(Key);
193   }
194   // Convert MBB's number to corresponding ID register.
195   Register getOrCreateMBBRegister(const MachineBasicBlock &MBB) {
196     auto Key = std::make_pair(MBB.getParent(), MBB.getNumber());
197     auto It = BBNumToRegMap.find(Key);
198     if (It != BBNumToRegMap.end())
199       return It->second;
200     Register NewReg = Register::index2VirtReg(getNextID());
201     BBNumToRegMap[Key] = NewReg;
202     return NewReg;
203   }
204 };
205 } // namespace SPIRV
206 
207 using InstrSignature = SmallVector<size_t>;
208 using InstrTraces = std::set<InstrSignature>;
209 using InstrGRegsMap = std::map<SmallVector<size_t>, unsigned>;
210 
211 struct SPIRVModuleAnalysis : public ModulePass {
212   static char ID;
213 
214 public:
215   SPIRVModuleAnalysis() : ModulePass(ID) {}
216 
217   bool runOnModule(Module &M) override;
218   void getAnalysisUsage(AnalysisUsage &AU) const override;
219   static struct SPIRV::ModuleAnalysisInfo MAI;
220 
221 private:
222   void setBaseInfo(const Module &M);
223   void collectFuncNames(MachineInstr &MI, const Function *F);
224   void processOtherInstrs(const Module &M);
225   void numberRegistersGlobally(const Module &M);
226 
227   // analyze dependencies to collect module scope definitions
228   void collectDeclarations(const Module &M);
229   void visitDecl(const MachineRegisterInfo &MRI, InstrGRegsMap &SignatureToGReg,
230                  std::map<const Value *, unsigned> &GlobalToGReg,
231                  const MachineFunction *MF, const MachineInstr &MI);
232   Register handleVariable(const MachineFunction *MF, const MachineInstr &MI,
233                           std::map<const Value *, unsigned> &GlobalToGReg);
234   Register handleTypeDeclOrConstant(const MachineInstr &MI,
235                                     InstrGRegsMap &SignatureToGReg);
236   Register
237   handleFunctionOrParameter(const MachineFunction *MF, const MachineInstr &MI,
238                             std::map<const Value *, unsigned> &GlobalToGReg,
239                             bool &IsFunDef);
240   void visitFunPtrUse(Register OpReg, InstrGRegsMap &SignatureToGReg,
241                       std::map<const Value *, unsigned> &GlobalToGReg,
242                       const MachineFunction *MF, const MachineInstr &MI);
243   bool isDeclSection(const MachineRegisterInfo &MRI, const MachineInstr &MI);
244 
245   const SPIRVSubtarget *ST;
246   SPIRVGlobalRegistry *GR;
247   const SPIRVInstrInfo *TII;
248   MachineModuleInfo *MMI;
249 };
250 } // namespace llvm
251 #endif // LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
252