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 
26 using namespace llvm;
27 using 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 
34 void 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 
49 void 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 
62 void 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 
69 void 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 
185 void 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 
200 void 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 
242 static 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 
258 void 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 
276 void 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 
310 void 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 
318 template <OperandCategory::OperandCategory category>
319 void 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