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

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

/*!
 * @file Types of the High-Level Instrumentation (HLI) arguments values and the
 * corresponding factory methods
 */

#ifndef GT_IARG_DEFS_H_
#define GT_IARG_DEFS_H_

#include "gt_basic_defs.h"
#include "igt_iarg.h"
#include "igt_iarg_factory.h"
#include "igt_ins.h"
#include "gt_ins_fields.h"

namespace gtpin
{
#pragma pack(push, 8)

/*! @addtogroup GTPIN_HLI_ARGUMENT
@{
*/

/*
 * =====================================================================================================
 * Classes that encapsulate argument values and provide the corresponding (non-virtual) factory method.
 * Each class defines the following functions:
 * 
 * // Make a new IGtIarg object for the parameter of type 'PRMT', using the specified factory
 * template <typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const;
 * 
 * // Return type of the argument
 * static constexpr GtIargType Type();
 * =====================================================================================================
 */

/* ===================================================================================================== */
// IargPrime
/* ===================================================================================================== */
/// @internal Prime HLI argument value of type ARGT
template <typename ARGT, typename ENABLE = void> struct IargPrime_;

/// @internal Specialization of IargPrime_ for immediate prime value
template <typename ARGT> struct IargPrime_<ARGT, EnableIf<IsValidHliPrimeType<ARGT>::value>>
{
    IargPrime_(const ARGT& v) : value(v) {}
    GtIargImmValue value;
};

/// @internal Specialization of IargPrime_ for ARGT = GtReg
template <typename ARGT> struct IargPrime_<ARGT, EnableIf<IsAkinType<ARGT, GtReg>::value>>
{
    IargPrime_(const ARGT& v) : value(v) {}
    GtReg value;
};

/*!
 * @brief Primary HLI argument value of type ARGT
 * 
 * The class provides factory method that encapsulates this argument value into IGtIarg object of type
 * GtIargType::PRIME
 * 
 * @param   ARGT    Valid primary type of the HLI argument (\e int, \e float, etc.), or GtReg
 */
template <typename ARGT> struct IargPrime : public IargPrime_<ARGT>
{
    /// Construct IargPrime object with the specified argument value
    IargPrime(const ARGT& v) : IargPrime_<ARGT>(v) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::PRIME; }

    /*!
     * @brief Create IGtIarg object that assigns this argument value to the formal parameter of type \e PRMT
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::PRIME
     * @pre PRMT is a valid primary type of the HLI argument, e.g. \e int, \e float, etc.
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        constexpr GtHliParamTraits paramTraits = MakeParamTraits<PRMT>();
        GTPIN_STATIC_ASSERT(paramTraits.IsPrime());
        return factory.MakePrime(paramTraits, IargPrime_<ARGT>::value);
    }
};

/* ===================================================================================================== */
// IargHostPtr
/* ===================================================================================================== */
/*!
 * @brief HLI argument that points to an object in the host memory
 *
 * The class provides factory method that encapsulates argument value into IGtIarg object of type
 * GtIargType::HOST_PTR
 */
struct IargHostPtr
{
    /// Construct IargHostPtr object with the specified argument value
    IargHostPtr(const void* v) : value(v) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::HOST_PTR; }

    /*!
     * @brief Create IGtIarg object that converts host pointer into into device address, and assigns it
     * to the formal parameter of type \e PRMT.
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::HOST_PTR
     * @pre PRMT is a pointer type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsPointer<RemoveCvRef<PRMT>>::value);
        return factory.MakeHostPtr(value);
    }

    const void* value;
};

/* ===================================================================================================== */
// IargDevicePtr
/* ===================================================================================================== */
/*!
 * @brief HLI argument that points to an object in the device memory
 *
 * The class provides factory method that encapsulates this pointer into IGtIarg object of type
 * GtIargType::DEVICE_PTR
 * 
 * @note Pointers to the device memory objects, can be passed only in registers
 */
struct IargDevicePtr
{
    /// Construct IargDevicePtr object for the argument value passed in the specified register
    IargDevicePtr(const GtReg v) : value(v) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::DEVICE_PTR; }

