1 //=- WebAssemblyMCCodeEmitter.cpp - Convert WebAssembly code to machine code -//
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
7 //===----------------------------------------------------------------------===//
10 /// This file implements the WebAssemblyMCCodeEmitter class.
12 //===----------------------------------------------------------------------===//
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/MCFixup.h"
20 #include "llvm/MC/MCInst.h"
21 #include "llvm/MC/MCInstrInfo.h"
22 #include "llvm/MC/MCRegisterInfo.h"
23 #include "llvm/MC/MCSubtargetInfo.h"
24 #include "llvm/MC/MCSymbol.h"
25 #include "llvm/Support/Debug.h"
26 #include "llvm/Support/EndianStream.h"
27 #include "llvm/Support/LEB128.h"
28 #include "llvm/Support/raw_ostream.h"
32 #define DEBUG_TYPE "mccodeemitter"
34 STATISTIC(MCNumEmitted
, "Number of MC instructions emitted.");
35 STATISTIC(MCNumFixups
, "Number of MC fixups created.");
38 class WebAssemblyMCCodeEmitter final
: public MCCodeEmitter
{
39 const MCInstrInfo
&MCII
;
41 // Implementation generated by tablegen.
42 uint64_t getBinaryCodeForInstr(const MCInst
&MI
,
43 SmallVectorImpl
<MCFixup
> &Fixups
,
44 const MCSubtargetInfo
&STI
) const;
46 void encodeInstruction(const MCInst
&MI
, raw_ostream
&OS
,
47 SmallVectorImpl
<MCFixup
> &Fixups
,
48 const MCSubtargetInfo
&STI
) const override
;
51 WebAssemblyMCCodeEmitter(const MCInstrInfo
&MCII
) : MCII(MCII
) {}
53 } // end anonymous namespace
55 MCCodeEmitter
*llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo
&MCII
) {
56 return new WebAssemblyMCCodeEmitter(MCII
);
59 void WebAssemblyMCCodeEmitter::encodeInstruction(
60 const MCInst
&MI
, raw_ostream
&OS
, SmallVectorImpl
<MCFixup
> &Fixups
,
61 const MCSubtargetInfo
&STI
) const {
62 uint64_t Start
= OS
.tell();
64 uint64_t Binary
= getBinaryCodeForInstr(MI
, Fixups
, STI
);
65 if (Binary
<= UINT8_MAX
) {
66 OS
<< uint8_t(Binary
);
68 assert(Binary
<= UINT16_MAX
&& "Several-byte opcodes not supported yet");
69 OS
<< uint8_t(Binary
>> 8);
70 encodeULEB128(uint8_t(Binary
), OS
);
73 // For br_table instructions, encode the size of the table. In the MCInst,
74 // there's an index operand (if not a stack instruction), one operand for
75 // each table entry, and the default operand.
76 if (MI
.getOpcode() == WebAssembly::BR_TABLE_I32_S
||
77 MI
.getOpcode() == WebAssembly::BR_TABLE_I64_S
)
78 encodeULEB128(MI
.getNumOperands() - 1, OS
);
79 if (MI
.getOpcode() == WebAssembly::BR_TABLE_I32
||
80 MI
.getOpcode() == WebAssembly::BR_TABLE_I64
)
81 encodeULEB128(MI
.getNumOperands() - 2, OS
);
83 const MCInstrDesc
&Desc
= MCII
.get(MI
.getOpcode());
84 for (unsigned I
= 0, E
= MI
.getNumOperands(); I
< E
; ++I
) {
85 const MCOperand
&MO
= MI
.getOperand(I
);
87 /* nothing to encode */
89 } else if (MO
.isImm()) {
90 if (I
< Desc
.getNumOperands()) {
91 const MCOperandInfo
&Info
= Desc
.OpInfo
[I
];
92 LLVM_DEBUG(dbgs() << "Encoding immediate: type="
93 << int(Info
.OperandType
) << "\n");
94 switch (Info
.OperandType
) {
95 case WebAssembly::OPERAND_I32IMM
:
96 encodeSLEB128(int32_t(MO
.getImm()), OS
);
98 case WebAssembly::OPERAND_OFFSET32
:
99 encodeULEB128(uint32_t(MO
.getImm()), OS
);
101 case WebAssembly::OPERAND_I64IMM
:
102 encodeSLEB128(int64_t(MO
.getImm()), OS
);
104 case WebAssembly::OPERAND_SIGNATURE
:
105 OS
<< uint8_t(MO
.getImm());
107 case WebAssembly::OPERAND_VEC_I8IMM
:
108 support::endian::write
<uint8_t>(OS
, MO
.getImm(), support::little
);
110 case WebAssembly::OPERAND_VEC_I16IMM
:
111 support::endian::write
<uint16_t>(OS
, MO
.getImm(), support::little
);
113 case WebAssembly::OPERAND_VEC_I32IMM
:
114 support::endian::write
<uint32_t>(OS
, MO
.getImm(), support::little
);
116 case WebAssembly::OPERAND_VEC_I64IMM
:
117 support::endian::write
<uint64_t>(OS
, MO
.getImm(), support::little
);
119 case WebAssembly::OPERAND_GLOBAL
:
120 llvm_unreachable("wasm globals should only be accessed symbolicly");
122 encodeULEB128(uint64_t(MO
.getImm()), OS
);
125 encodeULEB128(uint64_t(MO
.getImm()), OS
);
128 } else if (MO
.isFPImm()) {
129 const MCOperandInfo
&Info
= Desc
.OpInfo
[I
];
130 if (Info
.OperandType
== WebAssembly::OPERAND_F32IMM
) {
131 // TODO: MC converts all floating point immediate operands to double.
132 // This is fine for numeric values, but may cause NaNs to change bits.
133 auto F
= float(MO
.getFPImm());
134 support::endian::write
<float>(OS
, F
, support::little
);
136 assert(Info
.OperandType
== WebAssembly::OPERAND_F64IMM
);
137 double D
= MO
.getFPImm();
138 support::endian::write
<double>(OS
, D
, support::little
);
141 } else if (MO
.isExpr()) {
142 const MCOperandInfo
&Info
= Desc
.OpInfo
[I
];
143 llvm::MCFixupKind FixupKind
;
144 size_t PaddedSize
= 5;
145 switch (Info
.OperandType
) {
146 case WebAssembly::OPERAND_I32IMM
:
147 FixupKind
= MCFixupKind(WebAssembly::fixup_sleb128_i32
);
149 case WebAssembly::OPERAND_I64IMM
:
150 FixupKind
= MCFixupKind(WebAssembly::fixup_sleb128_i64
);
153 case WebAssembly::OPERAND_FUNCTION32
:
154 case WebAssembly::OPERAND_OFFSET32
:
155 case WebAssembly::OPERAND_SIGNATURE
:
156 case WebAssembly::OPERAND_TYPEINDEX
:
157 case WebAssembly::OPERAND_GLOBAL
:
158 case WebAssembly::OPERAND_EVENT
:
159 FixupKind
= MCFixupKind(WebAssembly::fixup_uleb128_i32
);
162 llvm_unreachable("unexpected symbolic operand kind");
164 Fixups
.push_back(MCFixup::create(OS
.tell() - Start
, MO
.getExpr(),
165 FixupKind
, MI
.getLoc()));
167 encodeULEB128(0, OS
, PaddedSize
);
169 llvm_unreachable("unexpected operand kind");
173 ++MCNumEmitted
; // Keep track of the # of mi's emitted.
176 #include "WebAssemblyGenMCCodeEmitter.inc"