/*========================== begin_copyright_notice ============================
Copyright (C) 2021-2026 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/

/*!
 * @file GEN registers
 */

#ifndef GT_REG_H_
#define GT_REG_H_

#include <string>
#include <sstream>

#include "ged.h"
#include "gt_basic_defs.h"

namespace gtpin
{
#pragma pack(push, 8)

/*! @addtogroup GTPIN_GEN_REG 
    @{
 */
using GtRegNum      = Uint32Validatable<struct _GtRegNum>;      ///< Register number
using GtSubRegNum   = Uint32Validatable<struct _GtSubRegNum>;   ///< Sub-register number
using GtVregNum     = Uint32Validatable<struct _VregNum>;       ///< Ordinal number of the virtual register
/*! @} */

/* ===================================================================================================== */
// Class GtRegFileType
/* ===================================================================================================== */
/*! @ingroup GTPIN_GEN_REG
 * @brief Type of the instruction operand. Specifies GEN register file or immediate
 */
class GtRegFileType
{
public:
    GtRegFileType(GED_REG_FILE v = GED_REG_FILE_INVALID) : _value(v) {} ///< Conversion constructor
    operator GED_REG_FILE() const { return _value; }                    ///< Conversion operator

    bool operator == (const GtRegFileType& other) const { return _value == other._value; }
    bool operator != (const GtRegFileType& other) const { return !(*this == other); }
    bool operator <  (const GtRegFileType& other) const { return _value < other._value; }

    bool        IsValid()   const { return _value != GED_REG_FILE_INVALID; }
    bool        IsArf()     const { return _value == GED_REG_FILE_ARF; }    ///< Return true for an architectural (ARF) register type
    bool        IsGrf()     const { return _value == GED_REG_FILE_GRF; }    ///< Return true for a general purpose (GRF) register type
    bool        IsImm()     const { return _value == GED_REG_FILE_IMM; }    ///< Return true for an immediate (IMM) type
    bool        IsReg()     const { return (IsArf() || IsGrf()); }          ///< Return true for a register type
    const char* ToString()  const { return GED_GetRegFileString(_value); }  ///< Return string representation
private:
    Enum32T<GED_REG_FILE> _value;
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtRegFileType);

/* ===================================================================================================== */
// Class GtArchRegType
/* ===================================================================================================== */
/*! @ingroup GTPIN_GEN_REG
 * @brief Type of the architecture (ARF) register
 */
class GtArchRegType
{
public:
    GtArchRegType(GED_ARCH_REG v = GED_ARCH_REG_INVALID) : _value(v) {} ///< Conversion constructor
    operator GED_ARCH_REG() const { return _value; }                    ///< Conversion operator

    bool operator == (const GtArchRegType& other) const { return _value == other._value; } ///< Equality operator
    bool operator != (const GtArchRegType& other) const { return !(*this == other); }      ///< Inequality operator
    bool operator <  (const GtArchRegType& other) const { return _value < other._value; }  ///< Operator 'less'

    bool        IsValid()           const { return _value != GED_ARCH_REG_INVALID; } ///< Return true for a valid register type
    bool        IsNull()            const { return _value == GED_ARCH_REG_null; }    ///< Return true for the NULL regiser type
    bool        IsIp()              const { return _value == GED_ARCH_REG_ip; }      ///< Return true for the IP regiser type
    bool        IsState()           const { return _value == GED_ARCH_REG_sr0; }     ///< Return true for the STATE regiser type
    bool        IsAddr()            const { return _value == GED_ARCH_REG_a0; }      ///< Return true for the ADDRESS regiser type
    bool        IsFlag()            const { return _value == GED_ARCH_REG_f; }       ///< Return true for the FLAG regiser type
    bool        IsAcc()             const { return _value == GED_ARCH_REG_acc; }     ///< Return true for the ACC regiser type
    bool        IsTimeStamp()       const { return _value == GED_ARCH_REG_tm0; }     ///< Return true for the TM regiser type
    bool        IsChannelEnable()   const { return _value == GED_ARCH_REG_ce; }      ///< Return true for the CE regiser type
    bool        IsDebug()           const { return _value == GED_ARCH_REG_dbg0; }    ///< Return true for the DBG regiser type
    bool        IsStackPointer()    const { return _value == GED_ARCH_REG_sp; }      ///< Return true for the SP regiser type
    bool        IsScalar()          const { return _value == GED_ARCH_REG_s; }       ///< Return true for the SCALAR regiser type
    const char* ToString()          const { return GED_GetArchRegString(_value); }   ///< Return string representation

private:
    Enum32T<GED_ARCH_REG> _value;
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtArchRegType);

/* ===================================================================================================== */
// Class GtReg
/* ===================================================================================================== */
/*! @ingroup GTPIN_GEN_REG
 * @brief Register descriptor.
 *
 * Specifies position and size of the register (element) in the register file.
 */
class GtReg
{
public:
    /// Construct invalid register descriptor
    GtReg() : _elementSize(0) {}

