/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2023 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

#pragma once

#include "MetaDataApiUtils.h"
#include "MetaDataApi.h"

// Following classes read/write its own specific structure from/to LLVM metadata.
namespace IGC::SPIRMD
{
    using VectorTypeHintMetaData = IGC::IGCMD::VectorTypeHintMetaData;
    using VectorTypeHintMetaDataHandle = MetaObjectHandle<VectorTypeHintMetaData>;

    class VersionMetaData;
    using VersionMetaDataHandle = MetaObjectHandle<VersionMetaData>;

    class WorkGroupDimensionsMetaData;
    using WorkGroupDimensionsMetaDataHandle = MetaObjectHandle<WorkGroupDimensionsMetaData>;

    class SubGroupDimensionsMetaData;
    using SubGroupDimensionsMetaDataHandle = MetaObjectHandle<SubGroupDimensionsMetaData>;

    class WorkgroupWalkOrderMetaData;
    using WorkgroupWalkOrderMetaDataHandle = MetaObjectHandle<WorkgroupWalkOrderMetaData>;

    class KernelMetaData;
    using KernelMetaDataHandle = MetaObjectHandle<KernelMetaData>;

    using InnerUsedKhrExtensionsMetaDataList = MetaDataList<std::string>;
    using InnerUsedKhrExtensionsMetaDataListHandle = MetaObjectHandle<InnerUsedKhrExtensionsMetaDataList>;

    using InnerUsedOptionalCoreFeaturesMetaDataList = MetaDataList<std::string>;
    using InnerUsedOptionalCoreFeaturesMetaDataListHandle = MetaObjectHandle<InnerUsedOptionalCoreFeaturesMetaDataList>;

    using InnerCompilerOptionsMetaDataList = MetaDataList<std::string>;
    using InnerCompilerOptionsMetaDataListHandle = MetaObjectHandle<InnerCompilerOptionsMetaDataList>;

    using InnerCompilerExternalOptionsMetaDataList = MetaDataList<std::string>;
    using InnerCompilerExternalOptionsMetaDataListHandle = MetaObjectHandle<InnerCompilerExternalOptionsMetaDataList>;

    class VersionMetaData : public IMetaDataObject
    {
    public:
        using _Mybase = IMetaDataObject;

        VersionMetaData(const llvm::MDNode* pNode, bool hasId);
        VersionMetaData();
        VersionMetaData(const char* name);

        // Returns true if any of the VersionMetaData`s members has changed
        bool dirty() const override;

        // Returns true if the structure was loaded from the metadata or was changed
        bool hasValue() const;

        // Discards the changes done to the VersionMetaData instance
        void discardChanges() override;

        // Generates the new MDNode hierarchy for the given structure
        llvm::Metadata* generateNode(llvm::LLVMContext& context) const;

        // Saves the structure changes to the given MDNode
        void save(llvm::LLVMContext& context, llvm::MDNode* pNode) const;

        //
        // Data members
        //
        using MajorType = MetaDataValue<int32_t>;
        using MinorType = MetaDataValue<int32_t>;

        // Major
        MajorType::value_type getMajor() const
        {
            return m_Major.get();
        }
        bool isMajorHasValue() const
        {
            return m_Major.hasValue();
        }

        // Minor
        MinorType::value_type getMinor() const
        {
            return m_Minor.get();
        }
        bool isMinorHasValue() const
        {
            return m_Minor.hasValue();
        }

    private:
        // parent node
        const llvm::MDNode* m_pNode;

        // data members
        MajorType m_Major;
        MinorType m_Minor;
    };

    class WorkGroupDimensionsMetaData : public IMetaDataObject
    {
    public:
        using _Mybase = IMetaDataObject;

        WorkGroupDimensionsMetaData(const llvm::MDNode* pNode, bool hasId);
        WorkGroupDimensionsMetaData();
        WorkGroupDimensionsMetaData(const char* name);

        // Returns true if any of the VersionMetaData`s members has changed
        bool dirty() const override;

        // Returns true if the structure was loaded from the metadata or was changed
        bool hasValue() const;