    /*!
     * @brief Create IGtIarg object that assigns this argument value to the formal parameter of type \e PRMT
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::DEVICE_PTR
     * @pre PRMT is a pointer type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsPointer<RemoveCvRef<PRMT>>::value);
        return factory.MakeDevicePtr(value);
    }

    GtReg value;
};

/* ===================================================================================================== */
// IargBufferOffset
/* ===================================================================================================== */
/// @internal HLI argument value of type ARGT that represents offset in the GTPin profile buffer
template <typename ARGT, typename ENABLE = void> struct IargBufferOffset_;

/// @internal Specialization of IargBufferOffset_ for immediate integral value of the offset
template <typename ARGT> struct IargBufferOffset_<ARGT, EnableIf<IsIntegral<RemoveCvRef<ARGT>>::value>>
{
    IargBufferOffset_(const ARGT& v) : value(v) {}
    GtIargImmValue value;
};

/// @internal Specialization of IargBufferOffset_ for ARGT = GtReg
template <typename ARGT> struct IargBufferOffset_<ARGT, EnableIf<IsAkinType<ARGT, GtReg>::value>>
{
    IargBufferOffset_(const ARGT& v) : value(v) {}
    GtReg value;
};

/*!
 * @brief  HLI argument value of type ARGT that represents offset in the GTPin profile buffer
 * 
 * The class provides factory method that encapsulates this argument value into IGtIarg object of type
 * GtIargType::BUFFER_OFFSET
 * 
 * @param   ARGT    Integral type or GtReg
 */
template <typename ARGT> struct IargBufferOffset : public IargBufferOffset_<ARGT>
{
    /// Construct IargBufferOffset object with the specified argument value
    IargBufferOffset(const ARGT& v) : IargBufferOffset_<ARGT>(v) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::BUFFER_OFFSET; }

    /*!
     * @brief Create IGtIarg object that converts offset in the profile buffer into device address, and assigns it
     * to the formal parameter of type \e PRMT.
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::BUFFER_OFFSET
     * @pre PRMT is a pointer type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsPointer<RemoveCvRef<PRMT>>::value);
        return factory.MakeBufferOffset(IargBufferOffset_<ARGT>::value);
    }
};

/* ===================================================================================================== */
// IargTid
/* ===================================================================================================== */
/*!
 * @brief HLI argument that carries current Thread ID value
 *
 * The class provides factory method that encapsulates this pointer into IGtIarg object of type
 * GtIargType::TID
 */
struct IargTid
{
    /// Default constructor
    IargTid() = default;

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::TID; }

    /*!
     * @brief Create IGtIarg object that assigns current Thread ID value to the formal parameter of type \e PRMT
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::TID
     * @pre PRMT is an integral 32-bit type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsIntegral<RemoveCvRef<PRMT>>::value && (sizeof(PRMT) == sizeof(uint32_t)));
        return factory.MakeTid();
    }
};

/* ===================================================================================================== */
// IargFfTid
/* ===================================================================================================== */
/*!
 * @brief HLI argument that carries current Fixed Function Thread ID value
 *
 * The class provides factory method that encapsulates this pointer into IGtIarg object of type
 * GtIargType::FFTID
 */
struct IargFfTid
{
    /// Default constructor
    IargFfTid() = default;

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::FFTID; }

