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