| 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 | |