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