1 //===-- SPIRVInstPrinter.cpp - Output SPIR-V MCInsts as ASM -----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This class prints a SPIR-V MCInst to a .s file.
11 //===----------------------------------------------------------------------===//
13 #include "SPIRVInstPrinter.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"
29 using namespace llvm::SPIRV
;
31 #define DEBUG_TYPE "asm-printer"
33 // Include the auto-generated portion of the assembly writer.
34 #include "SPIRVGenAsmWriter.inc"
36 void SPIRVInstPrinter::printRemainingVariableOps(const MCInst
*MI
,
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
)
46 printOperand(MI
, i
, O
);
51 void SPIRVInstPrinter::printOpConstantVarOps(const MCInst
*MI
,
54 unsigned IsBitwidth16
= MI
->getFlags() & SPIRV::ASM_PRINTER_WIDTH16
;
55 const unsigned NumVarOps
= MI
->getNumOperands() - StartIndex
;
57 assert((NumVarOps
== 1 || NumVarOps
== 2) &&
58 "Unsupported number of bits for literal variable");
62 uint64_t Imm
= MI
->getOperand(StartIndex
).getImm();
64 // Handle 64 bit literals.
66 Imm
|= (MI
->getOperand(StartIndex
+ 1).getImm() << 32);
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());
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()) {
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());
96 // Print integer values directly.
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
});
107 void SPIRVInstPrinter::printInst(const MCInst
*MI
, uint64_t Address
,
108 StringRef Annot
, const MCSubtargetInfo
&STI
,
110 const unsigned OpCode
= MI
->getOpcode();
111 printInstruction(MI
, Address
, OS
);
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
);
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.
132 case SPIRV::OpTypeImage
:
134 printSymbolicOperand
<OperandCategory::AccessQualifierOperand
>(
135 MI
, FirstVariableIndex
, OS
);
137 case SPIRV::OpVariable
:
139 printOperand(MI
, FirstVariableIndex
, OS
);
141 case SPIRV::OpEntryPoint
: {
142 // Print the interface ID operands, skipping the name's string
144 printRemainingVariableOps(MI
, NumFixedOps
, OS
, false, true);
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
);
155 break; // printStringImm has already been handled.
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.
165 printSymbolicOperand
<OperandCategory::MemoryOperandOperand
>(
166 MI
, FirstVariableIndex
, OS
);
167 printRemainingVariableOps(MI
, FirstVariableIndex
+ 1, OS
);
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
:
188 printSymbolicOperand
<OperandCategory::ImageOperandOperand
>(
189 MI
, FirstVariableIndex
, OS
);
190 printRemainingVariableOps(MI
, NumFixedOps
+ 1, OS
);
192 case SPIRV::OpCopyMemory
:
193 case SPIRV::OpCopyMemorySized
: {
194 const unsigned NumOps
= MI
->getNumOperands();
195 for (unsigned i
= NumFixedOps
; i
< NumOps
; ++i
) {
197 printSymbolicOperand
<OperandCategory::MemoryOperandOperand
>(MI
, i
,
199 if (MI
->getOperand(i
).getImm() & MemoryOperand::Aligned
) {
200 assert(i
+ 1 < NumOps
&& "Missing alignment operand");
202 printOperand(MI
, i
+ 1, OS
);
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
);
214 case SPIRV::OpCooperativeMatrixMulAddKHR
: {
215 const unsigned NumOps
= MI
->getNumOperands();
216 if (NumFixedOps
== NumOps
)
220 const unsigned MulAddOp
= MI
->getOperand(FirstVariableIndex
).getImm();
222 printSymbolicOperand
<
223 OperandCategory::CooperativeMatrixOperandsOperand
>(
224 MI
, FirstVariableIndex
, OS
);
227 for (unsigned Mask
= 0x1;
228 Mask
!= SPIRV::CooperativeMatrixOperands::
229 MatrixResultBFloat16ComponentsINTEL
;
231 if (MulAddOp
& Mask
) {
234 Buffer
+= getSymbolicOperandMnemonic(
235 OperandCategory::CooperativeMatrixOperandsOperand
, Mask
);
243 printRemainingVariableOps(MI
, NumFixedOps
, OS
);
250 printAnnotation(OS
, Annot
);
253 void SPIRVInstPrinter::printOpExtInst(const MCInst
*MI
, raw_ostream
&O
) {
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
)
264 // TODO: implement special printing for OpenCLExtInst::vstor*.
265 printRemainingVariableOps(MI
, NumFixedOps
, O
, true);
268 void SPIRVInstPrinter::printOpDecorate(const MCInst
*MI
, raw_ostream
&O
) {
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();
274 if (NumFixedOps
!= MI
->getNumOperands()) {
275 auto DecOp
= MI
->getOperand(NumFixedOps
- 1);
276 auto Dec
= static_cast<Decoration::Decoration
>(DecOp
.getImm());
281 case Decoration::BuiltIn
:
282 printSymbolicOperand
<OperandCategory::BuiltInOperand
>(MI
, NumFixedOps
, O
);
284 case Decoration::UniformId
:
285 printSymbolicOperand
<OperandCategory::ScopeOperand
>(MI
, NumFixedOps
, O
);
287 case Decoration::FuncParamAttr
:
288 printSymbolicOperand
<OperandCategory::FunctionParameterAttributeOperand
>(
291 case Decoration::FPRoundingMode
:
292 printSymbolicOperand
<OperandCategory::FPRoundingModeOperand
>(
295 case Decoration::FPFastMathMode
:
296 printSymbolicOperand
<OperandCategory::FPFastMathModeOperand
>(
299 case Decoration::LinkageAttributes
:
300 case Decoration::UserSemantic
:
301 printStringImm(MI
, NumFixedOps
, O
);
303 case Decoration::HostAccessINTEL
:
304 printOperand(MI
, NumFixedOps
, O
);
305 if (NumFixedOps
+ 1 < MI
->getNumOperands()) {
307 printStringImm(MI
, NumFixedOps
+ 1, O
);
311 printRemainingVariableOps(MI
, NumFixedOps
, O
, true);
317 static void printExpr(const MCExpr
*Expr
, raw_ostream
&O
) {
319 const MCSymbolRefExpr
*SRE
;
321 if (const MCBinaryExpr
*BE
= dyn_cast
<MCBinaryExpr
>(Expr
))
322 SRE
= cast
<MCSymbolRefExpr
>(BE
->getLHS());
324 SRE
= cast
<MCSymbolRefExpr
>(Expr
);
326 MCSymbolRefExpr::VariantKind Kind
= SRE
->getKind();
328 assert(Kind
== MCSymbolRefExpr::VK_None
);
333 void 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
);
339 O
<< '%' << (Register::virtReg2Index(Op
.getReg()) + 1);
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
);
347 llvm_unreachable("Unexpected operand type");
351 void SPIRVInstPrinter::printStringImm(const MCInst
*MI
, unsigned OpNo
,
353 const unsigned NumOps
= MI
->getNumOperands();
354 unsigned StrStartIndex
= OpNo
;
355 while (StrStartIndex
< NumOps
) {
356 if (MI
->getOperand(StrStartIndex
).isReg())
359 std::string Str
= getSPIRVStringOperand(*MI
, StrStartIndex
);
360 if (StrStartIndex
!= OpNo
)
361 O
<< ' '; // Add a space if we're starting a new string/argument.
364 // Escape ", \n characters (might break for complex UTF-8).
375 unsigned numOpsInString
= (Str
.size() / 4) + 1;
376 StrStartIndex
+= numOpsInString
;
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
)) {
383 printSymbolicOperand
<OperandCategory::LinkageTypeOperand
>(
384 MI
, StrStartIndex
, O
);
390 void SPIRVInstPrinter::printExtension(const MCInst
*MI
, unsigned OpNo
,
392 auto SetReg
= MI
->getOperand(2).getReg();
393 auto Set
= ExtInstSetIDs
[SetReg
];
394 auto Op
= MI
->getOperand(OpNo
).getImm();
395 O
<< getExtInstName(Set
, Op
);
398 template <OperandCategory::OperandCategory category
>
399 void SPIRVInstPrinter::printSymbolicOperand(const MCInst
*MI
, unsigned OpNo
,
401 if (OpNo
< MI
->getNumOperands()) {
402 O
<< getSymbolicOperandMnemonic(category
, MI
->getOperand(OpNo
).getImm());