//===-- EmulateInstruction.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_EmulateInstruction_h_
#define lldb_EmulateInstruction_h_

#include <string>

#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Opcode.h"
#include "lldb/Core/PluginInterface.h"

#include "lldb/Core/Address.h"              // for Address
#include "lldb/lldb-defines.h"              // for DISALLOW_COPY_AND_ASSIGN
#include "lldb/lldb-enumerations.h"         // for RegisterKind, ByteOrder
#include "lldb/lldb-private-enumerations.h" // for InstructionType
#include "lldb/lldb-private-types.h"        // for RegisterInfo
#include "lldb/lldb-types.h"                // for addr_t

#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint64_t, int64_t
namespace lldb_private {
class OptionValueDictionary;
}
namespace lldb_private {
class RegisterContext;
}
namespace lldb_private {
class RegisterValue;
}
namespace lldb_private {
class Stream;
}
namespace lldb_private {
class Target;
}
namespace lldb_private {
class UnwindPlan;
}

namespace lldb_private {

//----------------------------------------------------------------------
/// @class EmulateInstruction EmulateInstruction.h
/// "lldb/Core/EmulateInstruction.h"
/// @brief A class that allows emulation of CPU opcodes.
///
/// This class is a plug-in interface that is accessed through the
/// standard static FindPlugin function call in the EmulateInstruction
/// class. The FindPlugin takes a target triple and returns a new object
/// if there is a plug-in that supports the architecture and OS. Four
/// callbacks and a baton are provided. The four callbacks are read
/// register, write register, read memory and write memory.
///
/// This class is currently designed for these main use cases:
/// - Auto generation of Call Frame Information (CFI) from assembly code
/// - Predicting single step breakpoint locations
/// - Emulating instructions for breakpoint traps
///
/// Objects can be asked to read an instruction which will cause a call
/// to the read register callback to get the PC, followed by a read
/// memory call to read the opcode. If ReadInstruction () returns true,
/// then a call to EmulateInstruction::EvaluateInstruction () can be
/// made. At this point the EmulateInstruction subclass will use all of
/// the callbacks to emulate an instruction.
///
/// Clients that provide the callbacks can either do the read/write
/// registers/memory to actually emulate the instruction on a real or
/// virtual CPU, or watch for the EmulateInstruction::Context which
/// is context for the read/write register/memory which explains why
/// the callback is being called. Examples of a context are:
/// "pushing register 3 onto the stack at offset -12", or "adjusting
/// stack pointer by -16". This extra context allows the generation of
/// CFI information from assembly code without having to actually do
/// the read/write register/memory.
///
/// Clients must be prepared that not all instructions for an
/// Instruction Set Architecture (ISA) will be emulated.
///
/// Subclasses at the very least should implement the instructions that
/// save and restore registers onto the stack and adjustment to the stack
/// pointer. By just implementing a few instructions for an ISA that are
/// the typical prologue opcodes, you can then generate CFI using a
/// class that will soon be available.
///
/// Implementing all of the instructions that affect the PC can then
/// allow single step prediction support.
///
/// Implementing all of the instructions allows for emulation of opcodes
/// for breakpoint traps and will pave the way for "thread centric"
/// debugging. The current debugging model is "process centric" where
/// all threads must be stopped when any thread is stopped; when
/// hitting software breakpoints we must disable the breakpoint by
/// restoring the original breakpoint opcode, single stepping and
/// restoring the breakpoint trap. If all threads were allowed to run
/// then other threads could miss the breakpoint.
///
/// This class centralizes the code that usually is done in separate
/// code paths in a debugger (single step prediction, finding save
/// restore locations of registers for unwinding stack frame variables)
/// and emulating the instruction is just a bonus.
//----------------------------------------------------------------------

class EmulateInstruction : public PluginInterface {
public:
  static EmulateInstruction *FindPlugin(const ArchSpec &arch,
                                        InstructionType supported_inst_type,
                                        const char *plugin_name);

  enum ContextType {
    eContextInvalid = 0,
    // Read an instruction opcode from memory
    eContextReadOpcode,

    // Usually used for writing a register value whose source value is an
    // immediate
    eContextImmediate,

    // Exclusively used when saving a register to the stack as part of the
    // prologue
    eContextPushRegisterOnStack,

    // Exclusively used when restoring a register off the stack as part of
    // the epilogue
    eContextPopRegisterOffStack,

    // Add or subtract a value from the stack
    eContextAdjustStackPointer,

    // Adjust the frame pointer for the current frame
    eContextSetFramePointer,

    // Typically in an epilogue sequence.  Copy the frame pointer back
    // into the stack pointer, use SP for CFA calculations again.
    eContextRestoreStackPointer,

    // Add or subtract a value from a base address register (other than SP)
    eContextAdjustBaseRegister,