        // Discards the changes done to the VersionMetaData instance
        void discardChanges() override;

        // Generates the new MDNode hierarchy for the given structure
        llvm::Metadata* generateNode(llvm::LLVMContext& context) const;

        // Saves the structure changes to the given MDNode
        void save(llvm::LLVMContext& context, llvm::MDNode* pNode) const;

        //
        // Data members
        //
        using DimType = MetaDataValue<int32_t>;

        // XDim
        DimType::value_type getXDim() const
        {
            return m_XDim.get();
        }
        void setXDim(const DimType::value_type& val)
        {
            m_XDim.set(val);
        }
        bool isXDimHasValue() const
        {
            return m_XDim.hasValue();
        }

        // YDim
        DimType::value_type getYDim() const
        {
            return m_YDim.get();
        }
        void setYDim(const DimType::value_type& val)
        {
            m_YDim.set(val);
        }
        bool isYDimHasValue() const
        {
            return m_YDim.hasValue();
        }

        // ZDim
        DimType::value_type getZDim() const
        {
            return m_ZDim.get();
        }
        void setZDim(const DimType::value_type& val)
        {
            m_ZDim.set(val);
        }
        bool isZDimHasValue() const
        {
            return m_ZDim.hasValue();
        }

    private:
        // parent node
        const llvm::MDNode* m_pNode;

        // data members
        DimType m_XDim;
        DimType m_YDim;
        DimType m_ZDim;
    };

    class SubGroupDimensionsMetaData : public IMetaDataObject
    {
    public:
        using _Mybase = IMetaDataObject;

        SubGroupDimensionsMetaData(const llvm::MDNode* pNode, bool hasId);
        SubGroupDimensionsMetaData();
        SubGroupDimensionsMetaData(const char* name);

        // Returns true if any of the VersionMetaData`s members has changed
        bool dirty() const override;

        // Returns true if the structure was loaded from the metadata or was changed
        bool hasValue() const;

        // Discards the changes done to the VersionMetaData instance
        void discardChanges() override;

        // Generates the new MDNode hierarchy for the given structure
        llvm::Metadata* generateNode(llvm::LLVMContext& context) const;

        // Saves the structure changes to the given MDNode
        void save(llvm::LLVMContext& context, llvm::MDNode* pNode) const;

        //
        // Data members
        //
        using SIMDSizeType = MetaDataValue<int32_t>;

        // SIMDSize
        SIMDSizeType::value_type getSIMDSize() const
        {
            return m_SIMDSize.get();
        }
        void setSIMDSize(const SIMDSizeType::value_type& val)
        {
            m_SIMDSize.set(val);
        }
        bool isSIMDSizeHasValue() const
        {
            return m_SIMDSize.hasValue();
        }

    private:
        // parent node
        const llvm::MDNode* m_pNode;

        // data members
        SIMDSizeType m_SIMDSize;
    };

    class WorkgroupWalkOrderMetaData : public IMetaDataObject
    {
    public:
        using _Mybase = IMetaDataObject;

        WorkgroupWalkOrderMetaData(const llvm::MDNode* pNode, bool hasId);
        WorkgroupWalkOrderMetaData();
        WorkgroupWalkOrderMetaData(const char* name);

        // Returns true if any of the ArgInfoMetaData`s members has changed
        bool dirty() const override;

        // Returns true if the structure was loaded from the metadata or was changed
        bool hasValue() const;

        // Discards the changes done to the ArgInfoMetaData instance
        void discardChanges() override;

        // Generates the new MDNode hierarchy for the given structure
        llvm::Metadata* generateNode(llvm::LLVMContext& context) const;

        // Saves the structure changes to the given MDNode
        void save(llvm::LLVMContext& context, llvm::MDNode* pNode) const;

        //
        // Data members
        //
        using DimType = MetaDataValue<int32_t>;

        // workgroup walk order
        DimType::value_type getDim0() const
        {
            return m_Dim0.get();
        }
        DimType::value_type getDim1() const
        {
            return m_Dim1.get();
        }
        DimType::value_type getDim2() const
        {
            return m_Dim2.get();
        }

