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:
WebAssemblyMCCodeEmitter(const MCInstrInfo & MCII,MCContext & Ctx)51 WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx)
52 : MCII(MCII), Ctx{Ctx} {}
53 };
54 } // end anonymous namespace
55
createWebAssemblyMCCodeEmitter(const MCInstrInfo & MCII,MCContext & Ctx)56 MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII,
57 MCContext &Ctx) {
58 return new WebAssemblyMCCodeEmitter(MCII, Ctx);
59 }
60
encodeInstruction(const MCInst & MI,SmallVectorImpl<char> & CB,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const61 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 = 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