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