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

Copyright (C) 2017-2023 Intel Corporation

SPDX-License-Identifier: MIT

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

#include "MetaDataApi.h"
#include "Probe/Assertion.h"

namespace IGC::IGCMD {
    ArgInfoMetaData::ArgInfoMetaData(const llvm::MDNode* pNode, bool hasId) :
        _Mybase(pNode, hasId),
        m_ArgId(getNumberedNode(pNode, 0)),
        m_ExplicitArgNum(getNamedNode(pNode, "explicit_arg_num")),
        m_StructArgOffset(getNamedNode(pNode, "struct_arg_offset")),
        m_ImgAccessFloatCoords(getNamedNode(pNode, "img_access_float_coords")),
        m_ImgAccessIntCoords(getNamedNode(pNode, "img_access_int_coords")),
        m_pNode(pNode)
    {}

    ArgInfoMetaData::ArgInfoMetaData() :
        m_ExplicitArgNum("explicit_arg_num"),
        m_StructArgOffset("struct_arg_offset"),
        m_ImgAccessFloatCoords("img_access_float_coords"),
        m_ImgAccessIntCoords("img_access_int_coords"),
        m_pNode(nullptr)
    {}

    ArgInfoMetaData::ArgInfoMetaData(const char* name) :
        _Mybase(name),
        m_ExplicitArgNum("explicit_arg_num"),
        m_StructArgOffset("struct_arg_offset"),
        m_ImgAccessFloatCoords("img_access_float_coords"),
        m_ImgAccessIntCoords("img_access_int_coords"),
        m_pNode(nullptr)
    {}

    bool ArgInfoMetaData::hasValue() const
    {
        return m_ArgId.hasValue() ||
                m_ExplicitArgNum.hasValue() ||
                m_StructArgOffset.hasValue() ||
                m_ImgAccessFloatCoords.hasValue() ||
                m_ImgAccessIntCoords.hasValue() ||
                nullptr != m_pNode ||
                dirty();
    }

    bool ArgInfoMetaData::dirty() const
    {
        return m_ArgId.dirty() ||
                m_ExplicitArgNum.dirty() ||
                m_StructArgOffset.dirty() ||
                m_ImgAccessFloatCoords.dirty() ||
                m_ImgAccessIntCoords.dirty();
    }

    void ArgInfoMetaData::discardChanges()
    {
        m_ArgId.discardChanges();
        m_ExplicitArgNum.discardChanges();
        m_StructArgOffset.discardChanges();
        m_ImgAccessFloatCoords.discardChanges();
        m_ImgAccessIntCoords.discardChanges();
    }

    llvm::Metadata* ArgInfoMetaData::generateNode(llvm::LLVMContext& context) const
    {
        llvm::SmallVector<llvm::Metadata*, 5> args;

        llvm::Metadata* pIDNode = IMetaDataObject::generateNode(context);
        if (nullptr != pIDNode)
        {
            args.push_back(pIDNode);
        }

        args.push_back(m_ArgId.generateNode(context));
        if (isExplicitArgNumHasValue())
        {
            args.push_back(m_ExplicitArgNum.generateNode(context));
        }

        if (isStructArgOffsetHasValue())
        {
            args.push_back(m_StructArgOffset.generateNode(context));
        }

        if (isImgAccessFloatCoordsHasValue())
        {
            args.push_back(m_ImgAccessFloatCoords.generateNode(context));
        }

        if (isImgAccessIntCoordsHasValue())
        {
            args.push_back(m_ImgAccessIntCoords.generateNode(context));
        }

        return llvm::MDNode::get(context, args);
    }

    void ArgInfoMetaData::save(llvm::LLVMContext& context, llvm::MDNode* pNode) const
    {
        IGC_ASSERT_MESSAGE(nullptr != pNode, "The target node should be valid pointer");

        // we assume that underlying metadata node has not changed under our foot
        if (pNode == m_pNode && !dirty())
        {
            return;
        }

        m_ArgId.save(context, getNumberedNode(pNode, 0));
        m_ExplicitArgNum.save(context, getNamedNode(pNode, "explicit_arg_num"));
        m_StructArgOffset.save(context, getNamedNode(pNode, "struct_arg_offset"));
        m_ImgAccessFloatCoords.save(context, getNamedNode(pNode, "img_access_float_coords"));
        m_ImgAccessIntCoords.save(context, getNamedNode(pNode, "img_access_int_coords"));
    }

