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

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

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

#pragma once

#include "AdaptorCommon/ImplicitArgs.hpp"
#include "Compiler/MetaDataUtilsWrapper.h"

#include "common/LLVMWarningsPush.hpp"
#include <llvm/Pass.h>
#include <llvm/IR/InstVisitor.h>
#include "common/LLVMWarningsPop.hpp"

namespace IGC
{
    class WIFuncsAnalysis;

    /// @brief  WIFuncResolution pass used for resolving OpenCL WI (work item) functions.
    ///         This pass depends on the WIFuncAnalysis and AddImplicitArgs passes running before it

    class WIFuncResolution : public llvm::FunctionPass, public llvm::InstVisitor<WIFuncResolution>
    {
    public:
        // Pass identification, replacement for typeid
        static char ID;

        /// @brief  Constructor
        WIFuncResolution();

        /// @brief  Destructor
        ~WIFuncResolution() {}

        /// @brief  Provides name of pass
        virtual llvm::StringRef getPassName() const override
        {
            return "WIFuncResolution";
        }

        void getAnalysisUsage(llvm::AnalysisUsage& AU) const override
        {
            AU.addRequired<MetaDataUtilsWrapper>();
            AU.addRequired<CodeGenContextWrapper>();
        }

        /// @brief  Main entry point.
        ///         Finds all OpenCL WI (Work item) function calls and resolve them into an llvm sequence
        /// @param  F The destination function.
        virtual bool runOnFunction(llvm::Function& F) override;

        /// @brief  Call instructions visitor.
        ///         Checks for OpenCL WI functions and resolves them into appropriate sequence of code
        /// @param  CI The call instruction.
        void visitCallInst(llvm::CallInst& CI);

    private:

        /// @brief  Resolves get_local_id(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @param  argType The type of the appropriate implicit arg.
        /// @return A value representing the local id
        llvm::Value* getLocalId(llvm::CallInst& CI, ImplicitArg::ArgType argType);

        /// @brief  Resolves get_group_id(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the group id
        llvm::Value* getGroupId(llvm::CallInst& CI);
        /// @brief  Resolves get_local_thread_id(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the local thread id
        llvm::Value* getLocalThreadId(llvm::CallInst &CI);

        /// @brief  Resolves get_global_size(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the global size
        llvm::Value* getGlobalSize(llvm::CallInst& CI);

        /// @brief  Resolves get_local_size(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the local size
        llvm::Value* getLocalSize(llvm::CallInst& CI);

        /// @brief  Resolves get_enqueued_local_size(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the enqueued local size
        llvm::Value* getEnqueuedLocalSize(llvm::CallInst& CI);

        /// @brief  Resolves get_global_offset(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the global offset
        llvm::Value* getGlobalOffset(llvm::CallInst& CI);

        /// @brief  Resolves get_work_dim().
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the work dimension
        llvm::Value* getWorkDim(llvm::CallInst& CI);

        /// @brief  Resolves get_num_groups(dim).
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the number of work groups
        llvm::Value* getNumGroups(llvm::CallInst& CI);

        /// @brief  Returns the appropriate implicit argument of the function
        ///         containing the given call instruction, based on the given argument type
        /// @param  CI       The call instruction.
        /// @param  argType  The argument type.
        /// @return The function argument associated with the given implicit arg type
        llvm::Argument* getImplicitArg(llvm::CallInst& CI, ImplicitArg::ArgType argType);

        /// @brief  Resolves get_stage_in_grid_origin().
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the stage in grid origin
        llvm::Value* getStageInGridOrigin(llvm::CallInst& CI);

        /// @brief  Resolves get_stage_in_grid_size().
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the stage in grid size
        llvm::Value* getStageInGridSize(llvm::CallInst& CI);

        /// @brief  Resolves get_sync_buffer().
        ///         Adds the appropriate sequence of code before the given call instruction
        /// @param  CI The call instruction.
        /// @return A value representing the sync buffer
        llvm::Value* getSyncBufferPtr(llvm::CallInst& CI);

        /// @brief  get vector of work group size if reqd_work_group_size is set.
        /// @param  F the function to check
        /// @return A vector with work group size (e.g., <i32 16, i32 1, i32 1>)
        ///         or nullptr if not known.
        llvm::Constant* getKnownWorkGroupSize(
            IGCMD::MetaDataUtils* MDUtils,
            llvm::Function& F) const;

        /// @brief  The implicit arguments of the current function
        ImplicitArgs m_implicitArgs;

    private:
        /// @brief  Indicates if the pass changed the processed function
        bool m_changed;
    };

} // namespace IGC