    // Add or subtract a value from the PC or store a value to the PC.
    eContextAdjustPC,

    // Used in WriteRegister callbacks to indicate where the
    eContextRegisterPlusOffset,

    // Used in WriteMemory callback to indicate where the data came from
    eContextRegisterStore,

    eContextRegisterLoad,

    // Used when performing a PC-relative branch where the
    eContextRelativeBranchImmediate,

    // Used when performing an absolute branch where the
    eContextAbsoluteBranchRegister,

    // Used when performing a supervisor call to an operating system to
    // provide a service:
    eContextSupervisorCall,

    // Used when performing a MemU operation to read the PC-relative offset
    // from an address.
    eContextTableBranchReadMemory,

    // Used when random bits are written into a register
    eContextWriteRegisterRandomBits,

    // Used when random bits are written to memory
    eContextWriteMemoryRandomBits,

    eContextArithmetic,

    eContextAdvancePC,

    eContextReturnFromException
  };

  enum InfoType {
    eInfoTypeRegisterPlusOffset,
    eInfoTypeRegisterPlusIndirectOffset,
    eInfoTypeRegisterToRegisterPlusOffset,
    eInfoTypeRegisterToRegisterPlusIndirectOffset,
    eInfoTypeRegisterRegisterOperands,
    eInfoTypeOffset,
    eInfoTypeRegister,
    eInfoTypeImmediate,
    eInfoTypeImmediateSigned,
    eInfoTypeAddress,
    eInfoTypeISAAndImmediate,
    eInfoTypeISAAndImmediateSigned,
    eInfoTypeISA,
    eInfoTypeNoArgs
  } InfoType;

  struct Context {
    ContextType type;
    enum InfoType info_type;
    union {
      struct RegisterPlusOffset {
        RegisterInfo reg;      // base register
        int64_t signed_offset; // signed offset added to base register
      } RegisterPlusOffset;

      struct RegisterPlusIndirectOffset {
        RegisterInfo base_reg;   // base register number
        RegisterInfo offset_reg; // offset register kind
      } RegisterPlusIndirectOffset;

      struct RegisterToRegisterPlusOffset {
        RegisterInfo data_reg; // source/target register for data
        RegisterInfo base_reg; // base register for address calculation
        int64_t offset;        // offset for address calculation
      } RegisterToRegisterPlusOffset;

      struct RegisterToRegisterPlusIndirectOffset {
        RegisterInfo base_reg;   // base register for address calculation
        RegisterInfo offset_reg; // offset register for address calculation
        RegisterInfo data_reg;   // source/target register for data
      } RegisterToRegisterPlusIndirectOffset;

      struct RegisterRegisterOperands {
        RegisterInfo
            operand1; // register containing first operand for binary op
        RegisterInfo
            operand2; // register containing second operand for binary op
      } RegisterRegisterOperands;

      int64_t signed_offset; // signed offset by which to adjust self (for
                             // registers only)

      RegisterInfo reg; // plain register

      uint64_t unsigned_immediate; // unsigned immediate value
      int64_t signed_immediate;    // signed immediate value

      lldb::addr_t address; // direct address

      struct ISAAndImmediate {
        uint32_t isa;
        uint32_t unsigned_data32; // immediate data
      } ISAAndImmediate;

      struct ISAAndImmediateSigned {
        uint32_t isa;
        int32_t signed_data32; // signed immediate data
      } ISAAndImmediateSigned;

      uint32_t isa;
    } info;

    Context() : type(eContextInvalid), info_type(eInfoTypeNoArgs) {}

    void SetRegisterPlusOffset(RegisterInfo base_reg, int64_t signed_offset) {
      info_type = eInfoTypeRegisterPlusOffset;
      info.RegisterPlusOffset.reg = base_reg;
      info.RegisterPlusOffset.signed_offset = signed_offset;
    }

    void SetRegisterPlusIndirectOffset(RegisterInfo base_reg,
                                       RegisterInfo offset_reg) {
      info_type = eInfoTypeRegisterPlusIndirectOffset;
      info.RegisterPlusIndirectOffset.base_reg = base_reg;
      info.RegisterPlusIndirectOffset.offset_reg = offset_reg;
    }

    void SetRegisterToRegisterPlusOffset(RegisterInfo data_reg,
                                         RegisterInfo base_reg,
                                         int64_t offset) {
      info_type = eInfoTypeRegisterToRegisterPlusOffset;
      info.RegisterToRegisterPlusOffset.data_reg = data_reg;
      info.RegisterToRegisterPlusOffset.base_reg = base_reg;
      info.RegisterToRegisterPlusOffset.offset = offset;
    }