    ArgDependencyInfoMetaData::ArgDependencyInfoMetaData(const llvm::MDNode* pNode, bool hasId) :
        _Mybase(pNode, hasId),
        m_Arg(getNumberedNode(pNode, 0)),
        m_ArgDependency(getNumberedNode(pNode, 1)),
        m_pNode(pNode)
    {}

    ArgDependencyInfoMetaData::ArgDependencyInfoMetaData() :
        m_pNode(nullptr)
    {}

    ArgDependencyInfoMetaData::ArgDependencyInfoMetaData(const char* name) :
        _Mybase(name),
        m_pNode(nullptr)
    {}

    bool ArgDependencyInfoMetaData::hasValue() const
    {
        return m_Arg.hasValue() ||
                m_ArgDependency.hasValue() ||
                nullptr != m_pNode ||
                dirty();
    }

    bool ArgDependencyInfoMetaData::dirty() const
    {
        return m_Arg.dirty() ||
                m_ArgDependency.dirty();
    }

    void ArgDependencyInfoMetaData::discardChanges()
    {
        m_Arg.discardChanges();
        m_ArgDependency.discardChanges();
    }

    llvm::Metadata* ArgDependencyInfoMetaData::generateNode(llvm::LLVMContext& context) const
    {
        llvm::SmallVector<llvm::Metadata*, 5> args;

        llvm::Metadata* pIDNode = IMetaDataObject::generateNode(context);
        if (nullptr != pIDNode)
        {
            args.push_back(pIDNode);
        }

        args.push_back(m_Arg.generateNode(context));
        args.push_back(m_ArgDependency.generateNode(context));

        return llvm::MDNode::get(context, args);
    }

    void ArgDependencyInfoMetaData::save(llvm::LLVMContext& context, llvm::MDNode* pNode) const
    {
        IGC_ASSERT_MESSAGE(nullptr != pNode, "The target node should be valid pointer");

        // we assume that underlying metadata node has not changed under our foot
        if (pNode == m_pNode && !dirty())
        {
            return;
        }

        m_Arg.save(context, getNumberedNode(pNode, 0));
        m_ArgDependency.save(context, getNumberedNode(pNode, 1));
    }

    MaxRegPressureMetaData::MaxRegPressureMetaData(const llvm::MDNode* pNode, bool hasId) :
        _Mybase(pNode, hasId),
        m_MaxPressure(getNumberedNode(pNode, 0)),
        m_pNode(pNode)
    {}

    MaxRegPressureMetaData::MaxRegPressureMetaData() :
        m_pNode(nullptr)
    {}

    MaxRegPressureMetaData::MaxRegPressureMetaData(const char* name) :
        _Mybase(name),
        m_pNode(nullptr)
    {}

    bool MaxRegPressureMetaData::hasValue() const
    {
        return m_MaxPressure.hasValue() ||
                nullptr != m_pNode ||
                dirty();
    }

    bool MaxRegPressureMetaData::dirty() const
    {
        return m_MaxPressure.dirty();
    }

    void MaxRegPressureMetaData::discardChanges()
    {
        m_MaxPressure.discardChanges();
    }

    llvm::Metadata* MaxRegPressureMetaData::generateNode(llvm::LLVMContext& context) const
    {
        llvm::SmallVector<llvm::Metadata*, 5> args;

        llvm::Metadata* pIDNode = IMetaDataObject::generateNode(context);
        if (nullptr != pIDNode)
        {
            args.push_back(pIDNode);
        }

        args.push_back(m_MaxPressure.generateNode(context));

        return llvm::MDNode::get(context, args);
    }

    void MaxRegPressureMetaData::save(llvm::LLVMContext& context, llvm::MDNode* pNode) const
    {
        IGC_ASSERT_MESSAGE(nullptr != pNode, "The target node should be valid pointer");

        // we assume that underlying metadata node has not changed under our foot
        if (pNode == m_pNode && !dirty())
        {
            return;
        }

        m_MaxPressure.save(context, getNumberedNode(pNode, 0));
    }

    SubGroupSizeMetaData::SubGroupSizeMetaData(const llvm::MDNode* pNode, bool hasId) :
        _Mybase(pNode, hasId),
        m_SIMDSize(getNumberedNode(pNode, 0)),
        m_pNode(pNode)
    {}

