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 71fc3163b6SThomas 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; 124fc3163b6SThomas 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++) { 154*820c6263SWouter van Oortmerssen auto OT = OperandTable[WasmInst->OperandStart + OPI]; 155*820c6263SWouter van Oortmerssen switch (OT) { 15616c16827SSam Clegg // ULEB operands: 15716c16827SSam Clegg case WebAssembly::OPERAND_BASIC_BLOCK: 15816c16827SSam Clegg case WebAssembly::OPERAND_LOCAL: 15916c16827SSam Clegg case WebAssembly::OPERAND_GLOBAL: 16016c16827SSam Clegg case WebAssembly::OPERAND_FUNCTION32: 16116c16827SSam Clegg case WebAssembly::OPERAND_OFFSET32: 16216c16827SSam Clegg case WebAssembly::OPERAND_P2ALIGN: 16316c16827SSam Clegg case WebAssembly::OPERAND_TYPEINDEX: 16416c16827SSam Clegg case MCOI::OPERAND_IMMEDIATE: { 16516c16827SSam Clegg if (!parseLEBImmediate(MI, Size, Bytes, false)) 16616c16827SSam Clegg return MCDisassembler::Fail; 16716c16827SSam Clegg break; 16816c16827SSam Clegg } 16916c16827SSam Clegg // SLEB operands: 17016c16827SSam Clegg case WebAssembly::OPERAND_I32IMM: 171ad72f685SWouter van Oortmerssen case WebAssembly::OPERAND_I64IMM: { 17216c16827SSam Clegg if (!parseLEBImmediate(MI, Size, Bytes, true)) 17316c16827SSam Clegg return MCDisassembler::Fail; 17416c16827SSam Clegg break; 17516c16827SSam Clegg } 176ad72f685SWouter van Oortmerssen // block_type operands (uint8_t). 177ad72f685SWouter van Oortmerssen case WebAssembly::OPERAND_SIGNATURE: { 178ad72f685SWouter van Oortmerssen if (!parseImmediate<uint8_t>(MI, Size, Bytes)) 179ad72f685SWouter van Oortmerssen return MCDisassembler::Fail; 180ad72f685SWouter van Oortmerssen break; 181ad72f685SWouter van Oortmerssen } 18216c16827SSam Clegg // FP operands. 18316c16827SSam Clegg case WebAssembly::OPERAND_F32IMM: { 18422442924SThomas Lively if (!parseImmediate<float>(MI, Size, Bytes)) 18516c16827SSam Clegg return MCDisassembler::Fail; 18616c16827SSam Clegg break; 18716c16827SSam Clegg } 18816c16827SSam Clegg case WebAssembly::OPERAND_F64IMM: { 18922442924SThomas Lively if (!parseImmediate<double>(MI, Size, Bytes)) 19022442924SThomas Lively return MCDisassembler::Fail; 19122442924SThomas Lively break; 19222442924SThomas Lively } 19322442924SThomas Lively // Vector lane operands (not LEB encoded). 19422442924SThomas Lively case WebAssembly::OPERAND_VEC_I8IMM: { 19522442924SThomas Lively if (!parseImmediate<uint8_t>(MI, Size, Bytes)) 19622442924SThomas Lively return MCDisassembler::Fail; 19722442924SThomas Lively break; 19822442924SThomas Lively } 19922442924SThomas Lively case WebAssembly::OPERAND_VEC_I16IMM: { 20022442924SThomas Lively if (!parseImmediate<uint16_t>(MI, Size, Bytes)) 20122442924SThomas Lively return MCDisassembler::Fail; 20222442924SThomas Lively break; 20322442924SThomas Lively } 20422442924SThomas Lively case WebAssembly::OPERAND_VEC_I32IMM: { 20522442924SThomas Lively if (!parseImmediate<uint32_t>(MI, Size, Bytes)) 20622442924SThomas Lively return MCDisassembler::Fail; 20722442924SThomas Lively break; 20822442924SThomas Lively } 20922442924SThomas Lively case WebAssembly::OPERAND_VEC_I64IMM: { 21022442924SThomas Lively if (!parseImmediate<uint64_t>(MI, Size, Bytes)) 21116c16827SSam Clegg return MCDisassembler::Fail; 21216c16827SSam Clegg break; 21316c16827SSam Clegg } 214*820c6263SWouter van Oortmerssen case WebAssembly::OPERAND_BRLIST: { 215*820c6263SWouter van Oortmerssen int64_t TargetTableLen; 216*820c6263SWouter van Oortmerssen if (!nextLEB(TargetTableLen, Bytes, Size, false)) 217*820c6263SWouter van Oortmerssen return MCDisassembler::Fail; 218*820c6263SWouter van Oortmerssen for (int64_t I = 0; I < TargetTableLen; I++) { 219*820c6263SWouter van Oortmerssen if (!parseLEBImmediate(MI, Size, Bytes, false)) 220*820c6263SWouter van Oortmerssen return MCDisassembler::Fail; 221*820c6263SWouter van Oortmerssen } 222*820c6263SWouter van Oortmerssen // Default case. 223*820c6263SWouter van Oortmerssen if (!parseLEBImmediate(MI, Size, Bytes, false)) 224*820c6263SWouter van Oortmerssen return MCDisassembler::Fail; 225*820c6263SWouter van Oortmerssen break; 226*820c6263SWouter van Oortmerssen } 227a733d08dSWouter van Oortmerssen case MCOI::OPERAND_REGISTER: 228a733d08dSWouter van Oortmerssen // The tablegen header currently does not have any register operands since 229a733d08dSWouter van Oortmerssen // we use only the stack (_S) instructions. 230a733d08dSWouter van Oortmerssen // If you hit this that probably means a bad instruction definition in 231a733d08dSWouter van Oortmerssen // tablegen. 232a733d08dSWouter van Oortmerssen llvm_unreachable("Register operand in WebAssemblyDisassembler"); 23316c16827SSam Clegg default: 23416c16827SSam Clegg llvm_unreachable("Unknown operand type in WebAssemblyDisassembler"); 23516c16827SSam Clegg } 23616c16827SSam Clegg } 23716c16827SSam Clegg return MCDisassembler::Success; 2381a427287SDan Gohman } 239