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

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

/*!
 * @file Abstract representation of arguments of the High-Level Instrumentation
 * (HLI) functions
 */

#ifndef IGT_IARG_H_
#define IGT_IARG_H_

#include "gt_basic_defs.h"
#include "gt_basic_utils.h"
#include "gt_reg.h"
#include "igt_core.h"

namespace gtpin
{
#pragma pack(push, 8)

/* ===================================================================================================== */
// Class GtHliCallStd
/* ===================================================================================================== */
/*! @ingroup GTPIN_HLI_FUNCTION
 * @brief Calling convention of the HLI function
 * @note  Currently, GTPin supports only IGC stack-call convention for HLI functions
 */
class GtHliCallStd
{
public:
    enum ENUM :uint32_t
    {
        INVALID,       ///< Invalid calling convention
        IGC_STACK      ///< IGC stack-call convention
    } value;

public:
    constexpr GtHliCallStd(ENUM e = INVALID) : value(e) {}               ///< Conversion constructor
    constexpr operator ENUM() const { return value; }                    ///< Conversion operator

    constexpr bool IsValid()        const { return value != INVALID; }   ///< Return true for a valid calling convention
    constexpr bool IsIgcStackCall() const { return value == IGC_STACK; } ///< Return true for IGC stack-call convention
    inline const char* ToString()   const;                               ///< Return string representation
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtHliCallStd);

/*! @addtogroup GTPIN_HLI_ARGUMENT
 @{
 */

/* ===================================================================================================== */
// Class GtHliParamType
/* ===================================================================================================== */
/*!
 * @brief Type of an HLI function parameter
 * 
 * Following are primary HLI parameter types, supported by GTPin:
 *      - Integer           i8, i16, i32, i64
 *      - Floating-point    f16, f32, f64
 *      - Enumeration
 * 
 * Compound parameter types include:
 *      - Pointer           Pointer to a variable in the device (GPU) address space
 * 
 * The type of the function's return value can be any of the above types or void.
 * 
 * - @note The function parameter refer to the variable listed inside the parentheses in the function definition.\n
 *         The function argument is the value that is passed to the function when it is called\n
 *         The parameter type and the argument type are not always identical, but argument value must be convertible
 *         to the corresponding parameter type. The conversion is provided by either compiler or GTPin
 * 
 * - @note HLI function is executed in the context of a GPU device, so all pointer parameters of the function
 *         should refer to variables in the device memory. However, for tools that specify function arguments in
 *         the host (CPU) space, addresses in the device space are unknown.\n
 *         To allow passing pointer arguments to HLI functions, GTPin provides memory mapping services that
 *         transparently convert host pointers to device addresses and vice versa.
 *         See @ref GTPIN_MEMORY_MAPPER for details.\n
 *         The memory address mapping is applied to pointer arguments specified by immediate values.
 *         Pointers passed in registers are assumed to refer device memory.
 */
class GtHliParamType
{
public:
    enum ENUM :uint32_t
    {
        INVALID,  ///< Invalid HLI parameter type
        PRIME,    ///< Primary HLI parameter type: integer, floating-point, enumeration
        PTR       ///< Pointer to a variable in the device (GPU) address space
    } value;

public:
    inline constexpr GtHliParamType(ENUM e = INVALID) : value(e) {}     ///< Conversion constructor
    inline constexpr operator ENUM() const { return value; }            ///< Conversion operator
                                                                         
