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
30using namespace llvm;
31
32#define DEBUG_TYPE "mccodeemitter"
33
34STATISTIC(MCNumEmitted, "Number of MC instructions emitted.");
35STATISTIC(MCNumFixups, "Number of MC fixups created.");
36
37namespace {
38class 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
50public:
51 WebAssemblyMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx)
52 : MCII(MCII), Ctx{Ctx} {}
53};
54} // end anonymous namespace
55
56MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII,
57 MCContext &Ctx) {
58 return new WebAssemblyMCCodeEmitter(MCII, Ctx);
59}
60
61void 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(Value: uint8_t(Binary), OS);
73 } else if (Binary < (1 << 24)) {
74 OS << uint8_t(Binary >> 16);
75 encodeULEB128(Value: 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(Value: MI.getNumOperands() - 1, OS);
87 if (Opcode == WebAssembly::BR_TABLE_I32 ||
88 Opcode == WebAssembly::BR_TABLE_I64)
89 encodeULEB128(Value: 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: 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(Value: int32_t(MO.getImm()), OS);
105 break;
106 case WebAssembly::OPERAND_OFFSET32:
107 encodeULEB128(Value: uint32_t(MO.getImm()), OS);
108 break;
109 case WebAssembly::OPERAND_I64IMM:
110 encodeSLEB128(Value: 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&: OS, value: MO.getImm(),
115 endian: llvm::endianness::little);
116 break;
117 case WebAssembly::OPERAND_VEC_I16IMM:
118 support::endian::write<uint16_t>(os&: OS, value: MO.getImm(),
119 endian: llvm::endianness::little);
120 break;
121 case WebAssembly::OPERAND_VEC_I32IMM:
122 support::endian::write<uint32_t>(os&: OS, value: MO.getImm(),
123 endian: llvm::endianness::little);
124 break;
125 case WebAssembly::OPERAND_VEC_I64IMM:
126 support::endian::write<uint64_t>(os&: OS, value: MO.getImm(),
127 endian: llvm::endianness::little);
128 break;
129 case WebAssembly::OPERAND_GLOBAL:
130 Ctx.reportError(
131 L: SMLoc(),
132 Msg: Twine("Wasm globals should only be accessed symbolically!"));
133 break;
134 default:
135 encodeULEB128(Value: 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(Value: 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&: OS, value: F, endian: llvm::endianness::little);
149 } else if (MO.isDFPImm()) {
150 uint64_t D = MO.getDFPImm();
151 support::endian::write<uint64_t>(os&: OS, value: D, endian: 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(Elt: MCFixup::create(Offset: OS.tell() - Start, Value: MO.getExpr(),
187 Kind: FixupKind, Loc: MI.getLoc()));
188 ++MCNumFixups;
189 encodeULEB128(Value: 0, OS, PadTo: 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