Line data Source code
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:
52 154 : WebAssemblyMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {}
53 : };
54 : } // end anonymous namespace
55 :
56 154 : MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) {
57 154 : return new WebAssemblyMCCodeEmitter(MCII);
58 : }
59 :
60 4087 : 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 4087 : uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
66 4087 : if (Binary <= UINT8_MAX) {
67 3782 : OS << uint8_t(Binary);
68 : } else {
69 : assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet");
70 610 : OS << uint8_t(Binary >> 8) << uint8_t(Binary);
71 : }
72 :
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 4087 : if (MI.getOpcode() == WebAssembly::BR_TABLE_I32_S ||
77 : MI.getOpcode() == WebAssembly::BR_TABLE_I64_S)
78 0 : encodeULEB128(MI.getNumOperands() - 1, OS);
79 4087 : if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 ||
80 : MI.getOpcode() == WebAssembly::BR_TABLE_I64)
81 0 : encodeULEB128(MI.getNumOperands() - 2, OS);
82 :
83 4087 : const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
84 11834 : for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {
85 : const MCOperand &MO = MI.getOperand(i);
86 7747 : if (MO.isReg()) {
87 : /* nothing to encode */
88 1661 : } else if (MO.isImm()) {
89 2674 : if (i < Desc.getNumOperands()) {
90 : assert(Desc.TSFlags == 0 &&
91 : "WebAssembly non-variable_ops don't use TSFlags");
92 1337 : const MCOperandInfo &Info = Desc.OpInfo[i];
93 : LLVM_DEBUG(dbgs() << "Encoding immediate: type="
94 : << int(Info.OperandType) << "\n");
95 1337 : switch (Info.OperandType) {
96 872 : case WebAssembly::OPERAND_I32IMM:
97 872 : encodeSLEB128(int32_t(MO.getImm()), OS);
98 872 : break;
99 93 : case WebAssembly::OPERAND_OFFSET32:
100 93 : encodeULEB128(uint32_t(MO.getImm()), OS);
101 93 : break;
102 13 : case WebAssembly::OPERAND_I64IMM:
103 13 : encodeSLEB128(int64_t(MO.getImm()), OS);
104 13 : break;
105 9 : case WebAssembly::OPERAND_SIGNATURE:
106 9 : OS << uint8_t(MO.getImm());
107 : break;
108 46 : case WebAssembly::OPERAND_VEC_I8IMM:
109 46 : support::endian::write<uint8_t>(OS, MO.getImm(), support::little);
110 46 : break;
111 8 : case WebAssembly::OPERAND_VEC_I16IMM:
112 8 : support::endian::write<uint16_t>(OS, MO.getImm(), support::little);
113 8 : break;
114 0 : case WebAssembly::OPERAND_VEC_I32IMM:
115 0 : support::endian::write<uint32_t>(OS, MO.getImm(), support::little);
116 0 : break;
117 0 : case WebAssembly::OPERAND_VEC_I64IMM:
118 0 : support::endian::write<uint64_t>(OS, MO.getImm(), support::little);
119 0 : break;
120 : case WebAssembly::OPERAND_GLOBAL:
121 : llvm_unreachable("wasm globals should only be accessed symbolicly");
122 296 : default:
123 296 : encodeULEB128(uint64_t(MO.getImm()), OS);
124 : }
125 : } else {
126 : assert(Desc.TSFlags == (WebAssemblyII::VariableOpIsImmediate |
127 : WebAssemblyII::VariableOpImmediateIsLabel));
128 0 : encodeULEB128(uint64_t(MO.getImm()), OS);
129 : }
130 324 : } else if (MO.isFPImm()) {
131 : assert(i < Desc.getNumOperands() &&
132 : "Unexpected floating-point immediate as a non-fixed operand");
133 : assert(Desc.TSFlags == 0 &&
134 : "WebAssembly variable_ops floating point ops don't use TSFlags");
135 8 : const MCOperandInfo &Info = Desc.OpInfo[i];
136 8 : if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
137 : // TODO: MC converts all floating point immediate operands to double.
138 : // This is fine for numeric values, but may cause NaNs to change bits.
139 6 : float f = float(MO.getFPImm());
140 : support::endian::write<float>(OS, f, support::little);
141 : } else {
142 : assert(Info.OperandType == WebAssembly::OPERAND_F64IMM);
143 2 : double d = MO.getFPImm();
144 : support::endian::write<double>(OS, d, support::little);
145 : }
146 316 : } else if (MO.isExpr()) {
147 316 : const MCOperandInfo &Info = Desc.OpInfo[i];
148 : llvm::MCFixupKind FixupKind;
149 : size_t PaddedSize = 5;
150 316 : if (Info.OperandType == WebAssembly::OPERAND_I32IMM) {
151 : FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i32);
152 267 : } else if (Info.OperandType == WebAssembly::OPERAND_I64IMM) {
153 : FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i64);
154 : PaddedSize = 10;
155 267 : } else if (Info.OperandType == WebAssembly::OPERAND_FUNCTION32 ||
156 267 : Info.OperandType == WebAssembly::OPERAND_OFFSET32 ||
157 : Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
158 : FixupKind = MCFixupKind(WebAssembly::fixup_code_uleb128_i32);
159 20 : } else if (Info.OperandType == WebAssembly::OPERAND_GLOBAL) {
160 : FixupKind = MCFixupKind(WebAssembly::fixup_code_uleb128_i32);
161 : } else {
162 0 : llvm_unreachable("unexpected symbolic operand kind");
163 : }
164 316 : Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(),
165 316 : FixupKind, MI.getLoc()));
166 : ++MCNumFixups;
167 316 : encodeULEB128(0, OS, PaddedSize);
168 : } else {
169 0 : llvm_unreachable("unexpected operand kind");
170 : }
171 : }
172 :
173 : ++MCNumEmitted; // Keep track of the # of mi's emitted.
174 4087 : }
175 :
176 : #include "WebAssemblyGenMCCodeEmitter.inc"
|