11a427287SDan Gohman //==- WebAssemblyDisassembler.cpp - Disassembler for WebAssembly -*- C++ -*-==//
21a427287SDan Gohman //
31a427287SDan Gohman //                     The LLVM Compiler Infrastructure
41a427287SDan Gohman //
51a427287SDan Gohman // This file is distributed under the University of Illinois Open Source
61a427287SDan Gohman // License. See LICENSE.TXT for details.
71a427287SDan Gohman //
81a427287SDan Gohman //===----------------------------------------------------------------------===//
91a427287SDan Gohman ///
101a427287SDan Gohman /// \file
115f8f34e4SAdrian Prantl /// This file is part of the WebAssembly Disassembler.
121a427287SDan Gohman ///
131a427287SDan Gohman /// It contains code to translate the data produced by the decoder into
141a427287SDan Gohman /// MCInsts.
151a427287SDan Gohman ///
161a427287SDan Gohman //===----------------------------------------------------------------------===//
171a427287SDan Gohman 
181a427287SDan Gohman #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
191a427287SDan Gohman #include "llvm/MC/MCContext.h"
20c50b8907SBenjamin Kramer #include "llvm/MC/MCDisassembler/MCDisassembler.h"
2116c16827SSam Clegg #include "llvm/MC/MCFixedLenDisassembler.h"
221a427287SDan Gohman #include "llvm/MC/MCInst.h"
231a427287SDan Gohman #include "llvm/MC/MCInstrInfo.h"
241a427287SDan Gohman #include "llvm/MC/MCSubtargetInfo.h"
251a427287SDan Gohman #include "llvm/MC/MCSymbol.h"
261a427287SDan Gohman #include "llvm/Support/Endian.h"
2716c16827SSam Clegg #include "llvm/Support/LEB128.h"
281a427287SDan Gohman #include "llvm/Support/TargetRegistry.h"
2916c16827SSam Clegg 
301a427287SDan Gohman using namespace llvm;
311a427287SDan Gohman 
321a427287SDan Gohman #define DEBUG_TYPE "wasm-disassembler"
331a427287SDan Gohman 
3416c16827SSam Clegg using DecodeStatus = MCDisassembler::DecodeStatus;
3516c16827SSam Clegg 
3616c16827SSam Clegg #include "WebAssemblyGenDisassemblerTables.inc"
3716c16827SSam Clegg 
381a427287SDan Gohman namespace {
3949550663SFangrui Song static constexpr int WebAssemblyInstructionTableSize = 256;
4049550663SFangrui Song 
411a427287SDan Gohman class WebAssemblyDisassembler final : public MCDisassembler {
421a427287SDan Gohman   std::unique_ptr<const MCInstrInfo> MCII;
431a427287SDan Gohman 
441a427287SDan Gohman   DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
451a427287SDan Gohman                               ArrayRef<uint8_t> Bytes, uint64_t Address,
461a427287SDan Gohman                               raw_ostream &VStream,
471a427287SDan Gohman                               raw_ostream &CStream) const override;
481a427287SDan Gohman 
491a427287SDan Gohman public:
501a427287SDan Gohman   WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx,
511a427287SDan Gohman                           std::unique_ptr<const MCInstrInfo> MCII)
521a427287SDan Gohman       : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {}
531a427287SDan Gohman };
541a427287SDan Gohman } // end anonymous namespace
551a427287SDan Gohman 
561a427287SDan Gohman static MCDisassembler *createWebAssemblyDisassembler(const Target &T,
571a427287SDan Gohman                                                      const MCSubtargetInfo &STI,
581a427287SDan Gohman                                                      MCContext &Ctx) {
591a427287SDan Gohman   std::unique_ptr<const MCInstrInfo> MCII(T.createMCInstrInfo());
601a427287SDan Gohman   return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII));
611a427287SDan Gohman }
621a427287SDan Gohman 
631a427287SDan Gohman extern "C" void LLVMInitializeWebAssemblyDisassembler() {
641a427287SDan Gohman   // Register the disassembler for each target.
65f42454b9SMehdi Amini   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget32(),
661a427287SDan Gohman                                          createWebAssemblyDisassembler);
67f42454b9SMehdi Amini   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget64(),
681a427287SDan Gohman                                          createWebAssemblyDisassembler);
691a427287SDan Gohman }
701a427287SDan Gohman 
71*fc3163b6SThomas Lively static int nextByte(ArrayRef<uint8_t> Bytes, uint64_t &Size) {
7216c16827SSam Clegg   if (Size >= Bytes.size())
7316c16827SSam Clegg     return -1;
7416c16827SSam Clegg   auto V = Bytes[Size];
7516c16827SSam Clegg   Size++;
7616c16827SSam Clegg   return V;
7716c16827SSam Clegg }
7816c16827SSam Clegg 
792faf0794SThomas Lively static bool nextLEB(int64_t &Val, ArrayRef<uint8_t> Bytes, uint64_t &Size,
802faf0794SThomas Lively                     bool Signed = false) {
8116c16827SSam Clegg   unsigned N = 0;
8216c16827SSam Clegg   const char *Error = nullptr;
832faf0794SThomas Lively   Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N,
8416c16827SSam Clegg                                Bytes.data() + Bytes.size(), &Error)
852faf0794SThomas Lively                : static_cast<int64_t>(decodeULEB128(Bytes.data() + Size, &N,
862faf0794SThomas Lively                                                     Bytes.data() + Bytes.size(),
872faf0794SThomas Lively                                                     &Error));
8816c16827SSam Clegg   if (Error)
8916c16827SSam Clegg     return false;
9016c16827SSam Clegg   Size += N;
912faf0794SThomas Lively   return true;
922faf0794SThomas Lively }
932faf0794SThomas Lively 
942faf0794SThomas Lively static bool parseLEBImmediate(MCInst &MI, uint64_t &Size,
952faf0794SThomas Lively                               ArrayRef<uint8_t> Bytes, bool Signed) {
962faf0794SThomas Lively   int64_t Val;
972faf0794SThomas Lively   if (!nextLEB(Val, Bytes, Size, Signed))
982faf0794SThomas Lively     return false;
9916c16827SSam Clegg   MI.addOperand(MCOperand::createImm(Val));
10016c16827SSam Clegg   return true;
10116c16827SSam Clegg }
10216c16827SSam Clegg 
10316c16827SSam Clegg template <typename T>
10422442924SThomas Lively bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
10516c16827SSam Clegg   if (Size + sizeof(T) > Bytes.size())
10616c16827SSam Clegg     return false;
10716c16827SSam Clegg   T Val;
10816c16827SSam Clegg   memcpy(&Val, Bytes.data() + Size, sizeof(T));
10916c16827SSam Clegg   support::endian::byte_swap<T, support::endianness::little>(Val);
11016c16827SSam Clegg   Size += sizeof(T);
11122442924SThomas Lively   if (std::is_floating_point<T>::value) {
11216c16827SSam Clegg     MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
11322442924SThomas Lively   } else {
11422442924SThomas Lively     MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val)));
11522442924SThomas Lively   }
11616c16827SSam Clegg   return true;
11716c16827SSam Clegg }
11816c16827SSam Clegg 
1191a427287SDan Gohman MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
1201a427287SDan Gohman     MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/,
12116c16827SSam Clegg     raw_ostream & /*OS*/, raw_ostream &CS) const {
12216c16827SSam Clegg   CommentStream = &CS;
12316c16827SSam Clegg   Size = 0;
124*fc3163b6SThomas Lively   int Opc = nextByte(Bytes, Size);
12516c16827SSam Clegg   if (Opc < 0)
1261a427287SDan Gohman     return MCDisassembler::Fail;
12716c16827SSam Clegg   const auto *WasmInst = &InstructionTable0[Opc];
12816c16827SSam Clegg   // If this is a prefix byte, indirect to another table.
12916c16827SSam Clegg   if (WasmInst->ET == ET_Prefix) {
13016c16827SSam Clegg     WasmInst = nullptr;
13116c16827SSam Clegg     // Linear search, so far only 2 entries.
13216c16827SSam Clegg     for (auto PT = PrefixTable; PT->Table; PT++) {
13316c16827SSam Clegg       if (PT->Prefix == Opc) {
13416c16827SSam Clegg         WasmInst = PT->Table;
13516c16827SSam Clegg         break;
13616c16827SSam Clegg       }
13716c16827SSam Clegg     }
13816c16827SSam Clegg     if (!WasmInst)
13916c16827SSam Clegg       return MCDisassembler::Fail;
1402faf0794SThomas Lively     int64_t PrefixedOpc;
1412faf0794SThomas Lively     if (!nextLEB(PrefixedOpc, Bytes, Size))
14216c16827SSam Clegg       return MCDisassembler::Fail;
1432faf0794SThomas Lively     if (PrefixedOpc < 0 || PrefixedOpc >= WebAssemblyInstructionTableSize)
1442faf0794SThomas Lively       return MCDisassembler::Fail;
1452faf0794SThomas Lively     WasmInst += PrefixedOpc;
14616c16827SSam Clegg   }
14716c16827SSam Clegg   if (WasmInst->ET == ET_Unused)
14816c16827SSam Clegg     return MCDisassembler::Fail;
14916c16827SSam Clegg   // At this point we must have a valid instruction to decode.
15016c16827SSam Clegg   assert(WasmInst->ET == ET_Instruction);
15116c16827SSam Clegg   MI.setOpcode(WasmInst->Opcode);
15216c16827SSam Clegg   // Parse any operands.
15316c16827SSam Clegg   for (uint8_t OPI = 0; OPI < WasmInst->NumOperands; OPI++) {
154a733d08dSWouter van Oortmerssen     switch (OperandTable[WasmInst->OperandStart + OPI]) {
15516c16827SSam Clegg     // ULEB operands:
15616c16827SSam Clegg     case WebAssembly::OPERAND_BASIC_BLOCK:
15716c16827SSam Clegg     case WebAssembly::OPERAND_LOCAL:
15816c16827SSam Clegg     case WebAssembly::OPERAND_GLOBAL:
15916c16827SSam Clegg     case WebAssembly::OPERAND_FUNCTION32:
16016c16827SSam Clegg     case WebAssembly::OPERAND_OFFSET32:
16116c16827SSam Clegg     case WebAssembly::OPERAND_P2ALIGN:
16216c16827SSam Clegg     case WebAssembly::OPERAND_TYPEINDEX:
16316c16827SSam Clegg     case MCOI::OPERAND_IMMEDIATE: {
16416c16827SSam Clegg       if (!parseLEBImmediate(MI, Size, Bytes, false))
16516c16827SSam Clegg         return MCDisassembler::Fail;
16616c16827SSam Clegg       break;
16716c16827SSam Clegg     }
16816c16827SSam Clegg     // SLEB operands:
16916c16827SSam Clegg     case WebAssembly::OPERAND_I32IMM:
17016c16827SSam Clegg     case WebAssembly::OPERAND_I64IMM:
17116c16827SSam Clegg     case WebAssembly::OPERAND_SIGNATURE: {
17216c16827SSam Clegg       if (!parseLEBImmediate(MI, Size, Bytes, true))
17316c16827SSam Clegg         return MCDisassembler::Fail;
17416c16827SSam Clegg       break;
17516c16827SSam Clegg     }
17616c16827SSam Clegg     // FP operands.
17716c16827SSam Clegg     case WebAssembly::OPERAND_F32IMM: {
17822442924SThomas Lively       if (!parseImmediate<float>(MI, Size, Bytes))
17916c16827SSam Clegg         return MCDisassembler::Fail;
18016c16827SSam Clegg       break;
18116c16827SSam Clegg     }
18216c16827SSam Clegg     case WebAssembly::OPERAND_F64IMM: {
18322442924SThomas Lively       if (!parseImmediate<double>(MI, Size, Bytes))
18422442924SThomas Lively         return MCDisassembler::Fail;
18522442924SThomas Lively       break;
18622442924SThomas Lively     }
18722442924SThomas Lively     // Vector lane operands (not LEB encoded).
18822442924SThomas Lively     case WebAssembly::OPERAND_VEC_I8IMM: {
18922442924SThomas Lively       if (!parseImmediate<uint8_t>(MI, Size, Bytes))
19022442924SThomas Lively         return MCDisassembler::Fail;
19122442924SThomas Lively       break;
19222442924SThomas Lively     }
19322442924SThomas Lively     case WebAssembly::OPERAND_VEC_I16IMM: {
19422442924SThomas Lively       if (!parseImmediate<uint16_t>(MI, Size, Bytes))
19522442924SThomas Lively         return MCDisassembler::Fail;
19622442924SThomas Lively       break;
19722442924SThomas Lively     }
19822442924SThomas Lively     case WebAssembly::OPERAND_VEC_I32IMM: {
19922442924SThomas Lively       if (!parseImmediate<uint32_t>(MI, Size, Bytes))
20022442924SThomas Lively         return MCDisassembler::Fail;
20122442924SThomas Lively       break;
20222442924SThomas Lively     }
20322442924SThomas Lively     case WebAssembly::OPERAND_VEC_I64IMM: {
20422442924SThomas Lively       if (!parseImmediate<uint64_t>(MI, Size, Bytes))
20516c16827SSam Clegg         return MCDisassembler::Fail;
20616c16827SSam Clegg       break;
20716c16827SSam Clegg     }
208a733d08dSWouter van Oortmerssen     case MCOI::OPERAND_REGISTER:
209a733d08dSWouter van Oortmerssen       // The tablegen header currently does not have any register operands since
210a733d08dSWouter van Oortmerssen       // we use only the stack (_S) instructions.
211a733d08dSWouter van Oortmerssen       // If you hit this that probably means a bad instruction definition in
212a733d08dSWouter van Oortmerssen       // tablegen.
213a733d08dSWouter van Oortmerssen       llvm_unreachable("Register operand in WebAssemblyDisassembler");
21416c16827SSam Clegg     default:
21516c16827SSam Clegg       llvm_unreachable("Unknown operand type in WebAssemblyDisassembler");
21616c16827SSam Clegg     }
21716c16827SSam Clegg   }
21816c16827SSam Clegg   return MCDisassembler::Success;
2191a427287SDan Gohman }
220