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