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