xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
181ad6265SDimitry Andric //===-- SPIRVInstPrinter.cpp - Output SPIR-V MCInsts as ASM -----*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric // This class prints a SPIR-V MCInst to a .s file.
1081ad6265SDimitry Andric //
1181ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1281ad6265SDimitry Andric 
1381ad6265SDimitry Andric #include "SPIRVInstPrinter.h"
1481ad6265SDimitry Andric #include "SPIRV.h"
1581ad6265SDimitry Andric #include "SPIRVBaseInfo.h"
1681ad6265SDimitry Andric #include "llvm/CodeGen/Register.h"
1781ad6265SDimitry Andric #include "llvm/MC/MCAsmInfo.h"
1881ad6265SDimitry Andric #include "llvm/MC/MCExpr.h"
1981ad6265SDimitry Andric #include "llvm/MC/MCInst.h"
2081ad6265SDimitry Andric #include "llvm/MC/MCInstrInfo.h"
2181ad6265SDimitry Andric #include "llvm/MC/MCSymbol.h"
2281ad6265SDimitry Andric #include "llvm/Support/Casting.h"
2381ad6265SDimitry Andric #include "llvm/Support/ErrorHandling.h"
2481ad6265SDimitry Andric #include "llvm/Support/FormattedStream.h"
2581ad6265SDimitry Andric 
2681ad6265SDimitry Andric using namespace llvm;
27*bdd1243dSDimitry Andric using namespace llvm::SPIRV;
2881ad6265SDimitry Andric 
2981ad6265SDimitry Andric #define DEBUG_TYPE "asm-printer"
3081ad6265SDimitry Andric 
3181ad6265SDimitry Andric // Include the auto-generated portion of the assembly writer.
3281ad6265SDimitry Andric #include "SPIRVGenAsmWriter.inc"
3381ad6265SDimitry Andric 
3481ad6265SDimitry Andric void SPIRVInstPrinter::printRemainingVariableOps(const MCInst *MI,
3581ad6265SDimitry Andric                                                  unsigned StartIndex,
3681ad6265SDimitry Andric                                                  raw_ostream &O,
3781ad6265SDimitry Andric                                                  bool SkipFirstSpace,
3881ad6265SDimitry Andric                                                  bool SkipImmediates) {
3981ad6265SDimitry Andric   const unsigned NumOps = MI->getNumOperands();
4081ad6265SDimitry Andric   for (unsigned i = StartIndex; i < NumOps; ++i) {
4181ad6265SDimitry Andric     if (!SkipImmediates || !MI->getOperand(i).isImm()) {
4281ad6265SDimitry Andric       if (!SkipFirstSpace || i != StartIndex)
4381ad6265SDimitry Andric         O << ' ';
4481ad6265SDimitry Andric       printOperand(MI, i, O);
4581ad6265SDimitry Andric     }
4681ad6265SDimitry Andric   }
4781ad6265SDimitry Andric }
4881ad6265SDimitry Andric 
4981ad6265SDimitry Andric void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI,
5081ad6265SDimitry Andric                                              unsigned StartIndex,
5181ad6265SDimitry Andric                                              raw_ostream &O) {
5281ad6265SDimitry Andric   O << ' ';
5381ad6265SDimitry Andric   if (MI->getNumOperands() - StartIndex == 2) { // Handle 64 bit literals.
5481ad6265SDimitry Andric     uint64_t Imm = MI->getOperand(StartIndex).getImm();
5581ad6265SDimitry Andric     Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32);
5681ad6265SDimitry Andric     O << Imm;
5781ad6265SDimitry Andric   } else {
5881ad6265SDimitry Andric     printRemainingVariableOps(MI, StartIndex, O, true, false);
5981ad6265SDimitry Andric   }
6081ad6265SDimitry Andric }
6181ad6265SDimitry Andric 
6281ad6265SDimitry Andric void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {
63*bdd1243dSDimitry Andric   Register Reg = MI->getOperand(0).getReg();
64*bdd1243dSDimitry Andric   auto Name = getSPIRVStringOperand(*MI, 1);
65*bdd1243dSDimitry Andric   auto Set = getExtInstSetFromString(Name);
66*bdd1243dSDimitry Andric   ExtInstSetIDs.insert({Reg, Set});
6781ad6265SDimitry Andric }
6881ad6265SDimitry Andric 
6981ad6265SDimitry Andric void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
7081ad6265SDimitry Andric                                  StringRef Annot, const MCSubtargetInfo &STI,
7181ad6265SDimitry Andric                                  raw_ostream &OS) {
7281ad6265SDimitry Andric   const unsigned OpCode = MI->getOpcode();
7381ad6265SDimitry Andric   printInstruction(MI, Address, OS);
7481ad6265SDimitry Andric 
7581ad6265SDimitry Andric   if (OpCode == SPIRV::OpDecorate) {
7681ad6265SDimitry Andric     printOpDecorate(MI, OS);
7781ad6265SDimitry Andric   } else if (OpCode == SPIRV::OpExtInstImport) {
7881ad6265SDimitry Andric     recordOpExtInstImport(MI);
7981ad6265SDimitry Andric   } else if (OpCode == SPIRV::OpExtInst) {
8081ad6265SDimitry Andric     printOpExtInst(MI, OS);
8181ad6265SDimitry Andric   } else {
8281ad6265SDimitry Andric     // Print any extra operands for variadic instructions.
83*bdd1243dSDimitry Andric     const MCInstrDesc &MCDesc = MII.get(OpCode);
8481ad6265SDimitry Andric     if (MCDesc.isVariadic()) {
8581ad6265SDimitry Andric       const unsigned NumFixedOps = MCDesc.getNumOperands();
8681ad6265SDimitry Andric       const unsigned LastFixedIndex = NumFixedOps - 1;
8781ad6265SDimitry Andric       const int FirstVariableIndex = NumFixedOps;
88*bdd1243dSDimitry Andric       if (NumFixedOps > 0 && MCDesc.operands()[LastFixedIndex].OperandType ==
89*bdd1243dSDimitry Andric                                  MCOI::OPERAND_UNKNOWN) {
9081ad6265SDimitry Andric         // For instructions where a custom type (not reg or immediate) comes as
9181ad6265SDimitry Andric         // the last operand before the variable_ops. This is usually a StringImm
9281ad6265SDimitry Andric         // operand, but there are a few other cases.
9381ad6265SDimitry Andric         switch (OpCode) {
9481ad6265SDimitry Andric         case SPIRV::OpTypeImage:
9581ad6265SDimitry Andric           OS << ' ';
96*bdd1243dSDimitry Andric           printSymbolicOperand<OperandCategory::AccessQualifierOperand>(
97*bdd1243dSDimitry Andric               MI, FirstVariableIndex, OS);
9881ad6265SDimitry Andric           break;
9981ad6265SDimitry Andric         case SPIRV::OpVariable:
10081ad6265SDimitry Andric           OS << ' ';
10181ad6265SDimitry Andric           printOperand(MI, FirstVariableIndex, OS);
10281ad6265SDimitry Andric           break;
10381ad6265SDimitry Andric         case SPIRV::OpEntryPoint: {
10481ad6265SDimitry Andric           // Print the interface ID operands, skipping the name's string
10581ad6265SDimitry Andric           // literal.
10681ad6265SDimitry Andric           printRemainingVariableOps(MI, NumFixedOps, OS, false, true);
10781ad6265SDimitry Andric           break;
10881ad6265SDimitry Andric         }
10981ad6265SDimitry Andric         case SPIRV::OpExecutionMode:
11081ad6265SDimitry Andric         case SPIRV::OpExecutionModeId:
11181ad6265SDimitry Andric         case SPIRV::OpLoopMerge: {
11281ad6265SDimitry Andric           // Print any literals after the OPERAND_UNKNOWN argument normally.
11381ad6265SDimitry Andric           printRemainingVariableOps(MI, NumFixedOps, OS);
11481ad6265SDimitry Andric           break;
11581ad6265SDimitry Andric         }
11681ad6265SDimitry Andric         default:
117*bdd1243dSDimitry Andric           break; // printStringImm has already been handled.
11881ad6265SDimitry Andric         }
11981ad6265SDimitry Andric       } else {
12081ad6265SDimitry Andric         // For instructions with no fixed ops or a reg/immediate as the final
12181ad6265SDimitry Andric         // fixed operand, we can usually print the rest with "printOperand", but
12281ad6265SDimitry Andric         // check for a few cases with custom types first.
12381ad6265SDimitry Andric         switch (OpCode) {
12481ad6265SDimitry Andric         case SPIRV::OpLoad:
12581ad6265SDimitry Andric         case SPIRV::OpStore:
12681ad6265SDimitry Andric           OS << ' ';
127*bdd1243dSDimitry Andric           printSymbolicOperand<OperandCategory::MemoryOperandOperand>(
128*bdd1243dSDimitry Andric               MI, FirstVariableIndex, OS);
12981ad6265SDimitry Andric           printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
13081ad6265SDimitry Andric           break;
13181ad6265SDimitry Andric         case SPIRV::OpImageSampleImplicitLod:
13281ad6265SDimitry Andric         case SPIRV::OpImageSampleDrefImplicitLod:
13381ad6265SDimitry Andric         case SPIRV::OpImageSampleProjImplicitLod:
13481ad6265SDimitry Andric         case SPIRV::OpImageSampleProjDrefImplicitLod:
13581ad6265SDimitry Andric         case SPIRV::OpImageFetch:
13681ad6265SDimitry Andric         case SPIRV::OpImageGather:
13781ad6265SDimitry Andric         case SPIRV::OpImageDrefGather:
13881ad6265SDimitry Andric         case SPIRV::OpImageRead:
13981ad6265SDimitry Andric         case SPIRV::OpImageWrite:
14081ad6265SDimitry Andric         case SPIRV::OpImageSparseSampleImplicitLod:
14181ad6265SDimitry Andric         case SPIRV::OpImageSparseSampleDrefImplicitLod:
14281ad6265SDimitry Andric         case SPIRV::OpImageSparseSampleProjImplicitLod:
14381ad6265SDimitry Andric         case SPIRV::OpImageSparseSampleProjDrefImplicitLod:
14481ad6265SDimitry Andric         case SPIRV::OpImageSparseFetch:
14581ad6265SDimitry Andric         case SPIRV::OpImageSparseGather:
14681ad6265SDimitry Andric         case SPIRV::OpImageSparseDrefGather:
14781ad6265SDimitry Andric         case SPIRV::OpImageSparseRead:
14881ad6265SDimitry Andric         case SPIRV::OpImageSampleFootprintNV:
14981ad6265SDimitry Andric           OS << ' ';
150*bdd1243dSDimitry Andric           printSymbolicOperand<OperandCategory::ImageOperandOperand>(
151*bdd1243dSDimitry Andric               MI, FirstVariableIndex, OS);
15281ad6265SDimitry Andric           printRemainingVariableOps(MI, NumFixedOps + 1, OS);
15381ad6265SDimitry Andric           break;
15481ad6265SDimitry Andric         case SPIRV::OpCopyMemory:
15581ad6265SDimitry Andric         case SPIRV::OpCopyMemorySized: {
15681ad6265SDimitry Andric           const unsigned NumOps = MI->getNumOperands();
15781ad6265SDimitry Andric           for (unsigned i = NumFixedOps; i < NumOps; ++i) {
15881ad6265SDimitry Andric             OS << ' ';
159*bdd1243dSDimitry Andric             printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i,
160*bdd1243dSDimitry Andric                                                                         OS);
161*bdd1243dSDimitry Andric             if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {
16281ad6265SDimitry Andric               assert(i + 1 < NumOps && "Missing alignment operand");
16381ad6265SDimitry Andric               OS << ' ';
16481ad6265SDimitry Andric               printOperand(MI, i + 1, OS);
16581ad6265SDimitry Andric               i += 1;
16681ad6265SDimitry Andric             }
16781ad6265SDimitry Andric           }
16881ad6265SDimitry Andric           break;
16981ad6265SDimitry Andric         }
17081ad6265SDimitry Andric         case SPIRV::OpConstantI:
17181ad6265SDimitry Andric         case SPIRV::OpConstantF:
17281ad6265SDimitry Andric           printOpConstantVarOps(MI, NumFixedOps, OS);
17381ad6265SDimitry Andric           break;
17481ad6265SDimitry Andric         default:
17581ad6265SDimitry Andric           printRemainingVariableOps(MI, NumFixedOps, OS);
17681ad6265SDimitry Andric           break;
17781ad6265SDimitry Andric         }
17881ad6265SDimitry Andric       }
17981ad6265SDimitry Andric     }
18081ad6265SDimitry Andric   }
18181ad6265SDimitry Andric 
18281ad6265SDimitry Andric   printAnnotation(OS, Annot);
18381ad6265SDimitry Andric }
18481ad6265SDimitry Andric 
18581ad6265SDimitry Andric void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) {
186fcaf7f86SDimitry Andric   // The fixed operands have already been printed, so just need to decide what
187fcaf7f86SDimitry Andric   // type of ExtInst operands to print based on the instruction set and number.
188*bdd1243dSDimitry Andric   const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
189fcaf7f86SDimitry Andric   unsigned NumFixedOps = MCDesc.getNumOperands();
190fcaf7f86SDimitry Andric   const auto NumOps = MI->getNumOperands();
191fcaf7f86SDimitry Andric   if (NumOps == NumFixedOps)
192fcaf7f86SDimitry Andric     return;
193fcaf7f86SDimitry Andric 
194fcaf7f86SDimitry Andric   O << ' ';
195fcaf7f86SDimitry Andric 
196fcaf7f86SDimitry Andric   // TODO: implement special printing for OpenCLExtInst::vstor*.
197fcaf7f86SDimitry Andric   printRemainingVariableOps(MI, NumFixedOps, O, true);
19881ad6265SDimitry Andric }
19981ad6265SDimitry Andric 
20081ad6265SDimitry Andric void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) {
20181ad6265SDimitry Andric   // The fixed operands have already been printed, so just need to decide what
20281ad6265SDimitry Andric   // type of decoration operands to print based on the Decoration type.
203*bdd1243dSDimitry Andric   const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
20481ad6265SDimitry Andric   unsigned NumFixedOps = MCDesc.getNumOperands();
20581ad6265SDimitry Andric 
20681ad6265SDimitry Andric   if (NumFixedOps != MI->getNumOperands()) {
20781ad6265SDimitry Andric     auto DecOp = MI->getOperand(NumFixedOps - 1);
208*bdd1243dSDimitry Andric     auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());
20981ad6265SDimitry Andric 
21081ad6265SDimitry Andric     O << ' ';
21181ad6265SDimitry Andric 
21281ad6265SDimitry Andric     switch (Dec) {
213*bdd1243dSDimitry Andric     case Decoration::BuiltIn:
214*bdd1243dSDimitry Andric       printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O);
21581ad6265SDimitry Andric       break;
216*bdd1243dSDimitry Andric     case Decoration::UniformId:
217*bdd1243dSDimitry Andric       printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O);
21881ad6265SDimitry Andric       break;
219*bdd1243dSDimitry Andric     case Decoration::FuncParamAttr:
220*bdd1243dSDimitry Andric       printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>(
221*bdd1243dSDimitry Andric           MI, NumFixedOps, O);
22281ad6265SDimitry Andric       break;
223*bdd1243dSDimitry Andric     case Decoration::FPRoundingMode:
224*bdd1243dSDimitry Andric       printSymbolicOperand<OperandCategory::FPRoundingModeOperand>(
225*bdd1243dSDimitry Andric           MI, NumFixedOps, O);
22681ad6265SDimitry Andric       break;
227*bdd1243dSDimitry Andric     case Decoration::FPFastMathMode:
228*bdd1243dSDimitry Andric       printSymbolicOperand<OperandCategory::FPFastMathModeOperand>(
229*bdd1243dSDimitry Andric           MI, NumFixedOps, O);
23081ad6265SDimitry Andric       break;
231*bdd1243dSDimitry Andric     case Decoration::LinkageAttributes:
232*bdd1243dSDimitry Andric     case Decoration::UserSemantic:
23381ad6265SDimitry Andric       printStringImm(MI, NumFixedOps, O);
23481ad6265SDimitry Andric       break;
23581ad6265SDimitry Andric     default:
23681ad6265SDimitry Andric       printRemainingVariableOps(MI, NumFixedOps, O, true);
23781ad6265SDimitry Andric       break;
23881ad6265SDimitry Andric     }
23981ad6265SDimitry Andric   }
24081ad6265SDimitry Andric }
24181ad6265SDimitry Andric 
24281ad6265SDimitry Andric static void printExpr(const MCExpr *Expr, raw_ostream &O) {
24381ad6265SDimitry Andric #ifndef NDEBUG
24481ad6265SDimitry Andric   const MCSymbolRefExpr *SRE;
24581ad6265SDimitry Andric 
24681ad6265SDimitry Andric   if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr))
24781ad6265SDimitry Andric     SRE = cast<MCSymbolRefExpr>(BE->getLHS());
24881ad6265SDimitry Andric   else
24981ad6265SDimitry Andric     SRE = cast<MCSymbolRefExpr>(Expr);
25081ad6265SDimitry Andric 
25181ad6265SDimitry Andric   MCSymbolRefExpr::VariantKind Kind = SRE->getKind();
25281ad6265SDimitry Andric 
25381ad6265SDimitry Andric   assert(Kind == MCSymbolRefExpr::VK_None);
25481ad6265SDimitry Andric #endif
25581ad6265SDimitry Andric   O << *Expr;
25681ad6265SDimitry Andric }
25781ad6265SDimitry Andric 
25881ad6265SDimitry Andric void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
25981ad6265SDimitry Andric                                     raw_ostream &O, const char *Modifier) {
26081ad6265SDimitry Andric   assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported");
26181ad6265SDimitry Andric   if (OpNo < MI->getNumOperands()) {
26281ad6265SDimitry Andric     const MCOperand &Op = MI->getOperand(OpNo);
26381ad6265SDimitry Andric     if (Op.isReg())
26481ad6265SDimitry Andric       O << '%' << (Register::virtReg2Index(Op.getReg()) + 1);
26581ad6265SDimitry Andric     else if (Op.isImm())
26681ad6265SDimitry Andric       O << formatImm((int64_t)Op.getImm());
26781ad6265SDimitry Andric     else if (Op.isDFPImm())
26881ad6265SDimitry Andric       O << formatImm((double)Op.getDFPImm());
26981ad6265SDimitry Andric     else if (Op.isExpr())
27081ad6265SDimitry Andric       printExpr(Op.getExpr(), O);
27181ad6265SDimitry Andric     else
27281ad6265SDimitry Andric       llvm_unreachable("Unexpected operand type");
27381ad6265SDimitry Andric   }
27481ad6265SDimitry Andric }
27581ad6265SDimitry Andric 
27681ad6265SDimitry Andric void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
27781ad6265SDimitry Andric                                       raw_ostream &O) {
27881ad6265SDimitry Andric   const unsigned NumOps = MI->getNumOperands();
27981ad6265SDimitry Andric   unsigned StrStartIndex = OpNo;
28081ad6265SDimitry Andric   while (StrStartIndex < NumOps) {
28181ad6265SDimitry Andric     if (MI->getOperand(StrStartIndex).isReg())
28281ad6265SDimitry Andric       break;
28381ad6265SDimitry Andric 
28481ad6265SDimitry Andric     std::string Str = getSPIRVStringOperand(*MI, OpNo);
28581ad6265SDimitry Andric     if (StrStartIndex != OpNo)
28681ad6265SDimitry Andric       O << ' '; // Add a space if we're starting a new string/argument.
28781ad6265SDimitry Andric     O << '"';
28881ad6265SDimitry Andric     for (char c : Str) {
28981ad6265SDimitry Andric       if (c == '"')
29081ad6265SDimitry Andric         O.write('\\'); // Escape " characters (might break for complex UTF-8).
29181ad6265SDimitry Andric       O.write(c);
29281ad6265SDimitry Andric     }
29381ad6265SDimitry Andric     O << '"';
29481ad6265SDimitry Andric 
29581ad6265SDimitry Andric     unsigned numOpsInString = (Str.size() / 4) + 1;
29681ad6265SDimitry Andric     StrStartIndex += numOpsInString;
29781ad6265SDimitry Andric 
29881ad6265SDimitry Andric     // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
29981ad6265SDimitry Andric     if (MI->getOpcode() == SPIRV::OpDecorate &&
30081ad6265SDimitry Andric         MI->getOperand(1).getImm() ==
301*bdd1243dSDimitry Andric             static_cast<unsigned>(Decoration::LinkageAttributes)) {
30281ad6265SDimitry Andric       O << ' ';
303*bdd1243dSDimitry Andric       printSymbolicOperand<OperandCategory::LinkageTypeOperand>(
304*bdd1243dSDimitry Andric           MI, StrStartIndex, O);
30581ad6265SDimitry Andric       break;
30681ad6265SDimitry Andric     }
30781ad6265SDimitry Andric   }
30881ad6265SDimitry Andric }
30981ad6265SDimitry Andric 
310*bdd1243dSDimitry Andric void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,
31181ad6265SDimitry Andric                                       raw_ostream &O) {
312*bdd1243dSDimitry Andric   auto SetReg = MI->getOperand(2).getReg();
313*bdd1243dSDimitry Andric   auto Set = ExtInstSetIDs[SetReg];
314*bdd1243dSDimitry Andric   auto Op = MI->getOperand(OpNo).getImm();
315*bdd1243dSDimitry Andric   O << getExtInstName(Set, Op);
31681ad6265SDimitry Andric }
31781ad6265SDimitry Andric 
318*bdd1243dSDimitry Andric template <OperandCategory::OperandCategory category>
319*bdd1243dSDimitry Andric void SPIRVInstPrinter::printSymbolicOperand(const MCInst *MI, unsigned OpNo,
32081ad6265SDimitry Andric                                             raw_ostream &O) {
32181ad6265SDimitry Andric   if (OpNo < MI->getNumOperands()) {
322*bdd1243dSDimitry Andric     O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());
32381ad6265SDimitry Andric   }
32481ad6265SDimitry Andric }
325