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

Copyright (C) 2021 Intel Corporation

SPDX-License-Identifier: MIT

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

#include "common/LLVMWarningsPush.hpp"
#include <llvm/Analysis/CFG.h>
#include <llvm/Analysis/PostDominators.h>
#include <llvm/IR/Dominators.h>
#include <llvm/IR/IntrinsicInst.h>
#include "common/LLVMWarningsPop.hpp"

#include "common/IGCIRBuilder.h"
#include "Compiler/CodeGenPublic.h"
#include "Compiler/MetaDataUtilsWrapper.h"
#include "GenISAIntrinsics/GenIntrinsics.h"
#include "GenISAIntrinsics/GenIntrinsicInst.h"
#include "Probe/Assertion.h"
#include "IGCPassSupport.h"

#include "URBPartialWrites.hpp"

using namespace llvm;
namespace IGC
{
// This pass converts URB partial writes into full writes. Depending on platform
// type the minimum URB full-write access granularity (referred as `full-write
// granularity` for the rest of this file) is 32 or 16 bytes.
// A URB partial write is a URB write that writes chunks of data smaller than
// `full-write granularity` or writes data at address not aligned to `full-write
// granularity`.
//
// A URB partial write instruction can be changed to a full-mask write if it can
// be proved that writing additional URB channels does not overwrite data that
// is observable by URBReadOutput instruction in current shader stage or read
// in the next shader stage (i.e. written by other URBWrite instructions in
// current stage).
//
// High-level algorithm:
// For every partial write instruction (A):
//  - check if there exists a different URB write instruction (B) that
//    potentially writes to the same chunk of URB and that B is not strictly
//    dominated by A and does not strictly post-dominates A
//    The dominance/post-dominance condition is not valid in mesh and task
//    shaders. In mesh and task shaders a single URB location may be (partially)
//    written from multiple HW threads.
//  - check if there exists a URB read instruction (C) that potentially reads
//    the same chunk of URB accessed by A and is reachable from A
// If both conditions above are `false` the full mask can be set.
class URBPartialWrites : public FunctionPass
{
public:
    static char ID;

    URBPartialWrites() :
        m_SafeToExtend(std::make_pair(true, true)),
        m_SharedURB(false),
        FunctionPass(ID)
    {
        initializeURBPartialWritesPass(*PassRegistry::getPassRegistry());
    }

    virtual bool runOnFunction(Function& F);

    virtual void getAnalysisUsage(AnalysisUsage& AU) const
    {
        AU.setPreservesCFG();
        AU.addRequired<CodeGenContextWrapper>();
        AU.addRequired<MetaDataUtilsWrapper>();
        AU.addRequired<DominatorTreeWrapperPass>();
        AU.addRequired<PostDominatorTreeWrapperPass>();
        AU.addRequired<LoopInfoWrapperPass>();
        AU.addPreserved<DominatorTreeWrapperPass>();
        AU.addPreserved<PostDominatorTreeWrapperPass>();
        AU.addPreserved<LoopInfoWrapperPass>();
    }

    virtual llvm::StringRef getPassName() const { return "URBPartialWrites"; }

private:
    // URB access descriptor
    // member 0 - runtime URB offset
    // member 1 - constant URB offset
    // member 2 - URBReadOutput or URBWrite intrinsic
    // member 3 - intrinsic instruction number within its basic block
    typedef std::tuple<Value*, uint, GenIntrinsicInst*, uint> URBAccess;

