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

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

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

#include "AddImplicitArgs.hpp"
#include "AdaptorCommon/ImplicitArgs.hpp"
#include "Compiler/IGCPassSupport.h"
#include "Compiler/CISACodeGen/CISACodeGen.h"
#include "Compiler/DebugInfo/ScalarVISAModule.h"
#include "Compiler/Optimizer/OCLBIUtils.h"
#include "LLVM3DBuilder/MetadataBuilder.h"
#include "common/LLVMWarningsPush.hpp"
#include "llvm/ADT/SCCIterator.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/Function.h>
#include <llvmWrapper/ADT/STLExtras.h>
#include <llvmWrapper/IR/Instructions.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/DerivedTypes.h>
#include "llvm/IR/DIBuilder.h"
#include <llvm/ADT/DepthFirstIterator.h>
#include "common/LLVMWarningsPop.hpp"
#include "common/debug/Debug.hpp"
#include "DebugInfo/VISADebugEmitter.hpp"
#include <map>
#include <utility>
#include "Probe/Assertion.h"

using namespace llvm;
using namespace IGC;
using namespace IGC::IGCMD;

// Register pass to igc-opt
#define PASS_FLAG "igc-add-implicit-args"
#define PASS_DESCRIPTION "Add implicit args to all functions in the module and adjusts call to these functions"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
IGC_INITIALIZE_PASS_BEGIN(AddImplicitArgs, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
IGC_INITIALIZE_PASS_DEPENDENCY(CodeGenContextWrapper)
IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
IGC_INITIALIZE_PASS_END(AddImplicitArgs, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)

struct ImplicitStructArgument
{
    union
    {
        struct
        {
            unsigned int argId : 11;
            unsigned int argOffset : 11;
            unsigned int argExplicitNum : 10;
        } All;
        unsigned int Value;
    } DW0;
};

char AddImplicitArgs::ID = 0;

AddImplicitArgs::AddImplicitArgs() : ModulePass(ID)
{
    initializeAddImplicitArgsPass(*PassRegistry::getPassRegistry());
}

bool AddImplicitArgs::runOnModule(Module &M)
{
    MapList<Function*, Function*> funcsMapping;
    MapList<Function*, Function*> funcsMappingForReplacement;
    m_pMdUtils = getAnalysis<MetaDataUtilsWrapper>().getMetaDataUtils();
    CodeGenContext* ctx = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();

    // Update function signatures
    // Create new functions with implicit args
    for (Function& func : M)
    {
        // Only handle functions defined in this module
        if (func.isDeclaration()) continue;
        // skip non-entry functions
        if (m_pMdUtils->findFunctionsInfoItem(&func) == m_pMdUtils->end_FunctionsInfo()) continue;
        // Skip functions called from function marked with IndirectlyCalled attribute
        // TODO: This function verifies only parent caller, in the future we may need to
        //       also verify all the callers up to the tree.
        if (hasIndirectlyCalledParent(&func)) continue;
        // No implicit arg support for stack calls
        if (func.hasFnAttribute("visaStackCall")) continue;

        // see the detail in StatelessToStatefull.cpp.
        // If SToSProducesPositivePointer is true, do not generate implicit arguments.
        if (IGC_IS_FLAG_DISABLED(SToSProducesPositivePointer) &&
            (ctx->getModuleMetaData()->compOpt.HasBufferOffsetArg ||
             IGC_IS_FLAG_ENABLED(EnableSupportBufferOffset)))
        {
            ImplicitArgs::addBufferOffsetArgs(func, m_pMdUtils, ctx->getModuleMetaData());
        }

        if (ctx->getModuleMetaData()->compOpt.UseBindlessMode &&
            !ctx->getModuleMetaData()->compOpt.UseLegacyBindlessMode)
        {
            ImplicitArgs::addBindlessOffsetArgs(func, m_pMdUtils, ctx->getModuleMetaData());
        }

        ImplicitArgs implicitArgs(func, m_pMdUtils);

        // Create the new function body and insert it into the module
        FunctionType *pNewFTy = getNewFuncType(&func, implicitArgs);
        Function* pNewFunc = Function::Create(pNewFTy, func.getLinkage());
        pNewFunc->copyAttributesFrom(&func);
        pNewFunc->setSubprogram(func.getSubprogram());
        M.getFunctionList().insert(func.getIterator(), pNewFunc);
        pNewFunc->takeName(&func);

        // Since we have now created the new function, splice the body of the old
        // function right into the new function, leaving the old body of the function empty.
        pNewFunc->getBasicBlockList().splice(pNewFunc->begin(), func.getBasicBlockList());

        // Loop over the argument list, transferring uses of the old arguments over to
        // the new arguments
        updateNewFuncArgs(&func, pNewFunc, implicitArgs);

        // Map old func to new func
        funcsMapping[&func] = pNewFunc;

        if (!func.use_empty())
        {
            funcsMappingForReplacement[&func] = pNewFunc;
        }
    }

    if (IGC_GET_FLAG_VALUE(FunctionControl) != FLAG_FCALL_FORCE_INLINE)
    {
        for (auto I : funcsMappingForReplacement)
        {
            replaceAllUsesWithNewOCLBuiltinFunction(I.first, I.second);
        }
    }

    // Update IGC Metadata
    // Function declarations are changing, this needs to be reflected in the metadata.
    MetadataBuilder mbuilder(&M);
    auto &FuncMD = ctx->getModuleMetaData()->FuncMD;
    for (auto i : funcsMapping)
    {
        auto oldFuncIter = m_pMdUtils->findFunctionsInfoItem(i.first);
        m_pMdUtils->setFunctionsInfoItem(i.second, oldFuncIter->second);
        m_pMdUtils->eraseFunctionsInfoItem(oldFuncIter);
        mbuilder.UpdateShadingRate(i.first, i.second);
        auto loc = FuncMD.find(i.first);
        if (loc != FuncMD.end())
        {
            auto funcInfo = loc->second;
            FuncMD.erase(i.first);
            FuncMD[i.second] = funcInfo;
        }
    }

    // Update LLVM metadata based on IGC MetadataUtils
    m_pMdUtils->save(M.getContext());

    // Return if any error
    if (getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->HasError())
    {
        return false;
    }
    // Go over all changed functions
    for (const auto& I : funcsMapping)
    {
        Function* pFunc = I.first;

        IGC_ASSERT(nullptr != pFunc);
        IGC_ASSERT_MESSAGE(pFunc->use_empty(), "Assume all user function are inlined at this point");

        // Now, after changing function signature
        // and validating there are no calls to the old function, we can erase it.
        pFunc->eraseFromParent();
    }

    return true;
}

bool AddImplicitArgs::hasIndirectlyCalledParent(const Function* F)
{
    for (auto u = F->user_begin(), e = F->user_end(); u != e; u++)
    {
        if (const CallInst* call = dyn_cast<CallInst>(*u))
        {
            const Function* parent = call->getParent()->getParent();
            if (parent->hasFnAttribute("referenced-indirectly"))
                return true;
        }
    }
    return false;
}

FunctionType* AddImplicitArgs::getNewFuncType(const Function* pFunc, const ImplicitArgs& implicitArgs)
{
    // Add all explicit parameters
    const FunctionType* pFuncType = pFunc->getFunctionType();
    std::vector<Type *> newParamTypes(pFuncType->param_begin(), pFuncType->param_end());

    // Add implicit arguments parameter types
    for (unsigned int i = 0; i < implicitArgs.size(); ++i)
    {
        newParamTypes.push_back(implicitArgs[i].getLLVMType(pFunc->getContext()));
    }

    // Create new function type with explicit and implicit parameter types
    return FunctionType::get(pFunc->getReturnType(), newParamTypes, pFunc->isVarArg());
}

void AddImplicitArgs::updateNewFuncArgs(llvm::Function* pFunc, llvm::Function* pNewFunc, const ImplicitArgs& implicitArgs)
{
    // Loop over the argument list, transferring uses of the old arguments over to
    // the new arguments, also transferring over the names as well.
    std::vector<std::pair<llvm::Instruction*, unsigned int>> newAddr;
    bool fullDebugInfo = false;
    IF_DEBUG_INFO(bool lineNumbersOnly = false;)
    IF_DEBUG_INFO(CodeGenContext* ctx = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();)
    IF_DEBUG_INFO(DebugMetadataInfo::hasAnyDebugInfo(ctx, fullDebugInfo, lineNumbersOnly);)

    if (fullDebugInfo)
    {
        // Create a map storing function arguments of pFunc with their position
        // of occurrence.
        std::map<const llvm::Argument*, unsigned int> argMap;
        unsigned int i = 0;
        for (auto arg = pFunc->arg_begin(); arg != pFunc->arg_end(); ++arg, ++i)
        {
            argMap.insert(std::make_pair(&(*arg), i));
        }

        // Iterate over each dbg.declare intrinsic call. If the address operand
        // matches with any argument from old function, pFunc, store it in a
        // data structure so we can fix it later.
        for (auto& bb : *pNewFunc)
        {
            for (auto& inst : bb)
            {
                auto DIInst = dyn_cast_or_null<DbgDeclareInst>(&inst);
                if (DIInst)
                {
                    auto addr = dyn_cast_or_null<Argument>(DIInst->getAddress());
                    if (addr)
                    {
                        auto it = argMap.find(addr);
                        if (it != argMap.end())
                        {
                            newAddr.push_back(std::make_pair(DIInst, it->second));
                        }
                    }
                }
            }
        }
    }

    Function::arg_iterator currArg = pNewFunc->arg_begin();
    for (Function::arg_iterator I = pFunc->arg_begin(), E = pFunc->arg_end(); I != E; ++I, ++currArg)
    {
        llvm::Value* newArg = coerce(&(*currArg), I->getType(), &pNewFunc->getEntryBlock().front());
        // Move the name and users over to the new version.
        I->replaceAllUsesWith(newArg);
        currArg->takeName(&(*I));
    }

    // In the following loop, fix dbg.declare nodes that reference function arguments.
    // This occurs for example when a struct type is passed as a kernel parameter
    // byval. Bug#GD-429 had this exact issue. If we don't do this then we lose
    // mapping of argument to dbg.declare and elf file comes up with empty
    // storage location for the variable.
    for (auto toReplace : newAddr)
    {
        IF_DEBUG_INFO(auto d = dyn_cast<DbgDeclareInst>(toReplace.first);)

        IF_DEBUG_INFO(llvm::DIBuilder Builder(*pNewFunc->getParent()));
        IF_DEBUG_INFO(auto DIVar = d->getVariable();)
        IF_DEBUG_INFO(auto DIExpr = d->getExpression();)

        IGC_ASSERT(toReplace.second < pNewFunc->arg_size());
        IF_DEBUG_INFO(Value* v = pNewFunc->arg_begin() + toReplace.second);
        IF_DEBUG_INFO(Builder.insertDeclare(v, DIVar, DIExpr, d->getDebugLoc().get(), d);)
        IF_DEBUG_INFO(d->eraseFromParent();)
    }

    // Set implicit argument names
    InfoToImpArgMap &infoToArg = m_FuncInfoToImpArgMap[pNewFunc];
    ImpArgToExpNum &argImpToExpNum = m_FuncImpToExpNumMap[pNewFunc];
    for (unsigned int i = 0; i < implicitArgs.size(); ++i, ++currArg)
    {
        currArg->setName(implicitArgs[i].getName());

        ImplicitArg::ArgType argId = implicitArgs[i].getArgType();
        ImplicitStructArgument info;
        info.DW0.All.argId = argId;
        info.DW0.All.argExplicitNum = 0;
        info.DW0.All.argOffset = 0;
        if ((argId >= ImplicitArg::ArgType::STRUCT_START && argId <= ImplicitArg::IMAGES_END)
            || argId == ImplicitArg::ArgType::GET_OBJECT_ID || argId == ImplicitArg::ArgType::GET_BLOCK_SIMD_SIZE)
        {
            // struct, image
            FunctionInfoMetaDataHandle funcInfo = m_pMdUtils->getFunctionsInfoItem(pFunc);
            ArgInfoMetaDataHandle argInfo = funcInfo->getImplicitArgInfoListItem(i);
            IGC_ASSERT_MESSAGE(argInfo->isExplicitArgNumHasValue(), "wrong data in MetaData");

            argImpToExpNum[&(*currArg)] = info.DW0.All.argExplicitNum = argInfo->getExplicitArgNum();
            if (argId <= ImplicitArg::ArgType::STRUCT_END)
            {
                IGC_ASSERT_MESSAGE(argInfo->isStructArgOffsetHasValue(), "wrong data in MetaData");
                info.DW0.All.argOffset = argInfo->getStructArgOffset();
            }
        }
        infoToArg[info.DW0.Value] = &(*currArg);
    }
}

void AddImplicitArgs::replaceAllUsesWithNewOCLBuiltinFunction(llvm::Function* old_func, llvm::Function* new_func)
{
    IGC_ASSERT(!old_func->use_empty());

    FunctionInfoMetaDataHandle subFuncInfo = m_pMdUtils->getFunctionsInfoItem(old_func);

    std::vector<Instruction*> list_delete;
    old_func->removeDeadConstantUsers();
    std::vector<Value*> functionUserList(old_func->user_begin(), old_func->user_end());
    for (auto U : functionUserList)
    {
        CallInst *cInst = dyn_cast<CallInst>(U);
        auto BC = dyn_cast<BitCastInst>(U);
        if (BC && BC->hasOneUse())
            cInst = dyn_cast<CallInst>(BC->user_back());

        if (IGC_IS_FLAG_ENABLED(EnableFunctionPointer))
        {
            if (!cInst || IGCLLVM::getCalledValue(cInst) != old_func)
            {
                // Support indirect function pointer usages
                if (Instruction* userInst = dyn_cast<Instruction>(U))
                {
                    IRBuilder<> builder(userInst);
                    Value* fncast = builder.CreateBitCast(new_func, old_func->getType());
                    userInst->replaceUsesOfWith(old_func, fncast);
                    continue;
                } else if (ConstantExpr *OldExpr = dyn_cast<ConstantExpr>(U)) {
                    Constant* fncast = ConstantExpr::getBitCast(new_func, old_func->getType());
                    SmallVector<Constant*, 8> NewOps;
                    for (auto Op : OldExpr->operand_values()) {
                        NewOps.push_back(Op == old_func ? fncast : cast<Constant>(Op));
                    }
                    auto NewExpr = OldExpr->getWithOperands(NewOps);
                    OldExpr->replaceAllUsesWith(NewExpr);
                    OldExpr->destroyConstant();
                    continue;
                }
            }
        }

        if (!cInst)
        {
            IGC_ASSERT_MESSAGE(0, "Unknown function usage");
            getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->EmitError(" undefined reference to `jmp()' ", U);
            return;
        }
        // Return if any error
        if (getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->HasError())
        {
            return;
        }

        std::vector<Value*> new_args;
        Function *parent_func = cInst->getParent()->getParent();
        size_t numArgOperands = cInst->getNumArgOperands();

        // let's prepare argument list on new call function
        llvm::Function::arg_iterator new_arg_iter = new_func->arg_begin();
        llvm::Function::arg_iterator new_arg_end = new_func->arg_end();

        IGC_ASSERT(new_func->arg_size() >= numArgOperands);

        // basic arguments
        for (unsigned int i = 0; i < numArgOperands; ++i, ++new_arg_iter)
        {
            llvm::Value* arg = coerce(cInst->getOperand(i), new_arg_iter->getType(), cInst);
            new_args.push_back(arg);
        }

        // implicit arguments
        int cImpCount = 0;
        InfoToImpArgMap &infoToArg = m_FuncInfoToImpArgMap[parent_func];
        ImpArgToExpNum &argImpToExpNum = m_FuncImpToExpNumMap[new_func];
        while (new_arg_iter != new_arg_end)
        {
            ArgInfoMetaDataHandle argInfo = subFuncInfo->getImplicitArgInfoListItem(cImpCount);
            ImplicitArg::ArgType argId = static_cast<ImplicitArg::ArgType>(argInfo->getArgId());

            ImplicitStructArgument info;
            info.DW0.All.argId = argId;
            info.DW0.All.argExplicitNum = 0;
            info.DW0.All.argOffset = 0;

            if (argId < ImplicitArg::ArgType::STRUCT_START)
            {
                IGC_ASSERT_MESSAGE(infoToArg.find(info.DW0.Value) != infoToArg.end(), "Can't find the implicit argument on parent function");
                new_args.push_back(infoToArg[info.DW0.Value]);
            }
            else if (argId <= ImplicitArg::ArgType::STRUCT_END)
            {
                IGC_ASSERT_MESSAGE(0, "wrong argument type in user function");
            }
            else if (argId <= ImplicitArg::IMAGES_END || argId == ImplicitArg::ArgType::GET_OBJECT_ID || argId == ImplicitArg::ArgType::GET_BLOCK_SIMD_SIZE)
            {
                // special handling for image info types, such as ImageWidth, ImageHeight, ...
                // and struct type

                IGC_ASSERT_MESSAGE(argImpToExpNum.find(&(*new_arg_iter)) != argImpToExpNum.end(), "Can't find explicit argument number");

                // tracing it on parent function argument list
                Value* callArg = ValueTracker::track(cInst, argImpToExpNum[&(*new_arg_iter)]);
                Argument* arg = dyn_cast<Argument>(callArg);

                IGC_ASSERT_MESSAGE(arg, "Not supported");

                // build info

                info.DW0.All.argExplicitNum = arg->getArgNo();
                IGC_ASSERT_MESSAGE(infoToArg.find(info.DW0.Value) != infoToArg.end(), "Can't find the implicit argument on parent function");

                new_args.push_back(infoToArg[info.DW0.Value]);
            }
            else
            {
                IGC_ASSERT_MESSAGE(infoToArg.find(info.DW0.Value) != infoToArg.end(), "Can't find the implicit argument on parent function");
                new_args.push_back(infoToArg[info.DW0.Value]);
            }
            ++new_arg_iter;
            ++cImpCount;
        }

        // insert new call instruction before old one
        llvm::CallInst *inst;
        if (new_func->getReturnType()->isVoidTy())
        {
            inst = CallInst::Create(new_func, new_args, "", cInst);
        }
        else
        {
            inst = CallInst::Create(new_func, new_args, new_func->getName(), cInst);
        }
        inst->setCallingConv(new_func->getCallingConv());
        inst->setDebugLoc(cInst->getDebugLoc());
        cInst->replaceAllUsesWith(inst);
        list_delete.push_back(cInst);
    }

    for (auto i : list_delete)
    {
        i->eraseFromParent();
    }
}

llvm::Value* AddImplicitArgs::coerce(llvm::Value* arg, llvm::Type* type, llvm::Instruction* insertBefore)
{
    if (arg->getType() == type)
        return arg;

    // fix opaque type mismatch on %opencl...
    std::string str0;
    llvm::raw_string_ostream s(str0);
    arg->getType()->print(s);
    return new llvm::BitCastInst(arg, type, "", insertBefore);
}

// Builtin CallGraph Analysis
#define PASS_FLAG2 "igc-callgraphscc-analysis"
#define PASS_DESCRIPTION2 "Analyzes CallGraphSCC"
#define PASS_CFG_ONLY2 false
#define PASS_ANALYSIS2 false
IGC_INITIALIZE_PASS_BEGIN(BuiltinCallGraphAnalysis, PASS_FLAG2, PASS_DESCRIPTION2, PASS_CFG_ONLY2, PASS_ANALYSIS2)
IGC_INITIALIZE_PASS_DEPENDENCY(CodeGenContextWrapper)
IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
IGC_INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)
IGC_INITIALIZE_PASS_END(BuiltinCallGraphAnalysis, PASS_FLAG2, PASS_DESCRIPTION2, PASS_CFG_ONLY2, PASS_ANALYSIS2)

