1444ed5c5SDimitry Andric //==- WebAssemblyDisassembler.cpp - Disassembler for WebAssembly -*- C++ -*-==//
2444ed5c5SDimitry Andric //
3444ed5c5SDimitry Andric //                     The LLVM Compiler Infrastructure
4444ed5c5SDimitry Andric //
5444ed5c5SDimitry Andric // This file is distributed under the University of Illinois Open Source
6444ed5c5SDimitry Andric // License. See LICENSE.TXT for details.
7444ed5c5SDimitry Andric //
8444ed5c5SDimitry Andric //===----------------------------------------------------------------------===//
9444ed5c5SDimitry Andric ///
10444ed5c5SDimitry Andric /// \file
11444ed5c5SDimitry Andric /// \brief This file is part of the WebAssembly Disassembler.
12444ed5c5SDimitry Andric ///
13444ed5c5SDimitry Andric /// It contains code to translate the data produced by the decoder into
14444ed5c5SDimitry Andric /// MCInsts.
15444ed5c5SDimitry Andric ///
16444ed5c5SDimitry Andric //===----------------------------------------------------------------------===//
17444ed5c5SDimitry Andric 
18444ed5c5SDimitry Andric #include "WebAssembly.h"
19444ed5c5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
20444ed5c5SDimitry Andric #include "llvm/MC/MCContext.h"
213ca95b02SDimitry Andric #include "llvm/MC/MCDisassembler/MCDisassembler.h"
22444ed5c5SDimitry Andric #include "llvm/MC/MCInst.h"
23444ed5c5SDimitry Andric #include "llvm/MC/MCInstrInfo.h"
24444ed5c5SDimitry Andric #include "llvm/MC/MCSubtargetInfo.h"
25444ed5c5SDimitry Andric #include "llvm/MC/MCSymbol.h"
26444ed5c5SDimitry Andric #include "llvm/Support/Endian.h"
27444ed5c5SDimitry Andric #include "llvm/Support/TargetRegistry.h"
28444ed5c5SDimitry Andric using namespace llvm;
29444ed5c5SDimitry Andric 
30444ed5c5SDimitry Andric #define DEBUG_TYPE "wasm-disassembler"
31444ed5c5SDimitry Andric 
32444ed5c5SDimitry Andric namespace {
33444ed5c5SDimitry Andric class WebAssemblyDisassembler final : public MCDisassembler {
34444ed5c5SDimitry Andric   std::unique_ptr<const MCInstrInfo> MCII;
35444ed5c5SDimitry Andric 
36444ed5c5SDimitry Andric   DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
37444ed5c5SDimitry Andric                               ArrayRef<uint8_t> Bytes, uint64_t Address,
38444ed5c5SDimitry Andric                               raw_ostream &VStream,
39444ed5c5SDimitry Andric                               raw_ostream &CStream) const override;
40444ed5c5SDimitry Andric 
41444ed5c5SDimitry Andric public:
42444ed5c5SDimitry Andric   WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx,
43444ed5c5SDimitry Andric                           std::unique_ptr<const MCInstrInfo> MCII)
44444ed5c5SDimitry Andric       : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {}
45444ed5c5SDimitry Andric };
46444ed5c5SDimitry Andric } // end anonymous namespace
47444ed5c5SDimitry Andric 
48444ed5c5SDimitry Andric static MCDisassembler *createWebAssemblyDisassembler(const Target &T,
49444ed5c5SDimitry Andric                                                      const MCSubtargetInfo &STI,
50444ed5c5SDimitry Andric                                                      MCContext &Ctx) {
51444ed5c5SDimitry Andric   std::unique_ptr<const MCInstrInfo> MCII(T.createMCInstrInfo());
52444ed5c5SDimitry Andric   return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII));
53444ed5c5SDimitry Andric }
54444ed5c5SDimitry Andric 
55444ed5c5SDimitry Andric extern "C" void LLVMInitializeWebAssemblyDisassembler() {
56444ed5c5SDimitry Andric   // Register the disassembler for each target.
57444ed5c5SDimitry Andric   TargetRegistry::RegisterMCDisassembler(TheWebAssemblyTarget32,
58444ed5c5SDimitry Andric                                          createWebAssemblyDisassembler);
59444ed5c5SDimitry Andric   TargetRegistry::RegisterMCDisassembler(TheWebAssemblyTarget64,
60444ed5c5SDimitry Andric                                          createWebAssemblyDisassembler);
61444ed5c5SDimitry Andric }
62444ed5c5SDimitry Andric 
63444ed5c5SDimitry Andric MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
64444ed5c5SDimitry Andric     MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/,
65444ed5c5SDimitry Andric     raw_ostream &OS, raw_ostream &CS) const {
66444ed5c5SDimitry Andric   Size = 0;
67444ed5c5SDimitry Andric   uint64_t Pos = 0;
68444ed5c5SDimitry Andric 
69444ed5c5SDimitry Andric   // Read the opcode.
70444ed5c5SDimitry Andric   if (Pos + sizeof(uint64_t) > Bytes.size())
71444ed5c5SDimitry Andric     return MCDisassembler::Fail;
72444ed5c5SDimitry Andric   uint64_t Opcode = support::endian::read64le(Bytes.data() + Pos);
73444ed5c5SDimitry Andric   Pos += sizeof(uint64_t);
74444ed5c5SDimitry Andric 
75444ed5c5SDimitry Andric   if (Opcode >= WebAssembly::INSTRUCTION_LIST_END)
76444ed5c5SDimitry Andric     return MCDisassembler::Fail;
77444ed5c5SDimitry Andric 
78444ed5c5SDimitry Andric   MI.setOpcode(Opcode);
79444ed5c5SDimitry Andric   const MCInstrDesc &Desc = MCII->get(Opcode);
80444ed5c5SDimitry Andric   unsigned NumFixedOperands = Desc.NumOperands;
81444ed5c5SDimitry Andric 
82444ed5c5SDimitry Andric   // If it's variadic, read the number of extra operands.
83444ed5c5SDimitry Andric   unsigned NumExtraOperands = 0;
84444ed5c5SDimitry Andric   if (Desc.isVariadic()) {
85444ed5c5SDimitry Andric     if (Pos + sizeof(uint64_t) > Bytes.size())
86444ed5c5SDimitry Andric       return MCDisassembler::Fail;
87444ed5c5SDimitry Andric     NumExtraOperands = support::endian::read64le(Bytes.data() + Pos);
88444ed5c5SDimitry Andric     Pos += sizeof(uint64_t);
89444ed5c5SDimitry Andric   }
90444ed5c5SDimitry Andric 
91444ed5c5SDimitry Andric   // Read the fixed operands. These are described by the MCInstrDesc.
92444ed5c5SDimitry Andric   for (unsigned i = 0; i < NumFixedOperands; ++i) {
93444ed5c5SDimitry Andric     const MCOperandInfo &Info = Desc.OpInfo[i];
94444ed5c5SDimitry Andric     switch (Info.OperandType) {
95444ed5c5SDimitry Andric     case MCOI::OPERAND_IMMEDIATE:
963ca95b02SDimitry Andric     case WebAssembly::OPERAND_P2ALIGN:
97444ed5c5SDimitry Andric     case WebAssembly::OPERAND_BASIC_BLOCK: {
98444ed5c5SDimitry Andric       if (Pos + sizeof(uint64_t) > Bytes.size())
99444ed5c5SDimitry Andric         return MCDisassembler::Fail;
100444ed5c5SDimitry Andric       uint64_t Imm = support::endian::read64le(Bytes.data() + Pos);
101444ed5c5SDimitry Andric       Pos += sizeof(uint64_t);
102444ed5c5SDimitry Andric       MI.addOperand(MCOperand::createImm(Imm));
103444ed5c5SDimitry Andric       break;
104444ed5c5SDimitry Andric     }
105444ed5c5SDimitry Andric     case MCOI::OPERAND_REGISTER: {
106444ed5c5SDimitry Andric       if (Pos + sizeof(uint64_t) > Bytes.size())
107444ed5c5SDimitry Andric         return MCDisassembler::Fail;
108444ed5c5SDimitry Andric       uint64_t Reg = support::endian::read64le(Bytes.data() + Pos);
109444ed5c5SDimitry Andric       Pos += sizeof(uint64_t);
110444ed5c5SDimitry Andric       MI.addOperand(MCOperand::createReg(Reg));
111444ed5c5SDimitry Andric       break;
112444ed5c5SDimitry Andric     }
1133ca95b02SDimitry Andric     case WebAssembly::OPERAND_FP32IMM:
1143ca95b02SDimitry Andric     case WebAssembly::OPERAND_FP64IMM: {
115444ed5c5SDimitry Andric       // TODO: MC converts all floating point immediate operands to double.
116444ed5c5SDimitry Andric       // This is fine for numeric values, but may cause NaNs to change bits.
117444ed5c5SDimitry Andric       if (Pos + sizeof(uint64_t) > Bytes.size())
118444ed5c5SDimitry Andric         return MCDisassembler::Fail;
119444ed5c5SDimitry Andric       uint64_t Bits = support::endian::read64le(Bytes.data() + Pos);
120444ed5c5SDimitry Andric       Pos += sizeof(uint64_t);
121444ed5c5SDimitry Andric       double Imm;
122444ed5c5SDimitry Andric       memcpy(&Imm, &Bits, sizeof(Imm));
123444ed5c5SDimitry Andric       MI.addOperand(MCOperand::createFPImm(Imm));
124444ed5c5SDimitry Andric       break;
125444ed5c5SDimitry Andric     }
126444ed5c5SDimitry Andric     default:
127444ed5c5SDimitry Andric       llvm_unreachable("unimplemented operand kind");
128444ed5c5SDimitry Andric     }
129444ed5c5SDimitry Andric   }
130444ed5c5SDimitry Andric 
131444ed5c5SDimitry Andric   // Read the extra operands.
132444ed5c5SDimitry Andric   assert(NumExtraOperands == 0 || Desc.isVariadic());
133444ed5c5SDimitry Andric   for (unsigned i = 0; i < NumExtraOperands; ++i) {
134444ed5c5SDimitry Andric     if (Pos + sizeof(uint64_t) > Bytes.size())
135444ed5c5SDimitry Andric       return MCDisassembler::Fail;
136444ed5c5SDimitry Andric     if (Desc.TSFlags & WebAssemblyII::VariableOpIsImmediate) {
137444ed5c5SDimitry Andric       // Decode extra immediate operands.
138444ed5c5SDimitry Andric       uint64_t Imm = support::endian::read64le(Bytes.data() + Pos);
139444ed5c5SDimitry Andric       MI.addOperand(MCOperand::createImm(Imm));
140444ed5c5SDimitry Andric     } else {
141444ed5c5SDimitry Andric       // Decode extra register operands.
142444ed5c5SDimitry Andric       uint64_t Reg = support::endian::read64le(Bytes.data() + Pos);
143444ed5c5SDimitry Andric       MI.addOperand(MCOperand::createReg(Reg));
144444ed5c5SDimitry Andric     }
145444ed5c5SDimitry Andric     Pos += sizeof(uint64_t);
146444ed5c5SDimitry Andric   }
147444ed5c5SDimitry Andric 
148444ed5c5SDimitry Andric   Size = Pos;
149444ed5c5SDimitry Andric   return MCDisassembler::Success;
150444ed5c5SDimitry Andric }
151