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(), 116 llvm::endianness::little); 117 break; 118 case WebAssembly::OPERAND_VEC_I16IMM: 119 support::endian::write<uint16_t>(OS, MO.getImm(), 120 llvm::endianness::little); 121 break; 122 case WebAssembly::OPERAND_VEC_I32IMM: 123 support::endian::write<uint32_t>(OS, MO.getImm(), 124 llvm::endianness::little); 125 break; 126 case WebAssembly::OPERAND_VEC_I64IMM: 127 support::endian::write<uint64_t>(OS, MO.getImm(), 128 llvm::endianness::little); 129 break; 130 case WebAssembly::OPERAND_GLOBAL: 131 Ctx.reportError( 132 SMLoc(), 133 Twine("Wasm globals should only be accessed symbolically!")); 134 break; 135 default: 136 encodeULEB128(uint64_t(MO.getImm()), OS); 137 } 138 } else { 139 encodeULEB128(uint64_t(MO.getImm()), OS); 140 } 141 142 } else if (MO.isSFPImm()) { 143 uint32_t F = MO.getSFPImm(); 144 support::endian::write<uint32_t>(OS, F, llvm::endianness::little); 145 } else if (MO.isDFPImm()) { 146 uint64_t D = MO.getDFPImm(); 147 support::endian::write<uint64_t>(OS, D, llvm::endianness::little); 148 } else if (MO.isExpr()) { 149 const MCOperandInfo &Info = Desc.operands()[I]; 150 llvm::MCFixupKind FixupKind; 151 size_t PaddedSize = 5; 152 switch (Info.OperandType) { 153 case WebAssembly::OPERAND_I32IMM: 154 FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i32); 155 break; 156 case WebAssembly::OPERAND_I64IMM: 157 FixupKind = MCFixupKind(WebAssembly::fixup_sleb128_i64); 158 PaddedSize = 10; 159 break; 160 case WebAssembly::OPERAND_FUNCTION32: 161 case WebAssembly::OPERAND_TABLE: 162 case WebAssembly::OPERAND_OFFSET32: 163 case WebAssembly::OPERAND_SIGNATURE: 164 case WebAssembly::OPERAND_TYPEINDEX: 165 case WebAssembly::OPERAND_GLOBAL: 166 case WebAssembly::OPERAND_TAG: 167 FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i32); 168 break; 169 case WebAssembly::OPERAND_OFFSET64: 170 FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i64); 171 PaddedSize = 10; 172 break; 173 default: 174 llvm_unreachable("unexpected symbolic operand kind"); 175 } 176 Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(), 177 FixupKind, MI.getLoc())); 178 ++MCNumFixups; 179 encodeULEB128(0, OS, PaddedSize); 180 } else { 181 llvm_unreachable("unexpected operand kind"); 182 } 183 } 184 185 ++MCNumEmitted; // Keep track of the # of mi's emitted. 186 } 187 188 #include "WebAssemblyGenMCCodeEmitter.inc" 189