brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.9 KiB · 62f5e47 Raw
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