    inline constexpr bool IsValid()  const { return value != INVALID; } ///< Return true for a valid parameter type
    inline constexpr bool IsPrime()  const { return value == PRIME; }   ///< Return true for a primary parameter type
    inline constexpr bool IsPtr()    const { return value == PTR; }     ///< Return true if the parameter is a pointer
    inline const char*    ToString() const;                             ///< Return string representation
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtHliParamType);

/*! @internal
 * Structure that checks if type 'T' is a valid prime type of the HLI function parameter
 */
template<typename T> struct IsValidHliPrimeType
{
    using _T = RemoveCvRef<T>;
    #if (_GTPIN_STL_STD >= 11)
    static constexpr bool value = ((std::is_arithmetic<_T>::value || std::is_enum<_T>::value) && (sizeof(_T) <= sizeof(uint64_t)));
    #else
    static constexpr bool value = (sizeof(_T) <= sizeof(uint64_t));
    #endif
};

/*! @internal
 * Structure that checks if type 'T' is a valid type of the HLI function parameter
 */
template<typename T> struct IsValidHliParamType
{
    static constexpr bool value = (IsValidHliPrimeType<T>::value || IsPointer<RemoveCvRef<T>>::value);
};

/*! @internal
 * Structure that checks if type 'T' is a valid type of the HLI function's return value
 */
template<typename T> struct IsValidHliReturnType
{
    static constexpr bool value = IsValidHliParamType<T>::value || IsVoidType<T>::value;
};

/* ===================================================================================================== */
// Class GtHliParamTraits
/* ===================================================================================================== */
/*!
 * @brief Traits of HLI function parameter
 */
class GtHliParamTraits
{
public:
    /*!
     * @brief Constructor
     * 
     * @param[in]   type    Type of the HLI parameter
     * @param[in]   size    Size of the HLI parameter in bytes
     *                      The valid size must be a power of two, not exceeding sizeof(uint64_t)
     * 
     * The void parameter type has a special traits: GtHliParamTraits { GtHliParamType::PRIME, 0 }
     */
    inline constexpr GtHliParamTraits(GtHliParamType type = GtHliParamType::INVALID, uint32_t size = 0);

