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