17d523365SDimitry Andric //=- WebAssemblyMCCodeEmitter.cpp - Convert WebAssembly code to machine code -//
27d523365SDimitry Andric //
37d523365SDimitry Andric // The LLVM Compiler Infrastructure
47d523365SDimitry Andric //
57d523365SDimitry Andric // This file is distributed under the University of Illinois Open Source
67d523365SDimitry Andric // License. See LICENSE.TXT for details.
77d523365SDimitry Andric //
87d523365SDimitry Andric //===----------------------------------------------------------------------===//
97d523365SDimitry Andric ///
107d523365SDimitry Andric /// \file
114ba319b5SDimitry Andric /// This file implements the WebAssemblyMCCodeEmitter class.
127d523365SDimitry Andric ///
137d523365SDimitry Andric //===----------------------------------------------------------------------===//
147d523365SDimitry Andric
157a7e6055SDimitry Andric #include "MCTargetDesc/WebAssemblyFixupKinds.h"
16db17bf38SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
17444ed5c5SDimitry Andric #include "llvm/ADT/STLExtras.h"
187d523365SDimitry Andric #include "llvm/ADT/Statistic.h"
197d523365SDimitry Andric #include "llvm/MC/MCCodeEmitter.h"
207d523365SDimitry Andric #include "llvm/MC/MCFixup.h"
217d523365SDimitry Andric #include "llvm/MC/MCInst.h"
227d523365SDimitry Andric #include "llvm/MC/MCInstrInfo.h"
237d523365SDimitry Andric #include "llvm/MC/MCRegisterInfo.h"
247d523365SDimitry Andric #include "llvm/MC/MCSubtargetInfo.h"
257d523365SDimitry Andric #include "llvm/MC/MCSymbol.h"
264ba319b5SDimitry Andric #include "llvm/Support/Debug.h"
273ca95b02SDimitry Andric #include "llvm/Support/EndianStream.h"
28d88c1a5aSDimitry Andric #include "llvm/Support/LEB128.h"
297d523365SDimitry Andric #include "llvm/Support/raw_ostream.h"
304ba319b5SDimitry Andric
317d523365SDimitry Andric using namespace llvm;
327d523365SDimitry Andric
337d523365SDimitry Andric #define DEBUG_TYPE "mccodeemitter"
347d523365SDimitry Andric
35444ed5c5SDimitry Andric STATISTIC(MCNumEmitted, "Number of MC instructions emitted.");
36444ed5c5SDimitry Andric STATISTIC(MCNumFixups, "Number of MC fixups created.");
37444ed5c5SDimitry Andric
387d523365SDimitry Andric namespace {
397d523365SDimitry Andric class WebAssemblyMCCodeEmitter final : public MCCodeEmitter {
40444ed5c5SDimitry Andric const MCInstrInfo &MCII;
417d523365SDimitry Andric
42444ed5c5SDimitry Andric // Implementation generated by tablegen.
437d523365SDimitry Andric uint64_t getBinaryCodeForInstr(const MCInst &MI,
447d523365SDimitry Andric SmallVectorImpl<MCFixup> &Fixups,
457d523365SDimitry Andric const MCSubtargetInfo &STI) const;
467d523365SDimitry Andric
477d523365SDimitry Andric void encodeInstruction(const MCInst &MI, raw_ostream &OS,
487d523365SDimitry Andric SmallVectorImpl<MCFixup> &Fixups,
497d523365SDimitry Andric const MCSubtargetInfo &STI) const override;
50444ed5c5SDimitry Andric
51444ed5c5SDimitry Andric public:
WebAssemblyMCCodeEmitter(const MCInstrInfo & mcii)52edd7eaddSDimitry Andric WebAssemblyMCCodeEmitter(const MCInstrInfo &mcii) : MCII(mcii) {}
537d523365SDimitry Andric };
547d523365SDimitry Andric } // end anonymous namespace
557d523365SDimitry Andric
createWebAssemblyMCCodeEmitter(const MCInstrInfo & MCII)56edd7eaddSDimitry Andric MCCodeEmitter *llvm::createWebAssemblyMCCodeEmitter(const MCInstrInfo &MCII) {
57edd7eaddSDimitry Andric return new WebAssemblyMCCodeEmitter(MCII);
587d523365SDimitry Andric }
597d523365SDimitry Andric
encodeInstruction(const MCInst & MI,raw_ostream & OS,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const607d523365SDimitry Andric void WebAssemblyMCCodeEmitter::encodeInstruction(
617d523365SDimitry Andric const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups,
627d523365SDimitry Andric const MCSubtargetInfo &STI) const {
63d88c1a5aSDimitry Andric uint64_t Start = OS.tell();
64d88c1a5aSDimitry Andric
65d88c1a5aSDimitry Andric uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
662cab237bSDimitry Andric if (Binary <= UINT8_MAX) {
67d88c1a5aSDimitry Andric OS << uint8_t(Binary);
682cab237bSDimitry Andric } else {
692cab237bSDimitry Andric assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet");
70*b5893f02SDimitry Andric OS << uint8_t(Binary >> 8);
71*b5893f02SDimitry Andric encodeULEB128(uint8_t(Binary), OS);
722cab237bSDimitry Andric }
73d88c1a5aSDimitry Andric
747a7e6055SDimitry Andric // For br_table instructions, encode the size of the table. In the MCInst,
75*b5893f02SDimitry Andric // there's an index operand (if not a stack instruction), one operand for
76*b5893f02SDimitry Andric // each table entry, and the default operand.
77*b5893f02SDimitry Andric if (MI.getOpcode() == WebAssembly::BR_TABLE_I32_S ||
78*b5893f02SDimitry Andric MI.getOpcode() == WebAssembly::BR_TABLE_I64_S)
79*b5893f02SDimitry Andric encodeULEB128(MI.getNumOperands() - 1, OS);
807a7e6055SDimitry Andric if (MI.getOpcode() == WebAssembly::BR_TABLE_I32 ||
817a7e6055SDimitry Andric MI.getOpcode() == WebAssembly::BR_TABLE_I64)
827a7e6055SDimitry Andric encodeULEB128(MI.getNumOperands() - 2, OS);
837a7e6055SDimitry Andric
84444ed5c5SDimitry Andric const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
85444ed5c5SDimitry Andric for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {
86444ed5c5SDimitry Andric const MCOperand &MO = MI.getOperand(i);
87444ed5c5SDimitry Andric if (MO.isReg()) {
88d88c1a5aSDimitry Andric /* nothing to encode */
89*b5893f02SDimitry Andric
90444ed5c5SDimitry Andric } else if (MO.isImm()) {
91d88c1a5aSDimitry Andric if (i < Desc.getNumOperands()) {
92d88c1a5aSDimitry Andric const MCOperandInfo &Info = Desc.OpInfo[i];
934ba319b5SDimitry Andric LLVM_DEBUG(dbgs() << "Encoding immediate: type="
944ba319b5SDimitry Andric << int(Info.OperandType) << "\n");
95*b5893f02SDimitry Andric switch (Info.OperandType) {
96*b5893f02SDimitry Andric case WebAssembly::OPERAND_I32IMM:
97d88c1a5aSDimitry Andric encodeSLEB128(int32_t(MO.getImm()), OS);
98*b5893f02SDimitry Andric break;
99*b5893f02SDimitry Andric case WebAssembly::OPERAND_OFFSET32:
1004ba319b5SDimitry Andric encodeULEB128(uint32_t(MO.getImm()), OS);
101*b5893f02SDimitry Andric break;
102*b5893f02SDimitry Andric case WebAssembly::OPERAND_I64IMM:
103d88c1a5aSDimitry Andric encodeSLEB128(int64_t(MO.getImm()), OS);
104*b5893f02SDimitry Andric break;
105*b5893f02SDimitry Andric case WebAssembly::OPERAND_SIGNATURE:
1064ba319b5SDimitry Andric OS << uint8_t(MO.getImm());
107*b5893f02SDimitry Andric break;
108*b5893f02SDimitry Andric case WebAssembly::OPERAND_VEC_I8IMM:
109*b5893f02SDimitry Andric support::endian::write<uint8_t>(OS, MO.getImm(), support::little);
110*b5893f02SDimitry Andric break;
111*b5893f02SDimitry Andric case WebAssembly::OPERAND_VEC_I16IMM:
112*b5893f02SDimitry Andric support::endian::write<uint16_t>(OS, MO.getImm(), support::little);
113*b5893f02SDimitry Andric break;
114*b5893f02SDimitry Andric case WebAssembly::OPERAND_VEC_I32IMM:
115*b5893f02SDimitry Andric support::endian::write<uint32_t>(OS, MO.getImm(), support::little);
116*b5893f02SDimitry Andric break;
117*b5893f02SDimitry Andric case WebAssembly::OPERAND_VEC_I64IMM:
118*b5893f02SDimitry Andric support::endian::write<uint64_t>(OS, MO.getImm(), support::little);
119*b5893f02SDimitry Andric break;
120*b5893f02SDimitry Andric case WebAssembly::OPERAND_GLOBAL:
121*b5893f02SDimitry Andric llvm_unreachable("wasm globals should only be accessed symbolicly");
122*b5893f02SDimitry Andric default:
123d88c1a5aSDimitry Andric encodeULEB128(uint64_t(MO.getImm()), OS);
124d88c1a5aSDimitry Andric }
125d88c1a5aSDimitry Andric } else {
126d88c1a5aSDimitry Andric encodeULEB128(uint64_t(MO.getImm()), OS);
127d88c1a5aSDimitry Andric }
128*b5893f02SDimitry Andric
129444ed5c5SDimitry Andric } else if (MO.isFPImm()) {
130d88c1a5aSDimitry Andric const MCOperandInfo &Info = Desc.OpInfo[i];
131d88c1a5aSDimitry Andric if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
132d88c1a5aSDimitry Andric // TODO: MC converts all floating point immediate operands to double.
133d88c1a5aSDimitry Andric // This is fine for numeric values, but may cause NaNs to change bits.
134d88c1a5aSDimitry Andric float f = float(MO.getFPImm());
1354ba319b5SDimitry Andric support::endian::write<float>(OS, f, support::little);
136d88c1a5aSDimitry Andric } else {
137d88c1a5aSDimitry Andric assert(Info.OperandType == WebAssembly::OPERAND_F64IMM);
138d88c1a5aSDimitry Andric double d = MO.getFPImm();
1394ba319b5SDimitry Andric support::endian::write<double>(OS, d, support::little);
140d88c1a5aSDimitry Andric }
141*b5893f02SDimitry Andric
142444ed5c5SDimitry Andric } else if (MO.isExpr()) {
1437a7e6055SDimitry Andric const MCOperandInfo &Info = Desc.OpInfo[i];
1447a7e6055SDimitry Andric llvm::MCFixupKind FixupKind;
1452cab237bSDimitry Andric size_t PaddedSize = 5;
146*b5893f02SDimitry Andric switch (Info.OperandType) {
147*b5893f02SDimitry Andric case WebAssembly::OPERAND_I32IMM:
1487a7e6055SDimitry Andric FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i32);
149*b5893f02SDimitry Andric break;
150*b5893f02SDimitry Andric case WebAssembly::OPERAND_I64IMM:
1517a7e6055SDimitry Andric FixupKind = MCFixupKind(WebAssembly::fixup_code_sleb128_i64);
1527a7e6055SDimitry Andric PaddedSize = 10;
153*b5893f02SDimitry Andric break;
154*b5893f02SDimitry Andric case WebAssembly::OPERAND_FUNCTION32:
155*b5893f02SDimitry Andric case WebAssembly::OPERAND_OFFSET32:
156*b5893f02SDimitry Andric case WebAssembly::OPERAND_TYPEINDEX:
157*b5893f02SDimitry Andric case WebAssembly::OPERAND_GLOBAL:
158*b5893f02SDimitry Andric case WebAssembly::OPERAND_EVENT:
1597a7e6055SDimitry Andric FixupKind = MCFixupKind(WebAssembly::fixup_code_uleb128_i32);
160*b5893f02SDimitry Andric break;
161*b5893f02SDimitry Andric default:
1627a7e6055SDimitry Andric llvm_unreachable("unexpected symbolic operand kind");
1637a7e6055SDimitry Andric }
164*b5893f02SDimitry Andric Fixups.push_back(MCFixup::create(OS.tell() - Start, MO.getExpr(),
1657a7e6055SDimitry Andric FixupKind, MI.getLoc()));
166444ed5c5SDimitry Andric ++MCNumFixups;
1672cab237bSDimitry Andric encodeULEB128(0, OS, PaddedSize);
168444ed5c5SDimitry Andric } else {
169444ed5c5SDimitry Andric llvm_unreachable("unexpected operand kind");
170444ed5c5SDimitry Andric }
1717d523365SDimitry Andric }
1727d523365SDimitry Andric
173444ed5c5SDimitry Andric ++MCNumEmitted; // Keep track of the # of mi's emitted.
1747d523365SDimitry Andric }
1757d523365SDimitry Andric
1767d523365SDimitry Andric #include "WebAssemblyGenMCCodeEmitter.inc"
177