    void SetRegisterToRegisterPlusIndirectOffset(RegisterInfo base_reg,
                                                 RegisterInfo offset_reg,
                                                 RegisterInfo data_reg) {
      info_type = eInfoTypeRegisterToRegisterPlusIndirectOffset;
      info.RegisterToRegisterPlusIndirectOffset.base_reg = base_reg;
      info.RegisterToRegisterPlusIndirectOffset.offset_reg = offset_reg;
      info.RegisterToRegisterPlusIndirectOffset.data_reg = data_reg;
    }

    void SetRegisterRegisterOperands(RegisterInfo op1_reg,
                                     RegisterInfo op2_reg) {
      info_type = eInfoTypeRegisterRegisterOperands;
      info.RegisterRegisterOperands.operand1 = op1_reg;
      info.RegisterRegisterOperands.operand2 = op2_reg;
    }

    void SetOffset(int64_t signed_offset) {
      info_type = eInfoTypeOffset;
      info.signed_offset = signed_offset;
    }

    void SetRegister(RegisterInfo reg) {
      info_type = eInfoTypeRegister;
      info.reg = reg;
    }

    void SetImmediate(uint64_t immediate) {
      info_type = eInfoTypeImmediate;
      info.unsigned_immediate = immediate;
    }

    void SetImmediateSigned(int64_t signed_immediate) {
      info_type = eInfoTypeImmediateSigned;
      info.signed_immediate = signed_immediate;
    }

    void SetAddress(lldb::addr_t address) {
      info_type = eInfoTypeAddress;
      info.address = address;
    }
    void SetISAAndImmediate(uint32_t isa, uint32_t data) {
      info_type = eInfoTypeISAAndImmediate;
      info.ISAAndImmediate.isa = isa;
      info.ISAAndImmediate.unsigned_data32 = data;
    }

    void SetISAAndImmediateSigned(uint32_t isa, int32_t data) {
      info_type = eInfoTypeISAAndImmediateSigned;
      info.ISAAndImmediateSigned.isa = isa;
      info.ISAAndImmediateSigned.signed_data32 = data;
    }

    void SetISA(uint32_t isa) {
      info_type = eInfoTypeISA;
      info.isa = isa;
    }

    void SetNoArgs() { info_type = eInfoTypeNoArgs; }

    void Dump(Stream &s, EmulateInstruction *instruction) const;
  };

  typedef size_t (*ReadMemoryCallback)(EmulateInstruction *instruction,
                                       void *baton, const Context &context,
                                       lldb::addr_t addr, void *dst,
                                       size_t length);

  typedef size_t (*WriteMemoryCallback)(EmulateInstruction *instruction,
                                        void *baton, const Context &context,
                                        lldb::addr_t addr, const void *dst,
                                        size_t length);

  typedef bool (*ReadRegisterCallback)(EmulateInstruction *instruction,
                                       void *baton,
                                       const RegisterInfo *reg_info,
                                       RegisterValue &reg_value);

  typedef bool (*WriteRegisterCallback)(EmulateInstruction *instruction,
                                        void *baton, const Context &context,
                                        const RegisterInfo *reg_info,
                                        const RegisterValue &reg_value);

  // Type to represent the condition of an instruction. The UINT32 value is
  // reserved for the
  // unconditional case and all other value can be used in an architecture
  // dependent way.
  typedef uint32_t InstructionCondition;
  static const InstructionCondition UnconditionalCondition = UINT32_MAX;

  EmulateInstruction(const ArchSpec &arch);

  ~EmulateInstruction() override = default;

  //----------------------------------------------------------------------
  // Mandatory overrides
  //----------------------------------------------------------------------
  virtual bool
  SupportsEmulatingInstructionsOfType(InstructionType inst_type) = 0;

  virtual bool SetTargetTriple(const ArchSpec &arch) = 0;

  virtual bool ReadInstruction() = 0;

  virtual bool EvaluateInstruction(uint32_t evaluate_options) = 0;

  virtual InstructionCondition GetInstructionCondition() {
    return UnconditionalCondition;
  }

  virtual bool TestEmulation(Stream *out_stream, ArchSpec &arch,
                             OptionValueDictionary *test_data) = 0;

  virtual bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num,
                               RegisterInfo &reg_info) = 0;

  //----------------------------------------------------------------------
  // Optional overrides
  //----------------------------------------------------------------------
  virtual bool SetInstruction(const Opcode &insn_opcode,
                              const Address &inst_addr, Target *target);

