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

Copyright (C) 2019-2021 Intel Corporation

SPDX-License-Identifier: MIT

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

//===----------------------------------------------------------------------===//
///
/// This pass and its Resolution equivalent follow the familar AdaptorOCL
/// pattern of an analysis followed by a resolution pass.  The idea early
/// on was that continuations would be invoked often as function calls so we
/// need to promote the below intrinsics to implicit args so we could later call
/// BuiltinCallGraphAnalysis to propagate the arguments through all calls.  It
/// is not currently clear how much we'll use this approach: it may be that
/// BTD + inlining of small continuations may is the way to go.
///
//===----------------------------------------------------------------------===//

#include "IGC/common/StringMacros.hpp"
#include "common/LLVMUtils.h"
#include "Compiler/IGCPassSupport.h"
#include "Compiler/CodeGenPublic.h"
#include "AdaptorCommon/ImplicitArgs.hpp"

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

using namespace IGC;
using namespace llvm;

//////////////////////////////////////////////////////////////////////////
//
// Pass detects the use of ray tracing intrinsics and prepares them for
// addition as implicit arguments
class RayTracingIntrinsicAnalysis : public ModulePass, public InstVisitor<RayTracingIntrinsicAnalysis>
{
public:
    RayTracingIntrinsicAnalysis();
    bool runOnModule(Module& M) override;
    bool runOnFunction(Function &F);
    StringRef getPassName() const override
    {
        return "RayTracingIntrinsicAnalysis";
    }
    virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override
    {
        AU.addRequired<MetaDataUtilsWrapper>();
    }

    void visitCallInst(CallInst &CI);

    static char ID;
private:
    bool hasGlobalPointer = false;
    bool hasLocalPointer = false;
    bool hasStackID = false;
    bool hasInlinedData = false;
    IGCMD::MetaDataUtils* pMdUtils = nullptr;
};

#define PASS_FLAG "raytracing-intrinsic-analysis"
#define PASS_DESCRIPTION "Mark raytracing intrinsics as implicit args"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
IGC_INITIALIZE_PASS_BEGIN(RayTracingIntrinsicAnalysis, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
IGC_INITIALIZE_PASS_END(RayTracingIntrinsicAnalysis, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)

RayTracingIntrinsicAnalysis::RayTracingIntrinsicAnalysis() : ModulePass(ID) {
    initializeRayTracingIntrinsicAnalysisPass(*PassRegistry::getPassRegistry());
}

char RayTracingIntrinsicAnalysis::ID = 0;

void RayTracingIntrinsicAnalysis::visitCallInst(CallInst &CI)
{
    if (auto *GII = dyn_cast<GenIntrinsicInst>(&CI))
    {
        switch (GII->getIntrinsicID())
        {
        case GenISAIntrinsic::GenISA_GlobalBufferPointer:
            hasGlobalPointer = true;
            return;
        case GenISAIntrinsic::GenISA_LocalBufferPointer:
            hasLocalPointer = true;
            return;
        case GenISAIntrinsic::GenISA_AsyncStackID:
            hasStackID = true;
            return;
        case GenISAIntrinsic::GenISA_InlinedData:
            hasInlinedData = true;
            return;
        default:
            break;
        }
    }
}

bool RayTracingIntrinsicAnalysis::runOnModule(Module& M)
{
    bool changed = false;
    pMdUtils = getAnalysis<MetaDataUtilsWrapper>().getMetaDataUtils();
    // Run on all functions defined in this module
    for (Function& F : M)
    {
        if (F.isDeclaration())
        {
            continue;
        }
        if (runOnFunction(F))
        {
            changed = true;
        }
    }

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

    return changed;
}

bool RayTracingIntrinsicAnalysis::runOnFunction(Function &F)
{
    if (pMdUtils->findFunctionsInfoItem(&F) == pMdUtils->end_FunctionsInfo())
        return false;

    hasGlobalPointer = false;
    hasLocalPointer  = false;
    hasStackID       = false;
    hasInlinedData   = false;

    visit(F);

    bool changed = false;
    SmallVector<ImplicitArg::ArgType, ImplicitArg::NUM_IMPLICIT_ARGS> implicitArgs;

    if (hasGlobalPointer)
        implicitArgs.push_back(ImplicitArg::RT_GLOBAL_BUFFER_POINTER);

    if (hasLocalPointer)
        implicitArgs.push_back(ImplicitArg::RT_LOCAL_BUFFER_POINTER);

    if (hasStackID)
        implicitArgs.push_back(ImplicitArg::RT_STACK_ID);

    if (hasInlinedData)
        implicitArgs.push_back(ImplicitArg::RT_INLINED_DATA);

    if (!implicitArgs.empty())
    {
        // Create IGC metadata representing the implicit args needed by this function
        ImplicitArgs::addImplicitArgsTotally(
            F,
            implicitArgs,
            pMdUtils);
        changed = true;
    }
    return changed;
}
namespace IGC
{
    Pass* createRayTracingIntrinsicAnalysisPass()
    {
        return new RayTracingIntrinsicAnalysis();
    }
}
