| //===- SPIRVModuleAnalysis.h - analysis of global instrs & regs -*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // The analysis collects instructions that should be output at the module level | |
| // and performs the global register numbering. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H | |
| #define LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H | |
| #include "MCTargetDesc/SPIRVBaseInfo.h" | |
| #include "SPIRVGlobalRegistry.h" | |
| #include "SPIRVUtils.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/SmallSet.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| namespace llvm { | |
| class SPIRVSubtarget; | |
| class MachineFunction; | |
| class MachineModuleInfo; | |
| namespace SPIRV { | |
| // The enum contains logical module sections for the instruction collection. | |
| enum ModuleSectionType { | |
| // MB_Capabilities, MB_Extensions, MB_ExtInstImports, MB_MemoryModel, | |
| MB_EntryPoints, // All OpEntryPoint instructions (if any). | |
| // MB_ExecutionModes, MB_DebugSourceAndStrings, | |
| MB_DebugNames, // All OpName and OpMemberName intrs. | |
| MB_DebugStrings, // All OpString intrs. | |
| MB_DebugModuleProcessed, // All OpModuleProcessed instructions. | |
| MB_AliasingInsts, // SPV_INTEL_memory_access_aliasing instructions. | |
| MB_Annotations, // OpDecorate, OpMemberDecorate etc. | |
| MB_TypeConstVars, // OpTypeXXX, OpConstantXXX, and global OpVariables. | |
| MB_NonSemanticGlobalDI, // OpExtInst with e.g. DebugSource, DebugTypeBasic. | |
| MB_ExtFuncDecls, // OpFunction etc. to declare for external funcs. | |
| NUM_MODULE_SECTIONS // Total number of sections requiring basic blocks. | |
| }; | |
| struct Requirements { | |
| const bool IsSatisfiable; | |
| const std::optional<Capability::Capability> Cap; | |
| const ExtensionList Exts; | |
| const VersionTuple MinVer; // 0 if no min version is required. | |
| const VersionTuple MaxVer; // 0 if no max version is required. | |
| Requirements(bool IsSatisfiable = false, | |
| std::optional<Capability::Capability> Cap = {}, | |
| ExtensionList Exts = {}, VersionTuple MinVer = VersionTuple(), | |
| VersionTuple MaxVer = VersionTuple()) | |
| : IsSatisfiable(IsSatisfiable), Cap(Cap), Exts(std::move(Exts)), | |
| MinVer(MinVer), MaxVer(MaxVer) {} | |
| Requirements(Capability::Capability Cap) : Requirements(true, {Cap}) {} | |
| }; | |
| struct RequirementHandler { | |
| private: | |
| CapabilityList MinimalCaps; | |
| // AllCaps and AvailableCaps are related but different. AllCaps is a subset of | |
| // AvailableCaps. AvailableCaps is the complete set of capabilities that are | |
| // available to the current target. AllCaps is the set of capabilities that | |
| // are required by the current module. | |
| SmallSet<Capability::Capability, 8> AllCaps; | |
| DenseSet<unsigned> AvailableCaps; | |
| SmallSet<Extension::Extension, 4> AllExtensions; | |
| VersionTuple MinVersion; // 0 if no min version is defined. | |
| VersionTuple MaxVersion; // 0 if no max version is defined. | |
| // Add capabilities to AllCaps, recursing through their implicitly declared | |
| // capabilities too. | |
| void recursiveAddCapabilities(const CapabilityList &ToPrune); | |
| void initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget &ST); | |
| void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST); | |
| public: | |
| RequirementHandler() = default; | |
| void clear() { | |
| MinimalCaps.clear(); | |
| AllCaps.clear(); | |
| AvailableCaps.clear(); | |
| AllExtensions.clear(); | |
| MinVersion = VersionTuple(); | |
| MaxVersion = VersionTuple(); | |
| } | |
| const CapabilityList &getMinimalCapabilities() const { return MinimalCaps; } | |
| const SmallSet<Extension::Extension, 4> &getExtensions() const { | |
| return AllExtensions; | |
| } | |
| // Add a list of capabilities, ensuring AllCaps captures all the implicitly | |
| // declared capabilities, and MinimalCaps has the minimal set of required | |
| // capabilities (so all implicitly declared ones are removed). | |
| void addCapabilities(const CapabilityList &ToAdd); | |
| void addCapability(Capability::Capability ToAdd) { addCapabilities({ToAdd}); } | |
| void addExtensions(const ExtensionList &ToAdd) { | |
| AllExtensions.insert_range(ToAdd); | |
| } | |
| void addExtension(Extension::Extension ToAdd) { AllExtensions.insert(ToAdd); } | |
| // Add the given requirements to the lists. If constraints conflict, or these | |
| // requirements cannot be satisfied, then abort the compilation. | |
| void addRequirements(const Requirements &Req); | |
| // Get requirement and add it to the list. | |
| void getAndAddRequirements(SPIRV::OperandCategory::OperandCategory Category, | |
| uint32_t i, const SPIRVSubtarget &ST); | |
| // Check if all the requirements can be satisfied for the given subtarget, and | |
| // if not abort compilation. | |
| void checkSatisfiable(const SPIRVSubtarget &ST) const; | |
| void initAvailableCapabilities(const SPIRVSubtarget &ST); | |
| // Add the given capabilities to available and all their implicitly defined | |
| // capabilities too. | |
| void addAvailableCaps(const CapabilityList &ToAdd); | |
| bool isCapabilityAvailable(Capability::Capability Cap) const { | |
| return AvailableCaps.contains(Cap); | |
| } | |
| // Remove capability ToRemove, but only if IfPresent is present. | |
| void removeCapabilityIf(const Capability::Capability ToRemove, | |
| const Capability::Capability IfPresent); | |
| }; | |
| using InstrList = SmallVector<const MachineInstr *>; | |
| // Maps a local register to the corresponding global alias. | |
| using LocalToGlobalRegTable = std::map<Register, MCRegister>; | |
| using RegisterAliasMapTy = | |
| std::map<const MachineFunction *, LocalToGlobalRegTable>; | |
| // The struct contains results of the module analysis and methods | |
| // to access them. | |
| struct ModuleAnalysisInfo { | |
| RequirementHandler Reqs; | |
| MemoryModel::MemoryModel Mem; | |
| AddressingModel::AddressingModel Addr; | |
| SourceLanguage::SourceLanguage SrcLang; | |
| unsigned SrcLangVersion; | |
| StringSet<> SrcExt; | |
| // Maps ExtInstSet to corresponding ID register. | |
| DenseMap<unsigned, MCRegister> ExtInstSetMap; | |
| // Contains the list of all global OpVariables in the module. | |
| SmallVector<const MachineInstr *, 4> GlobalVarList; | |
| // Maps functions and global variables to corresponding ID registers. | |
| DenseMap<const GlobalObject *, MCRegister> GlobalObjMap; | |
| // The set contains machine instructions which are necessary | |
| // for correct MIR but will not be emitted in function bodies. | |
| DenseSet<const MachineInstr *> InstrsToDelete; | |
| // The table contains global aliases of local registers for each machine | |
| // function. The aliases are used to substitute local registers during | |
| // code emission. | |
| RegisterAliasMapTy RegisterAliasTable; | |
| // The counter holds the maximum ID we have in the module. | |
| unsigned MaxID; | |
| // The array contains lists of MIs for each module section. | |
| InstrList MS[NUM_MODULE_SECTIONS]; | |
| // The table maps MBB number to SPIR-V unique ID register. | |
| DenseMap<std::pair<const MachineFunction *, int>, MCRegister> BBNumToRegMap; | |
| // The table maps function pointers to their default FP fast math info. It can | |
| // be assumed that the SmallVector is sorted by the bit width of the type. The | |
| // first element is the smallest bit width, and the last element is the | |
| // largest bit width, therefore, we will have {half, float, double} in | |
| // the order of their bit widths. | |
| DenseMap<const Function *, SPIRV::FPFastMathDefaultInfoVector> | |
| FPFastMathDefaultInfoMap; | |
| MCRegister getGlobalObjReg(const GlobalObject *GO) { | |
| assert(GO && "GlobalObject is null"); | |
| return GlobalObjMap.lookup(GO); | |
| } | |
| MCRegister getExtInstSetReg(unsigned SetNum) { return ExtInstSetMap[SetNum]; } | |
| InstrList &getMSInstrs(unsigned MSType) { return MS[MSType]; } | |
| void setSkipEmission(const MachineInstr *MI) { InstrsToDelete.insert(MI); } | |
| bool getSkipEmission(const MachineInstr *MI) { | |
| return InstrsToDelete.contains(MI); | |
| } | |
| void setRegisterAlias(const MachineFunction *MF, Register Reg, | |
| MCRegister AliasReg) { | |
| RegisterAliasTable[MF][Reg] = AliasReg; | |
| } | |
| MCRegister getRegisterAlias(const MachineFunction *MF, Register Reg) { | |
| auto &RegTable = RegisterAliasTable[MF]; | |
| auto RI = RegTable.find(Reg); | |
| if (RI == RegTable.end()) { | |
| return MCRegister(); | |
| } | |
| return RI->second; | |
| } | |
| bool hasRegisterAlias(const MachineFunction *MF, Register Reg) { | |
| auto RI = RegisterAliasTable.find(MF); | |
| if (RI == RegisterAliasTable.end()) | |
| return false; | |
| return RI->second.find(Reg) != RI->second.end(); | |
| } | |
| unsigned getNextID() { return MaxID++; } | |
| MCRegister getNextIDRegister() { | |
| return MCRegister((1U << 31) | getNextID()); | |
| } | |
| bool hasMBBRegister(const MachineBasicBlock &MBB) { | |
| auto Key = std::make_pair(MBB.getParent(), MBB.getNumber()); | |
| return BBNumToRegMap.contains(Key); | |
| } | |
| // Convert MBB's number to corresponding ID register. | |
| MCRegister getOrCreateMBBRegister(const MachineBasicBlock &MBB) { | |
| auto Key = std::make_pair(MBB.getParent(), MBB.getNumber()); | |
| auto [It, Inserted] = BBNumToRegMap.try_emplace(Key); | |
| if (Inserted) | |
| It->second = getNextIDRegister(); | |
| return It->second; | |
| } | |
| }; | |
| } // namespace SPIRV | |
| using InstrSignature = SmallVector<size_t>; | |
| using InstrTraces = std::set<InstrSignature>; | |
| using InstrGRegsMap = std::map<SmallVector<size_t>, unsigned>; | |
| struct SPIRVModuleAnalysis : public ModulePass { | |
| static char ID; | |
| public: | |
| SPIRVModuleAnalysis() | |
| : ModulePass(ID), ST(nullptr), GR(nullptr), TII(nullptr), MMI(nullptr) {} | |
| bool runOnModule(Module &M) override; | |
| void getAnalysisUsage(AnalysisUsage &AU) const override; | |
| SPIRV::ModuleAnalysisInfo MAI; | |
| private: | |
| void setBaseInfo(const Module &M); | |
| void collectFuncNames(MachineInstr &MI, const Function *F); | |
| void processOtherInstrs(const Module &M); | |
| void numberRegistersGlobally(const Module &M); | |
| // analyze dependencies to collect module scope definitions | |
| void collectDeclarations(const Module &M); | |
| void visitDecl(const MachineRegisterInfo &MRI, InstrGRegsMap &SignatureToGReg, | |
| std::map<const Value *, unsigned> &GlobalToGReg, | |
| const MachineFunction *MF, const MachineInstr &MI); | |
| MCRegister handleVariable(const MachineFunction *MF, const MachineInstr &MI, | |
| std::map<const Value *, unsigned> &GlobalToGReg); | |
| MCRegister handleTypeDeclOrConstant(const MachineInstr &MI, | |
| InstrGRegsMap &SignatureToGReg); | |
| MCRegister | |
| handleFunctionOrParameter(const MachineFunction *MF, const MachineInstr &MI, | |
| std::map<const Value *, unsigned> &GlobalToGReg, | |
| bool &IsFunDef); | |
| void visitFunPtrUse(Register OpReg, InstrGRegsMap &SignatureToGReg, | |
| std::map<const Value *, unsigned> &GlobalToGReg, | |
| const MachineFunction *MF, const MachineInstr &MI); | |
| bool isDeclSection(const MachineRegisterInfo &MRI, const MachineInstr &MI); | |
| const SPIRVSubtarget *ST; | |
| SPIRVGlobalRegistry *GR; | |
| const SPIRVInstrInfo *TII; | |
| MachineModuleInfo *MMI; | |
| }; | |
| } // namespace llvm | |
| #endif // LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H |