    /*!
     * @brief Construct descriptor of a GEN register
     * 
     * @param[in] regType       Register type, either GRF or ARF
     * @param[in] archRegType   Type of the architecture register; valid only if regType == GED_REG_FILE_ARF
     * @param[in] regNum        Register number.
     *                          In GRF, this is the register index within GRF array
     *                          In ARF, this is the register ordinal number within 'archRegType' registers
     * @param[in] subRegNum     Sub-register number - index of the data element
     * @param[in] elementSize   Size of the data element in bytes
     * @pre regType.IsGrf() || regType.IsArf()
     * @pre regType.IsGrf() || archRegType.IsValid()
     */
    inline GtReg(GtRegFileType regType, GtArchRegType archRegType,
                 GtRegNum regNum, GtSubRegNum subRegNum, uint32_t elementSize);

    /*!
     * @brief Construct descriptor for a partial register, which consists of 'size' bytes starting at
     * (size * index) offset within 'enclosingReg' register
     * 
     * @param[in] enclosingReg  Register, containing the partial register to be constructed
     * @param[in] size          Size of the partial register, in bytes
     * @param[in] index         Index of the partial register within 'enclosingReg'
     * @pre size != 0
     * @pre enclosingReg.ElementSize() % size == 0
     * @pre enclosingReg.ElementSize() > size * index
     */
    inline GtReg(const GtReg& enclosingReg, uint32_t size, uint32_t index);

    /*!
     * @internal Associate the specified virtual register with this register descriptor and the corresponding
     * instruction operand.
     * 
     * Register allocation mechanism in GTPin substitutes all virtual registers in instruction operands
     * with appropriate physical registers before dispatching the instrumented code for execution.
     * Only GRF registers can be virtualized.
     * @param[in] vregNum   Virtual register number to be associated with this register descriptor
     * @return this descriptor
     * @pre IsGrf() || !vregNum.IsValid()
     */
    inline GtReg& AssignVreg(GtVregNum vregNum);

    /*!
     * @brief Set specified size of the register in bytes. Change subregister number accordingly.
     * @pre elementSize != 0
     * @pre SubRegNumInBytes() % elementSize == 0
     */
    inline GtReg& Resize(uint32_t elementSize);

    inline bool operator == (const GtReg& other) const;                                 ///< Equality operator
    inline bool operator != (const GtReg& other) const  { return !(*this == other); }   ///< Inequality operator
    inline bool operator <  (const GtReg& other) const;                                 ///< Operator 'less'

    /// Return true if this is a valid register
    inline operator bool() const  { return IsValid(); }

    /// Return true if this and specified registers are equal regardless of the virtual register assignment
    inline bool IsSamePreg(const GtReg& other) const;

    /// Return true if this register contains all bits of the specified register
    inline bool Contains(const GtReg& other) const;

