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/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 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 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 50 public: 51 WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) 52 : MCII(MCII), Ctx{Ctx} {} 53 }; 54 } // end anonymous namespace 55 56 MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII, 57 MCContext &Ctx) { 58 return new WebAssemblyMCCodeEmitter(MCII, Ctx); 59 } 60 61 void 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 for 82 // 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(int64_t(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 or 139 // try_table's operands (# of catch clauses, catch sub-opcodes, or catch 140 // 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 = MCFixupKind(WebAssembly::fixup_sleb128_i32); 160 break; 161 case WebAssembly::OPERAND_I64IMM: 162 FixupKind = MCFixupKind(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 = MCFixupKind(WebAssembly::fixup_uleb128_i32); 173 break; 174 case WebAssembly::OPERAND_OFFSET64: 175 FixupKind = MCFixupKind(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 = MCFixupKind(WebAssembly::fixup_uleb128_i32); 185 } 186 Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(), 187 FixupKind, MI.getLoc())); 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