1*81ad6265SDimitry Andric //===- SPIRVModuleAnalysis.cpp - analysis of global instrs & regs - C++ -*-===// 2*81ad6265SDimitry Andric // 3*81ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*81ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*81ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*81ad6265SDimitry Andric // 7*81ad6265SDimitry Andric //===----------------------------------------------------------------------===// 8*81ad6265SDimitry Andric // 9*81ad6265SDimitry Andric // The analysis collects instructions that should be output at the module level 10*81ad6265SDimitry Andric // and performs the global register numbering. 11*81ad6265SDimitry Andric // 12*81ad6265SDimitry Andric // The results of this analysis are used in AsmPrinter to rename registers 13*81ad6265SDimitry Andric // globally and to output required instructions at the module level. 14*81ad6265SDimitry Andric // 15*81ad6265SDimitry Andric //===----------------------------------------------------------------------===// 16*81ad6265SDimitry Andric 17*81ad6265SDimitry Andric #include "SPIRVModuleAnalysis.h" 18*81ad6265SDimitry Andric #include "SPIRV.h" 19*81ad6265SDimitry Andric #include "SPIRVGlobalRegistry.h" 20*81ad6265SDimitry Andric #include "SPIRVSubtarget.h" 21*81ad6265SDimitry Andric #include "SPIRVTargetMachine.h" 22*81ad6265SDimitry Andric #include "SPIRVUtils.h" 23*81ad6265SDimitry Andric #include "TargetInfo/SPIRVTargetInfo.h" 24*81ad6265SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 25*81ad6265SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 26*81ad6265SDimitry Andric 27*81ad6265SDimitry Andric using namespace llvm; 28*81ad6265SDimitry Andric 29*81ad6265SDimitry Andric #define DEBUG_TYPE "spirv-module-analysis" 30*81ad6265SDimitry Andric 31*81ad6265SDimitry Andric char llvm::SPIRVModuleAnalysis::ID = 0; 32*81ad6265SDimitry Andric 33*81ad6265SDimitry Andric namespace llvm { 34*81ad6265SDimitry Andric void initializeSPIRVModuleAnalysisPass(PassRegistry &); 35*81ad6265SDimitry Andric } // namespace llvm 36*81ad6265SDimitry Andric 37*81ad6265SDimitry Andric INITIALIZE_PASS(SPIRVModuleAnalysis, DEBUG_TYPE, "SPIRV module analysis", true, 38*81ad6265SDimitry Andric true) 39*81ad6265SDimitry Andric 40*81ad6265SDimitry Andric // Retrieve an unsigned from an MDNode with a list of them as operands. 41*81ad6265SDimitry Andric static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex, 42*81ad6265SDimitry Andric unsigned DefaultVal = 0) { 43*81ad6265SDimitry Andric if (MdNode && OpIndex < MdNode->getNumOperands()) { 44*81ad6265SDimitry Andric const auto &Op = MdNode->getOperand(OpIndex); 45*81ad6265SDimitry Andric return mdconst::extract<ConstantInt>(Op)->getZExtValue(); 46*81ad6265SDimitry Andric } 47*81ad6265SDimitry Andric return DefaultVal; 48*81ad6265SDimitry Andric } 49*81ad6265SDimitry Andric 50*81ad6265SDimitry Andric void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { 51*81ad6265SDimitry Andric MAI.MaxID = 0; 52*81ad6265SDimitry Andric for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++) 53*81ad6265SDimitry Andric MAI.MS[i].clear(); 54*81ad6265SDimitry Andric MAI.RegisterAliasTable.clear(); 55*81ad6265SDimitry Andric MAI.InstrsToDelete.clear(); 56*81ad6265SDimitry Andric MAI.FuncNameMap.clear(); 57*81ad6265SDimitry Andric MAI.GlobalVarList.clear(); 58*81ad6265SDimitry Andric 59*81ad6265SDimitry Andric // TODO: determine memory model and source language from the configuratoin. 60*81ad6265SDimitry Andric MAI.Mem = SPIRV::MemoryModel::OpenCL; 61*81ad6265SDimitry Andric MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_C; 62*81ad6265SDimitry Andric unsigned PtrSize = ST->getPointerSize(); 63*81ad6265SDimitry Andric MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32 64*81ad6265SDimitry Andric : PtrSize == 64 ? SPIRV::AddressingModel::Physical64 65*81ad6265SDimitry Andric : SPIRV::AddressingModel::Logical; 66*81ad6265SDimitry Andric // Get the OpenCL version number from metadata. 67*81ad6265SDimitry Andric // TODO: support other source languages. 68*81ad6265SDimitry Andric MAI.SrcLangVersion = 0; 69*81ad6265SDimitry Andric if (auto VerNode = M.getNamedMetadata("opencl.ocl.version")) { 70*81ad6265SDimitry Andric // Construct version literal according to OpenCL 2.2 environment spec. 71*81ad6265SDimitry Andric auto VersionMD = VerNode->getOperand(0); 72*81ad6265SDimitry Andric unsigned MajorNum = getMetadataUInt(VersionMD, 0, 2); 73*81ad6265SDimitry Andric unsigned MinorNum = getMetadataUInt(VersionMD, 1); 74*81ad6265SDimitry Andric unsigned RevNum = getMetadataUInt(VersionMD, 2); 75*81ad6265SDimitry Andric MAI.SrcLangVersion = 0 | (MajorNum << 16) | (MinorNum << 8) | RevNum; 76*81ad6265SDimitry Andric } 77*81ad6265SDimitry Andric } 78*81ad6265SDimitry Andric 79*81ad6265SDimitry Andric // True if there is an instruction in the MS list with all the same operands as 80*81ad6265SDimitry Andric // the given instruction has (after the given starting index). 81*81ad6265SDimitry Andric // TODO: maybe it needs to check Opcodes too. 82*81ad6265SDimitry Andric static bool findSameInstrInMS(const MachineInstr &A, 83*81ad6265SDimitry Andric SPIRV::ModuleSectionType MSType, 84*81ad6265SDimitry Andric SPIRV::ModuleAnalysisInfo &MAI, 85*81ad6265SDimitry Andric bool UpdateRegAliases, 86*81ad6265SDimitry Andric unsigned StartOpIndex = 0) { 87*81ad6265SDimitry Andric for (const auto *B : MAI.MS[MSType]) { 88*81ad6265SDimitry Andric const unsigned NumAOps = A.getNumOperands(); 89*81ad6265SDimitry Andric if (NumAOps == B->getNumOperands() && A.getNumDefs() == B->getNumDefs()) { 90*81ad6265SDimitry Andric bool AllOpsMatch = true; 91*81ad6265SDimitry Andric for (unsigned i = StartOpIndex; i < NumAOps && AllOpsMatch; ++i) { 92*81ad6265SDimitry Andric if (A.getOperand(i).isReg() && B->getOperand(i).isReg()) { 93*81ad6265SDimitry Andric Register RegA = A.getOperand(i).getReg(); 94*81ad6265SDimitry Andric Register RegB = B->getOperand(i).getReg(); 95*81ad6265SDimitry Andric AllOpsMatch = MAI.getRegisterAlias(A.getMF(), RegA) == 96*81ad6265SDimitry Andric MAI.getRegisterAlias(B->getMF(), RegB); 97*81ad6265SDimitry Andric } else { 98*81ad6265SDimitry Andric AllOpsMatch = A.getOperand(i).isIdenticalTo(B->getOperand(i)); 99*81ad6265SDimitry Andric } 100*81ad6265SDimitry Andric } 101*81ad6265SDimitry Andric if (AllOpsMatch) { 102*81ad6265SDimitry Andric if (UpdateRegAliases) { 103*81ad6265SDimitry Andric assert(A.getOperand(0).isReg() && B->getOperand(0).isReg()); 104*81ad6265SDimitry Andric Register LocalReg = A.getOperand(0).getReg(); 105*81ad6265SDimitry Andric Register GlobalReg = 106*81ad6265SDimitry Andric MAI.getRegisterAlias(B->getMF(), B->getOperand(0).getReg()); 107*81ad6265SDimitry Andric MAI.setRegisterAlias(A.getMF(), LocalReg, GlobalReg); 108*81ad6265SDimitry Andric } 109*81ad6265SDimitry Andric return true; 110*81ad6265SDimitry Andric } 111*81ad6265SDimitry Andric } 112*81ad6265SDimitry Andric } 113*81ad6265SDimitry Andric return false; 114*81ad6265SDimitry Andric } 115*81ad6265SDimitry Andric 116*81ad6265SDimitry Andric // Look for IDs declared with Import linkage, and map the imported name string 117*81ad6265SDimitry Andric // to the register defining that variable (which will usually be the result of 118*81ad6265SDimitry Andric // an OpFunction). This lets us call externally imported functions using 119*81ad6265SDimitry Andric // the correct ID registers. 120*81ad6265SDimitry Andric void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI, 121*81ad6265SDimitry Andric const Function &F) { 122*81ad6265SDimitry Andric if (MI.getOpcode() == SPIRV::OpDecorate) { 123*81ad6265SDimitry Andric // If it's got Import linkage. 124*81ad6265SDimitry Andric auto Dec = MI.getOperand(1).getImm(); 125*81ad6265SDimitry Andric if (Dec == static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) { 126*81ad6265SDimitry Andric auto Lnk = MI.getOperand(MI.getNumOperands() - 1).getImm(); 127*81ad6265SDimitry Andric if (Lnk == static_cast<unsigned>(SPIRV::LinkageType::Import)) { 128*81ad6265SDimitry Andric // Map imported function name to function ID register. 129*81ad6265SDimitry Andric std::string Name = getStringImm(MI, 2); 130*81ad6265SDimitry Andric Register Target = MI.getOperand(0).getReg(); 131*81ad6265SDimitry Andric // TODO: check defs from different MFs. 132*81ad6265SDimitry Andric MAI.FuncNameMap[Name] = MAI.getRegisterAlias(MI.getMF(), Target); 133*81ad6265SDimitry Andric } 134*81ad6265SDimitry Andric } 135*81ad6265SDimitry Andric } else if (MI.getOpcode() == SPIRV::OpFunction) { 136*81ad6265SDimitry Andric // Record all internal OpFunction declarations. 137*81ad6265SDimitry Andric Register Reg = MI.defs().begin()->getReg(); 138*81ad6265SDimitry Andric Register GlobalReg = MAI.getRegisterAlias(MI.getMF(), Reg); 139*81ad6265SDimitry Andric assert(GlobalReg.isValid()); 140*81ad6265SDimitry Andric // TODO: check that it does not conflict with existing entries. 141*81ad6265SDimitry Andric MAI.FuncNameMap[F.getGlobalIdentifier()] = GlobalReg; 142*81ad6265SDimitry Andric } 143*81ad6265SDimitry Andric } 144*81ad6265SDimitry Andric 145*81ad6265SDimitry Andric // Collect the given instruction in the specified MS. We assume global register 146*81ad6265SDimitry Andric // numbering has already occurred by this point. We can directly compare reg 147*81ad6265SDimitry Andric // arguments when detecting duplicates. 148*81ad6265SDimitry Andric static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI, 149*81ad6265SDimitry Andric SPIRV::ModuleSectionType MSType, 150*81ad6265SDimitry Andric bool IsConstOrType = false) { 151*81ad6265SDimitry Andric MAI.setSkipEmission(&MI); 152*81ad6265SDimitry Andric if (findSameInstrInMS(MI, MSType, MAI, IsConstOrType, IsConstOrType ? 1 : 0)) 153*81ad6265SDimitry Andric return; // Found a duplicate, so don't add it. 154*81ad6265SDimitry Andric // No duplicates, so add it. 155*81ad6265SDimitry Andric MAI.MS[MSType].push_back(&MI); 156*81ad6265SDimitry Andric } 157*81ad6265SDimitry Andric 158*81ad6265SDimitry Andric // Some global instructions make reference to function-local ID regs, so cannot 159*81ad6265SDimitry Andric // be correctly collected until these registers are globally numbered. 160*81ad6265SDimitry Andric void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) { 161*81ad6265SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 162*81ad6265SDimitry Andric if ((*F).isDeclaration()) 163*81ad6265SDimitry Andric continue; 164*81ad6265SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 165*81ad6265SDimitry Andric assert(MF); 166*81ad6265SDimitry Andric unsigned FCounter = 0; 167*81ad6265SDimitry Andric for (MachineBasicBlock &MBB : *MF) 168*81ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 169*81ad6265SDimitry Andric if (MI.getOpcode() == SPIRV::OpFunction) 170*81ad6265SDimitry Andric FCounter++; 171*81ad6265SDimitry Andric if (MAI.getSkipEmission(&MI)) 172*81ad6265SDimitry Andric continue; 173*81ad6265SDimitry Andric const unsigned OpCode = MI.getOpcode(); 174*81ad6265SDimitry Andric const bool IsFuncOrParm = 175*81ad6265SDimitry Andric OpCode == SPIRV::OpFunction || OpCode == SPIRV::OpFunctionParameter; 176*81ad6265SDimitry Andric const bool IsConstOrType = 177*81ad6265SDimitry Andric TII->isConstantInstr(MI) || TII->isTypeDeclInstr(MI); 178*81ad6265SDimitry Andric if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) { 179*81ad6265SDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames); 180*81ad6265SDimitry Andric } else if (OpCode == SPIRV::OpEntryPoint) { 181*81ad6265SDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints); 182*81ad6265SDimitry Andric } else if (TII->isDecorationInstr(MI)) { 183*81ad6265SDimitry Andric collectOtherInstr(MI, MAI, SPIRV::MB_Annotations); 184*81ad6265SDimitry Andric collectFuncNames(MI, *F); 185*81ad6265SDimitry Andric } else if (IsConstOrType || (FCounter > 1 && IsFuncOrParm)) { 186*81ad6265SDimitry Andric // Now OpSpecConstant*s are not in DT, 187*81ad6265SDimitry Andric // but they need to be collected anyway. 188*81ad6265SDimitry Andric enum SPIRV::ModuleSectionType Type = 189*81ad6265SDimitry Andric IsFuncOrParm ? SPIRV::MB_ExtFuncDecls : SPIRV::MB_TypeConstVars; 190*81ad6265SDimitry Andric collectOtherInstr(MI, MAI, Type, IsConstOrType); 191*81ad6265SDimitry Andric } else if (OpCode == SPIRV::OpFunction) { 192*81ad6265SDimitry Andric collectFuncNames(MI, *F); 193*81ad6265SDimitry Andric } 194*81ad6265SDimitry Andric } 195*81ad6265SDimitry Andric } 196*81ad6265SDimitry Andric } 197*81ad6265SDimitry Andric 198*81ad6265SDimitry Andric // Number registers in all functions globally from 0 onwards and store 199*81ad6265SDimitry Andric // the result in global register alias table. 200*81ad6265SDimitry Andric void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) { 201*81ad6265SDimitry Andric for (auto F = M.begin(), E = M.end(); F != E; ++F) { 202*81ad6265SDimitry Andric if ((*F).isDeclaration()) 203*81ad6265SDimitry Andric continue; 204*81ad6265SDimitry Andric MachineFunction *MF = MMI->getMachineFunction(*F); 205*81ad6265SDimitry Andric assert(MF); 206*81ad6265SDimitry Andric for (MachineBasicBlock &MBB : *MF) { 207*81ad6265SDimitry Andric for (MachineInstr &MI : MBB) { 208*81ad6265SDimitry Andric for (MachineOperand &Op : MI.operands()) { 209*81ad6265SDimitry Andric if (!Op.isReg()) 210*81ad6265SDimitry Andric continue; 211*81ad6265SDimitry Andric Register Reg = Op.getReg(); 212*81ad6265SDimitry Andric if (MAI.hasRegisterAlias(MF, Reg)) 213*81ad6265SDimitry Andric continue; 214*81ad6265SDimitry Andric Register NewReg = Register::index2VirtReg(MAI.getNextID()); 215*81ad6265SDimitry Andric MAI.setRegisterAlias(MF, Reg, NewReg); 216*81ad6265SDimitry Andric } 217*81ad6265SDimitry Andric } 218*81ad6265SDimitry Andric } 219*81ad6265SDimitry Andric } 220*81ad6265SDimitry Andric } 221*81ad6265SDimitry Andric 222*81ad6265SDimitry Andric struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI; 223*81ad6265SDimitry Andric 224*81ad6265SDimitry Andric void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const { 225*81ad6265SDimitry Andric AU.addRequired<TargetPassConfig>(); 226*81ad6265SDimitry Andric AU.addRequired<MachineModuleInfoWrapperPass>(); 227*81ad6265SDimitry Andric } 228*81ad6265SDimitry Andric 229*81ad6265SDimitry Andric bool SPIRVModuleAnalysis::runOnModule(Module &M) { 230*81ad6265SDimitry Andric SPIRVTargetMachine &TM = 231*81ad6265SDimitry Andric getAnalysis<TargetPassConfig>().getTM<SPIRVTargetMachine>(); 232*81ad6265SDimitry Andric ST = TM.getSubtargetImpl(); 233*81ad6265SDimitry Andric GR = ST->getSPIRVGlobalRegistry(); 234*81ad6265SDimitry Andric TII = ST->getInstrInfo(); 235*81ad6265SDimitry Andric 236*81ad6265SDimitry Andric MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI(); 237*81ad6265SDimitry Andric 238*81ad6265SDimitry Andric setBaseInfo(M); 239*81ad6265SDimitry Andric 240*81ad6265SDimitry Andric // TODO: Process type/const/global var/func decl instructions, number their 241*81ad6265SDimitry Andric // destination registers from 0 to N, collect Extensions and Capabilities. 242*81ad6265SDimitry Andric 243*81ad6265SDimitry Andric // Number rest of registers from N+1 onwards. 244*81ad6265SDimitry Andric numberRegistersGlobally(M); 245*81ad6265SDimitry Andric 246*81ad6265SDimitry Andric // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions. 247*81ad6265SDimitry Andric processOtherInstrs(M); 248*81ad6265SDimitry Andric 249*81ad6265SDimitry Andric return false; 250*81ad6265SDimitry Andric } 251