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 #include "llvm/ADT/StringMap.h"
24 
25 namespace llvm {
26 class SPIRVSubtarget;
27 class MachineFunction;
28 class MachineModuleInfo;
29 
30 namespace SPIRV {
31 // The enum contains logical module sections for the instruction collection.
32 enum ModuleSectionType {
33   //  MB_Capabilities, MB_Extensions, MB_ExtInstImports, MB_MemoryModel,
34   MB_EntryPoints, // All OpEntryPoint instructions (if any).
35   //  MB_ExecutionModes, MB_DebugSourceAndStrings,
36   MB_DebugNames,           // All OpName and OpMemberName intrs.
37   MB_DebugModuleProcessed, // All OpModuleProcessed instructions.
38   MB_Annotations,          // OpDecorate, OpMemberDecorate etc.
39   MB_TypeConstVars,        // OpTypeXXX, OpConstantXXX, and global OpVariables.
40   MB_ExtFuncDecls,         // OpFunction etc. to declare for external funcs.
41   NUM_MODULE_SECTIONS      // Total number of sections requiring basic blocks.
42 };
43 
44 struct Requirements {
45   const bool IsSatisfiable;
46   const std::optional<Capability::Capability> Cap;
47   const ExtensionList Exts;
48   const unsigned MinVer; // 0 if no min version is required.
49   const unsigned MaxVer; // 0 if no max version is required.
50 
51   Requirements(bool IsSatisfiable = false,
52                std::optional<Capability::Capability> Cap = {},
53                ExtensionList Exts = {}, unsigned MinVer = 0,
54                unsigned MaxVer = 0)
IsSatisfiableRequirements55       : IsSatisfiable(IsSatisfiable), Cap(Cap), Exts(Exts), MinVer(MinVer),
56         MaxVer(MaxVer) {}
RequirementsRequirements57   Requirements(Capability::Capability Cap) : Requirements(true, {Cap}) {}
58 };
59 
60 struct RequirementHandler {
61 private:
62   CapabilityList MinimalCaps;
63 
64   // AllCaps and AvailableCaps are related but different. AllCaps is a subset of
65   // AvailableCaps. AvailableCaps is the complete set of capabilities that are
66   // available to the current target. AllCaps is the set of capabilities that
67   // are required by the current module.
68   SmallSet<Capability::Capability, 8> AllCaps;
69   DenseSet<unsigned> AvailableCaps;
70 
71   SmallSet<Extension::Extension, 4> AllExtensions;
72   unsigned MinVersion; // 0 if no min version is defined.
73   unsigned MaxVersion; // 0 if no max version is defined.
74   // Remove a list of capabilities from dedupedCaps and add them to AllCaps,
75   // recursing through their implicitly declared capabilities too.
76   void pruneCapabilities(const CapabilityList &ToPrune);
77 
78   void initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget &ST);
79   void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST);
80 
81 public:
RequirementHandlerRequirementHandler82   RequirementHandler() : MinVersion(0), MaxVersion(0) {}
clearRequirementHandler83   void clear() {
84     MinimalCaps.clear();
85     AllCaps.clear();
86     AvailableCaps.clear();
87     AllExtensions.clear();
88     MinVersion = 0;
89     MaxVersion = 0;
90   }
getMinVersionRequirementHandler91   unsigned getMinVersion() const { return MinVersion; }
getMaxVersionRequirementHandler92   unsigned getMaxVersion() const { return MaxVersion; }
getMinimalCapabilitiesRequirementHandler93   const CapabilityList &getMinimalCapabilities() const { return MinimalCaps; }
getExtensionsRequirementHandler94   const SmallSet<Extension::Extension, 4> &getExtensions() const {
95     return AllExtensions;
96   }
97   // Add a list of capabilities, ensuring AllCaps captures all the implicitly
98   // declared capabilities, and MinimalCaps has the minimal set of required
99   // capabilities (so all implicitly declared ones are removed).
100   void addCapabilities(const CapabilityList &ToAdd);
addCapabilityRequirementHandler101   void addCapability(Capability::Capability ToAdd) { addCapabilities({ToAdd}); }
addExtensionsRequirementHandler102   void addExtensions(const ExtensionList &ToAdd) {
103     AllExtensions.insert(ToAdd.begin(), ToAdd.end());
104   }
addExtensionRequirementHandler105   void addExtension(Extension::Extension ToAdd) { AllExtensions.insert(ToAdd); }
106   // Add the given requirements to the lists. If constraints conflict, or these
107   // requirements cannot be satisfied, then abort the compilation.
108   void addRequirements(const Requirements &Req);
109   // Get requirement and add it to the list.
110   void getAndAddRequirements(SPIRV::OperandCategory::OperandCategory Category,
111                              uint32_t i, const SPIRVSubtarget &ST);
112   // Check if all the requirements can be satisfied for the given subtarget, and
113   // if not abort compilation.
114   void checkSatisfiable(const SPIRVSubtarget &ST) const;
115   void initAvailableCapabilities(const SPIRVSubtarget &ST);
116   // Add the given capabilities to available and all their implicitly defined
117   // capabilities too.
118   void addAvailableCaps(const CapabilityList &ToAdd);
isCapabilityAvailableRequirementHandler119   bool isCapabilityAvailable(Capability::Capability Cap) const {
120     return AvailableCaps.contains(Cap);
121   }
122 
123   // Remove capability ToRemove, but only if IfPresent is present.
124   void removeCapabilityIf(const Capability::Capability ToRemove,
125                           const Capability::Capability IfPresent);
126 };
127 
128 using InstrList = SmallVector<MachineInstr *>;
129 // Maps a local register to the corresponding global alias.
130 using LocalToGlobalRegTable = std::map<Register, Register>;
131 using RegisterAliasMapTy =
132     std::map<const MachineFunction *, LocalToGlobalRegTable>;
133 
134 // The struct contains results of the module analysis and methods
135 // to access them.
136 struct ModuleAnalysisInfo {
137   RequirementHandler Reqs;
138   MemoryModel::MemoryModel Mem;
139   AddressingModel::AddressingModel Addr;
140   SourceLanguage::SourceLanguage SrcLang;
141   unsigned SrcLangVersion;
142   StringSet<> SrcExt;
143   // Maps ExtInstSet to corresponding ID register.
144   DenseMap<unsigned, Register> ExtInstSetMap;
145   // Contains the list of all global OpVariables in the module.
146   SmallVector<MachineInstr *, 4> GlobalVarList;
147   // Maps functions to corresponding function ID registers.
148   DenseMap<const Function *, Register> FuncMap;
149   // The set contains machine instructions which are necessary
150   // for correct MIR but will not be emitted in function bodies.
151   DenseSet<MachineInstr *> InstrsToDelete;
152   // The table contains global aliases of local registers for each machine
153   // function. The aliases are used to substitute local registers during
154   // code emission.
155   RegisterAliasMapTy RegisterAliasTable;
156   // The counter holds the maximum ID we have in the module.
157   unsigned MaxID;
158   // The array contains lists of MIs for each module section.
159   InstrList MS[NUM_MODULE_SECTIONS];
160   // The table maps MBB number to SPIR-V unique ID register.
161   DenseMap<int, Register> BBNumToRegMap;
162 
getFuncRegModuleAnalysisInfo163   Register getFuncReg(const Function *F) {
164     assert(F && "Function is null");
165     auto FuncPtrRegPair = FuncMap.find(F);
166     assert(FuncPtrRegPair != FuncMap.end() && "Cannot find function ID");
167     return FuncPtrRegPair->second;
168   }
getExtInstSetRegModuleAnalysisInfo169   Register getExtInstSetReg(unsigned SetNum) { return ExtInstSetMap[SetNum]; }
getMSInstrsModuleAnalysisInfo170   InstrList &getMSInstrs(unsigned MSType) { return MS[MSType]; }
setSkipEmissionModuleAnalysisInfo171   void setSkipEmission(MachineInstr *MI) { InstrsToDelete.insert(MI); }
getSkipEmissionModuleAnalysisInfo172   bool getSkipEmission(const MachineInstr *MI) {
173     return InstrsToDelete.contains(MI);
174   }
setRegisterAliasModuleAnalysisInfo175   void setRegisterAlias(const MachineFunction *MF, Register Reg,
176                         Register AliasReg) {
177     RegisterAliasTable[MF][Reg] = AliasReg;
178   }
getRegisterAliasModuleAnalysisInfo179   Register getRegisterAlias(const MachineFunction *MF, Register Reg) {
180     auto RI = RegisterAliasTable[MF].find(Reg);
181     if (RI == RegisterAliasTable[MF].end()) {
182       return Register(0);
183     }
184     return RegisterAliasTable[MF][Reg];
185   }
hasRegisterAliasModuleAnalysisInfo186   bool hasRegisterAlias(const MachineFunction *MF, Register Reg) {
187     return RegisterAliasTable.find(MF) != RegisterAliasTable.end() &&
188            RegisterAliasTable[MF].find(Reg) != RegisterAliasTable[MF].end();
189   }
getNextIDModuleAnalysisInfo190   unsigned getNextID() { return MaxID++; }
hasMBBRegisterModuleAnalysisInfo191   bool hasMBBRegister(const MachineBasicBlock &MBB) {
192     return BBNumToRegMap.contains(MBB.getNumber());
193   }
194   // Convert MBB's number to corresponding ID register.
getOrCreateMBBRegisterModuleAnalysisInfo195   Register getOrCreateMBBRegister(const MachineBasicBlock &MBB) {
196     auto f = BBNumToRegMap.find(MBB.getNumber());
197     if (f != BBNumToRegMap.end())
198       return f->second;
199     Register NewReg = Register::index2VirtReg(getNextID());
200     BBNumToRegMap[MBB.getNumber()] = NewReg;
201     return NewReg;
202   }
203 };
204 } // namespace SPIRV
205 
206 struct SPIRVModuleAnalysis : public ModulePass {
207   static char ID;
208 
209 public:
SPIRVModuleAnalysisSPIRVModuleAnalysis210   SPIRVModuleAnalysis() : ModulePass(ID) {}
211 
212   bool runOnModule(Module &M) override;
213   void getAnalysisUsage(AnalysisUsage &AU) const override;
214   static struct SPIRV::ModuleAnalysisInfo MAI;
215 
216 private:
217   void setBaseInfo(const Module &M);
218   void collectGlobalEntities(
219       const std::vector<SPIRV::DTSortableEntry *> &DepsGraph,
220       SPIRV::ModuleSectionType MSType,
221       std::function<bool(const SPIRV::DTSortableEntry *)> Pred,
222       bool UsePreOrder);
223   void processDefInstrs(const Module &M);
224   void collectFuncNames(MachineInstr &MI, const Function *F);
225   void processOtherInstrs(const Module &M);
226   void numberRegistersGlobally(const Module &M);
227 
228   const SPIRVSubtarget *ST;
229   SPIRVGlobalRegistry *GR;
230   const SPIRVInstrInfo *TII;
231   MachineModuleInfo *MMI;
232 };
233 } // namespace llvm
234 #endif // LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
235