181ad6265SDimitry Andric //===- SPIRVModuleAnalysis.cpp - analysis of global instrs & regs - C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // The analysis collects instructions that should be output at the module level 1081ad6265SDimitry Andric // and performs the global register numbering. 1181ad6265SDimitry Andric // 1281ad6265SDimitry Andric // The results of this analysis are used in AsmPrinter to rename registers 1381ad6265SDimitry Andric // globally and to output required instructions at the module level. 1481ad6265SDimitry Andric // 1581ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1681ad6265SDimitry Andric 1781ad6265SDimitry Andric #include "SPIRVModuleAnalysis.h" 1881ad6265SDimitry Andric #include "SPIRV.h" 1981ad6265SDimitry Andric #include "SPIRVGlobalRegistry.h" 2081ad6265SDimitry Andric #include "SPIRVSubtarget.h" 2181ad6265SDimitry Andric #include "SPIRVTargetMachine.h" 2281ad6265SDimitry Andric #include "SPIRVUtils.h" 2381ad6265SDimitry Andric #include "TargetInfo/SPIRVTargetInfo.h" 2481ad6265SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 2581ad6265SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 2681ad6265SDimitry Andric 2781ad6265SDimitry Andric using namespace llvm; 2881ad6265SDimitry Andric 2981ad6265SDimitry Andric #define DEBUG_TYPE "spirv-module-analysis" 3081ad6265SDimitry Andric 31*753f127fSDimitry Andric static cl::opt<bool> 32*753f127fSDimitry Andric SPVDumpDeps("spv-dump-deps", 33*753f127fSDimitry Andric cl::desc("Dump MIR with SPIR-V dependencies info"), 34*753f127fSDimitry Andric cl::Optional, cl::init(false)); 35*753f127fSDimitry Andric 3681ad6265SDimitry Andric char llvm::SPIRVModuleAnalysis::ID = 0; 3781ad6265SDimitry Andric 3881ad6265SDimitry Andric namespace llvm { 3981ad6265SDimitry Andric void initializeSPIRVModuleAnalysisPass(PassRegistry &); 4081ad6265SDimitry Andric } // namespace llvm 4181ad6265SDimitry Andric 4281ad6265SDimitry Andric INITIALIZE_PASS(SPIRVModuleAnalysis, DEBUG_TYPE, "SPIRV module analysis", true, 4381ad6265SDimitry Andric true) 4481ad6265SDimitry Andric 4581ad6265SDimitry Andric // Retrieve an unsigned from an MDNode with a list of them as operands. 4681ad6265SDimitry Andric static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex, 4781ad6265SDimitry Andric unsigned DefaultVal = 0) { 4881ad6265SDimitry Andric if (MdNode && OpIndex < MdNode->getNumOperands()) { 4981ad6265SDimitry Andric const auto &Op = MdNode->getOperand(OpIndex); 5081ad6265SDimitry Andric return mdconst::extract<ConstantInt>(Op)->getZExtValue(); 5181ad6265SDimitry Andric } 5281ad6265SDimitry Andric return DefaultVal; 5381ad6265SDimitry Andric } 5481ad6265SDimitry Andric 5581ad6265SDimitry Andric void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { 5681ad6265SDimitry Andric MAI.MaxID = 0; 5781ad6265SDimitry Andric for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++) 5881ad6265SDimitry Andric MAI.MS[i].clear(); 5981ad6265SDimitry Andric MAI.RegisterAliasTable.clear(); 6081ad6265SDimitry Andric MAI.InstrsToDelete.clear(); 6181ad6265SDimitry Andric MAI.FuncNameMap.clear(); 6281ad6265SDimitry Andric MAI.GlobalVarList.clear(); 6381ad6265SDimitry Andric 6481ad6265SDimitry Andric // TODO: determine memory model and source language from the configuratoin. 6581ad6265SDimitry Andric MAI.Mem = SPIRV::MemoryModel::OpenCL; 6681ad6265SDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_C; 6781ad6265SDimitry Andric unsigned PtrSize = ST->getPointerSize(); 6881ad6265SDimitry Andric MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32 6981ad6265SDimitry Andric : PtrSize == 64 ? SPIRV::AddressingModel::Physical64 7081ad6265SDimitry Andric : SPIRV::AddressingModel::Logical; 7181ad6265SDimitry Andric // Get the OpenCL version number from metadata. 7281ad6265SDimitry Andric // TODO: support other source languages. 7381ad6265SDimitry Andric MAI.SrcLangVersion = 0; 7481ad6265SDimitry Andric if (auto VerNode = M.getNamedMetadata("opencl.ocl.version")) { 7581ad6265SDimitry Andric // Construct version literal according to OpenCL 2.2 environment spec. 7681ad6265SDimitry Andric auto VersionMD = VerNode->getOperand(0); 7781ad6265SDimitry Andric unsigned MajorNum = getMetadataUInt(VersionMD, 0, 2); 7881ad6265SDimitry Andric unsigned MinorNum = getMetadataUInt(VersionMD, 1); 7981ad6265SDimitry Andric unsigned RevNum = getMetadataUInt(VersionMD, 2); 8081ad6265SDimitry Andric MAI.SrcLangVersion = 0 | (MajorNum << 16) | (MinorNum << 8) | RevNum; 8181ad6265SDimitry Andric } 8281ad6265SDimitry Andric } 8381ad6265SDimitry Andric 8481ad6265SDimitry Andric // True if there is an instruction in the MS list with all the same operands as 8581ad6265SDimitry Andric // the given instruction has (after the given starting index). 8681ad6265SDimitry Andric // TODO: maybe it needs to check Opcodes too. 8781ad6265SDimitry Andric static bool findSameInstrInMS(const MachineInstr &A, 8881ad6265SDimitry Andric SPIRV::ModuleSectionType MSType, 8981ad6265SDimitry Andric SPIRV::ModuleAnalysisInfo &MAI, 9081ad6265SDimitry Andric bool UpdateRegAliases, 9181ad6265SDimitry Andric unsigned StartOpIndex = 0) { 9281ad6265SDimitry Andric for (const auto *B : MAI.MS[MSType]) { 9381ad6265SDimitry Andric const unsigned NumAOps = A.getNumOperands(); 9481ad6265SDimitry Andric if (NumAOps == B->getNumOperands() && A.getNumDefs() == B->getNumDefs()) { 9581ad6265SDimitry Andric bool AllOpsMatch = true; 9681ad6265SDimitry Andric for (unsigned i = StartOpIndex; i < NumAOps && AllOpsMatch; ++i) { 9781ad6265SDimitry Andric if (A.getOperand(i).isReg() && B->getOperand(i).isReg()) { 9881ad6265SDimitry Andric Register RegA = A.getOperand(i).getReg(); 9981ad6265SDimitry Andric Register RegB = B->getOperand(i).getReg(); 10081ad6265SDimitry Andric AllOpsMatch = MAI.getRegisterAlias(A.getMF(), RegA) == 10181ad6265SDimitry Andric MAI.getRegisterAlias(B->getMF(), RegB); 10281ad6265SDimitry Andric } else { 10381ad6265SDimitry Andric AllOpsMatch = A.getOperand(i).isIdenticalTo(B->getOperand(i)); 10481ad6265SDimitry Andric } 10581ad6265SDimitry Andric } 10681ad6265SDimitry Andric if (AllOpsMatch) { 10781ad6265SDimitry Andric if (UpdateRegAliases) { 10881ad6265SDimitry Andric assert(A.getOperand(0).isReg() && B->getOperand(0).isReg()); 10981ad6265SDimitry Andric Register LocalReg = A.getOperand(0).getReg(); 11081ad6265SDimitry Andric Register GlobalReg = 11181ad6265SDimitry Andric MAI.getRegisterAlias(B->getMF(), B->getOperand(0).getReg()); 11281ad6265SDimitry Andric MAI.setRegisterAlias(A.getMF(), LocalReg, GlobalReg); 11381ad6265SDimitry Andric } 11481ad6265SDimitry Andric return true; 11581ad6265SDimitry Andric } 11681ad6265SDimitry Andric } 11781ad6265SDimitry Andric } 11881ad6265SDimitry Andric return false; 11981ad6265SDimitry Andric } 12081ad6265SDimitry Andric 121*753f127fSDimitry Andric // Collect MI which defines the register in the given machine function. 122*753f127fSDimitry Andric static void collectDefInstr(Register Reg, const MachineFunction *MF, 123*753f127fSDimitry Andric SPIRV::ModuleAnalysisInfo *MAI, 124*753f127fSDimitry Andric SPIRV::ModuleSectionType MSType, 125*753f127fSDimitry Andric bool DoInsert = true) { 126*753f127fSDimitry Andric assert(MAI->hasRegisterAlias(MF, Reg) && "Cannot find register alias"); 127*753f127fSDimitry Andric MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(Reg); 128*753f127fSDimitry Andric assert(MI && "There should be an instruction that defines the register"); 129*753f127fSDimitry Andric MAI->setSkipEmission(MI); 130*753f127fSDimitry Andric if (DoInsert) 131*753f127fSDimitry Andric MAI->MS[MSType].push_back(MI); 132*753f127fSDimitry Andric } 133*753f127fSDimitry Andric 134*753f127fSDimitry Andric void SPIRVModuleAnalysis::collectGlobalEntities( 135*753f127fSDimitry Andric const std::vector<SPIRV::DTSortableEntry *> &DepsGraph, 136*753f127fSDimitry Andric SPIRV::ModuleSectionType MSType, 137*753f127fSDimitry Andric std::function<bool(const SPIRV::DTSortableEntry *)> Pred, 138*753f127fSDimitry Andric bool UsePreOrder) { 139*753f127fSDimitry Andric DenseSet<const SPIRV::DTSortableEntry *> Visited; 140*753f127fSDimitry Andric for (const auto *E : DepsGraph) { 141*753f127fSDimitry Andric std::function<void(const SPIRV::DTSortableEntry *)> RecHoistUtil; 142*753f127fSDimitry Andric // NOTE: here we prefer recursive approach over iterative because 143*753f127fSDimitry Andric // we don't expect depchains long enough to cause SO. 144*753f127fSDimitry Andric RecHoistUtil = [MSType, UsePreOrder, &Visited, &Pred, 145*753f127fSDimitry Andric &RecHoistUtil](const SPIRV::DTSortableEntry *E) { 146*753f127fSDimitry Andric if (Visited.count(E) || !Pred(E)) 147*753f127fSDimitry Andric return; 148*753f127fSDimitry Andric Visited.insert(E); 149*753f127fSDimitry Andric 150*753f127fSDimitry Andric // Traversing deps graph in post-order allows us to get rid of 151*753f127fSDimitry Andric // register aliases preprocessing. 152*753f127fSDimitry Andric // But pre-order is required for correct processing of function 153*753f127fSDimitry Andric // declaration and arguments processing. 154*753f127fSDimitry Andric if (!UsePreOrder) 155*753f127fSDimitry Andric for (auto *S : E->getDeps()) 156*753f127fSDimitry Andric RecHoistUtil(S); 157*753f127fSDimitry Andric 158*753f127fSDimitry Andric Register GlobalReg = Register::index2VirtReg(MAI.getNextID()); 159*753f127fSDimitry Andric bool IsFirst = true; 160*753f127fSDimitry Andric for (auto &U : *E) { 161*753f127fSDimitry Andric const MachineFunction *MF = U.first; 162*753f127fSDimitry Andric Register Reg = U.second; 163*753f127fSDimitry Andric MAI.setRegisterAlias(MF, Reg, GlobalReg); 164*753f127fSDimitry Andric if (!MF->getRegInfo().getUniqueVRegDef(Reg)) 165*753f127fSDimitry Andric continue; 166*753f127fSDimitry Andric collectDefInstr(Reg, MF, &MAI, MSType, IsFirst); 167*753f127fSDimitry Andric IsFirst = false; 168*753f127fSDimitry Andric if (E->getIsGV()) 169*753f127fSDimitry Andric MAI.GlobalVarList.push_back(MF->getRegInfo().getUniqueVRegDef(Reg)); 170*753f127fSDimitry Andric } 171*753f127fSDimitry Andric 172*753f127fSDimitry Andric if (UsePreOrder) 173*753f127fSDimitry Andric for (auto *S : E->getDeps()) 174*753f127fSDimitry Andric RecHoistUtil(S); 175*753f127fSDimitry Andric }; 176*753f127fSDimitry Andric RecHoistUtil(E); 177*753f127fSDimitry Andric } 178*753f127fSDimitry Andric } 179*753f127fSDimitry Andric 180*753f127fSDimitry Andric // The function initializes global register alias table for types, consts, 181*753f127fSDimitry Andric // global vars and func decls and collects these instruction for output 182*753f127fSDimitry Andric // at module level. Also it collects explicit OpExtension/OpCapability 183*753f127fSDimitry Andric // instructions. 184*753f127fSDimitry Andric void SPIRVModuleAnalysis::processDefInstrs(const Module &M) { 185*753f127fSDimitry Andric std::vector<SPIRV::DTSortableEntry *> DepsGraph; 186*753f127fSDimitry Andric 187*753f127fSDimitry Andric GR->buildDepsGraph(DepsGraph, SPVDumpDeps ? MMI : nullptr); 188*753f127fSDimitry Andric 189*753f127fSDimitry Andric collectGlobalEntities( 190*753f127fSDimitry Andric DepsGraph, SPIRV::MB_TypeConstVars, 191*753f127fSDimitry Andric [](const SPIRV::DTSortableEntry *E) { return !E->getIsFunc(); }, false); 192*753f127fSDimitry Andric 193*753f127fSDimitry Andric collectGlobalEntities( 194*753f127fSDimitry Andric DepsGraph, SPIRV::MB_ExtFuncDecls, 195*753f127fSDimitry Andric [](const SPIRV::DTSortableEntry *E) { return E->getIsFunc(); }, true); 196*753f127fSDimitry Andric } 197*753f127fSDimitry Andric 19881ad6265SDimitry Andric // Look for IDs declared with Import linkage, and map the imported name string 19981ad6265SDimitry Andric // to the register defining that variable (which will usually be the result of 20081ad6265SDimitry Andric // an OpFunction). This lets us call externally imported functions using 20181ad6265SDimitry Andric // the correct ID registers. 20281ad6265SDimitry Andric void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI, 20381ad6265SDimitry Andric const Function &F) { 20481ad6265SDimitry Andric if (MI.getOpcode() == SPIRV::OpDecorate) { 20581ad6265SDimitry Andric // If it's got Import linkage. 20681ad6265SDimitry Andric auto Dec = MI.getOperand(1).getImm(); 20781ad6265SDimitry Andric if (Dec == static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) { 20881ad6265SDimitry Andric auto Lnk = MI.getOperand(MI.getNumOperands() - 1).getImm(); 20981ad6265SDimitry Andric if (Lnk == static_cast<unsigned>(SPIRV::LinkageType::Import)) { 21081ad6265SDimitry Andric // Map imported function name to function ID register. 21181ad6265SDimitry Andric std::string Name = getStringImm(MI, 2); 21281ad6265SDimitry Andric Register Target = MI.getOperand(0).getReg(); 21381ad6265SDimitry Andric // TODO: check defs from different MFs. 21481ad6265SDimitry Andric MAI.FuncNameMap[Name] = MAI.getRegisterAlias(MI.getMF(), Target); 21581ad6265SDimitry Andric } 21681ad6265SDimitry Andric } 21781ad6265SDimitry Andric } else if (MI.getOpcode() == SPIRV::OpFunction) { 21881ad6265SDimitry Andric // Record all internal OpFunction declarations. 21981ad6265SDimitry Andric Register Reg = MI.defs().begin()->getReg(); 22081ad6265SDimitry Andric Register GlobalReg = MAI.getRegisterAlias(MI.getMF(), Reg); 22181ad6265SDimitry Andric assert(GlobalReg.isValid()); 22281ad6265SDimitry Andric // TODO: check that it does not conflict with existing entries. 22381ad6265SDimitry Andric MAI.FuncNameMap[F.getGlobalIdentifier()] = GlobalReg; 22481ad6265SDimitry Andric } 22581ad6265SDimitry Andric } 22681ad6265SDimitry Andric 22781ad6265SDimitry Andric // Collect the given instruction in the specified MS. We assume global register 22881ad6265SDimitry Andric // numbering has already occurred by this point. We can directly compare reg 22981ad6265SDimitry Andric // arguments when detecting duplicates. 23081ad6265SDimitry Andric static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI, 231*753f127fSDimitry Andric SPIRV::ModuleSectionType MSType) { 23281ad6265SDimitry Andric MAI.setSkipEmission(&MI); 233*753f127fSDimitry Andric if (findSameInstrInMS(MI, MSType, MAI, false)) 23481ad6265SDimitry Andric return; // Found a duplicate, so don't add it. 23581ad6265SDimitry Andric // No duplicates, so add it. 23681ad6265SDimitry Andric MAI.MS[MSType].push_back(&MI); 23781ad6265SDimitry Andric } 23881ad6265SDimitry Andric 23981ad6265SDimitry Andric // Some global instructions make reference to function-local ID regs, so cannot 24081ad6265SDimitry Andric // be correctly collected until these registers are globally numbered. 24181ad6265SDimitry Andric void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) { 24281ad6265SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 24381ad6265SDimitry Andric if ((*F).isDeclaration()) 24481ad6265SDimitry Andric continue; 24581ad6265SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 24681ad6265SDimitry Andric assert(MF); 24781ad6265SDimitry Andric for (MachineBasicBlock &MBB : *MF) 24881ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 24981ad6265SDimitry Andric if (MAI.getSkipEmission(&MI)) 25081ad6265SDimitry Andric continue; 25181ad6265SDimitry Andric const unsigned OpCode = MI.getOpcode(); 25281ad6265SDimitry Andric if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) { 25381ad6265SDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames); 25481ad6265SDimitry Andric } else if (OpCode == SPIRV::OpEntryPoint) { 25581ad6265SDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints); 25681ad6265SDimitry Andric } else if (TII->isDecorationInstr(MI)) { 25781ad6265SDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_Annotations); 25881ad6265SDimitry Andric collectFuncNames(MI, *F); 25981ad6265SDimitry Andric } else if (OpCode == SPIRV::OpFunction) { 26081ad6265SDimitry Andric collectFuncNames(MI, *F); 26181ad6265SDimitry Andric } 26281ad6265SDimitry Andric } 26381ad6265SDimitry Andric } 26481ad6265SDimitry Andric } 26581ad6265SDimitry Andric 26681ad6265SDimitry Andric // Number registers in all functions globally from 0 onwards and store 26781ad6265SDimitry Andric // the result in global register alias table. 26881ad6265SDimitry Andric void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) { 26981ad6265SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 27081ad6265SDimitry Andric if ((*F).isDeclaration()) 27181ad6265SDimitry Andric continue; 27281ad6265SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 27381ad6265SDimitry Andric assert(MF); 27481ad6265SDimitry Andric for (MachineBasicBlock &MBB : *MF) { 27581ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 27681ad6265SDimitry Andric for (MachineOperand &Op : MI.operands()) { 27781ad6265SDimitry Andric if (!Op.isReg()) 27881ad6265SDimitry Andric continue; 27981ad6265SDimitry Andric Register Reg = Op.getReg(); 28081ad6265SDimitry Andric if (MAI.hasRegisterAlias(MF, Reg)) 28181ad6265SDimitry Andric continue; 28281ad6265SDimitry Andric Register NewReg = Register::index2VirtReg(MAI.getNextID()); 28381ad6265SDimitry Andric MAI.setRegisterAlias(MF, Reg, NewReg); 28481ad6265SDimitry Andric } 28581ad6265SDimitry Andric } 28681ad6265SDimitry Andric } 28781ad6265SDimitry Andric } 28881ad6265SDimitry Andric } 28981ad6265SDimitry Andric 29081ad6265SDimitry Andric struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI; 29181ad6265SDimitry Andric 29281ad6265SDimitry Andric void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const { 29381ad6265SDimitry Andric AU.addRequired<TargetPassConfig>(); 29481ad6265SDimitry Andric AU.addRequired<MachineModuleInfoWrapperPass>(); 29581ad6265SDimitry Andric } 29681ad6265SDimitry Andric 29781ad6265SDimitry Andric bool SPIRVModuleAnalysis::runOnModule(Module &M) { 29881ad6265SDimitry Andric SPIRVTargetMachine &TM = 29981ad6265SDimitry Andric getAnalysis<TargetPassConfig>().getTM<SPIRVTargetMachine>(); 30081ad6265SDimitry Andric ST = TM.getSubtargetImpl(); 30181ad6265SDimitry Andric GR = ST->getSPIRVGlobalRegistry(); 30281ad6265SDimitry Andric TII = ST->getInstrInfo(); 30381ad6265SDimitry Andric 30481ad6265SDimitry Andric MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI(); 30581ad6265SDimitry Andric 30681ad6265SDimitry Andric setBaseInfo(M); 30781ad6265SDimitry Andric 30881ad6265SDimitry Andric // TODO: Process type/const/global var/func decl instructions, number their 30981ad6265SDimitry Andric // destination registers from 0 to N, collect Extensions and Capabilities. 310*753f127fSDimitry Andric processDefInstrs(M); 31181ad6265SDimitry Andric 31281ad6265SDimitry Andric // Number rest of registers from N+1 onwards. 31381ad6265SDimitry Andric numberRegistersGlobally(M); 31481ad6265SDimitry Andric 31581ad6265SDimitry Andric // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions. 31681ad6265SDimitry Andric processOtherInstrs(M); 31781ad6265SDimitry Andric 31881ad6265SDimitry Andric return false; 31981ad6265SDimitry Andric } 320