Home | History | Annotate | Line # | Download | only in MCTargetDesc
      1 //=- WebAssemblyMCCodeEmitter.cpp - Convert WebAssembly code to machine code -//
      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 /// This file implements the WebAssemblyMCCodeEmitter class.
     11 ///
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "MCTargetDesc/WebAssemblyFixupKinds.h"
     15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
     16 #include "llvm/ADT/STLExtras.h"
     17 #include "llvm/ADT/Statistic.h"
     18 #include "llvm/MC/MCCodeEmitter.h"
     19 #include "llvm/MC/MCFixup.h"
     20 #include "llvm/MC/MCInst.h"
     21 #include "llvm/MC/MCInstrInfo.h"
     22 #include "llvm/MC/MCRegisterInfo.h"
     23 #include "llvm/MC/MCSubtargetInfo.h"
     24 #include "llvm/MC/MCSymbol.h"
     25 #include "llvm/Support/Debug.h"
     26 #include "llvm/Support/EndianStream.h"
     27 #include "llvm/Support/LEB128.h"
     28 #include "llvm/Support/raw_ostream.h"
     29 
     30 using namespace llvm;
     31 
     32 #define DEBUG_TYPE "mccodeemitter"
     33 
     34 STATISTIC(MCNumEmitted, "Number of MC instructions emitted.");
     35 STATISTIC(MCNumFixups, "Number of MC fixups created.");
     36 
     37 namespace {
     38 class WebAssemblyMCCodeEmitter final : public MCCodeEmitter {
     39   const MCInstrInfo &MCII;
     40 
     41   // Implementation generated by tablegen.
     42   uint64_t getBinaryCodeForInstr(const MCInst &MI,
     43                                  SmallVectorImpl<MCFixup> &Fixups,
     44                                  const MCSubtargetInfo &STI) const;
     45 
     46   void encodeInstruction(const MCInst &MI, raw_ostream &OS,
     47                          SmallVectorImpl<MCFixup> &Fixups,
     48                          const MCSubtargetInfo &STI) const override;
     49 
     50 public:
     51   WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) : MCII(MCII) {}
     52 };
     53 } // end anonymous namespace
     54 
     55 MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) {
     56   return new WebAssemblyMCCodeEmitter(MCII);
     57 }
     58 
     59 void WebAssemblyMCCodeEmitter::encodeInstruction(
     60     const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups,
     61     const MCSubtargetInfo &STI) const {
     62   uint64_t Start = OS.tell();
     63 
     64   uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
     65   if (Binary < (1 << 8)) {
     66     OS << uint8_t(Binary);
     67   } else if (Binary < (1 << 16)) {
     68     OS << uint8_t(Binary >> 8);
     69     encodeULEB128(uint8_t(Binary), OS);
     70   } else if (Binary < (1 << 24)) {
     71     OS << uint8_t(Binary >> 16);
     72     encodeULEB128(uint16_t(Binary), OS);
     73   } else {
     74     llvm_unreachable("Very large (prefix + 3 byte) opcodes not supported");
     75   }
     76 
     77   // For br_table instructions, encode the size of the table. In the MCInst,
     78   // there's an index operand (if not a stack instruction), one operand for
     79   // each table entry, and the default operand.
     80   if (MI.getOpcode() == WebAssembly::BR_TABLE_I32_S ||
     81       MI.getOpcode() == WebAssembly::BR_TABLE_I64_S)
     82     encodeULEB128(MI.getNumOperands() - 1, OS);
     83   if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 ||
     84       MI.getOpcode() == WebAssembly::BR_TABLE_I64)
     85     encodeULEB128(MI.getNumOperands() - 2, OS);
     86 
     87   const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
     88   for (unsigned I = 0, E = MI.getNumOperands(); I < E; ++I) {
     89     const MCOperand &MO = MI.getOperand(I);
     90     if (MO.isReg()) {
     91       /* nothing to encode */
     92 
     93     } else if (MO.isImm()) {
     94       if (I < Desc.getNumOperands()) {
     95         const MCOperandInfo &Info = Desc.OpInfo[I];
     96         LLVM_DEBUG(dbgs() << "Encoding immediate: type="
     97                           << int(Info.OperandType) << "\n");
     98         switch (Info.OperandType) {
     99         case WebAssembly::OPERAND_I32IMM:
    100           encodeSLEB128(int32_t(MO.getImm()), OS);
    101           break;
    102         case WebAssembly::OPERAND_OFFSET32:
    103           encodeULEB128(uint32_t(MO.getImm()), OS);
    104           break;
    105         case WebAssembly::OPERAND_I64IMM:
    106           encodeSLEB128(int64_t(MO.getImm()), OS);
    107           break;
    108         case WebAssembly::OPERAND_SIGNATURE:
    109         case WebAssembly::OPERAND_HEAPTYPE:
    110           OS << uint8_t(MO.getImm());
    111           break;
    112         case WebAssembly::OPERAND_VEC_I8IMM:
    113           support::endian::write<uint8_t>(OS, MO.getImm(), support::little);
    114           break;
    115         case WebAssembly::OPERAND_VEC_I16IMM:
    116           support::endian::write<uint16_t>(OS, MO.getImm(), support::little);
    117           break;
    118         case WebAssembly::OPERAND_VEC_I32IMM:
    119           support::endian::write<uint32_t>(OS, MO.getImm(), support::little);
    120           break;
    121         case WebAssembly::OPERAND_VEC_I64IMM:
    122           support::endian::write<uint64_t>(OS, MO.getImm(), support::little);
    123           break;
    124         case WebAssembly::OPERAND_GLOBAL:
    125           llvm_unreachable("wasm globals should only be accessed symbolicly");
    126         default:
    127           encodeULEB128(uint64_t(MO.getImm()), OS);
    128         }
    129       } else {
    130         encodeULEB128(uint64_t(MO.getImm()), OS);
    131       }
    132 
    133     } else if (MO.isSFPImm()) {
    134       uint32_t F = MO.getSFPImm();
    135       support::endian::write<uint32_t>(OS, F, support::little);
    136     } else if (MO.isDFPImm()) {
    137       uint64_t D = MO.getDFPImm();
    138       support::endian::write<uint64_t>(OS, D, support::little);
    139     } else if (MO.isExpr()) {
    140       const MCOperandInfo &Info = Desc.OpInfo[I];
    141       llvm::MCFixupKind FixupKind;
    142       size_t PaddedSize = 5;
    143       switch (Info.OperandType) {
    144       case WebAssembly::OPERAND_I32IMM:
    145         FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i32);
    146         break;
    147       case WebAssembly::OPERAND_I64IMM:
    148         FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i64);
    149         PaddedSize = 10;
    150         break;
    151       case WebAssembly::OPERAND_FUNCTION32:
    152       case WebAssembly::OPERAND_TABLE:
    153       case WebAssembly::OPERAND_OFFSET32:
    154       case WebAssembly::OPERAND_SIGNATURE:
    155       case WebAssembly::OPERAND_TYPEINDEX:
    156       case WebAssembly::OPERAND_GLOBAL:
    157       case WebAssembly::OPERAND_EVENT:
    158         FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i32);
    159         break;
    160       case WebAssembly::OPERAND_OFFSET64:
    161         FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i64);
    162         PaddedSize = 10;
    163         break;
    164       default:
    165         llvm_unreachable("unexpected symbolic operand kind");
    166       }
    167       Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(),
    168                                        FixupKind, MI.getLoc()));
    169       ++MCNumFixups;
    170       encodeULEB128(0, OS, PaddedSize);
    171     } else {
    172       llvm_unreachable("unexpected operand kind");
    173     }
    174   }
    175 
    176   ++MCNumEmitted; // Keep track of the # of mi's emitted.
    177 }
    178 
    179 #include "WebAssemblyGenMCCodeEmitter.inc"
    180