    /*!
     * @brief Create IGtIarg object that assigns current Fixed Function Thread ID value to the formal
     * parameter of type \e PRMT
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::FFTID
     * @pre PRMT is an integral 32-bit type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsIntegral<RemoveCvRef<PRMT>>::value && (sizeof(PRMT) == sizeof(uint32_t)));
        return factory.MakeFfTid();
    }
};

/* ===================================================================================================== */
// IargGrfRange
/* ===================================================================================================== */
/*!
 * @brief  HLI argument that represents a mutable array of GRF registers
 */
struct IargGrfRange
{
    /*!
     * @brief Construct IargGrfRange object for the specified range of GRF registers
     * 
     * @param[in]   firstReg        Ordinal number of the first GRF register in the range
     * @param[in]   numRegs         Number of registers in the range
     */
    IargGrfRange(uint32_t firstReg, uint32_t numRegs) : rangeFirst(firstReg), rangeSize(numRegs) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::GRF_RANGE; }

    /*!
     * @brief Create IGtIarg object that assigns this argument value to the formal parameter of type \e PRMT
     * @note The array of GRF registers is passed by pointer
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::GRF_RANGE
     * @pre PRMT is a pointer type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsPointer<RemoveCvRef<PRMT>>::value);
        return factory.MakeGrfRange(rangeFirst, rangeSize);
    }

    uint32_t rangeFirst;
    uint32_t rangeSize;
};

/* ===================================================================================================== */
// IargConstGrfRange
/* ===================================================================================================== */
/*!
 * @brief  HLI argument that represents an immutable array of GRF registers
 */
struct IargConstGrfRange
{
    /*!
     * @brief Construct IargConstGrfRange object for the specified range of GRF registers
     * 
     * @param[in]   firstReg        Ordinal number of the first GRF register in the range
     * @param[in]   numRegs         Number of registers in the range
     */
    IargConstGrfRange(uint32_t firstReg, uint32_t numRegs) : rangeFirst(firstReg), rangeSize(numRegs) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::CONST_GRF_RANGE; }

    /*!
     * @brief Create IGtIarg object that assigns this argument value to the formal parameter of type \e PRMT
     * @note The array of GRF registers is passed by pointer
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::CONST_GRF_RANGE
     * @pre PRMT is a pointer type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsPointer<RemoveCvRef<PRMT>>::value);
        return factory.MakeConstGrfRange(rangeFirst, rangeSize);
    }

    uint32_t rangeFirst;
    uint32_t rangeSize;
};

/* ===================================================================================================== */
// IargSimdMask
/* ===================================================================================================== */
/*!
 * @brief HLI argument that represents mask of effective SIMD channels at the instrumentation point
 */
struct IargSimdMask
{
    /*!
     * @brief Construct HLI argument that evaluates the mask of effective SIMD channels at the instrumentation point
     *        ceMask   = ignoreCeMask ? 0xffffffff : ChannelEnableReg()
     *        simdMask = ceMask & DispatchMaskReg() & execMask & Mask(predicate)
     * 
     * @param[in]       ignoreCeMask    Flag that indicates whether the mask in ChannelEnableReg should be applied (false)
     *                                  or ignored (true) in the computation of effective SIMD channels
     * @param[in]       execMask        Execution mask to be used in the computation of effective SIMD channels.
     *                                  Normally, this mask is produced by the GtExecMask::Bits() function.
     * @param[in]       predicate       Predicate to be used in the computation. Invalid predicate is ignored
     */
    IargSimdMask(bool ignoreCeMask, uint32_t execMask, const GtPredicate& predicate = GtPredicate()) :
        ignoreCe(ignoreCeMask), exMask(execMask), pred(predicate) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::SIMD_MASK; }

