Path: blob/main/contrib/llvm-project/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
35294 views
//===-- SPIRVInstPrinter.cpp - Output SPIR-V MCInsts as ASM -----*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This class prints a SPIR-V MCInst to a .s file.9//10//===----------------------------------------------------------------------===//1112#include "SPIRVInstPrinter.h"13#include "SPIRV.h"14#include "SPIRVBaseInfo.h"15#include "SPIRVInstrInfo.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"2627using namespace llvm;28using namespace llvm::SPIRV;2930#define DEBUG_TYPE "asm-printer"3132// Include the auto-generated portion of the assembly writer.33#include "SPIRVGenAsmWriter.inc"3435void SPIRVInstPrinter::printRemainingVariableOps(const MCInst *MI,36unsigned StartIndex,37raw_ostream &O,38bool SkipFirstSpace,39bool SkipImmediates) {40const unsigned NumOps = MI->getNumOperands();41for (unsigned i = StartIndex; i < NumOps; ++i) {42if (!SkipImmediates || !MI->getOperand(i).isImm()) {43if (!SkipFirstSpace || i != StartIndex)44O << ' ';45printOperand(MI, i, O);46}47}48}4950void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI,51unsigned StartIndex,52raw_ostream &O) {53unsigned IsBitwidth16 = MI->getFlags() & SPIRV::ASM_PRINTER_WIDTH16;54const unsigned NumVarOps = MI->getNumOperands() - StartIndex;5556assert((NumVarOps == 1 || NumVarOps == 2) &&57"Unsupported number of bits for literal variable");5859O << ' ';6061uint64_t Imm = MI->getOperand(StartIndex).getImm();6263// Handle 64 bit literals.64if (NumVarOps == 2) {65Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32);66}6768// Format and print float values.69if (MI->getOpcode() == SPIRV::OpConstantF && IsBitwidth16 == 0) {70APFloat FP = NumVarOps == 1 ? APFloat(APInt(32, Imm).bitsToFloat())71: APFloat(APInt(64, Imm).bitsToDouble());7273// Print infinity and NaN as hex floats.74// TODO: Make sure subnormal numbers are handled correctly as they may also75// require hex float notation.76if (FP.isInfinity()) {77if (FP.isNegative())78O << '-';79O << "0x1p+128";80return;81}82if (FP.isNaN()) {83O << "0x1.8p+128";84return;85}8687// Format val as a decimal floating point or scientific notation (whichever88// is shorter), with enough digits of precision to produce the exact value.89O << format("%.*g", std::numeric_limits<double>::max_digits10,90FP.convertToDouble());9192return;93}9495// Print integer values directly.96O << Imm;97}9899void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {100Register Reg = MI->getOperand(0).getReg();101auto Name = getSPIRVStringOperand(*MI, 1);102auto Set = getExtInstSetFromString(Name);103ExtInstSetIDs.insert({Reg, Set});104}105106void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,107StringRef Annot, const MCSubtargetInfo &STI,108raw_ostream &OS) {109const unsigned OpCode = MI->getOpcode();110printInstruction(MI, Address, OS);111112if (OpCode == SPIRV::OpDecorate) {113printOpDecorate(MI, OS);114} else if (OpCode == SPIRV::OpExtInstImport) {115recordOpExtInstImport(MI);116} else if (OpCode == SPIRV::OpExtInst) {117printOpExtInst(MI, OS);118} else {119// Print any extra operands for variadic instructions.120const MCInstrDesc &MCDesc = MII.get(OpCode);121if (MCDesc.isVariadic()) {122const unsigned NumFixedOps = MCDesc.getNumOperands();123const unsigned LastFixedIndex = NumFixedOps - 1;124const int FirstVariableIndex = NumFixedOps;125if (NumFixedOps > 0 && MCDesc.operands()[LastFixedIndex].OperandType ==126MCOI::OPERAND_UNKNOWN) {127// For instructions where a custom type (not reg or immediate) comes as128// the last operand before the variable_ops. This is usually a StringImm129// operand, but there are a few other cases.130switch (OpCode) {131case SPIRV::OpTypeImage:132OS << ' ';133printSymbolicOperand<OperandCategory::AccessQualifierOperand>(134MI, FirstVariableIndex, OS);135break;136case SPIRV::OpVariable:137OS << ' ';138printOperand(MI, FirstVariableIndex, OS);139break;140case SPIRV::OpEntryPoint: {141// Print the interface ID operands, skipping the name's string142// literal.143printRemainingVariableOps(MI, NumFixedOps, OS, false, true);144break;145}146case SPIRV::OpExecutionMode:147case SPIRV::OpExecutionModeId:148case SPIRV::OpLoopMerge: {149// Print any literals after the OPERAND_UNKNOWN argument normally.150printRemainingVariableOps(MI, NumFixedOps, OS);151break;152}153default:154break; // printStringImm has already been handled.155}156} else {157// For instructions with no fixed ops or a reg/immediate as the final158// fixed operand, we can usually print the rest with "printOperand", but159// check for a few cases with custom types first.160switch (OpCode) {161case SPIRV::OpLoad:162case SPIRV::OpStore:163OS << ' ';164printSymbolicOperand<OperandCategory::MemoryOperandOperand>(165MI, FirstVariableIndex, OS);166printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);167break;168case SPIRV::OpImageSampleImplicitLod:169case SPIRV::OpImageSampleDrefImplicitLod:170case SPIRV::OpImageSampleProjImplicitLod:171case SPIRV::OpImageSampleProjDrefImplicitLod:172case SPIRV::OpImageFetch:173case SPIRV::OpImageGather:174case SPIRV::OpImageDrefGather:175case SPIRV::OpImageRead:176case SPIRV::OpImageWrite:177case SPIRV::OpImageSparseSampleImplicitLod:178case SPIRV::OpImageSparseSampleDrefImplicitLod:179case SPIRV::OpImageSparseSampleProjImplicitLod:180case SPIRV::OpImageSparseSampleProjDrefImplicitLod:181case SPIRV::OpImageSparseFetch:182case SPIRV::OpImageSparseGather:183case SPIRV::OpImageSparseDrefGather:184case SPIRV::OpImageSparseRead:185case SPIRV::OpImageSampleFootprintNV:186OS << ' ';187printSymbolicOperand<OperandCategory::ImageOperandOperand>(188MI, FirstVariableIndex, OS);189printRemainingVariableOps(MI, NumFixedOps + 1, OS);190break;191case SPIRV::OpCopyMemory:192case SPIRV::OpCopyMemorySized: {193const unsigned NumOps = MI->getNumOperands();194for (unsigned i = NumFixedOps; i < NumOps; ++i) {195OS << ' ';196printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i,197OS);198if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {199assert(i + 1 < NumOps && "Missing alignment operand");200OS << ' ';201printOperand(MI, i + 1, OS);202i += 1;203}204}205break;206}207case SPIRV::OpConstantI:208case SPIRV::OpConstantF:209// The last fixed operand along with any variadic operands that follow210// are part of the variable value.211printOpConstantVarOps(MI, NumFixedOps - 1, OS);212break;213default:214printRemainingVariableOps(MI, NumFixedOps, OS);215break;216}217}218}219}220221printAnnotation(OS, Annot);222}223224void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) {225// The fixed operands have already been printed, so just need to decide what226// type of ExtInst operands to print based on the instruction set and number.227const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());228unsigned NumFixedOps = MCDesc.getNumOperands();229const auto NumOps = MI->getNumOperands();230if (NumOps == NumFixedOps)231return;232233O << ' ';234235// TODO: implement special printing for OpenCLExtInst::vstor*.236printRemainingVariableOps(MI, NumFixedOps, O, true);237}238239void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) {240// The fixed operands have already been printed, so just need to decide what241// type of decoration operands to print based on the Decoration type.242const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());243unsigned NumFixedOps = MCDesc.getNumOperands();244245if (NumFixedOps != MI->getNumOperands()) {246auto DecOp = MI->getOperand(NumFixedOps - 1);247auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());248249O << ' ';250251switch (Dec) {252case Decoration::BuiltIn:253printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O);254break;255case Decoration::UniformId:256printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O);257break;258case Decoration::FuncParamAttr:259printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>(260MI, NumFixedOps, O);261break;262case Decoration::FPRoundingMode:263printSymbolicOperand<OperandCategory::FPRoundingModeOperand>(264MI, NumFixedOps, O);265break;266case Decoration::FPFastMathMode:267printSymbolicOperand<OperandCategory::FPFastMathModeOperand>(268MI, NumFixedOps, O);269break;270case Decoration::LinkageAttributes:271case Decoration::UserSemantic:272printStringImm(MI, NumFixedOps, O);273break;274case Decoration::HostAccessINTEL:275printOperand(MI, NumFixedOps, O);276if (NumFixedOps + 1 < MI->getNumOperands()) {277O << ' ';278printStringImm(MI, NumFixedOps + 1, O);279}280break;281default:282printRemainingVariableOps(MI, NumFixedOps, O, true);283break;284}285}286}287288static void printExpr(const MCExpr *Expr, raw_ostream &O) {289#ifndef NDEBUG290const MCSymbolRefExpr *SRE;291292if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr))293SRE = cast<MCSymbolRefExpr>(BE->getLHS());294else295SRE = cast<MCSymbolRefExpr>(Expr);296297MCSymbolRefExpr::VariantKind Kind = SRE->getKind();298299assert(Kind == MCSymbolRefExpr::VK_None);300#endif301O << *Expr;302}303304void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,305raw_ostream &O, const char *Modifier) {306assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported");307if (OpNo < MI->getNumOperands()) {308const MCOperand &Op = MI->getOperand(OpNo);309if (Op.isReg())310O << '%' << (Register::virtReg2Index(Op.getReg()) + 1);311else if (Op.isImm())312O << formatImm((int64_t)Op.getImm());313else if (Op.isDFPImm())314O << formatImm((double)Op.getDFPImm());315else if (Op.isExpr())316printExpr(Op.getExpr(), O);317else318llvm_unreachable("Unexpected operand type");319}320}321322void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,323raw_ostream &O) {324const unsigned NumOps = MI->getNumOperands();325unsigned StrStartIndex = OpNo;326while (StrStartIndex < NumOps) {327if (MI->getOperand(StrStartIndex).isReg())328break;329330std::string Str = getSPIRVStringOperand(*MI, StrStartIndex);331if (StrStartIndex != OpNo)332O << ' '; // Add a space if we're starting a new string/argument.333O << '"';334for (char c : Str) {335// Escape ", \n characters (might break for complex UTF-8).336if (c == '\n') {337O.write("\\n", 2);338} else {339if (c == '"')340O.write('\\');341O.write(c);342}343}344O << '"';345346unsigned numOpsInString = (Str.size() / 4) + 1;347StrStartIndex += numOpsInString;348349// Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".350if (MI->getOpcode() == SPIRV::OpDecorate &&351MI->getOperand(1).getImm() ==352static_cast<unsigned>(Decoration::LinkageAttributes)) {353O << ' ';354printSymbolicOperand<OperandCategory::LinkageTypeOperand>(355MI, StrStartIndex, O);356break;357}358}359}360361void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,362raw_ostream &O) {363auto SetReg = MI->getOperand(2).getReg();364auto Set = ExtInstSetIDs[SetReg];365auto Op = MI->getOperand(OpNo).getImm();366O << getExtInstName(Set, Op);367}368369template <OperandCategory::OperandCategory category>370void SPIRVInstPrinter::printSymbolicOperand(const MCInst *MI, unsigned OpNo,371raw_ostream &O) {372if (OpNo < MI->getNumOperands()) {373O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());374}375}376377378