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/IR/PassManager.h"
24
25namespace llvm {
26class SPIRVSubtarget;
27class MachineFunction;
28
29namespace SPIRV {
30// The enum contains logical module sections for the instruction collection.
31enum 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
46struct 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(std::move(Exts)),
58 MinVer(MinVer), MaxVer(MaxVer) {}
59 Requirements(Capability::Capability Cap) : Requirements(true, {Cap}) {}
60};
61
62struct RequirementHandler {
63private:
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
83public:
84 RequirementHandler() = default;
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
128using InstrList = SmallVector<const MachineInstr *>;
129// Maps a local register to the corresponding global alias.
130using LocalToGlobalRegTable = std::map<Register, MCRegister>;
131using RegisterAliasMapTy =
132 std::map<const MachineFunction *, LocalToGlobalRegTable>;
133using FPFastMathDefaultInfoMapTy =
134 DenseMap<const Function *, FPFastMathDefaultInfoVector>;
135
136// The struct contains results of the module analysis and methods
137// to access them.
138struct ModuleAnalysisInfo {
139 RequirementHandler Reqs;
140 MemoryModel::MemoryModel Mem;
141 AddressingModel::AddressingModel Addr;
142 SourceLanguage::SourceLanguage SrcLang;
143 unsigned SrcLangVersion;
144 StringSet<> SrcExt;
145 // Maps ExtInstSet to corresponding ID register.
146 DenseMap<unsigned, MCRegister> ExtInstSetMap;
147 // Contains the list of all global OpVariables in the module.
148 SmallVector<const MachineInstr *, 4> GlobalVarList;
149 // Maps functions and global variables to corresponding ID registers.
150 DenseMap<const GlobalObject *, MCRegister> GlobalObjMap;
151 // The set contains machine instructions which are necessary
152 // for correct MIR but will not be emitted in function bodies.
153 DenseSet<const MachineInstr *> InstrsToDelete;
154 // The table contains global aliases of local registers for each machine
155 // function. The aliases are used to substitute local registers during
156 // code emission.
157 RegisterAliasMapTy RegisterAliasTable;
158 // The counter holds the maximum ID we have in the module.
159 unsigned MaxID;
160 // The array contains lists of MIs for each module section.
161 InstrList MS[NUM_MODULE_SECTIONS];
162 // The table maps MBB number to SPIR-V unique ID register.
163 DenseMap<std::pair<const MachineFunction *, int>, MCRegister> BBNumToRegMap;
164 // Basic block IDs and names to emit as OpName.
165 SmallVector<std::pair<MCRegister, StringRef>> MBBNames;
166 // The table maps function pointers to their default FP fast math info. It can
167 // be assumed that the SmallVector is sorted by the bit width of the type. The
168 // first element is the smallest bit width, and the last element is the
169 // largest bit width, therefore, we will have {half, float, double} in
170 // the order of their bit widths.
171 FPFastMathDefaultInfoMapTy FPFastMathDefaultInfoMap;
172
173 enum class AMDGPUAtomicMDKind : uint8_t {
174 NoFineGrainedMemory,
175 NoRemoteMemory,
176 IgnoreDenormalMode,
177
178 Last = IgnoreDenormalMode,
179 };
180 struct InstrAuxDataRecord {
181 const MachineFunction *MF;
182 Register TargetReg;
183 AMDGPUAtomicMDKind Kind;
184 };
185 SmallVector<InstrAuxDataRecord> InstrAuxDataRecords;
186
187 MCRegister getGlobalObjReg(const GlobalObject *GO) {
188 assert(GO && "GlobalObject is null");
189 return GlobalObjMap.lookup(Val: GO);
190 }
191 MCRegister getExtInstSetReg(unsigned SetNum) { return ExtInstSetMap[SetNum]; }
192 InstrList &getMSInstrs(unsigned MSType) { return MS[MSType]; }
193 void setSkipEmission(const MachineInstr *MI) { InstrsToDelete.insert(V: MI); }
194 bool getSkipEmission(const MachineInstr *MI) {
195 return InstrsToDelete.contains(V: MI);
196 }
197 void setRegisterAlias(const MachineFunction *MF, Register Reg,
198 MCRegister AliasReg) {
199 RegisterAliasTable[MF][Reg] = AliasReg;
200 }
201 MCRegister getRegisterAlias(const MachineFunction *MF, Register Reg) {
202 auto &RegTable = RegisterAliasTable[MF];
203 auto RI = RegTable.find(x: Reg);
204 if (RI == RegTable.end()) {
205 return MCRegister();
206 }
207 return RI->second;
208 }
209 bool hasRegisterAlias(const MachineFunction *MF, Register Reg) {
210 auto RI = RegisterAliasTable.find(x: MF);
211 if (RI == RegisterAliasTable.end())
212 return false;
213 return RI->second.find(x: Reg) != RI->second.end();
214 }
215 unsigned getNextID() { return MaxID++; }
216 MCRegister getNextIDRegister() {
217 return MCRegister((1U << 31) | getNextID());
218 }
219 bool hasMBBRegister(const MachineBasicBlock &MBB) {
220 auto Key = std::make_pair(x: MBB.getParent(), y: MBB.getNumber());
221 return BBNumToRegMap.contains(Val: Key);
222 }
223 // Convert MBB's number to corresponding ID register.
224 MCRegister getOrCreateMBBRegister(const MachineBasicBlock &MBB) {
225 auto Key = std::make_pair(x: MBB.getParent(), y: MBB.getNumber());
226 auto [It, Inserted] = BBNumToRegMap.try_emplace(Key);
227 if (Inserted)
228 It->second = getNextIDRegister();
229 return It->second;
230 }
231 static StringRef getAMDGPUAtomicMDName(AMDGPUAtomicMDKind Kind) {
232 switch (Kind) {
233 case AMDGPUAtomicMDKind::NoFineGrainedMemory:
234 return "amdgpu.no.fine.grained.memory";
235 case AMDGPUAtomicMDKind::NoRemoteMemory:
236 return "amdgpu.no.remote.memory";
237 case AMDGPUAtomicMDKind::IgnoreDenormalMode:
238 return "atomic.ignore.denormal.mode";
239 }
240 llvm_unreachable("unknown AMDGPUAtomicMDKind");
241 }
242 // Must stay alive until the AsmPrinter consumes it.
243 bool invalidate(Module &, const PreservedAnalyses &,
244 ModuleAnalysisManager::Invalidator &) {
245 return false;
246 }
247};
248} // namespace SPIRV
249
250using InstrSignature = SmallVector<size_t>;
251using InstrTraces = std::set<InstrSignature>;
252using InstrGRegsMap = std::map<SmallVector<size_t>, unsigned>;
253using MachineFunctionGetter = function_ref<MachineFunction *(const Function &)>;
254
255class SPIRVModuleAnalysisImpl {
256public:
257 SPIRVModuleAnalysisImpl(const SPIRVSubtarget &ST,
258 SPIRV::ModuleAnalysisInfo &MAI,
259 MachineFunctionGetter GetMF);
260
261 void run(const Module &M);
262
263private:
264 void setBaseInfo(const Module &M);
265 void collectFuncNames(MachineInstr &MI, const Function *F);
266 void processOtherInstrs(const Module &M);
267 void numberRegistersGlobally(const Module &M);
268
269 // analyze dependencies to collect module scope definitions
270 void collectDeclarations(const Module &M);
271 void visitDecl(const MachineRegisterInfo &MRI, InstrGRegsMap &SignatureToGReg,
272 std::map<const Value *, unsigned> &GlobalToGReg,
273 const MachineFunction *MF, const MachineInstr &MI);
274 MCRegister handleVariable(const MachineFunction *MF, const MachineInstr &MI,
275 std::map<const Value *, unsigned> &GlobalToGReg);
276 MCRegister handleTypeDeclOrConstant(const MachineInstr &MI,
277 InstrGRegsMap &SignatureToGReg);
278 MCRegister
279 handleFunctionOrParameter(const MachineFunction *MF, const MachineInstr &MI,
280 std::map<const Value *, unsigned> &GlobalToGReg,
281 bool &IsFunDef);
282 void visitFunPtrUse(Register OpReg, const MachineOperand *FunPtrOp,
283 InstrGRegsMap &SignatureToGReg,
284 std::map<const Value *, unsigned> &GlobalToGReg,
285 const MachineFunction *MF);
286 bool isDeclSection(const MachineRegisterInfo &MRI, const MachineInstr &MI);
287
288 const SPIRVSubtarget *ST;
289 SPIRVGlobalRegistry *GR;
290 const SPIRVInstrInfo *TII;
291 SPIRV::ModuleAnalysisInfo &MAI;
292 MachineFunctionGetter GetMF;
293};
294
295struct SPIRVModuleAnalysisWrapperPass : public ModulePass {
296 static char ID;
297
298public:
299 SPIRVModuleAnalysisWrapperPass() : ModulePass(ID) {}
300
301 bool runOnModule(Module &M) override;
302 void getAnalysisUsage(AnalysisUsage &AU) const override;
303 SPIRV::ModuleAnalysisInfo MAI;
304};
305
306class SPIRVModuleAnalysis : public AnalysisInfoMixin<SPIRVModuleAnalysis> {
307 friend AnalysisInfoMixin<SPIRVModuleAnalysis>;
308 static AnalysisKey Key;
309
310public:
311 using Result = SPIRV::ModuleAnalysisInfo;
312
313 Result run(Module &M, ModuleAnalysisManager &MAM);
314};
315} // namespace llvm
316#endif // LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
317