//=- WebAssemblyMCCodeEmitter.cpp - Convert WebAssembly code to machine code -// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file implements the WebAssemblyMCCodeEmitter class. /// //===----------------------------------------------------------------------===// #include "MCTargetDesc/WebAssemblyFixupKinds.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/Debug.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; #define DEBUG_TYPE "mccodeemitter" STATISTIC(MCNumEmitted, "Number of MC instructions emitted."); STATISTIC(MCNumFixups, "Number of MC fixups created."); namespace { class WebAssemblyMCCodeEmitter final : public MCCodeEmitter { const MCInstrInfo &MCII; // Implementation generated by tablegen. uint64_t getBinaryCodeForInstr(const MCInst &MI, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; void encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const override; public: WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) : MCII(MCII) {} }; } // end anonymous namespace MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) { return new WebAssemblyMCCodeEmitter(MCII); } void WebAssemblyMCCodeEmitter::encodeInstruction( const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { uint64_t Start = OS.tell(); uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI); if (Binary <= UINT8_MAX) { OS << uint8_t(Binary); } else { assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet"); OS << uint8_t(Binary >> 8); encodeULEB128(uint8_t(Binary), OS); } // For br_table instructions, encode the size of the table. In the MCInst, // there's an index operand (if not a stack instruction), one operand for // each table entry, and the default operand. if (MI.getOpcode() == WebAssembly::BR_TABLE_I32_S || MI.getOpcode() == WebAssembly::BR_TABLE_I64_S) encodeULEB128(MI.getNumOperands() - 1, OS); if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 || MI.getOpcode() == WebAssembly::BR_TABLE_I64) encodeULEB128(MI.getNumOperands() - 2, OS); const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); for (unsigned I = 0, E = MI.getNumOperands(); I < E; ++I) { const MCOperand &MO = MI.getOperand(I); if (MO.isReg()) { /* nothing to encode */ } else if (MO.isImm()) { if (I < Desc.getNumOperands()) { const MCOperandInfo &Info = Desc.OpInfo[I]; LLVM_DEBUG(dbgs() << "Encoding immediate: type=" << int(Info.OperandType) << "\n"); switch (Info.OperandType) { case WebAssembly::OPERAND_I32IMM: encodeSLEB128(int32_t(MO.getImm()), OS); break; case WebAssembly::OPERAND_OFFSET32: encodeULEB128(uint32_t(MO.getImm()), OS); break; case WebAssembly::OPERAND_I64IMM: encodeSLEB128(int64_t(MO.getImm()), OS); break; case WebAssembly::OPERAND_SIGNATURE: OS << uint8_t(MO.getImm()); break; case WebAssembly::OPERAND_VEC_I8IMM: support::endian::write(OS, MO.getImm(), support::little); break; case WebAssembly::OPERAND_VEC_I16IMM: support::endian::write(OS, MO.getImm(), support::little); break; case WebAssembly::OPERAND_VEC_I32IMM: support::endian::write(OS, MO.getImm(), support::little); break; case WebAssembly::OPERAND_VEC_I64IMM: support::endian::write(OS, MO.getImm(), support::little); break; case WebAssembly::OPERAND_GLOBAL: llvm_unreachable("wasm globals should only be accessed symbolicly"); default: encodeULEB128(uint64_t(MO.getImm()), OS); } } else { encodeULEB128(uint64_t(MO.getImm()), OS); } } else if (MO.isFPImm()) { const MCOperandInfo &Info = Desc.OpInfo[I]; if (Info.OperandType == WebAssembly::OPERAND_F32IMM) { // TODO: MC converts all floating point immediate operands to double. // This is fine for numeric values, but may cause NaNs to change bits. auto F = float(MO.getFPImm()); support::endian::write(OS, F, support::little); } else { assert(Info.OperandType == WebAssembly::OPERAND_F64IMM); double D = MO.getFPImm(); support::endian::write(OS, D, support::little); } } else if (MO.isExpr()) { const MCOperandInfo &Info = Desc.OpInfo[I]; llvm::MCFixupKind FixupKind; size_t PaddedSize = 5; switch (Info.OperandType) { case WebAssembly::OPERAND_I32IMM: FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i32); break; case WebAssembly::OPERAND_I64IMM: FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i64); PaddedSize = 10; break; case WebAssembly::OPERAND_FUNCTION32: case WebAssembly::OPERAND_OFFSET32: case WebAssembly::OPERAND_SIGNATURE: case WebAssembly::OPERAND_TYPEINDEX: case WebAssembly::OPERAND_GLOBAL: case WebAssembly::OPERAND_EVENT: FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i32); break; default: llvm_unreachable("unexpected symbolic operand kind"); } Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(), FixupKind, MI.getLoc())); ++MCNumFixups; encodeULEB128(0, OS, PaddedSize); } else { llvm_unreachable("unexpected operand kind"); } } ++MCNumEmitted; // Keep track of the # of mi's emitted. } #include "WebAssemblyGenMCCodeEmitter.inc"