brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.4 KiB · 651f631 Raw
438 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// 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"30using namespace llvm;31 32#define DEBUG_TYPE "asm-printer"33 34#include "WebAssemblyGenAsmWriter.inc"35 36WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo &MAI,37                                               const MCInstrInfo &MII,38                                               const MCRegisterInfo &MRI)39    : MCInstPrinter(MAI, MII, MRI) {}40 41void 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 47void WebAssemblyInstPrinter::printInst(const MCInst *MI, uint64_t Address,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 table55    // operand is a symbol: the order of the type and table operands is inverted56    // in the text format relative to the binary format.  Otherwise if table the57    // operand isn't a symbol, then we have an MVP compilation unit, and the58    // 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).77    printInstruction(MI, Address, OS);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 MCInstLower91      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  auto PrintBranchAnnotation = [&](const MCOperand &Op,113                                   SmallSet<uint64_t, 8> &Printed) {114    uint64_t Depth = Op.getImm();115    if (!Printed.insert(Depth).second)116      return;117    if (Depth >= ControlFlowStack.size()) {118      printAnnotation(OS, "Invalid depth argument!");119    } else {120      const auto &Pair = ControlFlowStack.rbegin()[Depth];121      printAnnotation(OS, utostr(Depth) + ": " + (Pair.second ? "up" : "down") +122                              " to label" + utostr(Pair.first));123    }124  };125 126  if (CommentStream) {127    // Observe any effects on the control flow stack, for use in annotating128    // control flow label references.129    unsigned Opc = MI->getOpcode();130    switch (Opc) {131    default:132      break;133 134    case WebAssembly::LOOP:135    case WebAssembly::LOOP_S:136      printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');137      ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));138      return;139 140    case WebAssembly::BLOCK:141    case WebAssembly::BLOCK_S:142      ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));143      return;144 145    case WebAssembly::TRY:146    case WebAssembly::TRY_S:147      ControlFlowStack.push_back(std::make_pair(ControlFlowCounter, false));148      TryStack.push_back(ControlFlowCounter++);149      EHInstStack.push_back(TRY);150      return;151 152    case WebAssembly::TRY_TABLE:153    case WebAssembly::TRY_TABLE_S: {154      SmallSet<uint64_t, 8> Printed;155      unsigned OpIdx = 1;156      const MCOperand &Op = MI->getOperand(OpIdx++);157      unsigned NumCatches = Op.getImm();158      for (unsigned I = 0; I < NumCatches; I++) {159        int64_t CatchOpcode = MI->getOperand(OpIdx++).getImm();160        if (CatchOpcode == wasm::WASM_OPCODE_CATCH ||161            CatchOpcode == wasm::WASM_OPCODE_CATCH_REF)162          OpIdx++; // Skip tag163        PrintBranchAnnotation(MI->getOperand(OpIdx++), Printed);164      }165      ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));166      return;167    }168 169    case WebAssembly::END_LOOP:170    case WebAssembly::END_LOOP_S:171      if (ControlFlowStack.empty()) {172        printAnnotation(OS, "End marker mismatch!");173      } else {174        ControlFlowStack.pop_back();175      }176      return;177 178    case WebAssembly::END_BLOCK:179    case WebAssembly::END_BLOCK_S:180    case WebAssembly::END_TRY_TABLE:181    case WebAssembly::END_TRY_TABLE_S:182      if (ControlFlowStack.empty()) {183        printAnnotation(OS, "End marker mismatch!");184      } else {185        printAnnotation(186            OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');187      }188      return;189 190    case WebAssembly::END_TRY:191    case WebAssembly::END_TRY_S:192      if (ControlFlowStack.empty() || EHInstStack.empty()) {193        printAnnotation(OS, "End marker mismatch!");194      } else {195        printAnnotation(196            OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');197        EHInstStack.pop_back();198      }199      return;200 201    case WebAssembly::CATCH_LEGACY:202    case WebAssembly::CATCH_LEGACY_S:203    case WebAssembly::CATCH_ALL_LEGACY:204    case WebAssembly::CATCH_ALL_LEGACY_S:205      // There can be multiple catch instructions for one try instruction, so206      // we print a label only for the first 'catch' label.207      if (EHInstStack.empty()) {208        printAnnotation(OS, "try-catch mismatch!");209      } else if (EHInstStack.back() == CATCH_ALL_LEGACY) {210        printAnnotation(OS, "catch/catch_all cannot occur after catch_all");211      } else if (EHInstStack.back() == TRY) {212        if (TryStack.empty()) {213          printAnnotation(OS, "try-catch mismatch!");214        } else {215          printAnnotation(OS, "catch" + utostr(TryStack.pop_back_val()) + ':');216        }217        EHInstStack.pop_back();218        if (Opc == WebAssembly::CATCH_LEGACY ||219            Opc == WebAssembly::CATCH_LEGACY_S) {220          EHInstStack.push_back(CATCH_LEGACY);221        } else {222          EHInstStack.push_back(CATCH_ALL_LEGACY);223        }224      }225      return;226 227    case WebAssembly::RETHROW:228    case WebAssembly::RETHROW_S:229      // 'rethrow' rethrows to the nearest enclosing catch scope, if any. If230      // there's no enclosing catch scope, it throws up to the caller.231      if (TryStack.empty()) {232        printAnnotation(OS, "to caller");233      } else {234        printAnnotation(OS, "down to catch" + utostr(TryStack.back()));235      }236      return;237 238    case WebAssembly::DELEGATE:239    case WebAssembly::DELEGATE_S:240      if (ControlFlowStack.empty() || TryStack.empty() || EHInstStack.empty()) {241        printAnnotation(OS, "try-delegate mismatch!");242      } else {243        // 'delegate' is244        // 1. A marker for the end of block label245        // 2. A destination for throwing instructions246        // 3. An instruction that itself rethrows to another 'catch'247        assert(ControlFlowStack.back().first == TryStack.back());248        std::string Label = "label/catch" +249                            utostr(ControlFlowStack.pop_back_val().first) +250                            ": ";251        TryStack.pop_back();252        EHInstStack.pop_back();253        uint64_t Depth = MI->getOperand(0).getImm();254        if (Depth >= ControlFlowStack.size()) {255          Label += "to caller";256        } else {257          const auto &Pair = ControlFlowStack.rbegin()[Depth];258          if (Pair.second)259            printAnnotation(OS, "delegate cannot target a loop");260          else261            Label += "down to catch" + utostr(Pair.first);262        }263        printAnnotation(OS, Label);264      }265      return;266    }267 268    // Annotate any control flow label references.269 270    unsigned NumFixedOperands = Desc.NumOperands;271    SmallSet<uint64_t, 8> Printed;272    for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) {273      // See if this operand denotes a basic block target.274      if (I < NumFixedOperands) {275        // A non-variable_ops operand, check its type.276        if (Desc.operands()[I].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)277          continue;278      } else {279        // A variable_ops operand, which currently can be immediates (used in280        // br_table) which are basic block targets, or for call instructions281        // when using -wasm-keep-registers (in which case they are registers,282        // and should not be processed).283        if (!MI->getOperand(I).isImm())284          continue;285      }286      PrintBranchAnnotation(MI->getOperand(I), Printed);287    }288  }289}290 291static std::string toString(const APFloat &FP) {292  // Print NaNs with custom payloads specially.293  if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&294      !FP.bitwiseIsEqual(295          APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {296    APInt AI = FP.bitcastToAPInt();297    return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +298           utohexstr(AI.getZExtValue() &299                         (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)300                                                 : INT64_C(0x000fffffffffffff)),301                     /*LowerCase=*/true);302  }303 304  // Use C99's hexadecimal floating-point representation.305  static const size_t BufBytes = 128;306  char Buf[BufBytes];307  auto Written = FP.convertToHexString(308      Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven);309  (void)Written;310  assert(Written != 0);311  assert(Written < BufBytes);312  return Buf;313}314 315void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,316                                          raw_ostream &O, bool IsVariadicDef) {317  const MCOperand &Op = MI->getOperand(OpNo);318  if (Op.isReg()) {319    const MCInstrDesc &Desc = MII.get(MI->getOpcode());320    MCRegister WAReg = Op.getReg();321    if (int(WAReg.id()) >= 0)322      printRegName(O, WAReg);323    else if (OpNo >= Desc.getNumDefs() && !IsVariadicDef)324      O << "$pop" << WebAssembly::getWARegStackId(WAReg);325    else if (WAReg != WebAssembly::UnusedReg)326      O << "$push" << WebAssembly::getWARegStackId(WAReg);327    else328      O << "$drop";329    // Add a '=' suffix if this is a def.330    if (OpNo < MII.get(MI->getOpcode()).getNumDefs() || IsVariadicDef)331      O << '=';332  } else if (Op.isImm()) {333    O << Op.getImm();334  } else if (Op.isSFPImm()) {335    O << ::toString(APFloat(APFloat::IEEEsingle(), APInt(32, Op.getSFPImm())));336  } else if (Op.isDFPImm()) {337    O << ::toString(APFloat(APFloat::IEEEdouble(), APInt(64, Op.getDFPImm())));338  } else {339    assert(Op.isExpr() && "unknown operand kind in printOperand");340    // call_indirect instructions have a TYPEINDEX operand that we print341    // as a signature here, such that the assembler can recover this342    // information.343    auto SRE = static_cast<const MCSymbolRefExpr *>(Op.getExpr());344    if (SRE->getSpecifier() == WebAssembly::S_TYPEINDEX) {345      auto &Sym = static_cast<const MCSymbolWasm &>(SRE->getSymbol());346      O << WebAssembly::signatureToString(Sym.getSignature());347    } else {348      MAI.printExpr(O, *Op.getExpr());349    }350  }351}352 353void WebAssemblyInstPrinter::printBrList(const MCInst *MI, unsigned OpNo,354                                         raw_ostream &O) {355  O << "{";356  for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {357    if (I != OpNo)358      O << ", ";359    O << MI->getOperand(I).getImm();360  }361  O << "}";362}363 364void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,365                                                            unsigned OpNo,366                                                            raw_ostream &O) {367  int64_t Imm = MI->getOperand(OpNo).getImm();368  if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))369    return;370  O << ":p2align=" << Imm;371}372 373void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI,374                                                              unsigned OpNo,375                                                              raw_ostream &O) {376  const MCOperand &Op = MI->getOperand(OpNo);377  if (Op.isImm()) {378    auto Imm = static_cast<unsigned>(Op.getImm());379    if (Imm != wasm::WASM_TYPE_NORESULT)380      O << WebAssembly::anyTypeToString(Imm);381  } else {382    auto Expr = cast<MCSymbolRefExpr>(Op.getExpr());383    auto *Sym = static_cast<const MCSymbolWasm *>(&Expr->getSymbol());384    if (Sym->getSignature()) {385      O << WebAssembly::signatureToString(Sym->getSignature());386    } else {387      // Disassembler does not currently produce a signature388      O << "unknown_type";389    }390  }391}392 393void WebAssemblyInstPrinter::printCatchList(const MCInst *MI, unsigned OpNo,394                                            raw_ostream &O) {395  unsigned OpIdx = OpNo;396  const MCOperand &Op = MI->getOperand(OpIdx++);397  unsigned NumCatches = Op.getImm();398 399  auto PrintTagOp = [&](const MCOperand &Op) {400    const MCSymbolRefExpr *TagExpr = nullptr;401    const MCSymbol *TagSym = nullptr;402    if (Op.isExpr()) {403      TagExpr = cast<MCSymbolRefExpr>(Op.getExpr());404      TagSym = &TagExpr->getSymbol();405      O << TagSym->getName() << " ";406    } else {407      // When instructions are parsed from the disassembler, we have an408      // immediate tag index and not a tag expr409      O << Op.getImm() << " ";410    }411  };412 413  for (unsigned I = 0; I < NumCatches; I++) {414    const MCOperand &Op = MI->getOperand(OpIdx++);415    O << "(";416    switch (Op.getImm()) {417    case wasm::WASM_OPCODE_CATCH:418      O << "catch ";419      PrintTagOp(MI->getOperand(OpIdx++));420      break;421    case wasm::WASM_OPCODE_CATCH_REF:422      O << "catch_ref ";423      PrintTagOp(MI->getOperand(OpIdx++));424      break;425    case wasm::WASM_OPCODE_CATCH_ALL:426      O << "catch_all ";427      break;428    case wasm::WASM_OPCODE_CATCH_ALL_REF:429      O << "catch_all_ref ";430      break;431    }432    O << MI->getOperand(OpIdx++).getImm(); // destination433    O << ")";434    if (I < NumCatches - 1)435      O << " ";436  }437}438