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 "SPIRVInstrInfo.h" 17 #include "llvm/ADT/APFloat.h" 18 #include "llvm/CodeGen/Register.h" 19 #include "llvm/MC/MCAsmInfo.h" 20 #include "llvm/MC/MCExpr.h" 21 #include "llvm/MC/MCInst.h" 22 #include "llvm/MC/MCInstrInfo.h" 23 #include "llvm/MC/MCSymbol.h" 24 #include "llvm/Support/Casting.h" 25 #include "llvm/Support/ErrorHandling.h" 26 #include "llvm/Support/FormattedStream.h" 27 28 using namespace llvm; 29 using namespace llvm::SPIRV; 30 31 #define DEBUG_TYPE "asm-printer" 32 33 // Include the auto-generated portion of the assembly writer. 34 #include "SPIRVGenAsmWriter.inc" 35 36 void SPIRVInstPrinter::printRemainingVariableOps(const MCInst *MI, 37 unsigned StartIndex, 38 raw_ostream &O, 39 bool SkipFirstSpace, 40 bool SkipImmediates) { 41 const unsigned NumOps = MI->getNumOperands(); 42 for (unsigned i = StartIndex; i < NumOps; ++i) { 43 if (!SkipImmediates || !MI->getOperand(i).isImm()) { 44 if (!SkipFirstSpace || i != StartIndex) 45 O << ' '; 46 printOperand(MI, i, O); 47 } 48 } 49 } 50 51 void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI, 52 unsigned StartIndex, 53 raw_ostream &O) { 54 unsigned IsBitwidth16 = MI->getFlags() & SPIRV::ASM_PRINTER_WIDTH16; 55 const unsigned NumVarOps = MI->getNumOperands() - StartIndex; 56 57 assert((NumVarOps == 1 || NumVarOps == 2) && 58 "Unsupported number of bits for literal variable"); 59 60 O << ' '; 61 62 uint64_t Imm = MI->getOperand(StartIndex).getImm(); 63 64 // Handle 64 bit literals. 65 if (NumVarOps == 2) { 66 Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32); 67 } 68 69 // Format and print float values. 70 if (MI->getOpcode() == SPIRV::OpConstantF && IsBitwidth16 == 0) { 71 APFloat FP = NumVarOps == 1 ? APFloat(APInt(32, Imm).bitsToFloat()) 72 : APFloat(APInt(64, Imm).bitsToDouble()); 73 74 // Print infinity and NaN as hex floats. 75 // TODO: Make sure subnormal numbers are handled correctly as they may also 76 // require hex float notation. 77 if (FP.isInfinity()) { 78 if (FP.isNegative()) 79 O << '-'; 80 O << "0x1p+128"; 81 return; 82 } 83 if (FP.isNaN()) { 84 O << "0x1.8p+128"; 85 return; 86 } 87 88 // Format val as a decimal floating point or scientific notation (whichever 89 // is shorter), with enough digits of precision to produce the exact value. 90 O << format("%.*g", std::numeric_limits<double>::max_digits10, 91 FP.convertToDouble()); 92 93 return; 94 } 95 96 // Print integer values directly. 97 O << Imm; 98 } 99 100 void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) { 101 Register Reg = MI->getOperand(0).getReg(); 102 auto Name = getSPIRVStringOperand(*MI, 1); 103 auto Set = getExtInstSetFromString(Name); 104 ExtInstSetIDs.insert({Reg, Set}); 105 } 106 107 void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address, 108 StringRef Annot, const MCSubtargetInfo &STI, 109 raw_ostream &OS) { 110 const unsigned OpCode = MI->getOpcode(); 111 printInstruction(MI, Address, OS); 112 113 if (OpCode == SPIRV::OpDecorate) { 114 printOpDecorate(MI, OS); 115 } else if (OpCode == SPIRV::OpExtInstImport) { 116 recordOpExtInstImport(MI); 117 } else if (OpCode == SPIRV::OpExtInst) { 118 printOpExtInst(MI, OS); 119 } else { 120 // Print any extra operands for variadic instructions. 121 const MCInstrDesc &MCDesc = MII.get(OpCode); 122 if (MCDesc.isVariadic()) { 123 const unsigned NumFixedOps = MCDesc.getNumOperands(); 124 const unsigned LastFixedIndex = NumFixedOps - 1; 125 const int FirstVariableIndex = NumFixedOps; 126 if (NumFixedOps > 0 && MCDesc.operands()[LastFixedIndex].OperandType == 127 MCOI::OPERAND_UNKNOWN) { 128 // For instructions where a custom type (not reg or immediate) comes as 129 // the last operand before the variable_ops. This is usually a StringImm 130 // operand, but there are a few other cases. 131 switch (OpCode) { 132 case SPIRV::OpTypeImage: 133 OS << ' '; 134 printSymbolicOperand<OperandCategory::AccessQualifierOperand>( 135 MI, FirstVariableIndex, OS); 136 break; 137 case SPIRV::OpVariable: 138 OS << ' '; 139 printOperand(MI, FirstVariableIndex, OS); 140 break; 141 case SPIRV::OpEntryPoint: { 142 // Print the interface ID operands, skipping the name's string 143 // literal. 144 printRemainingVariableOps(MI, NumFixedOps, OS, false, true); 145 break; 146 } 147 case SPIRV::OpExecutionMode: 148 case SPIRV::OpExecutionModeId: 149 case SPIRV::OpLoopMerge: { 150 // Print any literals after the OPERAND_UNKNOWN argument normally. 151 printRemainingVariableOps(MI, NumFixedOps, OS); 152 break; 153 } 154 default: 155 break; // printStringImm has already been handled. 156 } 157 } else { 158 // For instructions with no fixed ops or a reg/immediate as the final 159 // fixed operand, we can usually print the rest with "printOperand", but 160 // check for a few cases with custom types first. 161 switch (OpCode) { 162 case SPIRV::OpLoad: 163 case SPIRV::OpStore: 164 OS << ' '; 165 printSymbolicOperand<OperandCategory::MemoryOperandOperand>( 166 MI, FirstVariableIndex, OS); 167 printRemainingVariableOps(MI, FirstVariableIndex + 1, OS); 168 break; 169 case SPIRV::OpImageSampleImplicitLod: 170 case SPIRV::OpImageSampleDrefImplicitLod: 171 case SPIRV::OpImageSampleProjImplicitLod: 172 case SPIRV::OpImageSampleProjDrefImplicitLod: 173 case SPIRV::OpImageFetch: 174 case SPIRV::OpImageGather: 175 case SPIRV::OpImageDrefGather: 176 case SPIRV::OpImageRead: 177 case SPIRV::OpImageWrite: 178 case SPIRV::OpImageSparseSampleImplicitLod: 179 case SPIRV::OpImageSparseSampleDrefImplicitLod: 180 case SPIRV::OpImageSparseSampleProjImplicitLod: 181 case SPIRV::OpImageSparseSampleProjDrefImplicitLod: 182 case SPIRV::OpImageSparseFetch: 183 case SPIRV::OpImageSparseGather: 184 case SPIRV::OpImageSparseDrefGather: 185 case SPIRV::OpImageSparseRead: 186 case SPIRV::OpImageSampleFootprintNV: 187 OS << ' '; 188 printSymbolicOperand<OperandCategory::ImageOperandOperand>( 189 MI, FirstVariableIndex, OS); 190 printRemainingVariableOps(MI, NumFixedOps + 1, OS); 191 break; 192 case SPIRV::OpCopyMemory: 193 case SPIRV::OpCopyMemorySized: { 194 const unsigned NumOps = MI->getNumOperands(); 195 for (unsigned i = NumFixedOps; i < NumOps; ++i) { 196 OS << ' '; 197 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i, 198 OS); 199 if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) { 200 assert(i + 1 < NumOps && "Missing alignment operand"); 201 OS << ' '; 202 printOperand(MI, i + 1, OS); 203 i += 1; 204 } 205 } 206 break; 207 } 208 case SPIRV::OpConstantI: 209 case SPIRV::OpConstantF: 210 // The last fixed operand along with any variadic operands that follow 211 // are part of the variable value. 212 printOpConstantVarOps(MI, NumFixedOps - 1, OS); 213 break; 214 case SPIRV::OpCooperativeMatrixMulAddKHR: { 215 const unsigned NumOps = MI->getNumOperands(); 216 if (NumFixedOps == NumOps) 217 break; 218 219 OS << ' '; 220 const unsigned MulAddOp = MI->getOperand(FirstVariableIndex).getImm(); 221 if (MulAddOp == 0) { 222 printSymbolicOperand< 223 OperandCategory::CooperativeMatrixOperandsOperand>( 224 MI, FirstVariableIndex, OS); 225 } else { 226 std::string Buffer; 227 for (unsigned Mask = 0x1; 228 Mask != SPIRV::CooperativeMatrixOperands:: 229 MatrixResultBFloat16ComponentsINTEL; 230 Mask <<= 1) { 231 if (MulAddOp & Mask) { 232 if (!Buffer.empty()) 233 Buffer += '|'; 234 Buffer += getSymbolicOperandMnemonic( 235 OperandCategory::CooperativeMatrixOperandsOperand, Mask); 236 } 237 } 238 OS << Buffer; 239 } 240 break; 241 } 242 default: 243 printRemainingVariableOps(MI, NumFixedOps, OS); 244 break; 245 } 246 } 247 } 248 } 249 250 printAnnotation(OS, Annot); 251 } 252 253 void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) { 254 // The fixed operands have already been printed, so just need to decide what 255 // type of ExtInst operands to print based on the instruction set and number. 256 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode()); 257 unsigned NumFixedOps = MCDesc.getNumOperands(); 258 const auto NumOps = MI->getNumOperands(); 259 if (NumOps == NumFixedOps) 260 return; 261 262 O << ' '; 263 264 // TODO: implement special printing for OpenCLExtInst::vstor*. 265 printRemainingVariableOps(MI, NumFixedOps, O, true); 266 } 267 268 void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) { 269 // The fixed operands have already been printed, so just need to decide what 270 // type of decoration operands to print based on the Decoration type. 271 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode()); 272 unsigned NumFixedOps = MCDesc.getNumOperands(); 273 274 if (NumFixedOps != MI->getNumOperands()) { 275 auto DecOp = MI->getOperand(NumFixedOps - 1); 276 auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm()); 277 278 O << ' '; 279 280 switch (Dec) { 281 case Decoration::BuiltIn: 282 printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O); 283 break; 284 case Decoration::UniformId: 285 printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O); 286 break; 287 case Decoration::FuncParamAttr: 288 printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>( 289 MI, NumFixedOps, O); 290 break; 291 case Decoration::FPRoundingMode: 292 printSymbolicOperand<OperandCategory::FPRoundingModeOperand>( 293 MI, NumFixedOps, O); 294 break; 295 case Decoration::FPFastMathMode: 296 printSymbolicOperand<OperandCategory::FPFastMathModeOperand>( 297 MI, NumFixedOps, O); 298 break; 299 case Decoration::LinkageAttributes: 300 case Decoration::UserSemantic: 301 printStringImm(MI, NumFixedOps, O); 302 break; 303 case Decoration::HostAccessINTEL: 304 printOperand(MI, NumFixedOps, O); 305 if (NumFixedOps + 1 < MI->getNumOperands()) { 306 O << ' '; 307 printStringImm(MI, NumFixedOps + 1, O); 308 } 309 break; 310 default: 311 printRemainingVariableOps(MI, NumFixedOps, O, true); 312 break; 313 } 314 } 315 } 316 317 static void printExpr(const MCExpr *Expr, raw_ostream &O) { 318 #ifndef NDEBUG 319 const MCSymbolRefExpr *SRE; 320 321 if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr)) 322 SRE = cast<MCSymbolRefExpr>(BE->getLHS()); 323 else 324 SRE = cast<MCSymbolRefExpr>(Expr); 325 326 MCSymbolRefExpr::VariantKind Kind = SRE->getKind(); 327 328 assert(Kind == MCSymbolRefExpr::VK_None); 329 #endif 330 O << *Expr; 331 } 332 333 void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, 334 raw_ostream &O, const char *Modifier) { 335 assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported"); 336 if (OpNo < MI->getNumOperands()) { 337 const MCOperand &Op = MI->getOperand(OpNo); 338 if (Op.isReg()) 339 O << '%' << (Register::virtReg2Index(Op.getReg()) + 1); 340 else if (Op.isImm()) 341 O << formatImm((int64_t)Op.getImm()); 342 else if (Op.isDFPImm()) 343 O << formatImm((double)Op.getDFPImm()); 344 else if (Op.isExpr()) 345 printExpr(Op.getExpr(), O); 346 else 347 llvm_unreachable("Unexpected operand type"); 348 } 349 } 350 351 void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo, 352 raw_ostream &O) { 353 const unsigned NumOps = MI->getNumOperands(); 354 unsigned StrStartIndex = OpNo; 355 while (StrStartIndex < NumOps) { 356 if (MI->getOperand(StrStartIndex).isReg()) 357 break; 358 359 std::string Str = getSPIRVStringOperand(*MI, StrStartIndex); 360 if (StrStartIndex != OpNo) 361 O << ' '; // Add a space if we're starting a new string/argument. 362 O << '"'; 363 for (char c : Str) { 364 // Escape ", \n characters (might break for complex UTF-8). 365 if (c == '\n') { 366 O.write("\\n", 2); 367 } else { 368 if (c == '"') 369 O.write('\\'); 370 O.write(c); 371 } 372 } 373 O << '"'; 374 375 unsigned numOpsInString = (Str.size() / 4) + 1; 376 StrStartIndex += numOpsInString; 377 378 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute". 379 if (MI->getOpcode() == SPIRV::OpDecorate && 380 MI->getOperand(1).getImm() == 381 static_cast<unsigned>(Decoration::LinkageAttributes)) { 382 O << ' '; 383 printSymbolicOperand<OperandCategory::LinkageTypeOperand>( 384 MI, StrStartIndex, O); 385 break; 386 } 387 } 388 } 389 390 void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo, 391 raw_ostream &O) { 392 auto SetReg = MI->getOperand(2).getReg(); 393 auto Set = ExtInstSetIDs[SetReg]; 394 auto Op = MI->getOperand(OpNo).getImm(); 395 O << getExtInstName(Set, Op); 396 } 397 398 template <OperandCategory::OperandCategory category> 399 void SPIRVInstPrinter::printSymbolicOperand(const MCInst *MI, unsigned OpNo, 400 raw_ostream &O) { 401 if (OpNo < MI->getNumOperands()) { 402 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm()); 403 } 404 } 405