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

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

/*!
 * @file Abstract representation of the High-Level Instrumentation (HLI) module, and the library of HLI modules
 */

#ifndef IGT_HLI_LIBRARY_H_
#define IGT_HLI_LIBRARY_H_

#include "gt_basic_defs.h"
#include "igt_core.h"

namespace gtpin
{
#pragma pack(push, 8)

/*! @addtogroup GTPIN_HLI_LIBRARY
@{
*/

/* ===================================================================================================== */
// Struct GtHliLanguage
/* ===================================================================================================== */
/*!
 * @brief Source language of the High-Level Instrumentation (HLI) module
 * 
 * @note Currently, only OpenCL C sources can be compiled by GTPin at runtime.
 * However, the tool can use an offline compiler to generate SPIR-V modules from other languages, and then
 * load and link these SPIR-V modules to the instrumented kernels.
 */
struct GtHliLanguage
{
    enum ENUM :uint32_t
    {
        INVALID,      ///< Invalid / unknown language
        OPENCL_C      ///< OpenCL C language
    } value;

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

    inline constexpr bool IsValid()  const { return value != INVALID; }  ///< Return true for any supported language
    inline constexpr bool IsOpenCl() const { return value == OPENCL_C; } ///< Return true for OpenCL C language
    inline const char*    ToString() const;                              ///< Return string representation
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtHliLanguage);

/* ===================================================================================================== */
// Struct GtHliCompilationOptions
/* ===================================================================================================== */
/*!
 * @brief HLI module compilation options
 * @see https://registry.khronos.org/OpenCL/specs/opencl-1.0.pdf and 
 *      https://registry.khronos.org/OpenCL/sdk/1.0/docs/man/xhtml/EXTENSION.html
 *      for description of OpenCL C compiler options
 * @see https://releases.llvm.org/11.0.0/tools/clang/docs/ClangCommandLineReference.html#compilation-flags
 *      for description of the Clang command line flags
 * @note GTPin always appends the directory of the compiled source file to the include search path.
 *       Other include directories can be added to the search path by "-I<dir>" arguments in the 'clangCmdFlags' string.
 */
struct GtHliCompilationOptions
{
    const char* clangCmdFlags;      ///< Command line flags of the Clang compiler
    bool        enableBaseAtomic64; ///< Indicates that the HLI module uses 64-bit base atomic operations
    bool        enableExtAtomic64;  ///< Indicates that the HLI module uses 64-bit extetnded atomic operations

    /// Constructor
    GtHliCompilationOptions(const char* clangFlags = nullptr, bool baseAtomic64 = true, bool extAtomic64 = true) :
        clangCmdFlags(clangFlags), enableBaseAtomic64(baseAtomic64), enableExtAtomic64(extAtomic64) {}
};
GTPIN_ASSERT_STANDARD_LAYOUT(GtHliCompilationOptions);

/* ===================================================================================================== */
// Class IGtHliModule
/* ===================================================================================================== */
/*!
 * @brief Abstract representation of the High-Level Instrumentation (HLI) module
 * 
 * The HLI module is a collection of HLI functions compiled to the SPIR-V representation.
 */
class IGtHliModule
{
public:
    /// @return Name of the module, or empty string if the module is unnamed
    virtual GtCoreString Name() const = 0;

    /// @return Content of the module in the SPIR-V format
    virtual ConstByteSpan Spirv() const = 0;

    /// @return true, if all functions of this module must be built/transformed to SIMD1
    virtual bool IsScalar() const = 0;

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

/*!
 * Handle to the HLI module in IGtHliLibrary
 */
using IGtHliModuleHandle = IGtHliModule*;

/* ===================================================================================================== */
// Class IGtHliLibrary
/* ===================================================================================================== */
/*!
 * @brief Collection of the High-Level Instrumentation (HLI) modules
 * 
 * The HLI library contains HLI modules, compiled to the SPIR-V representation.
 * 
 * @par Availability:
 * - Anytime and anywhere
 */
class IGtHliLibrary
{
public:

