1 //=- WebAssemblyInstPrinter.cpp - WebAssembly assembly instruction printing -=//
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
7 //===----------------------------------------------------------------------===//
10 /// Print MCInst instructions to wasm format.
12 //===----------------------------------------------------------------------===//
14 #include "MCTargetDesc/WebAssemblyInstPrinter.h"
15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16 #include "WebAssembly.h"
17 #include "WebAssemblyMachineFunctionInfo.h"
18 #include "WebAssemblyUtilities.h"
19 #include "llvm/ADT/SmallSet.h"
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/CodeGen/TargetRegisterInfo.h"
22 #include "llvm/MC/MCExpr.h"
23 #include "llvm/MC/MCInst.h"
24 #include "llvm/MC/MCInstrInfo.h"
25 #include "llvm/MC/MCSubtargetInfo.h"
26 #include "llvm/MC/MCSymbol.h"
27 #include "llvm/Support/ErrorHandling.h"
28 #include "llvm/Support/FormattedStream.h"
31 #define DEBUG_TYPE "asm-printer"
33 #include "WebAssemblyGenAsmWriter.inc"
35 WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo
&MAI
,
36 const MCInstrInfo
&MII
,
37 const MCRegisterInfo
&MRI
)
38 : MCInstPrinter(MAI
, MII
, MRI
) {}
40 void WebAssemblyInstPrinter::printRegName(raw_ostream
&OS
,
41 unsigned RegNo
) const {
42 assert(RegNo
!= WebAssemblyFunctionInfo::UnusedReg
);
43 // Note that there's an implicit local.get/local.set here!
47 void WebAssemblyInstPrinter::printInst(const MCInst
*MI
, raw_ostream
&OS
,
49 const MCSubtargetInfo
&STI
) {
50 // Print the instruction (this uses the AsmStrings from the .td files).
51 printInstruction(MI
, OS
);
53 // Print any additional variadic operands.
54 const MCInstrDesc
&Desc
= MII
.get(MI
->getOpcode());
55 if (Desc
.isVariadic()) {
56 if (Desc
.getNumOperands() == 0 && MI
->getNumOperands() > 0)
58 for (auto I
= Desc
.getNumOperands(), E
= MI
->getNumOperands(); I
< E
; ++I
) {
59 // FIXME: For CALL_INDIRECT_VOID, don't print a leading comma, because
60 // we have an extra flags operand which is not currently printed, for
61 // compatiblity reasons.
62 if (I
!= 0 && ((MI
->getOpcode() != WebAssembly::CALL_INDIRECT_VOID
&&
63 MI
->getOpcode() != WebAssembly::CALL_INDIRECT_VOID_S
) ||
64 I
!= Desc
.getNumOperands()))
66 printOperand(MI
, I
, OS
);
70 // Print any added annotation.
71 printAnnotation(OS
, Annot
);
74 // Observe any effects on the control flow stack, for use in annotating
75 // control flow label references.
76 unsigned Opc
= MI
->getOpcode();
81 case WebAssembly::LOOP
:
82 case WebAssembly::LOOP_S
:
83 printAnnotation(OS
, "label" + utostr(ControlFlowCounter
) + ':');
84 ControlFlowStack
.push_back(std::make_pair(ControlFlowCounter
++, true));
87 case WebAssembly::BLOCK
:
88 case WebAssembly::BLOCK_S
:
89 ControlFlowStack
.push_back(std::make_pair(ControlFlowCounter
++, false));
92 case WebAssembly::TRY
:
93 case WebAssembly::TRY_S
:
94 ControlFlowStack
.push_back(std::make_pair(ControlFlowCounter
++, false));
95 EHPadStack
.push_back(EHPadStackCounter
++);
99 case WebAssembly::END_LOOP
:
100 case WebAssembly::END_LOOP_S
:
101 if (ControlFlowStack
.empty()) {
102 printAnnotation(OS
, "End marker mismatch!");
104 ControlFlowStack
.pop_back();
108 case WebAssembly::END_BLOCK
:
109 case WebAssembly::END_BLOCK_S
:
110 if (ControlFlowStack
.empty()) {
111 printAnnotation(OS
, "End marker mismatch!");
114 OS
, "label" + utostr(ControlFlowStack
.pop_back_val().first
) + ':');
118 case WebAssembly::END_TRY
:
119 case WebAssembly::END_TRY_S
:
120 if (ControlFlowStack
.empty()) {
121 printAnnotation(OS
, "End marker mismatch!");
124 OS
, "label" + utostr(ControlFlowStack
.pop_back_val().first
) + ':');
125 LastSeenEHInst
= END_TRY
;
129 case WebAssembly::CATCH
:
130 case WebAssembly::CATCH_S
:
131 if (EHPadStack
.empty()) {
132 printAnnotation(OS
, "try-catch mismatch!");
134 printAnnotation(OS
, "catch" + utostr(EHPadStack
.pop_back_val()) + ':');
139 // Annotate any control flow label references.
141 // rethrow instruction does not take any depth argument and rethrows to the
142 // nearest enclosing catch scope, if any. If there's no enclosing catch
143 // scope, it throws up to the caller.
144 if (Opc
== WebAssembly::RETHROW
|| Opc
== WebAssembly::RETHROW_S
) {
145 if (EHPadStack
.empty()) {
146 printAnnotation(OS
, "to caller");
148 printAnnotation(OS
, "down to catch" + utostr(EHPadStack
.back()));
152 unsigned NumFixedOperands
= Desc
.NumOperands
;
153 SmallSet
<uint64_t, 8> Printed
;
154 for (unsigned I
= 0, E
= MI
->getNumOperands(); I
< E
; ++I
) {
155 // See if this operand denotes a basic block target.
156 if (I
< NumFixedOperands
) {
157 // A non-variable_ops operand, check its type.
158 if (Desc
.OpInfo
[I
].OperandType
!= WebAssembly::OPERAND_BASIC_BLOCK
)
161 // A variable_ops operand, which currently can be immediates (used in
162 // br_table) which are basic block targets, or for call instructions
163 // when using -wasm-keep-registers (in which case they are registers,
164 // and should not be processed).
165 if (!MI
->getOperand(I
).isImm())
168 uint64_t Depth
= MI
->getOperand(I
).getImm();
169 if (!Printed
.insert(Depth
).second
)
171 if (Depth
>= ControlFlowStack
.size()) {
172 printAnnotation(OS
, "Invalid depth argument!");
174 const auto &Pair
= ControlFlowStack
.rbegin()[Depth
];
175 printAnnotation(OS
, utostr(Depth
) + ": " +
176 (Pair
.second
? "up" : "down") + " to label" +
184 static std::string
toString(const APFloat
&FP
) {
185 // Print NaNs with custom payloads specially.
186 if (FP
.isNaN() && !FP
.bitwiseIsEqual(APFloat::getQNaN(FP
.getSemantics())) &&
188 APFloat::getQNaN(FP
.getSemantics(), /*Negative=*/true))) {
189 APInt AI
= FP
.bitcastToAPInt();
190 return std::string(AI
.isNegative() ? "-" : "") + "nan:0x" +
191 utohexstr(AI
.getZExtValue() &
192 (AI
.getBitWidth() == 32 ? INT64_C(0x007fffff)
193 : INT64_C(0x000fffffffffffff)),
197 // Use C99's hexadecimal floating-point representation.
198 static const size_t BufBytes
= 128;
200 auto Written
= FP
.convertToHexString(
201 Buf
, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven
);
203 assert(Written
!= 0);
204 assert(Written
< BufBytes
);
208 void WebAssemblyInstPrinter::printOperand(const MCInst
*MI
, unsigned OpNo
,
210 const MCOperand
&Op
= MI
->getOperand(OpNo
);
212 unsigned WAReg
= Op
.getReg();
214 printRegName(O
, WAReg
);
215 else if (OpNo
>= MII
.get(MI
->getOpcode()).getNumDefs())
216 O
<< "$pop" << WebAssemblyFunctionInfo::getWARegStackId(WAReg
);
217 else if (WAReg
!= WebAssemblyFunctionInfo::UnusedReg
)
218 O
<< "$push" << WebAssemblyFunctionInfo::getWARegStackId(WAReg
);
221 // Add a '=' suffix if this is a def.
222 if (OpNo
< MII
.get(MI
->getOpcode()).getNumDefs())
224 } else if (Op
.isImm()) {
226 } else if (Op
.isFPImm()) {
227 const MCInstrDesc
&Desc
= MII
.get(MI
->getOpcode());
228 const MCOperandInfo
&Info
= Desc
.OpInfo
[OpNo
];
229 if (Info
.OperandType
== WebAssembly::OPERAND_F32IMM
) {
230 // TODO: MC converts all floating point immediate operands to double.
231 // This is fine for numeric values, but may cause NaNs to change bits.
232 O
<< ::toString(APFloat(float(Op
.getFPImm())));
234 assert(Info
.OperandType
== WebAssembly::OPERAND_F64IMM
);
235 O
<< ::toString(APFloat(Op
.getFPImm()));
238 assert(Op
.isExpr() && "unknown operand kind in printOperand");
239 // call_indirect instructions have a TYPEINDEX operand that we print
240 // as a signature here, such that the assembler can recover this
242 auto SRE
= static_cast<const MCSymbolRefExpr
*>(Op
.getExpr());
243 if (SRE
->getKind() == MCSymbolRefExpr::VK_WASM_TYPEINDEX
) {
244 auto &Sym
= static_cast<const MCSymbolWasm
&>(SRE
->getSymbol());
245 O
<< WebAssembly::signatureToString(Sym
.getSignature());
247 Op
.getExpr()->print(O
, &MAI
);
252 void WebAssemblyInstPrinter::printBrList(const MCInst
*MI
, unsigned OpNo
,
255 for (unsigned I
= OpNo
, E
= MI
->getNumOperands(); I
!= E
; ++I
) {
258 O
<< MI
->getOperand(I
).getImm();
263 void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst
*MI
,
266 int64_t Imm
= MI
->getOperand(OpNo
).getImm();
267 if (Imm
== WebAssembly::GetDefaultP2Align(MI
->getOpcode()))
269 O
<< ":p2align=" << Imm
;
272 void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst
*MI
,
275 const MCOperand
&Op
= MI
->getOperand(OpNo
);
277 auto Imm
= static_cast<unsigned>(Op
.getImm());
278 if (Imm
!= wasm::WASM_TYPE_NORESULT
)
279 O
<< WebAssembly::anyTypeToString(Imm
);
281 auto Expr
= cast
<MCSymbolRefExpr
>(Op
.getExpr());
282 auto *Sym
= cast
<MCSymbolWasm
>(&Expr
->getSymbol());
283 if (Sym
->getSignature()) {
284 O
<< WebAssembly::signatureToString(Sym
->getSignature());
286 // Disassembler does not currently produce a signature
292 // We have various enums representing a subset of these types, use this
293 // function to convert any of them to text.
294 const char *WebAssembly::anyTypeToString(unsigned Ty
) {
296 case wasm::WASM_TYPE_I32
:
298 case wasm::WASM_TYPE_I64
:
300 case wasm::WASM_TYPE_F32
:
302 case wasm::WASM_TYPE_F64
:
304 case wasm::WASM_TYPE_V128
:
306 case wasm::WASM_TYPE_FUNCREF
:
308 case wasm::WASM_TYPE_FUNC
:
310 case wasm::WASM_TYPE_EXNREF
:
312 case wasm::WASM_TYPE_NORESULT
:
315 return "invalid_type";
319 const char *WebAssembly::typeToString(wasm::ValType Ty
) {
320 return anyTypeToString(static_cast<unsigned>(Ty
));
323 std::string
WebAssembly::typeListToString(ArrayRef
<wasm::ValType
> List
) {
325 for (auto &Ty
: List
) {
326 if (&Ty
!= &List
[0]) S
+= ", ";
327 S
+= WebAssembly::typeToString(Ty
);
332 std::string
WebAssembly::signatureToString(const wasm::WasmSignature
*Sig
) {
334 S
+= typeListToString(Sig
->Params
);
336 S
+= typeListToString(Sig
->Returns
);