xref: /llvm-project/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp (revision 27c769d28ab35034739de4cf62a9970ab87864d9)
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 class WebAssemblyDisassembler final : public MCDisassembler {
40   std::unique_ptr<const MCInstrInfo> MCII;
41 
42   DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
43                               ArrayRef<uint8_t> Bytes, uint64_t Address,
44                               raw_ostream &VStream,
45                               raw_ostream &CStream) const override;
46 
47 public:
48   WebAssemblyDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx,
49                           std::unique_ptr<const MCInstrInfo> MCII)
50       : MCDisassembler(STI, Ctx), MCII(std::move(MCII)) {}
51 };
52 } // end anonymous namespace
53 
54 static MCDisassembler *createWebAssemblyDisassembler(const Target &T,
55                                                      const MCSubtargetInfo &STI,
56                                                      MCContext &Ctx) {
57   std::unique_ptr<const MCInstrInfo> MCII(T.createMCInstrInfo());
58   return new WebAssemblyDisassembler(STI, Ctx, std::move(MCII));
59 }
60 
61 extern "C" void LLVMInitializeWebAssemblyDisassembler() {
62   // Register the disassembler for each target.
63   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget32(),
64                                          createWebAssemblyDisassembler);
65   TargetRegistry::RegisterMCDisassembler(getTheWebAssemblyTarget64(),
66                                          createWebAssemblyDisassembler);
67 }
68 
69 static int nextByte(ArrayRef<uint8_t> Bytes, uint64_t &Size) {
70   if (Size >= Bytes.size())
71     return -1;
72   auto V = Bytes[Size];
73   Size++;
74   return V;
75 }
76 
77 static bool parseLEBImmediate(MCInst &MI, uint64_t &Size,
78                               ArrayRef<uint8_t> Bytes, bool Signed) {
79   unsigned N = 0;
80   const char *Error = nullptr;
81   auto Val = Signed ? decodeSLEB128(Bytes.data() + Size, &N,
82                                     Bytes.data() + Bytes.size(), &Error)
83                     : static_cast<int64_t>(
84                           decodeULEB128(Bytes.data() + Size, &N,
85                                         Bytes.data() + Bytes.size(), &Error));
86   if (Error)
87     return false;
88   Size += N;
89   MI.addOperand(MCOperand::createImm(Val));
90   return true;
91 }
92 
93 template <typename T>
94 bool parseImmediate(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes) {
95   if (Size + sizeof(T) > Bytes.size())
96     return false;
97   T Val;
98   memcpy(&Val, Bytes.data() + Size, sizeof(T));
99   support::endian::byte_swap<T, support::endianness::little>(Val);
100   Size += sizeof(T);
101   if (std::is_floating_point<T>::value) {
102     MI.addOperand(MCOperand::createFPImm(static_cast<double>(Val)));
103   } else {
104     MI.addOperand(MCOperand::createImm(static_cast<int64_t>(Val)));
105   }
106   return true;
107 }
108 
109 MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
110     MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t /*Address*/,
111     raw_ostream & /*OS*/, raw_ostream &CS) const {
112   CommentStream = &CS;
113   Size = 0;
114   auto Opc = nextByte(Bytes, Size);
115   if (Opc < 0)
116     return MCDisassembler::Fail;
117   const auto *WasmInst = &InstructionTable0[Opc];
118   // If this is a prefix byte, indirect to another table.
119   if (WasmInst->ET == ET_Prefix) {
120     WasmInst = nullptr;
121     // Linear search, so far only 2 entries.
122     for (auto PT = PrefixTable; PT->Table; PT++) {
123       if (PT->Prefix == Opc) {
124         WasmInst = PT->Table;
125         break;
126       }
127     }
128     if (!WasmInst)
129       return MCDisassembler::Fail;
130     Opc = nextByte(Bytes, Size);
131     if (Opc < 0)
132       return MCDisassembler::Fail;
133     WasmInst += Opc;
134   }
135   if (WasmInst->ET == ET_Unused)
136     return MCDisassembler::Fail;
137   // At this point we must have a valid instruction to decode.
138   assert(WasmInst->ET == ET_Instruction);
139   MI.setOpcode(WasmInst->Opcode);
140   // Parse any operands.
141   for (uint8_t OPI = 0; OPI < WasmInst->NumOperands; OPI++) {
142     switch (OperandTable[WasmInst->OperandStart + OPI]) {
143     // ULEB operands:
144     case WebAssembly::OPERAND_BASIC_BLOCK:
145     case WebAssembly::OPERAND_LOCAL:
146     case WebAssembly::OPERAND_GLOBAL:
147     case WebAssembly::OPERAND_FUNCTION32:
148     case WebAssembly::OPERAND_OFFSET32:
149     case WebAssembly::OPERAND_P2ALIGN:
150     case WebAssembly::OPERAND_TYPEINDEX:
151     case MCOI::OPERAND_IMMEDIATE: {
152       if (!parseLEBImmediate(MI, Size, Bytes, false))
153         return MCDisassembler::Fail;
154       break;
155     }
156     // SLEB operands:
157     case WebAssembly::OPERAND_I32IMM:
158     case WebAssembly::OPERAND_I64IMM:
159     case WebAssembly::OPERAND_SIGNATURE: {
160       if (!parseLEBImmediate(MI, Size, Bytes, true))
161         return MCDisassembler::Fail;
162       break;
163     }
164     // FP operands.
165     case WebAssembly::OPERAND_F32IMM: {
166       if (!parseImmediate<float>(MI, Size, Bytes))
167         return MCDisassembler::Fail;
168       break;
169     }
170     case WebAssembly::OPERAND_F64IMM: {
171       if (!parseImmediate<double>(MI, Size, Bytes))
172         return MCDisassembler::Fail;
173       break;
174     }
175     // Vector lane operands (not LEB encoded).
176     case WebAssembly::OPERAND_VEC_I8IMM: {
177       if (!parseImmediate<uint8_t>(MI, Size, Bytes))
178         return MCDisassembler::Fail;
179       break;
180     }
181     case WebAssembly::OPERAND_VEC_I16IMM: {
182       if (!parseImmediate<uint16_t>(MI, Size, Bytes))
183         return MCDisassembler::Fail;
184       break;
185     }
186     case WebAssembly::OPERAND_VEC_I32IMM: {
187       if (!parseImmediate<uint32_t>(MI, Size, Bytes))
188         return MCDisassembler::Fail;
189       break;
190     }
191     case WebAssembly::OPERAND_VEC_I64IMM: {
192       if (!parseImmediate<uint64_t>(MI, Size, Bytes))
193         return MCDisassembler::Fail;
194       break;
195     }
196     case MCOI::OPERAND_REGISTER:
197       // The tablegen header currently does not have any register operands since
198       // we use only the stack (_S) instructions.
199       // If you hit this that probably means a bad instruction definition in
200       // tablegen.
201       llvm_unreachable("Register operand in WebAssemblyDisassembler");
202     default:
203       llvm_unreachable("Unknown operand type in WebAssemblyDisassembler");
204     }
205   }
206   return MCDisassembler::Success;
207 }
208