    /*!
     * @brief Add an HLI module to this library
     * 
     * @param[in]       spirv       Content of the HLI module in the SPIRV format
     * @param[in]       name        Optional name of the module
     * @param[in]       isScalar    true  - always build this HLI module in SIMD1
     *                              false - build this HLI module in SIMD of the module being instrumented
     * 
     * @return Handle to the new module, or nullptr if the module could not be added to the library.
     *         In case of error, the IGtCore::LastError() function provides the specific failure reason
     */
    virtual IGtHliModuleHandle AddModule(ConstByteSpan spirv, const char* name = nullptr, bool isScalar = true) = 0;

    /*!
     * @brief Load an HLI module from the specified file and add it to this library
     * 
     * @param[in]       fileName    Full name of the file that contains HLI module in the SPIRV format
     * @param[in]       name        Optional name of the module
     * @param[in]       isScalar    true  - always build this HLI module in SIMD1
     *                              false - build this HLI module in SIMD of the module being instrumented
     * 
     * @return Handle to the loaded module, or nullptr if the module loading failed.
     *         In case of error, the IGtCore::LastError() function provides the specific failure reason
     */
    virtual IGtHliModuleHandle LoadModule(const char* fileName, const char* name = nullptr, bool isScalar = true) = 0;

    /*!
     * @brief Compile the specifed source to the SPIR-V representation, and add the compiled module to this library
     * 
     * @param[in]       source      Source code of the HLI module
     * @param[in]       language    The source language
     * @param[in]       options     The module's compilation options
     * @param[in]       name        Optional name of the module
     * @param[in]       isScalar    true  - always build this HLI module in SIMD1
     *                              false - build this HLI module in SIMD of the module being instrumented
     * 
     * @return Handle to the new module, or nullptr if the module could not be added to the library.
     *         In case of error, the IGtCore::LastError() function provides the specific failure reason
     */
    virtual IGtHliModuleHandle CompileModule(const char* source,
                                             GtHliLanguage language          = GtHliLanguage::OPENCL_C,
                                             GtHliCompilationOptions options = {},
                                             const char* name                = nullptr,
                                             bool isScalar                   = true) = 0;

    /*!
     * @brief Compile the specifed source file to the SPIR-V representation, and add the compiled module to this library
     * 
     * @param[in]       filePath    Path of the file that contains source code of the HLI module
     * @param[in]       language    The source language
     * @param[in]       options     The module's compilation options
     * @param[in]       name        Optional name of the module
     * @param[in]       isScalar    true  - always build this HLI module in SIMD1
     *                              false - build this HLI module in SIMD of the module being instrumented
     * 
     * @return Handle to the new module, or nullptr if the module could not be added to the library.
     *         In case of error, the IGtCore::LastError() function provides the specific failure reason
     */
    virtual IGtHliModuleHandle CompileModuleFromFile(const char* filePath,
                                                     GtHliLanguage language          = GtHliLanguage::OPENCL_C,
                                                     GtHliCompilationOptions options = {},
                                                     const char* name                = nullptr,
                                                     bool isScalar                   = true) = 0;

    /*!
     * @return Number of HLI modules in this library
     */
    virtual uint32_t NumModules() const = 0;

    /*!
     * @brief Get handle to the HLI module specified by its ordinal number
     * 
     * @pre ordNum is an integer in the [0, NumModules()] range
     * @return Handle to the requested module
     */
    virtual IGtHliModuleHandle GetModule(uint32_t ordNum) const = 0;

    /*!
     * @brief Check to see if this library contains HLI module with the specified handle
     * 
     * @param[in]       moduleHandle    The handle to be checked
     * @return True, if this library contains a module with the specified handle, false - otherwise
     */
    virtual bool HasModule(IGtHliModuleHandle moduleHandle) const = 0;

    /*!
     * @brief Find HLI module with the specified name
     * 
     * @param[in]       name      The name assigned to the module
     * @return Handle to the function, or nullptr if the module is not found in this library
     */
    virtual IGtHliModuleHandle FindModule(const char* name) const = 0;

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

/*! @} */

/* ===================================================================================================== */
// GtHliLanguage implementation
/* ===================================================================================================== */
const char* GtHliLanguage::ToString() const
{
    switch (value)
    {
    case OPENCL_C:    return "OpenCL_C";
    default:          return "";
    }
}

#pragma pack(pop)
}
#endif
