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

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

/*!
 * @file Virtual registers in GTPin
 */

#ifndef IGT_VREG_H_
#define IGT_VREG_H_

#include <string>
#include <sstream>

#include "gt_basic_defs.h"
#include "gt_reg.h"

namespace gtpin
{
#pragma pack(push, 8)

/*! @addtogroup GTPIN_REG_ALLOC 
@{
*/

/* ===================================================================================================== */
// Enum VREG_TYPE
/* ===================================================================================================== */
/// Type of the virtual register
enum VREG_TYPE :uint32_t
{
    VREG_TYPE_INVALID,
    VREG_TYPE_BYTE,      ///< 1-byte general purpose virtual register
    VREG_TYPE_WORD,      ///< 2-byte general purpose virtual register
    VREG_TYPE_DWORD,     ///< 4-byte general purpose virtual register
    VREG_TYPE_QWORD,     ///< 8-byte general purpose virtual register
    VREG_TYPE_OWORD,     ///< 16-byte general purpose virtual register
    VREG_TYPE_HWORD,     ///< 32-byte general purpose virtual register
    VREG_TYPE_DHWORD,    ///< 64-byte general purpose virtual register
    VREG_TYPE_PTR,       ///< Virtual register capable of holding memory pointer in the current configuration
    VREG_TYPE_GRF_REG    ///< Virtual register capable of holding full GRF register in the current configuration
};

/// Type of the virtual register
class GtVregType
{
public:
    inline constexpr GtVregType(VREG_TYPE v = VREG_TYPE_INVALID) : _value(v) {}     ///< Conversion constructor
    inline constexpr operator VREG_TYPE() const { return _value; }                  ///< Conversion operator

    inline constexpr bool IsValid()   const { return _value != VREG_TYPE_INVALID; } ///< Return true for a valid type
    inline const char*    ToString()  const;                                        ///< Return string representation

    /// Given size of the virtual register in bytes, return the corresponding register type
    static inline GtVregType MakeBySize(uint32_t size);

private:
    VREG_TYPE _value;
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtVregType);

/* ===================================================================================================== */
// Enum VREG_FLAG
/* ===================================================================================================== */
/// Vreg flags; can be combined to specify multiple boolean properties of the virtual register
enum VREG_FLAG :uint32_t
{
    VREG_FLAG_NONE              = 0,    ///< "nothing special" flag
    VREG_FLAG_BUFFER_ADDR       = 0x1,  ///< Deprecated (ignored) flag
    VREG_FLAG_COUNTER           = 0x2,  ///< Counter register - zero-initialized on entry to the kernel
    VREG_FLAG_SCRATCH           = 0x4,  ///< Scratch register - dead on entry to instrumentation procedure.
                                        ///< This flag is a hint that enables more effective liveness analysis
    VREG_FLAG_MSG_ADDR          = 0x8,  ///< Indicates message address payload register. Used as a register allocation hint
    VREG_FLAG_MSG_DATA          = 0x10, ///< Indicates message data payload register. Used as a register allocation hint

    VREG_FLAG_MSG_ADDR_SCRATCH  = VREG_FLAG_MSG_ADDR | VREG_FLAG_SCRATCH,
    VREG_FLAG_MSG_DATA_SCRATCH  = VREG_FLAG_MSG_DATA | VREG_FLAG_SCRATCH
};

/* ===================================================================================================== */
// Class IGtVreg
/* ===================================================================================================== */
/*!
 * @brief Virtual register
 * @note For now, we assume that virtual register can only be mapped to a GRF (sub)register.
 * ARF registers and non-scalar GRF regions can't be virtualized.
 * 
 * @par Availability:
 * - IGtTool::OnKernelBuild
 */
class IGtVreg
{
public:
    virtual bool        IsValid()           const = 0; ///< Return true for a valid virtual register
    virtual GtVregNum   Num()               const = 0; ///< Return unique register number, assigned by IGtVregFactory
    virtual uint32_t    Size()              const = 0; ///< Return size of the register in bytes
    virtual GtVregType  Type()              const = 0; ///< Return type of the register
    virtual uint32_t    Flags()             const = 0; ///< Return combination of boolean flags associated with the register
    virtual GtSubRegNum RequiredSubregNum() const = 0; ///< Return required subregister number, or GtSubRegNum::Invalid()
                                                       ///< if this vreg accepts any subregister assignment
    /*!
     * Convert this virtual register into an exemplar physical register.
     * Usually, this conversion is used to generate operand that represents virtual register in GEN instruction (IGtIns).
     * @note For now, we assume that all virtual registers are of GRF type 
     */
    virtual operator GtReg() const = 0;

    /// Return string representation of this virtual register
    inline std::string ToString() const;

protected:
    /// @internal Protected destructor
    virtual ~IGtVreg() = default;
};
GTPIN_ASSERT_PURE_ABSTRACT(IGtVreg);

/*! @} */

/* ===================================================================================================== */
// GtVregType implementation 
/* ===================================================================================================== */
const char* GtVregType::ToString()  const
{
    switch (_value)
    {
    case VREG_TYPE_BYTE:    return "b";
    case VREG_TYPE_WORD:    return "w";
    case VREG_TYPE_DWORD:   return "dw";
    case VREG_TYPE_QWORD:   return "qw";
    case VREG_TYPE_OWORD:   return "ow";
    case VREG_TYPE_HWORD:   return "hw";
    case VREG_TYPE_DHWORD:  return "dhw";
    case VREG_TYPE_PTR:     return "p";
    case VREG_TYPE_GRF_REG: return "gr";
    default:                return "?";
    }
}

GtVregType GtVregType::MakeBySize(uint32_t size)
{
    switch (size)
    {
    case 1:     return VREG_TYPE_BYTE;
    case 2:     return VREG_TYPE_WORD;
    case 4:     return VREG_TYPE_DWORD;
    case 8:     return VREG_TYPE_QWORD;
    case 16:    return VREG_TYPE_OWORD;
    case 32:    return VREG_TYPE_HWORD;
    case 64:    return VREG_TYPE_DHWORD;
    default:    return VREG_TYPE_INVALID;
    }
}

/* ===================================================================================================== */
// IGtVreg inline implementation 
/* ===================================================================================================== */
std::string IGtVreg::ToString() const
{
    if (!IsValid())
    {
        return "invalid";
    }

    std::ostringstream ostr;
    ostr << "v" << uint32_t(Num()) << ":" << Type().ToString();
    return ostr.str();
}

#pragma pack(pop)
}
#endif
