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_AliasingInsts, // SPV_INTEL_memory_access_aliasing instructions. |
39 | MB_Annotations, // OpDecorate, OpMemberDecorate etc. |
40 | MB_TypeConstVars, // OpTypeXXX, OpConstantXXX, and global OpVariables. |
41 | MB_NonSemanticGlobalDI, // OpExtInst with e.g. DebugSource, DebugTypeBasic. |
42 | MB_ExtFuncDecls, // OpFunction etc. to declare for external funcs. |
43 | NUM_MODULE_SECTIONS // Total number of sections requiring basic blocks. |
44 | }; |
45 | |
46 | struct Requirements { |
47 | const bool IsSatisfiable; |
48 | const std::optional<Capability::Capability> Cap; |
49 | const ExtensionList Exts; |
50 | const VersionTuple MinVer; // 0 if no min version is required. |
51 | const VersionTuple MaxVer; // 0 if no max version is required. |
52 | |
53 | Requirements(bool IsSatisfiable = false, |
54 | std::optional<Capability::Capability> Cap = {}, |
55 | ExtensionList Exts = {}, VersionTuple MinVer = VersionTuple(), |
56 | VersionTuple MaxVer = VersionTuple()) |
57 | : IsSatisfiable(IsSatisfiable), Cap(Cap), Exts(Exts), MinVer(MinVer), |
58 | MaxVer(MaxVer) {} |
59 | Requirements(Capability::Capability Cap) : Requirements(true, {Cap}) {} |
60 | }; |
61 | |
62 | struct RequirementHandler { |
63 | private: |
64 | CapabilityList MinimalCaps; |
65 | |
66 | // AllCaps and AvailableCaps are related but different. AllCaps is a subset of |
67 | // AvailableCaps. AvailableCaps is the complete set of capabilities that are |
68 | // available to the current target. AllCaps is the set of capabilities that |
69 | // are required by the current module. |
70 | SmallSet<Capability::Capability, 8> AllCaps; |
71 | DenseSet<unsigned> AvailableCaps; |
72 | |
73 | SmallSet<Extension::Extension, 4> AllExtensions; |
74 | VersionTuple MinVersion; // 0 if no min version is defined. |
75 | VersionTuple MaxVersion; // 0 if no max version is defined. |
76 | // Add capabilities to AllCaps, recursing through their implicitly declared |
77 | // capabilities too. |
78 | void recursiveAddCapabilities(const CapabilityList &ToPrune); |
79 | |
80 | void initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget &ST); |
81 | void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST); |
82 | |
83 | public: |
84 | RequirementHandler() {} |
85 | void clear() { |
86 | MinimalCaps.clear(); |
87 | AllCaps.clear(); |
88 | AvailableCaps.clear(); |
89 | AllExtensions.clear(); |
90 | MinVersion = VersionTuple(); |
91 | MaxVersion = VersionTuple(); |
92 | } |
93 | const CapabilityList &getMinimalCapabilities() const { return MinimalCaps; } |
94 | 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); |
101 | void addCapability(Capability::Capability ToAdd) { addCapabilities(ToAdd: {ToAdd}); } |
102 | void addExtensions(const ExtensionList &ToAdd) { |
103 | AllExtensions.insert_range(R: ToAdd); |
104 | } |
105 | void addExtension(Extension::Extension ToAdd) { AllExtensions.insert(V: 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); |
119 | bool isCapabilityAvailable(Capability::Capability Cap) const { |
120 | return AvailableCaps.contains(V: 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<const MachineInstr *>; |
129 | // Maps a local register to the corresponding global alias. |
130 | using LocalToGlobalRegTable = std::map<Register, MCRegister>; |
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, MCRegister> ExtInstSetMap; |
145 | // Contains the list of all global OpVariables in the module. |
146 | SmallVector<const MachineInstr *, 4> GlobalVarList; |
147 | // Maps functions to corresponding function ID registers. |
148 | DenseMap<const Function *, MCRegister> FuncMap; |
149 | // The set contains machine instructions which are necessary |
150 | // for correct MIR but will not be emitted in function bodies. |
151 | DenseSet<const 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<std::pair<const MachineFunction *, int>, MCRegister> BBNumToRegMap; |
162 | |
163 | MCRegister getFuncReg(const Function *F) { |
164 | assert(F && "Function is null" ); |
165 | auto FuncPtrRegPair = FuncMap.find(Val: F); |
166 | return FuncPtrRegPair == FuncMap.end() ? MCRegister() |
167 | : FuncPtrRegPair->second; |
168 | } |
169 | MCRegister getExtInstSetReg(unsigned SetNum) { return ExtInstSetMap[SetNum]; } |
170 | InstrList &getMSInstrs(unsigned MSType) { return MS[MSType]; } |
171 | void setSkipEmission(const MachineInstr *MI) { InstrsToDelete.insert(V: MI); } |
172 | bool getSkipEmission(const MachineInstr *MI) { |
173 | return InstrsToDelete.contains(V: MI); |
174 | } |
175 | void setRegisterAlias(const MachineFunction *MF, Register Reg, |
176 | MCRegister AliasReg) { |
177 | RegisterAliasTable[MF][Reg] = AliasReg; |
178 | } |
179 | MCRegister getRegisterAlias(const MachineFunction *MF, Register Reg) { |
180 | auto &RegTable = RegisterAliasTable[MF]; |
181 | auto RI = RegTable.find(x: Reg); |
182 | if (RI == RegTable.end()) { |
183 | return MCRegister(); |
184 | } |
185 | return RI->second; |
186 | } |
187 | bool hasRegisterAlias(const MachineFunction *MF, Register Reg) { |
188 | auto RI = RegisterAliasTable.find(x: MF); |
189 | if (RI == RegisterAliasTable.end()) |
190 | return false; |
191 | return RI->second.find(x: Reg) != RI->second.end(); |
192 | } |
193 | unsigned getNextID() { return MaxID++; } |
194 | MCRegister getNextIDRegister() { |
195 | return MCRegister((1U << 31) | getNextID()); |
196 | } |
197 | bool hasMBBRegister(const MachineBasicBlock &MBB) { |
198 | auto Key = std::make_pair(x: MBB.getParent(), y: MBB.getNumber()); |
199 | return BBNumToRegMap.contains(Val: Key); |
200 | } |
201 | // Convert MBB's number to corresponding ID register. |
202 | MCRegister getOrCreateMBBRegister(const MachineBasicBlock &MBB) { |
203 | auto Key = std::make_pair(x: MBB.getParent(), y: MBB.getNumber()); |
204 | auto [It, Inserted] = BBNumToRegMap.try_emplace(Key); |
205 | if (Inserted) |
206 | It->second = getNextIDRegister(); |
207 | return It->second; |
208 | } |
209 | }; |
210 | } // namespace SPIRV |
211 | |
212 | using InstrSignature = SmallVector<size_t>; |
213 | using InstrTraces = std::set<InstrSignature>; |
214 | using InstrGRegsMap = std::map<SmallVector<size_t>, unsigned>; |
215 | |
216 | struct SPIRVModuleAnalysis : public ModulePass { |
217 | static char ID; |
218 | |
219 | public: |
220 | SPIRVModuleAnalysis() : ModulePass(ID) {} |
221 | |
222 | bool runOnModule(Module &M) override; |
223 | void getAnalysisUsage(AnalysisUsage &AU) const override; |
224 | static struct SPIRV::ModuleAnalysisInfo MAI; |
225 | |
226 | private: |
227 | void setBaseInfo(const Module &M); |
228 | void collectFuncNames(MachineInstr &MI, const Function *F); |
229 | void processOtherInstrs(const Module &M); |
230 | void numberRegistersGlobally(const Module &M); |
231 | |
232 | // analyze dependencies to collect module scope definitions |
233 | void collectDeclarations(const Module &M); |
234 | void visitDecl(const MachineRegisterInfo &MRI, InstrGRegsMap &SignatureToGReg, |
235 | std::map<const Value *, unsigned> &GlobalToGReg, |
236 | const MachineFunction *MF, const MachineInstr &MI); |
237 | MCRegister handleVariable(const MachineFunction *MF, const MachineInstr &MI, |
238 | std::map<const Value *, unsigned> &GlobalToGReg); |
239 | MCRegister handleTypeDeclOrConstant(const MachineInstr &MI, |
240 | InstrGRegsMap &SignatureToGReg); |
241 | MCRegister |
242 | handleFunctionOrParameter(const MachineFunction *MF, const MachineInstr &MI, |
243 | std::map<const Value *, unsigned> &GlobalToGReg, |
244 | bool &IsFunDef); |
245 | void visitFunPtrUse(Register OpReg, InstrGRegsMap &SignatureToGReg, |
246 | std::map<const Value *, unsigned> &GlobalToGReg, |
247 | const MachineFunction *MF, const MachineInstr &MI); |
248 | bool isDeclSection(const MachineRegisterInfo &MRI, const MachineInstr &MI); |
249 | |
250 | const SPIRVSubtarget *ST; |
251 | SPIRVGlobalRegistry *GR; |
252 | const SPIRVInstrInfo *TII; |
253 | MachineModuleInfo *MMI; |
254 | }; |
255 | } // namespace llvm |
256 | #endif // LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H |
257 | |