507 lines · cpp
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-exception6//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/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/ErrorHandling.h"23 24using namespace llvm;25using namespace llvm::SPIRV;26 27#define DEBUG_TYPE "asm-printer"28 29// Include the auto-generated portion of the assembly writer.30#include "SPIRVGenAsmWriter.inc"31 32void SPIRVInstPrinter::printRemainingVariableOps(const MCInst *MI,33 unsigned StartIndex,34 raw_ostream &O,35 bool SkipFirstSpace,36 bool SkipImmediates) {37 const unsigned NumOps = MI->getNumOperands();38 for (unsigned i = StartIndex; i < NumOps; ++i) {39 if (!SkipImmediates || !MI->getOperand(i).isImm()) {40 if (!SkipFirstSpace || i != StartIndex)41 O << ' ';42 printOperand(MI, i, O);43 }44 }45}46 47void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI,48 unsigned StartIndex,49 raw_ostream &O) {50 unsigned IsBitwidth16 = MI->getFlags() & SPIRV::INST_PRINTER_WIDTH16;51 const unsigned NumVarOps = MI->getNumOperands() - StartIndex;52 53 assert((NumVarOps == 1 || NumVarOps == 2) &&54 "Unsupported number of bits for literal variable");55 56 O << ' ';57 58 uint64_t Imm = MI->getOperand(StartIndex).getImm();59 60 // Handle 64 bit literals.61 if (NumVarOps == 2) {62 Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32);63 }64 65 // Format and print float values.66 if (MI->getOpcode() == SPIRV::OpConstantF && IsBitwidth16 == 0) {67 APFloat FP = NumVarOps == 1 ? APFloat(APInt(32, Imm).bitsToFloat())68 : APFloat(APInt(64, Imm).bitsToDouble());69 70 // Print infinity and NaN as hex floats.71 // TODO: Make sure subnormal numbers are handled correctly as they may also72 // require hex float notation.73 if (FP.isInfinity()) {74 if (FP.isNegative())75 O << '-';76 O << "0x1p+128";77 return;78 }79 if (FP.isNaN()) {80 O << "0x1.8p+128";81 return;82 }83 84 // Format val as a decimal floating point or scientific notation (whichever85 // is shorter), with enough digits of precision to produce the exact value.86 O << format("%.*g", std::numeric_limits<double>::max_digits10,87 FP.convertToDouble());88 89 return;90 }91 92 // Print integer values directly.93 O << Imm;94}95 96void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {97 MCRegister Reg = MI->getOperand(0).getReg();98 auto Name = getSPIRVStringOperand(*MI, 1);99 auto Set = getExtInstSetFromString(std::move(Name));100 ExtInstSetIDs.insert({Reg, Set});101}102 103void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,104 StringRef Annot, const MCSubtargetInfo &STI,105 raw_ostream &OS) {106 const unsigned OpCode = MI->getOpcode();107 printInstruction(MI, Address, OS);108 109 if (OpCode == SPIRV::OpDecorate) {110 printOpDecorate(MI, OS);111 } else if (OpCode == SPIRV::OpExtInstImport) {112 recordOpExtInstImport(MI);113 } else if (OpCode == SPIRV::OpExtInst) {114 printOpExtInst(MI, OS);115 } else if (OpCode == SPIRV::UNKNOWN_type) {116 printUnknownType(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 as127 // the last operand before the variable_ops. This is usually a StringImm128 // 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 string141 // literal.142 printRemainingVariableOps(MI, NumFixedOps, OS, false, true);143 break;144 }145 case SPIRV::OpMemberDecorate:146 printRemainingVariableOps(MI, NumFixedOps, OS);147 break;148 case SPIRV::OpExecutionMode:149 case SPIRV::OpExecutionModeId:150 case SPIRV::OpLoopMerge: {151 // Print any literals after the OPERAND_UNKNOWN argument normally.152 printRemainingVariableOps(MI, NumFixedOps, OS);153 break;154 }155 default:156 break; // printStringImm has already been handled.157 }158 } else {159 // For instructions with no fixed ops or a reg/immediate as the final160 // fixed operand, we can usually print the rest with "printOperand", but161 // check for a few cases with custom types first.162 switch (OpCode) {163 case SPIRV::OpLoad:164 case SPIRV::OpStore:165 OS << ' ';166 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(167 MI, FirstVariableIndex, OS);168 printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);169 break;170 case SPIRV::OpSwitch:171 if (MI->getFlags() & SPIRV::INST_PRINTER_WIDTH64) {172 // In binary format 64-bit types are split into two 32-bit operands,173 // but in text format combine these into a single 64-bit value as174 // this is what tools such as spirv-as require.175 const unsigned NumOps = MI->getNumOperands();176 for (unsigned OpIdx = NumFixedOps; OpIdx < NumOps;) {177 if (OpIdx + 1 >= NumOps || !MI->getOperand(OpIdx).isImm() ||178 !MI->getOperand(OpIdx + 1).isImm()) {179 llvm_unreachable("Unexpected OpSwitch operands");180 continue;181 }182 OS << ' ';183 uint64_t LowBits = MI->getOperand(OpIdx).getImm();184 uint64_t HighBits = MI->getOperand(OpIdx + 1).getImm();185 uint64_t CombinedValue = (HighBits << 32) | LowBits;186 OS << formatImm(CombinedValue);187 OpIdx += 2;188 189 // Next should be the label190 if (OpIdx < NumOps) {191 OS << ' ';192 printOperand(MI, OpIdx, OS);193 OpIdx++;194 }195 }196 } else {197 printRemainingVariableOps(MI, NumFixedOps, OS);198 }199 break;200 case SPIRV::OpImageSampleImplicitLod:201 case SPIRV::OpImageSampleDrefImplicitLod:202 case SPIRV::OpImageSampleProjImplicitLod:203 case SPIRV::OpImageSampleProjDrefImplicitLod:204 case SPIRV::OpImageFetch:205 case SPIRV::OpImageGather:206 case SPIRV::OpImageDrefGather:207 case SPIRV::OpImageRead:208 case SPIRV::OpImageWrite:209 case SPIRV::OpImageSparseSampleImplicitLod:210 case SPIRV::OpImageSparseSampleDrefImplicitLod:211 case SPIRV::OpImageSparseSampleProjImplicitLod:212 case SPIRV::OpImageSparseSampleProjDrefImplicitLod:213 case SPIRV::OpImageSparseFetch:214 case SPIRV::OpImageSparseGather:215 case SPIRV::OpImageSparseDrefGather:216 case SPIRV::OpImageSparseRead:217 case SPIRV::OpImageSampleFootprintNV:218 OS << ' ';219 printSymbolicOperand<OperandCategory::ImageOperandOperand>(220 MI, FirstVariableIndex, OS);221 printRemainingVariableOps(MI, NumFixedOps + 1, OS);222 break;223 case SPIRV::OpCopyMemory:224 case SPIRV::OpCopyMemorySized: {225 const unsigned NumOps = MI->getNumOperands();226 for (unsigned i = NumFixedOps; i < NumOps; ++i) {227 OS << ' ';228 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i,229 OS);230 if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {231 assert(i + 1 < NumOps && "Missing alignment operand");232 OS << ' ';233 printOperand(MI, i + 1, OS);234 i += 1;235 }236 }237 break;238 }239 case SPIRV::OpConstantI:240 case SPIRV::OpConstantF:241 // The last fixed operand along with any variadic operands that follow242 // are part of the variable value.243 assert(NumFixedOps > 0 && "Expected at least one fixed operand");244 printOpConstantVarOps(MI, NumFixedOps - 1, OS);245 break;246 case SPIRV::OpCooperativeMatrixMulAddKHR: {247 const unsigned NumOps = MI->getNumOperands();248 if (NumFixedOps == NumOps)249 break;250 251 OS << ' ';252 const unsigned MulAddOp = MI->getOperand(FirstVariableIndex).getImm();253 if (MulAddOp == 0) {254 printSymbolicOperand<255 OperandCategory::CooperativeMatrixOperandsOperand>(256 MI, FirstVariableIndex, OS);257 } else {258 std::string Buffer;259 for (unsigned Mask = 0x1;260 Mask != SPIRV::CooperativeMatrixOperands::261 MatrixResultBFloat16ComponentsINTEL;262 Mask <<= 1) {263 if (MulAddOp & Mask) {264 if (!Buffer.empty())265 Buffer += '|';266 Buffer += getSymbolicOperandMnemonic(267 OperandCategory::CooperativeMatrixOperandsOperand, Mask);268 }269 }270 OS << Buffer;271 }272 break;273 }274 case SPIRV::OpSubgroupMatrixMultiplyAccumulateINTEL: {275 const unsigned NumOps = MI->getNumOperands();276 if (NumFixedOps >= NumOps)277 break;278 OS << ' ';279 const unsigned Flags = MI->getOperand(NumOps - 1).getImm();280 if (Flags == 0) {281 printSymbolicOperand<282 OperandCategory::MatrixMultiplyAccumulateOperandsOperand>(283 MI, NumOps - 1, OS);284 } else {285 std::string Buffer;286 for (unsigned Mask = 0x1;287 Mask <= SPIRV::MatrixMultiplyAccumulateOperands::288 MatrixBPackedBFloat16INTEL;289 Mask <<= 1) {290 if (Flags & Mask) {291 if (!Buffer.empty())292 Buffer += '|';293 Buffer += getSymbolicOperandMnemonic(294 OperandCategory::MatrixMultiplyAccumulateOperandsOperand,295 Mask);296 }297 }298 OS << Buffer;299 }300 break;301 }302 case SPIRV::OpSDot:303 case SPIRV::OpUDot:304 case SPIRV::OpSUDot:305 case SPIRV::OpSDotAccSat:306 case SPIRV::OpUDotAccSat:307 case SPIRV::OpSUDotAccSat: {308 const unsigned NumOps = MI->getNumOperands();309 if (NumOps > NumFixedOps) {310 OS << ' ';311 printSymbolicOperand<OperandCategory::PackedVectorFormatsOperand>(312 MI, NumOps - 1, OS);313 break;314 }315 break;316 }317 case SPIRV::OpPredicatedLoadINTEL:318 case SPIRV::OpPredicatedStoreINTEL: {319 const unsigned NumOps = MI->getNumOperands();320 if (NumOps > NumFixedOps) {321 OS << ' ';322 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(323 MI, NumOps - 1, OS);324 break;325 }326 break;327 }328 default:329 printRemainingVariableOps(MI, NumFixedOps, OS);330 break;331 }332 }333 }334 }335 336 printAnnotation(OS, Annot);337}338 339void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) {340 // The fixed operands have already been printed, so just need to decide what341 // type of ExtInst operands to print based on the instruction set and number.342 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());343 unsigned NumFixedOps = MCDesc.getNumOperands();344 const auto NumOps = MI->getNumOperands();345 if (NumOps == NumFixedOps)346 return;347 348 O << ' ';349 350 // TODO: implement special printing for OpenCLExtInst::vstor*.351 printRemainingVariableOps(MI, NumFixedOps, O, true);352}353 354void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) {355 // The fixed operands have already been printed, so just need to decide what356 // type of decoration operands to print based on the Decoration type.357 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());358 unsigned NumFixedOps = MCDesc.getNumOperands();359 360 if (NumFixedOps != MI->getNumOperands()) {361 auto DecOp = MI->getOperand(NumFixedOps - 1);362 auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());363 364 O << ' ';365 366 switch (Dec) {367 case Decoration::BuiltIn:368 printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O);369 break;370 case Decoration::UniformId:371 printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O);372 break;373 case Decoration::FuncParamAttr:374 printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>(375 MI, NumFixedOps, O);376 break;377 case Decoration::FPRoundingMode:378 printSymbolicOperand<OperandCategory::FPRoundingModeOperand>(379 MI, NumFixedOps, O);380 break;381 case Decoration::FPFastMathMode:382 printSymbolicOperand<OperandCategory::FPFastMathModeOperand>(383 MI, NumFixedOps, O);384 break;385 case Decoration::LinkageAttributes:386 case Decoration::UserSemantic:387 printStringImm(MI, NumFixedOps, O);388 break;389 case Decoration::HostAccessINTEL:390 printOperand(MI, NumFixedOps, O);391 if (NumFixedOps + 1 < MI->getNumOperands()) {392 O << ' ';393 printStringImm(MI, NumFixedOps + 1, O);394 }395 break;396 default:397 printRemainingVariableOps(MI, NumFixedOps, O, true);398 break;399 }400 }401}402 403void SPIRVInstPrinter::printUnknownType(const MCInst *MI, raw_ostream &O) {404 const auto EnumOperand = MI->getOperand(1);405 assert(EnumOperand.isImm() &&406 "second operand of UNKNOWN_type must be opcode!");407 408 const auto Enumerant = EnumOperand.getImm();409 const auto NumOps = MI->getNumOperands();410 411 // Print the opcode using the spirv-as unknown opcode syntax412 O << "OpUnknown(" << Enumerant << ", " << NumOps << ") ";413 414 // The result ID must be printed after the opcode when using this syntax415 printOperand(MI, 0, O);416 417 O << " ";418 419 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());420 unsigned NumFixedOps = MCDesc.getNumOperands();421 if (NumOps == NumFixedOps)422 return;423 424 // Print the rest of the operands425 printRemainingVariableOps(MI, NumFixedOps, O, true);426}427 428void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,429 raw_ostream &O) {430 if (OpNo < MI->getNumOperands()) {431 const MCOperand &Op = MI->getOperand(OpNo);432 if (Op.isReg())433 O << '%' << (getIDFromRegister(Op.getReg().id()) + 1);434 else if (Op.isImm()) {435 int64_t Imm = Op.getImm();436 // For OpVectorShuffle:437 // A Component literal may also be FFFFFFFF, which means the corresponding438 // result component has no source and is undefined.439 // LLVM representation of poison/undef becomes -1 when lowered to MI.440 if (MI->getOpcode() == SPIRV::OpVectorShuffle && Imm == -1)441 O << "0xFFFFFFFF";442 else443 O << formatImm(Imm);444 } else if (Op.isDFPImm())445 O << formatImm((double)Op.getDFPImm());446 else if (Op.isExpr())447 MAI.printExpr(O, *Op.getExpr());448 else449 llvm_unreachable("Unexpected operand type");450 }451}452 453void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,454 raw_ostream &O) {455 const unsigned NumOps = MI->getNumOperands();456 unsigned StrStartIndex = OpNo;457 while (StrStartIndex < NumOps) {458 if (MI->getOperand(StrStartIndex).isReg())459 break;460 461 std::string Str = getSPIRVStringOperand(*MI, StrStartIndex);462 if (StrStartIndex != OpNo)463 O << ' '; // Add a space if we're starting a new string/argument.464 O << '"';465 for (char c : Str) {466 // Escape ", \n characters (might break for complex UTF-8).467 if (c == '\n') {468 O.write("\\n", 2);469 } else {470 if (c == '"')471 O.write('\\');472 O.write(c);473 }474 }475 O << '"';476 477 unsigned numOpsInString = (Str.size() / 4) + 1;478 StrStartIndex += numOpsInString;479 480 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".481 if (MI->getOpcode() == SPIRV::OpDecorate &&482 MI->getOperand(1).getImm() ==483 static_cast<unsigned>(Decoration::LinkageAttributes)) {484 O << ' ';485 printSymbolicOperand<OperandCategory::LinkageTypeOperand>(486 MI, StrStartIndex, O);487 break;488 }489 }490}491 492void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,493 raw_ostream &O) {494 auto SetReg = MI->getOperand(2).getReg();495 auto Set = ExtInstSetIDs[SetReg];496 auto Op = MI->getOperand(OpNo).getImm();497 O << getExtInstName(Set, Op);498}499 500template <OperandCategory::OperandCategory category>501void SPIRVInstPrinter::printSymbolicOperand(const MCInst *MI, unsigned OpNo,502 raw_ostream &O) {503 if (OpNo < MI->getNumOperands()) {504 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());505 }506}507