1 //=- WebAssemblyMCCodeEmitter.cpp - Convert WebAssembly code to machine code -//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// This file implements the WebAssemblyMCCodeEmitter class.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "MCTargetDesc/WebAssemblyFixupKinds.h"
16 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/Statistic.h"
19 #include "llvm/MC/MCCodeEmitter.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/raw_ostream.h"
30
31 using namespace llvm;
32
33 #define DEBUG_TYPE "mccodeemitter"
34
35 STATISTIC(MCNumEmitted, "Number of MC instructions emitted.");
36 STATISTIC(MCNumFixups, "Number of MC fixups created.");
37
38 namespace {
39 class WebAssemblyMCCodeEmitter final : public MCCodeEmitter {
40 const MCInstrInfo &MCII;
41
42 // Implementation generated by tablegen.
43 uint64_t getBinaryCodeForInstr(const MCInst &MI,
44 SmallVectorImpl<MCFixup> &Fixups,
45 const MCSubtargetInfo &STI) const;
46
47 void encodeInstruction(const MCInst &MI, raw_ostream &OS,
48 SmallVectorImpl<MCFixup> &Fixups,
49 const MCSubtargetInfo &STI) const override;
50
51 public:
WebAssemblyMCCodeEmitter(const MCInstrInfo & mcii)52 WebAssemblyMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {}
53 };
54 } // end anonymous namespace
55
createWebAssemblyMCCodeEmitter(const MCInstrInfo & MCII)56 MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) {
57 return new WebAssemblyMCCodeEmitter(MCII);
58 }
59
encodeInstruction(const MCInst & MI,raw_ostream & OS,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const60 void WebAssemblyMCCodeEmitter::encodeInstruction(
61 const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups,
62 const MCSubtargetInfo &STI) const {
63 uint64_t Start = OS.tell();
64
65 uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
66 if (Binary <= UINT8_MAX) {
67 OS << uint8_t(Binary);
68 } else {
69 assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet");
70 OS << uint8_t(Binary >> 8)
71 << uint8_t(Binary);
72 }
73
74 // For br_table instructions, encode the size of the table. In the MCInst,
75 // there's an index operand, one operand for each table entry, and the
76 // default operand.
77 if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 ||
78 MI.getOpcode() == WebAssembly::BR_TABLE_I64)
79 encodeULEB128(MI.getNumOperands() - 2, OS);
80
81 const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
82 for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {
83 const MCOperand &MO = MI.getOperand(i);
84 if (MO.isReg()) {
85 /* nothing to encode */
86 } else if (MO.isImm()) {
87 if (i < Desc.getNumOperands()) {
88 assert(Desc.TSFlags == 0 &&
89 "WebAssembly non-variable_ops don't use TSFlags");
90 const MCOperandInfo &Info = Desc.OpInfo[i];
91 LLVM_DEBUG(dbgs() << "Encoding immediate: type="
92 << int(Info.OperandType) << "\n");
93 if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
94 encodeSLEB128(int32_t(MO.getImm()), OS);
95 } else if (Info.OperandType == WebAssembly::OPERAND_OFFSET32) {
96 encodeULEB128(uint32_t(MO.getImm()), OS);
97 } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
98 encodeSLEB128(int64_t(MO.getImm()), OS);
99 } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) {
100 llvm_unreachable("wasm globals should only be accessed symbolicly");
101 } else if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {
102 OS << uint8_t(MO.getImm());
103 } else {
104 encodeULEB128(uint64_t(MO.getImm()), OS);
105 }
106 } else {
107 assert(Desc.TSFlags == (WebAssemblyII::VariableOpIsImmediate |
108 WebAssemblyII::VariableOpImmediateIsLabel));
109 encodeULEB128(uint64_t(MO.getImm()), OS);
110 }
111 } else if (MO.isFPImm()) {
112 assert(i < Desc.getNumOperands() &&
113 "Unexpected floating-point immediate as a non-fixed operand");
114 assert(Desc.TSFlags == 0 &&
115 "WebAssembly variable_ops floating point ops don't use TSFlags");
116 const MCOperandInfo &Info = Desc.OpInfo[i];
117 if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
118 // TODO: MC converts all floating point immediate operands to double.
119 // This is fine for numeric values, but may cause NaNs to change bits.
120 float f = float(MO.getFPImm());
121 support::endian::write<float>(OS, f, support::little);
122 } else {
123 assert(Info.OperandType == WebAssembly::OPERAND_F64IMM);
124 double d = MO.getFPImm();
125 support::endian::write<double>(OS, d, support::little);
126 }
127 } else if (MO.isExpr()) {
128 const MCOperandInfo &Info = Desc.OpInfo[i];
129 llvm::MCFixupKind FixupKind;
130 size_t PaddedSize = 5;
131 if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
132 FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i32);
133 } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
134 FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i64);
135 PaddedSize = 10;
136 } else if (Info.OperandType == WebAssembly::OPERAND_FUNCTION32 ||
137 Info.OperandType == WebAssembly::OPERAND_OFFSET32 ||
138 Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
139 FixupKind = MCFixupKind(WebAssembly::fixup_code_uleb128_i32);
140 } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) {
141 FixupKind = MCFixupKind(WebAssembly::fixup_code_global_index);
142 } else {
143 llvm_unreachable("unexpected symbolic operand kind");
144 }
145 Fixups.push_back(MCFixup::create(
146 OS.tell() - Start, MO.getExpr(),
147 FixupKind, MI.getLoc()));
148 ++MCNumFixups;
149 encodeULEB128(0, OS, PaddedSize);
150 } else {
151 llvm_unreachable("unexpected operand kind");
152 }
153 }
154
155 ++MCNumEmitted; // Keep track of the # of mi's emitted.
156 }
157
158 #include "WebAssemblyGenMCCodeEmitter.inc"
159