    /*!
     * @brief Create IGtIarg object that assigns this argument value to the formal parameter of type \e PRMT
     * @note The array of GRF registers is passed by pointer
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::SIMD_MASK
     * @pre PRMT is a pointer type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsIntegral<RemoveCvRef<PRMT>>::value && (sizeof(PRMT) == sizeof(uint32_t)));
        return factory.MakeSimdMask(ignoreCe, exMask, pred);
    }

    bool        ignoreCe;
    uint32_t    exMask;
    GtPredicate pred;
};

/* ===================================================================================================== */
// IargInsOpMask
/* ===================================================================================================== */
/*!
 * @brief HLI argument that represents mask of scalar operations performed by the instruction on the
 * vector of data elements.
 * 
 * @note This argument is similar to the SIMD mask generated by the IargSimdMask argument.
 *       However, there is one significant difference: the operation mask starts with the state of the
 *       GtExecMask::ChannelOffset() channel in the LSB position of the mask, while SIMD mask always
 *       holds state of the channel #0 in the LSB position.
 *       In other words, insOpMask = (simdMask >> GtExecMask::ChannelOffset())
 * 
 * @note The HLI function with this argument should be called immediately before the instruction for
 *       which operation mask is evaluated. If (!ins.IsFlowControl() && !ins.IsFlagModifier()), this
 *       argument can be used after instruction 'ins'.
 */
struct IargInsOpMask
{
    /*!
     * @brief Construct HLI argument that evaluates the mask of scalar operations performed by the
     * instruction on the vector of data elements
     *        ceMask    = ins.IsWriteMaskEnabled() ? ChannelEnableReg() : 0xffffffff
     *        insOpMask = ceMask & DispatchMaskReg() & ins.ExecMask().Bits() & Mask(ins.Predicate())
     * 
     * @param[in] ins   Instruction, the operation mask is evaluated for
     */
    IargInsOpMask(const IGtIns& ins) : insPtr(&ins) {}

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::INS_OP_MASK; }

    /*!
     * @brief Create IGtIarg object that assigns this argument value to the formal parameter of type \e PRMT
     * @note The array of GRF registers is passed by pointer
     * 
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::INS_OP_MASK
     * @pre PRMT is a pointer type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsIntegral<RemoveCvRef<PRMT>>::value && (sizeof(PRMT) == sizeof(uint32_t)));
        return factory.MakeInsOpMask(*insPtr);
    }

    const IGtIns* insPtr;
};

/* ===================================================================================================== */
// IargSlmPtr
/* ===================================================================================================== */
/*!
 * @brief HLI argument that points to SLM memory
 *
 * The class provides factory method that encapsulates argument value into IGtIarg object of type
 * GtIargType::SLM_PTR
 */
struct IargSlmPtr
{
    /// Default constructor
    IargSlmPtr() = default;

    /// @internal Return type of the argument
    static constexpr GtIargType Type() { return GtIargType::SLM_PTR; }

