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 "llvm/ADT/APFloat.h"
18#include "llvm/ADT/SmallSet.h"
20#include "llvm/MC/MCExpr.h"
21#include "llvm/MC/MCInst.h"
22#include "llvm/MC/MCInstrInfo.h"
24#include "llvm/MC/MCSymbol.h"
28using namespace llvm;
29
30#define DEBUG_TYPE "asm-printer"
31
32#include "WebAssemblyGenAsmWriter.inc"
33
35 const MCInstrInfo &MII,
36 const MCRegisterInfo &MRI)
37 : MCInstPrinter(MAI, MII, MRI) {}
38
40 assert(Reg.id() != WebAssembly::UnusedReg);
41 // Note that there's an implicit local.get/local.set here!
42 OS << "$" << Reg.id();
43}
44
46 StringRef Annot,
47 const MCSubtargetInfo &STI,
48 raw_ostream &OS) {
49 switch (MI->getOpcode()) {
50 case WebAssembly::CALL_INDIRECT_S:
51 case WebAssembly::RET_CALL_INDIRECT_S: {
52 // A special case for call_indirect (and ret_call_indirect), if the table
53 // operand is a symbol: the order of the type and table operands is inverted
54 // in the text format relative to the binary format. Otherwise if table the
55 // operand isn't a symbol, then we have an MVP compilation unit, and the
56 // table shouldn't appear in the output.
57 OS << "\t";
58 OS << getMnemonic(*MI).first;
59 OS << " ";
60
61 assert(MI->getNumOperands() == 2);
62 const unsigned TypeOperand = 0;
63 const unsigned TableOperand = 1;
64 if (MI->getOperand(TableOperand).isExpr()) {
65 printOperand(MI, TableOperand, OS);
66 OS << ", ";
67 } else {
68 assert(MI->getOperand(TableOperand).getImm() == 0);
69 }
70 printOperand(MI, TypeOperand, OS);
71 break;
72 }
73 default:
74 // Print the instruction (this uses the AsmStrings from the .td files).
76 break;
77 }
78
79 // Print any additional variadic operands.
80 const MCInstrDesc &Desc = MII.get(MI->getOpcode());
81 if (Desc.isVariadic()) {
82 if ((Desc.getNumOperands() == 0 && MI->getNumOperands() > 0) ||
83 Desc.variadicOpsAreDefs())
84 OS << "\t";
85 unsigned Start = Desc.getNumOperands();
86 unsigned NumVariadicDefs = 0;
87 if (Desc.variadicOpsAreDefs()) {
88 // The number of variadic defs is encoded in an immediate by MCInstLower
89 NumVariadicDefs = MI->getOperand(0).getImm();
90 Start = 1;
91 }
92 bool NeedsComma = Desc.getNumOperands() > 0 && !Desc.variadicOpsAreDefs();
93 for (auto I = Start, E = MI->getNumOperands(); I < E; ++I) {
94 if (MI->getOpcode() == WebAssembly::CALL_INDIRECT &&
95 I - Start == NumVariadicDefs) {
96 // Skip type and table arguments when printing for tests.
97 ++I;
98 continue;
99 }
100 if (NeedsComma)
101 OS << ", ";
102 printOperand(MI, I, OS, I - Start < NumVariadicDefs);
103 NeedsComma = true;
104 }
105 }
106
107 // Print any added annotation.
108 printAnnotation(OS, Annot);
109
110 auto PrintBranchAnnotation = [&](const MCOperand &Op,
111 SmallSet<uint64_t, 8> &Printed) {
112 uint64_t Depth = Op.getImm();
113 if (!Printed.insert(Depth).second)
114 return;
115 if (Depth >= ControlFlowStack.size()) {
116 printAnnotation(OS, "Invalid depth argument!");
117 } else {
118 const auto &Pair = ControlFlowStack.rbegin()[Depth];
119 printAnnotation(OS, utostr(Depth) + ": " + (Pair.second ? "up" : "down") +
120 " to label" + utostr(Pair.first));
121 }
122 };
123
124 if (CommentStream) {
125 // Observe any effects on the control flow stack, for use in annotating
126 // control flow label references.
127 unsigned Opc = MI->getOpcode();
128 switch (Opc) {
129 default:
130 break;
131
132 case WebAssembly::LOOP:
133 case WebAssembly::LOOP_S:
134 printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');
135 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));
136 return;
137
138 case WebAssembly::BLOCK:
139 case WebAssembly::BLOCK_S:
140 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
141 return;
142
143 case WebAssembly::TRY:
144 case WebAssembly::TRY_S:
145 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));
146 TryStack.push_back(ControlFlowCounter++);
147 EHInstStack.push_back(TRY);
148 return;
149
150 case WebAssembly::TRY_TABLE:
151 case WebAssembly::TRY_TABLE_S: {
152 SmallSet<uint64_t, 8> Printed;
153 unsigned OpIdx = 1;
154 const MCOperand &Op = MI->getOperand(OpIdx++);
155 unsigned NumCatches = Op.getImm();
156 for (unsigned I = 0; I < NumCatches; I++) {
157 int64_t CatchOpcode = MI->getOperand(OpIdx++).getImm();
158 if (CatchOpcode == wasm::WASM_OPCODE_CATCH ||
159 CatchOpcode == wasm::WASM_OPCODE_CATCH_REF)
160 OpIdx++; // Skip tag
161 PrintBranchAnnotation(MI->getOperand(OpIdx++), Printed);
162 }
163 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
164 return;
165 }
166
167 case WebAssembly::END_LOOP:
168 case WebAssembly::END_LOOP_S:
169 if (ControlFlowStack.empty()) {
170 printAnnotation(OS, "End marker mismatch!");
171 } else {
172 ControlFlowStack.pop_back();
173 }
174 return;
175
176 case WebAssembly::END_BLOCK:
177 case WebAssembly::END_BLOCK_S:
178 case WebAssembly::END_TRY_TABLE:
179 case WebAssembly::END_TRY_TABLE_S:
180 if (ControlFlowStack.empty()) {
181 printAnnotation(OS, "End marker mismatch!");
182 } else {
184 OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
185 }
186 return;
187
188 case WebAssembly::END_TRY:
189 case WebAssembly::END_TRY_S:
190 if (ControlFlowStack.empty() || EHInstStack.empty()) {
191 printAnnotation(OS, "End marker mismatch!");
192 } else {
194 OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
195 EHInstStack.pop_back();
196 }
197 return;
198
199 case WebAssembly::CATCH_LEGACY:
200 case WebAssembly::CATCH_LEGACY_S:
201 case WebAssembly::CATCH_ALL_LEGACY:
202 case WebAssembly::CATCH_ALL_LEGACY_S:
203 // There can be multiple catch instructions for one try instruction, so
204 // we print a label only for the first 'catch' label.
205 if (EHInstStack.empty()) {
206 printAnnotation(OS, "try-catch mismatch!");
207 } else if (EHInstStack.back() == CATCH_ALL_LEGACY) {
208 printAnnotation(OS, "catch/catch_all cannot occur after catch_all");
209 } else if (EHInstStack.back() == TRY) {
210 if (TryStack.empty()) {
211 printAnnotation(OS, "try-catch mismatch!");
212 } else {
213 printAnnotation(OS, "catch" + utostr(TryStack.pop_back_val()) + ':');
214 }
215 EHInstStack.pop_back();
216 if (Opc == WebAssembly::CATCH_LEGACY ||
217 Opc == WebAssembly::CATCH_LEGACY_S) {
218 EHInstStack.push_back(CATCH_LEGACY);
219 } else {
220 EHInstStack.push_back(CATCH_ALL_LEGACY);
221 }
222 }
223 return;
224
225 case WebAssembly::RETHROW:
226 case WebAssembly::RETHROW_S:
227 // 'rethrow' rethrows to the nearest enclosing catch scope, if any. If
228 // there's no enclosing catch scope, it throws up to the caller.
229 if (TryStack.empty()) {
230 printAnnotation(OS, "to caller");
231 } else {
232 printAnnotation(OS, "down to catch" + utostr(TryStack.back()));
233 }
234 return;
235
236 case WebAssembly::DELEGATE:
237 case WebAssembly::DELEGATE_S:
238 if (ControlFlowStack.empty() || TryStack.empty() || EHInstStack.empty()) {
239 printAnnotation(OS, "try-delegate mismatch!");
240 } else {
241 // 'delegate' is
242 // 1. A marker for the end of block label
243 // 2. A destination for throwing instructions
244 // 3. An instruction that itself rethrows to another 'catch'
245 assert(ControlFlowStack.back().first == TryStack.back());
246 std::string Label = "label/catch" +
247 utostr(ControlFlowStack.pop_back_val().first) +
248 ": ";
249 TryStack.pop_back();
250 EHInstStack.pop_back();
251 uint64_t Depth = MI->getOperand(0).getImm();
252 if (Depth >= ControlFlowStack.size()) {
253 Label += "to caller";
254 } else {
255 const auto &Pair = ControlFlowStack.rbegin()[Depth];
256 if (Pair.second)
257 printAnnotation(OS, "delegate cannot target a loop");
258 else
259 Label += "down to catch" + utostr(Pair.first);
260 }
261 printAnnotation(OS, Label);
262 }
263 return;
264 }
265
266 // Annotate any control flow label references.
267
268 unsigned NumFixedOperands = Desc.NumOperands;
269 SmallSet<uint64_t, 8> Printed;
270 for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) {
271 // See if this operand denotes a basic block target.
272 if (I < NumFixedOperands) {
273 // A non-variable_ops operand, check its type.
274 if (Desc.operands()[I].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
275 continue;
276 } else {
277 // A variable_ops operand, which currently can be immediates (used in
278 // br_table) which are basic block targets, or for call instructions
279 // when using -wasm-keep-registers (in which case they are registers,
280 // and should not be processed).
281 if (!MI->getOperand(I).isImm())
282 continue;
283 }
284 PrintBranchAnnotation(MI->getOperand(I), Printed);
285 }
286 }
287}
288
289static std::string toString(const APFloat &FP) {
290 // Print NaNs with custom payloads specially.
291 if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
292 !FP.bitwiseIsEqual(
293 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
294 APInt AI = FP.bitcastToAPInt();
295 return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
296 utohexstr(AI.getZExtValue() &
297 (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
298 : INT64_C(0x000fffffffffffff)),
299 /*LowerCase=*/true);
300 }
301
302 // Use C99's hexadecimal floating-point representation.
303 static const size_t BufBytes = 128;
304 char Buf[BufBytes];
305 auto Written = FP.convertToHexString(
306 Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven);
307 (void)Written;
308 assert(Written != 0);
309 assert(Written < BufBytes);
310 return Buf;
311}
312
314 raw_ostream &O, bool IsVariadicDef) {
315 const MCOperand &Op = MI->getOperand(OpNo);
316 if (Op.isReg()) {
317 const MCInstrDesc &Desc = MII.get(MI->getOpcode());
318 unsigned WAReg = Op.getReg();
319 if (int(WAReg) >= 0)
320 printRegName(O, WAReg);
321 else if (OpNo >= Desc.getNumDefs() && !IsVariadicDef)
322 O << "$pop" << WebAssembly::getWARegStackId(WAReg);
323 else if (WAReg != WebAssembly::UnusedReg)
324 O << "$push" << WebAssembly::getWARegStackId(WAReg);
325 else
326 O << "$drop";
327 // Add a '=' suffix if this is a def.
328 if (OpNo < MII.get(MI->getOpcode()).getNumDefs() || IsVariadicDef)
329 O << '=';
330 } else if (Op.isImm()) {
331 O << Op.getImm();
332 } else if (Op.isSFPImm()) {
333 O << ::toString(APFloat(APFloat::IEEEsingle(), APInt(32, Op.getSFPImm())));
334 } else if (Op.isDFPImm()) {
335 O << ::toString(APFloat(APFloat::IEEEdouble(), APInt(64, Op.getDFPImm())));
336 } else {
337 assert(Op.isExpr() && "unknown operand kind in printOperand");
338 // call_indirect instructions have a TYPEINDEX operand that we print
339 // as a signature here, such that the assembler can recover this
340 // information.
341 auto SRE = static_cast<const MCSymbolRefExpr *>(Op.getExpr());
342 if (SRE->getKind() == MCSymbolRefExpr::VK_WASM_TYPEINDEX) {
343 auto &Sym = static_cast<const MCSymbolWasm &>(SRE->getSymbol());
344 O << WebAssembly::signatureToString(Sym.getSignature());
345 } else {
346 Op.getExpr()->print(O, &MAI);
347 }
348 }
349}
350
352 raw_ostream &O) {
353 O << "{";
354 for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {
355 if (I != OpNo)
356 O << ", ";
357 O << MI->getOperand(I).getImm();
358 }
359 O << "}";
360}
361
363 unsigned OpNo,
364 raw_ostream &O) {
365 int64_t Imm = MI->getOperand(OpNo).getImm();
366 if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))
367 return;
368 O << ":p2align=" << Imm;
369}
370
372 unsigned OpNo,
373 raw_ostream &O) {
374 const MCOperand &Op = MI->getOperand(OpNo);
375 if (Op.isImm()) {
376 auto Imm = static_cast<unsigned>(Op.getImm());
377 if (Imm != wasm::WASM_TYPE_NORESULT)
379 } else {
380 auto Expr = cast<MCSymbolRefExpr>(Op.getExpr());
381 auto *Sym = cast<MCSymbolWasm>(&Expr->getSymbol());
382 if (Sym->getSignature()) {
383 O << WebAssembly::signatureToString(Sym->getSignature());
384 } else {
385 // Disassembler does not currently produce a signature
386 O << "unknown_type";
387 }
388 }
389}
390
392 raw_ostream &O) {
393 unsigned OpIdx = OpNo;
394 const MCOperand &Op = MI->getOperand(OpIdx++);
395 unsigned NumCatches = Op.getImm();
396
397 auto PrintTagOp = [&](const MCOperand &Op) {
398 const MCSymbolRefExpr *TagExpr = nullptr;
399 const MCSymbolWasm *TagSym = nullptr;
400 if (Op.isExpr()) {
401 TagExpr = cast<MCSymbolRefExpr>(Op.getExpr());
402 TagSym = cast<MCSymbolWasm>(&TagExpr->getSymbol());
403 O << TagSym->getName() << " ";
404 } else {
405 // When instructions are parsed from the disassembler, we have an
406 // immediate tag index and not a tag expr
407 O << Op.getImm() << " ";
408 }
409 };
410
411 for (unsigned I = 0; I < NumCatches; I++) {
412 const MCOperand &Op = MI->getOperand(OpIdx++);
413 O << "(";
414 switch (Op.getImm()) {
416 O << "catch ";
417 PrintTagOp(MI->getOperand(OpIdx++));
418 break;
420 O << "catch_ref ";
421 PrintTagOp(MI->getOperand(OpIdx++));
422 break;
424 O << "catch_all ";
425 break;
427 O << "catch_all_ref ";
428 break;
429 }
430 O << MI->getOperand(OpIdx++).getImm(); // destination
431 O << ")";
432 if (I < NumCatches - 1)
433 O << " ";
434 }
435}
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.
static APFloat getQNaN(const fltSemantics &Sem, bool Negative=false, const APInt *payload=nullptr)
Factory for QNaN values.
Definition: APFloat.h:1117
Class for arbitrary precision integers.
Definition: APInt.h:78
uint64_t getZExtValue() const
Get zero extended value.
Definition: APInt.h:1520
unsigned getBitWidth() const
Return the number of bits in the APInt.
Definition: APInt.h:1468
bool isNegative() const
Determine sign of this APInt.
Definition: APInt.h:329
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:46
const MCInstrInfo & MII
Definition: MCInstPrinter.h:53
raw_ostream * CommentStream
A stream that comments can be emitted to if desired.
Definition: MCInstPrinter.h:51
void printAnnotation(raw_ostream &OS, StringRef Annot)
Utility function for printing annotations.
const MCAsmInfo & MAI
Definition: MCInstPrinter.h:52
Instances of this class represent a single low-level machine instruction.
Definition: MCInst.h:185
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:37
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:192
const MCSymbol & getSymbol() const
Definition: MCExpr.h:411
StringRef getName() const
getName - Get the symbol name.
Definition: MCSymbol.h:205
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:132
bool empty() const
Definition: SmallVector.h:81
size_t size() const
Definition: SmallVector.h:78
void push_back(const T &Elt)
Definition: SmallVector.h:413
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
void printWebAssemblySignatureOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O)
void printRegName(raw_ostream &OS, MCRegister Reg) 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) const 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 printCatchList(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)
@ WASM_TYPE_NORESULT
Definition: Wasm.h:79
@ WASM_OPCODE_CATCH_ALL_REF
Definition: Wasm.h:152
@ WASM_OPCODE_CATCH
Definition: Wasm.h:149
@ WASM_OPCODE_CATCH_ALL
Definition: Wasm.h:151
@ WASM_OPCODE_CATCH_REF
Definition: Wasm.h:150
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
DWARFExpression::Operation Op
const char * toString(DWARFSectionKind Kind)
static const fltSemantics & IEEEsingle() LLVM_READNONE
Definition: APFloat.cpp:265
static constexpr roundingMode rmNearestTiesToEven
Definition: APFloat.h:297
static const fltSemantics & IEEEdouble() LLVM_READNONE
Definition: APFloat.cpp:266
Description of the encoding of one expression Op.