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