    /*!
     * @brief Create IGtIarg object that assigns SLM pointer  \e PRMT
     *
     * @param   PRMT    Type of the formal parameter of the function
     * @return New IGtIarg object of type GtIargType::SLM_PTR
     * @pre PRMT is an integral 32-bit type
     */
    template<typename PRMT> const IGtIarg& Make(IGtIargFactory& factory) const
    {
        GTPIN_STATIC_ASSERT(IsIntegral<RemoveCvRef<PRMT>>::value && (sizeof(PRMT) == sizeof(uint32_t)));
        return factory.MakeSlmPtr();
    }
};

/* ===================================================================================================== */
// CreateIarg
/* ===================================================================================================== */
/*! @internal
 * @brief Create a new IGtIarg object
 * 
 * @param[in]   factory     Factory of HLI arguments
 * @param[in]   argVal      Argument value of type ARGT
 * @param       PRMT        Type of the HLI parameter
 * @param       ARGT        Type of the HLI argument
 * @pre  IsValidHliParamType<PRMT>() == true
 * @pre  ARGT type is compatible with the PRMT type, see @ref GtIargType::IsCompatible()
 * @return Pointer to the new IGtIarg object
 * 
@verbatim
 * ========================================================================================================*
 *                   Supported types of argument values (ARGT) and their interpretation                    *
 * ========================================================================================================*
    - <b>Any type that provides a method GtIargType ARGT::Type()</b>\n
           E.g. IargPrime<ARGT>(), IargHostPtr, IargDevicePtr, IargTid etc.
           For these types, the corresponding IGtIarg object is generated by the ARGT::Make() method\n\n
    
    - <b>Any valid prime type (IsValidHliPrimeType<ARGT>)</b>\n
           These types are treated as IargPrime<ARGT>, e.g.
           int32_t => IargPrime<int32_t>; float => IargPrime<float>; enum E => IargPrime<enum E>; etc.\n\n
    
    - <b>The GtReg type</b>\n
           This type is treated as IargPrime<GtReg>\n\n
    
    - <b>Any pointer (IsPointer<ARGT>)</b>\n
           This type is treated as IargHostPtr<ARGT>, e.g.
           void* => IargHostPtr<void*>; const MyType* => IargHostPtr<const MyType*>; etc.\n\n
    
 * ========================================================================================================*
 *                                          Examples                                                       *
 * ========================================================================================================*
    IGtIargFactory& f;

    CreateIarg<float>   (f, 10.2);                       // 32-bit floating point constant
    CreateIarg<uint64_t>(f, reg);                        // 64-bit integer, passed in a register
    CreateIarg<MyType*> (f, &myVar);                     // Host pointer, convetrted into device pointer
    CreateIarg<MyType*> (f, IargDevicePtr(vreg));        // Device pointer, passed in a virtual register
    CreateIarg<void*>   (f, IargBufferOffset(myOffset)); // Offset within profile buffer, convetrted into device pointer
    CreateIarg<uint32_t>(f, IargTid());                  // Current Thread ID (TID)
 * ========================================================================================================*
@endverbatim
 */
template <typename PRMT, typename ARGT, EnableIf<IsSameType<decltype(ARGT::Type), GtIargType()>::value, bool> = true>
inline IargCptr CreateIarg(IGtIargFactory& factory, const ARGT& argVal)
{
    GTPIN_STATIC_ASSERT_MSG((ARGT::Type().IsCompatible(MakeParamTraits<PRMT>().Type())), "Incompatible HLI argument");
    return &(argVal.template Make<PRMT>(factory));
}

template <typename PRMT, typename ARGT, EnableIf<IsValidHliPrimeType<ARGT>::value || IsAkinType<ARGT, GtReg>::value, bool> = true>
inline IargCptr CreateIarg(IGtIargFactory& factory, const ARGT& argVal)
{
    #if (_GTPIN_STL_STD >= 11)
    GTPIN_STATIC_ASSERT_MSG((IsAkinType<ARGT, GtReg>::value || std::is_convertible<ARGT, PRMT>::value), "Incompatible HLI argument");
    #endif
    return &(IargPrime<ARGT>(argVal).template Make<PRMT>(factory));
}

template <typename PRMT, typename ARGT, EnableIf<IsPointer<RemoveCvRef<ARGT>>::value, bool> = true>
inline IargCptr CreateIarg(IGtIargFactory& factory, const ARGT& argVal)
{
    #if (_GTPIN_STL_STD >= 11)
    GTPIN_STATIC_ASSERT_MSG((std::is_convertible<ARGT, PRMT>::value), "Incompatible HLI argument");
    #endif
    return &(IargHostPtr(argVal).template Make<PRMT>(factory));
}

/* ===================================================================================================== */
// CreateIret
/* ===================================================================================================== */
/*! @internal
 * @brief Create a new IGtIret object
 * 
 * @param[in]   factory     Factory of HLI arguments
 * @param[in]   retReg      Register that receives the return value of the HLI function
 * @param       RET         Type of the return value of the function
 * @return Pointer to the new IGtIret object, or NULL if (IsVoidType<RET> || !retReg.IsValid() || retReg.IsNullReg())
 */
template <typename RET> inline IretCptr CreateIret(IGtIargFactory& factory, const GtReg& retReg)
{
    if (IsVoidType<RET>::value || !retReg.IsValid() || retReg.IsNullReg())
    {
        return nullptr;
    }
    return &factory.MakeIret(MakeParamTraits<RET>(), retReg);
}

/*! @} */

#pragma pack(pop)
}
#endif