char BuiltinCallGraphAnalysis::ID = 0;

BuiltinCallGraphAnalysis::BuiltinCallGraphAnalysis() : ModulePass(ID)
{
    initializeBuiltinCallGraphAnalysisPass(*PassRegistry::getPassRegistry());
}

bool BuiltinCallGraphAnalysis::runOnModule(Module &M)
{
    m_pMdUtils = getAnalysis<MetaDataUtilsWrapper>().getMetaDataUtils();
    CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();

    if (IGC_GET_FLAG_VALUE(FunctionControl) == FLAG_FCALL_FORCE_INLINE)
    {
        return false;
    }

    // Traverse each SCC
    for (auto I = scc_begin(&CG), IE = scc_end(&CG); I != IE; ++I)
    {
        traverseCallGraphSCC(*I);
    }
    m_pMdUtils->save(M.getContext());

    // Detect stack calls that use implicit args, and force inline them, since they are not supported
    if (IGC_IS_FLAG_ENABLED(ForceInlineStackCallWithImplArg))
    {
        pruneCallGraphForStackCalls(CG);
    }

    return false;
}

// Do a DFS in the call graph, and for any stack-call func that has implicit args,
// force inline on it and all callers along the DFS path.
bool BuiltinCallGraphAnalysis::pruneCallGraphForStackCalls(CallGraph& CG)
{
    bool changed = false;
    llvm::SmallSet<Function*, 16> PrunedFuncs;
    for (auto IT = df_begin(&CG), EI = df_end(&CG); IT != EI; IT++)
    {
        Function* F = IT->getFunction();
        if (F && !F->isDeclaration() && F->hasFnAttribute("visaStackCall"))
        {
            FunctionInfoMetaDataHandle funcInfo = m_pMdUtils->getFunctionsInfoItem(F);
            if (!funcInfo->empty_ImplicitArgInfoList())
            {
                for (unsigned i = 0; i < IT.getPathLength(); i++)
                {
                    Function* pFuncOnPath = IT.getPath(i)->getFunction();
                    if (pFuncOnPath)
                    {
                        if (pFuncOnPath->hasFnAttribute("forceRecurse"))
                        {
                            IGC_ASSERT_MESSAGE(0, "Cannot inline for recursion!");
                            return false;
                        }
                        PrunedFuncs.insert(pFuncOnPath);
                        changed = true;
                    }
                }
            }
        }
    }
    for (auto pF : PrunedFuncs)
    {
        pF->removeFnAttr("visaStackCall");
        pF->removeFnAttr(llvm::Attribute::NoInline);
        pF->addFnAttr(llvm::Attribute::AlwaysInline);
    }
    return changed;
}