    bool           constexpr IsValid()  const { return _type.IsValid(); } ///< Return true for a valid parameter/return type
    bool           constexpr IsVoid()   const;                            ///< Return true for a void parameter type
    GtHliParamType constexpr Type()     const { return _type; }           ///< Return type of the parameter
    uint32_t       constexpr Size()     const { return _size; }           ///< Return size of the parameter in bytes
    bool           constexpr IsPrime()  const { return _type.IsPrime(); } ///< Return true for a primary parameter type
    bool           constexpr IsPtr()    const { return _type.IsPtr(); }   ///< Return true if the parameter is a pointer

private:
    GtHliParamType  _type;
    uint32_t        _size;
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtHliParamTraits);

/// Return GtHliParamTraits for the specified parameter type 'T'
template <typename T> constexpr GtHliParamTraits MakeParamTraits();

/* ===================================================================================================== */
// Class GtIargType
/* ===================================================================================================== */
/*!
 * @brief Type of an HLI function argument
 *
 * This type specifies how the HLI function argument should be treated before passing it to the
 * function (assigning to the function parameter). For example, if function paramter is a pointer in the
 * device (GPU) space, and the corresponding argument is a pointer in the host (CPU) space, GTPin must
 * perform host-to-device pointer conversion before passing the argument value to the function.
 *
 * @note The function parameter refer to the variable listed inside the parentheses in the function definition.\n
 *       The function argument is the value that is passed to the function when it is called\n
 *       The parameter type and the argument type are not always identical, but argument value must be convertible
 *       to the corresponding parameter type. The conversion is provided by either compiler or GTPin
 * 
 * Folowing list provides allowed argument-to-parameter assignments and corresponding conversions and
 * computations automatically performed by GTPin
 * 
 * <b>Types of HLI function arguments</b>
 *  
 *  <b>GtIargType::PRIME</b> An argument of prime type passed by value\n
 *       Function parameter: any prime type\n
 *       Argument value: any prime type\n
 *       Specified by: immediate, register, or IargPrime structure\n
 *       Argument conversion: none\n
 *       Factory method: IGtIargFactory::MakePrime\n\n
 *  
 *  <b>GtIargType::HOST_PTR</b> Pointer in the host memory, converted into device address. See @ref IGtMemoryMapper
 *  for details of the host-to-device memory mapping
 *       Function parameter: pointer in the device memory\n
 *       Argument value: pointer in the host memory\n
 *       Specified by: immediate or IargHostPtr structure\n
 *       Argument conversion: host-to-device address mapping\n
 *       Factory method: IGtIargFactory::MakeHostPtr\n\n
 *  
 *  <b>GtIargType::DEVICE_PTR</b> Address in the device memory space, passed by register value
 *       Function parameter: pointer in the device memory\n
 *       Argument value: pointer in the device memory\n
 *       Specified by: register or IargDevicePtr structure\n
 *       Argument conversion: none\n
 *       Factory method: IGtIargFactory::MakeDevicePtr\n\n
 *  
 *  <b>GtIargType::BUFFER_OFFSET</b> Offset in the GTPin profile buffer, converted into device address\n
 *       Function parameter: pointer in the device memory\n
 *       Argument value: offset in the GTPin profile buffer\n
 *       Specified by: immediate, register, or IargBufferOffset structure\n
 *       Argument conversion: buffer offset-to-address mapping\n
 *       Factory method: IGtIargFactory::MakeBufferOffset\n\n
 *  
 *  <b>GtIargType::TID</b> HW thread ID extracted from the State Register\n
 *       Function parameter: 32-bit integer\n
 *       Argument value: void\n
 *       Specified by: IargTid structure\n
 *       Argument conversion: Thread ID extracted from the State Register\n
 *       Factory method: IGtIargFactory::MakeTid\n\n
 *  
 *  <b>GtIargType::FFTID</b> Fixed Function Thread ID extracted from the State Register
 *       Function parameter: 32-bit integer\n
 *       Argument value: void\n
 *       Specified by: IargFfTid structure\n
 *       Argument conversion: Fixed Function Thread ID extracted from the State Register\n
 *       Factory method: IGtIargFactory::MakeFfTid\n\n
 *  
 *  <b>GtIargType::GRF_RANGE</b> Mutable GRF range\n
 *       Function parameter: pointer to a mutable array of GRF registers\n
 *       Argument value: GRF range\n
 *       Specified by: IargGrfRange structure\n
 *       Argument conversion: Map GRF range into an array in device memory. Set array's address in the parameter\n
 *       Factory method: IGtIargFactory::MakeGrfRange\n\n
 *  
 *  <b>GtIargType::CONST_GRF_RANGE</b> Immutable GRF range\n
 *       Function parameter: pointer to a constant array of GRF registers\n
 *       Argument value: GRF range\n
 *       Specified by: IargConstGrfRange structure\n
 *       Argument conversion: Copy GRF range into an array in device memory. Set array's address in the parameter\n
 *       Factory method: IGtIargFactory::MakeConstGrfRange\n\n
 *  
 *  <b>GtIargType::SIMD_MASK</b> Mask of effective SIMD channels\n
 *       Function parameter: 32-bit integer\n
 *       Specified by: IargSimdMask structure\n
 *       Factory method: IGtIargFactory::MakeSimdMask\n\n
 *  
 *  <b>GtIargType::INS_OP_MASK</b> Mask of scalar operations performed by the instruction on the vector of data elements\n
 *       Function parameter: 32-bit integer\n
 *       Specified by: IargInsOpMask structure\n
 *       Factory method: IGtIargFactory::MakeInsOpMask\n\n
 *  
 *  *  <b>GtIargType::SLM_PTR</b> Pointer to the base of SLM (should be 0)\n
 *       Function parameter: 32-bit integer\n
 *       Specified by: IargSlmPtr structure\n
 *       Factory method: IGtIargFactory::MakeIargSlmPtr\n\n
 */
class GtIargType
{
public:
    enum ENUM :uint32_t
    {
        INVALID,        ///< Invalid HLI argument type
        PRIME,          ///< Primary HLI argument type: integer, floating-point, enumeration
        HOST_PTR,       ///< Pointer to a variable in the host (CPU) address space
        DEVICE_PTR,     ///< Pointer to a variable in the device (GPU) address space
        BUFFER_OFFSET,  ///< Offset within the profile buffer
        TID,            ///< Current Thread ID (TID)
        FFTID,          ///< Current Fixed Function Thread ID (FFTID)
        GRF_RANGE,      ///< Mutable GRF range
        CONST_GRF_RANGE,///< Immutable GRF range
        SIMD_MASK,      ///< Mask of effective SIMD channels
        INS_OP_MASK,    ///< Mask of scalar operations performed by the instruction on the vector of data elements
        SLM_PTR         ///< Pointer to the base of SLM
    } value;

public:
    inline constexpr GtIargType(ENUM e = INVALID) : value(e) {}                    ///< Conversion constructor
    inline constexpr operator ENUM() const { return value; }                       ///< Conversion operator

