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