    SubGroupSizeMetaData::SubGroupSizeMetaData() :
        m_pNode(nullptr)
    {}

    SubGroupSizeMetaData::SubGroupSizeMetaData(const char* name) :
        _Mybase(name),
        m_pNode(nullptr)
    {}

    bool SubGroupSizeMetaData::hasValue() const
    {
        return m_SIMDSize.hasValue() ||
                nullptr != m_pNode ||
                dirty();
    }

    bool SubGroupSizeMetaData::dirty() const
    {
        return m_SIMDSize.dirty();
    }

    void SubGroupSizeMetaData::discardChanges()
    {
        m_SIMDSize.discardChanges();
    }

    llvm::Metadata* SubGroupSizeMetaData::generateNode(llvm::LLVMContext& context) const
    {
        llvm::SmallVector<llvm::Metadata*, 5> args;

        llvm::Metadata* pIDNode = IMetaDataObject::generateNode(context);
        if (nullptr != pIDNode)
        {
            args.push_back(pIDNode);
        }

        args.push_back(m_SIMDSize.generateNode(context));

        return llvm::MDNode::get(context, args);
    }

    void SubGroupSizeMetaData::save(llvm::LLVMContext& context, llvm::MDNode* pNode) const
    {
        IGC_ASSERT_MESSAGE(nullptr != pNode, "The target node should be valid pointer");

        // we assume that underlying metadata node has not changed under our foot
        if (pNode == m_pNode && !dirty())
        {
            return;
        }

        m_SIMDSize.save(context, getNumberedNode(pNode, 0));
    }

    VectorTypeHintMetaData::VectorTypeHintMetaData(const llvm::MDNode* pNode, bool hasId) :
        _Mybase(pNode, hasId),
        m_VecType(getNumberedNode(pNode, 0)),
        m_Sign(getNumberedNode(pNode, 1)),
        m_pNode(pNode)
    {}

    VectorTypeHintMetaData::VectorTypeHintMetaData() :
        m_pNode(nullptr)
    {}

    VectorTypeHintMetaData::VectorTypeHintMetaData(const char* name) :
        _Mybase(name),
        m_pNode(nullptr)
    {}

    bool VectorTypeHintMetaData::hasValue() const
    {
        return m_VecType.hasValue() ||
                m_Sign.hasValue() ||
                nullptr != m_pNode ||
                dirty();
    }

    bool VectorTypeHintMetaData::dirty() const
    {
        return m_VecType.dirty() ||
                m_Sign.dirty();
    }

    void VectorTypeHintMetaData::discardChanges()
    {
        m_VecType.discardChanges();
        m_Sign.discardChanges();
    }

    llvm::Metadata* VectorTypeHintMetaData::generateNode(llvm::LLVMContext& context) const
    {
        llvm::SmallVector<llvm::Metadata*, 5> args;

        llvm::Metadata* pIDNode = IMetaDataObject::generateNode(context);
        if (nullptr != pIDNode)
        {
            args.push_back(pIDNode);
        }

        args.push_back(m_VecType.generateNode(context));
        args.push_back(m_Sign.generateNode(context));

        return llvm::MDNode::get(context, args);
    }

    void VectorTypeHintMetaData::save(llvm::LLVMContext& context, llvm::MDNode* pNode) const
    {
        IGC_ASSERT_MESSAGE(nullptr != pNode, "The target node should be valid pointer");

        // we assume that underlying metadata node has not changed under our foot
        if (pNode == m_pNode && !dirty())
        {
            return;
        }

        m_VecType.save(context, getNumberedNode(pNode, 0));
        m_Sign.save(context, getNumberedNode(pNode, 1));
    }

    ThreadGroupSizeMetaData::ThreadGroupSizeMetaData(const llvm::MDNode* pNode, bool hasId) :
        _Mybase(pNode, hasId),
        m_XDim(getNumberedNode(pNode, 0)),
        m_YDim(getNumberedNode(pNode, 1)),
        m_ZDim(getNumberedNode(pNode, 2)),
        m_pNode(pNode)
    {}

    ThreadGroupSizeMetaData::ThreadGroupSizeMetaData() :
        m_pNode(nullptr)
    {}

    ThreadGroupSizeMetaData::ThreadGroupSizeMetaData(const char* name) :
        _Mybase(name),
        m_pNode(nullptr)
    {}

