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 "llvm/ADT/APFloat.h"
17 #include "llvm/CodeGen/Register.h"
18 #include "llvm/MC/MCAsmInfo.h"
19 #include "llvm/MC/MCExpr.h"
20 #include "llvm/MC/MCInst.h"
21 #include "llvm/MC/MCInstrInfo.h"
22 #include "llvm/MC/MCSymbol.h"
23 #include "llvm/Support/Casting.h"
24 #include "llvm/Support/ErrorHandling.h"
25 #include "llvm/Support/FormattedStream.h"
28 using namespace llvm::SPIRV
;
30 #define DEBUG_TYPE "asm-printer"
32 // Include the auto-generated portion of the assembly writer.
33 #include "SPIRVGenAsmWriter.inc"
35 void SPIRVInstPrinter::printRemainingVariableOps(const MCInst
*MI
,
39 bool SkipImmediates
) {
40 const unsigned NumOps
= MI
->getNumOperands();
41 for (unsigned i
= StartIndex
; i
< NumOps
; ++i
) {
42 if (!SkipImmediates
|| !MI
->getOperand(i
).isImm()) {
43 if (!SkipFirstSpace
|| i
!= StartIndex
)
45 printOperand(MI
, i
, O
);
50 void SPIRVInstPrinter::printOpConstantVarOps(const MCInst
*MI
,
53 const unsigned NumVarOps
= MI
->getNumOperands() - StartIndex
;
55 assert((NumVarOps
== 1 || NumVarOps
== 2) &&
56 "Unsupported number of bits for literal variable");
60 uint64_t Imm
= MI
->getOperand(StartIndex
).getImm();
62 // Handle 64 bit literals.
64 Imm
|= (MI
->getOperand(StartIndex
+ 1).getImm() << 32);
67 // Format and print float values.
68 if (MI
->getOpcode() == SPIRV::OpConstantF
) {
69 APFloat FP
= NumVarOps
== 1 ? APFloat(APInt(32, Imm
).bitsToFloat())
70 : APFloat(APInt(64, Imm
).bitsToDouble());
72 // Print infinity and NaN as hex floats.
73 // TODO: Make sure subnormal numbers are handled correctly as they may also
74 // require hex float notation.
75 if (FP
.isInfinity()) {
86 // Format val as a decimal floating point or scientific notation (whichever
87 // is shorter), with enough digits of precision to produce the exact value.
88 O
<< format("%.*g", std::numeric_limits
<double>::max_digits10
,
89 FP
.convertToDouble());
94 // Print integer values directly.
98 void SPIRVInstPrinter::recordOpExtInstImport(const MCInst
*MI
) {
99 Register Reg
= MI
->getOperand(0).getReg();
100 auto Name
= getSPIRVStringOperand(*MI
, 1);
101 auto Set
= getExtInstSetFromString(Name
);
102 ExtInstSetIDs
.insert({Reg
, Set
});
105 void SPIRVInstPrinter::printInst(const MCInst
*MI
, uint64_t Address
,
106 StringRef Annot
, const MCSubtargetInfo
&STI
,
108 const unsigned OpCode
= MI
->getOpcode();
109 printInstruction(MI
, Address
, OS
);
111 if (OpCode
== SPIRV::OpDecorate
) {
112 printOpDecorate(MI
, OS
);
113 } else if (OpCode
== SPIRV::OpExtInstImport
) {
114 recordOpExtInstImport(MI
);
115 } else if (OpCode
== SPIRV::OpExtInst
) {
116 printOpExtInst(MI
, OS
);
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
==
125 MCOI::OPERAND_UNKNOWN
) {
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.
130 case SPIRV::OpTypeImage
:
132 printSymbolicOperand
<OperandCategory::AccessQualifierOperand
>(
133 MI
, FirstVariableIndex
, OS
);
135 case SPIRV::OpVariable
:
137 printOperand(MI
, FirstVariableIndex
, OS
);
139 case SPIRV::OpEntryPoint
: {
140 // Print the interface ID operands, skipping the name's string
142 printRemainingVariableOps(MI
, NumFixedOps
, OS
, false, true);
145 case SPIRV::OpExecutionMode
:
146 case SPIRV::OpExecutionModeId
:
147 case SPIRV::OpLoopMerge
: {
148 // Print any literals after the OPERAND_UNKNOWN argument normally.
149 printRemainingVariableOps(MI
, NumFixedOps
, OS
);
153 break; // printStringImm has already been handled.
156 // For instructions with no fixed ops or a reg/immediate as the final
157 // fixed operand, we can usually print the rest with "printOperand", but
158 // check for a few cases with custom types first.
163 printSymbolicOperand
<OperandCategory::MemoryOperandOperand
>(
164 MI
, FirstVariableIndex
, OS
);
165 printRemainingVariableOps(MI
, FirstVariableIndex
+ 1, OS
);
167 case SPIRV::OpImageSampleImplicitLod
:
168 case SPIRV::OpImageSampleDrefImplicitLod
:
169 case SPIRV::OpImageSampleProjImplicitLod
:
170 case SPIRV::OpImageSampleProjDrefImplicitLod
:
171 case SPIRV::OpImageFetch
:
172 case SPIRV::OpImageGather
:
173 case SPIRV::OpImageDrefGather
:
174 case SPIRV::OpImageRead
:
175 case SPIRV::OpImageWrite
:
176 case SPIRV::OpImageSparseSampleImplicitLod
:
177 case SPIRV::OpImageSparseSampleDrefImplicitLod
:
178 case SPIRV::OpImageSparseSampleProjImplicitLod
:
179 case SPIRV::OpImageSparseSampleProjDrefImplicitLod
:
180 case SPIRV::OpImageSparseFetch
:
181 case SPIRV::OpImageSparseGather
:
182 case SPIRV::OpImageSparseDrefGather
:
183 case SPIRV::OpImageSparseRead
:
184 case SPIRV::OpImageSampleFootprintNV
:
186 printSymbolicOperand
<OperandCategory::ImageOperandOperand
>(
187 MI
, FirstVariableIndex
, OS
);
188 printRemainingVariableOps(MI
, NumFixedOps
+ 1, OS
);
190 case SPIRV::OpCopyMemory
:
191 case SPIRV::OpCopyMemorySized
: {
192 const unsigned NumOps
= MI
->getNumOperands();
193 for (unsigned i
= NumFixedOps
; i
< NumOps
; ++i
) {
195 printSymbolicOperand
<OperandCategory::MemoryOperandOperand
>(MI
, i
,
197 if (MI
->getOperand(i
).getImm() & MemoryOperand::Aligned
) {
198 assert(i
+ 1 < NumOps
&& "Missing alignment operand");
200 printOperand(MI
, i
+ 1, OS
);
206 case SPIRV::OpConstantI
:
207 case SPIRV::OpConstantF
:
208 // The last fixed operand along with any variadic operands that follow
209 // are part of the variable value.
210 printOpConstantVarOps(MI
, NumFixedOps
- 1, OS
);
213 printRemainingVariableOps(MI
, NumFixedOps
, OS
);
220 printAnnotation(OS
, Annot
);
223 void SPIRVInstPrinter::printOpExtInst(const MCInst
*MI
, raw_ostream
&O
) {
224 // The fixed operands have already been printed, so just need to decide what
225 // type of ExtInst operands to print based on the instruction set and number.
226 const MCInstrDesc
&MCDesc
= MII
.get(MI
->getOpcode());
227 unsigned NumFixedOps
= MCDesc
.getNumOperands();
228 const auto NumOps
= MI
->getNumOperands();
229 if (NumOps
== NumFixedOps
)
234 // TODO: implement special printing for OpenCLExtInst::vstor*.
235 printRemainingVariableOps(MI
, NumFixedOps
, O
, true);
238 void SPIRVInstPrinter::printOpDecorate(const MCInst
*MI
, raw_ostream
&O
) {
239 // The fixed operands have already been printed, so just need to decide what
240 // type of decoration operands to print based on the Decoration type.
241 const MCInstrDesc
&MCDesc
= MII
.get(MI
->getOpcode());
242 unsigned NumFixedOps
= MCDesc
.getNumOperands();
244 if (NumFixedOps
!= MI
->getNumOperands()) {
245 auto DecOp
= MI
->getOperand(NumFixedOps
- 1);
246 auto Dec
= static_cast<Decoration::Decoration
>(DecOp
.getImm());
251 case Decoration::BuiltIn
:
252 printSymbolicOperand
<OperandCategory::BuiltInOperand
>(MI
, NumFixedOps
, O
);
254 case Decoration::UniformId
:
255 printSymbolicOperand
<OperandCategory::ScopeOperand
>(MI
, NumFixedOps
, O
);
257 case Decoration::FuncParamAttr
:
258 printSymbolicOperand
<OperandCategory::FunctionParameterAttributeOperand
>(
261 case Decoration::FPRoundingMode
:
262 printSymbolicOperand
<OperandCategory::FPRoundingModeOperand
>(
265 case Decoration::FPFastMathMode
:
266 printSymbolicOperand
<OperandCategory::FPFastMathModeOperand
>(
269 case Decoration::LinkageAttributes
:
270 case Decoration::UserSemantic
:
271 printStringImm(MI
, NumFixedOps
, O
);
274 printRemainingVariableOps(MI
, NumFixedOps
, O
, true);
280 static void printExpr(const MCExpr
*Expr
, raw_ostream
&O
) {
282 const MCSymbolRefExpr
*SRE
;
284 if (const MCBinaryExpr
*BE
= dyn_cast
<MCBinaryExpr
>(Expr
))
285 SRE
= cast
<MCSymbolRefExpr
>(BE
->getLHS());
287 SRE
= cast
<MCSymbolRefExpr
>(Expr
);
289 MCSymbolRefExpr::VariantKind Kind
= SRE
->getKind();
291 assert(Kind
== MCSymbolRefExpr::VK_None
);
296 void SPIRVInstPrinter::printOperand(const MCInst
*MI
, unsigned OpNo
,
297 raw_ostream
&O
, const char *Modifier
) {
298 assert((Modifier
== 0 || Modifier
[0] == 0) && "No modifiers supported");
299 if (OpNo
< MI
->getNumOperands()) {
300 const MCOperand
&Op
= MI
->getOperand(OpNo
);
302 O
<< '%' << (Register::virtReg2Index(Op
.getReg()) + 1);
304 O
<< formatImm((int64_t)Op
.getImm());
305 else if (Op
.isDFPImm())
306 O
<< formatImm((double)Op
.getDFPImm());
307 else if (Op
.isExpr())
308 printExpr(Op
.getExpr(), O
);
310 llvm_unreachable("Unexpected operand type");
314 void SPIRVInstPrinter::printStringImm(const MCInst
*MI
, unsigned OpNo
,
316 const unsigned NumOps
= MI
->getNumOperands();
317 unsigned StrStartIndex
= OpNo
;
318 while (StrStartIndex
< NumOps
) {
319 if (MI
->getOperand(StrStartIndex
).isReg())
322 std::string Str
= getSPIRVStringOperand(*MI
, OpNo
);
323 if (StrStartIndex
!= OpNo
)
324 O
<< ' '; // Add a space if we're starting a new string/argument.
328 O
.write('\\'); // Escape " characters (might break for complex UTF-8).
333 unsigned numOpsInString
= (Str
.size() / 4) + 1;
334 StrStartIndex
+= numOpsInString
;
336 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
337 if (MI
->getOpcode() == SPIRV::OpDecorate
&&
338 MI
->getOperand(1).getImm() ==
339 static_cast<unsigned>(Decoration::LinkageAttributes
)) {
341 printSymbolicOperand
<OperandCategory::LinkageTypeOperand
>(
342 MI
, StrStartIndex
, O
);
348 void SPIRVInstPrinter::printExtension(const MCInst
*MI
, unsigned OpNo
,
350 auto SetReg
= MI
->getOperand(2).getReg();
351 auto Set
= ExtInstSetIDs
[SetReg
];
352 auto Op
= MI
->getOperand(OpNo
).getImm();
353 O
<< getExtInstName(Set
, Op
);
356 template <OperandCategory::OperandCategory category
>
357 void SPIRVInstPrinter::printSymbolicOperand(const MCInst
*MI
, unsigned OpNo
,
359 if (OpNo
< MI
->getNumOperands()) {
360 O
<< getSymbolicOperandMnemonic(category
, MI
->getOperand(OpNo
).getImm());