| //===-- Opcode.h ------------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef lldb_Opcode_h |
| #define lldb_Opcode_h |
| |
| #include "lldb/Utility/Endian.h" |
| #include "lldb/lldb-enumerations.h" // for ByteOrder, ByteOrder::eByteOrde... |
| |
| #include "llvm/Support/MathExtras.h" |
| |
| #include <assert.h> // for assert |
| #include <stdint.h> // for uint32_t, uint8_t, uint16_t |
| #include <string.h> |
| |
| namespace lldb_private { |
| class DataExtractor; |
| } |
| namespace lldb_private { |
| class Stream; |
| } |
| |
| namespace lldb { |
| class SBInstruction; |
| } |
| |
| namespace lldb_private { |
| |
| class Opcode { |
| public: |
| enum Type { |
| eTypeInvalid, |
| eType8, |
| eType16, |
| eType16_2, // a 32-bit Thumb instruction, made up of two words |
| eType32, |
| eType64, |
| eTypeBytes |
| }; |
| |
| Opcode() : m_byte_order(lldb::eByteOrderInvalid), m_type(eTypeInvalid) {} |
| |
| Opcode(uint8_t inst, lldb::ByteOrder order) |
| : m_byte_order(order), m_type(eType8) { |
| m_data.inst8 = inst; |
| } |
| |
| Opcode(uint16_t inst, lldb::ByteOrder order) |
| : m_byte_order(order), m_type(eType16) { |
| m_data.inst16 = inst; |
| } |
| |
| Opcode(uint32_t inst, lldb::ByteOrder order) |
| : m_byte_order(order), m_type(eType32) { |
| m_data.inst32 = inst; |
| } |
| |
| Opcode(uint64_t inst, lldb::ByteOrder order) |
| : m_byte_order(order), m_type(eType64) { |
| m_data.inst64 = inst; |
| } |
| |
| Opcode(uint8_t *bytes, size_t length) |
| : m_byte_order(lldb::eByteOrderInvalid) { |
| SetOpcodeBytes(bytes, length); |
| } |
| |
| void Clear() { |
| m_byte_order = lldb::eByteOrderInvalid; |
| m_type = Opcode::eTypeInvalid; |
| } |
| |
| Opcode::Type GetType() const { return m_type; } |
| |
| uint8_t GetOpcode8(uint8_t invalid_opcode = UINT8_MAX) const { |
| switch (m_type) { |
| case Opcode::eTypeInvalid: |
| break; |
| case Opcode::eType8: |
| return m_data.inst8; |
| case Opcode::eType16: |
| break; |
| case Opcode::eType16_2: |
| break; |
| case Opcode::eType32: |
| break; |
| case Opcode::eType64: |
| break; |
| case Opcode::eTypeBytes: |
| break; |
| } |
| return invalid_opcode; |
| } |
| |
| uint16_t GetOpcode16(uint16_t invalid_opcode = UINT16_MAX) const { |
| switch (m_type) { |
| case Opcode::eTypeInvalid: |
| break; |
| case Opcode::eType8: |
| return m_data.inst8; |
| case Opcode::eType16: |
| return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16; |
| case Opcode::eType16_2: |
| break; |
| case Opcode::eType32: |
| break; |
| case Opcode::eType64: |
| break; |
| case Opcode::eTypeBytes: |
| break; |
| } |
| return invalid_opcode; |
| } |
| |
| uint32_t GetOpcode32(uint32_t invalid_opcode = UINT32_MAX) const { |
| switch (m_type) { |
| case Opcode::eTypeInvalid: |
| break; |
| case Opcode::eType8: |
| return m_data.inst8; |
| case Opcode::eType16: |
| return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16; |
| case Opcode::eType16_2: // passthrough |
| case Opcode::eType32: |
| return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32; |
| case Opcode::eType64: |
| break; |
| case Opcode::eTypeBytes: |
| break; |
| } |
| return invalid_opcode; |
| } |
| |
| uint64_t GetOpcode64(uint64_t invalid_opcode = UINT64_MAX) const { |
| switch (m_type) { |
| case Opcode::eTypeInvalid: |
| break; |
| case Opcode::eType8: |
| return m_data.inst8; |
| case Opcode::eType16: |
| return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16; |
| case Opcode::eType16_2: // passthrough |
| case Opcode::eType32: |
| return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32; |
| case Opcode::eType64: |
| return GetEndianSwap() ? llvm::ByteSwap_64(m_data.inst64) : m_data.inst64; |
| case Opcode::eTypeBytes: |
| break; |
| } |
| return invalid_opcode; |
| } |
| |
| void SetOpcode8(uint8_t inst, lldb::ByteOrder order) { |
| m_type = eType8; |
| m_data.inst8 = inst; |
| m_byte_order = order; |
| } |
| |
| void SetOpcode16(uint16_t inst, lldb::ByteOrder order) { |
| m_type = eType16; |
| m_data.inst16 = inst; |
| m_byte_order = order; |
| } |
| |
| void SetOpcode16_2(uint32_t inst, lldb::ByteOrder order) { |
| m_type = eType16_2; |
| m_data.inst32 = inst; |
| m_byte_order = order; |
| } |
| |
| void SetOpcode32(uint32_t inst, lldb::ByteOrder order) { |
| m_type = eType32; |
| m_data.inst32 = inst; |
| m_byte_order = order; |
| } |
| |
| void SetOpcode64(uint64_t inst, lldb::ByteOrder order) { |
| m_type = eType64; |
| m_data.inst64 = inst; |
| m_byte_order = order; |
| } |
| |
| void SetOpcodeBytes(const void *bytes, size_t length) { |
| if (bytes != nullptr && length > 0) { |
| m_type = eTypeBytes; |
| m_data.inst.length = length; |
| assert(length < sizeof(m_data.inst.bytes)); |
| memcpy(m_data.inst.bytes, bytes, length); |
| m_byte_order = lldb::eByteOrderInvalid; |
| } else { |
| m_type = eTypeInvalid; |
| m_data.inst.length = 0; |
| } |
| } |
| |
| int Dump(Stream *s, uint32_t min_byte_width); |
| |
| const void *GetOpcodeBytes() const { |
| return ((m_type == Opcode::eTypeBytes) ? m_data.inst.bytes : nullptr); |
| } |
| |
| uint32_t GetByteSize() const { |
| switch (m_type) { |
| case Opcode::eTypeInvalid: |
| break; |
| case Opcode::eType8: |
| return sizeof(m_data.inst8); |
| case Opcode::eType16: |
| return sizeof(m_data.inst16); |
| case Opcode::eType16_2: // passthrough |
| case Opcode::eType32: |
| return sizeof(m_data.inst32); |
| case Opcode::eType64: |
| return sizeof(m_data.inst64); |
| case Opcode::eTypeBytes: |
| return m_data.inst.length; |
| } |
| return 0; |
| } |
| |
| // Get the opcode exactly as it would be laid out in memory. |
| uint32_t GetData(DataExtractor &data) const; |
| |
| protected: |
| friend class lldb::SBInstruction; |
| |
| const void *GetOpcodeDataBytes() const { |
| switch (m_type) { |
| case Opcode::eTypeInvalid: |
| break; |
| case Opcode::eType8: |
| return &m_data.inst8; |
| case Opcode::eType16: |
| return &m_data.inst16; |
| case Opcode::eType16_2: // passthrough |
| case Opcode::eType32: |
| return &m_data.inst32; |
| case Opcode::eType64: |
| return &m_data.inst64; |
| case Opcode::eTypeBytes: |
| return m_data.inst.bytes; |
| } |
| return nullptr; |
| } |
| |
| lldb::ByteOrder GetDataByteOrder() const; |
| |
| bool GetEndianSwap() const { |
| return (m_byte_order == lldb::eByteOrderBig && |
| endian::InlHostByteOrder() == lldb::eByteOrderLittle) || |
| (m_byte_order == lldb::eByteOrderLittle && |
| endian::InlHostByteOrder() == lldb::eByteOrderBig); |
| } |
| |
| lldb::ByteOrder m_byte_order; |
| |
| Opcode::Type m_type; |
| union { |
| uint8_t inst8; |
| uint16_t inst16; |
| uint32_t inst32; |
| uint64_t inst64; |
| struct { |
| uint8_t bytes[16]; // This must be big enough to handle any opcode for any |
| // supported target. |
| uint8_t length; |
| } inst; |
| } m_data; |
| }; |
| |
| } // namespace lldb_private |
| |
| #endif // lldb_Opcode_h |