/*========================== begin_copyright_notice ============================
Copyright (C) 2019-2022 Intel Corporation

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

/*!
 * @file SIMD operation counting tool definitions
 */
#ifndef SIMDPROF_H_
#define SIMDPROF_H_

#include <vector>
#include <map>

#include "gtpin_api.h"
#include "gtpin_tool_utils.h"

using namespace gtpin;

/* ============================================================================================= */
// Struct SimdProfRecord
/* ============================================================================================= */
/*!
 * Layout of records collected in profile buffer by the Simdprof tool
 */
struct SimdProfRecord
{
    uint64_t opCount;    ///< Number of SIMD operations executed by a group of instructions
};

/* ============================================================================================= */
// Struct SimdProfArgs
/* ============================================================================================= */
/*!
 * SimdProf instrumentation arguments (instruction properties).
 * Each unique combination of these arguments requires a separate instrumentation procedure
 * to be generated for each group of instructions with these properties
 */
struct SimdProfArgs
{
    SimdProfArgs(bool ctrl, uint32_t mask, GtPredicate pred, bool isSend = false) :
                 maskCtrl(ctrl), execMask(mask), predicate(pred), isSendIns(isSend){}

    inline bool operator <  (const SimdProfArgs& other) const;

    bool            maskCtrl;       ///< 'MaskCtrl' flag of instrumented instructions
    uint32_t        execMask;       ///< Execution mask of instrumented instructions
    GtPredicate     predicate;      ///< Predicate of instrumented instructions
    bool            isSendIns;      ///< true if instrumented instructions are SEND instructions
};

/* ============================================================================================= */
// Struct SimdProfGroup
/* ============================================================================================= */
/*!
 * Structure that holds information and profiling results for a group of instructions being
 * instrumented by a single instrumentation routine.
 * @note All instructions within a group have exactly the same SimdProfArgs.
 * @note In order to provide separate channel counters per instruction category (e.g. integer, FP, etc.),
 *       replace the {insCount, opCount} pair with an array of counter pairs per category.
 */
struct SimdProfGroup
{
    SimdProfGroup(uint32_t bbl, uint32_t numIns) : bblId(bbl), insCount(numIns), opCount(0) {}

    BblId    bblId;     ///< Identifier of a BBL that contains this group of instructions
    uint32_t insCount;  ///< Number of instructions in the group
    uint64_t opCount;   ///< Number of SIMD operations (effective channels) executed by each instruction in the group
};

/* ============================================================================================= */
// Struct SimdProfSection
/* ============================================================================================= */
/*!
 * Structure that holds information on a SimdProf section - sequence of instructions for which
 * instrumentation routines can be inserted at the same point.
 * @note All instructions within a section are executed with the same value of the flag register -
 * single dynamic parameter of the SIMD operation calulator
 */
struct SimdProfSection
{
    SimdProfSection(const IGtIns& headIns) :  headInsId(headIns.Id()) {}

    /// Add a new instruction to the section. Update the corresponding SimdProf group within this section
    void AddInstruction(const IGtIns& ins);

    InsId                            headInsId; ///< First intruction of the section - common 
                                                ///< instrumentation point for all groups in the section
    std::map<SimdProfArgs, uint32_t> groups;    ///< SimdProf groups along with the number of instructions
};

/* ============================================================================================= */
// Class SimdProfKernelProfile
/* ============================================================================================= */
/*!
 * Class that represents a kernel profiled by the SimdProf instrumentation
 */
class SimdProfKernelProfile
{
public:
    SimdProfKernelProfile(const IGtKernel& kernel);

    /*!
     * Instrument the kernel.
     * The function is called by the OnKernelBuild handler
     * @return success/failure status
     */
    void Instrument(IGtKernelInstrument& instrumentor);

    /*!
     * Read profiling results which are assumed to be collected and stored in the buffer
     * associated with the kernel.
     * The function is called by the OnKernelComplete handler
     */
     void ReadProfileData(const IGtProfileBuffer* buffer);

     /// @return Total number of SIMD operations executed by the kernel
     uint64_t GetTotalOpCounter() const { return _totalOpCount; }

    std::string           ToString()        const;                          ///< @return Text representation of the profile data
    const GtProfileArray& GetProfileArray() const { return _profileArray; } ///< @return Profile buffer accessor

private:
    /*!
     * Generate instrumentation procedures for all SimdProf groups of the specified SimdProf section.
     * Insert instrumentation at the beginning of the section.
     * Initialize the _profileData array
     * @param[in]      instrumentor     Instrumentor of the GEN kernel
     * @param[in]      section          SimdProf section to be instrumented
     */
    void InstrumentSection(IGtKernelInstrument& instrumentor, const SimdProfSection& section);

    /// @return true/false - use 64-bit/32-bit integer for the operation counter
    static bool Use64BitCounters(const IGtGenCoder& coder);

    /// Increment counter of SIMD operations for the specified BBL by 'incValue'
    void UpdateBblOpCounter(BblId bblId, uint64_t incValue);

    /// @return Extended kernel name
    std::string ExtendedName() const { return _extName; }

private:
    /// Kernel descriptor
    std::string                         _name;              ///< Kernel's name
    std::string                         _extName;           ///< Kernel's extended name
    GtKernelType                        _type;              ///< Kernel's type
    GtGpuPlatform                       _platform;          ///< Kernel's platform
    uint64_t                            _hashId;            ///< Kernel's hash identifier
    GtSimdWidth                         _simd;              ///< Kernel's SIMD width
    uint64_t                            _binarySignature;   ///< Kernel's binary signature
	
    GtProfileArray                      _profileArray;      ///< Profile buffer accessor
    std::vector<SimdProfGroup>          _profileData;       ///< Profiling data for instrumented SimdProf groups

    GtReg _addrReg;     ///< Virtual register that holds address within profile buffer
    GtReg _dataReg;     ///< Virtual register that holds data to be read from/written to profile buffer 

    std::map<BblId, std::pair<InsId, InsId> >         _bblInsInfo;   ///< Head and tail instructions per BBL
    std::map<BblId, uint64_t>                         _bblOpCounts;  ///< Number of executed SIMD operations per BBL
    uint64_t                                          _totalOpCount; ///< Number of SIMD operations executed by the kernel
};

/* ============================================================================================= */
// Class SimdProf
/* ============================================================================================= */
/*!
 * Implementation of the IGtTool interface for the SimdProf tool
 */
class SimdProf : public GtTool
{
public:
    /// Implementation of the IGtTool interface
    const char* Name() const { return "simdprof"; }

    void OnKernelBuild(IGtKernelInstrument& instrumentor);
    void OnKernelRun(IGtKernelDispatch& dispatcher);
    void OnKernelComplete(IGtKernelDispatch& dispatcher);

public:
    std::string ToString() const;                ///< @return Text representation of the profile data
    static SimdProf* Instance();                 ///< @return Single instance of this class
    static void OnFini() { Instance()->Fini(); } ///< Callback function registered with atexit()

protected:
    SimdProf() = default;
    SimdProf(const SimdProf&) = delete;
    SimdProf& operator = (const SimdProf&) = delete;
    ~SimdProf() = default;

    void Fini();                                ///< Post process and dump profiling data

private:
    /// Collection of kernel profiles
    typedef std::map<GtKernelId, SimdProfKernelProfile>  KernelProfiles;
    KernelProfiles  _kernels;
};
#endif
