181ad6265SDimitry Andric //===-- SPIRVAsmPrinter.cpp - SPIR-V LLVM assembly writer ------*- 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 // This file contains a printer that converts from our internal representation 1081ad6265SDimitry Andric // of machine-dependent LLVM code to the SPIR-V assembly language. 1181ad6265SDimitry Andric // 1281ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1381ad6265SDimitry Andric 1481ad6265SDimitry Andric #include "MCTargetDesc/SPIRVInstPrinter.h" 1581ad6265SDimitry Andric #include "SPIRV.h" 1681ad6265SDimitry Andric #include "SPIRVInstrInfo.h" 1781ad6265SDimitry Andric #include "SPIRVMCInstLower.h" 1881ad6265SDimitry Andric #include "SPIRVModuleAnalysis.h" 1981ad6265SDimitry Andric #include "SPIRVSubtarget.h" 2081ad6265SDimitry Andric #include "SPIRVTargetMachine.h" 2181ad6265SDimitry Andric #include "SPIRVUtils.h" 2281ad6265SDimitry Andric #include "TargetInfo/SPIRVTargetInfo.h" 2381ad6265SDimitry Andric #include "llvm/ADT/DenseMap.h" 24fcaf7f86SDimitry Andric #include "llvm/Analysis/ValueTracking.h" 2581ad6265SDimitry Andric #include "llvm/CodeGen/AsmPrinter.h" 2681ad6265SDimitry Andric #include "llvm/CodeGen/MachineConstantPool.h" 2781ad6265SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 2881ad6265SDimitry Andric #include "llvm/CodeGen/MachineInstr.h" 2981ad6265SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 3081ad6265SDimitry Andric #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" 3181ad6265SDimitry Andric #include "llvm/MC/MCAsmInfo.h" 32*0fca6ea1SDimitry Andric #include "llvm/MC/MCAssembler.h" 3381ad6265SDimitry Andric #include "llvm/MC/MCInst.h" 34*0fca6ea1SDimitry Andric #include "llvm/MC/MCObjectStreamer.h" 35*0fca6ea1SDimitry Andric #include "llvm/MC/MCSPIRVObjectWriter.h" 3681ad6265SDimitry Andric #include "llvm/MC/MCStreamer.h" 3781ad6265SDimitry Andric #include "llvm/MC/MCSymbol.h" 3881ad6265SDimitry Andric #include "llvm/MC/TargetRegistry.h" 3981ad6265SDimitry Andric #include "llvm/Support/raw_ostream.h" 4081ad6265SDimitry Andric 4181ad6265SDimitry Andric using namespace llvm; 4281ad6265SDimitry Andric 4381ad6265SDimitry Andric #define DEBUG_TYPE "asm-printer" 4481ad6265SDimitry Andric 4581ad6265SDimitry Andric namespace { 4681ad6265SDimitry Andric class SPIRVAsmPrinter : public AsmPrinter { 47*0fca6ea1SDimitry Andric unsigned NLabels = 0; 48*0fca6ea1SDimitry Andric 4981ad6265SDimitry Andric public: 5081ad6265SDimitry Andric explicit SPIRVAsmPrinter(TargetMachine &TM, 5181ad6265SDimitry Andric std::unique_ptr<MCStreamer> Streamer) 5281ad6265SDimitry Andric : AsmPrinter(TM, std::move(Streamer)), ST(nullptr), TII(nullptr) {} 5381ad6265SDimitry Andric bool ModuleSectionsEmitted; 5481ad6265SDimitry Andric const SPIRVSubtarget *ST; 5581ad6265SDimitry Andric const SPIRVInstrInfo *TII; 5681ad6265SDimitry Andric 5781ad6265SDimitry Andric StringRef getPassName() const override { return "SPIRV Assembly Printer"; } 5881ad6265SDimitry Andric void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O); 5981ad6265SDimitry Andric bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, 6081ad6265SDimitry Andric const char *ExtraCode, raw_ostream &O) override; 6181ad6265SDimitry Andric 6281ad6265SDimitry Andric void outputMCInst(MCInst &Inst); 6381ad6265SDimitry Andric void outputInstruction(const MachineInstr *MI); 6481ad6265SDimitry Andric void outputModuleSection(SPIRV::ModuleSectionType MSType); 65bdd1243dSDimitry Andric void outputGlobalRequirements(); 6681ad6265SDimitry Andric void outputEntryPoints(); 6781ad6265SDimitry Andric void outputDebugSourceAndStrings(const Module &M); 68fcaf7f86SDimitry Andric void outputOpExtInstImports(const Module &M); 6981ad6265SDimitry Andric void outputOpMemoryModel(); 7081ad6265SDimitry Andric void outputOpFunctionEnd(); 7181ad6265SDimitry Andric void outputExtFuncDecls(); 72fcaf7f86SDimitry Andric void outputExecutionModeFromMDNode(Register Reg, MDNode *Node, 73*0fca6ea1SDimitry Andric SPIRV::ExecutionMode::ExecutionMode EM, 74*0fca6ea1SDimitry Andric unsigned ExpectMDOps, int64_t DefVal); 755f757f3fSDimitry Andric void outputExecutionModeFromNumthreadsAttribute( 765f757f3fSDimitry Andric const Register &Reg, const Attribute &Attr, 775f757f3fSDimitry Andric SPIRV::ExecutionMode::ExecutionMode EM); 78fcaf7f86SDimitry Andric void outputExecutionMode(const Module &M); 79fcaf7f86SDimitry Andric void outputAnnotations(const Module &M); 8081ad6265SDimitry Andric void outputModuleSections(); 8181ad6265SDimitry Andric 8281ad6265SDimitry Andric void emitInstruction(const MachineInstr *MI) override; 8381ad6265SDimitry Andric void emitFunctionEntryLabel() override {} 8481ad6265SDimitry Andric void emitFunctionHeader() override; 8581ad6265SDimitry Andric void emitFunctionBodyStart() override {} 8681ad6265SDimitry Andric void emitFunctionBodyEnd() override; 8781ad6265SDimitry Andric void emitBasicBlockStart(const MachineBasicBlock &MBB) override; 8881ad6265SDimitry Andric void emitBasicBlockEnd(const MachineBasicBlock &MBB) override {} 8981ad6265SDimitry Andric void emitGlobalVariable(const GlobalVariable *GV) override {} 9081ad6265SDimitry Andric void emitOpLabel(const MachineBasicBlock &MBB); 9181ad6265SDimitry Andric void emitEndOfAsmFile(Module &M) override; 9281ad6265SDimitry Andric bool doInitialization(Module &M) override; 9381ad6265SDimitry Andric 9481ad6265SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override; 9581ad6265SDimitry Andric SPIRV::ModuleAnalysisInfo *MAI; 9681ad6265SDimitry Andric }; 9781ad6265SDimitry Andric } // namespace 9881ad6265SDimitry Andric 9981ad6265SDimitry Andric void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const { 10081ad6265SDimitry Andric AU.addRequired<SPIRVModuleAnalysis>(); 10181ad6265SDimitry Andric AU.addPreserved<SPIRVModuleAnalysis>(); 10281ad6265SDimitry Andric AsmPrinter::getAnalysisUsage(AU); 10381ad6265SDimitry Andric } 10481ad6265SDimitry Andric 10581ad6265SDimitry Andric // If the module has no functions, we need output global info anyway. 10681ad6265SDimitry Andric void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) { 10781ad6265SDimitry Andric if (ModuleSectionsEmitted == false) { 10881ad6265SDimitry Andric outputModuleSections(); 10981ad6265SDimitry Andric ModuleSectionsEmitted = true; 11081ad6265SDimitry Andric } 111*0fca6ea1SDimitry Andric 112*0fca6ea1SDimitry Andric ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl(); 113*0fca6ea1SDimitry Andric VersionTuple SPIRVVersion = ST->getSPIRVVersion(); 114*0fca6ea1SDimitry Andric uint32_t Major = SPIRVVersion.getMajor(); 115*0fca6ea1SDimitry Andric uint32_t Minor = SPIRVVersion.getMinor().value_or(0); 116*0fca6ea1SDimitry Andric // Bound is an approximation that accounts for the maximum used register 117*0fca6ea1SDimitry Andric // number and number of generated OpLabels 118*0fca6ea1SDimitry Andric unsigned Bound = 2 * (ST->getBound() + 1) + NLabels; 119*0fca6ea1SDimitry Andric if (MCAssembler *Asm = OutStreamer->getAssemblerPtr()) 120*0fca6ea1SDimitry Andric static_cast<SPIRVObjectWriter &>(Asm->getWriter()) 121*0fca6ea1SDimitry Andric .setBuildVersion(Major, Minor, Bound); 12281ad6265SDimitry Andric } 12381ad6265SDimitry Andric 12481ad6265SDimitry Andric void SPIRVAsmPrinter::emitFunctionHeader() { 12581ad6265SDimitry Andric if (ModuleSectionsEmitted == false) { 12681ad6265SDimitry Andric outputModuleSections(); 12781ad6265SDimitry Andric ModuleSectionsEmitted = true; 12881ad6265SDimitry Andric } 12981ad6265SDimitry Andric // Get the subtarget from the current MachineFunction. 13081ad6265SDimitry Andric ST = &MF->getSubtarget<SPIRVSubtarget>(); 13181ad6265SDimitry Andric TII = ST->getInstrInfo(); 13281ad6265SDimitry Andric const Function &F = MF->getFunction(); 13381ad6265SDimitry Andric 13481ad6265SDimitry Andric if (isVerbose()) { 13581ad6265SDimitry Andric OutStreamer->getCommentOS() 13681ad6265SDimitry Andric << "-- Begin function " 13781ad6265SDimitry Andric << GlobalValue::dropLLVMManglingEscape(F.getName()) << '\n'; 13881ad6265SDimitry Andric } 13981ad6265SDimitry Andric 14081ad6265SDimitry Andric auto Section = getObjFileLowering().SectionForGlobal(&F, TM); 14181ad6265SDimitry Andric MF->setSection(Section); 14281ad6265SDimitry Andric } 14381ad6265SDimitry Andric 14481ad6265SDimitry Andric void SPIRVAsmPrinter::outputOpFunctionEnd() { 14581ad6265SDimitry Andric MCInst FunctionEndInst; 14681ad6265SDimitry Andric FunctionEndInst.setOpcode(SPIRV::OpFunctionEnd); 14781ad6265SDimitry Andric outputMCInst(FunctionEndInst); 14881ad6265SDimitry Andric } 14981ad6265SDimitry Andric 15081ad6265SDimitry Andric // Emit OpFunctionEnd at the end of MF and clear BBNumToRegMap. 15181ad6265SDimitry Andric void SPIRVAsmPrinter::emitFunctionBodyEnd() { 15281ad6265SDimitry Andric outputOpFunctionEnd(); 15381ad6265SDimitry Andric MAI->BBNumToRegMap.clear(); 15481ad6265SDimitry Andric } 15581ad6265SDimitry Andric 15681ad6265SDimitry Andric void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) { 15781ad6265SDimitry Andric MCInst LabelInst; 15881ad6265SDimitry Andric LabelInst.setOpcode(SPIRV::OpLabel); 15981ad6265SDimitry Andric LabelInst.addOperand(MCOperand::createReg(MAI->getOrCreateMBBRegister(MBB))); 16081ad6265SDimitry Andric outputMCInst(LabelInst); 161*0fca6ea1SDimitry Andric ++NLabels; 16281ad6265SDimitry Andric } 16381ad6265SDimitry Andric 16481ad6265SDimitry Andric void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) { 16506c3fb27SDimitry Andric assert(!MBB.empty() && "MBB is empty!"); 16606c3fb27SDimitry Andric 16781ad6265SDimitry Andric // If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so 16881ad6265SDimitry Andric // OpLabel should be output after them. 16981ad6265SDimitry Andric if (MBB.getNumber() == MF->front().getNumber()) { 17081ad6265SDimitry Andric for (const MachineInstr &MI : MBB) 17181ad6265SDimitry Andric if (MI.getOpcode() == SPIRV::OpFunction) 17281ad6265SDimitry Andric return; 17381ad6265SDimitry Andric // TODO: this case should be checked by the verifier. 17481ad6265SDimitry Andric report_fatal_error("OpFunction is expected in the front MBB of MF"); 17581ad6265SDimitry Andric } 17681ad6265SDimitry Andric emitOpLabel(MBB); 17781ad6265SDimitry Andric } 17881ad6265SDimitry Andric 17981ad6265SDimitry Andric void SPIRVAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, 18081ad6265SDimitry Andric raw_ostream &O) { 18181ad6265SDimitry Andric const MachineOperand &MO = MI->getOperand(OpNum); 18281ad6265SDimitry Andric 18381ad6265SDimitry Andric switch (MO.getType()) { 18481ad6265SDimitry Andric case MachineOperand::MO_Register: 18581ad6265SDimitry Andric O << SPIRVInstPrinter::getRegisterName(MO.getReg()); 18681ad6265SDimitry Andric break; 18781ad6265SDimitry Andric 18881ad6265SDimitry Andric case MachineOperand::MO_Immediate: 18981ad6265SDimitry Andric O << MO.getImm(); 19081ad6265SDimitry Andric break; 19181ad6265SDimitry Andric 19281ad6265SDimitry Andric case MachineOperand::MO_FPImmediate: 19381ad6265SDimitry Andric O << MO.getFPImm(); 19481ad6265SDimitry Andric break; 19581ad6265SDimitry Andric 19681ad6265SDimitry Andric case MachineOperand::MO_MachineBasicBlock: 19781ad6265SDimitry Andric O << *MO.getMBB()->getSymbol(); 19881ad6265SDimitry Andric break; 19981ad6265SDimitry Andric 20081ad6265SDimitry Andric case MachineOperand::MO_GlobalAddress: 20181ad6265SDimitry Andric O << *getSymbol(MO.getGlobal()); 20281ad6265SDimitry Andric break; 20381ad6265SDimitry Andric 20481ad6265SDimitry Andric case MachineOperand::MO_BlockAddress: { 20581ad6265SDimitry Andric MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress()); 20681ad6265SDimitry Andric O << BA->getName(); 20781ad6265SDimitry Andric break; 20881ad6265SDimitry Andric } 20981ad6265SDimitry Andric 21081ad6265SDimitry Andric case MachineOperand::MO_ExternalSymbol: 21181ad6265SDimitry Andric O << *GetExternalSymbolSymbol(MO.getSymbolName()); 21281ad6265SDimitry Andric break; 21381ad6265SDimitry Andric 21481ad6265SDimitry Andric case MachineOperand::MO_JumpTableIndex: 21581ad6265SDimitry Andric case MachineOperand::MO_ConstantPoolIndex: 21681ad6265SDimitry Andric default: 21781ad6265SDimitry Andric llvm_unreachable("<unknown operand type>"); 21881ad6265SDimitry Andric } 21981ad6265SDimitry Andric } 22081ad6265SDimitry Andric 22181ad6265SDimitry Andric bool SPIRVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, 22281ad6265SDimitry Andric const char *ExtraCode, raw_ostream &O) { 22381ad6265SDimitry Andric if (ExtraCode && ExtraCode[0]) 22481ad6265SDimitry Andric return true; // Invalid instruction - SPIR-V does not have special modifiers 22581ad6265SDimitry Andric 22681ad6265SDimitry Andric printOperand(MI, OpNo, O); 22781ad6265SDimitry Andric return false; 22881ad6265SDimitry Andric } 22981ad6265SDimitry Andric 23081ad6265SDimitry Andric static bool isFuncOrHeaderInstr(const MachineInstr *MI, 23181ad6265SDimitry Andric const SPIRVInstrInfo *TII) { 23281ad6265SDimitry Andric return TII->isHeaderInstr(*MI) || MI->getOpcode() == SPIRV::OpFunction || 23381ad6265SDimitry Andric MI->getOpcode() == SPIRV::OpFunctionParameter; 23481ad6265SDimitry Andric } 23581ad6265SDimitry Andric 23681ad6265SDimitry Andric void SPIRVAsmPrinter::outputMCInst(MCInst &Inst) { 23781ad6265SDimitry Andric OutStreamer->emitInstruction(Inst, *OutContext.getSubtargetInfo()); 23881ad6265SDimitry Andric } 23981ad6265SDimitry Andric 24081ad6265SDimitry Andric void SPIRVAsmPrinter::outputInstruction(const MachineInstr *MI) { 24181ad6265SDimitry Andric SPIRVMCInstLower MCInstLowering; 24281ad6265SDimitry Andric MCInst TmpInst; 24381ad6265SDimitry Andric MCInstLowering.lower(MI, TmpInst, MAI); 24481ad6265SDimitry Andric outputMCInst(TmpInst); 24581ad6265SDimitry Andric } 24681ad6265SDimitry Andric 24781ad6265SDimitry Andric void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) { 248753f127fSDimitry Andric SPIRV_MC::verifyInstructionPredicates(MI->getOpcode(), 249753f127fSDimitry Andric getSubtargetInfo().getFeatureBits()); 250753f127fSDimitry Andric 25181ad6265SDimitry Andric if (!MAI->getSkipEmission(MI)) 25281ad6265SDimitry Andric outputInstruction(MI); 25381ad6265SDimitry Andric 25481ad6265SDimitry Andric // Output OpLabel after OpFunction and OpFunctionParameter in the first MBB. 25581ad6265SDimitry Andric const MachineInstr *NextMI = MI->getNextNode(); 25681ad6265SDimitry Andric if (!MAI->hasMBBRegister(*MI->getParent()) && isFuncOrHeaderInstr(MI, TII) && 25781ad6265SDimitry Andric (!NextMI || !isFuncOrHeaderInstr(NextMI, TII))) { 25881ad6265SDimitry Andric assert(MI->getParent()->getNumber() == MF->front().getNumber() && 25981ad6265SDimitry Andric "OpFunction is not in the front MBB of MF"); 26081ad6265SDimitry Andric emitOpLabel(*MI->getParent()); 26181ad6265SDimitry Andric } 26281ad6265SDimitry Andric } 26381ad6265SDimitry Andric 26481ad6265SDimitry Andric void SPIRVAsmPrinter::outputModuleSection(SPIRV::ModuleSectionType MSType) { 26581ad6265SDimitry Andric for (MachineInstr *MI : MAI->getMSInstrs(MSType)) 26681ad6265SDimitry Andric outputInstruction(MI); 26781ad6265SDimitry Andric } 26881ad6265SDimitry Andric 26981ad6265SDimitry Andric void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) { 270fcaf7f86SDimitry Andric // Output OpSourceExtensions. 271fcaf7f86SDimitry Andric for (auto &Str : MAI->SrcExt) { 272fcaf7f86SDimitry Andric MCInst Inst; 273fcaf7f86SDimitry Andric Inst.setOpcode(SPIRV::OpSourceExtension); 274fcaf7f86SDimitry Andric addStringImm(Str.first(), Inst); 275fcaf7f86SDimitry Andric outputMCInst(Inst); 276fcaf7f86SDimitry Andric } 27781ad6265SDimitry Andric // Output OpSource. 27881ad6265SDimitry Andric MCInst Inst; 27981ad6265SDimitry Andric Inst.setOpcode(SPIRV::OpSource); 28081ad6265SDimitry Andric Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->SrcLang))); 28181ad6265SDimitry Andric Inst.addOperand( 28281ad6265SDimitry Andric MCOperand::createImm(static_cast<unsigned>(MAI->SrcLangVersion))); 28381ad6265SDimitry Andric outputMCInst(Inst); 28481ad6265SDimitry Andric } 28581ad6265SDimitry Andric 286fcaf7f86SDimitry Andric void SPIRVAsmPrinter::outputOpExtInstImports(const Module &M) { 287fcaf7f86SDimitry Andric for (auto &CU : MAI->ExtInstSetMap) { 288fcaf7f86SDimitry Andric unsigned Set = CU.first; 289fcaf7f86SDimitry Andric Register Reg = CU.second; 290fcaf7f86SDimitry Andric MCInst Inst; 291fcaf7f86SDimitry Andric Inst.setOpcode(SPIRV::OpExtInstImport); 292fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createReg(Reg)); 293bdd1243dSDimitry Andric addStringImm(getExtInstSetName( 294bdd1243dSDimitry Andric static_cast<SPIRV::InstructionSet::InstructionSet>(Set)), 295fcaf7f86SDimitry Andric Inst); 296fcaf7f86SDimitry Andric outputMCInst(Inst); 297fcaf7f86SDimitry Andric } 298fcaf7f86SDimitry Andric } 299fcaf7f86SDimitry Andric 30081ad6265SDimitry Andric void SPIRVAsmPrinter::outputOpMemoryModel() { 30181ad6265SDimitry Andric MCInst Inst; 30281ad6265SDimitry Andric Inst.setOpcode(SPIRV::OpMemoryModel); 30381ad6265SDimitry Andric Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Addr))); 30481ad6265SDimitry Andric Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Mem))); 30581ad6265SDimitry Andric outputMCInst(Inst); 30681ad6265SDimitry Andric } 30781ad6265SDimitry Andric 30881ad6265SDimitry Andric // Before the OpEntryPoints' output, we need to add the entry point's 30981ad6265SDimitry Andric // interfaces. The interface is a list of IDs of global OpVariable instructions. 31081ad6265SDimitry Andric // These declare the set of global variables from a module that form 31181ad6265SDimitry Andric // the interface of this entry point. 31281ad6265SDimitry Andric void SPIRVAsmPrinter::outputEntryPoints() { 31381ad6265SDimitry Andric // Find all OpVariable IDs with required StorageClass. 31481ad6265SDimitry Andric DenseSet<Register> InterfaceIDs; 31581ad6265SDimitry Andric for (MachineInstr *MI : MAI->GlobalVarList) { 31681ad6265SDimitry Andric assert(MI->getOpcode() == SPIRV::OpVariable); 317bdd1243dSDimitry Andric auto SC = static_cast<SPIRV::StorageClass::StorageClass>( 318bdd1243dSDimitry Andric MI->getOperand(2).getImm()); 31981ad6265SDimitry Andric // Before version 1.4, the interface's storage classes are limited to 32081ad6265SDimitry Andric // the Input and Output storage classes. Starting with version 1.4, 32181ad6265SDimitry Andric // the interface's storage classes are all storage classes used in 32281ad6265SDimitry Andric // declaring all global variables referenced by the entry point call tree. 323*0fca6ea1SDimitry Andric if (ST->isAtLeastSPIRVVer(VersionTuple(1, 4)) || 324*0fca6ea1SDimitry Andric SC == SPIRV::StorageClass::Input || SC == SPIRV::StorageClass::Output) { 32581ad6265SDimitry Andric MachineFunction *MF = MI->getMF(); 32681ad6265SDimitry Andric Register Reg = MAI->getRegisterAlias(MF, MI->getOperand(0).getReg()); 32781ad6265SDimitry Andric InterfaceIDs.insert(Reg); 32881ad6265SDimitry Andric } 32981ad6265SDimitry Andric } 33081ad6265SDimitry Andric 33181ad6265SDimitry Andric // Output OpEntryPoints adding interface args to all of them. 33281ad6265SDimitry Andric for (MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_EntryPoints)) { 33381ad6265SDimitry Andric SPIRVMCInstLower MCInstLowering; 33481ad6265SDimitry Andric MCInst TmpInst; 33581ad6265SDimitry Andric MCInstLowering.lower(MI, TmpInst, MAI); 33681ad6265SDimitry Andric for (Register Reg : InterfaceIDs) { 33781ad6265SDimitry Andric assert(Reg.isValid()); 33881ad6265SDimitry Andric TmpInst.addOperand(MCOperand::createReg(Reg)); 33981ad6265SDimitry Andric } 34081ad6265SDimitry Andric outputMCInst(TmpInst); 34181ad6265SDimitry Andric } 34281ad6265SDimitry Andric } 34381ad6265SDimitry Andric 344bdd1243dSDimitry Andric // Create global OpCapability instructions for the required capabilities. 345bdd1243dSDimitry Andric void SPIRVAsmPrinter::outputGlobalRequirements() { 346bdd1243dSDimitry Andric // Abort here if not all requirements can be satisfied. 347bdd1243dSDimitry Andric MAI->Reqs.checkSatisfiable(*ST); 348bdd1243dSDimitry Andric 349bdd1243dSDimitry Andric for (const auto &Cap : MAI->Reqs.getMinimalCapabilities()) { 350bdd1243dSDimitry Andric MCInst Inst; 351bdd1243dSDimitry Andric Inst.setOpcode(SPIRV::OpCapability); 352bdd1243dSDimitry Andric Inst.addOperand(MCOperand::createImm(Cap)); 353bdd1243dSDimitry Andric outputMCInst(Inst); 354bdd1243dSDimitry Andric } 355bdd1243dSDimitry Andric 356bdd1243dSDimitry Andric // Generate the final OpExtensions with strings instead of enums. 357bdd1243dSDimitry Andric for (const auto &Ext : MAI->Reqs.getExtensions()) { 358bdd1243dSDimitry Andric MCInst Inst; 359bdd1243dSDimitry Andric Inst.setOpcode(SPIRV::OpExtension); 360bdd1243dSDimitry Andric addStringImm(getSymbolicOperandMnemonic( 361bdd1243dSDimitry Andric SPIRV::OperandCategory::ExtensionOperand, Ext), 362bdd1243dSDimitry Andric Inst); 363bdd1243dSDimitry Andric outputMCInst(Inst); 364bdd1243dSDimitry Andric } 365bdd1243dSDimitry Andric // TODO add a pseudo instr for version number. 366bdd1243dSDimitry Andric } 367bdd1243dSDimitry Andric 36881ad6265SDimitry Andric void SPIRVAsmPrinter::outputExtFuncDecls() { 36981ad6265SDimitry Andric // Insert OpFunctionEnd after each declaration. 37081ad6265SDimitry Andric SmallVectorImpl<MachineInstr *>::iterator 37181ad6265SDimitry Andric I = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).begin(), 37281ad6265SDimitry Andric E = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).end(); 37381ad6265SDimitry Andric for (; I != E; ++I) { 37481ad6265SDimitry Andric outputInstruction(*I); 37581ad6265SDimitry Andric if ((I + 1) == E || (*(I + 1))->getOpcode() == SPIRV::OpFunction) 37681ad6265SDimitry Andric outputOpFunctionEnd(); 37781ad6265SDimitry Andric } 37881ad6265SDimitry Andric } 37981ad6265SDimitry Andric 380fcaf7f86SDimitry Andric // Encode LLVM type by SPIR-V execution mode VecTypeHint. 381fcaf7f86SDimitry Andric static unsigned encodeVecTypeHint(Type *Ty) { 382fcaf7f86SDimitry Andric if (Ty->isHalfTy()) 383fcaf7f86SDimitry Andric return 4; 384fcaf7f86SDimitry Andric if (Ty->isFloatTy()) 385fcaf7f86SDimitry Andric return 5; 386fcaf7f86SDimitry Andric if (Ty->isDoubleTy()) 387fcaf7f86SDimitry Andric return 6; 388fcaf7f86SDimitry Andric if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) { 389fcaf7f86SDimitry Andric switch (IntTy->getIntegerBitWidth()) { 390fcaf7f86SDimitry Andric case 8: 391fcaf7f86SDimitry Andric return 0; 392fcaf7f86SDimitry Andric case 16: 393fcaf7f86SDimitry Andric return 1; 394fcaf7f86SDimitry Andric case 32: 395fcaf7f86SDimitry Andric return 2; 396fcaf7f86SDimitry Andric case 64: 397fcaf7f86SDimitry Andric return 3; 398fcaf7f86SDimitry Andric default: 399fcaf7f86SDimitry Andric llvm_unreachable("invalid integer type"); 400fcaf7f86SDimitry Andric } 401fcaf7f86SDimitry Andric } 402fcaf7f86SDimitry Andric if (FixedVectorType *VecTy = dyn_cast<FixedVectorType>(Ty)) { 403fcaf7f86SDimitry Andric Type *EleTy = VecTy->getElementType(); 404fcaf7f86SDimitry Andric unsigned Size = VecTy->getNumElements(); 405fcaf7f86SDimitry Andric return Size << 16 | encodeVecTypeHint(EleTy); 406fcaf7f86SDimitry Andric } 407fcaf7f86SDimitry Andric llvm_unreachable("invalid type"); 408fcaf7f86SDimitry Andric } 409fcaf7f86SDimitry Andric 410fcaf7f86SDimitry Andric static void addOpsFromMDNode(MDNode *MDN, MCInst &Inst, 411fcaf7f86SDimitry Andric SPIRV::ModuleAnalysisInfo *MAI) { 412fcaf7f86SDimitry Andric for (const MDOperand &MDOp : MDN->operands()) { 413fcaf7f86SDimitry Andric if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) { 414fcaf7f86SDimitry Andric Constant *C = CMeta->getValue(); 415fcaf7f86SDimitry Andric if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) { 416fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createImm(Const->getZExtValue())); 417fcaf7f86SDimitry Andric } else if (auto *CE = dyn_cast<Function>(C)) { 418bdd1243dSDimitry Andric Register FuncReg = MAI->getFuncReg(CE); 419fcaf7f86SDimitry Andric assert(FuncReg.isValid()); 420fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createReg(FuncReg)); 421fcaf7f86SDimitry Andric } 422fcaf7f86SDimitry Andric } 423fcaf7f86SDimitry Andric } 424fcaf7f86SDimitry Andric } 425fcaf7f86SDimitry Andric 426bdd1243dSDimitry Andric void SPIRVAsmPrinter::outputExecutionModeFromMDNode( 427*0fca6ea1SDimitry Andric Register Reg, MDNode *Node, SPIRV::ExecutionMode::ExecutionMode EM, 428*0fca6ea1SDimitry Andric unsigned ExpectMDOps, int64_t DefVal) { 429fcaf7f86SDimitry Andric MCInst Inst; 430fcaf7f86SDimitry Andric Inst.setOpcode(SPIRV::OpExecutionMode); 431fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createReg(Reg)); 432fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM))); 433fcaf7f86SDimitry Andric addOpsFromMDNode(Node, Inst, MAI); 434*0fca6ea1SDimitry Andric // reqd_work_group_size and work_group_size_hint require 3 operands, 435*0fca6ea1SDimitry Andric // if metadata contains less operands, just add a default value 436*0fca6ea1SDimitry Andric unsigned NodeSz = Node->getNumOperands(); 437*0fca6ea1SDimitry Andric if (ExpectMDOps > 0 && NodeSz < ExpectMDOps) 438*0fca6ea1SDimitry Andric for (unsigned i = NodeSz; i < ExpectMDOps; ++i) 439*0fca6ea1SDimitry Andric Inst.addOperand(MCOperand::createImm(DefVal)); 440fcaf7f86SDimitry Andric outputMCInst(Inst); 441fcaf7f86SDimitry Andric } 442fcaf7f86SDimitry Andric 4435f757f3fSDimitry Andric void SPIRVAsmPrinter::outputExecutionModeFromNumthreadsAttribute( 4445f757f3fSDimitry Andric const Register &Reg, const Attribute &Attr, 4455f757f3fSDimitry Andric SPIRV::ExecutionMode::ExecutionMode EM) { 4465f757f3fSDimitry Andric assert(Attr.isValid() && "Function called with an invalid attribute."); 4475f757f3fSDimitry Andric 4485f757f3fSDimitry Andric MCInst Inst; 4495f757f3fSDimitry Andric Inst.setOpcode(SPIRV::OpExecutionMode); 4505f757f3fSDimitry Andric Inst.addOperand(MCOperand::createReg(Reg)); 4515f757f3fSDimitry Andric Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM))); 4525f757f3fSDimitry Andric 4535f757f3fSDimitry Andric SmallVector<StringRef> NumThreads; 4545f757f3fSDimitry Andric Attr.getValueAsString().split(NumThreads, ','); 4555f757f3fSDimitry Andric assert(NumThreads.size() == 3 && "invalid numthreads"); 4565f757f3fSDimitry Andric for (uint32_t i = 0; i < 3; ++i) { 4575f757f3fSDimitry Andric uint32_t V; 4585f757f3fSDimitry Andric [[maybe_unused]] bool Result = NumThreads[i].getAsInteger(10, V); 4595f757f3fSDimitry Andric assert(!Result && "Failed to parse numthreads"); 4605f757f3fSDimitry Andric Inst.addOperand(MCOperand::createImm(V)); 4615f757f3fSDimitry Andric } 4625f757f3fSDimitry Andric 4635f757f3fSDimitry Andric outputMCInst(Inst); 4645f757f3fSDimitry Andric } 4655f757f3fSDimitry Andric 466fcaf7f86SDimitry Andric void SPIRVAsmPrinter::outputExecutionMode(const Module &M) { 467fcaf7f86SDimitry Andric NamedMDNode *Node = M.getNamedMetadata("spirv.ExecutionMode"); 468fcaf7f86SDimitry Andric if (Node) { 469fcaf7f86SDimitry Andric for (unsigned i = 0; i < Node->getNumOperands(); i++) { 470fcaf7f86SDimitry Andric MCInst Inst; 471fcaf7f86SDimitry Andric Inst.setOpcode(SPIRV::OpExecutionMode); 472fcaf7f86SDimitry Andric addOpsFromMDNode(cast<MDNode>(Node->getOperand(i)), Inst, MAI); 473fcaf7f86SDimitry Andric outputMCInst(Inst); 474fcaf7f86SDimitry Andric } 475fcaf7f86SDimitry Andric } 476fcaf7f86SDimitry Andric for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) { 477fcaf7f86SDimitry Andric const Function &F = *FI; 478*0fca6ea1SDimitry Andric // Only operands of OpEntryPoint instructions are allowed to be 479*0fca6ea1SDimitry Andric // <Entry Point> operands of OpExecutionMode 480*0fca6ea1SDimitry Andric if (F.isDeclaration() || !isEntryPoint(F)) 481fcaf7f86SDimitry Andric continue; 482bdd1243dSDimitry Andric Register FReg = MAI->getFuncReg(&F); 483fcaf7f86SDimitry Andric assert(FReg.isValid()); 484fcaf7f86SDimitry Andric if (MDNode *Node = F.getMetadata("reqd_work_group_size")) 485*0fca6ea1SDimitry Andric outputExecutionModeFromMDNode(FReg, Node, SPIRV::ExecutionMode::LocalSize, 486*0fca6ea1SDimitry Andric 3, 1); 4875f757f3fSDimitry Andric if (Attribute Attr = F.getFnAttribute("hlsl.numthreads"); Attr.isValid()) 4885f757f3fSDimitry Andric outputExecutionModeFromNumthreadsAttribute( 4895f757f3fSDimitry Andric FReg, Attr, SPIRV::ExecutionMode::LocalSize); 490fcaf7f86SDimitry Andric if (MDNode *Node = F.getMetadata("work_group_size_hint")) 491fcaf7f86SDimitry Andric outputExecutionModeFromMDNode(FReg, Node, 492*0fca6ea1SDimitry Andric SPIRV::ExecutionMode::LocalSizeHint, 3, 1); 493fcaf7f86SDimitry Andric if (MDNode *Node = F.getMetadata("intel_reqd_sub_group_size")) 494fcaf7f86SDimitry Andric outputExecutionModeFromMDNode(FReg, Node, 495*0fca6ea1SDimitry Andric SPIRV::ExecutionMode::SubgroupSize, 0, 0); 496fcaf7f86SDimitry Andric if (MDNode *Node = F.getMetadata("vec_type_hint")) { 497fcaf7f86SDimitry Andric MCInst Inst; 498fcaf7f86SDimitry Andric Inst.setOpcode(SPIRV::OpExecutionMode); 499fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createReg(FReg)); 500fcaf7f86SDimitry Andric unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::VecTypeHint); 501fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createImm(EM)); 502fcaf7f86SDimitry Andric unsigned TypeCode = encodeVecTypeHint(getMDOperandAsType(Node, 0)); 503fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createImm(TypeCode)); 504fcaf7f86SDimitry Andric outputMCInst(Inst); 505fcaf7f86SDimitry Andric } 5065f757f3fSDimitry Andric if (ST->isOpenCLEnv() && !M.getNamedMetadata("spirv.ExecutionMode") && 507bdd1243dSDimitry Andric !M.getNamedMetadata("opencl.enable.FP_CONTRACT")) { 508bdd1243dSDimitry Andric MCInst Inst; 509bdd1243dSDimitry Andric Inst.setOpcode(SPIRV::OpExecutionMode); 510bdd1243dSDimitry Andric Inst.addOperand(MCOperand::createReg(FReg)); 511bdd1243dSDimitry Andric unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::ContractionOff); 512bdd1243dSDimitry Andric Inst.addOperand(MCOperand::createImm(EM)); 513bdd1243dSDimitry Andric outputMCInst(Inst); 514bdd1243dSDimitry Andric } 515fcaf7f86SDimitry Andric } 516fcaf7f86SDimitry Andric } 517fcaf7f86SDimitry Andric 518fcaf7f86SDimitry Andric void SPIRVAsmPrinter::outputAnnotations(const Module &M) { 519fcaf7f86SDimitry Andric outputModuleSection(SPIRV::MB_Annotations); 520fcaf7f86SDimitry Andric // Process llvm.global.annotations special global variable. 521fcaf7f86SDimitry Andric for (auto F = M.global_begin(), E = M.global_end(); F != E; ++F) { 522fcaf7f86SDimitry Andric if ((*F).getName() != "llvm.global.annotations") 523fcaf7f86SDimitry Andric continue; 524fcaf7f86SDimitry Andric const GlobalVariable *V = &(*F); 525fcaf7f86SDimitry Andric const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0)); 526fcaf7f86SDimitry Andric for (Value *Op : CA->operands()) { 527fcaf7f86SDimitry Andric ConstantStruct *CS = cast<ConstantStruct>(Op); 528fcaf7f86SDimitry Andric // The first field of the struct contains a pointer to 529fcaf7f86SDimitry Andric // the annotated variable. 530fcaf7f86SDimitry Andric Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts(); 531fcaf7f86SDimitry Andric if (!isa<Function>(AnnotatedVar)) 532bdd1243dSDimitry Andric report_fatal_error("Unsupported value in llvm.global.annotations"); 533fcaf7f86SDimitry Andric Function *Func = cast<Function>(AnnotatedVar); 534bdd1243dSDimitry Andric Register Reg = MAI->getFuncReg(Func); 535*0fca6ea1SDimitry Andric if (!Reg.isValid()) { 536*0fca6ea1SDimitry Andric std::string DiagMsg; 537*0fca6ea1SDimitry Andric raw_string_ostream OS(DiagMsg); 538*0fca6ea1SDimitry Andric AnnotatedVar->print(OS); 539*0fca6ea1SDimitry Andric DiagMsg = "Unknown function in llvm.global.annotations: " + DiagMsg; 540*0fca6ea1SDimitry Andric report_fatal_error(DiagMsg.c_str()); 541*0fca6ea1SDimitry Andric } 542fcaf7f86SDimitry Andric 543fcaf7f86SDimitry Andric // The second field contains a pointer to a global annotation string. 544fcaf7f86SDimitry Andric GlobalVariable *GV = 545fcaf7f86SDimitry Andric cast<GlobalVariable>(CS->getOperand(1)->stripPointerCasts()); 546fcaf7f86SDimitry Andric 547fcaf7f86SDimitry Andric StringRef AnnotationString; 548fcaf7f86SDimitry Andric getConstantStringInfo(GV, AnnotationString); 549fcaf7f86SDimitry Andric MCInst Inst; 550fcaf7f86SDimitry Andric Inst.setOpcode(SPIRV::OpDecorate); 551fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createReg(Reg)); 552fcaf7f86SDimitry Andric unsigned Dec = static_cast<unsigned>(SPIRV::Decoration::UserSemantic); 553fcaf7f86SDimitry Andric Inst.addOperand(MCOperand::createImm(Dec)); 554fcaf7f86SDimitry Andric addStringImm(AnnotationString, Inst); 555fcaf7f86SDimitry Andric outputMCInst(Inst); 556fcaf7f86SDimitry Andric } 557fcaf7f86SDimitry Andric } 558fcaf7f86SDimitry Andric } 559fcaf7f86SDimitry Andric 56081ad6265SDimitry Andric void SPIRVAsmPrinter::outputModuleSections() { 56181ad6265SDimitry Andric const Module *M = MMI->getModule(); 56281ad6265SDimitry Andric // Get the global subtarget to output module-level info. 56381ad6265SDimitry Andric ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl(); 56481ad6265SDimitry Andric TII = ST->getInstrInfo(); 56581ad6265SDimitry Andric MAI = &SPIRVModuleAnalysis::MAI; 56681ad6265SDimitry Andric assert(ST && TII && MAI && M && "Module analysis is required"); 56781ad6265SDimitry Andric // Output instructions according to the Logical Layout of a Module: 568bdd1243dSDimitry Andric // 1,2. All OpCapability instructions, then optional OpExtension instructions. 569bdd1243dSDimitry Andric outputGlobalRequirements(); 570fcaf7f86SDimitry Andric // 3. Optional OpExtInstImport instructions. 571fcaf7f86SDimitry Andric outputOpExtInstImports(*M); 57281ad6265SDimitry Andric // 4. The single required OpMemoryModel instruction. 57381ad6265SDimitry Andric outputOpMemoryModel(); 57481ad6265SDimitry Andric // 5. All entry point declarations, using OpEntryPoint. 57581ad6265SDimitry Andric outputEntryPoints(); 57681ad6265SDimitry Andric // 6. Execution-mode declarations, using OpExecutionMode or OpExecutionModeId. 577fcaf7f86SDimitry Andric outputExecutionMode(*M); 57881ad6265SDimitry Andric // 7a. Debug: all OpString, OpSourceExtension, OpSource, and 57981ad6265SDimitry Andric // OpSourceContinued, without forward references. 58081ad6265SDimitry Andric outputDebugSourceAndStrings(*M); 58181ad6265SDimitry Andric // 7b. Debug: all OpName and all OpMemberName. 58281ad6265SDimitry Andric outputModuleSection(SPIRV::MB_DebugNames); 58381ad6265SDimitry Andric // 7c. Debug: all OpModuleProcessed instructions. 58481ad6265SDimitry Andric outputModuleSection(SPIRV::MB_DebugModuleProcessed); 58581ad6265SDimitry Andric // 8. All annotation instructions (all decorations). 586fcaf7f86SDimitry Andric outputAnnotations(*M); 58781ad6265SDimitry Andric // 9. All type declarations (OpTypeXXX instructions), all constant 58881ad6265SDimitry Andric // instructions, and all global variable declarations. This section is 58981ad6265SDimitry Andric // the first section to allow use of: OpLine and OpNoLine debug information; 59081ad6265SDimitry Andric // non-semantic instructions with OpExtInst. 59181ad6265SDimitry Andric outputModuleSection(SPIRV::MB_TypeConstVars); 59281ad6265SDimitry Andric // 10. All function declarations (functions without a body). 59381ad6265SDimitry Andric outputExtFuncDecls(); 59481ad6265SDimitry Andric // 11. All function definitions (functions with a body). 59581ad6265SDimitry Andric // This is done in regular function output. 59681ad6265SDimitry Andric } 59781ad6265SDimitry Andric 59881ad6265SDimitry Andric bool SPIRVAsmPrinter::doInitialization(Module &M) { 59981ad6265SDimitry Andric ModuleSectionsEmitted = false; 60081ad6265SDimitry Andric // We need to call the parent's one explicitly. 60181ad6265SDimitry Andric return AsmPrinter::doInitialization(M); 60281ad6265SDimitry Andric } 60381ad6265SDimitry Andric 60481ad6265SDimitry Andric // Force static initialization. 60581ad6265SDimitry Andric extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVAsmPrinter() { 60681ad6265SDimitry Andric RegisterAsmPrinter<SPIRVAsmPrinter> X(getTheSPIRV32Target()); 60781ad6265SDimitry Andric RegisterAsmPrinter<SPIRVAsmPrinter> Y(getTheSPIRV64Target()); 6085f757f3fSDimitry Andric RegisterAsmPrinter<SPIRVAsmPrinter> Z(getTheSPIRVLogicalTarget()); 60981ad6265SDimitry Andric } 610