    inline constexpr bool IsValid()        const { return value != INVALID; }      ///< Return true for a valid argument type
    inline constexpr bool IsPrime()        const { return value == PRIME; }        ///< Return true for a primary argument type
    inline constexpr bool IsHostPtr()      const { return value == HOST_PTR; }     ///< Return true for a host pointer
    inline constexpr bool IsDevicePtr()    const { return value == DEVICE_PTR; }   ///< Return true for a device pointer
    inline constexpr bool IsBufferOffset() const { return value == BUFFER_OFFSET; }///< Return true for an offset in profile buffer
    inline constexpr bool IsTid()          const { return value == TID; }          ///< Return true if argument is the current TID
    inline constexpr bool IsFfTid()        const { return value == FFTID; }        ///< Return true if argument is the current FFTID
    inline constexpr bool IsSlmPtr()       const { return value == SLM_PTR; }      ///< Return true if argument is the current SLM_PTR
    inline const char*    ToString()       const;                                  ///< Return string representation

    /*!
     * @brief Check to see if this argument type is compatible with the specified parameter type
     * 
     * The HLI function argument is called compatible with the corresponding HLI parameter if the argument
     * value can be assigned to the function parameter. The assignment may involve the argument conversion
     * provided by GTPin or compiler.
     * 
     * @param[in]   paramType    Type of the HLI parameter
     * @return true if this argument is compatible with the specified parameter type
     */
    inline constexpr bool IsCompatible(GtHliParamType paramType) const;

};
GTPIN_ASSERT_STANDARD_LAYOUT(GtIargType);

/* ===================================================================================================== */
// Class IGtIargHandler
/* ===================================================================================================== */
/*!
 * @brief Abstract and opaque handler of HLI function arguments. Used as a 'Visitor' of objects that
 * provide IGtIarg interface
 */
class IGtIargHandler;

/* ===================================================================================================== */
// Class IGtIarg
/* ===================================================================================================== */
/*!
 * @brief Abstract representation of the HLI function argument
 */
class IGtIarg
{
public:
    virtual GtHliParamTraits         ParamTraits() const = 0; ///< Return traits of the function parameter
    virtual GtIargType               Type()        const = 0; ///< Return type of the argument
    virtual void Handle(IGtIargHandler& handler)   const = 0; ///< Process this argument with the specified handler
    virtual GtCoreString             ToString()    const = 0; ///< Return string representation of this object
                                           
    /// @internal Destructor
    virtual ~IGtIarg() = default;
};
GTPIN_ASSERT_PURE_ABSTRACT(IGtIarg);

using IargCptr = const IGtIarg*; ///< Pointer to the constant IGtIarg object

/* ===================================================================================================== */
// Class GtIargSpan
/* ===================================================================================================== */
/*!
 * @brief Span of pointers to IGtIarg objects that represent arguments of the HLI function.
 * 
 * IGtIarg objects in the span follow the order the formal paramters appear in the function definition.
 */
using GtIargSpan = Span<const IargCptr>;

/* ===================================================================================================== */
// Class IGtIret
/* ===================================================================================================== */
/*!
 * @brief Abstract representation of the HLI function's return value
 */
class IGtIret
{
public:
    virtual GtHliParamTraits         ParamTraits() const = 0; ///< Return traits of the function's return value
    virtual bool                     IsVoid()      const = 0; ///< Return true for a void / unused return value
    virtual GtReg                    Reg()         const = 0; ///< Return register that holds the return value. @pre !IsVoid()
    virtual GtCoreString             ToString()    const = 0; ///< Return string representation of this object

