LLVM 20.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 "SPIRVInstrInfo.h"
17#include "llvm/ADT/APFloat.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"
27
28using namespace llvm;
29using 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
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
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
100void 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
108 StringRef Annot, const MCSubtargetInfo &STI,
109 raw_ostream &OS) {
110 const unsigned OpCode = MI->getOpcode();
112
113 if (OpCode == SPIRV::OpDecorate) {
115 } else if (OpCode == SPIRV::OpExtInstImport) {
116 recordOpExtInstImport(MI);
117 } else if (OpCode == SPIRV::OpExtInst) {
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 ==
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 case SPIRV::OpCooperativeMatrixMulAddKHR: {
215 const unsigned NumOps = MI->getNumOperands();
216 if (NumFixedOps == NumOps)
217 break;
218
219 OS << ' ';
220 const unsigned MulAddOp = MI->getOperand(FirstVariableIndex).getImm();
221 if (MulAddOp == 0) {
223 OperandCategory::CooperativeMatrixOperandsOperand>(
224 MI, FirstVariableIndex, OS);
225 } else {
226 std::string Buffer;
227 for (unsigned Mask = 0x1;
228 Mask != SPIRV::CooperativeMatrixOperands::
229 MatrixResultBFloat16ComponentsINTEL;
230 Mask <<= 1) {
231 if (MulAddOp & Mask) {
232 if (!Buffer.empty())
233 Buffer += '|';
235 OperandCategory::CooperativeMatrixOperandsOperand, Mask);
236 }
237 }
238 OS << Buffer;
239 }
240 break;
241 }
242 default:
243 printRemainingVariableOps(MI, NumFixedOps, OS);
244 break;
245 }
246 }
247 }
248 }
249
250 printAnnotation(OS, Annot);
251}
252
254 // The fixed operands have already been printed, so just need to decide what
255 // type of ExtInst operands to print based on the instruction set and number.
256 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
257 unsigned NumFixedOps = MCDesc.getNumOperands();
258 const auto NumOps = MI->getNumOperands();
259 if (NumOps == NumFixedOps)
260 return;
261
262 O << ' ';
263
264 // TODO: implement special printing for OpenCLExtInst::vstor*.
265 printRemainingVariableOps(MI, NumFixedOps, O, true);
266}
267
269 // The fixed operands have already been printed, so just need to decide what
270 // type of decoration operands to print based on the Decoration type.
271 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
272 unsigned NumFixedOps = MCDesc.getNumOperands();
273
274 if (NumFixedOps != MI->getNumOperands()) {
275 auto DecOp = MI->getOperand(NumFixedOps - 1);
276 auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());
277
278 O << ' ';
279
280 switch (Dec) {
281 case Decoration::BuiltIn:
282 printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O);
283 break;
284 case Decoration::UniformId:
285 printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O);
286 break;
287 case Decoration::FuncParamAttr:
288 printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>(
289 MI, NumFixedOps, O);
290 break;
291 case Decoration::FPRoundingMode:
292 printSymbolicOperand<OperandCategory::FPRoundingModeOperand>(
293 MI, NumFixedOps, O);
294 break;
295 case Decoration::FPFastMathMode:
296 printSymbolicOperand<OperandCategory::FPFastMathModeOperand>(
297 MI, NumFixedOps, O);
298 break;
299 case Decoration::LinkageAttributes:
300 case Decoration::UserSemantic:
301 printStringImm(MI, NumFixedOps, O);
302 break;
303 case Decoration::HostAccessINTEL:
304 printOperand(MI, NumFixedOps, O);
305 if (NumFixedOps + 1 < MI->getNumOperands()) {
306 O << ' ';
307 printStringImm(MI, NumFixedOps + 1, O);
308 }
309 break;
310 default:
311 printRemainingVariableOps(MI, NumFixedOps, O, true);
312 break;
313 }
314 }
315}
316
317static void printExpr(const MCExpr *Expr, raw_ostream &O) {
318#ifndef NDEBUG
319 const MCSymbolRefExpr *SRE;
320
321 if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr))
322 SRE = cast<MCSymbolRefExpr>(BE->getLHS());
323 else
324 SRE = cast<MCSymbolRefExpr>(Expr);
325
327
329#endif
330 O << *Expr;
331}
332
333void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
334 raw_ostream &O, const char *Modifier) {
335 assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported");
336 if (OpNo < MI->getNumOperands()) {
337 const MCOperand &Op = MI->getOperand(OpNo);
338 if (Op.isReg())
339 O << '%' << (Register::virtReg2Index(Op.getReg()) + 1);
340 else if (Op.isImm())
341 O << formatImm((int64_t)Op.getImm());
342 else if (Op.isDFPImm())
343 O << formatImm((double)Op.getDFPImm());
344 else if (Op.isExpr())
345 printExpr(Op.getExpr(), O);
346 else
347 llvm_unreachable("Unexpected operand type");
348 }
349}
350
351void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
352 raw_ostream &O) {
353 const unsigned NumOps = MI->getNumOperands();
354 unsigned StrStartIndex = OpNo;
355 while (StrStartIndex < NumOps) {
356 if (MI->getOperand(StrStartIndex).isReg())
357 break;
358
359 std::string Str = getSPIRVStringOperand(*MI, StrStartIndex);
360 if (StrStartIndex != OpNo)
361 O << ' '; // Add a space if we're starting a new string/argument.
362 O << '"';
363 for (char c : Str) {
364 // Escape ", \n characters (might break for complex UTF-8).
365 if (c == '\n') {
366 O.write("\\n", 2);
367 } else {
368 if (c == '"')
369 O.write('\\');
370 O.write(c);
371 }
372 }
373 O << '"';
374
375 unsigned numOpsInString = (Str.size() / 4) + 1;
376 StrStartIndex += numOpsInString;
377
378 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
379 if (MI->getOpcode() == SPIRV::OpDecorate &&
380 MI->getOperand(1).getImm() ==
381 static_cast<unsigned>(Decoration::LinkageAttributes)) {
382 O << ' ';
383 printSymbolicOperand<OperandCategory::LinkageTypeOperand>(
384 MI, StrStartIndex, O);
385 break;
386 }
387 }
388}
389
390void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,
391 raw_ostream &O) {
392 auto SetReg = MI->getOperand(2).getReg();
393 auto Set = ExtInstSetIDs[SetReg];
394 auto Op = MI->getOperand(OpNo).getImm();
395 O << getExtInstName(Set, Op);
396}
397
398template <OperandCategory::OperandCategory category>
400 raw_ostream &O) {
401 if (OpNo < MI->getNumOperands()) {
402 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());
403 }
404}
This file declares a class to represent arbitrary precision floating point values and provide a varie...
static void printExpr(const MCExpr *Expr, const MCAsmInfo *MAI, raw_ostream &OS)
std::string Name
IRTranslator LLVM IR MI
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
Class for arbitrary precision integers.
Definition: APInt.h:78
This class represents an Operation in the Expression.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition: DenseMap.h:211
Binary assembler expressions.
Definition: MCExpr.h:493
Base class for the full range of assembler expressions which are needed for parsing.
Definition: MCExpr.h:34
const MCInstrInfo & MII
Definition: MCInstPrinter.h:53
void printAnnotation(raw_ostream &OS, StringRef Annot)
Utility function for printing annotations.
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:185
Describe properties that are true of each instruction in the target description file.
Definition: MCInstrDesc.h:198
unsigned getNumOperands() const
Return the number of declared MachineOperands for this MachineInstruction.
Definition: MCInstrDesc.h:237
ArrayRef< MCOperandInfo > operands() const
Definition: MCInstrDesc.h:239
bool isVariadic() const
Return true if this instruction can have a variable number of operands.
Definition: MCInstrDesc.h:261
const MCInstrDesc & get(unsigned Opcode) const
Return the machine instruction descriptor that corresponds to the specified instruction opcode.
Definition: MCInstrInfo.h:63
Instances of this class represent operands of the MCInst class.
Definition: MCInst.h:37
Generic base class for all target subtargets.
Represent a reference to a symbol from inside an expression.
Definition: MCExpr.h:192
VariantKind getKind() const
Definition: MCExpr.h:413
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
static unsigned virtReg2Index(Register Reg)
Convert a virtual register number to a 0-based index.
Definition: Register.h:77
void printExtension(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printStringImm(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O, const char *Modifier=nullptr)
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 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)
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ OPERAND_UNKNOWN
Definition: MCInstrDesc.h:59
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
std::string getExtInstName(SPIRV::InstructionSet::InstructionSet Set, uint32_t InstructionNumber)
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:125
std::string getSymbolicOperandMnemonic(SPIRV::OperandCategory::OperandCategory Category, int32_t Value)