xref: /llvm-project/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp (revision fc3163b67a87e93e8beda976ccd16418ca879284)
1 //==- WebAssemblyDisassembler.cpp - Disassembler for WebAssembly -*- C++ -*-==//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// This file is part of the WebAssembly Disassembler.
12 ///
13 /// It contains code to translate the data produced by the decoder into
14 /// MCInsts.
15 ///
16 //===----------------------------------------------------------------------===//
17 
18 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
19 #include "llvm/MC/MCContext.h"
20 #include "llvm/MC/MCDisassembler/MCDisassembler.h"
21 #include "llvm/MC/MCFixedLenDisassembler.h"
22 #include "llvm/MC/MCInst.h"
23 #include "llvm/MC/MCInstrInfo.h"
24 #include "llvm/MC/MCSubtargetInfo.h"
25 #include "llvm/MC/MCSymbol.h"
26 #include "llvm/Support/Endian.h"
27 #include "llvm/Support/LEB128.h"
28 #include "llvm/Support/TargetRegistry.h"
29 
30 using namespace llvm;
31 
32 #define DEBUG_TYPE "wasm-disassembler"
33 
34 using DecodeStatus = MCDisassembler::DecodeStatus;
35 
36 #include "WebAssemblyGenDisassemblerTables.inc"
37 
38 namespace {
39 static constexpr int WebAssemblyInstructionTableSize = 256;
40 
41 class WebAssemblyDisassembler final : public MCDisassembler {
42   std::unique_ptr<const MCInstrInfo> MCII;
43 
44   DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
45                               ArrayRef<uint8_t> Bytes, uint64_t Address,
46                               raw_ostream &VStream,
47                               raw_ostream &CStream) const override;
48 
49 public:
50   WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx,
51                           std::unique_ptr<const MCInstrInfo> MCII)
52       : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {}
53 };
54 } // end anonymous namespace
55 
56 static MCDisassembler *createWebAssemblyDisassembler(const Target &T,
57                                                      const MCSubtargetInfo &STI,
58                                                      MCContext &Ctx) {
59   std::unique_ptr<const MCInstrInfo> MCII(T.createMCInstrInfo());
60   return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII));
61 }
62 
63 extern "C" void LLVMInitializeWebAssemblyDisassembler() {
64   // Register the disassembler for each target.
65   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget32(),
66                                          createWebAssemblyDisassembler);
67   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget64(),
68                                          createWebAssemblyDisassembler);
69 }
70 
71 static int nextByte(ArrayRef<uint8_t> Bytes, uint64_t &Size) {
72   if (Size >= Bytes.size())
73     return -1;
74   auto V = Bytes[Size];
75   Size++;
76   return V;
77 }
78 
79 static bool nextLEB(int64_t &Val, ArrayRef<uint8_t> Bytes, uint64_t &Size,
80                     bool Signed = false) {
81   unsigned N = 0;
82   const char *Error = nullptr;
83   Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N,
84                                Bytes.data() + Bytes.size(), &Error)
85                : static_cast<int64_t>(decodeULEB128(Bytes.data() + Size, &N,
86                                                     Bytes.data() + Bytes.size(),
87                                                     &Error));
88   if (Error)
89     return false;
90   Size += N;
91   return true;
92 }
93 
94 static bool parseLEBImmediate(MCInst &MI, uint64_t &Size,
95                               ArrayRef<uint8_t> Bytes, bool Signed) {
96   int64_t Val;
97   if (!nextLEB(Val, Bytes, Size, Signed))
98     return false;
99   MI.addOperand(MCOperand::createImm(Val));
100   return true;
101 }
102 
103 template <typename T>
104 bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
105   if (Size + sizeof(T) > Bytes.size())
106     return false;
107   T Val;
108   memcpy(&Val, Bytes.data() + Size, sizeof(T));
109   support::endian::byte_swap<T, support::endianness::little>(Val);
110   Size += sizeof(T);
111   if (std::is_floating_point<T>::value) {
112     MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
113   } else {
114     MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val)));
115   }
116   return true;
117 }
118 
119 MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
120     MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/,
121     raw_ostream & /*OS*/, raw_ostream &CS) const {
122   CommentStream = &CS;
123   Size = 0;
124   int Opc = nextByte(Bytes, Size);
125   if (Opc < 0)
126     return MCDisassembler::Fail;
127   const auto *WasmInst = &InstructionTable0[Opc];
128   // If this is a prefix byte, indirect to another table.
129   if (WasmInst->ET == ET_Prefix) {
130     WasmInst = nullptr;
131     // Linear search, so far only 2 entries.
132     for (auto PT = PrefixTable; PT->Table; PT++) {
133       if (PT->Prefix == Opc) {
134         WasmInst = PT->Table;
135         break;
136       }
137     }
138     if (!WasmInst)
139       return MCDisassembler::Fail;
140     int64_t PrefixedOpc;
141     if (!nextLEB(PrefixedOpc, Bytes, Size))
142       return MCDisassembler::Fail;
143     if (PrefixedOpc < 0 || PrefixedOpc >= WebAssemblyInstructionTableSize)
144       return MCDisassembler::Fail;
145     WasmInst += PrefixedOpc;
146   }
147   if (WasmInst->ET == ET_Unused)
148     return MCDisassembler::Fail;
149   // At this point we must have a valid instruction to decode.
150   assert(WasmInst->ET == ET_Instruction);
151   MI.setOpcode(WasmInst->Opcode);
152   // Parse any operands.
153   for (uint8_t OPI = 0; OPI < WasmInst->NumOperands; OPI++) {
154     switch (OperandTable[WasmInst->OperandStart + OPI]) {
155     // ULEB operands:
156     case WebAssembly::OPERAND_BASIC_BLOCK:
157     case WebAssembly::OPERAND_LOCAL:
158     case WebAssembly::OPERAND_GLOBAL:
159     case WebAssembly::OPERAND_FUNCTION32:
160     case WebAssembly::OPERAND_OFFSET32:
161     case WebAssembly::OPERAND_P2ALIGN:
162     case WebAssembly::OPERAND_TYPEINDEX:
163     case MCOI::OPERAND_IMMEDIATE: {
164       if (!parseLEBImmediate(MI, Size, Bytes, false))
165         return MCDisassembler::Fail;
166       break;
167     }
168     // SLEB operands:
169     case WebAssembly::OPERAND_I32IMM:
170     case WebAssembly::OPERAND_I64IMM:
171     case WebAssembly::OPERAND_SIGNATURE: {
172       if (!parseLEBImmediate(MI, Size, Bytes, true))
173         return MCDisassembler::Fail;
174       break;
175     }
176     // FP operands.
177     case WebAssembly::OPERAND_F32IMM: {
178       if (!parseImmediate<float>(MI, Size, Bytes))
179         return MCDisassembler::Fail;
180       break;
181     }
182     case WebAssembly::OPERAND_F64IMM: {
183       if (!parseImmediate<double>(MI, Size, Bytes))
184         return MCDisassembler::Fail;
185       break;
186     }
187     // Vector lane operands (not LEB encoded).
188     case WebAssembly::OPERAND_VEC_I8IMM: {
189       if (!parseImmediate<uint8_t>(MI, Size, Bytes))
190         return MCDisassembler::Fail;
191       break;
192     }
193     case WebAssembly::OPERAND_VEC_I16IMM: {
194       if (!parseImmediate<uint16_t>(MI, Size, Bytes))
195         return MCDisassembler::Fail;
196       break;
197     }
198     case WebAssembly::OPERAND_VEC_I32IMM: {
199       if (!parseImmediate<uint32_t>(MI, Size, Bytes))
200         return MCDisassembler::Fail;
201       break;
202     }
203     case WebAssembly::OPERAND_VEC_I64IMM: {
204       if (!parseImmediate<uint64_t>(MI, Size, Bytes))
205         return MCDisassembler::Fail;
206       break;
207     }
208     case MCOI::OPERAND_REGISTER:
209       // The tablegen header currently does not have any register operands since
210       // we use only the stack (_S) instructions.
211       // If you hit this that probably means a bad instruction definition in
212       // tablegen.
213       llvm_unreachable("Register operand in WebAssemblyDisassembler");
214     default:
215       llvm_unreachable("Unknown operand type in WebAssemblyDisassembler");
216     }
217   }
218   return MCDisassembler::Success;
219 }
220