    void GetURBWritesAndReads(Function& F);
    bool ResolvePartialWrites16B();
    bool ResolvePartialWrites32B();
    bool Dominates(
        const URBAccess& access0,
        const URBAccess& access1) const;
    bool PostDominates(
        const URBAccess& access0,
        const URBAccess& access1) const;
    std::pair<bool, bool> IsSafeToExtendChannelMask(
        const URBAccess& write0,
        const URBAccess& access1) const;
    std::pair<bool, bool> CheckURBWrites(
        const URBAccess& write0) const;
    std::pair<bool, bool> CheckURBReads(
        const URBAccess& write0) const;
private:
    const std::pair<bool, bool> m_SafeToExtend;
    bool m_SharedURB; // multiple HW threads may write the same URB location
    std::vector<URBAccess> m_UrbWrites;
    std::vector<URBAccess> m_UrbReads;
};
char URBPartialWrites::ID = 0;

static inline uint GetZExtValue(Value* v)
{
    IGC_ASSERT(isa<ConstantInt>(v));
    return int_cast<uint>(cast<ConstantInt>(v)->getZExtValue());
}

// Returns true if `v` is a llvm::Constant* and is equal to `a`.
static inline bool IsConstAndEqualTo(Value* v, uint a)
{
    if (isa<ConstantInt>(v) && a == GetZExtValue(v))
    {
        return true;
    }
    return false;
}

// Returns `v` casted to llvm::Instruction* if `v` is an llvm::Instruction* with
// opcode equal to the template argument.
template <uint Opcode>
Instruction* dyn_cast(Value* v)
{
    if (isa<Instruction>(v) &&
        Operator::getOpcode(v) == Opcode)
    {
        return cast<Instruction>(v);
    }
    return nullptr;
}

// Returns the channel mask for a URBReadOutput or URBWrite instruction. For
// URBWrite, if channel mask operand is not a constant the function returns the
// mask of channels that are potentially written by the instruction.
// For URBReadOutput, if all usages are not ExtractElement instruction with
// constant indices the function returns the mask of channels that are
// potentially required.
static inline uint GetChannelMask(GenIntrinsicInst* inst)
{
    if (inst->getIntrinsicID() == llvm::GenISAIntrinsic::GenISA_URBReadOutput)
    {
        uint mask = 0;
        for (auto iit = inst->user_begin(), eit = inst->user_end();
            iit != eit;
            ++iit)
        {
            ExtractElementInst* ee = dyn_cast<ExtractElementInst>(*iit);
            if (ee && isa<ConstantInt>(ee->getIndexOperand()))
            {
                mask |= 1 << GetZExtValue(ee->getIndexOperand());
            }
            else
            {
                mask = 0xFF;
                break;
            }
        }
        return mask;
    }
    IGC_ASSERT(inst->getIntrinsicID() == llvm::GenISAIntrinsic::GenISA_URBWrite);
    Value* channelMask = inst->getOperand(1);
    uint mask = 0xFF;
    if (isa<ConstantInt>(channelMask))
    {
        mask = GetZExtValue(channelMask);
    }
    else
    {
        // Non-constant mask:
        // Pattern 1:
        //  %155 = and i32 %153, 3
        //  %156 = shl i32 1, %155
        // Pattern 2:
        //  %155 = and i32 %153, 3
        //  %156 = shl i32 8, %155
        Instruction* shlInst = dyn_cast<Instruction::Shl>(channelMask);
        Instruction* andInst = shlInst ?
            dyn_cast<Instruction::And>(shlInst->getOperand(1)) : nullptr;
        if (andInst && IsConstAndEqualTo(andInst->getOperand(1), 3))
        {
            if (IsConstAndEqualTo(shlInst->getOperand(0), 1))
            {
                mask = 0x0F;
            }
            if (IsConstAndEqualTo(shlInst->getOperand(0), 8))
            {
                mask = 0xF0;
            }
        }
    }
    // disable channels corresponding to the undef operands
    for (uint i = 0; i < 8; ++i)
    {
        if (isa<UndefValue>(inst->getOperand(2 + i)))
        {
            mask &= ~(1 << i);
        }
    }
    return mask;
}

// Returns true if the `val` is even. If the function returns `false`
// `val` can be even or odd.
static inline bool IsEven(Value* val)
{
    if (val == nullptr)
    {
        return true;
    }
    Instruction* inst = dyn_cast<Instruction>(val);
    if (inst &&
        inst->getNumOperands() == 2 &&
        isa<ConstantInt>(inst->getOperand(1)))
    {
        const uint src1 = GetZExtValue(inst->getOperand(1));
        if (Operator::getOpcode(inst) == Instruction::Shl &&
            src1 > 0)
        {
            return true;
        }
        if (Operator::getOpcode(inst) == Instruction::Mul &&
            (src1 % 2) == 0)
        {
            return true;
        }
    }
    return false;
}

// Collects all URBWrite and URBReadOutput instructions in a function.
void URBPartialWrites::GetURBWritesAndReads(Function& F)
{
    for (auto& BB : F)
    {
        uint instCounter = 0;
        for (auto II = BB.begin(), IE = BB.end();
            II != IE;
            ++II, ++instCounter)
        {
            GenIntrinsicInst* intr = dyn_cast<GenIntrinsicInst>(&(*II));
            if (intr &&
                intr->getIntrinsicID() == llvm::GenISAIntrinsic::GenISA_URBWrite)
            {
                std::pair<Value*, uint> baseAndOffset =
                    GetURBBaseAndOffset(intr->getOperand(0));
                m_UrbWrites.push_back(std::make_tuple(
                    baseAndOffset.first,
                    baseAndOffset.second,
                    intr,
                    instCounter));
            }
            else if (intr &&
                intr->getIntrinsicID() == llvm::GenISAIntrinsic::GenISA_URBReadOutput)
            {
                std::pair<Value*, uint> baseAndOffset =
                    GetURBBaseAndOffset(intr->getOperand(0));
                m_UrbReads.push_back(std::make_tuple(
                    baseAndOffset.first,
                    baseAndOffset.second,
                    intr,
                    instCounter));
            }
        }
    }
}

// Checks if extending the channel mask of the `write0` may potentially
// overwrite data accessed by `access1`. The returned value is a pair of `bool`
// values, the first member corresponds to the lower 16-byte chunk and the
// second member corresponds to the higher 16-byte chunk. If true is returned it
// means that the channel mask of corresponding 16-byte chunk can be extended
// to full mask.
std::pair<bool, bool> URBPartialWrites::IsSafeToExtendChannelMask(
    const URBAccess& write0,
    const URBAccess& access1) const
{
    Value* const offset0 = std::get<0>(write0);
    const uint constOffset0 = std::get<1>(write0);
    GenIntrinsicInst* const intr0 = std::get<2>(write0);
    IGC_ASSERT(intr0->getIntrinsicID() == llvm::GenISAIntrinsic::GenISA_URBWrite);
    const bool immMask0 = isa<ConstantInt>(intr0->getOperand(1));
    const uint mask0 = GetChannelMask(intr0);
    Value* const offset1 = std::get<0>(access1);
    const uint constOffset1 = std::get<1>(access1);
    GenIntrinsicInst* const intr1 = std::get<2>(access1);
    const uint mask1 = GetChannelMask(intr1);

    // Get lower 4 bits of channel mask
    auto Lo = [](uint mask)->uint
    {
        return mask & 0xF;
    };
    // Get higher 4 bits of channel mask
    auto Hi = [](uint mask)->uint
    {
        return (mask >> 4) & 0xF;
    };
    // Returns true if full mask can be set.
    auto SafeToSetFullMask = [this, immMask0](
        uint mask0,
        uint mask1)->bool
    {
        if (mask1 == 0)
        {
            // not overlapping accesses
            return true;
        }
        if (immMask0 &&
            !m_SharedURB &&
            (mask0 | mask1) == mask0)
        {
            // The second URB access is for a subset of channels accessed by the
            // first access.
            return true;
        }
        return false;
    };

    bool overlapLo = false;
    bool overlapHi = false;
    if ((offset0 == nullptr && offset1 == nullptr) ||
        offset0 == offset1)
    {
        if (constOffset0 == constOffset1)
        {
            // e.g.:
            // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 1, ...)
            // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 18, ...)
            if (!SafeToSetFullMask(Lo(mask0), Lo(mask1)))
            {
                overlapLo = true;
            }
            // e.g.:
            // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 1, ...)
            // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 18, ...)
            if (!SafeToSetFullMask(Hi(mask0), Hi(mask1)))
            {
                overlapHi = true;
            }
        }
        // e.g.:
        // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 16, ...)
        // call void @llvm.genx.GenISA.URBWrite(i32 2, i32 2, ...)
        else if (constOffset0 + 1 == constOffset1 &&
                !SafeToSetFullMask(Hi(mask0), Lo(mask1)))
        {
            overlapHi = true;
        }
        // e.g.:
        // call void @llvm.genx.GenISA.URBWrite(i32 2, i32 2, ...)
        // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 16, ...)
        else if (constOffset1 + 1 == constOffset0 &&
                 !SafeToSetFullMask(Lo(mask0), Hi(mask1)))
        {
            overlapLo = true;
        }
    }
    // e.g.:
    // %offset0 = and i32 %153, 2
    // call void @llvm.genx.GenISA.URBWrite(i32 offset0, i32 2, ...)
    // call void @llvm.genx.GenISA.URBWrite(i32 0, i32 16, ...)
    else if ((offset0 != nullptr && offset1 == nullptr) &&
        (constOffset1 < constOffset0 && constOffset1 + 2 <= constOffset0))
    {
        // no ovelap
    }
    // e.g.:
    // call void @llvm.genx.GenISA.URBWrite(i32 0, i32 2, ...)
    // %offset1 = and i32 %153, 2
    // call void @llvm.genx.GenISA.URBWrite(i32 offset1, i32 16, ...)
    else if ((offset0 == nullptr && offset1 != nullptr) &&
        (constOffset0 < constOffset1 && constOffset0 + 2 <= constOffset1))
    {
        // no ovelap
    }
    // e.g.:
    // %offset0 = and i32 %153, 2
    // call void @llvm.genx.GenISA.URBWrite(i32 offset0, i32 2, ...)
    // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 16, ...)
    else if ((offset0 != nullptr && offset1 == nullptr) &&
        constOffset1 + 1 == constOffset0 &&
        !SafeToSetFullMask(Lo(mask0), Hi(mask1)))
    {
        overlapLo = true;
    }
    // e.g.:
    // call void @llvm.genx.GenISA.URBWrite(i32 1, i32 32, ...)
    // %offset1 = and i32 %153, 2
    // call void @llvm.genx.GenISA.URBWrite(i32 offset1, i32 2, ...)
    else if ((offset0 == nullptr && offset1 != nullptr) &&
        constOffset0 + 1 == constOffset1 &&
        !SafeToSetFullMask(Hi(mask0), Lo(mask1)))
    {
        overlapHi = true;
    }
    else
    {
        overlapLo = true;
        overlapHi = true;
    }
    return std::make_pair(!overlapLo, !overlapHi);
}

// Checks if there exists a URBWrite instruction (different than `write0`) such
// that:
// - it is not strictly dominated by `write0` and does not strictly
//   post-dominates `write0`
// - writes to URB channels that are not written by `write0` but belong to the
//   32-byte chunk accessed by `write0`
// The returned value is a pair of `bool` values, the first member corresponds
// to the lower 16-byte chunk and the second member corresponds to the higher
// 16-byte chunk. If true is returned it means that the channel mask of
// corresponding 16-byte chunk can be extended to full mask.
std::pair<bool, bool> URBPartialWrites::CheckURBWrites(
    const URBAccess& write0) const
{
    std::pair<bool, bool> safeToExtend = m_SafeToExtend;
    for (uint k = 0; k < m_UrbWrites.size(); ++k)
    {
        const URBAccess& write1 = m_UrbWrites[k];
        std::pair<bool, bool> extendMask = IsSafeToExtendChannelMask(write0, write1);
        if (extendMask == m_SafeToExtend)
        {
            continue;
        }
        if (write0 != write1 &&
            !Dominates(write0, write1) &&
            !PostDominates(write1, write0))
        {
            safeToExtend.first &= extendMask.first;
            safeToExtend.second &= extendMask.second;
        }
        // In Mesh and Task shaders URB may be written from multiple HW threads.
        else if (write0 != write1 && m_SharedURB)
        {
            safeToExtend.first &= extendMask.first;
            safeToExtend.second &= extendMask.second;
        };
        if (!safeToExtend.first && !safeToExtend.second)
        {
            break;
        }
    }
    return safeToExtend;
}

// Checks if there exists a URBReadOutput instruction such that:
// - URBReadOutput is reachable from `write0`
// - reads URB channels that are not written by `write0` but belong to the
//   32-byte chunk accessed by `write0`
// The returned value is a pair of `bool` values, the first member corresponds
// to the lower 16-byte chunk and the second member corresponds to the higher
// 16-byte chunk. If true is returned it means that the channel mask of
// corresponding 16-byte chunk can be extended to full mask.
std::pair<bool, bool> URBPartialWrites::CheckURBReads(
    const URBAccess& write) const
{
    DominatorTree* DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
    GenIntrinsicInst* const writeIntr = std::get<2>(write);
    const bool immMask = isa<ConstantInt>(writeIntr->getOperand(1));
    const uint writeMask = GetChannelMask(writeIntr);
    std::pair<bool, bool> safeToExtend = m_SafeToExtend;
    for (uint i = 0; i < m_UrbReads.size(); ++i)
    {
        const URBAccess& read = m_UrbReads[i];
        GenIntrinsicInst* const readIntr = std::get<2>(read);
        const uint readMask1 = GetChannelMask(readIntr);
        std::pair<bool, bool> extendMask = IsSafeToExtendChannelMask(write, read);
        if (extendMask == m_SafeToExtend)
        {
            continue;
        }
        // Note: this method can potentially be refined by checking if between
        // the `write` and the `read` exists a different URBWrite that
        // overwrites the URB chunk written by `write`.
        if (isPotentiallyReachable(writeIntr, readIntr, nullptr, DT))
        {
            safeToExtend.first &= extendMask.first;
            safeToExtend.second &= extendMask.second;
            if (!safeToExtend.first && !safeToExtend.second)
            {
                break;
            }
        }
    }
    return safeToExtend;
}

// Returns true if `access0` strictly dominates `access1`
bool URBPartialWrites::Dominates(
    const URBAccess& access0,
    const URBAccess& access1) const
{
    GenIntrinsicInst* const intr0 = std::get<2>(access0);
    GenIntrinsicInst* const intr1 = std::get<2>(access1);
    if (intr0 == intr1)
    {
        return false;
    }
    if (intr0->getParent() == intr1->getParent())
    {
        return std::get<3>(access0) < std::get<3>(access1);
    }
    DominatorTree* DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
    return DT->dominates(intr0->getParent(), intr1->getParent());
}

// Returns true if `access0` strictly post-dominates `access1`
bool URBPartialWrites::PostDominates(
    const URBAccess& access0,
    const URBAccess& access1) const
{
    GenIntrinsicInst* const intr0 = std::get<2>(access0);
    GenIntrinsicInst* const intr1 = std::get<2>(access1);
    if (intr0 == intr1)
    {
        return false;
    }
    if (intr0->getParent() == intr1->getParent())
    {
        return std::get<3>(access0) > std::get<3>(access1);
    }
    PostDominatorTree* PDT = &getAnalysis<PostDominatorTreeWrapperPass>().getPostDomTree();
    return PDT->properlyDominates(intr0->getParent(), intr1->getParent());
}

// Converts URB partial writes into full writes on platforms with `full-write
// granularity` of 32 bytes.
bool URBPartialWrites::ResolvePartialWrites32B()
{
    bool modified = false;
    LoopInfo* LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
    for (uint i = 0; i < m_UrbWrites.size(); ++i)
    {
        const URBAccess& write0 = m_UrbWrites[i];
        Value* const offset0 = std::get<0>(write0);
        const uint constOffset0 = std::get<1>(write0);
        GenIntrinsicInst* const intr0 = std::get<2>(write0);
        const bool immMask0 = isa<ConstantInt>(intr0->getOperand(1));
        const uint mask0 = GetChannelMask(intr0);
        if (immMask0 && mask0 == 0xFF)
        {
            // not a partial write
            continue;
        }
        if (!immMask0 && LI->getLoopFor(intr0->getParent()))
        {
            // not immediate mask and in a loop
            continue;
        }
        if (!IsEven(offset0) || (constOffset0 % 2) == 1)
        {
            // unaligned URB write
            continue;
        }
        if (m_SafeToExtend == CheckURBWrites(write0) &&
            m_SafeToExtend == CheckURBReads(write0))
        {
            Value* fullMask = ConstantInt::get(
                intr0->getOperand(1)->getType(),
                0xFF);
            intr0->setOperand(1, fullMask);
            modified = true;
        }
    }
    return modified;
}

// Converts URB partial writes into full writes on platforms with `full-write
// granularity` of 16 bytes.
bool URBPartialWrites::ResolvePartialWrites16B()
{
    bool modified = false;
    LoopInfo* LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
    for (uint i = 0; i < m_UrbWrites.size(); ++i)
    {
        const URBAccess& write0 = m_UrbWrites[i];
        GenIntrinsicInst* const intr0 = std::get<2>(write0);
        const bool immMask0 = isa<ConstantInt>(intr0->getOperand(1));
        const uint mask0 = GetChannelMask(intr0);
        if (immMask0 &&
            (mask0 == 0xFF || mask0 == 0x0F || mask0 == 0xF0))
        {
            // not a partial write
            continue;
        }
        if (!immMask0 && LI->getLoopFor(intr0->getParent()))
        {
            // not immediate mask and in a loop
            continue;
        }
        std::pair<bool, bool> extendMask = CheckURBWrites(write0);
        std::pair<bool, bool> extendMask1 = CheckURBReads(write0);
        bool extendLo = extendMask.first && extendMask1.first;
        bool extendHi = extendMask.second && extendMask1.second;
        if (extendLo || extendHi)
        {
            uint newMask0 = mask0;
            if (extendLo && (mask0 & 0x0F) != 0)
            {
                newMask0 |= 0x0F;
            }
            if (extendHi && (mask0 & 0xF0) != 0)
            {
                newMask0 |= 0xF0;
            }
            Type* maskType = intr0->getOperand(1)->getType();
            Value* newMask = ConstantInt::get(maskType, newMask0);
            if ((!immMask0 && extendLo && extendHi) ||
                (!immMask0 && extendLo && (mask0 & 0xF0) == 0) ||
                (!immMask0 && extendHi && (mask0 & 0x0F) == 0) ||
                (immMask0 && newMask0 != mask0))
            {
                intr0->setOperand(1, newMask);
                modified = true;
            }
            else if (!immMask0)
            {
                IGCIRBuilder<> builder(intr0);
                uint orMask = extendHi ? 0xF0 : 0x0F;
                if ((orMask & mask0) == orMask)
                {
                    newMask = builder.CreateOr(
                        intr0->getOperand(1),
                        ConstantInt::get(maskType, orMask));
                    intr0->setOperand(1, newMask);
                    modified = true;
                }
            }
        }
    }
    return modified;
}

bool URBPartialWrites::runOnFunction(Function& F)
{
    bool modified = false;
    m_UrbWrites.clear();
    m_UrbReads.clear();

    GetURBWritesAndReads(F);

    const CodeGenContext* const ctx = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();
    if (ctx->type == ShaderType::MESH_SHADER ||
        ctx->type == ShaderType::TASK_SHADER)
    {
        // In Mesh and Task shaders a single URB location may be written from
        // multiple HW threads.
        const IGC::ModuleMetaData* md =
            getAnalysis<MetaDataUtilsWrapper>().getModuleMetaData();
        const uint workGroupSize = ctx->type == ShaderType::MESH_SHADER ?
            md->msInfo.WorkGroupSize : md->taskInfo.WorkGroupSize;
        uint minSimd = ctx->platform.getMinDispatchMode() == SIMDMode::SIMD8 ? 8 : 16;
        if (workGroupSize > minSimd)
        {
            m_SharedURB = true;
        }
    }
    const uint fullWriteGranularity = ctx->platform.getURBFullWriteMinGranularity();
    if (fullWriteGranularity == 16)
    {
        modified = ResolvePartialWrites16B();
    }
    else
    {
        IGC_ASSERT(fullWriteGranularity == 32);
        modified = ResolvePartialWrites32B();
    }
    return modified;
}

llvm::FunctionPass* createURBPartialWritesPass()
{
    return new URBPartialWrites();
}

#define PASS_FLAG "igc-urb-partial-writes"
#define PASS_DESCRIPTION "Convert URB partial writes to full-mask writes."
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
IGC_INITIALIZE_PASS_BEGIN(URBPartialWrites, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
    IGC_INITIALIZE_PASS_DEPENDENCY(CodeGenContextWrapper)
    IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
    IGC_INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
    IGC_INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass)
    IGC_INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
IGC_INITIALIZE_PASS_END(URBPartialWrites, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)

} // namespace IGC
