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

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

/*!
 * @file Abstract factory of HLI function arguments and return value
 */

#ifndef IGT_IARG_FACTORY_H_
#define IGT_IARG_FACTORY_H_

#include "gt_basic_defs.h"
#include "gt_basic_utils.h"
#include "gt_reg.h"
#include "igt_iarg.h"
#include "igt_ins.h"
#include "gt_ins_fields.h"

namespace gtpin
{
#pragma pack(push, 8)

/*! @addtogroup GTPIN_HLI_ARGUMENT
 @{
 */

/* ===================================================================================================== */
// Class GtIargImmValue
/* ===================================================================================================== */
/*!
 * @brief Class that represents an immediate scalar value of the HLI argument and provides conversion methods
 * to/from other trivially copyable types
 */
class GtIargImmValue
{
public:
    GtIargImmValue() = default;

    #if (_GTPIN_STL_STD >= 11)

    /// Construct GtIargImmValue object from a trivially copyable type 'U'
    template <typename U, EnableIf<std::is_trivially_copyable<U>::value && (sizeof(U) <= sizeof(uint64_t)), bool> = true>
    GtIargImmValue(U val) { *reinterpret_cast<U*>(_val) = val; }

    /// Convert GtIargImmValue object to a trivially copyable type 'U'
    template <typename U, EnableIf<std::is_trivially_copyable<U>::value && (sizeof(U) <= sizeof(uint64_t)), bool> = true>
    operator U() const { return *reinterpret_cast<const U*>(_val); }

    #else

    /// Construct GtIargImmValue object from type 'U'
    template <typename U> GtIargImmValue(U val) { *reinterpret_cast<U*>(_val) = val; }

    /// Convert GtIargImmValue object to type 'U'
    template <typename U> operator U() const { return *reinterpret_cast<const U*>(_val); }

    #endif

private:
    uint8_t  _val[sizeof(uint64_t)];
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtIargImmValue);

/* ===================================================================================================== */
// Class IGtIargFactory
/* ===================================================================================================== */
/*!
 * @brief Factory and container of HLI function arguments and return value representations
 * 
 * Each instance of this factory is associated with a single instrumentation session. The factory methods
 * create IGtIarg objects and store them in the internal container. All these objects are destroyed when
 * the instrumentation session ends, so the methods for freeing individual IGtIarg instances are not provided.
 * 
 * @par Availability:
 * - IGtTool::OnKernelBuild
 */
class IGtIargFactory
{
/* ===================================================================================================== */
// The implementation of this class is not required to be thread safe. It is intended to be used
// within serialized instrumentation sessions
/* ===================================================================================================== */
public:
    /*!
     * @brief Create an argument of GtIargType::PRIME type, passed by an immediate value
     * @param[in]   paramTraits     Parameter traits
     * @param[in]   value           Immediate argument value
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakePrime(GtHliParamTraits paramTraits, GtIargImmValue value) = 0;

    /*!
     * @brief Create an argument of GtIargType::PRIME type, passed by a register value
     * @param[in]   paramTraits     Parameter traits
     * @param[in]   reg             Register that holds the argument value
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakePrime(GtHliParamTraits paramTraits, const GtReg& reg) = 0;

    /*!
     * @brief Create an argument of GtIargType::HOST_PTR type
     * @param[in]   hostPtr         Pointer in the host memory space
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeHostPtr(const void* hostPtr) = 0;

    /*!
     * @brief Create an argument of GtIargType::DEVICE_PTR type, passed by a register value
     * @param[in]   addrReg         Register that holds an address in the device memory space
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeDevicePtr(const GtReg& addrReg) = 0;

    /*!
     * @brief Create an argument of GtIargType::BUFFER_OFFSET type, passed by a 32-bit immediate value
     * @param[in]   offset          Offset in the GTPin profile buffer
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeBufferOffset(uint32_t offset) = 0;

    /*!
     * @brief Create an argument of GtIargType::BUFFER_OFFSET type, passed by a register value
     * @param[in]   offsetReg       Register that holds an offset in the GTPin profile buffer
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeBufferOffset(const GtReg& offsetReg) = 0;

    /*!
     * @brief Create an argument of GtIargType::TID type
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeTid() = 0;

    /*!
     * @brief Create an argument of GtIargType::FFTID type
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeFfTid() = 0;

    /*!
     * @brief Create an argument of GtIargType::GRF_RANGE type
     * @param[in]   firstReg        Ordinal number of the first GRF register in the range
     * @param[in]   numRegs         Number of registers in the range
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeGrfRange(uint32_t firstReg, uint32_t numRegs) = 0;

    /*!
     * @brief Create an argument of GtIargType::CONST_GRF_RANGE type
     * @param[in]   firstReg        Ordinal number of the first GRF register in the range
     * @param[in]   numRegs         Number of registers in the range
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeConstGrfRange(uint32_t firstReg, uint32_t numRegs) = 0;

    /*!
     * @brief Create an argument of GtIargType::SIMD_MASK type
     * 
     * The new argument 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
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeSimdMask(bool ignoreCeMask, uint32_t execMask, const GtPredicate& predicate) = 0;

    /*!
     * Create an argument of GtIargType::INS_OP_MASK type
     * 
     * The new argument represents 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())
     * 
     * @note The INS_OP_MASK argument is similar to the SIMD_MASK argument created by the MakeSimdMask function.
     *       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())
     * 
     * @param[in] ins   Instruction, the operation mask is evaluated for
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeInsOpMask(const IGtIns& ins) = 0;


    /*!
     * @brief Create an argument of GtIargType::SLM_PTR type
     * @return Reference to the new IGtIarg object
     */
    virtual const IGtIarg& MakeSlmPtr() = 0;

    /*!
     * Create an IGtIret object that represents HLI function's return value
     * @param[in]   retTraits       Traits of the function's return value
     * @param[in]   reg             Register that holds the return value
     * @return Reference to the new IGtIret object
     */
    virtual const IGtIret& MakeIret(GtHliParamTraits retTraits, const GtReg& reg) = 0;

    /*!
     * Create an IGtIret object that represents void / unused HLI function's return value
     * @return Reference to the new IGtIret object
     */
    virtual const IGtIret& MakeIret() = 0;

protected:
    /// @internal Destructor
    virtual ~IGtIargFactory() = default;
};
GTPIN_ASSERT_PURE_ABSTRACT(IGtIargFactory);

/*! @} */

#pragma pack(pop)
}
#endif