    bool ThreadGroupSizeMetaData::hasValue() const
    {
        return m_XDim.hasValue() ||
                m_YDim.hasValue() ||
                m_ZDim.hasValue() ||
                nullptr != m_pNode ||
                dirty();
    }

    bool ThreadGroupSizeMetaData::dirty() const
    {
        return m_XDim.dirty() ||
                m_YDim.dirty() ||
                m_ZDim.dirty();
    }

    void ThreadGroupSizeMetaData::discardChanges()
    {
        m_XDim.discardChanges();
        m_YDim.discardChanges();
        m_ZDim.discardChanges();
    }

    llvm::Metadata* ThreadGroupSizeMetaData::generateNode(llvm::LLVMContext& context) const
    {
        llvm::SmallVector<llvm::Metadata*, 5> args;

        llvm::Metadata* pIDNode = IMetaDataObject::generateNode(context);
        if (nullptr != pIDNode)
        {
            args.push_back(pIDNode);
        }

        args.push_back(m_XDim.generateNode(context));
        args.push_back(m_YDim.generateNode(context));
        args.push_back(m_ZDim.generateNode(context));

        return llvm::MDNode::get(context, args);
    }

    void ThreadGroupSizeMetaData::save(llvm::LLVMContext& context, llvm::MDNode* pNode) const
    {
        IGC_ASSERT_MESSAGE(nullptr != pNode, "The target node should be valid pointer");

        // we assume that underlying metadata node has not changed under our foot
        if (pNode == m_pNode && !dirty())
        {
            return;
        }

        m_XDim.save(context, getNumberedNode(pNode, 0));
        m_YDim.save(context, getNumberedNode(pNode, 1));
        m_ZDim.save(context, getNumberedNode(pNode, 2));
    }

    FunctionInfoMetaData::FunctionInfoMetaData(const llvm::MDNode* pNode, bool hasId) :
        _Mybase(pNode, hasId),
        m_Type(getNamedNode(pNode, "function_type")),
        m_ArgInfoList(getNamedNode(pNode, "arg_desc"), true),
        m_ImplicitArgInfoList(getNamedNode(pNode, "implicit_arg_desc"), true),
        m_ThreadGroupSize(new ThreadGroupSizeMetaData(getNamedNode(pNode, "thread_group_size"), true)),
        m_ThreadGroupSizeHint(new ThreadGroupSizeMetaData(getNamedNode(pNode, "thread_group_size_hint"), true)),
        m_SubGroupSize(new SubGroupSizeMetaData(getNamedNode(pNode, "sub_group_size"), true)),
        m_MaxRegPressure(new MaxRegPressureMetaData(getNamedNode(pNode, "max_reg_pressure"), true)),
        m_OpenCLVectorTypeHint(new VectorTypeHintMetaData(getNamedNode(pNode, "opencl_vec_type_hint"), true)),
        m_pNode(pNode)
    {}

    FunctionInfoMetaData::FunctionInfoMetaData() :
        m_Type("function_type"),
        m_ArgInfoList("arg_desc"),
        m_ImplicitArgInfoList("implicit_arg_desc"),
        m_ThreadGroupSize(new ThreadGroupSizeMetaDataHandle::ObjectType("thread_group_size")),
        m_ThreadGroupSizeHint(new ThreadGroupSizeMetaDataHandle::ObjectType("thread_group_size_hint")),
        m_SubGroupSize(new SubGroupSizeMetaDataHandle::ObjectType("sub_group_size")),
        m_MaxRegPressure(new MaxRegPressureMetaDataHandle::ObjectType("max_reg_pressure")),
        m_OpenCLVectorTypeHint(new VectorTypeHintMetaDataHandle::ObjectType("opencl_vec_type_hint")),
        m_pNode(nullptr)
    {}

    FunctionInfoMetaData::FunctionInfoMetaData(const char* name) :
        _Mybase(name), m_Type("function_type"),
        m_ArgInfoList("arg_desc"),
        m_ImplicitArgInfoList("implicit_arg_desc"),
        m_ThreadGroupSize(new ThreadGroupSizeMetaDataHandle::ObjectType("thread_group_size")),
        m_ThreadGroupSizeHint(new ThreadGroupSizeMetaDataHandle::ObjectType("thread_group_size_hint")),
        m_SubGroupSize(new SubGroupSizeMetaDataHandle::ObjectType("sub_group_size")),
        m_MaxRegPressure(new MaxRegPressureMetaDataHandle::ObjectType("max_reg_pressure")),
        m_OpenCLVectorTypeHint(new VectorTypeHintMetaDataHandle::ObjectType("opencl_vec_type_hint")),
        m_pNode(nullptr)
    {}

