LLVM 18.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"
17#include "llvm/MC/MCAsmInfo.h"
18#include "llvm/MC/MCExpr.h"
19#include "llvm/MC/MCInst.h"
20#include "llvm/MC/MCInstrInfo.h"
21#include "llvm/MC/MCSymbol.h"
25
26using namespace llvm;
27using namespace llvm::SPIRV;
28
29#define DEBUG_TYPE "asm-printer"
30
31// Include the auto-generated portion of the assembly writer.
32#include "SPIRVGenAsmWriter.inc"
33
35 unsigned StartIndex,
36 raw_ostream &O,
37 bool SkipFirstSpace,
38 bool SkipImmediates) {
39 const unsigned NumOps = MI->getNumOperands();
40 for (unsigned i = StartIndex; i < NumOps; ++i) {
41 if (!SkipImmediates || !MI->getOperand(i).isImm()) {
42 if (!SkipFirstSpace || i != StartIndex)
43 O << ' ';
44 printOperand(MI, i, O);
45 }
46 }
47}
48
50 unsigned StartIndex,
51 raw_ostream &O) {
52 O << ' ';
53 if (MI->getNumOperands() - StartIndex == 2) { // Handle 64 bit literals.
54 uint64_t Imm = MI->getOperand(StartIndex).getImm();
55 Imm |= (MI->getOperand(StartIndex + 1).getImm() << 32);
56 O << Imm;
57 } else {
58 printRemainingVariableOps(MI, StartIndex, O, true, false);
59 }
60}
61
62void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {
63 Register Reg = MI->getOperand(0).getReg();
64 auto Name = getSPIRVStringOperand(*MI, 1);
65 auto Set = getExtInstSetFromString(Name);
66 ExtInstSetIDs.insert({Reg, Set});
67}
68
70 StringRef Annot, const MCSubtargetInfo &STI,
71 raw_ostream &OS) {
72 const unsigned OpCode = MI->getOpcode();
74
75 if (OpCode == SPIRV::OpDecorate) {
77 } else if (OpCode == SPIRV::OpExtInstImport) {
78 recordOpExtInstImport(MI);
79 } else if (OpCode == SPIRV::OpExtInst) {
81 } else {
82 // Print any extra operands for variadic instructions.
83 const MCInstrDesc &MCDesc = MII.get(OpCode);
84 if (MCDesc.isVariadic()) {
85 const unsigned NumFixedOps = MCDesc.getNumOperands();
86 const unsigned LastFixedIndex = NumFixedOps - 1;
87 const int FirstVariableIndex = NumFixedOps;
88 if (NumFixedOps > 0 && MCDesc.operands()[LastFixedIndex].OperandType ==
90 // For instructions where a custom type (not reg or immediate) comes as
91 // the last operand before the variable_ops. This is usually a StringImm
92 // operand, but there are a few other cases.
93 switch (OpCode) {
94 case SPIRV::OpTypeImage:
95 OS << ' ';
96 printSymbolicOperand<OperandCategory::AccessQualifierOperand>(
97 MI, FirstVariableIndex, OS);
98 break;
99 case SPIRV::OpVariable:
100 OS << ' ';
101 printOperand(MI, FirstVariableIndex, OS);
102 break;
103 case SPIRV::OpEntryPoint: {
104 // Print the interface ID operands, skipping the name's string
105 // literal.
106 printRemainingVariableOps(MI, NumFixedOps, OS, false, true);
107 break;
108 }
109 case SPIRV::OpExecutionMode:
110 case SPIRV::OpExecutionModeId:
111 case SPIRV::OpLoopMerge: {
112 // Print any literals after the OPERAND_UNKNOWN argument normally.
113 printRemainingVariableOps(MI, NumFixedOps, OS);
114 break;
115 }
116 default:
117 break; // printStringImm has already been handled.
118 }
119 } else {
120 // For instructions with no fixed ops or a reg/immediate as the final
121 // fixed operand, we can usually print the rest with "printOperand", but
122 // check for a few cases with custom types first.
123 switch (OpCode) {
124 case SPIRV::OpLoad:
125 case SPIRV::OpStore:
126 OS << ' ';
127 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(
128 MI, FirstVariableIndex, OS);
129 printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
130 break;
131 case SPIRV::OpImageSampleImplicitLod:
132 case SPIRV::OpImageSampleDrefImplicitLod:
133 case SPIRV::OpImageSampleProjImplicitLod:
134 case SPIRV::OpImageSampleProjDrefImplicitLod:
135 case SPIRV::OpImageFetch:
136 case SPIRV::OpImageGather:
137 case SPIRV::OpImageDrefGather:
138 case SPIRV::OpImageRead:
139 case SPIRV::OpImageWrite:
140 case SPIRV::OpImageSparseSampleImplicitLod:
141 case SPIRV::OpImageSparseSampleDrefImplicitLod:
142 case SPIRV::OpImageSparseSampleProjImplicitLod:
143 case SPIRV::OpImageSparseSampleProjDrefImplicitLod:
144 case SPIRV::OpImageSparseFetch:
145 case SPIRV::OpImageSparseGather:
146 case SPIRV::OpImageSparseDrefGather:
147 case SPIRV::OpImageSparseRead:
148 case SPIRV::OpImageSampleFootprintNV:
149 OS << ' ';
150 printSymbolicOperand<OperandCategory::ImageOperandOperand>(
151 MI, FirstVariableIndex, OS);
152 printRemainingVariableOps(MI, NumFixedOps + 1, OS);
153 break;
154 case SPIRV::OpCopyMemory:
155 case SPIRV::OpCopyMemorySized: {
156 const unsigned NumOps = MI->getNumOperands();
157 for (unsigned i = NumFixedOps; i < NumOps; ++i) {
158 OS << ' ';
159 printSymbolicOperand<OperandCategory::MemoryOperandOperand>(MI, i,
160 OS);
161 if (MI->getOperand(i).getImm() & MemoryOperand::Aligned) {
162 assert(i + 1 < NumOps && "Missing alignment operand");
163 OS << ' ';
164 printOperand(MI, i + 1, OS);
165 i += 1;
166 }
167 }
168 break;
169 }
170 case SPIRV::OpConstantI:
171 case SPIRV::OpConstantF:
172 // The last fixed operand along with any variadic operands that follow
173 // are part of the variable value.
174 printOpConstantVarOps(MI, NumFixedOps - 1, OS);
175 break;
176 default:
177 printRemainingVariableOps(MI, NumFixedOps, OS);
178 break;
179 }
180 }
181 }
182 }
183
184 printAnnotation(OS, Annot);
185}
186
188 // The fixed operands have already been printed, so just need to decide what
189 // type of ExtInst operands to print based on the instruction set and number.
190 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
191 unsigned NumFixedOps = MCDesc.getNumOperands();
192 const auto NumOps = MI->getNumOperands();
193 if (NumOps == NumFixedOps)
194 return;
195
196 O << ' ';
197
198 // TODO: implement special printing for OpenCLExtInst::vstor*.
199 printRemainingVariableOps(MI, NumFixedOps, O, true);
200}
201
203 // The fixed operands have already been printed, so just need to decide what
204 // type of decoration operands to print based on the Decoration type.
205 const MCInstrDesc &MCDesc = MII.get(MI->getOpcode());
206 unsigned NumFixedOps = MCDesc.getNumOperands();
207
208 if (NumFixedOps != MI->getNumOperands()) {
209 auto DecOp = MI->getOperand(NumFixedOps - 1);
210 auto Dec = static_cast<Decoration::Decoration>(DecOp.getImm());
211
212 O << ' ';
213
214 switch (Dec) {
215 case Decoration::BuiltIn:
216 printSymbolicOperand<OperandCategory::BuiltInOperand>(MI, NumFixedOps, O);
217 break;
218 case Decoration::UniformId:
219 printSymbolicOperand<OperandCategory::ScopeOperand>(MI, NumFixedOps, O);
220 break;
221 case Decoration::FuncParamAttr:
222 printSymbolicOperand<OperandCategory::FunctionParameterAttributeOperand>(
223 MI, NumFixedOps, O);
224 break;
225 case Decoration::FPRoundingMode:
226 printSymbolicOperand<OperandCategory::FPRoundingModeOperand>(
227 MI, NumFixedOps, O);
228 break;
229 case Decoration::FPFastMathMode:
230 printSymbolicOperand<OperandCategory::FPFastMathModeOperand>(
231 MI, NumFixedOps, O);
232 break;
233 case Decoration::LinkageAttributes:
234 case Decoration::UserSemantic:
235 printStringImm(MI, NumFixedOps, O);
236 break;
237 default:
238 printRemainingVariableOps(MI, NumFixedOps, O, true);
239 break;
240 }
241 }
242}
243
244static void printExpr(const MCExpr *Expr, raw_ostream &O) {
245#ifndef NDEBUG
246 const MCSymbolRefExpr *SRE;
247
248 if (const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr))
249 SRE = cast<MCSymbolRefExpr>(BE->getLHS());
250 else
251 SRE = cast<MCSymbolRefExpr>(Expr);
252
254
256#endif
257 O << *Expr;
258}
259
260void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
261 raw_ostream &O, const char *Modifier) {
262 assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported");
263 if (OpNo < MI->getNumOperands()) {
264 const MCOperand &Op = MI->getOperand(OpNo);
265 if (Op.isReg())
266 O << '%' << (Register::virtReg2Index(Op.getReg()) + 1);
267 else if (Op.isImm())
268 O << formatImm((int64_t)Op.getImm());
269 else if (Op.isDFPImm())
270 O << formatImm((double)Op.getDFPImm());
271 else if (Op.isExpr())
272 printExpr(Op.getExpr(), O);
273 else
274 llvm_unreachable("Unexpected operand type");
275 }
276}
277
278void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
279 raw_ostream &O) {
280 const unsigned NumOps = MI->getNumOperands();
281 unsigned StrStartIndex = OpNo;
282 while (StrStartIndex < NumOps) {
283 if (MI->getOperand(StrStartIndex).isReg())
284 break;
285
286 std::string Str = getSPIRVStringOperand(*MI, OpNo);
287 if (StrStartIndex != OpNo)
288 O << ' '; // Add a space if we're starting a new string/argument.
289 O << '"';
290 for (char c : Str) {
291 if (c == '"')
292 O.write('\\'); // Escape " characters (might break for complex UTF-8).
293 O.write(c);
294 }
295 O << '"';
296
297 unsigned numOpsInString = (Str.size() / 4) + 1;
298 StrStartIndex += numOpsInString;
299
300 // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
301 if (MI->getOpcode() == SPIRV::OpDecorate &&
302 MI->getOperand(1).getImm() ==
303 static_cast<unsigned>(Decoration::LinkageAttributes)) {
304 O << ' ';
305 printSymbolicOperand<OperandCategory::LinkageTypeOperand>(
306 MI, StrStartIndex, O);
307 break;
308 }
309 }
310}
311
312void SPIRVInstPrinter::printExtension(const MCInst *MI, unsigned OpNo,
313 raw_ostream &O) {
314 auto SetReg = MI->getOperand(2).getReg();
315 auto Set = ExtInstSetIDs[SetReg];
316 auto Op = MI->getOperand(OpNo).getImm();
317 O << getExtInstName(Set, Op);
318}
319
320template <OperandCategory::OperandCategory category>
322 raw_ostream &O) {
323 if (OpNo < MI->getNumOperands()) {
324 O << getSymbolicOperandMnemonic(category, MI->getOperand(OpNo).getImm());
325 }
326}
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
This class represents an Operation in the Expression.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition: DenseMap.h:220
Binary assembler expressions.
Definition: MCExpr.h:484
Base class for the full range of assembler expressions which are needed for parsing.
Definition: MCExpr.h:35
const MCInstrInfo & MII
Definition: MCInstPrinter.h:52
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:184
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:36
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:404
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:50
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
Lowers a builtin funtion call using the provided DemangledCall skeleton and external instruction Set.
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)
std::string getSymbolicOperandMnemonic(SPIRV::OperandCategory::OperandCategory Category, int32_t Value)