    private:
        // parent node
        const llvm::MDNode* m_pNode;

        // data members
        DimType m_Dim0;
        DimType m_Dim1;
        DimType m_Dim2;
    };

    class KernelMetaData : public IMetaDataObject
    {
    public:
        using _Mybase = IMetaDataObject;

        KernelMetaData(const llvm::MDNode* pNode, bool hasId);
        KernelMetaData();
        KernelMetaData(const char* name);

        // Returns true if any of the ArgInfoMetaData`s members has changed
        bool dirty() const override;

        // Returns true if the structure was loaded from the metadata or was changed
        bool hasValue() const;

        // Discards the changes done to the ArgInfoMetaData instance
        void discardChanges() override;

        // Generates the new MDNode hierarchy for the given structure
        llvm::Metadata* generateNode(llvm::LLVMContext& context) const;

        // Saves the structure changes to the given MDNode
        void save(llvm::LLVMContext& context, llvm::MDNode* pNode) const;

        //
        // Data members
        //
        using FunctionType = MetaDataValue<llvm::Function>;
        using WorkGroupSizeHintType = WorkGroupDimensionsMetaDataHandle;
        using RequiredWorkGroupSizeType = WorkGroupDimensionsMetaDataHandle;
        using RequiredSubGroupSizeType = SubGroupDimensionsMetaDataHandle;
        using WorkgroupWalkOrderType = WorkgroupWalkOrderMetaDataHandle;
        using VectorTypeHintType = VectorTypeHintMetaDataHandle;
        using ArgAddressSpacesList = MetaDataList<int32_t>;
        using ArgAccessQualifiersList = MetaDataList<std::string>;
        using ArgTypesList = MetaDataList<std::string>;
        using ArgBaseTypesList = MetaDataList<std::string>;
        using ArgTypeQualifiersList = MetaDataList<std::string>;
        using ArgNamesList = MetaDataList<std::string>;

        // Function
        FunctionType::value_type getFunction() const
        {
            return m_Function.get();
        }
        bool isFunctionHasValue() const
        {
            return m_Function.hasValue();
        }

        // WorkGroupSizeHint
        const WorkGroupSizeHintType getWorkGroupSizeHint()
        {
            return m_WorkGroupSizeHint;
        }

        // RequiredWorkGroupSize
        const RequiredWorkGroupSizeType getRequiredWorkGroupSize()
        {
            return m_RequiredWorkGroupSize;
        }

        // RequiredSubGroupSize
        const RequiredSubGroupSizeType getRequiredSubGroupSize()
        {
            return m_RequiredSubGroupSize;
        }

        // WorkgroupWalkOrder
        const WorkgroupWalkOrderType getWorkgroupWalkOrder()
        {
            return m_WorkgroupWalkOrder;
        }

        // VectorTypeHint
        const VectorTypeHintType getVectorTypeHint()
        {
            return m_VectorTypeHint;
        }

        // ArgAddressSpaces
        ArgAddressSpacesList::const_iterator begin_ArgAddressSpaces() const
        {
            return m_ArgAddressSpaces.begin();
        }
        ArgAddressSpacesList::const_iterator end_ArgAddressSpaces() const
        {
            return m_ArgAddressSpaces.end();
        }
        size_t size_ArgAddressSpaces()  const
        {
            return m_ArgAddressSpaces.size();
        }
        bool empty_ArgAddressSpaces()  const
        {
            return m_ArgAddressSpaces.empty();
        }
        bool isArgAddressSpacesHasValue() const
        {
            return m_ArgAddressSpaces.hasValue();
        }
        const ArgAddressSpacesList::item_type getArgAddressSpacesItem(size_t index) const
        {
            return m_ArgAddressSpaces.getItem(index);
        }

