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 if (MI->getOpcode() == SPIRV::OpConstantI && NumVarOps > 2) {
54 // SPV_ALTERA_arbitrary_precision_integers allows for integer widths greater
55 // than 64, which will be encoded via multiple operands.
56 for (unsigned I = StartIndex; I != MI->getNumOperands(); ++I)
57 O << ' ' << MI->getOperand(I).getImm();
58 return;
59 }
60
61 assert((NumVarOps == 1 || NumVarOps == 2) &&
62 "Unsupported number of bits for literal variable");
63
64 O << ' ';
65
66 uint64_t Imm = MI->getOperand(StartIndex).getImm();
67
68 // Handle 64 bit literals.
69 if (NumVarOps == 2) {
70 Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32);
71 }
72
73 // Format and print float values.
74 if (MI->getOpcode() == SPIRV::OpConstantF && IsBitwidth16 == 0) {
75 APFloat FP = NumVarOps == 1 ? APFloat(APInt(32, Imm).bitsToFloat())
76 : APFloat(APInt(64, Imm).bitsToDouble());
77
78 // Print infinity and NaN as hex floats.
79 // TODO: Make sure subnormal numbers are handled correctly as they may also
80 // require hex float notation.
81 if (FP.isInfinity()) {
82 if (FP.isNegative())
83 O << '-';
84 O << "0x1p+128";
85 return;
86 }
87 if (FP.isNaN()) {
88 O << "0x1.8p+128";
89 return;
90 }
91
92 // Format val as a decimal floating point or scientific notation (whichever
93 // is shorter), with enough digits of precision to produce the exact value.
94 O << format("%.*g", std::numeric_limits<double>::max_digits10,
95 FP.convertToDouble());
96
97 return;
98 }
99
100 // Print integer values directly.
101 O << Imm;
102}
103
104void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {
105 MCRegister Reg = MI->getOperand(0).getReg();
106 auto Name = getSPIRVStringOperand(*MI, 1);
107 auto Set = getExtInstSetFromString(std::move(Name));
108 ExtInstSetIDs.insert({Reg, Set});
109}
110
112 StringRef Annot, const MCSubtargetInfo &STI,
113 raw_ostream &OS) {
114 const unsigned OpCode = MI->getOpcode();
116
117 if (OpCode == SPIRV::OpDecorate) {
118 printOpDecorate(MI, OS);
119 } else if (OpCode == SPIRV::OpExtInstImport) {
120 recordOpExtInstImport(MI);
121 } else if (OpCode == SPIRV::OpExtInst) {
122 printOpExtInst(MI, OS);
123 } else if (OpCode == SPIRV::UNKNOWN_type) {
124 printUnknownType(MI, OS);
125 } else {
126 // Print any extra operands for variadic instructions.
127 const MCInstrDesc &MCDesc = MII.get(OpCode);
128 if (MCDesc.isVariadic()) {
129 const unsigned NumFixedOps = MCDesc.getNumOperands();
130 const unsigned LastFixedIndex = NumFixedOps - 1;
131 const int FirstVariableIndex = NumFixedOps;
132 if (NumFixedOps > 0 && MCDesc.operands()[LastFixedIndex].OperandType ==
134 // For instructions where a custom type (not reg or immediate) comes as
135 // the last operand before the variable_ops. This is usually a StringImm
136 // operand, but there are a few other cases.
137 switch (OpCode) {
138 case SPIRV::OpTypeImage:
139 OS << ' ';
141 MI, FirstVariableIndex, OS);
142 break;
143 case SPIRV::OpVariable:
144 OS << ' ';
145 printOperand(MI, FirstVariableIndex, OS);
146 break;
147 case SPIRV::OpEntryPoint: {
148 // Print the interface ID operands, skipping the name's string
149 // literal.
150 printRemainingVariableOps(MI, NumFixedOps, OS, false, true);
151 break;
152 }
153 case SPIRV::OpMemberDecorate:
154 printRemainingVariableOps(MI, NumFixedOps, OS);
155 break;
156 case SPIRV::OpExecutionMode:
157 case SPIRV::OpExecutionModeId:
158 case SPIRV::OpLoopMerge: {
159 // Print any literals after the OPERAND_UNKNOWN argument normally.
160 printRemainingVariableOps(MI, NumFixedOps, OS);
161 break;
162 }
163 default:
164 break; // printStringImm has already been handled.
165 }
166 } else {
167 // For instructions with no fixed ops or a reg/immediate as the final
168 // fixed operand, we can usually print the rest with "printOperand", but
169 // check for a few cases with custom types first.
170 switch (OpCode) {
171 case SPIRV::OpLoad:
172 case SPIRV::OpStore:
173 OS << ' ';
175 MI, FirstVariableIndex, OS);
176 printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
177 break;
178 case SPIRV::OpSwitch:
179 if (MI->getFlags() & SPIRV::INST_PRINTER_WIDTH64) {
180 // In binary format 64-bit types are split into two 32-bit operands,
181 // but in text format combine these into a single 64-bit value as
182 // this is what tools such as spirv-as require.
183 const unsigned NumOps = MI->getNumOperands();
184 for (unsigned OpIdx = NumFixedOps; OpIdx < NumOps;) {
185 if (OpIdx + 1 >= NumOps || !MI->getOperand(OpIdx).isImm() ||
186 !MI->getOperand(OpIdx + 1).isImm()) {
187 llvm_unreachable("Unexpected OpSwitch operands");
188 continue;
189 }
190 OS << ' ';
191 uint64_t LowBits = MI->getOperand(OpIdx).getImm();
192 uint64_t HighBits = MI->getOperand(OpIdx + 1).getImm();
193 uint64_t CombinedValue = (HighBits << 32) | LowBits;
194 OS << formatImm(CombinedValue);
195 OpIdx += 2;
196
197 // Next should be the label
198 if (OpIdx < NumOps) {
199 OS << ' ';
200 printOperand(MI, OpIdx, OS);
201 OpIdx++;
202 }
203 }
204 } else {
205 printRemainingVariableOps(MI, NumFixedOps, OS);
206 }
207 break;
208 case SPIRV::OpImageSampleImplicitLod:
209 case SPIRV::OpImageSampleDrefImplicitLod:
210 case SPIRV::OpImageSampleProjImplicitLod:
211 case SPIRV::OpImageSampleProjDrefImplicitLod:
212 case SPIRV::OpImageFetch:
213 case SPIRV::OpImageGather:
214 case SPIRV::OpImageDrefGather:
215 case SPIRV::OpImageRead:
216 case SPIRV::OpImageWrite:
217 case SPIRV::OpImageSparseSampleImplicitLod:
218 case SPIRV::OpImageSparseSampleDrefImplicitLod:
219 case SPIRV::OpImageSparseSampleProjImplicitLod:
220 case SPIRV::OpImageSparseSampleProjDrefImplicitLod:
221 case SPIRV::OpImageSparseFetch:
222 case SPIRV::OpImageSparseGather:
223 case SPIRV::OpImageSparseDrefGather:
224 case SPIRV::OpImageSparseRead:
225 case SPIRV::OpImageSampleFootprintNV:
226 OS << ' ';
228 MI, FirstVariableIndex, OS);
229 printRemainingVariableOps(MI, NumFixedOps + 1, OS);
230 break;
231 case SPIRV::OpCopyMemory:
232 case SPIRV::OpCopyMemorySized: {
233 const unsigned NumOps = MI->getNumOperands();
234 for (unsigned i = NumFixedOps; i < NumOps; ++i) {
235 OS << ' ';
237 OS);
238 if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {
239 assert(i + 1 < NumOps && "Missing alignment operand");
240 OS << ' ';
241 printOperand(MI, i + 1, OS);
242 i += 1;
243 }
244 }
245 break;
246 }
247 case SPIRV::OpConstantI:
248 case SPIRV::OpConstantF:
249 // The last fixed operand along with any variadic operands that follow
250 // are part of the variable value.
251 assert(NumFixedOps > 0 && "Expected at least one fixed operand");
252 printOpConstantVarOps(MI, NumFixedOps - 1, OS);
253 break;
254 case SPIRV::OpCooperativeMatrixMulAddKHR: {
255 const unsigned NumOps = MI->getNumOperands();
256 if (NumFixedOps == NumOps)
257 break;
258
259 OS << ' ';
260 const unsigned MulAddOp = MI->getOperand(FirstVariableIndex).getImm();
261 if (MulAddOp == 0) {
263 OperandCategory::CooperativeMatrixOperandsOperand>(
264 MI, FirstVariableIndex, OS);
265 } else {
266 std::string Buffer;
267 for (unsigned Mask = 0x1;
268 Mask != SPIRV::CooperativeMatrixOperands::
269 MatrixResultBFloat16ComponentsINTEL;
270 Mask <<= 1) {
271 if (MulAddOp & Mask) {
272 if (!Buffer.empty())
273 Buffer += '|';
275 OperandCategory::CooperativeMatrixOperandsOperand, Mask);
276 }
277 }
278 OS << Buffer;
279 }
280 break;
281 }
282 case SPIRV::OpSubgroupMatrixMultiplyAccumulateINTEL: {
283 const unsigned NumOps = MI->getNumOperands();
284 if (NumFixedOps >= NumOps)
285 break;
286 OS << ' ';
287 const unsigned Flags = MI->getOperand(NumOps - 1).getImm();
288 if (Flags == 0) {
290 OperandCategory::MatrixMultiplyAccumulateOperandsOperand>(
291 MI, NumOps - 1, OS);
292 } else {
293 std::string Buffer;
294 for (unsigned Mask = 0x1;
295 Mask <= SPIRV::MatrixMultiplyAccumulateOperands::
296 MatrixBPackedBFloat16INTEL;
297 Mask <<= 1) {
298 if (Flags & Mask) {
299 if (!Buffer.empty())
300 Buffer += '|';
302 OperandCategory::MatrixMultiplyAccumulateOperandsOperand,
303 Mask);
304 }
305 }
306 OS << Buffer;
307 }
308 break;
309 }
310 case SPIRV::OpSDot:
311 case SPIRV::OpUDot:
312 case SPIRV::OpSUDot:
313 case SPIRV::OpSDotAccSat:
314 case SPIRV::OpUDotAccSat:
315 case SPIRV::OpSUDotAccSat: {
316 const unsigned NumOps = MI->getNumOperands();
317 if (NumOps > NumFixedOps) {
318 OS << ' ';
320 MI, NumOps - 1, OS);
321 break;
322 }
323 break;
324 }
325 case SPIRV::OpPredicatedLoadINTEL:
326 case SPIRV::OpPredicatedStoreINTEL: {
327 const unsigned NumOps = MI->getNumOperands();
328 if (NumOps > NumFixedOps) {
329 OS << ' ';
331 MI, NumOps - 1, OS);
332 break;
333 }
334 break;
335 }
336 default:
337 printRemainingVariableOps(MI, NumFixedOps, OS);
338 break;
339 }
340 }
341 }
342 }
343
344 printAnnotation(OS, Annot);
345}
346
348 // The fixed operands have already been printed, so just need to decide what
349 // type of ExtInst operands to print based on the instruction set and number.
350 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
351 unsigned NumFixedOps = MCDesc.getNumOperands();
352 const auto NumOps = MI->getNumOperands();
353 if (NumOps == NumFixedOps)
354 return;
355
356 O << ' ';
357
358 // TODO: implement special printing for OpenCLExtInst::vstor*.
359 printRemainingVariableOps(MI, NumFixedOps, O, true);
360}
361
363 // The fixed operands have already been printed, so just need to decide what
364 // type of decoration operands to print based on the Decoration type.
365 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
366 unsigned NumFixedOps = MCDesc.getNumOperands();
367
368 if (NumFixedOps != MI->getNumOperands()) {
369 auto DecOp = MI->getOperand(NumFixedOps - 1);
370 auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());
371
372 O << ' ';
373
374 switch (Dec) {
375 case Decoration::BuiltIn:
377 break;
378 case Decoration::UniformId:
380 break;
381 case Decoration::FuncParamAttr:
383 MI, NumFixedOps, O);
384 break;
385 case Decoration::FPRoundingMode:
387 MI, NumFixedOps, O);
388 break;
389 case Decoration::FPFastMathMode:
391 MI, NumFixedOps, O);
392 break;
393 case Decoration::LinkageAttributes:
394 case Decoration::UserSemantic:
395 printStringImm(MI, NumFixedOps, O);
396 break;
397 case Decoration::HostAccessINTEL:
398 printOperand(MI, NumFixedOps, O);
399 if (NumFixedOps + 1 < MI->getNumOperands()) {
400 O << ' ';
401 printStringImm(MI, NumFixedOps + 1, O);
402 }
403 break;
404 default:
405 printRemainingVariableOps(MI, NumFixedOps, O, true);
406 break;
407 }
408 }
409}
410
412 const auto EnumOperand = MI->getOperand(1);
413 assert(EnumOperand.isImm() &&
414 "second operand of UNKNOWN_type must be opcode!");
415
416 const auto Enumerant = EnumOperand.getImm();
417 const auto NumOps = MI->getNumOperands();
418
419 // Print the opcode using the spirv-as unknown opcode syntax
420 O << "OpUnknown(" << Enumerant << ", " << NumOps << ") ";
421
422 // The result ID must be printed after the opcode when using this syntax
423 printOperand(MI, 0, O);
424
425 O << " ";
426
427 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
428 unsigned NumFixedOps = MCDesc.getNumOperands();
429 if (NumOps == NumFixedOps)
430 return;
431
432 // Print the rest of the operands
433 printRemainingVariableOps(MI, NumFixedOps, O, true);
434}
435
436void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
437 raw_ostream &O) {
438 if (OpNo < MI->getNumOperands()) {
439 const MCOperand &Op = MI->getOperand(OpNo);
440 if (Op.isReg())
441 O << '%' << (getIDFromRegister(Op.getReg().id()) + 1);
442 else if (Op.isImm()) {
443 int64_t Imm = Op.getImm();
444 // For OpVectorShuffle:
445 // A Component literal may also be FFFFFFFF, which means the corresponding
446 // result component has no source and is undefined.
447 // LLVM representation of poison/undef becomes -1 when lowered to MI.
448 if (MI->getOpcode() == SPIRV::OpVectorShuffle && Imm == -1)
449 O << "0xFFFFFFFF";
450 else
451 O << formatImm(Imm);
452 } else if (Op.isDFPImm())
453 O << formatImm((double)Op.getDFPImm());
454 else if (Op.isExpr())
455 MAI.printExpr(O, *Op.getExpr());
456 else
457 llvm_unreachable("Unexpected operand type");
458 }
459}
460
461void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
462 raw_ostream &O) {
463 const unsigned NumOps = MI->getNumOperands();
464 unsigned StrStartIndex = OpNo;
465 while (StrStartIndex < NumOps) {
466 if (MI->getOperand(StrStartIndex).isReg())
467 break;
468
469 std::string Str = getSPIRVStringOperand(*MI, StrStartIndex);
470 if (StrStartIndex != OpNo)
471 O << ' '; // Add a space if we're starting a new string/argument.
472 O << '"';
473 for (char c : Str) {
474 // Escape ", \n characters (might break for complex UTF-8).
475 if (c == '\n') {
476 O.write("\\n", 2);
477 } else {
478 if (c == '"')
479 O.write('\\');
480 O.write(c);
481 }
482 }
483 O << '"';
484
485 unsigned numOpsInString = (Str.size() / 4) + 1;
486 StrStartIndex += numOpsInString;
487
488 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
489 if (MI->getOpcode() == SPIRV::OpDecorate &&
490 MI->getOperand(1).getImm() ==
491 static_cast<unsigned>(Decoration::LinkageAttributes)) {
492 O << ' ';
494 MI, StrStartIndex, O);
495 break;
496 }
497 }
498}
499
500void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,
501 raw_ostream &O) {
502 auto SetReg = MI->getOperand(2).getReg();
503 auto Set = ExtInstSetIDs[SetReg];
504 auto Op = MI->getOperand(OpNo).getImm();
505 O << getExtInstName(Set, Op);
506}
507
508template <OperandCategory::OperandCategory category>
510 raw_ostream &O) {
511 if (OpNo < MI->getNumOperands()) {
512 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());
513 }
514}
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
#define I(x, y, z)
Definition MD5.cpp:57
MachineInstr unsigned OpIdx
Class for arbitrary precision integers.
Definition APInt.h:78
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:41
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