void BuiltinCallGraphAnalysis::traverseCallGraphSCC(const std::vector<CallGraphNode *> &SCCNodes)
{
    // all functions in one scc should end up with the same result
    ImplicitArgumentDetail *argData = nullptr;
    for (auto CGN : SCCNodes)
    {
        Function *f = CGN->getFunction();
        if (!f || f->isDeclaration())
            continue;
        // Fail on variadic functions.
        if (f->isVarArg())
        {
            std::string Msg = "Invalid user defined function being processed: ";
            Msg += f->getName();
            Msg += "()\n";
            getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->EmitError(Msg.c_str(), f);
            return;
        }
        if (argData == nullptr)
        {
            argDetails.push_back(IGCLLVM::make_unique<ImplicitArgumentDetail>());
            argData = argDetails[argDetails.size() - 1].get();
        }

        // calculate args from sub-routine.
        // This function have not been processed yet, therefore no map-entry for it yet
        IGC_ASSERT(argMap.count(f) == 0);
        for (auto N : (*CGN))
        {
            Function *sub = N.second->getFunction();
            // if callee has not been visited
            auto argMapIter = argMap.find(sub);
            // if we have processed the arguments for callee
            if (argMapIter != argMap.end())
            {
                IGC_ASSERT(nullptr != argMapIter->second);
                combineTwoArgDetail(*argData, *(argMapIter->second),
#if LLVM_VERSION_MAJOR <= 10
                    N.first
#else
                    *N.first
#endif
                    );
            }
        }
    }

    for (auto CGN : SCCNodes)
    {
        Function *f = CGN->getFunction();
        if (!f || f->isDeclaration())
            continue;
        FunctionInfoMetaDataHandle funcInfo = m_pMdUtils->getFunctionsInfoItem(f);
        // calculate implicit args from function metadata

        // build everything
        for (auto I = funcInfo->begin_ImplicitArgInfoList(), E = funcInfo->end_ImplicitArgInfoList(); I != E; I++)
        {
            ArgInfoMetaDataHandle argInfo = *I;
            ImplicitArg::ArgType argId = static_cast<ImplicitArg::ArgType>(argInfo->getArgId());

            if (argId < ImplicitArg::ArgType::STRUCT_START)
            {
                // unique implicit argument
                // if doesn't exist, the following line will add one.
                ImplicitArg::ArgValSet &setx = argData->ArgsMaps[argId];
                setx.insert(0);
            }
            else if (argId <= ImplicitArg::ArgType::STRUCT_END)
            {
                // aggregate implicity argument

                ImplicitStructArgument info;
                info.DW0.All.argExplicitNum = argInfo->getExplicitArgNum();
                info.DW0.All.argOffset = argInfo->getStructArgOffset();
                info.DW0.All.argId = argId;
                argData->StructArgSet.insert(info.DW0.Value);
            }
            else if (argId <= ImplicitArg::ArgType::IMAGES_END || argId == ImplicitArg::ArgType::GET_OBJECT_ID || argId == ImplicitArg::ArgType::GET_BLOCK_SIMD_SIZE)
            {
                // image index, project id

                int argNum = argInfo->getExplicitArgNum();
                ImplicitArg::ArgValSet &setx = argData->ArgsMaps[argId];
                setx.insert(argNum);
            }
            else
            {
                // unique implicit argument
                // if doesn't exist, the following line will add one.
                ImplicitArg::ArgValSet &setx = argData->ArgsMaps[argId];
                setx.insert(0);
            }
        }
    }
    for (auto CGN : SCCNodes)
    {
        Function *f = CGN->getFunction();
        if (!f || f->isDeclaration())
            continue;
        // write everything back into metaData
        writeBackAllIntoMetaData(*argData, f);
        argMap.insert(std::make_pair(f, argData));
    }
}