        // ArgAccessQualifiers
        ArgAccessQualifiersList::const_iterator begin_ArgAccessQualifiers() const
        {
            return m_ArgAccessQualifiers.begin();
        }
        ArgAccessQualifiersList::const_iterator end_ArgAccessQualifiers() const
        {
            return m_ArgAccessQualifiers.end();
        }
        size_t size_ArgAccessQualifiers()  const
        {
            return m_ArgAccessQualifiers.size();
        }
        bool empty_ArgAccessQualifiers()  const
        {
            return m_ArgAccessQualifiers.empty();
        }
        bool isArgAccessQualifiersHasValue() const
        {
            return m_ArgAccessQualifiers.hasValue();
        }
        const ArgAccessQualifiersList::item_type getArgAccessQualifiersItem(size_t index) const
        {
            return m_ArgAccessQualifiers.getItem(index);
        }

        // ArgTypes
        ArgTypesList::const_iterator begin_ArgTypes() const
        {
            return m_ArgTypes.begin();
        }
        ArgTypesList::const_iterator end_ArgTypes() const
        {
            return m_ArgTypes.end();
        }
        size_t size_ArgTypes()  const
        {
            return m_ArgTypes.size();
        }
        bool empty_ArgTypes()  const
        {
            return m_ArgTypes.empty();
        }
        bool isArgTypesHasValue() const
        {
            return m_ArgTypes.hasValue();
        }
        const ArgTypesList::item_type getArgTypesItem(size_t index) const
        {
            return m_ArgTypes.getItem(index);
        }

        // ArgBaseTypes
        ArgBaseTypesList::const_iterator begin_ArgBaseTypes() const
        {
            return m_ArgBaseTypes.begin();
        }
        ArgBaseTypesList::const_iterator end_ArgBaseTypes() const
        {
            return m_ArgBaseTypes.end();
        }
        size_t size_ArgBaseTypes()  const
        {
            return m_ArgBaseTypes.size();
        }
        bool empty_ArgBaseTypes()  const
        {
            return m_ArgBaseTypes.empty();
        }
        bool isArgBaseTypesHasValue() const
        {
            return m_ArgBaseTypes.hasValue();
        }
        const ArgBaseTypesList::item_type getArgBaseTypesItem(size_t index) const
        {
            return m_ArgBaseTypes.getItem(index);
        }

        // ArgTypeQualifiers
        ArgTypeQualifiersList::const_iterator begin_ArgTypeQualifiers() const
        {
            return m_ArgTypeQualifiers.begin();
        }
        ArgTypeQualifiersList::const_iterator end_ArgTypeQualifiers() const
        {
            return m_ArgTypeQualifiers.end();
        }
        size_t size_ArgTypeQualifiers()  const
        {
            return m_ArgTypeQualifiers.size();
        }
        bool empty_ArgTypeQualifiers()  const
        {
            return m_ArgTypeQualifiers.empty();
        }
        bool isArgTypeQualifiersHasValue() const
        {
            return m_ArgTypeQualifiers.hasValue();
        }
        const ArgTypeQualifiersList::item_type getArgTypeQualifiersItem(size_t index) const
        {
            return m_ArgTypeQualifiers.getItem(index);
        }

        // ArgNames
        ArgNamesList::const_iterator begin_ArgNames() const
        {
            return m_ArgNames.begin();
        }
        ArgNamesList::const_iterator end_ArgNames() const
        {
            return m_ArgNames.end();
        }
        size_t size_ArgNames()  const
        {
            return m_ArgNames.size();
        }
        bool empty_ArgNames()  const
        {
            return m_ArgNames.empty();
        }
        bool isArgNamesHasValue() const
        {
            return m_ArgNames.hasValue();
        }
        const ArgNamesList::item_type getArgNamesItem(size_t index) const
        {
            return m_ArgNames.getItem(index);
        }

    private:
        // parent node
        const llvm::MDNode* m_pNode;

        // data members
        FunctionType m_Function;
        WorkGroupSizeHintType m_WorkGroupSizeHint;
        RequiredWorkGroupSizeType m_RequiredWorkGroupSize;
        RequiredSubGroupSizeType m_RequiredSubGroupSize;
        WorkgroupWalkOrderType m_WorkgroupWalkOrder;
        VectorTypeHintType m_VectorTypeHint;
        ArgAddressSpacesList m_ArgAddressSpaces;
        ArgAccessQualifiersList m_ArgAccessQualifiers;
        ArgTypesList m_ArgTypes;
        ArgBaseTypesList m_ArgBaseTypes;
        ArgTypeQualifiersList m_ArgTypeQualifiers;
        ArgNamesList m_ArgNames;
    };