  virtual bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan);

  static const char *TranslateRegister(lldb::RegisterKind reg_kind,
                                       uint32_t reg_num, std::string &reg_name);

  //----------------------------------------------------------------------
  // RegisterInfo variants
  //----------------------------------------------------------------------
  bool ReadRegister(const RegisterInfo *reg_info, RegisterValue &reg_value);

  uint64_t ReadRegisterUnsigned(const RegisterInfo *reg_info,
                                uint64_t fail_value, bool *success_ptr);

  bool WriteRegister(const Context &context, const RegisterInfo *ref_info,
                     const RegisterValue &reg_value);

  bool WriteRegisterUnsigned(const Context &context,
                             const RegisterInfo *reg_info, uint64_t reg_value);

  //----------------------------------------------------------------------
  // Register kind and number variants
  //----------------------------------------------------------------------
  bool ReadRegister(lldb::RegisterKind reg_kind, uint32_t reg_num,
                    RegisterValue &reg_value);

  bool WriteRegister(const Context &context, lldb::RegisterKind reg_kind,
                     uint32_t reg_num, const RegisterValue &reg_value);

  uint64_t ReadRegisterUnsigned(lldb::RegisterKind reg_kind, uint32_t reg_num,
                                uint64_t fail_value, bool *success_ptr);

  bool WriteRegisterUnsigned(const Context &context,
                             lldb::RegisterKind reg_kind, uint32_t reg_num,
                             uint64_t reg_value);

  size_t ReadMemory(const Context &context, lldb::addr_t addr, void *dst,
                    size_t dst_len);

  uint64_t ReadMemoryUnsigned(const Context &context, lldb::addr_t addr,
                              size_t byte_size, uint64_t fail_value,
                              bool *success_ptr);

  bool WriteMemory(const Context &context, lldb::addr_t addr, const void *src,
                   size_t src_len);

  bool WriteMemoryUnsigned(const Context &context, lldb::addr_t addr,
                           uint64_t uval, size_t uval_byte_size);

  uint32_t GetAddressByteSize() const { return m_arch.GetAddressByteSize(); }

  lldb::ByteOrder GetByteOrder() const { return m_arch.GetByteOrder(); }

  const Opcode &GetOpcode() const { return m_opcode; }

  lldb::addr_t GetAddress() const { return m_addr; }

  const ArchSpec &GetArchitecture() const { return m_arch; }

  static size_t ReadMemoryFrame(EmulateInstruction *instruction, void *baton,
                                const Context &context, lldb::addr_t addr,
                                void *dst, size_t length);

  static size_t WriteMemoryFrame(EmulateInstruction *instruction, void *baton,
                                 const Context &context, lldb::addr_t addr,
                                 const void *dst, size_t length);

  static bool ReadRegisterFrame(EmulateInstruction *instruction, void *baton,
                                const RegisterInfo *reg_info,
                                RegisterValue &reg_value);

  static bool WriteRegisterFrame(EmulateInstruction *instruction, void *baton,
                                 const Context &context,
                                 const RegisterInfo *reg_info,
                                 const RegisterValue &reg_value);

  static size_t ReadMemoryDefault(EmulateInstruction *instruction, void *baton,
                                  const Context &context, lldb::addr_t addr,
                                  void *dst, size_t length);

  static size_t WriteMemoryDefault(EmulateInstruction *instruction, void *baton,
                                   const Context &context, lldb::addr_t addr,
                                   const void *dst, size_t length);

  static bool ReadRegisterDefault(EmulateInstruction *instruction, void *baton,
                                  const RegisterInfo *reg_info,
                                  RegisterValue &reg_value);

  static bool WriteRegisterDefault(EmulateInstruction *instruction, void *baton,
                                   const Context &context,
                                   const RegisterInfo *reg_info,
                                   const RegisterValue &reg_value);

  void SetBaton(void *baton);

  void SetCallbacks(ReadMemoryCallback read_mem_callback,
                    WriteMemoryCallback write_mem_callback,
                    ReadRegisterCallback read_reg_callback,
                    WriteRegisterCallback write_reg_callback);

  void SetReadMemCallback(ReadMemoryCallback read_mem_callback);

  void SetWriteMemCallback(WriteMemoryCallback write_mem_callback);

  void SetReadRegCallback(ReadRegisterCallback read_reg_callback);

  void SetWriteRegCallback(WriteRegisterCallback write_reg_callback);

  static bool GetBestRegisterKindAndNumber(const RegisterInfo *reg_info,
                                           lldb::RegisterKind &reg_kind,
                                           uint32_t &reg_num);

  static uint32_t GetInternalRegisterNumber(RegisterContext *reg_ctx,
                                            const RegisterInfo &reg_info);

protected:
  ArchSpec m_arch;
  void *m_baton;
  ReadMemoryCallback m_read_mem_callback;
  WriteMemoryCallback m_write_mem_callback;
  ReadRegisterCallback m_read_reg_callback;
  WriteRegisterCallback m_write_reg_callback;
  lldb::addr_t m_addr;
  Opcode m_opcode;

private:
  //------------------------------------------------------------------
  // For EmulateInstruction only
  //------------------------------------------------------------------
  DISALLOW_COPY_AND_ASSIGN(EmulateInstruction);
};

} // namespace lldb_private

#endif // lldb_EmulateInstruction_h_