void BuiltinCallGraphAnalysis::combineTwoArgDetail(
    ImplicitArgumentDetail &retD,
    const ImplicitArgumentDetail &argD,
    llvm::Value* v) const
{
    for (const auto& argPair : argD.ArgsMaps)
    {
        ImplicitArg::ArgType argId = argPair.first;
        if (argId < ImplicitArg::ArgType::STRUCT_START)
        {
            // unique implicit argument
            // if doesn't exist, the following line will add one.
            ImplicitArg::ArgValSet &setx = retD.ArgsMaps[argId];
            setx.insert(0);
        }
        else if (argId <= ImplicitArg::ArgType::STRUCT_END)
        {
            // aggregate implicit argument
            IGC_ASSERT_MESSAGE(0, "wrong location for this kind of argument type");

        }
        else if (argId <= ImplicitArg::ArgType::IMAGES_END || argId == ImplicitArg::ArgType::GET_OBJECT_ID || argId == ImplicitArg::ArgType::GET_BLOCK_SIMD_SIZE)
        {
            // image index

            CallInst *cInst = dyn_cast<CallInst>(v);
            if (!cInst)
            {
                IGC_ASSERT_MESSAGE(0, " Not supported");
                getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->EmitError(" undefined reference to `jmp()' ", v);
                return;
            }

            ImplicitArg::ArgValSet &setx = retD.ArgsMaps[argId];

            // loop all image arguments on the sub-funtion.
            for (const auto& argI : argPair.second)
            {
                // find it from calling instruction, and trace it back to parent function argument
                Value* callArg = ValueTracker::track(cInst, argI);
                const Argument* arg = dyn_cast<Argument>(callArg);
                IGC_ASSERT_MESSAGE(nullptr != arg, "Not supported");
                setx.insert(arg->getArgNo());
            }
        }
        else
        {
            // unique implicit argument
            // if doesn't exist, the following line will add one.
            ImplicitArg::ArgValSet &setx = retD.ArgsMaps[argId];
            setx.insert(0);
        }
    }

    IGC_ASSERT_MESSAGE(0 == argD.StructArgSet.size(), "wrong argument type in user function");
}