    class SpirMetaDataUtils
    {
    public:
        // data member types
        using KernelsList = NamedMDNodeList<KernelMetaDataHandle>;
        using CompilerOptionsList = NamedMDNodeList<InnerCompilerOptionsMetaDataListHandle>;
        using CompilerExternalOptionsList = NamedMDNodeList<InnerCompilerExternalOptionsMetaDataListHandle>;
        using FloatingPointContractionsList = NamedMDNodeList<int32_t>;
        using UsedOptionalCoreFeaturesList = NamedMDNodeList<InnerUsedOptionalCoreFeaturesMetaDataListHandle>;
        using UsedKhrExtensionsList = NamedMDNodeList<InnerUsedKhrExtensionsMetaDataListHandle>;
        using SpirVersionsList = NamedMDNodeList<VersionMetaDataHandle>;
        using OpenCLVersionsList = NamedMDNodeList<VersionMetaDataHandle>;

        // If using this constructor, setting the llvm module by the setModule
        // function is needed for correct operation.
        SpirMetaDataUtils() {}

        SpirMetaDataUtils(llvm::Module* pModule) :
            m_Kernels(pModule->getNamedMetadata("opencl.kernels")),
            m_CompilerOptions(pModule->getNamedMetadata("opencl.compiler.options")),
            m_CompilerExternalOptions(pModule->getNamedMetadata("opencl.compiler.ext.options")),
            m_FloatingPointContractions(pModule->getNamedMetadata("opencl.enable.FP_CONTRACT")),
            m_UsedOptionalCoreFeatures(pModule->getNamedMetadata("opencl.used.optional.core.features")),
            m_UsedKhrExtensions(pModule->getNamedMetadata("opencl.used.extensions")),
            m_SpirVersions(pModule->getNamedMetadata("opencl.spir.version")),
            m_OpenCLVersions(pModule->getNamedMetadata("opencl.ocl.version")),
            m_pModule(pModule)
        {}

        void setModule(llvm::Module* pModule) {
            m_Kernels = pModule->getNamedMetadata("opencl.kernels");
            m_CompilerOptions = pModule->getNamedMetadata("opencl.compiler.options");
            m_CompilerExternalOptions = pModule->getNamedMetadata("opencl.compiler.ext.options");
            m_FloatingPointContractions = pModule->getNamedMetadata("opencl.enable.FP_CONTRACT");
            m_UsedOptionalCoreFeatures = pModule->getNamedMetadata("opencl.used.optional.core.features");
            m_UsedKhrExtensions = pModule->getNamedMetadata("opencl.used.extensions");
            m_SpirVersions = pModule->getNamedMetadata("opencl.spir.version");
            m_OpenCLVersions = pModule->getNamedMetadata("opencl.ocl.version");
            m_pModule = pModule;
        }

        ~SpirMetaDataUtils() {}

        // Kernels
        KernelsList::const_iterator begin_Kernels() const
        {
            return m_Kernels.begin();
        }

        KernelsList::const_iterator end_Kernels() const
        {
            return m_Kernels.end();
        }

        size_t size_Kernels()  const
        {
            return m_Kernels.size();
        }

        bool empty_Kernels()  const
        {
            return m_Kernels.empty();
        }

        bool isKernelsHasValue() const
        {
            return m_Kernels.hasValue();
        }

        const KernelsList::item_type getKernelsItem(size_t index) const
        {
            return m_Kernels.getItem(index);
        }

        // CompilerOptions
        CompilerOptionsList::const_iterator begin_CompilerOptions() const
        {
            return m_CompilerOptions.begin();
        }

        CompilerOptionsList::const_iterator end_CompilerOptions() const
        {
            return m_CompilerOptions.end();
        }

