LLVM 22.0.0git
SPIRVInstPrinter.cpp
Go to the documentation of this file.
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/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"
23
24using namespace llvm;
25using namespace llvm::SPIRV;
26
27#define DEBUG_TYPE "asm-printer"
28
29// Include the auto-generated portion of the assembly writer.
30#include "SPIRVGenAsmWriter.inc"
31
33 unsigned StartIndex,
34 raw_ostream &O,
35 bool SkipFirstSpace,
36 bool SkipImmediates) {
37 const unsigned NumOps = MI->getNumOperands();
38 for (unsigned i = StartIndex; i < NumOps; ++i) {
39 if (!SkipImmediates || !MI->getOperand(i).isImm()) {
40 if (!SkipFirstSpace || i != StartIndex)
41 O << ' ';
42 printOperand(MI, i, O);
43 }
44 }
45}
46
48 unsigned StartIndex,
49 raw_ostream &O) {
50 unsigned IsBitwidth16 = MI->getFlags() & SPIRV::INST_PRINTER_WIDTH16;
51 const unsigned NumVarOps = MI->getNumOperands() - StartIndex;
52
53 assert((NumVarOps == 1 || NumVarOps == 2) &&
54 "Unsupported number of bits for literal variable");
55
56 O << ' ';
57
58 uint64_t Imm = MI->getOperand(StartIndex).getImm();
59
60 // Handle 64 bit literals.
61 if (NumVarOps == 2) {
62 Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32);
63 }
64
65 // Format and print float values.
66 if (MI->getOpcode() == SPIRV::OpConstantF && IsBitwidth16 == 0) {
67 APFloat FP = NumVarOps == 1 ? APFloat(APInt(32, Imm).bitsToFloat())
68 : APFloat(APInt(64, Imm).bitsToDouble());
69
70 // Print infinity and NaN as hex floats.
71 // TODO: Make sure subnormal numbers are handled correctly as they may also
72 // require hex float notation.
73 if (FP.isInfinity()) {
74 if (FP.isNegative())
75 O << '-';
76 O << "0x1p+128";
77 return;
78 }
79 if (FP.isNaN()) {
80 O << "0x1.8p+128";
81 return;
82 }
83
84 // Format val as a decimal floating point or scientific notation (whichever
85 // is shorter), with enough digits of precision to produce the exact value.
86 O << format("%.*g", std::numeric_limits<double>::max_digits10,
87 FP.convertToDouble());
88
89 return;
90 }
91
92 // Print integer values directly.
93 O << Imm;
94}
95
96void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {
97 MCRegister Reg = MI->getOperand(0).getReg();
98 auto Name = getSPIRVStringOperand(*MI, 1);
99 auto Set = getExtInstSetFromString(std::move(Name));
100 ExtInstSetIDs.insert({Reg, Set});
101}
102
104 StringRef Annot, const MCSubtargetInfo &STI,
105 raw_ostream &OS) {
106 const unsigned OpCode = MI->getOpcode();
108
109 if (OpCode == SPIRV::OpDecorate) {
110 printOpDecorate(MI, OS);
111 } else if (OpCode == SPIRV::OpExtInstImport) {
112 recordOpExtInstImport(MI);
113 } else if (OpCode == SPIRV::OpExtInst) {
114 printOpExtInst(MI, OS);
115 } else if (OpCode == SPIRV::UNKNOWN_type) {
116 printUnknownType(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 ==
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 << ' ';
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::OpMemberDecorate:
146 printRemainingVariableOps(MI, NumFixedOps, OS);
147 break;
148 case SPIRV::OpExecutionMode:
149 case SPIRV::OpExecutionModeId:
150 case SPIRV::OpLoopMerge: {
151 // Print any literals after the OPERAND_UNKNOWN argument normally.
152 printRemainingVariableOps(MI, NumFixedOps, OS);
153 break;
154 }
155 default:
156 break; // printStringImm has already been handled.
157 }
158 } else {
159 // For instructions with no fixed ops or a reg/immediate as the final
160 // fixed operand, we can usually print the rest with "printOperand", but
161 // check for a few cases with custom types first.
162 switch (OpCode) {
163 case SPIRV::OpLoad:
164 case SPIRV::OpStore:
165 OS << ' ';
167 MI, FirstVariableIndex, OS);
168 printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
169 break;
170 case SPIRV::OpSwitch:
171 if (MI->getFlags() & SPIRV::INST_PRINTER_WIDTH64) {
172 // In binary format 64-bit types are split into two 32-bit operands,
173 // but in text format combine these into a single 64-bit value as
174 // this is what tools such as spirv-as require.
175 const unsigned NumOps = MI->getNumOperands();
176 for (unsigned OpIdx = NumFixedOps; OpIdx < NumOps;) {
177 if (OpIdx + 1 >= NumOps || !MI->getOperand(OpIdx).isImm() ||
178 !MI->getOperand(OpIdx + 1).isImm()) {
179 llvm_unreachable("Unexpected OpSwitch operands");
180 continue;
181 }
182 OS << ' ';
183 uint64_t LowBits = MI->getOperand(OpIdx).getImm();
184 uint64_t HighBits = MI->getOperand(OpIdx + 1).getImm();
185 uint64_t CombinedValue = (HighBits << 32) | LowBits;
186 OS << formatImm(CombinedValue);
187 OpIdx += 2;
188
189 // Next should be the label
190 if (OpIdx < NumOps) {
191 OS << ' ';
192 printOperand(MI, OpIdx, OS);
193 OpIdx++;
194 }
195 }
196 } else {
197 printRemainingVariableOps(MI, NumFixedOps, OS);
198 }
199 break;
200 case SPIRV::OpImageSampleImplicitLod:
201 case SPIRV::OpImageSampleDrefImplicitLod:
202 case SPIRV::OpImageSampleProjImplicitLod:
203 case SPIRV::OpImageSampleProjDrefImplicitLod:
204 case SPIRV::OpImageFetch:
205 case SPIRV::OpImageGather:
206 case SPIRV::OpImageDrefGather:
207 case SPIRV::OpImageRead:
208 case SPIRV::OpImageWrite:
209 case SPIRV::OpImageSparseSampleImplicitLod:
210 case SPIRV::OpImageSparseSampleDrefImplicitLod:
211 case SPIRV::OpImageSparseSampleProjImplicitLod:
212 case SPIRV::OpImageSparseSampleProjDrefImplicitLod:
213 case SPIRV::OpImageSparseFetch:
214 case SPIRV::OpImageSparseGather:
215 case SPIRV::OpImageSparseDrefGather:
216 case SPIRV::OpImageSparseRead:
217 case SPIRV::OpImageSampleFootprintNV:
218 OS << ' ';
220 MI, FirstVariableIndex, OS);
221 printRemainingVariableOps(MI, NumFixedOps + 1, OS);
222 break;
223 case SPIRV::OpCopyMemory:
224 case SPIRV::OpCopyMemorySized: {
225 const unsigned NumOps = MI->getNumOperands();
226 for (unsigned i = NumFixedOps; i < NumOps; ++i) {
227 OS << ' ';
229 OS);
230 if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {
231 assert(i + 1 < NumOps && "Missing alignment operand");
232 OS << ' ';
233 printOperand(MI, i + 1, OS);
234 i += 1;
235 }
236 }
237 break;
238 }
239 case SPIRV::OpConstantI:
240 case SPIRV::OpConstantF:
241 // The last fixed operand along with any variadic operands that follow
242 // are part of the variable value.
243 assert(NumFixedOps > 0 && "Expected at least one fixed operand");
244 printOpConstantVarOps(MI, NumFixedOps - 1, OS);
245 break;
246 case SPIRV::OpCooperativeMatrixMulAddKHR: {
247 const unsigned NumOps = MI->getNumOperands();
248 if (NumFixedOps == NumOps)
249 break;
250
251 OS << ' ';
252 const unsigned MulAddOp = MI->getOperand(FirstVariableIndex).getImm();
253 if (MulAddOp == 0) {
255 OperandCategory::CooperativeMatrixOperandsOperand>(
256 MI, FirstVariableIndex, OS);
257 } else {
258 std::string Buffer;
259 for (unsigned Mask = 0x1;
260 Mask != SPIRV::CooperativeMatrixOperands::
261 MatrixResultBFloat16ComponentsINTEL;
262 Mask <<= 1) {
263 if (MulAddOp & Mask) {
264 if (!Buffer.empty())
265 Buffer += '|';
267 OperandCategory::CooperativeMatrixOperandsOperand, Mask);
268 }
269 }
270 OS << Buffer;
271 }
272 break;
273 }
274 case SPIRV::OpSubgroupMatrixMultiplyAccumulateINTEL: {
275 const unsigned NumOps = MI->getNumOperands();
276 if (NumFixedOps >= NumOps)
277 break;
278 OS << ' ';
279 const unsigned Flags = MI->getOperand(NumOps - 1).getImm();
280 if (Flags == 0) {
282 OperandCategory::MatrixMultiplyAccumulateOperandsOperand>(
283 MI, NumOps - 1, OS);
284 } else {
285 std::string Buffer;
286 for (unsigned Mask = 0x1;
287 Mask <= SPIRV::MatrixMultiplyAccumulateOperands::
288 MatrixBPackedBFloat16INTEL;
289 Mask <<= 1) {
290 if (Flags & Mask) {
291 if (!Buffer.empty())
292 Buffer += '|';
294 OperandCategory::MatrixMultiplyAccumulateOperandsOperand,
295 Mask);
296 }
297 }
298 OS << Buffer;
299 }
300 break;
301 }
302 case SPIRV::OpSDot:
303 case SPIRV::OpUDot:
304 case SPIRV::OpSUDot:
305 case SPIRV::OpSDotAccSat:
306 case SPIRV::OpUDotAccSat:
307 case SPIRV::OpSUDotAccSat: {
308 const unsigned NumOps = MI->getNumOperands();
309 if (NumOps > NumFixedOps) {
310 OS << ' ';
312 MI, NumOps - 1, OS);
313 break;
314 }
315 break;
316 }
317 case SPIRV::OpPredicatedLoadINTEL:
318 case SPIRV::OpPredicatedStoreINTEL: {
319 const unsigned NumOps = MI->getNumOperands();
320 if (NumOps > NumFixedOps) {
321 OS << ' ';
323 MI, NumOps - 1, OS);
324 break;
325 }
326 break;
327 }
328 default:
329 printRemainingVariableOps(MI, NumFixedOps, OS);
330 break;
331 }
332 }
333 }
334 }
335
336 printAnnotation(OS, Annot);
337}
338
340 // The fixed operands have already been printed, so just need to decide what
341 // type of ExtInst operands to print based on the instruction set and number.
342 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
343 unsigned NumFixedOps = MCDesc.getNumOperands();
344 const auto NumOps = MI->getNumOperands();
345 if (NumOps == NumFixedOps)
346 return;
347
348 O << ' ';
349
350 // TODO: implement special printing for OpenCLExtInst::vstor*.
351 printRemainingVariableOps(MI, NumFixedOps, O, true);
352}
353
355 // The fixed operands have already been printed, so just need to decide what
356 // type of decoration operands to print based on the Decoration type.
357 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
358 unsigned NumFixedOps = MCDesc.getNumOperands();
359
360 if (NumFixedOps != MI->getNumOperands()) {
361 auto DecOp = MI->getOperand(NumFixedOps - 1);
362 auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());
363
364 O << ' ';
365
366 switch (Dec) {
367 case Decoration::BuiltIn:
369 break;
370 case Decoration::UniformId:
372 break;
373 case Decoration::FuncParamAttr:
375 MI, NumFixedOps, O);
376 break;
377 case Decoration::FPRoundingMode:
379 MI, NumFixedOps, O);
380 break;
381 case Decoration::FPFastMathMode:
383 MI, NumFixedOps, O);
384 break;
385 case Decoration::LinkageAttributes:
386 case Decoration::UserSemantic:
387 printStringImm(MI, NumFixedOps, O);
388 break;
389 case Decoration::HostAccessINTEL:
390 printOperand(MI, NumFixedOps, O);
391 if (NumFixedOps + 1 < MI->getNumOperands()) {
392 O << ' ';
393 printStringImm(MI, NumFixedOps + 1, O);
394 }
395 break;
396 default:
397 printRemainingVariableOps(MI, NumFixedOps, O, true);
398 break;
399 }
400 }
401}
402
404 const auto EnumOperand = MI->getOperand(1);
405 assert(EnumOperand.isImm() &&
406 "second operand of UNKNOWN_type must be opcode!");
407
408 const auto Enumerant = EnumOperand.getImm();
409 const auto NumOps = MI->getNumOperands();
410
411 // Print the opcode using the spirv-as unknown opcode syntax
412 O << "OpUnknown(" << Enumerant << ", " << NumOps << ") ";
413
414 // The result ID must be printed after the opcode when using this syntax
415 printOperand(MI, 0, O);
416
417 O << " ";
418
419 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
420 unsigned NumFixedOps = MCDesc.getNumOperands();
421 if (NumOps == NumFixedOps)
422 return;
423
424 // Print the rest of the operands
425 printRemainingVariableOps(MI, NumFixedOps, O, true);
426}
427
428void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
429 raw_ostream &O) {
430 if (OpNo < MI->getNumOperands()) {
431 const MCOperand &Op = MI->getOperand(OpNo);
432 if (Op.isReg())
433 O << '%' << (getIDFromRegister(Op.getReg().id()) + 1);
434 else if (Op.isImm()) {
435 int64_t Imm = Op.getImm();
436 // For OpVectorShuffle:
437 // A Component literal may also be FFFFFFFF, which means the corresponding
438 // result component has no source and is undefined.
439 // LLVM representation of poison/undef becomes -1 when lowered to MI.
440 if (MI->getOpcode() == SPIRV::OpVectorShuffle && Imm == -1)
441 O << "0xFFFFFFFF";
442 else
443 O << formatImm(Imm);
444 } else if (Op.isDFPImm())
445 O << formatImm((double)Op.getDFPImm());
446 else if (Op.isExpr())
447 MAI.printExpr(O, *Op.getExpr());
448 else
449 llvm_unreachable("Unexpected operand type");
450 }
451}
452
453void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
454 raw_ostream &O) {
455 const unsigned NumOps = MI->getNumOperands();
456 unsigned StrStartIndex = OpNo;
457 while (StrStartIndex < NumOps) {
458 if (MI->getOperand(StrStartIndex).isReg())
459 break;
460
461 std::string Str = getSPIRVStringOperand(*MI, StrStartIndex);
462 if (StrStartIndex != OpNo)
463 O << ' '; // Add a space if we're starting a new string/argument.
464 O << '"';
465 for (char c : Str) {
466 // Escape ", \n characters (might break for complex UTF-8).
467 if (c == '\n') {
468 O.write("\\n", 2);
469 } else {
470 if (c == '"')
471 O.write('\\');
472 O.write(c);
473 }
474 }
475 O << '"';
476
477 unsigned numOpsInString = (Str.size() / 4) + 1;
478 StrStartIndex += numOpsInString;
479
480 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
481 if (MI->getOpcode() == SPIRV::OpDecorate &&
482 MI->getOperand(1).getImm() ==
483 static_cast<unsigned>(Decoration::LinkageAttributes)) {
484 O << ' ';
486 MI, StrStartIndex, O);
487 break;
488 }
489 }
490}
491
492void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,
493 raw_ostream &O) {
494 auto SetReg = MI->getOperand(2).getReg();
495 auto Set = ExtInstSetIDs[SetReg];
496 auto Op = MI->getOperand(OpNo).getImm();
497 O << getExtInstName(Set, Op);
498}
499
500template <OperandCategory::OperandCategory category>
502 raw_ostream &O) {
503 if (OpNo < MI->getNumOperands()) {
504 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());
505 }
506}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file declares a class to represent arbitrary precision floating point values and provide a varie...
IRTranslator LLVM IR MI
const size_t AbstractManglingParser< Derived, Alloc >::NumOps
MachineInstr unsigned OpIdx
Class for arbitrary precision integers.
Definition APInt.h:78
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition DenseMap.h:233
const MCInstrInfo & MII
void printAnnotation(raw_ostream &OS, StringRef Annot)
Utility function for printing annotations.
const MCAsmInfo & MAI
format_object< int64_t > formatImm(int64_t Value) const
Utility function to print immediates in decimal or hex.
Instances of this class represent a single low-level machine instruction.
Definition MCInst.h:188
Describe properties that are true of each instruction in the target description file.
unsigned getNumOperands() const
Return the number of declared MachineOperands for this MachineInstruction.
ArrayRef< MCOperandInfo > operands() const
bool isVariadic() const
Return true if this instruction can have a variable number of operands.
Instances of this class represent operands of the MCInst class.
Definition MCInst.h:40
Wrapper class representing physical registers. Should be passed by value.
Definition MCRegister.h:33
Generic base class for all target subtargets.
void printExtension(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printStringImm(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O)
void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, const MCSubtargetInfo &STI, raw_ostream &OS) override
Print the specified MCInst to the specified raw_ostream.
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printOpExtInst(const MCInst *MI, raw_ostream &O)
void printOpConstantVarOps(const MCInst *MI, unsigned StartIndex, raw_ostream &O)
void printSymbolicOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printRemainingVariableOps(const MCInst *MI, unsigned StartIndex, raw_ostream &O, bool SkipFirstSpace=false, bool SkipImmediates=false)
void printOpDecorate(const MCInst *MI, raw_ostream &O)
void printUnknownType(const MCInst *MI, raw_ostream &O)
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned getIDFromRegister(unsigned Reg)
This is an optimization pass for GlobalISel generic memory operations.
std::string getExtInstName(SPIRV::InstructionSet::InstructionSet Set, uint32_t InstructionNumber)
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
std::string getSPIRVStringOperand(const InstType &MI, unsigned StartIndex)
SPIRV::InstructionSet::InstructionSet getExtInstSetFromString(std::string SetName)
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:129
std::string getSymbolicOperandMnemonic(SPIRV::OperandCategory::OperandCategory Category, int32_t Value)
DWARFExpression::Operation Op