LLVM 20.0.0git
WebAssemblyInstPrinter.cpp
Go to the documentation of this file.
1//=- WebAssemblyInstPrinter.cpp - WebAssembly assembly instruction printing -=//
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/// \file
10/// Print MCInst instructions to wasm format.
11///
12//===----------------------------------------------------------------------===//
13
17#include "WebAssembly.h"
18#include "llvm/ADT/APFloat.h"
19#include "llvm/ADT/SmallSet.h"
21#include "llvm/MC/MCExpr.h"
22#include "llvm/MC/MCInst.h"
23#include "llvm/MC/MCInstrInfo.h"
25#include "llvm/MC/MCSymbol.h"
30using namespace llvm;
31
32#define DEBUG_TYPE "asm-printer"
33
34#include "WebAssemblyGenAsmWriter.inc"
35
37 const MCInstrInfo &MII,
38 const MCRegisterInfo &MRI)
39 : MCInstPrinter(MAI, MII, MRI) {}
40
42 MCRegister Reg) const {
43 assert(Reg.id() != WebAssembly::UnusedReg);
44 // Note that there's an implicit local.get/local.set here!
45 OS << "$" << Reg.id();
46}
47
49 StringRef Annot,
50 const MCSubtargetInfo &STI,
51 raw_ostream &OS) {
52 switch (MI->getOpcode()) {
53 case WebAssembly::CALL_INDIRECT_S:
54 case WebAssembly::RET_CALL_INDIRECT_S: {
55 // A special case for call_indirect (and ret_call_indirect), if the table
56 // operand is a symbol: the order of the type and table operands is inverted
57 // in the text format relative to the binary format. Otherwise if table the
58 // operand isn't a symbol, then we have an MVP compilation unit, and the
59 // table shouldn't appear in the output.
60 OS << "\t";
61 OS << getMnemonic(MI).first;
62 OS << " ";
63
64 assert(MI->getNumOperands() == 2);
65 const unsigned TypeOperand = 0;
66 const unsigned TableOperand = 1;
67 if (MI->getOperand(TableOperand).isExpr()) {
68 printOperand(MI, TableOperand, OS);
69 OS << ", ";
70 } else {
71 assert(MI->getOperand(TableOperand).getImm() == 0);
72 }
73 printOperand(MI, TypeOperand, OS);
74 break;
75 }
76 default:
77 // Print the instruction (this uses the AsmStrings from the .td files).
79 break;
80 }
81
82 // Print any additional variadic operands.
83 const MCInstrDesc &Desc = MII.get(MI->getOpcode());
84 if (Desc.isVariadic()) {
85 if ((Desc.getNumOperands() == 0 && MI->getNumOperands() > 0) ||
86 Desc.variadicOpsAreDefs())
87 OS << "\t";
88 unsigned Start = Desc.getNumOperands();
89 unsigned NumVariadicDefs = 0;
90 if (Desc.variadicOpsAreDefs()) {
91 // The number of variadic defs is encoded in an immediate by MCInstLower
92 NumVariadicDefs = MI->getOperand(0).getImm();
93 Start = 1;
94 }
95 bool NeedsComma = Desc.getNumOperands() > 0 && !Desc.variadicOpsAreDefs();
96 for (auto I = Start, E = MI->getNumOperands(); I < E; ++I) {
97 if (MI->getOpcode() == WebAssembly::CALL_INDIRECT &&
98 I - Start == NumVariadicDefs) {
99 // Skip type and table arguments when printing for tests.
100 ++I;
101 continue;
102 }
103 if (NeedsComma)
104 OS << ", ";
105 printOperand(MI, I, OS, I - Start < NumVariadicDefs);
106 NeedsComma = true;
107 }
108 }
109
110 // Print any added annotation.
111 printAnnotation(OS, Annot);
112
113 if (CommentStream) {
114 // Observe any effects on the control flow stack, for use in annotating
115 // control flow label references.
116 unsigned Opc = MI->getOpcode();
117 switch (Opc) {
118 default:
119 break;
120
121 case WebAssembly::LOOP:
122 case WebAssembly::LOOP_S:
123 printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');
124 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));
125 return;
126
127 case WebAssembly::BLOCK:
128 case WebAssembly::BLOCK_S:
129 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
130 return;
131
132 case WebAssembly::TRY:
133 case WebAssembly::TRY_S:
134 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));
135 TryStack.push_back(ControlFlowCounter++);
136 EHInstStack.push_back(TRY);
137 return;
138
139 case WebAssembly::END_LOOP:
140 case WebAssembly::END_LOOP_S:
141 if (ControlFlowStack.empty()) {
142 printAnnotation(OS, "End marker mismatch!");
143 } else {
144 ControlFlowStack.pop_back();
145 }
146 return;
147
148 case WebAssembly::END_BLOCK:
149 case WebAssembly::END_BLOCK_S:
150 if (ControlFlowStack.empty()) {
151 printAnnotation(OS, "End marker mismatch!");
152 } else {
154 OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
155 }
156 return;
157
158 case WebAssembly::END_TRY:
159 case WebAssembly::END_TRY_S:
160 if (ControlFlowStack.empty() || EHInstStack.empty()) {
161 printAnnotation(OS, "End marker mismatch!");
162 } else {
164 OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
165 EHInstStack.pop_back();
166 }
167 return;
168
169 case WebAssembly::CATCH:
170 case WebAssembly::CATCH_S:
171 case WebAssembly::CATCH_ALL:
172 case WebAssembly::CATCH_ALL_S:
173 // There can be multiple catch instructions for one try instruction, so
174 // we print a label only for the first 'catch' label.
175 if (EHInstStack.empty()) {
176 printAnnotation(OS, "try-catch mismatch!");
177 } else if (EHInstStack.back() == CATCH_ALL) {
178 printAnnotation(OS, "catch/catch_all cannot occur after catch_all");
179 } else if (EHInstStack.back() == TRY) {
180 if (TryStack.empty()) {
181 printAnnotation(OS, "try-catch mismatch!");
182 } else {
183 printAnnotation(OS, "catch" + utostr(TryStack.pop_back_val()) + ':');
184 }
185 EHInstStack.pop_back();
186 if (Opc == WebAssembly::CATCH || Opc == WebAssembly::CATCH_S) {
187 EHInstStack.push_back(CATCH);
188 } else {
189 EHInstStack.push_back(CATCH_ALL);
190 }
191 }
192 return;
193
194 case WebAssembly::RETHROW:
195 case WebAssembly::RETHROW_S:
196 // 'rethrow' rethrows to the nearest enclosing catch scope, if any. If
197 // there's no enclosing catch scope, it throws up to the caller.
198 if (TryStack.empty()) {
199 printAnnotation(OS, "to caller");
200 } else {
201 printAnnotation(OS, "down to catch" + utostr(TryStack.back()));
202 }
203 return;
204
205 case WebAssembly::DELEGATE:
206 case WebAssembly::DELEGATE_S:
207 if (ControlFlowStack.empty() || TryStack.empty() || EHInstStack.empty()) {
208 printAnnotation(OS, "try-delegate mismatch!");
209 } else {
210 // 'delegate' is
211 // 1. A marker for the end of block label
212 // 2. A destination for throwing instructions
213 // 3. An instruction that itself rethrows to another 'catch'
214 assert(ControlFlowStack.back().first == TryStack.back());
215 std::string Label = "label/catch" +
216 utostr(ControlFlowStack.pop_back_val().first) +
217 ": ";
218 TryStack.pop_back();
219 EHInstStack.pop_back();
220 uint64_t Depth = MI->getOperand(0).getImm();
221 if (Depth >= ControlFlowStack.size()) {
222 Label += "to caller";
223 } else {
224 const auto &Pair = ControlFlowStack.rbegin()[Depth];
225 if (Pair.second)
226 printAnnotation(OS, "delegate cannot target a loop");
227 else
228 Label += "down to catch" + utostr(Pair.first);
229 }
230 printAnnotation(OS, Label);
231 }
232 return;
233 }
234
235 // Annotate any control flow label references.
236
237 unsigned NumFixedOperands = Desc.NumOperands;
238 SmallSet<uint64_t, 8> Printed;
239 for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) {
240 // See if this operand denotes a basic block target.
241 if (I < NumFixedOperands) {
242 // A non-variable_ops operand, check its type.
243 if (Desc.operands()[I].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
244 continue;
245 } else {
246 // A variable_ops operand, which currently can be immediates (used in
247 // br_table) which are basic block targets, or for call instructions
248 // when using -wasm-keep-registers (in which case they are registers,
249 // and should not be processed).
250 if (!MI->getOperand(I).isImm())
251 continue;
252 }
253 uint64_t Depth = MI->getOperand(I).getImm();
254 if (!Printed.insert(Depth).second)
255 continue;
256 if (Depth >= ControlFlowStack.size()) {
257 printAnnotation(OS, "Invalid depth argument!");
258 } else {
259 const auto &Pair = ControlFlowStack.rbegin()[Depth];
260 printAnnotation(OS, utostr(Depth) + ": " +
261 (Pair.second ? "up" : "down") + " to label" +
262 utostr(Pair.first));
263 }
264 }
265 }
266}
267
268static std::string toString(const APFloat &FP) {
269 // Print NaNs with custom payloads specially.
270 if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
271 !FP.bitwiseIsEqual(
272 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
273 APInt AI = FP.bitcastToAPInt();
274 return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
275 utohexstr(AI.getZExtValue() &
276 (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
277 : INT64_C(0x000fffffffffffff)),
278 /*LowerCase=*/true);
279 }
280
281 // Use C99's hexadecimal floating-point representation.
282 static const size_t BufBytes = 128;
283 char Buf[BufBytes];
284 auto Written = FP.convertToHexString(
285 Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven);
286 (void)Written;
287 assert(Written != 0);
288 assert(Written < BufBytes);
289 return Buf;
290}
291
293 raw_ostream &O, bool IsVariadicDef) {
294 const MCOperand &Op = MI->getOperand(OpNo);
295 if (Op.isReg()) {
296 const MCInstrDesc &Desc = MII.get(MI->getOpcode());
297 unsigned WAReg = Op.getReg();
298 if (int(WAReg) >= 0)
299 printRegName(O, WAReg);
300 else if (OpNo >= Desc.getNumDefs() && !IsVariadicDef)
301 O << "$pop" << WebAssembly::getWARegStackId(WAReg);
302 else if (WAReg != WebAssembly::UnusedReg)
303 O << "$push" << WebAssembly::getWARegStackId(WAReg);
304 else
305 O << "$drop";
306 // Add a '=' suffix if this is a def.
307 if (OpNo < MII.get(MI->getOpcode()).getNumDefs() || IsVariadicDef)
308 O << '=';
309 } else if (Op.isImm()) {
310 O << Op.getImm();
311 } else if (Op.isSFPImm()) {
312 O << ::toString(APFloat(APFloat::IEEEsingle(), APInt(32, Op.getSFPImm())));
313 } else if (Op.isDFPImm()) {
314 O << ::toString(APFloat(APFloat::IEEEdouble(), APInt(64, Op.getDFPImm())));
315 } else {
316 assert(Op.isExpr() && "unknown operand kind in printOperand");
317 // call_indirect instructions have a TYPEINDEX operand that we print
318 // as a signature here, such that the assembler can recover this
319 // information.
320 auto SRE = static_cast<const MCSymbolRefExpr *>(Op.getExpr());
321 if (SRE->getKind() == MCSymbolRefExpr::VK_WASM_TYPEINDEX) {
322 auto &Sym = static_cast<const MCSymbolWasm &>(SRE->getSymbol());
323 O << WebAssembly::signatureToString(Sym.getSignature());
324 } else {
325 Op.getExpr()->print(O, &MAI);
326 }
327 }
328}
329
331 raw_ostream &O) {
332 O << "{";
333 for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {
334 if (I != OpNo)
335 O << ", ";
336 O << MI->getOperand(I).getImm();
337 }
338 O << "}";
339}
340
342 unsigned OpNo,
343 raw_ostream &O) {
344 int64_t Imm = MI->getOperand(OpNo).getImm();
345 if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))
346 return;
347 O << ":p2align=" << Imm;
348}
349
351 unsigned OpNo,
352 raw_ostream &O) {
353 const MCOperand &Op = MI->getOperand(OpNo);
354 if (Op.isImm()) {
355 auto Imm = static_cast<unsigned>(Op.getImm());
356 if (Imm != wasm::WASM_TYPE_NORESULT)
358 } else {
359 auto Expr = cast<MCSymbolRefExpr>(Op.getExpr());
360 auto *Sym = cast<MCSymbolWasm>(&Expr->getSymbol());
361 if (Sym->getSignature()) {
362 O << WebAssembly::signatureToString(Sym->getSignature());
363 } else {
364 // Disassembler does not currently produce a signature
365 O << "unknown_type";
366 }
367 }
368}
unsigned const MachineRegisterInfo * MRI
This file declares a class to represent arbitrary precision floating point values and provide a varie...
Symbol * Sym
Definition: ELF_riscv.cpp:479
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition: MD5.cpp:58
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
This file defines the SmallSet class.
This file contains some functions that are useful when dealing with strings.
This class prints an WebAssembly MCInst to wasm file syntax.
This file provides WebAssembly-specific target descriptions.
This file contains the declaration of the WebAssembly-specific type parsing utility functions.
This file contains the entry points for global functions defined in the LLVM WebAssembly back-end.
static APFloat getQNaN(const fltSemantics &Sem, bool Negative=false, const APInt *payload=nullptr)
Factory for QNaN values.
Definition: APFloat.h:1032
Class for arbitrary precision integers.
Definition: APInt.h:78
uint64_t getZExtValue() const
Get zero extended value.
Definition: APInt.h:1498
unsigned getBitWidth() const
Return the number of bits in the APInt.
Definition: APInt.h:1446
bool isNegative() const
Determine sign of this APInt.
Definition: APInt.h:307
This class represents an Operation in the Expression.
bool print(raw_ostream &OS, DIDumpOptions DumpOpts, const DWARFExpression *Expr, DWARFUnit *U) const
This class is intended to be used as a base class for asm properties and features specific to the tar...
Definition: MCAsmInfo.h:56
This is an instance of a target assembly language printer that converts an MCInst to valid target ass...
Definition: MCInstPrinter.h:45
const MCInstrInfo & MII
Definition: MCInstPrinter.h:52
raw_ostream * CommentStream
A stream that comments can be emitted to if desired.
Definition: MCInstPrinter.h:50
void printAnnotation(raw_ostream &OS, StringRef Annot)
Utility function for printing annotations.
const MCAsmInfo & MAI
Definition: MCInstPrinter.h:51
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 getNumDefs() const
Return the number of MachineOperands that are register definitions.
Definition: MCInstrDesc.h:248
Interface to description of machine instruction set.
Definition: MCInstrInfo.h:26
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
MCRegisterInfo base class - We assume that the target defines a static array of MCRegisterDesc object...
Wrapper class representing physical registers. Should be passed by value.
Definition: MCRegister.h:33
Generic base class for all target subtargets.
Represent a reference to a symbol from inside an expression.
Definition: MCExpr.h:188
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:135
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition: SmallSet.h:179
bool empty() const
Definition: SmallVector.h:94
size_t size() const
Definition: SmallVector.h:91
void push_back(const T &Elt)
Definition: SmallVector.h:426
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
void printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printRegName(raw_ostream &OS, MCRegister Reg) const override
Print the assembler register name.
void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O)
std::pair< const char *, uint64_t > getMnemonic(const MCInst *MI) override
Returns a pair containing the mnemonic for MI and the number of bits left for further processing by p...
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 printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O, bool IsVariadicDef=false)
void printBrList(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printWebAssemblyP2AlignOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O)
WebAssemblyInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, const MCRegisterInfo &MRI)
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
unsigned GetDefaultP2Align(unsigned Opc)
static const unsigned UnusedReg
@ OPERAND_BASIC_BLOCK
Basic block label in a branch construct.
std::string signatureToString(const wasm::WasmSignature *Sig)
const char * anyTypeToString(unsigned Type)
unsigned getWARegStackId(unsigned Reg)
std::optional< const char * > toString(const std::optional< DWARFFormValue > &V)
Take an optional DWARFFormValue and try to extract a string value from it.
@ WASM_TYPE_NORESULT
Definition: Wasm.h:79
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
static const fltSemantics & IEEEsingle() LLVM_READNONE
Definition: APFloat.cpp:281
static constexpr roundingMode rmNearestTiesToEven
Definition: APFloat.h:254
static const fltSemantics & IEEEdouble() LLVM_READNONE
Definition: APFloat.cpp:282
Description of the encoding of one expression Op.