1 //===-- SPIRVInstPrinter.cpp - Output SPIR-V MCInsts as ASM -----*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This class prints a SPIR-V MCInst to a .s file. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "SPIRVInstPrinter.h" 14 #include "SPIRV.h" 15 #include "SPIRVBaseInfo.h" 16 #include "llvm/CodeGen/Register.h" 17 #include "llvm/MC/MCAsmInfo.h" 18 #include "llvm/MC/MCExpr.h" 19 #include "llvm/MC/MCInst.h" 20 #include "llvm/MC/MCInstrInfo.h" 21 #include "llvm/MC/MCSymbol.h" 22 #include "llvm/Support/Casting.h" 23 #include "llvm/Support/ErrorHandling.h" 24 #include "llvm/Support/FormattedStream.h" 25 26 using namespace llvm; 27 using namespace llvm::SPIRV; 28 29 #define DEBUG_TYPE "asm-printer" 30 31 // Include the auto-generated portion of the assembly writer. 32 #include "SPIRVGenAsmWriter.inc" 33 34 void SPIRVInstPrinter::printRemainingVariableOps(const MCInst *MI, 35 unsigned StartIndex, 36 raw_ostream &O, 37 bool SkipFirstSpace, 38 bool SkipImmediates) { 39 const unsigned NumOps = MI->getNumOperands(); 40 for (unsigned i = StartIndex; i < NumOps; ++i) { 41 if (!SkipImmediates || !MI->getOperand(i).isImm()) { 42 if (!SkipFirstSpace || i != StartIndex) 43 O << ' '; 44 printOperand(MI, i, O); 45 } 46 } 47 } 48 49 void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI, 50 unsigned StartIndex, 51 raw_ostream &O) { 52 O << ' '; 53 if (MI->getNumOperands() - StartIndex == 2) { // Handle 64 bit literals. 54 uint64_t Imm = MI->getOperand(StartIndex).getImm(); 55 Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32); 56 O << Imm; 57 } else { 58 printRemainingVariableOps(MI, StartIndex, O, true, false); 59 } 60 } 61 62 void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) { 63 Register Reg = MI->getOperand(0).getReg(); 64 auto Name = getSPIRVStringOperand(*MI, 1); 65 auto Set = getExtInstSetFromString(Name); 66 ExtInstSetIDs.insert({Reg, Set}); 67 } 68 69 void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address, 70 StringRef Annot, const MCSubtargetInfo &STI, 71 raw_ostream &OS) { 72 const unsigned OpCode = MI->getOpcode(); 73 printInstruction(MI, Address, OS); 74 75 if (OpCode == SPIRV::OpDecorate) { 76 printOpDecorate(MI, OS); 77 } else if (OpCode == SPIRV::OpExtInstImport) { 78 recordOpExtInstImport(MI); 79 } else if (OpCode == SPIRV::OpExtInst) { 80 printOpExtInst(MI, OS); 81 } else { 82 // Print any extra operands for variadic instructions. 83 const MCInstrDesc &MCDesc = MII.get(OpCode); 84 if (MCDesc.isVariadic()) { 85 const unsigned NumFixedOps = MCDesc.getNumOperands(); 86 const unsigned LastFixedIndex = NumFixedOps - 1; 87 const int FirstVariableIndex = NumFixedOps; 88 if (NumFixedOps > 0 && MCDesc.operands()[LastFixedIndex].OperandType == 89 MCOI::OPERAND_UNKNOWN) { 90 // For instructions where a custom type (not reg or immediate) comes as 91 // the last operand before the variable_ops. This is usually a StringImm 92 // operand, but there are a few other cases. 93 switch (OpCode) { 94 case SPIRV::OpTypeImage: 95 OS << ' '; 96 printSymbolicOperand<OperandCategory::AccessQualifierOperand>( 97 MI, FirstVariableIndex, OS); 98 break; 99 case SPIRV::OpVariable: 100 OS << ' '; 101 printOperand(MI, FirstVariableIndex, OS); 102 break; 103 case SPIRV::OpEntryPoint: { 104 // Print the interface ID operands, skipping the name's string 105 // literal. 106 printRemainingVariableOps(MI, NumFixedOps, OS, false, true); 107 break; 108 } 109 case SPIRV::OpExecutionMode: 110 case SPIRV::OpExecutionModeId: 111 case SPIRV::OpLoopMerge: { 112 // Print any literals after the OPERAND_UNKNOWN argument normally. 113 printRemainingVariableOps(MI, NumFixedOps, OS); 114 break; 115 } 116 default: 117 break; // printStringImm has already been handled. 118 } 119 } else { 120 // For instructions with no fixed ops or a reg/immediate as the final 121 // fixed operand, we can usually print the rest with "printOperand", but 122 // check for a few cases with custom types first. 123 switch (OpCode) { 124 case SPIRV::OpLoad: 125 case SPIRV::OpStore: 126 OS << ' '; 127 printSymbolicOperand<OperandCategory::MemoryOperandOperand>( 128 MI, FirstVariableIndex, OS); 129 printRemainingVariableOps(MI, FirstVariableIndex + 1, OS); 130 break; 131 case SPIRV::OpImageSampleImplicitLod: 132 case SPIRV::OpImageSampleDrefImplicitLod: 133 case SPIRV::OpImageSampleProjImplicitLod: 134 case SPIRV::OpImageSampleProjDrefImplicitLod: 135 case SPIRV::OpImageFetch: 136 case SPIRV::OpImageGather: 137 case SPIRV::OpImageDrefGather: 138 case SPIRV::OpImageRead: 139 case SPIRV::OpImageWrite: 140 case SPIRV::OpImageSparseSampleImplicitLod: 141 case SPIRV::OpImageSparseSampleDrefImplicitLod: 142 case SPIRV::OpImageSparseSampleProjImplicitLod: 143 case SPIRV::OpImageSparseSampleProjDrefImplicitLod: 144 case SPIRV::OpImageSparseFetch: 145 case SPIRV::OpImageSparseGather: 146 case SPIRV::OpImageSparseDrefGather: 147 case SPIRV::OpImageSparseRead: 148 case SPIRV::OpImageSampleFootprintNV: 149 OS << ' '; 150 printSymbolicOperand<OperandCategory::ImageOperandOperand>( 151 MI, FirstVariableIndex, OS); 152 printRemainingVariableOps(MI, NumFixedOps + 1, OS); 153 break; 154 case SPIRV::OpCopyMemory: 155 case SPIRV::OpCopyMemorySized: { 156 const unsigned NumOps = MI->getNumOperands(); 157 for (unsigned i = NumFixedOps; i < NumOps; ++i) { 158 OS << ' '; 159 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i, 160 OS); 161 if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) { 162 assert(i + 1 < NumOps && "Missing alignment operand"); 163 OS << ' '; 164 printOperand(MI, i + 1, OS); 165 i += 1; 166 } 167 } 168 break; 169 } 170 case SPIRV::OpConstantI: 171 case SPIRV::OpConstantF: 172 printOpConstantVarOps(MI, NumFixedOps, OS); 173 break; 174 default: 175 printRemainingVariableOps(MI, NumFixedOps, OS); 176 break; 177 } 178 } 179 } 180 } 181 182 printAnnotation(OS, Annot); 183 } 184 185 void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) { 186 // The fixed operands have already been printed, so just need to decide what 187 // type of ExtInst operands to print based on the instruction set and number. 188 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode()); 189 unsigned NumFixedOps = MCDesc.getNumOperands(); 190 const auto NumOps = MI->getNumOperands(); 191 if (NumOps == NumFixedOps) 192 return; 193 194 O << ' '; 195 196 // TODO: implement special printing for OpenCLExtInst::vstor*. 197 printRemainingVariableOps(MI, NumFixedOps, O, true); 198 } 199 200 void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) { 201 // The fixed operands have already been printed, so just need to decide what 202 // type of decoration operands to print based on the Decoration type. 203 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode()); 204 unsigned NumFixedOps = MCDesc.getNumOperands(); 205 206 if (NumFixedOps != MI->getNumOperands()) { 207 auto DecOp = MI->getOperand(NumFixedOps - 1); 208 auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm()); 209 210 O << ' '; 211 212 switch (Dec) { 213 case Decoration::BuiltIn: 214 printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O); 215 break; 216 case Decoration::UniformId: 217 printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O); 218 break; 219 case Decoration::FuncParamAttr: 220 printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>( 221 MI, NumFixedOps, O); 222 break; 223 case Decoration::FPRoundingMode: 224 printSymbolicOperand<OperandCategory::FPRoundingModeOperand>( 225 MI, NumFixedOps, O); 226 break; 227 case Decoration::FPFastMathMode: 228 printSymbolicOperand<OperandCategory::FPFastMathModeOperand>( 229 MI, NumFixedOps, O); 230 break; 231 case Decoration::LinkageAttributes: 232 case Decoration::UserSemantic: 233 printStringImm(MI, NumFixedOps, O); 234 break; 235 default: 236 printRemainingVariableOps(MI, NumFixedOps, O, true); 237 break; 238 } 239 } 240 } 241 242 static void printExpr(const MCExpr *Expr, raw_ostream &O) { 243 #ifndef NDEBUG 244 const MCSymbolRefExpr *SRE; 245 246 if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr)) 247 SRE = cast<MCSymbolRefExpr>(BE->getLHS()); 248 else 249 SRE = cast<MCSymbolRefExpr>(Expr); 250 251 MCSymbolRefExpr::VariantKind Kind = SRE->getKind(); 252 253 assert(Kind == MCSymbolRefExpr::VK_None); 254 #endif 255 O << *Expr; 256 } 257 258 void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, 259 raw_ostream &O, const char *Modifier) { 260 assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported"); 261 if (OpNo < MI->getNumOperands()) { 262 const MCOperand &Op = MI->getOperand(OpNo); 263 if (Op.isReg()) 264 O << '%' << (Register::virtReg2Index(Op.getReg()) + 1); 265 else if (Op.isImm()) 266 O << formatImm((int64_t)Op.getImm()); 267 else if (Op.isDFPImm()) 268 O << formatImm((double)Op.getDFPImm()); 269 else if (Op.isExpr()) 270 printExpr(Op.getExpr(), O); 271 else 272 llvm_unreachable("Unexpected operand type"); 273 } 274 } 275 276 void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo, 277 raw_ostream &O) { 278 const unsigned NumOps = MI->getNumOperands(); 279 unsigned StrStartIndex = OpNo; 280 while (StrStartIndex < NumOps) { 281 if (MI->getOperand(StrStartIndex).isReg()) 282 break; 283 284 std::string Str = getSPIRVStringOperand(*MI, OpNo); 285 if (StrStartIndex != OpNo) 286 O << ' '; // Add a space if we're starting a new string/argument. 287 O << '"'; 288 for (char c : Str) { 289 if (c == '"') 290 O.write('\\'); // Escape " characters (might break for complex UTF-8). 291 O.write(c); 292 } 293 O << '"'; 294 295 unsigned numOpsInString = (Str.size() / 4) + 1; 296 StrStartIndex += numOpsInString; 297 298 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute". 299 if (MI->getOpcode() == SPIRV::OpDecorate && 300 MI->getOperand(1).getImm() == 301 static_cast<unsigned>(Decoration::LinkageAttributes)) { 302 O << ' '; 303 printSymbolicOperand<OperandCategory::LinkageTypeOperand>( 304 MI, StrStartIndex, O); 305 break; 306 } 307 } 308 } 309 310 void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo, 311 raw_ostream &O) { 312 auto SetReg = MI->getOperand(2).getReg(); 313 auto Set = ExtInstSetIDs[SetReg]; 314 auto Op = MI->getOperand(OpNo).getImm(); 315 O << getExtInstName(Set, Op); 316 } 317 318 template <OperandCategory::OperandCategory category> 319 void SPIRVInstPrinter::printSymbolicOperand(const MCInst *MI, unsigned OpNo, 320 raw_ostream &O) { 321 if (OpNo < MI->getNumOperands()) { 322 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm()); 323 } 324 } 325