//===-- 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/lldb-private.h"
#include "lldb/lldb-public.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/Opcode.h"
#include "lldb/Core/RegisterValue.h"

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,
        
        // 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);

    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 bool
    IsInstructionConditional() { return false; }

    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_