        size_t size_CompilerOptions()  const
        {
            return m_CompilerOptions.size();
        }

        bool empty_CompilerOptions()  const
        {
            return m_CompilerOptions.empty();
        }

        bool isCompilerOptionsHasValue() const
        {
            return m_CompilerOptions.hasValue();
        }

        const CompilerOptionsList::item_type getCompilerOptionsItem(size_t index) const
        {
            return m_CompilerOptions.getItem(index);
        }

        // CompilerExternalOptions
        CompilerExternalOptionsList::const_iterator begin_CompilerExternalOptions() const
        {
            return m_CompilerExternalOptions.begin();
        }

        CompilerExternalOptionsList::const_iterator end_CompilerExternalOptions() const
        {
            return m_CompilerExternalOptions.end();
        }

        size_t size_CompilerExternalOptions()  const
        {
            return m_CompilerExternalOptions.size();
        }

        bool empty_CompilerExternalOptions()  const
        {
            return m_CompilerExternalOptions.empty();
        }

        bool isCompilerExternalOptionsHasValue() const
        {
            return m_CompilerExternalOptions.hasValue();
        }

        const CompilerExternalOptionsList::item_type getCompilerExternalOptionsItem(size_t index) const
        {
            return m_CompilerExternalOptions.getItem(index);
        }

        // FloatingPointContractions
        FloatingPointContractionsList::const_iterator begin_FloatingPointContractions() const
        {
            return m_FloatingPointContractions.begin();
        }

        FloatingPointContractionsList::const_iterator end_FloatingPointContractions() const
        {
            return m_FloatingPointContractions.end();
        }

        size_t size_FloatingPointContractions()  const
        {
            return m_FloatingPointContractions.size();
        }

        bool empty_FloatingPointContractions()  const
        {
            return m_FloatingPointContractions.empty();
        }

        bool isFloatingPointContractionsHasValue() const
        {
            return m_FloatingPointContractions.hasValue();
        }

        const FloatingPointContractionsList::item_type getFloatingPointContractionsItem(size_t index) const
        {
            return m_FloatingPointContractions.getItem(index);
        }

        // UsedOptionalCoreFeatures
        UsedOptionalCoreFeaturesList::const_iterator begin_UsedOptionalCoreFeatures() const
        {
            return m_UsedOptionalCoreFeatures.begin();
        }

        UsedOptionalCoreFeaturesList::const_iterator end_UsedOptionalCoreFeatures() const
        {
            return m_UsedOptionalCoreFeatures.end();
        }

        size_t size_UsedOptionalCoreFeatures()  const
        {
            return m_UsedOptionalCoreFeatures.size();
        }

        bool empty_UsedOptionalCoreFeatures()  const
        {
            return m_UsedOptionalCoreFeatures.empty();
        }

        bool isUsedOptionalCoreFeaturesHasValue() const
        {
            return m_UsedOptionalCoreFeatures.hasValue();
        }

        const UsedOptionalCoreFeaturesList::item_type getUsedOptionalCoreFeaturesItem(size_t index) const
        {
            return m_UsedOptionalCoreFeatures.getItem(index);
        }

        // UsedKhrExtensions
        UsedKhrExtensionsList::const_iterator begin_UsedKhrExtensions() const
        {
            return m_UsedKhrExtensions.begin();
        }

        UsedKhrExtensionsList::const_iterator end_UsedKhrExtensions() const
        {
            return m_UsedKhrExtensions.end();
        }

        size_t size_UsedKhrExtensions()  const
        {
            return m_UsedKhrExtensions.size();
        }

        bool empty_UsedKhrExtensions()  const
        {
            return m_UsedKhrExtensions.empty();
        }

        bool isUsedKhrExtensionsHasValue() const
        {
            return m_UsedKhrExtensions.hasValue();
        }

        const UsedKhrExtensionsList::item_type getUsedKhrExtensionsItem(size_t index) const
        {
            return m_UsedKhrExtensions.getItem(index);
        }

        // SpirVersions
        SpirVersionsList::const_iterator begin_SpirVersions() const
        {
            return m_SpirVersions.begin();
        }

