[llvm-shlib] Fix the version naming style of libLLVM for Windows (#85710)
[llvm-project.git] / llvm / lib / Target / SPIRV / MCTargetDesc / SPIRVInstPrinter.cpp
blob163b2ec0fefe4d9aa081a75d4b9eddda350306fe
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.
11 //===----------------------------------------------------------------------===//
13 #include "SPIRVInstPrinter.h"
14 #include "SPIRV.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"
27 using namespace llvm;
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,
36 unsigned StartIndex,
37 raw_ostream &O,
38 bool SkipFirstSpace,
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)
44 O << ' ';
45 printOperand(MI, i, O);
50 void SPIRVInstPrinter::printOpConstantVarOps(const MCInst *MI,
51 unsigned StartIndex,
52 raw_ostream &O) {
53 const unsigned NumVarOps = MI->getNumOperands() - StartIndex;
55 assert((NumVarOps == 1 || NumVarOps == 2) &&
56 "Unsupported number of bits for literal variable");
58 O << ' ';
60 uint64_t Imm = MI->getOperand(StartIndex).getImm();
62 // Handle 64 bit literals.
63 if (NumVarOps == 2) {
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()) {
76 if (FP.isNegative())
77 O << '-';
78 O << "0x1p+128";
79 return;
81 if (FP.isNaN()) {
82 O << "0x1.8p+128";
83 return;
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());
91 return;
94 // Print integer values directly.
95 O << Imm;
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,
107 raw_ostream &OS) {
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);
117 } else {
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.
129 switch (OpCode) {
130 case SPIRV::OpTypeImage:
131 OS << ' ';
132 printSymbolicOperand<OperandCategory::AccessQualifierOperand>(
133 MI, FirstVariableIndex, OS);
134 break;
135 case SPIRV::OpVariable:
136 OS << ' ';
137 printOperand(MI, FirstVariableIndex, OS);
138 break;
139 case SPIRV::OpEntryPoint: {
140 // Print the interface ID operands, skipping the name's string
141 // literal.
142 printRemainingVariableOps(MI, NumFixedOps, OS, false, true);
143 break;
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);
150 break;
152 default:
153 break; // printStringImm has already been handled.
155 } else {
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.
159 switch (OpCode) {
160 case SPIRV::OpLoad:
161 case SPIRV::OpStore:
162 OS << ' ';
163 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(
164 MI, FirstVariableIndex, OS);
165 printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
166 break;
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:
185 OS << ' ';
186 printSymbolicOperand<OperandCategory::ImageOperandOperand>(
187 MI, FirstVariableIndex, OS);
188 printRemainingVariableOps(MI, NumFixedOps + 1, OS);
189 break;
190 case SPIRV::OpCopyMemory:
191 case SPIRV::OpCopyMemorySized: {
192 const unsigned NumOps = MI->getNumOperands();
193 for (unsigned i = NumFixedOps; i < NumOps; ++i) {
194 OS << ' ';
195 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i,
196 OS);
197 if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {
198 assert(i + 1 < NumOps && "Missing alignment operand");
199 OS << ' ';
200 printOperand(MI, i + 1, OS);
201 i += 1;
204 break;
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);
211 break;
212 default:
213 printRemainingVariableOps(MI, NumFixedOps, OS);
214 break;
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)
230 return;
232 O << ' ';
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());
248 O << ' ';
250 switch (Dec) {
251 case Decoration::BuiltIn:
252 printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O);
253 break;
254 case Decoration::UniformId:
255 printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O);
256 break;
257 case Decoration::FuncParamAttr:
258 printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>(
259 MI, NumFixedOps, O);
260 break;
261 case Decoration::FPRoundingMode:
262 printSymbolicOperand<OperandCategory::FPRoundingModeOperand>(
263 MI, NumFixedOps, O);
264 break;
265 case Decoration::FPFastMathMode:
266 printSymbolicOperand<OperandCategory::FPFastMathModeOperand>(
267 MI, NumFixedOps, O);
268 break;
269 case Decoration::LinkageAttributes:
270 case Decoration::UserSemantic:
271 printStringImm(MI, NumFixedOps, O);
272 break;
273 default:
274 printRemainingVariableOps(MI, NumFixedOps, O, true);
275 break;
280 static void printExpr(const MCExpr *Expr, raw_ostream &O) {
281 #ifndef NDEBUG
282 const MCSymbolRefExpr *SRE;
284 if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr))
285 SRE = cast<MCSymbolRefExpr>(BE->getLHS());
286 else
287 SRE = cast<MCSymbolRefExpr>(Expr);
289 MCSymbolRefExpr::VariantKind Kind = SRE->getKind();
291 assert(Kind == MCSymbolRefExpr::VK_None);
292 #endif
293 O << *Expr;
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);
301 if (Op.isReg())
302 O << '%' << (Register::virtReg2Index(Op.getReg()) + 1);
303 else if (Op.isImm())
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);
309 else
310 llvm_unreachable("Unexpected operand type");
314 void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
315 raw_ostream &O) {
316 const unsigned NumOps = MI->getNumOperands();
317 unsigned StrStartIndex = OpNo;
318 while (StrStartIndex < NumOps) {
319 if (MI->getOperand(StrStartIndex).isReg())
320 break;
322 std::string Str = getSPIRVStringOperand(*MI, OpNo);
323 if (StrStartIndex != OpNo)
324 O << ' '; // Add a space if we're starting a new string/argument.
325 O << '"';
326 for (char c : Str) {
327 if (c == '"')
328 O.write('\\'); // Escape " characters (might break for complex UTF-8).
329 O.write(c);
331 O << '"';
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)) {
340 O << ' ';
341 printSymbolicOperand<OperandCategory::LinkageTypeOperand>(
342 MI, StrStartIndex, O);
343 break;
348 void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,
349 raw_ostream &O) {
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,
358 raw_ostream &O) {
359 if (OpNo < MI->getNumOperands()) {
360 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());