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
);
215 printRemainingVariableOps(MI
, NumFixedOps
, OS
);
222 printAnnotation(OS
, Annot
);
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
)
236 // TODO: implement special printing for OpenCLExtInst::vstor*.
237 printRemainingVariableOps(MI
, NumFixedOps
, O
, true);
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();
246 if (NumFixedOps
!= MI
->getNumOperands()) {
247 auto DecOp
= MI
->getOperand(NumFixedOps
- 1);
248 auto Dec
= static_cast<Decoration::Decoration
>(DecOp
.getImm());
253 case Decoration::BuiltIn
:
254 printSymbolicOperand
<OperandCategory::BuiltInOperand
>(MI
, NumFixedOps
, O
);
256 case Decoration::UniformId
:
257 printSymbolicOperand
<OperandCategory::ScopeOperand
>(MI
, NumFixedOps
, O
);
259 case Decoration::FuncParamAttr
:
260 printSymbolicOperand
<OperandCategory::FunctionParameterAttributeOperand
>(
263 case Decoration::FPRoundingMode
:
264 printSymbolicOperand
<OperandCategory::FPRoundingModeOperand
>(
267 case Decoration::FPFastMathMode
:
268 printSymbolicOperand
<OperandCategory::FPFastMathModeOperand
>(
271 case Decoration::LinkageAttributes
:
272 case Decoration::UserSemantic
:
273 printStringImm(MI
, NumFixedOps
, O
);
275 case Decoration::HostAccessINTEL
:
276 printOperand(MI
, NumFixedOps
, O
);
277 if (NumFixedOps
+ 1 < MI
->getNumOperands()) {
279 printStringImm(MI
, NumFixedOps
+ 1, O
);
283 printRemainingVariableOps(MI
, NumFixedOps
, O
, true);
289 static void printExpr(const MCExpr
*Expr
, raw_ostream
&O
) {
291 const MCSymbolRefExpr
*SRE
;
293 if (const MCBinaryExpr
*BE
= dyn_cast
<MCBinaryExpr
>(Expr
))
294 SRE
= cast
<MCSymbolRefExpr
>(BE
->getLHS());
296 SRE
= cast
<MCSymbolRefExpr
>(Expr
);
298 MCSymbolRefExpr::VariantKind Kind
= SRE
->getKind();
300 assert(Kind
== MCSymbolRefExpr::VK_None
);
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
);
311 O
<< '%' << (Register::virtReg2Index(Op
.getReg()) + 1);
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
);
319 llvm_unreachable("Unexpected operand type");
323 void SPIRVInstPrinter::printStringImm(const MCInst
*MI
, unsigned OpNo
,
325 const unsigned NumOps
= MI
->getNumOperands();
326 unsigned StrStartIndex
= OpNo
;
327 while (StrStartIndex
< NumOps
) {
328 if (MI
->getOperand(StrStartIndex
).isReg())
331 std::string Str
= getSPIRVStringOperand(*MI
, StrStartIndex
);
332 if (StrStartIndex
!= OpNo
)
333 O
<< ' '; // Add a space if we're starting a new string/argument.
336 // Escape ", \n characters (might break for complex UTF-8).
347 unsigned numOpsInString
= (Str
.size() / 4) + 1;
348 StrStartIndex
+= numOpsInString
;
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
)) {
355 printSymbolicOperand
<OperandCategory::LinkageTypeOperand
>(
356 MI
, StrStartIndex
, O
);
362 void SPIRVInstPrinter::printExtension(const MCInst
*MI
, unsigned OpNo
,
364 auto SetReg
= MI
->getOperand(2).getReg();
365 auto Set
= ExtInstSetIDs
[SetReg
];
366 auto Op
= MI
->getOperand(OpNo
).getImm();
367 O
<< getExtInstName(Set
, Op
);
370 template <OperandCategory::OperandCategory category
>
371 void SPIRVInstPrinter::printSymbolicOperand(const MCInst
*MI
, unsigned OpNo
,
373 if (OpNo
< MI
->getNumOperands()) {
374 O
<< getSymbolicOperandMnemonic(category
, MI
->getOperand(OpNo
).getImm());