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