xref: /freebsd/contrib/llvm-project/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp (revision a90b9d0159070121c221b966469c3e36d912bf82)
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/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"
26 
27 using namespace llvm;
28 using namespace llvm::SPIRV;
29 
30 #define DEBUG_TYPE "asm-printer"
31 
32 // Include the auto-generated portion of the assembly writer.
33 #include "SPIRVGenAsmWriter.inc"
34 
35 void SPIRVInstPrinter::printRemainingVariableOps(const MCInst *MI,
36                                                  unsigned StartIndex,
37                                                  raw_ostream &O,
38                                                  bool SkipFirstSpace,
39                                                  bool SkipImmediates) {
40   const unsigned NumOps = MI->getNumOperands();
41   for (unsigned i = StartIndex; i < NumOps; ++i) {
42     if (!SkipImmediates || !MI->getOperand(i).isImm()) {
43       if (!SkipFirstSpace || i != StartIndex)
44         O << ' ';
45       printOperand(MI, i, O);
46     }
47   }
48 }
49 
50 void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI,
51                                              unsigned StartIndex,
52                                              raw_ostream &O) {
53   const unsigned NumVarOps = MI->getNumOperands() - StartIndex;
54 
55   assert((NumVarOps == 1 || NumVarOps == 2) &&
56          "Unsupported number of bits for literal variable");
57 
58   O << ' ';
59 
60   uint64_t Imm = MI->getOperand(StartIndex).getImm();
61 
62   // Handle 64 bit literals.
63   if (NumVarOps == 2) {
64     Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32);
65   }
66 
67   // Format and print float values.
68   if (MI->getOpcode() == SPIRV::OpConstantF) {
69     APFloat FP = NumVarOps == 1 ? APFloat(APInt(32, Imm).bitsToFloat())
70                                 : APFloat(APInt(64, Imm).bitsToDouble());
71 
72     // Print infinity and NaN as hex floats.
73     // TODO: Make sure subnormal numbers are handled correctly as they may also
74     // require hex float notation.
75     if (FP.isInfinity()) {
76       if (FP.isNegative())
77         O << '-';
78       O << "0x1p+128";
79       return;
80     }
81     if (FP.isNaN()) {
82       O << "0x1.8p+128";
83       return;
84     }
85 
86     // Format val as a decimal floating point or scientific notation (whichever
87     // is shorter), with enough digits of precision to produce the exact value.
88     O << format("%.*g", std::numeric_limits<double>::max_digits10,
89                 FP.convertToDouble());
90 
91     return;
92   }
93 
94   // Print integer values directly.
95   O << Imm;
96 }
97 
98 void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {
99   Register Reg = MI->getOperand(0).getReg();
100   auto Name = getSPIRVStringOperand(*MI, 1);
101   auto Set = getExtInstSetFromString(Name);
102   ExtInstSetIDs.insert({Reg, Set});
103 }
104 
105 void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
106                                  StringRef Annot, const MCSubtargetInfo &STI,
107                                  raw_ostream &OS) {
108   const unsigned OpCode = MI->getOpcode();
109   printInstruction(MI, Address, OS);
110 
111   if (OpCode == SPIRV::OpDecorate) {
112     printOpDecorate(MI, OS);
113   } else if (OpCode == SPIRV::OpExtInstImport) {
114     recordOpExtInstImport(MI);
115   } else if (OpCode == SPIRV::OpExtInst) {
116     printOpExtInst(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 as
127         // the last operand before the variable_ops. This is usually a StringImm
128         // 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 string
141           // literal.
142           printRemainingVariableOps(MI, NumFixedOps, OS, false, true);
143           break;
144         }
145         case SPIRV::OpExecutionMode:
146         case SPIRV::OpExecutionModeId:
147         case SPIRV::OpLoopMerge: {
148           // Print any literals after the OPERAND_UNKNOWN argument normally.
149           printRemainingVariableOps(MI, NumFixedOps, OS);
150           break;
151         }
152         default:
153           break; // printStringImm has already been handled.
154         }
155       } else {
156         // For instructions with no fixed ops or a reg/immediate as the final
157         // fixed operand, we can usually print the rest with "printOperand", but
158         // check for a few cases with custom types first.
159         switch (OpCode) {
160         case SPIRV::OpLoad:
161         case SPIRV::OpStore:
162           OS << ' ';
163           printSymbolicOperand<OperandCategory::MemoryOperandOperand>(
164               MI, FirstVariableIndex, OS);
165           printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
166           break;
167         case SPIRV::OpImageSampleImplicitLod:
168         case SPIRV::OpImageSampleDrefImplicitLod:
169         case SPIRV::OpImageSampleProjImplicitLod:
170         case SPIRV::OpImageSampleProjDrefImplicitLod:
171         case SPIRV::OpImageFetch:
172         case SPIRV::OpImageGather:
173         case SPIRV::OpImageDrefGather:
174         case SPIRV::OpImageRead:
175         case SPIRV::OpImageWrite:
176         case SPIRV::OpImageSparseSampleImplicitLod:
177         case SPIRV::OpImageSparseSampleDrefImplicitLod:
178         case SPIRV::OpImageSparseSampleProjImplicitLod:
179         case SPIRV::OpImageSparseSampleProjDrefImplicitLod:
180         case SPIRV::OpImageSparseFetch:
181         case SPIRV::OpImageSparseGather:
182         case SPIRV::OpImageSparseDrefGather:
183         case SPIRV::OpImageSparseRead:
184         case SPIRV::OpImageSampleFootprintNV:
185           OS << ' ';
186           printSymbolicOperand<OperandCategory::ImageOperandOperand>(
187               MI, FirstVariableIndex, OS);
188           printRemainingVariableOps(MI, NumFixedOps + 1, OS);
189           break;
190         case SPIRV::OpCopyMemory:
191         case SPIRV::OpCopyMemorySized: {
192           const unsigned NumOps = MI->getNumOperands();
193           for (unsigned i = NumFixedOps; i < NumOps; ++i) {
194             OS << ' ';
195             printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i,
196                                                                         OS);
197             if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {
198               assert(i + 1 < NumOps && "Missing alignment operand");
199               OS << ' ';
200               printOperand(MI, i + 1, OS);
201               i += 1;
202             }
203           }
204           break;
205         }
206         case SPIRV::OpConstantI:
207         case SPIRV::OpConstantF:
208           // The last fixed operand along with any variadic operands that follow
209           // are part of the variable value.
210           printOpConstantVarOps(MI, NumFixedOps - 1, OS);
211           break;
212         default:
213           printRemainingVariableOps(MI, NumFixedOps, OS);
214           break;
215         }
216       }
217     }
218   }
219 
220   printAnnotation(OS, Annot);
221 }
222 
223 void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) {
224   // The fixed operands have already been printed, so just need to decide what
225   // type of ExtInst operands to print based on the instruction set and number.
226   const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
227   unsigned NumFixedOps = MCDesc.getNumOperands();
228   const auto NumOps = MI->getNumOperands();
229   if (NumOps == NumFixedOps)
230     return;
231 
232   O << ' ';
233 
234   // TODO: implement special printing for OpenCLExtInst::vstor*.
235   printRemainingVariableOps(MI, NumFixedOps, O, true);
236 }
237 
238 void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) {
239   // The fixed operands have already been printed, so just need to decide what
240   // type of decoration operands to print based on the Decoration type.
241   const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
242   unsigned NumFixedOps = MCDesc.getNumOperands();
243 
244   if (NumFixedOps != MI->getNumOperands()) {
245     auto DecOp = MI->getOperand(NumFixedOps - 1);
246     auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());
247 
248     O << ' ';
249 
250     switch (Dec) {
251     case Decoration::BuiltIn:
252       printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O);
253       break;
254     case Decoration::UniformId:
255       printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O);
256       break;
257     case Decoration::FuncParamAttr:
258       printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>(
259           MI, NumFixedOps, O);
260       break;
261     case Decoration::FPRoundingMode:
262       printSymbolicOperand<OperandCategory::FPRoundingModeOperand>(
263           MI, NumFixedOps, O);
264       break;
265     case Decoration::FPFastMathMode:
266       printSymbolicOperand<OperandCategory::FPFastMathModeOperand>(
267           MI, NumFixedOps, O);
268       break;
269     case Decoration::LinkageAttributes:
270     case Decoration::UserSemantic:
271       printStringImm(MI, NumFixedOps, O);
272       break;
273     default:
274       printRemainingVariableOps(MI, NumFixedOps, O, true);
275       break;
276     }
277   }
278 }
279 
280 static void printExpr(const MCExpr *Expr, raw_ostream &O) {
281 #ifndef NDEBUG
282   const MCSymbolRefExpr *SRE;
283 
284   if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr))
285     SRE = cast<MCSymbolRefExpr>(BE->getLHS());
286   else
287     SRE = cast<MCSymbolRefExpr>(Expr);
288 
289   MCSymbolRefExpr::VariantKind Kind = SRE->getKind();
290 
291   assert(Kind == MCSymbolRefExpr::VK_None);
292 #endif
293   O << *Expr;
294 }
295 
296 void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
297                                     raw_ostream &O, const char *Modifier) {
298   assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported");
299   if (OpNo < MI->getNumOperands()) {
300     const MCOperand &Op = MI->getOperand(OpNo);
301     if (Op.isReg())
302       O << '%' << (Register::virtReg2Index(Op.getReg()) + 1);
303     else if (Op.isImm())
304       O << formatImm((int64_t)Op.getImm());
305     else if (Op.isDFPImm())
306       O << formatImm((double)Op.getDFPImm());
307     else if (Op.isExpr())
308       printExpr(Op.getExpr(), O);
309     else
310       llvm_unreachable("Unexpected operand type");
311   }
312 }
313 
314 void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
315                                       raw_ostream &O) {
316   const unsigned NumOps = MI->getNumOperands();
317   unsigned StrStartIndex = OpNo;
318   while (StrStartIndex < NumOps) {
319     if (MI->getOperand(StrStartIndex).isReg())
320       break;
321 
322     std::string Str = getSPIRVStringOperand(*MI, OpNo);
323     if (StrStartIndex != OpNo)
324       O << ' '; // Add a space if we're starting a new string/argument.
325     O << '"';
326     for (char c : Str) {
327       if (c == '"')
328         O.write('\\'); // Escape " characters (might break for complex UTF-8).
329       O.write(c);
330     }
331     O << '"';
332 
333     unsigned numOpsInString = (Str.size() / 4) + 1;
334     StrStartIndex += numOpsInString;
335 
336     // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
337     if (MI->getOpcode() == SPIRV::OpDecorate &&
338         MI->getOperand(1).getImm() ==
339             static_cast<unsigned>(Decoration::LinkageAttributes)) {
340       O << ' ';
341       printSymbolicOperand<OperandCategory::LinkageTypeOperand>(
342           MI, StrStartIndex, O);
343       break;
344     }
345   }
346 }
347 
348 void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,
349                                       raw_ostream &O) {
350   auto SetReg = MI->getOperand(2).getReg();
351   auto Set = ExtInstSetIDs[SetReg];
352   auto Op = MI->getOperand(OpNo).getImm();
353   O << getExtInstName(Set, Op);
354 }
355 
356 template <OperandCategory::OperandCategory category>
357 void SPIRVInstPrinter::printSymbolicOperand(const MCInst *MI, unsigned OpNo,
358                                             raw_ostream &O) {
359   if (OpNo < MI->getNumOperands()) {
360     O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());
361   }
362 }
363