    inline bool            IsValid()            const { return _regType.IsValid(); } ///< Return true for a valid register
    inline bool            IsGrf()              const { return _regType.IsGrf(); }   ///< Return true for a GRF register
    inline bool            IsArf()              const { return _regType.IsArf(); }   ///< Return true for an ARF register
    inline bool            IsVirtual()          const { return _vregNum.IsValid(); } ///< @internal Return true for a virtual register
    inline GtRegFileType   Type()               const { return _regType; }           ///< Return type of the register file
    inline GtArchRegType   ArchRegType()        const { return _archRegType; }       ///< Return type of the ARF register
    inline GtRegNum        RegNum()             const { return _regNum; }            ///< Return the register number
    inline GtSubRegNum     SubRegNum()          const { return _subRegNum; }         ///< Return the subregister number
    inline uint32_t        ElementSize()        const { return _elementSize; }       ///< Return size of the data element in bytes
    inline uint32_t        SubRegNumInBytes()   const { return _subRegNum * _elementSize; } ///< Return offset of the subregister within the enclosing register
    inline GtVregNum       VregNumber()         const { return _vregNum; }           ///< @internal Return the virtual register number

    inline bool            IsNullReg()          const { return ArchRegType().IsNull(); }            ///< Return true for NULL regiser
    inline bool            IsIpReg()            const { return ArchRegType().IsIp(); }              ///< Return true for IP regiser
    inline bool            IsStateReg()         const { return ArchRegType().IsState(); }           ///< Return true for STATE regiser
    inline bool            IsAddrReg()          const { return ArchRegType().IsAddr(); }            ///< Return true for ADDRESS regiser
    inline bool            IsFlagReg()          const { return ArchRegType().IsFlag(); }            ///< Return true for FLAG regiser
    inline bool            IsAccReg()           const { return ArchRegType().IsAcc(); }             ///< Return true for ACC regiser
    inline bool            IsTimeStampReg()     const { return ArchRegType().IsTimeStamp(); }       ///< Return true for TM regiser
    inline bool            IsChannelEnableReg() const { return ArchRegType().IsChannelEnable(); }   ///< Return true for CE regiser
    inline bool            IsDebugReg()         const { return ArchRegType().IsDebug(); }           ///< Return true for DBG regiser
    inline bool            IsStackPointerReg()  const { return ArchRegType().IsStackPointer(); }    ///< Return true for SP regiser
    inline bool            IsPerfMonReg()       const { return (IsTimeStampReg() && (SubRegNum() == 3)); } ///< Return true for PM regiser

    inline GtReg& SetRegNum(GtRegNum regNum)          { _regNum = regNum; return *this; }       ///< Set the specified register number
    inline GtReg& SetSubRegNum(GtSubRegNum subRegNum) { _subRegNum = subRegNum; return *this; } ///< Set the specified subregister number