        SpirVersionsList::const_iterator end_SpirVersions() const
        {
            return m_SpirVersions.end();
        }

        size_t size_SpirVersions()  const
        {
            return m_SpirVersions.size();
        }

        bool empty_SpirVersions()  const
        {
            return m_SpirVersions.empty();
        }

        bool isSpirVersionsHasValue() const
        {
            return m_SpirVersions.hasValue();
        }

        const SpirVersionsList::item_type getSpirVersionsItem(size_t index) const
        {
            return m_SpirVersions.getItem(index);
        }

        // OpenCLVersions
        OpenCLVersionsList::const_iterator begin_OpenCLVersions() const
        {
            return m_OpenCLVersions.begin();
        }

        OpenCLVersionsList::const_iterator end_OpenCLVersions() const
        {
            return m_OpenCLVersions.end();
        }

        size_t size_OpenCLVersions()  const
        {
            return m_OpenCLVersions.size();
        }

        bool empty_OpenCLVersions()  const
        {
            return m_OpenCLVersions.empty();
        }

        bool isOpenCLVersionsHasValue() const
        {
            return m_OpenCLVersions.hasValue();
        }

        const OpenCLVersionsList::item_type getOpenCLVersionsItem(size_t index) const
        {
            return m_OpenCLVersions.getItem(index);
        }

        void deleteMetadata()
        {
            llvm::NamedMDNode* KernelsNode = m_pModule->getNamedMetadata("opencl.kernels");
            if (KernelsNode)
            {
                m_pModule->eraseNamedMetadata(KernelsNode);
            }

            llvm::NamedMDNode* CompilerOptionsNode = m_pModule->getNamedMetadata("opencl.compiler.options");
            if (CompilerOptionsNode)
            {
                m_pModule->eraseNamedMetadata(CompilerOptionsNode);
            }

            llvm::NamedMDNode* CompilerExternalOptionsNode = m_pModule->getNamedMetadata("opencl.compiler.ext.options");
            if (CompilerExternalOptionsNode)
            {
                m_pModule->eraseNamedMetadata(CompilerExternalOptionsNode);
            }

            llvm::NamedMDNode* FloatingPointContractionsNode = m_pModule->getNamedMetadata("opencl.enable.FP_CONTRACT");
            if (FloatingPointContractionsNode)
            {
                m_pModule->eraseNamedMetadata(FloatingPointContractionsNode);
            }

            llvm::NamedMDNode* UsedOptionalCoreFeaturesNode = m_pModule->getNamedMetadata("opencl.used.optional.core.features");
            if (UsedOptionalCoreFeaturesNode)
            {
                m_pModule->eraseNamedMetadata(UsedOptionalCoreFeaturesNode);
            }

            llvm::NamedMDNode* UsedKhrExtensionsNode = m_pModule->getNamedMetadata("opencl.used.extensions");
            if (UsedKhrExtensionsNode)
            {
                m_pModule->eraseNamedMetadata(UsedKhrExtensionsNode);
            }

            llvm::NamedMDNode* SpirVersionsNode = m_pModule->getNamedMetadata("opencl.spir.version");
            if (SpirVersionsNode)
            {
                m_pModule->eraseNamedMetadata(SpirVersionsNode);
            }

            llvm::NamedMDNode* OpenCLVersionsNode = m_pModule->getNamedMetadata("opencl.ocl.version");
            if (OpenCLVersionsNode)
            {
                m_pModule->eraseNamedMetadata(OpenCLVersionsNode);
            }
        }

    private:
        llvm::Module* m_pModule;

        // data members
        KernelsList m_Kernels;
        CompilerOptionsList m_CompilerOptions;
        CompilerExternalOptionsList m_CompilerExternalOptions;
        FloatingPointContractionsList m_FloatingPointContractions;
        UsedOptionalCoreFeaturesList m_UsedOptionalCoreFeatures;
        UsedKhrExtensionsList m_UsedKhrExtensions;
        SpirVersionsList m_SpirVersions;
        OpenCLVersionsList m_OpenCLVersions;
    };
}