    bool FunctionInfoMetaData::hasValue() const
    {
        return m_Type.hasValue() ||
                m_ArgInfoList.hasValue() ||
                m_ImplicitArgInfoList.hasValue() ||
                m_ThreadGroupSize->hasValue() ||
                m_ThreadGroupSizeHint->hasValue() ||
                m_SubGroupSize->hasValue() ||
                m_MaxRegPressure->hasValue() ||
                m_OpenCLVectorTypeHint->hasValue() ||
                nullptr != m_pNode ||
                dirty();
    }

    bool FunctionInfoMetaData::dirty() const
    {
        return m_Type.dirty() ||
                m_ArgInfoList.dirty() ||
                m_ImplicitArgInfoList.dirty() ||
                m_ThreadGroupSize.dirty() ||
                m_ThreadGroupSizeHint.dirty() ||
                m_SubGroupSize.dirty() ||
                m_MaxRegPressure.dirty() ||
                m_OpenCLVectorTypeHint.dirty();
    }

    void FunctionInfoMetaData::discardChanges()
    {
        m_Type.discardChanges();
        m_ArgInfoList.discardChanges();
        m_ImplicitArgInfoList.discardChanges();
        m_ThreadGroupSize.discardChanges();
        m_ThreadGroupSizeHint.discardChanges();
        m_SubGroupSize.discardChanges();
        m_MaxRegPressure.discardChanges();
        m_OpenCLVectorTypeHint.discardChanges();
    }

    llvm::Metadata* FunctionInfoMetaData::generateNode(llvm::LLVMContext& context) const
    {
        llvm::SmallVector<llvm::Metadata*, 5> args;

        llvm::Metadata* pIDNode = IMetaDataObject::generateNode(context);
        if (nullptr != pIDNode)
        {
            args.push_back(pIDNode);
        }

        args.push_back(m_Type.generateNode(context));
        if (isArgInfoListHasValue())
        {
            args.push_back(m_ArgInfoList.generateNode(context));
        }
        if (isImplicitArgInfoListHasValue())
        {
            args.push_back(m_ImplicitArgInfoList.generateNode(context));
        }
        if (m_ThreadGroupSize->hasValue())
        {
            args.push_back(m_ThreadGroupSize.generateNode(context));
        }
        if (m_ThreadGroupSizeHint->hasValue())
        {
            args.push_back(m_ThreadGroupSizeHint.generateNode(context));
        }
        if (m_SubGroupSize->hasValue())
        {
            args.push_back(m_SubGroupSize.generateNode(context));
        }
        if (m_MaxRegPressure->hasValue())
        {
            args.push_back(m_MaxRegPressure.generateNode(context));
        }
        if (m_OpenCLVectorTypeHint->hasValue())
        {
            args.push_back(m_OpenCLVectorTypeHint.generateNode(context));
        }
        return llvm::MDNode::get(context, args);
    }

    void FunctionInfoMetaData::save(llvm::LLVMContext& context, llvm::MDNode* pNode) const
    {
        IGC_ASSERT_MESSAGE(nullptr != pNode, "The target node should be valid pointer");

        // we assume that underlying metadata node has not changed under our foot
        if (pNode == m_pNode && !dirty())
        {
            return;
        }

        m_Type.save(context, getNamedNode(pNode, "function_type"));
        m_ArgInfoList.save(context, getNamedNode(pNode, "arg_desc"));
        m_ImplicitArgInfoList.save(context, getNamedNode(pNode, "implicit_arg_desc"));
        m_ThreadGroupSize.save(context, getNamedNode(pNode, "thread_group_size"));
        m_ThreadGroupSizeHint.save(context, getNamedNode(pNode, "thread_group_size_hint"));
        m_SubGroupSize.save(context, getNamedNode(pNode, "sub_group_size"));
        m_MaxRegPressure.save(context, getNamedNode(pNode, "max_reg_pressure"));
        m_OpenCLVectorTypeHint.save(context, getNamedNode(pNode, "opencl_vec_type_hint"));
    }
}