    inline std::string ToString() const;    ///< Return string representation

private:
    GtRegFileType   _regType;       ///< Register (file) type
    GtArchRegType   _archRegType;   ///< ARF register type
    GtRegNum        _regNum;        ///< Register number
    GtSubRegNum     _subRegNum;     ///< Subregister number
    uint32_t        _elementSize;   ///< Size of the register in bytes
    GtVregNum       _vregNum;       ///< Number of a virtual register represented by this descriptor, or GtVregNum::Invalid()
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtReg);

/* ===================================================================================================== */
// Register makers
/* ===================================================================================================== */

/// Construct GRF register
inline GtReg GrfReg(GtRegNum regNum, GtSubRegNum subRegNum = 0, uint32_t elementSize = sizeof(uint32_t),
                    GtVregNum vregNum = GtVregNum::Invalid())
{
    return GtReg(GED_REG_FILE_GRF, GtArchRegType(), regNum, subRegNum, elementSize).AssignVreg(vregNum);
}

/// Construct ARF register
inline GtReg ArfReg(GtArchRegType archRegType, GtRegNum regNum, GtSubRegNum subRegNum = 0,
                    uint32_t elementSize = sizeof(uint32_t))
{
    return GtReg(GED_REG_FILE_ARF, archRegType, regNum, subRegNum, elementSize);
}

/*! @addtogroup GTPIN_GEN_REG 
    @{
 */
inline GtReg  NullReg()                                         { return ArfReg(GED_ARCH_REG_null, 0, 0); }             ///< Make NULL register
inline GtReg  IpReg()                                           { return ArfReg(GED_ARCH_REG_ip, 0, 0); }               ///< Make IP register
inline GtReg  StateReg(uint32_t subRegNum = 0)                  { return ArfReg(GED_ARCH_REG_sr0, 0, subRegNum); }      ///< Make STATE register
inline GtReg  TimeStampReg(uint32_t subRegNum = 0)              { return ArfReg(GED_ARCH_REG_tm0, 0, subRegNum); }      ///< Make TM register
inline GtReg  ChannelEnableReg()                                { return ArfReg(GED_ARCH_REG_ce, 0, 0); }               ///< Make CE register
inline GtReg  DebugReg(uint32_t subRegNum = 0)                  { return ArfReg(GED_ARCH_REG_dbg0, 0, subRegNum); }     ///< Make DBG register
inline GtReg  StackPointerReg(uint32_t subRegNum = 0)           { return ArfReg(GED_ARCH_REG_sp, 0, subRegNum); }       ///< Make SP register
inline GtReg  AccReg(uint32_t regNum, uint32_t subRegNum = 0)   { return ArfReg(GED_ARCH_REG_acc, regNum, subRegNum); } ///< Make ACC register
inline GtReg  ControlReg(uint32_t subRegNum = 0)                { return ArfReg(GED_ARCH_REG_cr0, 0, subRegNum); }      ///< Make CONTROL register
inline GtReg  DispatchMaskReg()                                 { return StateReg(2); }                                 ///< Make DM register
inline GtReg  PerfMonReg()                                      { return TimeStampReg(3); }                             ///< Make PM register
inline GtReg  FcReg(uint32_t regNum, uint32_t subRegNum)        { return ArfReg(GED_ARCH_REG_fc, regNum, subRegNum); }  ///< Make FC register
inline GtReg  A0Reg(uint32_t subRegNum = 0)                     { return ArfReg(GED_ARCH_REG_a0, 0, subRegNum); }       ///< Make ADDR register

/*!
 * @brief Make an ADDRESS register with the specified sub-register number and the element size
 * 
 * @param[in] subRegNum     Sub-register number - index of the data element
 * @param[in] elementSize   Size of the sub-register in bytes. Valid values are 2 or 4 bytes
 * @pre elementSize == sizeof(uint16_t) || elementSize == sizeof(uint32_t)
 */
inline GtReg AddrReg(uint32_t subRegNum = 0, uint32_t elementSize = sizeof(uint32_t))
{
    GTPIN_ASSERT((elementSize == sizeof(uint16_t)) || (elementSize == sizeof(uint32_t)));
    return ArfReg(GED_ARCH_REG_a0, 0, subRegNum, elementSize);
}
inline GtReg AddrRegDw(uint32_t subRegNum = 0) { return AddrReg(subRegNum, sizeof(uint32_t)); } ///< Make a0.{subRegNum}:ud register
inline GtReg AddrRegW(uint32_t subRegNum = 0)  { return AddrReg(subRegNum, sizeof(uint16_t)); } ///< Make a0.{subRegNum}:uw register

/*!
 * @brief Make a FLAG register with the specified sub-register number and the element size
 * 
 * @param[in] subRegNum     Sub-register number - index of the data element
 * @param[in] elementSize   Size of the sub-register in bytes. Valid values are 2 or 4 bytes
 * @pre elementSize == sizeof(uint16_t) || elementSize == sizeof(uint32_t)
 */
inline GtReg FlagReg(uint32_t flagNum, uint32_t subRegNum = 0, uint32_t elementSize = sizeof(uint32_t))
{
    GTPIN_ASSERT((elementSize == sizeof(uint16_t)) || (elementSize == sizeof(uint32_t)));
    return ArfReg(GED_ARCH_REG_f, flagNum, subRegNum, elementSize);
}

/// Make f{flagNum}.{subRegNum}:ud register
inline GtReg FlagRegDw(uint32_t flagNum) { return FlagReg(flagNum, 0, sizeof(uint32_t)); }
/// Make f{flagNum}.{subRegNum}:uw register
inline GtReg FlagRegW(uint32_t flagNum, uint32_t subRegNum = 0) { return FlagReg(flagNum, subRegNum, sizeof(uint16_t)); }

/*! @} */

/* ===================================================================================================== */
// GtReg implementation
/* ===================================================================================================== */
GtReg::GtReg(GtRegFileType regType, GtArchRegType archRegType, GtRegNum regNum, GtSubRegNum subRegNum, uint32_t elementSize) :
            _regType(regType), _archRegType(archRegType), _regNum(regNum), _subRegNum(subRegNum), _elementSize(elementSize)
{
    GTPIN_ASSERT(_regType.IsGrf() || _regType.IsArf());
    GTPIN_ASSERT(_regType.IsGrf() || _archRegType.IsValid());
}

GtReg::GtReg(const GtReg& enclosingReg, uint32_t size, uint32_t index):
    _regType(enclosingReg._regType),
    _archRegType(enclosingReg._archRegType),
    _regNum(enclosingReg._regNum),
    _elementSize(size),
    _vregNum(enclosingReg._vregNum)
{
    GTPIN_ASSERT(size != 0);
    GTPIN_ASSERT(enclosingReg.ElementSize() % size == 0);
    GTPIN_ASSERT(enclosingReg.ElementSize() > size * index);
    _subRegNum = (enclosingReg.SubRegNumInBytes() / size) + index;
}

GtReg& GtReg::AssignVreg(GtVregNum vregNum)
{
    GTPIN_ASSERT(IsGrf() || !vregNum.IsValid());
    _vregNum = vregNum;
    return *this;
}

bool GtReg::IsSamePreg(const GtReg& other) const
{
    return ((_regType       == other._regType)          &&
            (_archRegType   == other._archRegType)      &&
            (_regNum        == other._regNum)           &&
            (_subRegNum     == other._subRegNum)        &&
            (_elementSize   == other._elementSize));
}

bool GtReg::Contains(const GtReg& other) const
{
    uint32_t first      = SubRegNumInBytes();
    uint32_t otherFirst = other.SubRegNumInBytes();
    return ((_regType == other._regType) &&
            (_archRegType == other._archRegType) &&
            (_regNum == other._regNum) &&
            (first <= otherFirst) && (first + _elementSize >= otherFirst + other._elementSize));
}

bool GtReg::operator ==(const GtReg& other) const
{
    return (IsSamePreg(other) && (_vregNum == other._vregNum));
}

bool GtReg::operator <(const GtReg& other) const
{
    return  ((_regType      < other._regType)       || ((_regType        == other._regType)      &&
            ((_archRegType  < other._archRegType)   || ((_archRegType    == other._archRegType)  &&
            ((_regNum       < other._regNum)        || ((_regNum         == other._regNum)       &&
            ((_subRegNum    < other._subRegNum)     || ((_subRegNum      == other._subRegNum)    &&
            ((_elementSize  < other._elementSize)   || ((_elementSize    == other._elementSize)  &&
            (_vregNum       < other._vregNum)))))))))));
}

GtReg& GtReg::Resize(uint32_t elementSize)
{
    if (elementSize != _elementSize)
    {
        GTPIN_ASSERT(elementSize != 0);
        GTPIN_ASSERT(SubRegNumInBytes() % elementSize == 0);
        _subRegNum = SubRegNumInBytes() / elementSize;
        _elementSize = elementSize;
    }
    return *this;
}

std::string GtReg::ToString() const
{
    std::ostringstream ostr;

    if (!IsValid())
    {
        ostr << "invalid";
    }
    else if (_regType.IsArf())
    {
        ostr << _archRegType.ToString() << "." << _regNum << "." << _subRegNum << "(" << _elementSize << ")";
    }
    else if (_regType.IsGrf())
    {
        ostr << "r" << std::dec << _regNum << "." << _subRegNum << "(" << _elementSize << ")";
    }

    if (_vregNum.IsValid())
    {
        ostr << "[v" << uint32_t(_vregNum) << "]";
    }

    return ostr.str();
}

#pragma pack(pop)
}
#endif