13dac3a9bSDimitry Andric //=- WebAssemblyInstPrinter.cpp - WebAssembly assembly instruction printing -=//
23dac3a9bSDimitry Andric //
33dac3a9bSDimitry Andric // The LLVM Compiler Infrastructure
43dac3a9bSDimitry Andric //
53dac3a9bSDimitry Andric // This file is distributed under the University of Illinois Open Source
63dac3a9bSDimitry Andric // License. See LICENSE.TXT for details.
73dac3a9bSDimitry Andric //
83dac3a9bSDimitry Andric //===----------------------------------------------------------------------===//
93dac3a9bSDimitry Andric ///
103dac3a9bSDimitry Andric /// \file
114ba319b5SDimitry Andric /// Print MCInst instructions to wasm format.
123dac3a9bSDimitry Andric ///
133dac3a9bSDimitry Andric //===----------------------------------------------------------------------===//
143dac3a9bSDimitry Andric
153dac3a9bSDimitry Andric #include "InstPrinter/WebAssemblyInstPrinter.h"
167d523365SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
173dac3a9bSDimitry Andric #include "WebAssembly.h"
187d523365SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h"
19444ed5c5SDimitry Andric #include "llvm/ADT/SmallSet.h"
20444ed5c5SDimitry Andric #include "llvm/ADT/StringExtras.h"
212cab237bSDimitry Andric #include "llvm/CodeGen/TargetRegisterInfo.h"
223dac3a9bSDimitry Andric #include "llvm/MC/MCExpr.h"
233dac3a9bSDimitry Andric #include "llvm/MC/MCInst.h"
243dac3a9bSDimitry Andric #include "llvm/MC/MCInstrInfo.h"
253dac3a9bSDimitry Andric #include "llvm/MC/MCSubtargetInfo.h"
263dac3a9bSDimitry Andric #include "llvm/MC/MCSymbol.h"
273dac3a9bSDimitry Andric #include "llvm/Support/ErrorHandling.h"
283dac3a9bSDimitry Andric #include "llvm/Support/FormattedStream.h"
293dac3a9bSDimitry Andric using namespace llvm;
303dac3a9bSDimitry Andric
313dac3a9bSDimitry Andric #define DEBUG_TYPE "asm-printer"
323dac3a9bSDimitry Andric
337d523365SDimitry Andric #include "WebAssemblyGenAsmWriter.inc"
347d523365SDimitry Andric
WebAssemblyInstPrinter(const MCAsmInfo & MAI,const MCInstrInfo & MII,const MCRegisterInfo & MRI)353dac3a9bSDimitry Andric WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo &MAI,
363dac3a9bSDimitry Andric const MCInstrInfo &MII,
373dac3a9bSDimitry Andric const MCRegisterInfo &MRI)
38*b5893f02SDimitry Andric : MCInstPrinter(MAI, MII, MRI) {}
393dac3a9bSDimitry Andric
printRegName(raw_ostream & OS,unsigned RegNo) const403dac3a9bSDimitry Andric void WebAssemblyInstPrinter::printRegName(raw_ostream &OS,
413dac3a9bSDimitry Andric unsigned RegNo) const {
427d523365SDimitry Andric assert(RegNo != WebAssemblyFunctionInfo::UnusedReg);
43*b5893f02SDimitry Andric // Note that there's an implicit local.get/local.set here!
447d523365SDimitry Andric OS << "$" << RegNo;
453dac3a9bSDimitry Andric }
463dac3a9bSDimitry Andric
printInst(const MCInst * MI,raw_ostream & OS,StringRef Annot,const MCSubtargetInfo & STI)473dac3a9bSDimitry Andric void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
483dac3a9bSDimitry Andric StringRef Annot,
494ba319b5SDimitry Andric const MCSubtargetInfo &STI) {
507d523365SDimitry Andric // Print the instruction (this uses the AsmStrings from the .td files).
517d523365SDimitry Andric printInstruction(MI, OS);
527d523365SDimitry Andric
537d523365SDimitry Andric // Print any additional variadic operands.
547d523365SDimitry Andric const MCInstrDesc &Desc = MII.get(MI->getOpcode());
557d523365SDimitry Andric if (Desc.isVariadic())
567d523365SDimitry Andric for (auto i = Desc.getNumOperands(), e = MI->getNumOperands(); i < e; ++i) {
57d88c1a5aSDimitry Andric // FIXME: For CALL_INDIRECT_VOID, don't print a leading comma, because
58d88c1a5aSDimitry Andric // we have an extra flags operand which is not currently printed, for
59d88c1a5aSDimitry Andric // compatiblity reasons.
60*b5893f02SDimitry Andric if (i != 0 && ((MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID &&
61*b5893f02SDimitry Andric MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID_S) ||
62d88c1a5aSDimitry Andric i != Desc.getNumOperands()))
637d523365SDimitry Andric OS << ", ";
647d523365SDimitry Andric printOperand(MI, i, OS);
657d523365SDimitry Andric }
667d523365SDimitry Andric
677d523365SDimitry Andric // Print any added annotation.
687d523365SDimitry Andric printAnnotation(OS, Annot);
69444ed5c5SDimitry Andric
70444ed5c5SDimitry Andric if (CommentStream) {
71444ed5c5SDimitry Andric // Observe any effects on the control flow stack, for use in annotating
72444ed5c5SDimitry Andric // control flow label references.
73*b5893f02SDimitry Andric unsigned Opc = MI->getOpcode();
74*b5893f02SDimitry Andric switch (Opc) {
75444ed5c5SDimitry Andric default:
76444ed5c5SDimitry Andric break;
77*b5893f02SDimitry Andric
78*b5893f02SDimitry Andric case WebAssembly::LOOP:
79*b5893f02SDimitry Andric case WebAssembly::LOOP_S:
80d88c1a5aSDimitry Andric printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');
81d88c1a5aSDimitry Andric ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));
82444ed5c5SDimitry Andric break;
83*b5893f02SDimitry Andric
84444ed5c5SDimitry Andric case WebAssembly::BLOCK:
85*b5893f02SDimitry Andric case WebAssembly::BLOCK_S:
86444ed5c5SDimitry Andric ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
87444ed5c5SDimitry Andric break;
88*b5893f02SDimitry Andric
89*b5893f02SDimitry Andric case WebAssembly::TRY:
90*b5893f02SDimitry Andric case WebAssembly::TRY_S:
91*b5893f02SDimitry Andric ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
92*b5893f02SDimitry Andric EHPadStack.push_back(EHPadStackCounter++);
93*b5893f02SDimitry Andric LastSeenEHInst = TRY;
94444ed5c5SDimitry Andric break;
95*b5893f02SDimitry Andric
96*b5893f02SDimitry Andric case WebAssembly::END_LOOP:
97*b5893f02SDimitry Andric case WebAssembly::END_LOOP_S:
98*b5893f02SDimitry Andric if (ControlFlowStack.empty()) {
99*b5893f02SDimitry Andric printAnnotation(OS, "End marker mismatch!");
100*b5893f02SDimitry Andric } else {
101*b5893f02SDimitry Andric ControlFlowStack.pop_back();
102*b5893f02SDimitry Andric }
103*b5893f02SDimitry Andric break;
104*b5893f02SDimitry Andric
105444ed5c5SDimitry Andric case WebAssembly::END_BLOCK:
106*b5893f02SDimitry Andric case WebAssembly::END_BLOCK_S:
107*b5893f02SDimitry Andric if (ControlFlowStack.empty()) {
108*b5893f02SDimitry Andric printAnnotation(OS, "End marker mismatch!");
109*b5893f02SDimitry Andric } else {
110*b5893f02SDimitry Andric printAnnotation(
111444ed5c5SDimitry Andric OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
112*b5893f02SDimitry Andric }
113*b5893f02SDimitry Andric break;
114*b5893f02SDimitry Andric
115*b5893f02SDimitry Andric case WebAssembly::END_TRY:
116*b5893f02SDimitry Andric case WebAssembly::END_TRY_S:
117*b5893f02SDimitry Andric if (ControlFlowStack.empty()) {
118*b5893f02SDimitry Andric printAnnotation(OS, "End marker mismatch!");
119*b5893f02SDimitry Andric } else {
120*b5893f02SDimitry Andric printAnnotation(
121*b5893f02SDimitry Andric OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
122*b5893f02SDimitry Andric LastSeenEHInst = END_TRY;
123*b5893f02SDimitry Andric }
124*b5893f02SDimitry Andric break;
125*b5893f02SDimitry Andric
126*b5893f02SDimitry Andric case WebAssembly::CATCH_I32:
127*b5893f02SDimitry Andric case WebAssembly::CATCH_I32_S:
128*b5893f02SDimitry Andric case WebAssembly::CATCH_I64:
129*b5893f02SDimitry Andric case WebAssembly::CATCH_I64_S:
130*b5893f02SDimitry Andric case WebAssembly::CATCH_ALL:
131*b5893f02SDimitry Andric case WebAssembly::CATCH_ALL_S:
132*b5893f02SDimitry Andric // There can be multiple catch instructions for one try instruction, so we
133*b5893f02SDimitry Andric // print a label only for the first 'catch' label.
134*b5893f02SDimitry Andric if (LastSeenEHInst != CATCH) {
135*b5893f02SDimitry Andric if (EHPadStack.empty()) {
136*b5893f02SDimitry Andric printAnnotation(OS, "try-catch mismatch!");
137*b5893f02SDimitry Andric } else {
138*b5893f02SDimitry Andric printAnnotation(OS,
139*b5893f02SDimitry Andric "catch" + utostr(EHPadStack.pop_back_val()) + ':');
140*b5893f02SDimitry Andric }
141*b5893f02SDimitry Andric }
142*b5893f02SDimitry Andric LastSeenEHInst = CATCH;
143444ed5c5SDimitry Andric break;
144444ed5c5SDimitry Andric }
145444ed5c5SDimitry Andric
146444ed5c5SDimitry Andric // Annotate any control flow label references.
147444ed5c5SDimitry Andric unsigned NumFixedOperands = Desc.NumOperands;
148444ed5c5SDimitry Andric SmallSet<uint64_t, 8> Printed;
149444ed5c5SDimitry Andric for (unsigned i = 0, e = MI->getNumOperands(); i < e; ++i) {
150*b5893f02SDimitry Andric // See if this operand denotes a basic block target.
151*b5893f02SDimitry Andric if (i < NumFixedOperands) {
152*b5893f02SDimitry Andric // A non-variable_ops operand, check its type.
153*b5893f02SDimitry Andric if (Desc.OpInfo[i].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
154444ed5c5SDimitry Andric continue;
155*b5893f02SDimitry Andric } else {
156*b5893f02SDimitry Andric // A variable_ops operand, which currently can be immediates (used in
157*b5893f02SDimitry Andric // br_table) which are basic block targets, or for call instructions
158*b5893f02SDimitry Andric // when using -wasm-keep-registers (in which case they are registers,
159*b5893f02SDimitry Andric // and should not be processed).
160*b5893f02SDimitry Andric if (!MI->getOperand(i).isImm())
161*b5893f02SDimitry Andric continue;
162*b5893f02SDimitry Andric }
163444ed5c5SDimitry Andric uint64_t Depth = MI->getOperand(i).getImm();
164444ed5c5SDimitry Andric if (!Printed.insert(Depth).second)
165444ed5c5SDimitry Andric continue;
166*b5893f02SDimitry Andric
167*b5893f02SDimitry Andric if (Opc == WebAssembly::RETHROW || Opc == WebAssembly::RETHROW_S) {
168*b5893f02SDimitry Andric if (Depth > EHPadStack.size()) {
169*b5893f02SDimitry Andric printAnnotation(OS, "Invalid depth argument!");
170*b5893f02SDimitry Andric } else if (Depth == EHPadStack.size()) {
171*b5893f02SDimitry Andric // This can happen when rethrow instruction breaks out of all nests
172*b5893f02SDimitry Andric // and throws up to the current function's caller.
173*b5893f02SDimitry Andric printAnnotation(OS, utostr(Depth) + ": " + "to caller");
174*b5893f02SDimitry Andric } else {
175*b5893f02SDimitry Andric uint64_t CatchNo = EHPadStack.rbegin()[Depth];
176*b5893f02SDimitry Andric printAnnotation(OS, utostr(Depth) + ": " + "down to catch" +
177*b5893f02SDimitry Andric utostr(CatchNo));
178*b5893f02SDimitry Andric }
179*b5893f02SDimitry Andric
180*b5893f02SDimitry Andric } else {
181*b5893f02SDimitry Andric if (Depth >= ControlFlowStack.size()) {
182*b5893f02SDimitry Andric printAnnotation(OS, "Invalid depth argument!");
183*b5893f02SDimitry Andric } else {
184444ed5c5SDimitry Andric const auto &Pair = ControlFlowStack.rbegin()[Depth];
185*b5893f02SDimitry Andric printAnnotation(OS, utostr(Depth) + ": " +
186*b5893f02SDimitry Andric (Pair.second ? "up" : "down") + " to label" +
187*b5893f02SDimitry Andric utostr(Pair.first));
188*b5893f02SDimitry Andric }
189*b5893f02SDimitry Andric }
190444ed5c5SDimitry Andric }
191444ed5c5SDimitry Andric }
1927d523365SDimitry Andric }
1937d523365SDimitry Andric
toString(const APFloat & FP)1947d523365SDimitry Andric static std::string toString(const APFloat &FP) {
1953ca95b02SDimitry Andric // Print NaNs with custom payloads specially.
196*b5893f02SDimitry Andric if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
197d88c1a5aSDimitry Andric !FP.bitwiseIsEqual(
198d88c1a5aSDimitry Andric APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
1993ca95b02SDimitry Andric APInt AI = FP.bitcastToAPInt();
200*b5893f02SDimitry Andric return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
2013ca95b02SDimitry Andric utohexstr(AI.getZExtValue() &
202*b5893f02SDimitry Andric (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
203*b5893f02SDimitry Andric : INT64_C(0x000fffffffffffff)),
2043ca95b02SDimitry Andric /*LowerCase=*/true);
2053ca95b02SDimitry Andric }
2063ca95b02SDimitry Andric
2073ca95b02SDimitry Andric // Use C99's hexadecimal floating-point representation.
2087d523365SDimitry Andric static const size_t BufBytes = 128;
2097d523365SDimitry Andric char buf[BufBytes];
2107d523365SDimitry Andric auto Written = FP.convertToHexString(
2117d523365SDimitry Andric buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven);
2127d523365SDimitry Andric (void)Written;
2137d523365SDimitry Andric assert(Written != 0);
2147d523365SDimitry Andric assert(Written < BufBytes);
2157d523365SDimitry Andric return buf;
2167d523365SDimitry Andric }
2177d523365SDimitry Andric
printOperand(const MCInst * MI,unsigned OpNo,raw_ostream & O)2187d523365SDimitry Andric void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
2197d523365SDimitry Andric raw_ostream &O) {
2207d523365SDimitry Andric const MCOperand &Op = MI->getOperand(OpNo);
2217d523365SDimitry Andric if (Op.isReg()) {
2227d523365SDimitry Andric unsigned WAReg = Op.getReg();
2237d523365SDimitry Andric if (int(WAReg) >= 0)
2247d523365SDimitry Andric printRegName(O, WAReg);
2257d523365SDimitry Andric else if (OpNo >= MII.get(MI->getOpcode()).getNumDefs())
2263ca95b02SDimitry Andric O << "$pop" << WebAssemblyFunctionInfo::getWARegStackId(WAReg);
2277d523365SDimitry Andric else if (WAReg != WebAssemblyFunctionInfo::UnusedReg)
2283ca95b02SDimitry Andric O << "$push" << WebAssemblyFunctionInfo::getWARegStackId(WAReg);
2297d523365SDimitry Andric else
2303ca95b02SDimitry Andric O << "$drop";
2317d523365SDimitry Andric // Add a '=' suffix if this is a def.
2327d523365SDimitry Andric if (OpNo < MII.get(MI->getOpcode()).getNumDefs())
2337d523365SDimitry Andric O << '=';
2347d523365SDimitry Andric } else if (Op.isImm()) {
2357d523365SDimitry Andric O << Op.getImm();
236444ed5c5SDimitry Andric } else if (Op.isFPImm()) {
2373ca95b02SDimitry Andric const MCInstrDesc &Desc = MII.get(MI->getOpcode());
2383ca95b02SDimitry Andric const MCOperandInfo &Info = Desc.OpInfo[OpNo];
239d88c1a5aSDimitry Andric if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
2403ca95b02SDimitry Andric // TODO: MC converts all floating point immediate operands to double.
2413ca95b02SDimitry Andric // This is fine for numeric values, but may cause NaNs to change bits.
2424ba319b5SDimitry Andric O << ::toString(APFloat(float(Op.getFPImm())));
2433ca95b02SDimitry Andric } else {
244d88c1a5aSDimitry Andric assert(Info.OperandType == WebAssembly::OPERAND_F64IMM);
2454ba319b5SDimitry Andric O << ::toString(APFloat(Op.getFPImm()));
2463ca95b02SDimitry Andric }
247444ed5c5SDimitry Andric } else {
2487d523365SDimitry Andric assert(Op.isExpr() && "unknown operand kind in printOperand");
2497d523365SDimitry Andric Op.getExpr()->print(O, &MAI);
2507d523365SDimitry Andric }
2517d523365SDimitry Andric }
2527d523365SDimitry Andric
printBrList(const MCInst * MI,unsigned OpNo,raw_ostream & O)253*b5893f02SDimitry Andric void WebAssemblyInstPrinter::printBrList(const MCInst *MI, unsigned OpNo,
254*b5893f02SDimitry Andric raw_ostream &O) {
255*b5893f02SDimitry Andric O << "{";
256*b5893f02SDimitry Andric for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {
257*b5893f02SDimitry Andric if (I != OpNo)
258*b5893f02SDimitry Andric O << ", ";
259*b5893f02SDimitry Andric O << MI->getOperand(I).getImm();
260*b5893f02SDimitry Andric }
261*b5893f02SDimitry Andric O << "}";
262*b5893f02SDimitry Andric }
263*b5893f02SDimitry Andric
printWebAssemblyP2AlignOperand(const MCInst * MI,unsigned OpNo,raw_ostream & O)264*b5893f02SDimitry Andric void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,
265*b5893f02SDimitry Andric unsigned OpNo,
266*b5893f02SDimitry Andric raw_ostream &O) {
2673ca95b02SDimitry Andric int64_t Imm = MI->getOperand(OpNo).getImm();
2683ca95b02SDimitry Andric if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))
2693ca95b02SDimitry Andric return;
2703ca95b02SDimitry Andric O << ":p2align=" << Imm;
2713ca95b02SDimitry Andric }
2723ca95b02SDimitry Andric
printWebAssemblySignatureOperand(const MCInst * MI,unsigned OpNo,raw_ostream & O)273*b5893f02SDimitry Andric void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI,
274*b5893f02SDimitry Andric unsigned OpNo,
275*b5893f02SDimitry Andric raw_ostream &O) {
276*b5893f02SDimitry Andric auto Imm = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
277*b5893f02SDimitry Andric if (Imm != wasm::WASM_TYPE_NORESULT)
278*b5893f02SDimitry Andric O << WebAssembly::anyTypeToString(Imm);
279d88c1a5aSDimitry Andric }
280d88c1a5aSDimitry Andric
281*b5893f02SDimitry Andric // We have various enums representing a subset of these types, use this
282*b5893f02SDimitry Andric // function to convert any of them to text.
anyTypeToString(unsigned Ty)283*b5893f02SDimitry Andric const char *llvm::WebAssembly::anyTypeToString(unsigned Ty) {
284*b5893f02SDimitry Andric switch (Ty) {
285*b5893f02SDimitry Andric case wasm::WASM_TYPE_I32:
2867d523365SDimitry Andric return "i32";
287*b5893f02SDimitry Andric case wasm::WASM_TYPE_I64:
2887d523365SDimitry Andric return "i64";
289*b5893f02SDimitry Andric case wasm::WASM_TYPE_F32:
2907d523365SDimitry Andric return "f32";
291*b5893f02SDimitry Andric case wasm::WASM_TYPE_F64:
2927d523365SDimitry Andric return "f64";
293*b5893f02SDimitry Andric case wasm::WASM_TYPE_V128:
294d88c1a5aSDimitry Andric return "v128";
295*b5893f02SDimitry Andric case wasm::WASM_TYPE_FUNCREF:
296*b5893f02SDimitry Andric return "funcref";
297*b5893f02SDimitry Andric case wasm::WASM_TYPE_FUNC:
298*b5893f02SDimitry Andric return "func";
299*b5893f02SDimitry Andric case wasm::WASM_TYPE_EXCEPT_REF:
3004ba319b5SDimitry Andric return "except_ref";
301*b5893f02SDimitry Andric case wasm::WASM_TYPE_NORESULT:
302*b5893f02SDimitry Andric return "void";
3037d523365SDimitry Andric default:
304*b5893f02SDimitry Andric return "invalid_type";
3057d523365SDimitry Andric }
3063dac3a9bSDimitry Andric }
3077a7e6055SDimitry Andric
typeToString(wasm::ValType Ty)308*b5893f02SDimitry Andric const char *llvm::WebAssembly::typeToString(wasm::ValType Ty) {
309*b5893f02SDimitry Andric return anyTypeToString(static_cast<unsigned>(Ty));
3107a7e6055SDimitry Andric }
311