    /// @internal Destructor
    virtual ~IGtIret() = default;
};
GTPIN_ASSERT_PURE_ABSTRACT(IGtIret);

using IretCptr = const IGtIret*; ///< Pointer to the constant IGtIret object

/*! @} */

/* ===================================================================================================== */
// GtHliCallStd implementation
/* ===================================================================================================== */
inline const char* GtHliCallStd::ToString() const
{
    switch (value)
    {
    case IGC_STACK: return "IGC stack-call";
    default:        return "invalid";
    }
}

/* ===================================================================================================== */
// GtHliParamType implementation
/* ===================================================================================================== */
inline const char* GtHliParamType::ToString() const
{
    switch (value)
    {
    case PRIME: return "primary";
    case PTR:   return "pointer";
    default:    return "invalid";
    }
}

/* ===================================================================================================== */
// GtHliParamTraits implementation
/* ===================================================================================================== */
constexpr GtHliParamTraits::GtHliParamTraits(GtHliParamType type, uint32_t size) :
    _type((IsPower2(size) && (size <= sizeof(uint64_t))) ? type : GtHliParamType(GtHliParamType::INVALID)),
    _size(size) {}

bool constexpr GtHliParamTraits::IsVoid() const
{
    return (_type.IsPrime() && (_size == 0));
}

template <typename T> inline constexpr GtHliParamTraits MakeParamTraits()
{
    GTPIN_STATIC_ASSERT_MSG(IsValidHliParamType<T>::value, "Invalid HLI parameter type");
    return IsPointer<RemoveCvRef<T>>::value ? GtHliParamTraits(GtHliParamType::PTR,   sizeof(uintptr_t)) :
                                              GtHliParamTraits(GtHliParamType::PRIME, sizeof(T));
}

template <> inline constexpr GtHliParamTraits MakeParamTraits<void>()
{
    return { GtHliParamType::PRIME, 0 };
}

/* ===================================================================================================== */
// GtIargType implementation
/* ===================================================================================================== */
constexpr bool GtIargType::IsCompatible(GtHliParamType paramType) const
{
    return ( IsValid()                                              &&
             (!IsPrime()                    || paramType.IsPrime()) &&
             (!IsHostPtr()                  || paramType.IsPtr())   &&
             (!IsDevicePtr()                || paramType.IsPtr())   &&
             (!IsBufferOffset()             || paramType.IsPtr())   &&
             (!IsTid()                      || paramType.IsPrime()) &&
             (!IsFfTid()                    || paramType.IsPrime()) &&
             (!IsSlmPtr()                   || paramType.IsPrime()) &&
             ((value != GRF_RANGE)          || paramType.IsPtr())   &&
             ((value != CONST_GRF_RANGE)    || paramType.IsPtr())   &&
             ((value != SIMD_MASK)          || paramType.IsPrime()) &&
             ((value != INS_OP_MASK)        || paramType.IsPrime())
           );
}

const char* GtIargType::ToString() const
{
    switch (value)
    {
    case PRIME:             return "primary";
    case HOST_PTR:          return "host pointer";
    case DEVICE_PTR:        return "device pointer";
    case BUFFER_OFFSET:     return "buffer offset";
    case TID:               return "tid";
    case FFTID:             return "fftid";
    case GRF_RANGE:         return "mutable GRF range";
    case CONST_GRF_RANGE:   return "immutable GRF range";
    case SIMD_MASK:         return "simd mask";
    case INS_OP_MASK:       return "operation mask";
    case SLM_PTR:           return "SLM pointer";
    default:                return "invalid";
    }
}

#pragma pack(pop)
}
#endif