void BuiltinCallGraphAnalysis::writeBackAllIntoMetaData(const ImplicitArgumentDetail& data, Function * f)
{
    if (f->hasFnAttribute("referenced-indirectly"))
        return;

    FunctionInfoMetaDataHandle funcInfo = m_pMdUtils->getFunctionsInfoItem(f);
    funcInfo->clearImplicitArgInfoList();

    for (const auto& A : data.ArgsMaps)
    {
        ImplicitArg::ArgType argId = A.first;
        if (argId < ImplicitArg::ArgType::STRUCT_START)
        {
            // unique implicit argument, add it on metadata

            ArgInfoMetaDataHandle argMD = ArgInfoMetaDataHandle(ArgInfoMetaData::get());
            argMD->setArgId(argId);
            funcInfo->addImplicitArgInfoListItem(argMD);
        }
        else if (argId <= ImplicitArg::ArgType::STRUCT_END)
        {
            // aggregate implicity argument

            IGC_ASSERT_MESSAGE(0, "wrong location for this kind of argument type");

        }
        else if (argId <= ImplicitArg::ArgType::IMAGES_END || argId == ImplicitArg::ArgType::GET_OBJECT_ID || argId == ImplicitArg::ArgType::GET_BLOCK_SIMD_SIZE)
        {
            // image index

            for (const auto& vOnSet: A.second)
            {
                ArgInfoMetaDataHandle argMD = ArgInfoMetaDataHandle(ArgInfoMetaData::get());
                argMD->setArgId(argId);
                argMD->setExplicitArgNum(vOnSet);
                funcInfo->addImplicitArgInfoListItem(argMD);
            }
        }
        else
        {
            // unique implicit argument

            ArgInfoMetaDataHandle argMD = ArgInfoMetaDataHandle(ArgInfoMetaData::get());
            argMD->setArgId(argId);
            funcInfo->addImplicitArgInfoListItem(argMD);
        }
    }

    for (const auto N : data.StructArgSet) // argument number
    {
        ArgInfoMetaDataHandle argMD = ArgInfoMetaDataHandle(ArgInfoMetaData::get());
        ImplicitStructArgument info;
        info.DW0.Value = N;

        argMD->setExplicitArgNum(info.DW0.All.argExplicitNum);
        argMD->setStructArgOffset(info.DW0.All.argOffset);   // offset
        argMD->setArgId(info.DW0.All.argId);  // type
        funcInfo->addImplicitArgInfoListItem(argMD);
    }

}

