diff options
Diffstat (limited to 'llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp')
-rw-r--r-- | llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp new file mode 100644 index 000000000000..fa78dd7942c6 --- /dev/null +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp @@ -0,0 +1,250 @@ +//===- SPIRVModuleAnalysis.cpp - 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. +// +// The results of this analysis are used in AsmPrinter to rename registers +// globally and to output required instructions at the module level. +// +//===----------------------------------------------------------------------===// + +#include "SPIRVModuleAnalysis.h" +#include "SPIRV.h" +#include "SPIRVGlobalRegistry.h" +#include "SPIRVSubtarget.h" +#include "SPIRVTargetMachine.h" +#include "SPIRVUtils.h" +#include "TargetInfo/SPIRVTargetInfo.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetPassConfig.h" + +using namespace llvm; + +#define DEBUG_TYPE "spirv-module-analysis" + +char llvm::SPIRVModuleAnalysis::ID = 0; + +namespace llvm { +void initializeSPIRVModuleAnalysisPass(PassRegistry &); +} // namespace llvm + +INITIALIZE_PASS(SPIRVModuleAnalysis, DEBUG_TYPE, "SPIRV module analysis", true, + true) + +// Retrieve an unsigned from an MDNode with a list of them as operands. +static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex, + unsigned DefaultVal = 0) { + if (MdNode && OpIndex < MdNode->getNumOperands()) { + const auto &Op = MdNode->getOperand(OpIndex); + return mdconst::extract<ConstantInt>(Op)->getZExtValue(); + } + return DefaultVal; +} + +void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { + MAI.MaxID = 0; + for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++) + MAI.MS[i].clear(); + MAI.RegisterAliasTable.clear(); + MAI.InstrsToDelete.clear(); + MAI.FuncNameMap.clear(); + MAI.GlobalVarList.clear(); + + // TODO: determine memory model and source language from the configuratoin. + MAI.Mem = SPIRV::MemoryModel::OpenCL; + MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_C; + unsigned PtrSize = ST->getPointerSize(); + MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32 + : PtrSize == 64 ? SPIRV::AddressingModel::Physical64 + : SPIRV::AddressingModel::Logical; + // Get the OpenCL version number from metadata. + // TODO: support other source languages. + MAI.SrcLangVersion = 0; + if (auto VerNode = M.getNamedMetadata("opencl.ocl.version")) { + // Construct version literal according to OpenCL 2.2 environment spec. + auto VersionMD = VerNode->getOperand(0); + unsigned MajorNum = getMetadataUInt(VersionMD, 0, 2); + unsigned MinorNum = getMetadataUInt(VersionMD, 1); + unsigned RevNum = getMetadataUInt(VersionMD, 2); + MAI.SrcLangVersion = 0 | (MajorNum << 16) | (MinorNum << 8) | RevNum; + } +} + +// True if there is an instruction in the MS list with all the same operands as +// the given instruction has (after the given starting index). +// TODO: maybe it needs to check Opcodes too. +static bool findSameInstrInMS(const MachineInstr &A, + SPIRV::ModuleSectionType MSType, + SPIRV::ModuleAnalysisInfo &MAI, + bool UpdateRegAliases, + unsigned StartOpIndex = 0) { + for (const auto *B : MAI.MS[MSType]) { + const unsigned NumAOps = A.getNumOperands(); + if (NumAOps == B->getNumOperands() && A.getNumDefs() == B->getNumDefs()) { + bool AllOpsMatch = true; + for (unsigned i = StartOpIndex; i < NumAOps && AllOpsMatch; ++i) { + if (A.getOperand(i).isReg() && B->getOperand(i).isReg()) { + Register RegA = A.getOperand(i).getReg(); + Register RegB = B->getOperand(i).getReg(); + AllOpsMatch = MAI.getRegisterAlias(A.getMF(), RegA) == + MAI.getRegisterAlias(B->getMF(), RegB); + } else { + AllOpsMatch = A.getOperand(i).isIdenticalTo(B->getOperand(i)); + } + } + if (AllOpsMatch) { + if (UpdateRegAliases) { + assert(A.getOperand(0).isReg() && B->getOperand(0).isReg()); + Register LocalReg = A.getOperand(0).getReg(); + Register GlobalReg = + MAI.getRegisterAlias(B->getMF(), B->getOperand(0).getReg()); + MAI.setRegisterAlias(A.getMF(), LocalReg, GlobalReg); + } + return true; + } + } + } + return false; +} + +// Look for IDs declared with Import linkage, and map the imported name string +// to the register defining that variable (which will usually be the result of +// an OpFunction). This lets us call externally imported functions using +// the correct ID registers. +void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI, + const Function &F) { + if (MI.getOpcode() == SPIRV::OpDecorate) { + // If it's got Import linkage. + auto Dec = MI.getOperand(1).getImm(); + if (Dec == static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) { + auto Lnk = MI.getOperand(MI.getNumOperands() - 1).getImm(); + if (Lnk == static_cast<unsigned>(SPIRV::LinkageType::Import)) { + // Map imported function name to function ID register. + std::string Name = getStringImm(MI, 2); + Register Target = MI.getOperand(0).getReg(); + // TODO: check defs from different MFs. + MAI.FuncNameMap[Name] = MAI.getRegisterAlias(MI.getMF(), Target); + } + } + } else if (MI.getOpcode() == SPIRV::OpFunction) { + // Record all internal OpFunction declarations. + Register Reg = MI.defs().begin()->getReg(); + Register GlobalReg = MAI.getRegisterAlias(MI.getMF(), Reg); + assert(GlobalReg.isValid()); + // TODO: check that it does not conflict with existing entries. + MAI.FuncNameMap[F.getGlobalIdentifier()] = GlobalReg; + } +} + +// Collect the given instruction in the specified MS. We assume global register +// numbering has already occurred by this point. We can directly compare reg +// arguments when detecting duplicates. +static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI, + SPIRV::ModuleSectionType MSType, + bool IsConstOrType = false) { + MAI.setSkipEmission(&MI); + if (findSameInstrInMS(MI, MSType, MAI, IsConstOrType, IsConstOrType ? 1 : 0)) + return; // Found a duplicate, so don't add it. + // No duplicates, so add it. + MAI.MS[MSType].push_back(&MI); +} + +// Some global instructions make reference to function-local ID regs, so cannot +// be correctly collected until these registers are globally numbered. +void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) { + for (auto F = M.begin(), E = M.end(); F != E; ++F) { + if ((*F).isDeclaration()) + continue; + MachineFunction *MF = MMI->getMachineFunction(*F); + assert(MF); + unsigned FCounter = 0; + for (MachineBasicBlock &MBB : *MF) + for (MachineInstr &MI : MBB) { + if (MI.getOpcode() == SPIRV::OpFunction) + FCounter++; + if (MAI.getSkipEmission(&MI)) + continue; + const unsigned OpCode = MI.getOpcode(); + const bool IsFuncOrParm = + OpCode == SPIRV::OpFunction || OpCode == SPIRV::OpFunctionParameter; + const bool IsConstOrType = + TII->isConstantInstr(MI) || TII->isTypeDeclInstr(MI); + if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) { + collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames); + } else if (OpCode == SPIRV::OpEntryPoint) { + collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints); + } else if (TII->isDecorationInstr(MI)) { + collectOtherInstr(MI, MAI, SPIRV::MB_Annotations); + collectFuncNames(MI, *F); + } else if (IsConstOrType || (FCounter > 1 && IsFuncOrParm)) { + // Now OpSpecConstant*s are not in DT, + // but they need to be collected anyway. + enum SPIRV::ModuleSectionType Type = + IsFuncOrParm ? SPIRV::MB_ExtFuncDecls : SPIRV::MB_TypeConstVars; + collectOtherInstr(MI, MAI, Type, IsConstOrType); + } else if (OpCode == SPIRV::OpFunction) { + collectFuncNames(MI, *F); + } + } + } +} + +// Number registers in all functions globally from 0 onwards and store +// the result in global register alias table. +void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) { + for (auto F = M.begin(), E = M.end(); F != E; ++F) { + if ((*F).isDeclaration()) + continue; + MachineFunction *MF = MMI->getMachineFunction(*F); + assert(MF); + for (MachineBasicBlock &MBB : *MF) { + for (MachineInstr &MI : MBB) { + for (MachineOperand &Op : MI.operands()) { + if (!Op.isReg()) + continue; + Register Reg = Op.getReg(); + if (MAI.hasRegisterAlias(MF, Reg)) + continue; + Register NewReg = Register::index2VirtReg(MAI.getNextID()); + MAI.setRegisterAlias(MF, Reg, NewReg); + } + } + } + } +} + +struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI; + +void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired<TargetPassConfig>(); + AU.addRequired<MachineModuleInfoWrapperPass>(); +} + +bool SPIRVModuleAnalysis::runOnModule(Module &M) { + SPIRVTargetMachine &TM = + getAnalysis<TargetPassConfig>().getTM<SPIRVTargetMachine>(); + ST = TM.getSubtargetImpl(); + GR = ST->getSPIRVGlobalRegistry(); + TII = ST->getInstrInfo(); + + MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI(); + + setBaseInfo(M); + + // TODO: Process type/const/global var/func decl instructions, number their + // destination registers from 0 to N, collect Extensions and Capabilities. + + // Number rest of registers from N+1 onwards. + numberRegistersGlobally(M); + + // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions. + processOtherInstrs(M); + + return false; +} |