1//===-- Opcode.h ------------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef lldb_Opcode_h
10#define lldb_Opcode_h
11
12#include "lldb/Utility/Endian.h"
13#include "lldb/lldb-enumerations.h"
14
15#include "llvm/Support/MathExtras.h"
16
17#include <assert.h>
18#include <stdint.h>
19#include <string.h>
20
21namespace lldb {
22class SBInstruction;
23}
24
25namespace lldb_private {
26class DataExtractor;
27class Stream;
28
29class Opcode {
30public:
31  enum Type {
32    eTypeInvalid,
33    eType8,
34    eType16,
35    eType16_2, // a 32-bit Thumb instruction, made up of two words
36    eType32,
37    eType64,
38    eTypeBytes
39  };
40
41  Opcode() : m_byte_order(lldb::eByteOrderInvalid), m_type(eTypeInvalid) {}
42
43  Opcode(uint8_t inst, lldb::ByteOrder order)
44      : m_byte_order(order), m_type(eType8) {
45    m_data.inst8 = inst;
46  }
47
48  Opcode(uint16_t inst, lldb::ByteOrder order)
49      : m_byte_order(order), m_type(eType16) {
50    m_data.inst16 = inst;
51  }
52
53  Opcode(uint32_t inst, lldb::ByteOrder order)
54      : m_byte_order(order), m_type(eType32) {
55    m_data.inst32 = inst;
56  }
57
58  Opcode(uint64_t inst, lldb::ByteOrder order)
59      : m_byte_order(order), m_type(eType64) {
60    m_data.inst64 = inst;
61  }
62
63  Opcode(uint8_t *bytes, size_t length)
64      : m_byte_order(lldb::eByteOrderInvalid) {
65    SetOpcodeBytes(bytes, length);
66  }
67
68  void Clear() {
69    m_byte_order = lldb::eByteOrderInvalid;
70    m_type = Opcode::eTypeInvalid;
71  }
72
73  Opcode::Type GetType() const { return m_type; }
74
75  uint8_t GetOpcode8(uint8_t invalid_opcode = UINT8_MAX) const {
76    switch (m_type) {
77    case Opcode::eTypeInvalid:
78      break;
79    case Opcode::eType8:
80      return m_data.inst8;
81    case Opcode::eType16:
82      break;
83    case Opcode::eType16_2:
84      break;
85    case Opcode::eType32:
86      break;
87    case Opcode::eType64:
88      break;
89    case Opcode::eTypeBytes:
90      break;
91    }
92    return invalid_opcode;
93  }
94
95  uint16_t GetOpcode16(uint16_t invalid_opcode = UINT16_MAX) const {
96    switch (m_type) {
97    case Opcode::eTypeInvalid:
98      break;
99    case Opcode::eType8:
100      return m_data.inst8;
101    case Opcode::eType16:
102      return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16;
103    case Opcode::eType16_2:
104      break;
105    case Opcode::eType32:
106      break;
107    case Opcode::eType64:
108      break;
109    case Opcode::eTypeBytes:
110      break;
111    }
112    return invalid_opcode;
113  }
114
115  uint32_t GetOpcode32(uint32_t invalid_opcode = UINT32_MAX) const {
116    switch (m_type) {
117    case Opcode::eTypeInvalid:
118      break;
119    case Opcode::eType8:
120      return m_data.inst8;
121    case Opcode::eType16:
122      return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16;
123    case Opcode::eType16_2: // passthrough
124    case Opcode::eType32:
125      return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32;
126    case Opcode::eType64:
127      break;
128    case Opcode::eTypeBytes:
129      break;
130    }
131    return invalid_opcode;
132  }
133
134  uint64_t GetOpcode64(uint64_t invalid_opcode = UINT64_MAX) const {
135    switch (m_type) {
136    case Opcode::eTypeInvalid:
137      break;
138    case Opcode::eType8:
139      return m_data.inst8;
140    case Opcode::eType16:
141      return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16;
142    case Opcode::eType16_2: // passthrough
143    case Opcode::eType32:
144      return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32;
145    case Opcode::eType64:
146      return GetEndianSwap() ? llvm::ByteSwap_64(m_data.inst64) : m_data.inst64;
147    case Opcode::eTypeBytes:
148      break;
149    }
150    return invalid_opcode;
151  }
152
153  void SetOpcode8(uint8_t inst, lldb::ByteOrder order) {
154    m_type = eType8;
155    m_data.inst8 = inst;
156    m_byte_order = order;
157  }
158
159  void SetOpcode16(uint16_t inst, lldb::ByteOrder order) {
160    m_type = eType16;
161    m_data.inst16 = inst;
162    m_byte_order = order;
163  }
164
165  void SetOpcode16_2(uint32_t inst, lldb::ByteOrder order) {
166    m_type = eType16_2;
167    m_data.inst32 = inst;
168    m_byte_order = order;
169  }
170
171  void SetOpcode32(uint32_t inst, lldb::ByteOrder order) {
172    m_type = eType32;
173    m_data.inst32 = inst;
174    m_byte_order = order;
175  }
176
177  void SetOpcode64(uint64_t inst, lldb::ByteOrder order) {
178    m_type = eType64;
179    m_data.inst64 = inst;
180    m_byte_order = order;
181  }
182
183  void SetOpcodeBytes(const void *bytes, size_t length) {
184    if (bytes != nullptr && length > 0) {
185      m_type = eTypeBytes;
186      m_data.inst.length = length;
187      assert(length < sizeof(m_data.inst.bytes));
188      memcpy(m_data.inst.bytes, bytes, length);
189      m_byte_order = lldb::eByteOrderInvalid;
190    } else {
191      m_type = eTypeInvalid;
192      m_data.inst.length = 0;
193    }
194  }
195
196  int Dump(Stream *s, uint32_t min_byte_width);
197
198  const void *GetOpcodeBytes() const {
199    return ((m_type == Opcode::eTypeBytes) ? m_data.inst.bytes : nullptr);
200  }
201
202  uint32_t GetByteSize() const {
203    switch (m_type) {
204    case Opcode::eTypeInvalid:
205      break;
206    case Opcode::eType8:
207      return sizeof(m_data.inst8);
208    case Opcode::eType16:
209      return sizeof(m_data.inst16);
210    case Opcode::eType16_2: // passthrough
211    case Opcode::eType32:
212      return sizeof(m_data.inst32);
213    case Opcode::eType64:
214      return sizeof(m_data.inst64);
215    case Opcode::eTypeBytes:
216      return m_data.inst.length;
217    }
218    return 0;
219  }
220
221  // Get the opcode exactly as it would be laid out in memory.
222  uint32_t GetData(DataExtractor &data) const;
223
224protected:
225  friend class lldb::SBInstruction;
226
227  const void *GetOpcodeDataBytes() const {
228    switch (m_type) {
229    case Opcode::eTypeInvalid:
230      break;
231    case Opcode::eType8:
232      return &m_data.inst8;
233    case Opcode::eType16:
234      return &m_data.inst16;
235    case Opcode::eType16_2: // passthrough
236    case Opcode::eType32:
237      return &m_data.inst32;
238    case Opcode::eType64:
239      return &m_data.inst64;
240    case Opcode::eTypeBytes:
241      return m_data.inst.bytes;
242    }
243    return nullptr;
244  }
245
246  lldb::ByteOrder GetDataByteOrder() const;
247
248  bool GetEndianSwap() const {
249    return (m_byte_order == lldb::eByteOrderBig &&
250            endian::InlHostByteOrder() == lldb::eByteOrderLittle) ||
251           (m_byte_order == lldb::eByteOrderLittle &&
252            endian::InlHostByteOrder() == lldb::eByteOrderBig);
253  }
254
255  lldb::ByteOrder m_byte_order;
256
257  Opcode::Type m_type;
258  union {
259    uint8_t inst8;
260    uint16_t inst16;
261    uint32_t inst32;
262    uint64_t inst64;
263    struct {
264      uint8_t bytes[16]; // This must be big enough to handle any opcode for any
265                         // supported target.
266      uint8_t length;
267    } inst;
268  } m_data;
269};
270
271} // namespace lldb_private
272
273#endif // lldb_Opcode_h
274