181ad6265SDimitry Andric //===-- SPIRVInstPrinter.cpp - Output SPIR-V MCInsts as ASM -----*- 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 class prints a SPIR-V MCInst to a .s file. 1081ad6265SDimitry Andric // 1181ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1281ad6265SDimitry Andric 1381ad6265SDimitry Andric #include "SPIRVInstPrinter.h" 1481ad6265SDimitry Andric #include "SPIRV.h" 1581ad6265SDimitry Andric #include "SPIRVBaseInfo.h" 1681ad6265SDimitry Andric #include "llvm/CodeGen/Register.h" 1781ad6265SDimitry Andric #include "llvm/MC/MCAsmInfo.h" 1881ad6265SDimitry Andric #include "llvm/MC/MCExpr.h" 1981ad6265SDimitry Andric #include "llvm/MC/MCInst.h" 2081ad6265SDimitry Andric #include "llvm/MC/MCInstrInfo.h" 2181ad6265SDimitry Andric #include "llvm/MC/MCSymbol.h" 2281ad6265SDimitry Andric #include "llvm/Support/Casting.h" 2381ad6265SDimitry Andric #include "llvm/Support/ErrorHandling.h" 2481ad6265SDimitry Andric #include "llvm/Support/FormattedStream.h" 2581ad6265SDimitry Andric 2681ad6265SDimitry Andric using namespace llvm; 27*bdd1243dSDimitry Andric using namespace llvm::SPIRV; 2881ad6265SDimitry Andric 2981ad6265SDimitry Andric #define DEBUG_TYPE "asm-printer" 3081ad6265SDimitry Andric 3181ad6265SDimitry Andric // Include the auto-generated portion of the assembly writer. 3281ad6265SDimitry Andric #include "SPIRVGenAsmWriter.inc" 3381ad6265SDimitry Andric 3481ad6265SDimitry Andric void SPIRVInstPrinter::printRemainingVariableOps(const MCInst *MI, 3581ad6265SDimitry Andric unsigned StartIndex, 3681ad6265SDimitry Andric raw_ostream &O, 3781ad6265SDimitry Andric bool SkipFirstSpace, 3881ad6265SDimitry Andric bool SkipImmediates) { 3981ad6265SDimitry Andric const unsigned NumOps = MI->getNumOperands(); 4081ad6265SDimitry Andric for (unsigned i = StartIndex; i < NumOps; ++i) { 4181ad6265SDimitry Andric if (!SkipImmediates || !MI->getOperand(i).isImm()) { 4281ad6265SDimitry Andric if (!SkipFirstSpace || i != StartIndex) 4381ad6265SDimitry Andric O << ' '; 4481ad6265SDimitry Andric printOperand(MI, i, O); 4581ad6265SDimitry Andric } 4681ad6265SDimitry Andric } 4781ad6265SDimitry Andric } 4881ad6265SDimitry Andric 4981ad6265SDimitry Andric void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI, 5081ad6265SDimitry Andric unsigned StartIndex, 5181ad6265SDimitry Andric raw_ostream &O) { 5281ad6265SDimitry Andric O << ' '; 5381ad6265SDimitry Andric if (MI->getNumOperands() - StartIndex == 2) { // Handle 64 bit literals. 5481ad6265SDimitry Andric uint64_t Imm = MI->getOperand(StartIndex).getImm(); 5581ad6265SDimitry Andric Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32); 5681ad6265SDimitry Andric O << Imm; 5781ad6265SDimitry Andric } else { 5881ad6265SDimitry Andric printRemainingVariableOps(MI, StartIndex, O, true, false); 5981ad6265SDimitry Andric } 6081ad6265SDimitry Andric } 6181ad6265SDimitry Andric 6281ad6265SDimitry Andric void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) { 63*bdd1243dSDimitry Andric Register Reg = MI->getOperand(0).getReg(); 64*bdd1243dSDimitry Andric auto Name = getSPIRVStringOperand(*MI, 1); 65*bdd1243dSDimitry Andric auto Set = getExtInstSetFromString(Name); 66*bdd1243dSDimitry Andric ExtInstSetIDs.insert({Reg, Set}); 6781ad6265SDimitry Andric } 6881ad6265SDimitry Andric 6981ad6265SDimitry Andric void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address, 7081ad6265SDimitry Andric StringRef Annot, const MCSubtargetInfo &STI, 7181ad6265SDimitry Andric raw_ostream &OS) { 7281ad6265SDimitry Andric const unsigned OpCode = MI->getOpcode(); 7381ad6265SDimitry Andric printInstruction(MI, Address, OS); 7481ad6265SDimitry Andric 7581ad6265SDimitry Andric if (OpCode == SPIRV::OpDecorate) { 7681ad6265SDimitry Andric printOpDecorate(MI, OS); 7781ad6265SDimitry Andric } else if (OpCode == SPIRV::OpExtInstImport) { 7881ad6265SDimitry Andric recordOpExtInstImport(MI); 7981ad6265SDimitry Andric } else if (OpCode == SPIRV::OpExtInst) { 8081ad6265SDimitry Andric printOpExtInst(MI, OS); 8181ad6265SDimitry Andric } else { 8281ad6265SDimitry Andric // Print any extra operands for variadic instructions. 83*bdd1243dSDimitry Andric const MCInstrDesc &MCDesc = MII.get(OpCode); 8481ad6265SDimitry Andric if (MCDesc.isVariadic()) { 8581ad6265SDimitry Andric const unsigned NumFixedOps = MCDesc.getNumOperands(); 8681ad6265SDimitry Andric const unsigned LastFixedIndex = NumFixedOps - 1; 8781ad6265SDimitry Andric const int FirstVariableIndex = NumFixedOps; 88*bdd1243dSDimitry Andric if (NumFixedOps > 0 && MCDesc.operands()[LastFixedIndex].OperandType == 89*bdd1243dSDimitry Andric MCOI::OPERAND_UNKNOWN) { 9081ad6265SDimitry Andric // For instructions where a custom type (not reg or immediate) comes as 9181ad6265SDimitry Andric // the last operand before the variable_ops. This is usually a StringImm 9281ad6265SDimitry Andric // operand, but there are a few other cases. 9381ad6265SDimitry Andric switch (OpCode) { 9481ad6265SDimitry Andric case SPIRV::OpTypeImage: 9581ad6265SDimitry Andric OS << ' '; 96*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::AccessQualifierOperand>( 97*bdd1243dSDimitry Andric MI, FirstVariableIndex, OS); 9881ad6265SDimitry Andric break; 9981ad6265SDimitry Andric case SPIRV::OpVariable: 10081ad6265SDimitry Andric OS << ' '; 10181ad6265SDimitry Andric printOperand(MI, FirstVariableIndex, OS); 10281ad6265SDimitry Andric break; 10381ad6265SDimitry Andric case SPIRV::OpEntryPoint: { 10481ad6265SDimitry Andric // Print the interface ID operands, skipping the name's string 10581ad6265SDimitry Andric // literal. 10681ad6265SDimitry Andric printRemainingVariableOps(MI, NumFixedOps, OS, false, true); 10781ad6265SDimitry Andric break; 10881ad6265SDimitry Andric } 10981ad6265SDimitry Andric case SPIRV::OpExecutionMode: 11081ad6265SDimitry Andric case SPIRV::OpExecutionModeId: 11181ad6265SDimitry Andric case SPIRV::OpLoopMerge: { 11281ad6265SDimitry Andric // Print any literals after the OPERAND_UNKNOWN argument normally. 11381ad6265SDimitry Andric printRemainingVariableOps(MI, NumFixedOps, OS); 11481ad6265SDimitry Andric break; 11581ad6265SDimitry Andric } 11681ad6265SDimitry Andric default: 117*bdd1243dSDimitry Andric break; // printStringImm has already been handled. 11881ad6265SDimitry Andric } 11981ad6265SDimitry Andric } else { 12081ad6265SDimitry Andric // For instructions with no fixed ops or a reg/immediate as the final 12181ad6265SDimitry Andric // fixed operand, we can usually print the rest with "printOperand", but 12281ad6265SDimitry Andric // check for a few cases with custom types first. 12381ad6265SDimitry Andric switch (OpCode) { 12481ad6265SDimitry Andric case SPIRV::OpLoad: 12581ad6265SDimitry Andric case SPIRV::OpStore: 12681ad6265SDimitry Andric OS << ' '; 127*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::MemoryOperandOperand>( 128*bdd1243dSDimitry Andric MI, FirstVariableIndex, OS); 12981ad6265SDimitry Andric printRemainingVariableOps(MI, FirstVariableIndex + 1, OS); 13081ad6265SDimitry Andric break; 13181ad6265SDimitry Andric case SPIRV::OpImageSampleImplicitLod: 13281ad6265SDimitry Andric case SPIRV::OpImageSampleDrefImplicitLod: 13381ad6265SDimitry Andric case SPIRV::OpImageSampleProjImplicitLod: 13481ad6265SDimitry Andric case SPIRV::OpImageSampleProjDrefImplicitLod: 13581ad6265SDimitry Andric case SPIRV::OpImageFetch: 13681ad6265SDimitry Andric case SPIRV::OpImageGather: 13781ad6265SDimitry Andric case SPIRV::OpImageDrefGather: 13881ad6265SDimitry Andric case SPIRV::OpImageRead: 13981ad6265SDimitry Andric case SPIRV::OpImageWrite: 14081ad6265SDimitry Andric case SPIRV::OpImageSparseSampleImplicitLod: 14181ad6265SDimitry Andric case SPIRV::OpImageSparseSampleDrefImplicitLod: 14281ad6265SDimitry Andric case SPIRV::OpImageSparseSampleProjImplicitLod: 14381ad6265SDimitry Andric case SPIRV::OpImageSparseSampleProjDrefImplicitLod: 14481ad6265SDimitry Andric case SPIRV::OpImageSparseFetch: 14581ad6265SDimitry Andric case SPIRV::OpImageSparseGather: 14681ad6265SDimitry Andric case SPIRV::OpImageSparseDrefGather: 14781ad6265SDimitry Andric case SPIRV::OpImageSparseRead: 14881ad6265SDimitry Andric case SPIRV::OpImageSampleFootprintNV: 14981ad6265SDimitry Andric OS << ' '; 150*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::ImageOperandOperand>( 151*bdd1243dSDimitry Andric MI, FirstVariableIndex, OS); 15281ad6265SDimitry Andric printRemainingVariableOps(MI, NumFixedOps + 1, OS); 15381ad6265SDimitry Andric break; 15481ad6265SDimitry Andric case SPIRV::OpCopyMemory: 15581ad6265SDimitry Andric case SPIRV::OpCopyMemorySized: { 15681ad6265SDimitry Andric const unsigned NumOps = MI->getNumOperands(); 15781ad6265SDimitry Andric for (unsigned i = NumFixedOps; i < NumOps; ++i) { 15881ad6265SDimitry Andric OS << ' '; 159*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i, 160*bdd1243dSDimitry Andric OS); 161*bdd1243dSDimitry Andric if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) { 16281ad6265SDimitry Andric assert(i + 1 < NumOps && "Missing alignment operand"); 16381ad6265SDimitry Andric OS << ' '; 16481ad6265SDimitry Andric printOperand(MI, i + 1, OS); 16581ad6265SDimitry Andric i += 1; 16681ad6265SDimitry Andric } 16781ad6265SDimitry Andric } 16881ad6265SDimitry Andric break; 16981ad6265SDimitry Andric } 17081ad6265SDimitry Andric case SPIRV::OpConstantI: 17181ad6265SDimitry Andric case SPIRV::OpConstantF: 17281ad6265SDimitry Andric printOpConstantVarOps(MI, NumFixedOps, OS); 17381ad6265SDimitry Andric break; 17481ad6265SDimitry Andric default: 17581ad6265SDimitry Andric printRemainingVariableOps(MI, NumFixedOps, OS); 17681ad6265SDimitry Andric break; 17781ad6265SDimitry Andric } 17881ad6265SDimitry Andric } 17981ad6265SDimitry Andric } 18081ad6265SDimitry Andric } 18181ad6265SDimitry Andric 18281ad6265SDimitry Andric printAnnotation(OS, Annot); 18381ad6265SDimitry Andric } 18481ad6265SDimitry Andric 18581ad6265SDimitry Andric void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) { 186fcaf7f86SDimitry Andric // The fixed operands have already been printed, so just need to decide what 187fcaf7f86SDimitry Andric // type of ExtInst operands to print based on the instruction set and number. 188*bdd1243dSDimitry Andric const MCInstrDesc &MCDesc = MII.get(MI->getOpcode()); 189fcaf7f86SDimitry Andric unsigned NumFixedOps = MCDesc.getNumOperands(); 190fcaf7f86SDimitry Andric const auto NumOps = MI->getNumOperands(); 191fcaf7f86SDimitry Andric if (NumOps == NumFixedOps) 192fcaf7f86SDimitry Andric return; 193fcaf7f86SDimitry Andric 194fcaf7f86SDimitry Andric O << ' '; 195fcaf7f86SDimitry Andric 196fcaf7f86SDimitry Andric // TODO: implement special printing for OpenCLExtInst::vstor*. 197fcaf7f86SDimitry Andric printRemainingVariableOps(MI, NumFixedOps, O, true); 19881ad6265SDimitry Andric } 19981ad6265SDimitry Andric 20081ad6265SDimitry Andric void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) { 20181ad6265SDimitry Andric // The fixed operands have already been printed, so just need to decide what 20281ad6265SDimitry Andric // type of decoration operands to print based on the Decoration type. 203*bdd1243dSDimitry Andric const MCInstrDesc &MCDesc = MII.get(MI->getOpcode()); 20481ad6265SDimitry Andric unsigned NumFixedOps = MCDesc.getNumOperands(); 20581ad6265SDimitry Andric 20681ad6265SDimitry Andric if (NumFixedOps != MI->getNumOperands()) { 20781ad6265SDimitry Andric auto DecOp = MI->getOperand(NumFixedOps - 1); 208*bdd1243dSDimitry Andric auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm()); 20981ad6265SDimitry Andric 21081ad6265SDimitry Andric O << ' '; 21181ad6265SDimitry Andric 21281ad6265SDimitry Andric switch (Dec) { 213*bdd1243dSDimitry Andric case Decoration::BuiltIn: 214*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O); 21581ad6265SDimitry Andric break; 216*bdd1243dSDimitry Andric case Decoration::UniformId: 217*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O); 21881ad6265SDimitry Andric break; 219*bdd1243dSDimitry Andric case Decoration::FuncParamAttr: 220*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>( 221*bdd1243dSDimitry Andric MI, NumFixedOps, O); 22281ad6265SDimitry Andric break; 223*bdd1243dSDimitry Andric case Decoration::FPRoundingMode: 224*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::FPRoundingModeOperand>( 225*bdd1243dSDimitry Andric MI, NumFixedOps, O); 22681ad6265SDimitry Andric break; 227*bdd1243dSDimitry Andric case Decoration::FPFastMathMode: 228*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::FPFastMathModeOperand>( 229*bdd1243dSDimitry Andric MI, NumFixedOps, O); 23081ad6265SDimitry Andric break; 231*bdd1243dSDimitry Andric case Decoration::LinkageAttributes: 232*bdd1243dSDimitry Andric case Decoration::UserSemantic: 23381ad6265SDimitry Andric printStringImm(MI, NumFixedOps, O); 23481ad6265SDimitry Andric break; 23581ad6265SDimitry Andric default: 23681ad6265SDimitry Andric printRemainingVariableOps(MI, NumFixedOps, O, true); 23781ad6265SDimitry Andric break; 23881ad6265SDimitry Andric } 23981ad6265SDimitry Andric } 24081ad6265SDimitry Andric } 24181ad6265SDimitry Andric 24281ad6265SDimitry Andric static void printExpr(const MCExpr *Expr, raw_ostream &O) { 24381ad6265SDimitry Andric #ifndef NDEBUG 24481ad6265SDimitry Andric const MCSymbolRefExpr *SRE; 24581ad6265SDimitry Andric 24681ad6265SDimitry Andric if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr)) 24781ad6265SDimitry Andric SRE = cast<MCSymbolRefExpr>(BE->getLHS()); 24881ad6265SDimitry Andric else 24981ad6265SDimitry Andric SRE = cast<MCSymbolRefExpr>(Expr); 25081ad6265SDimitry Andric 25181ad6265SDimitry Andric MCSymbolRefExpr::VariantKind Kind = SRE->getKind(); 25281ad6265SDimitry Andric 25381ad6265SDimitry Andric assert(Kind == MCSymbolRefExpr::VK_None); 25481ad6265SDimitry Andric #endif 25581ad6265SDimitry Andric O << *Expr; 25681ad6265SDimitry Andric } 25781ad6265SDimitry Andric 25881ad6265SDimitry Andric void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, 25981ad6265SDimitry Andric raw_ostream &O, const char *Modifier) { 26081ad6265SDimitry Andric assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported"); 26181ad6265SDimitry Andric if (OpNo < MI->getNumOperands()) { 26281ad6265SDimitry Andric const MCOperand &Op = MI->getOperand(OpNo); 26381ad6265SDimitry Andric if (Op.isReg()) 26481ad6265SDimitry Andric O << '%' << (Register::virtReg2Index(Op.getReg()) + 1); 26581ad6265SDimitry Andric else if (Op.isImm()) 26681ad6265SDimitry Andric O << formatImm((int64_t)Op.getImm()); 26781ad6265SDimitry Andric else if (Op.isDFPImm()) 26881ad6265SDimitry Andric O << formatImm((double)Op.getDFPImm()); 26981ad6265SDimitry Andric else if (Op.isExpr()) 27081ad6265SDimitry Andric printExpr(Op.getExpr(), O); 27181ad6265SDimitry Andric else 27281ad6265SDimitry Andric llvm_unreachable("Unexpected operand type"); 27381ad6265SDimitry Andric } 27481ad6265SDimitry Andric } 27581ad6265SDimitry Andric 27681ad6265SDimitry Andric void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo, 27781ad6265SDimitry Andric raw_ostream &O) { 27881ad6265SDimitry Andric const unsigned NumOps = MI->getNumOperands(); 27981ad6265SDimitry Andric unsigned StrStartIndex = OpNo; 28081ad6265SDimitry Andric while (StrStartIndex < NumOps) { 28181ad6265SDimitry Andric if (MI->getOperand(StrStartIndex).isReg()) 28281ad6265SDimitry Andric break; 28381ad6265SDimitry Andric 28481ad6265SDimitry Andric std::string Str = getSPIRVStringOperand(*MI, OpNo); 28581ad6265SDimitry Andric if (StrStartIndex != OpNo) 28681ad6265SDimitry Andric O << ' '; // Add a space if we're starting a new string/argument. 28781ad6265SDimitry Andric O << '"'; 28881ad6265SDimitry Andric for (char c : Str) { 28981ad6265SDimitry Andric if (c == '"') 29081ad6265SDimitry Andric O.write('\\'); // Escape " characters (might break for complex UTF-8). 29181ad6265SDimitry Andric O.write(c); 29281ad6265SDimitry Andric } 29381ad6265SDimitry Andric O << '"'; 29481ad6265SDimitry Andric 29581ad6265SDimitry Andric unsigned numOpsInString = (Str.size() / 4) + 1; 29681ad6265SDimitry Andric StrStartIndex += numOpsInString; 29781ad6265SDimitry Andric 29881ad6265SDimitry Andric // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute". 29981ad6265SDimitry Andric if (MI->getOpcode() == SPIRV::OpDecorate && 30081ad6265SDimitry Andric MI->getOperand(1).getImm() == 301*bdd1243dSDimitry Andric static_cast<unsigned>(Decoration::LinkageAttributes)) { 30281ad6265SDimitry Andric O << ' '; 303*bdd1243dSDimitry Andric printSymbolicOperand<OperandCategory::LinkageTypeOperand>( 304*bdd1243dSDimitry Andric MI, StrStartIndex, O); 30581ad6265SDimitry Andric break; 30681ad6265SDimitry Andric } 30781ad6265SDimitry Andric } 30881ad6265SDimitry Andric } 30981ad6265SDimitry Andric 310*bdd1243dSDimitry Andric void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo, 31181ad6265SDimitry Andric raw_ostream &O) { 312*bdd1243dSDimitry Andric auto SetReg = MI->getOperand(2).getReg(); 313*bdd1243dSDimitry Andric auto Set = ExtInstSetIDs[SetReg]; 314*bdd1243dSDimitry Andric auto Op = MI->getOperand(OpNo).getImm(); 315*bdd1243dSDimitry Andric O << getExtInstName(Set, Op); 31681ad6265SDimitry Andric } 31781ad6265SDimitry Andric 318*bdd1243dSDimitry Andric template <OperandCategory::OperandCategory category> 319*bdd1243dSDimitry Andric void SPIRVInstPrinter::printSymbolicOperand(const MCInst *MI, unsigned OpNo, 32081ad6265SDimitry Andric raw_ostream &O) { 32181ad6265SDimitry Andric if (OpNo < MI->getNumOperands()) { 322*bdd1243dSDimitry Andric O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm()); 32381ad6265SDimitry Andric } 32481ad6265SDimitry Andric } 325