| 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 | |
| 14 | #include "MCTargetDesc/WebAssemblyInstPrinter.h" |
| 15 | #include "MCTargetDesc/WebAssemblyMCAsmInfo.h" |
| 16 | #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
| 17 | #include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" |
| 18 | #include "llvm/ADT/APFloat.h" |
| 19 | #include "llvm/ADT/SmallSet.h" |
| 20 | #include "llvm/ADT/StringExtras.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" |
| 25 | #include "llvm/MC/MCSubtargetInfo.h" |
| 26 | #include "llvm/MC/MCSymbol.h" |
| 27 | #include "llvm/MC/MCSymbolWasm.h" |
| 28 | #include "llvm/Support/Casting.h" |
| 29 | #include "llvm/Support/ErrorHandling.h" |
| 30 | using namespace llvm; |
| 31 | |
| 32 | #define DEBUG_TYPE "asm-printer" |
| 33 | |
| 34 | #include "WebAssemblyGenAsmWriter.inc" |
| 35 | |
| 36 | WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo &MAI, |
| 37 | const MCInstrInfo &MII, |
| 38 | const MCRegisterInfo &MRI) |
| 39 | : MCInstPrinter(MAI, MII, MRI) {} |
| 40 | |
| 41 | void WebAssemblyInstPrinter::printRegName(raw_ostream &OS, MCRegister Reg) { |
| 42 | assert(Reg.id() != WebAssembly::UnusedReg); |
| 43 | // Note that there's an implicit local.get/local.set here! |
| 44 | OS << "$" << Reg.id(); |
| 45 | } |
| 46 | |
| 47 | void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address, |
| 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(i: 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): the order of |
| 64 | // the type and table operands is inverted in the text format relative to |
| 65 | // the binary format. The table operand is omitted when it refers to the |
| 66 | // default table 0 of an MVP compilation unit. Otherwise (a table symbol, |
| 67 | // or a non-zero table index from a multi-table module) it is printed |
| 68 | // first. A disassembler can also hand us an arbitrary immediate here when |
| 69 | // decoding misaligned bytes, so print it rather than asserting. |
| 70 | OS << "\t" ; |
| 71 | OS << getMnemonic(MI: *MI).first; |
| 72 | OS << " " ; |
| 73 | const MCOperand &TableOp = MI->getOperand(i: TableOperand); |
| 74 | if (TableOp.isExpr() || (TableOp.isImm() && TableOp.getImm() != 0)) { |
| 75 | printOperand(MI, OpNo: TableOperand, STI, O&: OS); |
| 76 | OS << ", " ; |
| 77 | } |
| 78 | printOperand(MI, OpNo: TypeOperand, STI, O&: OS); |
| 79 | if (MI->getOpcode() == WebAssembly::CALL_INDIRECT) |
| 80 | OS << ", " ; |
| 81 | break; |
| 82 | } |
| 83 | default: |
| 84 | // Print the instruction (this uses the AsmStrings from the .td files). |
| 85 | printInstruction(MI, Address, STI, O&: OS); |
| 86 | break; |
| 87 | } |
| 88 | |
| 89 | // Print any additional variadic operands. |
| 90 | const MCInstrDesc &Desc = MII.get(Opcode: MI->getOpcode()); |
| 91 | if (Desc.isVariadic()) { |
| 92 | if ((Desc.getNumOperands() == 0 && MI->getNumOperands() > 0) || |
| 93 | Desc.variadicOpsAreDefs()) |
| 94 | OS << "\t" ; |
| 95 | unsigned Start = Desc.getNumOperands(); |
| 96 | unsigned NumVariadicDefs = 0; |
| 97 | if (Desc.variadicOpsAreDefs()) { |
| 98 | // The number of variadic defs is encoded in an immediate by MCInstLower |
| 99 | NumVariadicDefs = MI->getOperand(i: 0).getImm(); |
| 100 | Start = 1; |
| 101 | } |
| 102 | bool NeedsComma = Desc.getNumOperands() > 0 && !Desc.variadicOpsAreDefs(); |
| 103 | for (auto I = Start, E = MI->getNumOperands(); I < E; ++I) { |
| 104 | if (MI->getOpcode() == WebAssembly::CALL_INDIRECT && |
| 105 | I - Start == NumVariadicDefs) { |
| 106 | // Skip type and table arguments when printing for tests. |
| 107 | ++I; |
| 108 | continue; |
| 109 | } |
| 110 | if (NeedsComma) |
| 111 | OS << ", " ; |
| 112 | printOperand(MI, OpNo: I, STI, O&: OS, IsVariadicDef: I - Start < NumVariadicDefs); |
| 113 | NeedsComma = true; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | // Print any added annotation. |
| 118 | printAnnotation(OS, Annot); |
| 119 | |
| 120 | auto PrintBranchAnnotation = [&](const MCOperand &Op, |
| 121 | SmallSet<uint64_t, 8> &Printed) { |
| 122 | uint64_t Depth = Op.getImm(); |
| 123 | if (!Printed.insert(V: Depth).second) |
| 124 | return; |
| 125 | if (Depth >= ControlFlowStack.size()) { |
| 126 | printAnnotation(OS, Annot: "Invalid depth argument!" ); |
| 127 | } else { |
| 128 | const auto &Pair = ControlFlowStack.rbegin()[Depth]; |
| 129 | printAnnotation(OS, Annot: utostr(X: Depth) + ": " + (Pair.second ? "up" : "down" ) + |
| 130 | " to label" + utostr(X: Pair.first)); |
| 131 | } |
| 132 | }; |
| 133 | |
| 134 | if (CommentStream) { |
| 135 | // Observe any effects on the control flow stack, for use in annotating |
| 136 | // control flow label references. |
| 137 | unsigned Opc = MI->getOpcode(); |
| 138 | switch (Opc) { |
| 139 | default: |
| 140 | break; |
| 141 | |
| 142 | case WebAssembly::LOOP: |
| 143 | case WebAssembly::LOOP_S: |
| 144 | printAnnotation(OS, Annot: "label" + utostr(X: ControlFlowCounter) + ':'); |
| 145 | ControlFlowStack.push_back(Elt: std::make_pair(x: ControlFlowCounter++, y: true)); |
| 146 | return; |
| 147 | |
| 148 | case WebAssembly::BLOCK: |
| 149 | case WebAssembly::BLOCK_S: |
| 150 | ControlFlowStack.push_back(Elt: std::make_pair(x: ControlFlowCounter++, y: false)); |
| 151 | return; |
| 152 | |
| 153 | case WebAssembly::TRY: |
| 154 | case WebAssembly::TRY_S: |
| 155 | ControlFlowStack.push_back(Elt: std::make_pair(x&: ControlFlowCounter, y: false)); |
| 156 | TryStack.push_back(Elt: ControlFlowCounter++); |
| 157 | EHInstStack.push_back(Elt: TRY); |
| 158 | return; |
| 159 | |
| 160 | case WebAssembly::TRY_TABLE: |
| 161 | case WebAssembly::TRY_TABLE_S: { |
| 162 | SmallSet<uint64_t, 8> Printed; |
| 163 | unsigned OpIdx = 1; |
| 164 | const MCOperand &Op = MI->getOperand(i: OpIdx++); |
| 165 | unsigned NumCatches = Op.getImm(); |
| 166 | for (unsigned I = 0; I < NumCatches; I++) { |
| 167 | int64_t CatchOpcode = MI->getOperand(i: OpIdx++).getImm(); |
| 168 | if (CatchOpcode == wasm::WASM_OPCODE_CATCH || |
| 169 | CatchOpcode == wasm::WASM_OPCODE_CATCH_REF) |
| 170 | OpIdx++; // Skip tag |
| 171 | PrintBranchAnnotation(MI->getOperand(i: OpIdx++), Printed); |
| 172 | } |
| 173 | ControlFlowStack.push_back(Elt: std::make_pair(x: ControlFlowCounter++, y: false)); |
| 174 | return; |
| 175 | } |
| 176 | |
| 177 | case WebAssembly::SELECT_T: |
| 178 | case WebAssembly::SELECT_T_S: |
| 179 | // The trailing operands encode a vec of valtypes, not branch targets; |
| 180 | // skip the generic branch-annotation pass below. |
| 181 | return; |
| 182 | |
| 183 | case WebAssembly::END_LOOP: |
| 184 | case WebAssembly::END_LOOP_S: |
| 185 | if (ControlFlowStack.empty()) { |
| 186 | printAnnotation(OS, Annot: "End marker mismatch!" ); |
| 187 | } else { |
| 188 | ControlFlowStack.pop_back(); |
| 189 | } |
| 190 | return; |
| 191 | |
| 192 | case WebAssembly::END_BLOCK: |
| 193 | case WebAssembly::END_BLOCK_S: |
| 194 | case WebAssembly::END_TRY_TABLE: |
| 195 | case WebAssembly::END_TRY_TABLE_S: |
| 196 | if (ControlFlowStack.empty()) { |
| 197 | printAnnotation(OS, Annot: "End marker mismatch!" ); |
| 198 | } else { |
| 199 | printAnnotation( |
| 200 | OS, Annot: "label" + utostr(X: ControlFlowStack.pop_back_val().first) + ':'); |
| 201 | } |
| 202 | return; |
| 203 | |
| 204 | case WebAssembly::END_TRY: |
| 205 | case WebAssembly::END_TRY_S: |
| 206 | if (ControlFlowStack.empty() || EHInstStack.empty()) { |
| 207 | printAnnotation(OS, Annot: "End marker mismatch!" ); |
| 208 | } else { |
| 209 | printAnnotation( |
| 210 | OS, Annot: "label" + utostr(X: ControlFlowStack.pop_back_val().first) + ':'); |
| 211 | EHInstStack.pop_back(); |
| 212 | } |
| 213 | return; |
| 214 | |
| 215 | case WebAssembly::CATCH_LEGACY: |
| 216 | case WebAssembly::CATCH_LEGACY_S: |
| 217 | case WebAssembly::CATCH_ALL_LEGACY: |
| 218 | case WebAssembly::CATCH_ALL_LEGACY_S: |
| 219 | // There can be multiple catch instructions for one try instruction, so |
| 220 | // we print a label only for the first 'catch' label. |
| 221 | if (EHInstStack.empty()) { |
| 222 | printAnnotation(OS, Annot: "try-catch mismatch!" ); |
| 223 | } else if (EHInstStack.back() == CATCH_ALL_LEGACY) { |
| 224 | printAnnotation(OS, Annot: "catch/catch_all cannot occur after catch_all" ); |
| 225 | } else if (EHInstStack.back() == TRY) { |
| 226 | if (TryStack.empty()) { |
| 227 | printAnnotation(OS, Annot: "try-catch mismatch!" ); |
| 228 | } else { |
| 229 | printAnnotation(OS, Annot: "catch" + utostr(X: TryStack.pop_back_val()) + ':'); |
| 230 | } |
| 231 | EHInstStack.pop_back(); |
| 232 | if (Opc == WebAssembly::CATCH_LEGACY || |
| 233 | Opc == WebAssembly::CATCH_LEGACY_S) { |
| 234 | EHInstStack.push_back(Elt: CATCH_LEGACY); |
| 235 | } else { |
| 236 | EHInstStack.push_back(Elt: CATCH_ALL_LEGACY); |
| 237 | } |
| 238 | } |
| 239 | return; |
| 240 | |
| 241 | case WebAssembly::RETHROW: |
| 242 | case WebAssembly::RETHROW_S: |
| 243 | // 'rethrow' rethrows to the nearest enclosing catch scope, if any. If |
| 244 | // there's no enclosing catch scope, it throws up to the caller. |
| 245 | if (TryStack.empty()) { |
| 246 | printAnnotation(OS, Annot: "to caller" ); |
| 247 | } else { |
| 248 | printAnnotation(OS, Annot: "down to catch" + utostr(X: TryStack.back())); |
| 249 | } |
| 250 | return; |
| 251 | |
| 252 | case WebAssembly::DELEGATE: |
| 253 | case WebAssembly::DELEGATE_S: |
| 254 | if (ControlFlowStack.empty() || TryStack.empty() || EHInstStack.empty()) { |
| 255 | printAnnotation(OS, Annot: "try-delegate mismatch!" ); |
| 256 | } else { |
| 257 | // 'delegate' is |
| 258 | // 1. A marker for the end of block label |
| 259 | // 2. A destination for throwing instructions |
| 260 | // 3. An instruction that itself rethrows to another 'catch' |
| 261 | assert(ControlFlowStack.back().first == TryStack.back()); |
| 262 | std::string Label = "label/catch" + |
| 263 | utostr(X: ControlFlowStack.pop_back_val().first) + |
| 264 | ": " ; |
| 265 | TryStack.pop_back(); |
| 266 | EHInstStack.pop_back(); |
| 267 | uint64_t Depth = MI->getOperand(i: 0).getImm(); |
| 268 | if (Depth >= ControlFlowStack.size()) { |
| 269 | Label += "to caller" ; |
| 270 | } else { |
| 271 | const auto &Pair = ControlFlowStack.rbegin()[Depth]; |
| 272 | if (Pair.second) |
| 273 | printAnnotation(OS, Annot: "delegate cannot target a loop" ); |
| 274 | else |
| 275 | Label += "down to catch" + utostr(X: Pair.first); |
| 276 | } |
| 277 | printAnnotation(OS, Annot: Label); |
| 278 | } |
| 279 | return; |
| 280 | } |
| 281 | |
| 282 | // Annotate any control flow label references. |
| 283 | |
| 284 | unsigned NumFixedOperands = Desc.NumOperands; |
| 285 | SmallSet<uint64_t, 8> Printed; |
| 286 | for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) { |
| 287 | // See if this operand denotes a basic block target. |
| 288 | if (I < NumFixedOperands) { |
| 289 | // A non-variable_ops operand, check its type. |
| 290 | if (Desc.operands()[I].OperandType != WebAssembly::OPERAND_BASIC_BLOCK) |
| 291 | continue; |
| 292 | } else { |
| 293 | // A variable_ops operand, which currently can be immediates (used in |
| 294 | // br_table) which are basic block targets, or for call instructions |
| 295 | // when using -wasm-keep-registers (in which case they are registers, |
| 296 | // and should not be processed). |
| 297 | if (!MI->getOperand(i: I).isImm()) |
| 298 | continue; |
| 299 | } |
| 300 | PrintBranchAnnotation(MI->getOperand(i: I), Printed); |
| 301 | } |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | static std::string toString(const APFloat &FP) { |
| 306 | // Print NaNs with custom payloads specially. |
| 307 | if (FP.isNaN() && !FP.bitwiseIsEqual(RHS: APFloat::getQNaN(Sem: FP.getSemantics())) && |
| 308 | !FP.bitwiseIsEqual( |
| 309 | RHS: APFloat::getQNaN(Sem: FP.getSemantics(), /*Negative=*/true))) { |
| 310 | APInt AI = FP.bitcastToAPInt(); |
| 311 | return std::string(AI.isNegative() ? "-" : "" ) + "nan:0x" + |
| 312 | utohexstr(X: AI.getZExtValue() & |
| 313 | (AI.getBitWidth() == 32 ? INT64_C(0x007fffff) |
| 314 | : INT64_C(0x000fffffffffffff)), |
| 315 | /*LowerCase=*/true); |
| 316 | } |
| 317 | |
| 318 | // Use C99's hexadecimal floating-point representation. |
| 319 | static const size_t BufBytes = 128; |
| 320 | char Buf[BufBytes]; |
| 321 | auto Written = FP.convertToHexString( |
| 322 | DST: Buf, /*HexDigits=*/0, /*UpperCase=*/false, RM: APFloat::rmNearestTiesToEven); |
| 323 | (void)Written; |
| 324 | assert(Written != 0); |
| 325 | assert(Written < BufBytes); |
| 326 | return Buf; |
| 327 | } |
| 328 | |
| 329 | void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, |
| 330 | const MCSubtargetInfo &STI, |
| 331 | raw_ostream &O, bool IsVariadicDef) { |
| 332 | const MCOperand &Op = MI->getOperand(i: OpNo); |
| 333 | if (Op.isReg()) { |
| 334 | const MCInstrDesc &Desc = MII.get(Opcode: MI->getOpcode()); |
| 335 | MCRegister WAReg = Op.getReg(); |
| 336 | if (int(WAReg.id()) >= 0) |
| 337 | printRegName(OS&: O, Reg: WAReg); |
| 338 | else if (OpNo >= Desc.getNumDefs() && !IsVariadicDef) |
| 339 | O << "$pop" << WebAssembly::getWARegStackId(Reg: WAReg); |
| 340 | else if (WAReg != WebAssembly::UnusedReg) |
| 341 | O << "$push" << WebAssembly::getWARegStackId(Reg: WAReg); |
| 342 | else |
| 343 | O << "$drop" ; |
| 344 | // Add a '=' suffix if this is a def. |
| 345 | if (OpNo < MII.get(Opcode: MI->getOpcode()).getNumDefs() || IsVariadicDef) |
| 346 | O << '='; |
| 347 | } else if (Op.isImm()) { |
| 348 | O << Op.getImm(); |
| 349 | } else if (Op.isSFPImm()) { |
| 350 | O << ::toString(FP: APFloat(APFloat::IEEEsingle(), APInt(32, Op.getSFPImm()))); |
| 351 | } else if (Op.isDFPImm()) { |
| 352 | O << ::toString(FP: APFloat(APFloat::IEEEdouble(), APInt(64, Op.getDFPImm()))); |
| 353 | } else { |
| 354 | assert(Op.isExpr() && "unknown operand kind in printOperand" ); |
| 355 | // call_indirect instructions have a TYPEINDEX operand that we print |
| 356 | // as a signature here, such that the assembler can recover this |
| 357 | // information. |
| 358 | auto SRE = static_cast<const MCSymbolRefExpr *>(Op.getExpr()); |
| 359 | if (SRE->getSpecifier() == WebAssembly::S_TYPEINDEX) { |
| 360 | auto &Sym = static_cast<const MCSymbolWasm &>(SRE->getSymbol()); |
| 361 | O << WebAssembly::signatureToString(Sig: Sym.getSignature()); |
| 362 | } else { |
| 363 | MAI.printExpr(O, *Op.getExpr()); |
| 364 | } |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | void WebAssemblyInstPrinter::printBrList(const MCInst *MI, unsigned OpNo, |
| 369 | const MCSubtargetInfo &STI, |
| 370 | raw_ostream &O) { |
| 371 | O << "{" ; |
| 372 | for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) { |
| 373 | if (I != OpNo) |
| 374 | O << ", " ; |
| 375 | O << MI->getOperand(i: I).getImm(); |
| 376 | } |
| 377 | O << "}" ; |
| 378 | } |
| 379 | |
| 380 | void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand( |
| 381 | const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, |
| 382 | raw_ostream &O) { |
| 383 | int64_t Imm = MI->getOperand(i: OpNo).getImm(); |
| 384 | if (Imm == WebAssembly::GetDefaultP2Align(Opc: MI->getOpcode())) |
| 385 | return; |
| 386 | O << ":p2align=" << Imm; |
| 387 | } |
| 388 | |
| 389 | void WebAssemblyInstPrinter::printWebAssemblyMemOrderOperand( |
| 390 | const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, |
| 391 | raw_ostream &O) { |
| 392 | int64_t Imm = MI->getOperand(i: OpNo).getImm(); |
| 393 | |
| 394 | switch (Imm) { |
| 395 | case wasm::WASM_MEM_ORDER_RMW_ACQ_REL: |
| 396 | case wasm::WASM_MEM_ORDER_ACQ_REL: |
| 397 | O << "acqrel" ; |
| 398 | break; |
| 399 | case wasm::WASM_MEM_ORDER_SEQ_CST: |
| 400 | if (STI.getFeatureBits()[WebAssembly::FeatureRelaxedAtomics]) |
| 401 | O << "seqcst" ; |
| 402 | break; |
| 403 | default: |
| 404 | llvm_unreachable("Unknown memory ordering" ); |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | void WebAssemblyInstPrinter::printWebAssemblySignatureOperand( |
| 409 | const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, |
| 410 | raw_ostream &O) { |
| 411 | const MCOperand &Op = MI->getOperand(i: OpNo); |
| 412 | if (Op.isImm()) { |
| 413 | auto Imm = static_cast<unsigned>(Op.getImm()); |
| 414 | if (Imm != wasm::WASM_TYPE_NORESULT) |
| 415 | O << WebAssembly::anyTypeToString(Type: Imm); |
| 416 | } else { |
| 417 | auto Expr = cast<MCSymbolRefExpr>(Val: Op.getExpr()); |
| 418 | auto *Sym = static_cast<const MCSymbolWasm *>(&Expr->getSymbol()); |
| 419 | if (Sym->getSignature()) { |
| 420 | O << WebAssembly::signatureToString(Sig: Sym->getSignature()); |
| 421 | } else { |
| 422 | // Disassembler does not currently produce a signature |
| 423 | O << "unknown_type" ; |
| 424 | } |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | void WebAssemblyInstPrinter::printCatchList(const MCInst *MI, unsigned OpNo, |
| 429 | const MCSubtargetInfo &STI, |
| 430 | raw_ostream &O) { |
| 431 | unsigned OpIdx = OpNo; |
| 432 | const MCOperand &Op = MI->getOperand(i: OpIdx++); |
| 433 | unsigned NumCatches = Op.getImm(); |
| 434 | |
| 435 | auto PrintTagOp = [&](const MCOperand &Op) { |
| 436 | const MCSymbolRefExpr *TagExpr = nullptr; |
| 437 | const MCSymbol *TagSym = nullptr; |
| 438 | if (Op.isExpr()) { |
| 439 | TagExpr = cast<MCSymbolRefExpr>(Val: Op.getExpr()); |
| 440 | TagSym = &TagExpr->getSymbol(); |
| 441 | O << TagSym->getName() << " " ; |
| 442 | } else { |
| 443 | // When instructions are parsed from the disassembler, we have an |
| 444 | // immediate tag index and not a tag expr |
| 445 | O << Op.getImm() << " " ; |
| 446 | } |
| 447 | }; |
| 448 | |
| 449 | for (unsigned I = 0; I < NumCatches; I++) { |
| 450 | const MCOperand &Op = MI->getOperand(i: OpIdx++); |
| 451 | O << "(" ; |
| 452 | switch (Op.getImm()) { |
| 453 | case wasm::WASM_OPCODE_CATCH: |
| 454 | O << "catch " ; |
| 455 | PrintTagOp(MI->getOperand(i: OpIdx++)); |
| 456 | break; |
| 457 | case wasm::WASM_OPCODE_CATCH_REF: |
| 458 | O << "catch_ref " ; |
| 459 | PrintTagOp(MI->getOperand(i: OpIdx++)); |
| 460 | break; |
| 461 | case wasm::WASM_OPCODE_CATCH_ALL: |
| 462 | O << "catch_all " ; |
| 463 | break; |
| 464 | case wasm::WASM_OPCODE_CATCH_ALL_REF: |
| 465 | O << "catch_all_ref " ; |
| 466 | break; |
| 467 | } |
| 468 | O << MI->getOperand(i: OpIdx++).getImm(); // destination |
| 469 | O << ")" ; |
| 470 | if (I < NumCatches - 1) |
| 471 | O << " " ; |
| 472 | } |
| 473 | } |
| 474 | |
| 475 | void WebAssemblyInstPrinter::printTypeList(const MCInst *MI, unsigned OpNo, |
| 476 | const MCSubtargetInfo &STI, |
| 477 | raw_ostream &O) { |
| 478 | unsigned OpIdx = OpNo; |
| 479 | uint64_t NumTypes = uint64_t(MI->getOperand(i: OpIdx++).getImm()); |
| 480 | uint64_t Remaining = MI->getNumOperands() - OpIdx; |
| 481 | if (NumTypes > Remaining) |
| 482 | NumTypes = Remaining; |
| 483 | for (uint64_t I = 0; I < NumTypes; I++) { |
| 484 | if (I != 0) |
| 485 | O << ' '; |
| 486 | O << WebAssembly::anyTypeToString(Type: MI->getOperand(i: OpIdx++).getImm()); |
| 487 | } |
| 488 | } |
| 489 | |