LLVM 17.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"
19#include "llvm/ADT/SmallSet.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"
29using namespace llvm;
30
31#define DEBUG_TYPE "asm-printer"
32
33#include "WebAssemblyGenAsmWriter.inc"
34
36 const MCInstrInfo &MII,
37 const MCRegisterInfo &MRI)
38 : MCInstPrinter(MAI, MII, MRI) {}
39
41 MCRegister Reg) const {
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 if (CommentStream) {
113 // Observe any effects on the control flow stack, for use in annotating
114 // control flow label references.
115 unsigned Opc = MI->getOpcode();
116 switch (Opc) {
117 default:
118 break;
119
120 case WebAssembly::LOOP:
121 case WebAssembly::LOOP_S:
122 printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');
123 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));
124 return;
125
126 case WebAssembly::BLOCK:
127 case WebAssembly::BLOCK_S:
128 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
129 return;
130
131 case WebAssembly::TRY:
132 case WebAssembly::TRY_S:
133 ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));
134 TryStack.push_back(ControlFlowCounter++);
135 EHInstStack.push_back(TRY);
136 return;
137
138 case WebAssembly::END_LOOP:
139 case WebAssembly::END_LOOP_S:
140 if (ControlFlowStack.empty()) {
141 printAnnotation(OS, "End marker mismatch!");
142 } else {
143 ControlFlowStack.pop_back();
144 }
145 return;
146
147 case WebAssembly::END_BLOCK:
148 case WebAssembly::END_BLOCK_S:
149 if (ControlFlowStack.empty()) {
150 printAnnotation(OS, "End marker mismatch!");
151 } else {
153 OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
154 }
155 return;
156
157 case WebAssembly::END_TRY:
158 case WebAssembly::END_TRY_S:
159 if (ControlFlowStack.empty() || EHInstStack.empty()) {
160 printAnnotation(OS, "End marker mismatch!");
161 } else {
163 OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
164 EHInstStack.pop_back();
165 }
166 return;
167
168 case WebAssembly::CATCH:
169 case WebAssembly::CATCH_S:
170 case WebAssembly::CATCH_ALL:
171 case WebAssembly::CATCH_ALL_S:
172 // There can be multiple catch instructions for one try instruction, so
173 // we print a label only for the first 'catch' label.
174 if (EHInstStack.empty()) {
175 printAnnotation(OS, "try-catch mismatch!");
176 } else if (EHInstStack.back() == CATCH_ALL) {
177 printAnnotation(OS, "catch/catch_all cannot occur after catch_all");
178 } else if (EHInstStack.back() == TRY) {
179 if (TryStack.empty()) {
180 printAnnotation(OS, "try-catch mismatch!");
181 } else {
182 printAnnotation(OS, "catch" + utostr(TryStack.pop_back_val()) + ':');
183 }
184 EHInstStack.pop_back();
185 if (Opc == WebAssembly::CATCH || Opc == WebAssembly::CATCH_S) {
186 EHInstStack.push_back(CATCH);
187 } else {
188 EHInstStack.push_back(CATCH_ALL);
189 }
190 }
191 return;
192
193 case WebAssembly::RETHROW:
194 case WebAssembly::RETHROW_S:
195 // 'rethrow' rethrows to the nearest enclosing catch scope, if any. If
196 // there's no enclosing catch scope, it throws up to the caller.
197 if (TryStack.empty()) {
198 printAnnotation(OS, "to caller");
199 } else {
200 printAnnotation(OS, "down to catch" + utostr(TryStack.back()));
201 }
202 return;
203
204 case WebAssembly::DELEGATE:
205 case WebAssembly::DELEGATE_S:
206 if (ControlFlowStack.empty() || TryStack.empty() || EHInstStack.empty()) {
207 printAnnotation(OS, "try-delegate mismatch!");
208 } else {
209 // 'delegate' is
210 // 1. A marker for the end of block label
211 // 2. A destination for throwing instructions
212 // 3. An instruction that itself rethrows to another 'catch'
213 assert(ControlFlowStack.back().first == TryStack.back());
214 std::string Label = "label/catch" +
215 utostr(ControlFlowStack.pop_back_val().first) +
216 ": ";
217 TryStack.pop_back();
218 EHInstStack.pop_back();
219 uint64_t Depth = MI->getOperand(0).getImm();
220 if (Depth >= ControlFlowStack.size()) {
221 Label += "to caller";
222 } else {
223 const auto &Pair = ControlFlowStack.rbegin()[Depth];
224 if (Pair.second)
225 printAnnotation(OS, "delegate cannot target a loop");
226 else
227 Label += "down to catch" + utostr(Pair.first);
228 }
229 printAnnotation(OS, Label);
230 }
231 return;
232 }
233
234 // Annotate any control flow label references.
235
236 unsigned NumFixedOperands = Desc.NumOperands;
237 SmallSet<uint64_t, 8> Printed;
238 for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) {
239 // See if this operand denotes a basic block target.
240 if (I < NumFixedOperands) {
241 // A non-variable_ops operand, check its type.
242 if (Desc.operands()[I].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
243 continue;
244 } else {
245 // A variable_ops operand, which currently can be immediates (used in
246 // br_table) which are basic block targets, or for call instructions
247 // when using -wasm-keep-registers (in which case they are registers,
248 // and should not be processed).
249 if (!MI->getOperand(I).isImm())
250 continue;
251 }
252 uint64_t Depth = MI->getOperand(I).getImm();
253 if (!Printed.insert(Depth).second)
254 continue;
255 if (Depth >= ControlFlowStack.size()) {
256 printAnnotation(OS, "Invalid depth argument!");
257 } else {
258 const auto &Pair = ControlFlowStack.rbegin()[Depth];
259 printAnnotation(OS, utostr(Depth) + ": " +
260 (Pair.second ? "up" : "down") + " to label" +
261 utostr(Pair.first));
262 }
263 }
264 }
265}
266
267static std::string toString(const APFloat &FP) {
268 // Print NaNs with custom payloads specially.
269 if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
270 !FP.bitwiseIsEqual(
271 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
272 APInt AI = FP.bitcastToAPInt();
273 return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
274 utohexstr(AI.getZExtValue() &
275 (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
276 : INT64_C(0x000fffffffffffff)),
277 /*LowerCase=*/true);
278 }
279
280 // Use C99's hexadecimal floating-point representation.
281 static const size_t BufBytes = 128;
282 char Buf[BufBytes];
283 auto Written = FP.convertToHexString(
284 Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven);
285 (void)Written;
286 assert(Written != 0);
287 assert(Written < BufBytes);
288 return Buf;
289}
290
292 raw_ostream &O, bool IsVariadicDef) {
293 const MCOperand &Op = MI->getOperand(OpNo);
294 if (Op.isReg()) {
295 const MCInstrDesc &Desc = MII.get(MI->getOpcode());
296 unsigned WAReg = Op.getReg();
297 if (int(WAReg) >= 0)
298 printRegName(O, WAReg);
299 else if (OpNo >= Desc.getNumDefs() && !IsVariadicDef)
300 O << "$pop" << WebAssemblyFunctionInfo::getWARegStackId(WAReg);
301 else if (WAReg != WebAssemblyFunctionInfo::UnusedReg)
302 O << "$push" << WebAssemblyFunctionInfo::getWARegStackId(WAReg);
303 else
304 O << "$drop";
305 // Add a '=' suffix if this is a def.
306 if (OpNo < MII.get(MI->getOpcode()).getNumDefs() || IsVariadicDef)
307 O << '=';
308 } else if (Op.isImm()) {
309 O << Op.getImm();
310 } else if (Op.isSFPImm()) {
311 O << ::toString(APFloat(APFloat::IEEEsingle(), APInt(32, Op.getSFPImm())));
312 } else if (Op.isDFPImm()) {
313 O << ::toString(APFloat(APFloat::IEEEdouble(), APInt(64, Op.getDFPImm())));
314 } else {
315 assert(Op.isExpr() && "unknown operand kind in printOperand");
316 // call_indirect instructions have a TYPEINDEX operand that we print
317 // as a signature here, such that the assembler can recover this
318 // information.
319 auto SRE = static_cast<const MCSymbolRefExpr *>(Op.getExpr());
320 if (SRE->getKind() == MCSymbolRefExpr::VK_WASM_TYPEINDEX) {
321 auto &Sym = static_cast<const MCSymbolWasm &>(SRE->getSymbol());
322 O << WebAssembly::signatureToString(Sym.getSignature());
323 } else {
324 Op.getExpr()->print(O, &MAI);
325 }
326 }
327}
328
330 raw_ostream &O) {
331 O << "{";
332 for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {
333 if (I != OpNo)
334 O << ", ";
335 O << MI->getOperand(I).getImm();
336 }
337 O << "}";
338}
339
341 unsigned OpNo,
342 raw_ostream &O) {
343 int64_t Imm = MI->getOperand(OpNo).getImm();
344 if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))
345 return;
346 O << ":p2align=" << Imm;
347}
348
350 unsigned OpNo,
351 raw_ostream &O) {
352 const MCOperand &Op = MI->getOperand(OpNo);
353 if (Op.isImm()) {
354 auto Imm = static_cast<unsigned>(Op.getImm());
355 if (Imm != wasm::WASM_TYPE_NORESULT)
357 } else {
358 auto Expr = cast<MCSymbolRefExpr>(Op.getExpr());
359 auto *Sym = cast<MCSymbolWasm>(&Expr->getSymbol());
360 if (Sym->getSignature()) {
361 O << WebAssembly::signatureToString(Sym->getSignature());
362 } else {
363 // Disassembler does not currently produce a signature
364 O << "unknown_type";
365 }
366 }
367}
unsigned const MachineRegisterInfo * MRI
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
Symbol * Sym
Definition: ELF_riscv.cpp:463
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 declares WebAssembly-specific per-machine-function information.
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:962
Class for arbitrary precision integers.
Definition: APInt.h:75
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:1443
bool isNegative() const
Determine sign of this APInt.
Definition: APInt.h:312
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:44
const MCInstrInfo & MII
Definition: MCInstPrinter.h:51
raw_ostream * CommentStream
A stream that comments can be emitted to if desired.
Definition: MCInstPrinter.h:49
void printAnnotation(raw_ostream &OS, StringRef Annot)
Utility function for printing annotations.
const MCAsmInfo & MAI
Definition: MCInstPrinter.h:50
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
unsigned getNumDefs() const
Return the number of MachineOperands that are register definitions.
Definition: MCInstrDesc.h:248
unsigned short NumOperands
Definition: MCInstrDesc.h:206
bool variadicOpsAreDefs() const
Return true if variadic operands of this instruction are definitions.
Definition: MCInstrDesc.h:418
bool isVariadic() const
Return true if this instruction can have a variable number of operands.
Definition: MCInstrDesc.h:261
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:24
Generic base class for all target subtargets.
Represent a reference to a symbol from inside an expression.
Definition: MCExpr.h:192
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:416
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
static unsigned getWARegStackId(unsigned Reg)
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)
@ OPERAND_BASIC_BLOCK
Basic block label in a branch construct.
std::string signatureToString(const wasm::WasmSignature *Sig)
const char * anyTypeToString(unsigned Type)
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:269
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
static const fltSemantics & IEEEsingle() LLVM_READNONE
Definition: APFloat.cpp:244
static constexpr roundingMode rmNearestTiesToEven
Definition: APFloat.h:225
static const fltSemantics & IEEEdouble() LLVM_READNONE
Definition: APFloat.cpp:245