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

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

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

#include "LVN.hpp"
#include "../Timer.h"

#include <fstream>
#include <sstream>
#include <map>

using namespace vISA;

//#define DEBUG_LVN_ON

#define DUMMY_HSTRIDE_2_2_0     0x8000
#define DUMMY_HSTRIDE_4_4_0     0x4000
#define DUMMY_HSTRIDE_8_8_0     0xc000
#define DUMMY_HSTRIDE_16_16_0   0x2000

bool isSpecialRegion(const RegionDesc* desc, uint16_t& hstride)
{
    bool isSpecial = false;

    // Look for regions like <N;N,0> in general,
    // except for <1;1,0> since it is contiguous.
    // DS uses N=4 case.
    if (desc->vertStride == desc->width &&
        desc->vertStride != 1 &&
        desc->horzStride == 0)
    {
        isSpecial = true;

        // Update hstride
        switch (desc->vertStride)
        {
        case 2:
            hstride = DUMMY_HSTRIDE_2_2_0;
            break;
        case 4:
            hstride = DUMMY_HSTRIDE_4_4_0;
            break;
        case 8:
            hstride = DUMMY_HSTRIDE_8_8_0;
            break;
        case 16:
            hstride = DUMMY_HSTRIDE_16_16_0;
            break;
        default:
            MUST_BE_TRUE(false, "Unexpected special hstride seen");
        }
    }

    return isSpecial;
}

// This function looks at region description and execution size
// and using API of RegionDesc class, decides what normalized
// hstride is. Overlapping regions or non-uniform regions, ie
// disconnected regions are not expected in LVN.
uint16_t getActualHStride(G4_SrcRegRegion* srcRgn)
{
    const RegionDesc* desc = srcRgn->getRegion();
    uint32_t execSize = srcRgn->getInst()->getExecSize();
    uint16_t stride = desc->horzStride;
    bool isSpecialRgn = isSpecialRegion(desc, stride);

    if (!isSpecialRgn)
    {
        desc->isSingleStride(execSize, stride);
    }

    return stride;
}

bool LVN::getDstData(int64_t srcImm, G4_Type srcType, int64_t& dstImm, G4_Type dstType, bool& canNegate)
{
    // mov (...) V..:dstType       0x...:srcType
    // Given srcImm data, compute what dst register will contain
    bool dstImmValid = false;
    int64_t andMask = 0;
    canNegate = (srcImm == 0 ? false : true);
    andMask = (0xffffffffffffffff >> ((sizeof(int64_t) * 8) - TypeBitSize(dstType)));

    if (srcType == Type_W || srcType == Type_UW ||
        srcType == Type_D || srcType == Type_UD ||
        srcType == Type_Q || srcType == Type_UQ)
    {
        dstImm = srcImm;
        if (IS_TYPE_INT(dstType))
        {
            dstImm &= andMask;
            dstImmValid = true;
        }
        else if (srcImm == 0)
        {
            // Make special case for following type
            // conversion:
            // mov (..) V1:f    0:w/uw/d/ud
            dstImm &= andMask;
            dstImmValid = true;
        }
        else if (dstType == Type_F)
        {
            union
            {
                int32_t i;
                float f;
            } ftod;
            int32_t intSrc = (int32_t)srcImm;
            ftod.f = (float)intSrc;
            dstImm = (int64_t)ftod.i;
            dstImm &= andMask;

            // Now ensure the int is within representable
            // precision range of float. So reverse
            // convert and compare with original data.
            int32_t reverseInt;
            reverseInt = (int32_t)ftod.f;
            if (reverseInt == intSrc)
            {
                dstImmValid = true;
            }
        }
    }
    else if (srcType == Type_HF || srcType == Type_F ||
        srcType == Type_DF)
    {
        dstImm = srcImm;
        if (dstType == srcType ||
            srcImm == 0)
        {
            dstImm &= andMask;
            dstImmValid = true;
        }
    }
    else if (IS_VTYPE(srcType))
    {
        dstImm = srcImm;
        dstImmValid = true;
        canNegate = false;
    }

    return dstImmValid;
}

bool LVN::getAllUses(G4_INST* def, UseList& uses)
{
    bool defFound = false;
    auto it = defUse.find(def);
    if (it != defUse.end())
    {
        defFound = true;
        uses = (*it).second;
    }

    return defFound;
}

bool LVN::canReplaceUses(INST_LIST_ITER inst_it, UseList& uses, G4_INST* lvnInst, bool negMatch, bool noPartialUse)
{
    // Look up all uses of current instruction.
    // Then check if each use is fully defined by current
    // instruction. Return true if this is true for all
    // uses.
    G4_INST* defInst = (*inst_it);
    G4_DstRegRegion* def = defInst->getDst();
    G4_DstRegRegion* lvnDst = lvnInst->getDst();
    G4_Declare* lvnDstTopDcl = lvnDst->getTopDcl();

#ifdef DEBUG_LVN_ON
    std::cerr << "Inst with same value in LVN Table:" << std::endl;
    lvnInst->emit(std::cerr);
    std::cerr << " #" << lvnInst->getLineNo() << ":$" << lvnInst->getCISAOff();
    std::cerr << std::endl;
    std::cerr << "Current inst:" << std::endl;
    defInst->emit(std::cerr);
    std::cerr << " #" << defInst->getLineNo() << ":$" << defInst->getCISAOff();
    std::cerr << std::endl << "Uses:" << std::endl;

    char shouldReplace[2];
#endif

    // Check if def defines each use fully
    unsigned int lb = def->getLeftBound();
    unsigned int rb = def->getRightBound();
    unsigned int hs = def->getHorzStride();

    bool canReplace = true;
    for (auto use_it = uses.begin(), uend = uses.end(); use_it != uend; use_it++)
    {
        G4_INST* useInst = (*use_it).first;
        Gen4_Operand_Number opndNum = (*use_it).second;
        G4_Operand* use = useInst->getOperand(opndNum);
        unsigned int use_lb = use->getLeftBound();
        unsigned int use_rb = use->getRightBound();

        // Ensure a single def flows in to the use
        auto it = useDef.find(use);
        if (it == useDef.end() ||
            (*it).second.size() != 1)
        {
            canReplace = false;
            break;
        }

        if (!bb->isAllLaneActive())
        {
            auto defCoversUseEmask = defInst->getMaskOffset() <= useInst->getMaskOffset() &&
                (defInst->getMaskOffset() + defInst->getExecSize() >= useInst->getMaskOffset() + useInst->getExecSize());
            if (!defInst->isWriteEnableInst() &&
                (!defCoversUseEmask || useInst->isWriteEnableInst()))
            {
                // if defInst does not fully cover useInst, it's generally unsafe to do LVN
                canReplace = false;
                break;
            }
        }

        // Compute a single positive stride if exists.
        unsigned int use_hs = 0;
        {
            uint16_t stride = 0;
            const RegionDesc* rd = use->asSrcRegRegion()->getRegion();
            if (rd->isSingleStride(useInst->getExecSize(), stride))
            {
                use_hs = stride;
            }
        }

        if (noPartialUse)
        {
            if (lb != use_lb ||
                rb != use_rb ||
                (hs != use_hs && defInst->getExecSize() > g4::SIMD1))
            {
                canReplace = false;
                break;
            }
        }
        else
        {
            // ok as long as def covers entire use
            if (lb > use_lb || rb < use_rb)
            {
                canReplace = false;
                break;
            }
        }

        if (hs != 1)
        {
            auto relation = defInst->getDst()->compareOperand(use, builder);
            if (!(relation == Rel_eq || relation == Rel_gt))
            {
                canReplace = false;
                break;
            }
        }

        if (useInst->isSend())
        {
            // send operand doesn't take subreg, so the operand has to be GRF-aligned
            if (!builder.isOpndAligned(lvnDst, builder.numEltPerGRF<Type_UB>()))
            {
                canReplace = false;
                break;
            }
        }

        if (useInst->isSplitSend())
        {
            // Ensure both src opnds of split are not the same
            if ((opndNum == Opnd_src0 &&
                useInst->getSrc(1)->getTopDcl() == lvnDstTopDcl) ||
                (opndNum == Opnd_src1 &&
                    useInst->getSrc(0)->getTopDcl() == lvnDstTopDcl))
            {
                canReplace = false;
                break;
            }
        }

        if (negMatch)
        {
            if (!useInst->canSupportSrcModifier())
            {
                // LVN is invalid if instruction does not support negatve modifier
                canReplace = false;
                break;
            }

            if (useInst->isRawMov() && useInst->getDst()->getTypeSize() == 1)
            {
                // For byte-type raw movs, src modifier '-' is invalid
                canReplace = false;
                break;
            }

            if (IS_UNSIGNED_INT(use->getType()))
            {
                // Bug fix for following sequence:
                // a:w = 1:w
                // b:uw = 0xffff:uw
                // c:d = x:d + b:uw
                //
                // It is incorrect to replace b:uw above with -a:uw.
                canReplace = false;
                break;
            }
            if (use->getType() != lvnInst->getDst()->getType())
            {
                //
                // negate's meaning can change based on type
                //
                canReplace = false;
                break;
            }
        }

        // Iterate to ensure there is no WAR/WAW
        auto fwdInst_it = inst_it;
        fwdInst_it++;
        bool lvnTopDclAddresses = lvnDstTopDcl->getAddressed();
        bool lvnTopDclGRFAssigned = (lvnDstTopDcl->getRegVar()->getPhyReg() != NULL);
        while ((*fwdInst_it) != useInst)
        {
            G4_DstRegRegion* curDst = (*fwdInst_it)->getDst();

            // FIXME: why not use isWAWDep()?
            if (curDst && !curDst->isNullReg())
            {
                if (lvnTopDclAddresses &&
                    curDst->isIndirect())
                {
                    canReplace = false;
                    break;
                }

                auto dstRgn = curDst;
                auto topdcl = dstRgn->getTopDcl();

                unsigned int lvnlb = lvnDst->getLeftBound();
                unsigned int lvnrb = lvnDst->getRightBound();

                unsigned int curlb = curDst->getLeftBound();
                unsigned int currb = curDst->getRightBound();

                if (topdcl == lvnDstTopDcl &&
                    ((lvnlb <= curlb && lvnrb >= curlb) ||
                    (curlb <= lvnlb && currb >= lvnlb)))
                {
                    canReplace = false;
                    break;
                }

                if (lvnTopDclGRFAssigned &&
                    curDst->getTopDcl()->getRegVar()->getPhyReg() != NULL)
                {
                    canReplace = sameGRFRef(lvnDstTopDcl, curDst->getTopDcl());
                    if (!canReplace)
                    {
                        break;
                    }
                }
            }

            fwdInst_it++;
        }
    }

    if (canReplace)
    {
        // Ensure lvnInst's dst type and that of current inst is the same
        G4_Type lvnDstType = lvnDst->getType();
        G4_Type defType = def->getType();
        // For non-integers, they must have the same type to be replaced
        if (lvnDstType != defType &&
            (!IS_TYPE_INT(lvnDstType) || !IS_TYPE_INT(defType)))
        {
            canReplace = false;
        }
        // For integers, they must have the same type width to be replaced
        if ((IS_TYPE_INT(lvnDstType) && IS_TYPE_INT(defType)) &&
            TypeSize(lvnDstType) != TypeSize(defType))
        {
            canReplace = false;
        }

        if (canReplace &&
            lvnDst->getHorzStride() !=
            def->getHorzStride())
        {
            // Catch the case:
            // mov (8) V0<1>:d    V3
            // mov (8) V1<2>:d    V3
            // add (8) V2<1>:q    V2    V1<16;8,2>
            //
            // => Dont replace V1<16;8,2> with V0<8;8,1> because it would make
            // code HW non-conformant.
            canReplace = false;
        }
    }

    if (canReplace &&
        lvnInst->getExecSize() < defInst->getExecSize())
    {
        // Allow following case:
        // mov (8) V1(0,0) x -- x is an immdiate
        // op (8) ... V1(0,0)
        // mov (1) V2(0,0) x => remove
        // op (1) ... V2(0,0) => op (1) ... V1(0,0)
        //
        // But disallow following case:
        // mov (1) V1(0,0) x <-- lvnInst
        // op (1) ... V1(0,0)
        // mov (8) V2(0,0) x <-- defInst
        // op (8) ... V2(0,0) => Cant replace V2 with V1
        //

        canReplace = false;
    }

    if (canReplace &&
        defInst->getExecSize() > g4::SIMD1)
    {
        // Check whether alignment matches for vectors
        // mov (8) V2(0,6)    V1(0,1) ... <-- lvnInst
        // ...
        // mov (8) V3(0,0)    V2(0,6)
        // op (8) V4          V3(0,0)   ...
        //
        // In general, V3(0,0) src in op cannot be replaced by
        // V2(0,6).
        if (lb % builder.numEltPerGRF<Type_UB>() != lvnDst->getLeftBound() % builder.numEltPerGRF<Type_UB>())
        {
            canReplace = false;
        }
    }

#ifdef DEBUG_LVN_ON
    if (canReplace)
    {
        for (auto use_it = uses.begin();
            use_it != uses.end();
            use_it++)
        {
            (*use_it).first->emit(std::cerr);
            std::cerr << std::endl;
        }

        std::cerr << std::endl << std::endl << "Replace this occurence?";
        scanf("%1s", &shouldReplace);

        canReplace = (shouldReplace[0] == 'y' ? true : false);
        if (canReplace)
            std::cerr << "Ok, will replace" << std::endl;
    }
#endif

    return canReplace;
}

// transfer alignment of fromDcl to toDcl if the former is more restrictive
void LVN::transferAlign(G4_Declare* toDcl, G4_Declare* fromDcl)
{
    if (!toDcl->isEvenAlign() && fromDcl->isEvenAlign())
    {
        toDcl->setEvenAlign();
    }
    G4_SubReg_Align align1 = toDcl->getSubRegAlign();
    G4_SubReg_Align align2 = fromDcl->getSubRegAlign();


    // Compute most constrained sub-reg alignment and assign that
    // to lvnDst dcl since it will replace curDst based operands.
    if (align1 == align2)
    {
        return;
    }

    G4_SubReg_Align ret = std::max(align1, align2);
    toDcl->setSubRegAlign(ret);
}

void LVN::replaceAllUses(G4_INST* defInst, bool negate, UseList& uses, G4_INST* lvnInst, bool keepRegion)
{
    G4_Declare* dstTopDcl = lvnInst->getDst()->getTopDcl();
    const unsigned int regOff = lvnInst->getDst()->getRegOff();
    const unsigned int subRegOff = lvnInst->getDst()->getSubRegOff();

    // Ensure most constrained alignment gets applied to lvnInst's dst
    transferAlign(dstTopDcl, defInst->getDst()->getTopDcl());

#ifdef DEBUG_VERBOSE_ON
    std::cerr << "Inst with same value in LVN Table:" << std::endl;
    lvnInst->emit(std::cerr);
    std::cerr << " #" << lvnInst->getLineNo() << ":$" << lvnInst->getCISAOff();
    std::cerr << std::endl;
    std::cerr << "Current inst:" << std::endl;
    defInst->emit(std::cerr);
    std::cerr << " #" << defInst->getLineNo() << ":$" << defInst->getCISAOff();
    std::cerr << std::endl << "Uses:" << std::endl;
#endif

    for (auto use : uses)
    {
        G4_INST* useInst = use.first;
        G4_SrcRegRegion* srcToReplace = useInst->getOperand(use.second)->asSrcRegRegion();
        G4_SrcModifier srcMod = srcToReplace->getModifier();
        if (negate == true)
        {
            // Negate will be true only when value is based off an immediate operand
            // so there is no need to check for combination of src modifiers.
            if (srcMod == Mod_src_undef)
            {
                srcMod = Mod_Minus;
            }
            else if (srcMod == Mod_Minus)
            {
                srcMod = Mod_src_undef;
            }
            else
            {
                MUST_BE_TRUE(false, "Unexpected src modifier found in LVN");
            }
        }

        G4_SrcRegRegion* srcRgn = nullptr;
        if (keepRegion)
        {
            // new offset should include the offset between the use and its original def
            assert(srcToReplace->getLeftBound() >= defInst->getDst()->getLeftBound() && "orig dst does not fully define use");
            int offsetFromOrigDst = srcToReplace->getLeftBound() - defInst->getDst()->getLeftBound();
            // we can replace the regVar directly without changing the rest of the region
            auto typeSize = srcToReplace->getTypeSize();
            int offset = regOff * builder.numEltPerGRF<Type_UB>() + subRegOff * lvnInst->getDst()->getTypeSize() + offsetFromOrigDst;
            short newRegOff = offset / builder.numEltPerGRF<Type_UB>();
            short newSubRegOff = (offset % builder.numEltPerGRF<Type_UB>()) / typeSize;

            srcRgn = builder.createSrcRegRegion(srcMod, Direct, lvnInst->getDst()->getBase()->asRegVar(),
                newRegOff, newSubRegOff, srcToReplace->getRegion(), srcToReplace->getType());
        }
        else
        {
            unsigned short vstride = srcToReplace->getRegion()->vertStride;
            unsigned short width = srcToReplace->getRegion()->width;
            unsigned short hstride = getActualHStride(srcToReplace);
            G4_Type type = srcToReplace->getType();

            unsigned int subRegOffScaled = subRegOff * lvnInst->getDst()->getTypeSize() / TypeSize(type);

            srcRgn = builder.createSrcRegRegion(srcMod, Direct,
                lvnInst->getDst()->getBase()->asRegVar(),
                (short)regOff,
                (short)subRegOffScaled,
                builder.createRegionDesc(vstride, width, hstride),
                type);
        }

        if (srcToReplace->isAccRegValid())
        {
            srcRgn->setAccRegSel(srcToReplace->getAccRegSel());
        }

        unsigned int srcIndex = G4_INST::getSrcNum(use.second);
        useInst->setSrc(srcRgn, srcIndex);
    }
}

void LVN::removeVirtualVarRedefs(G4_DstRegRegion* dst)
{
    auto dstTopDcl = dst->getTopDcl();
    if (!dstTopDcl)
        return;

    auto it = dclValueTable.find(dstTopDcl);

    if (it != dclValueTable.end())
    {
        for (auto second = it->second.begin(), end = it->second.end();
            second != end;
            )
        {
            auto potentialRedef = (*second);
#define IS_VAR_REDEFINED(origopnd, opnd) \
    (((origopnd->getLeftBound() <= opnd->getLeftBound() && origopnd->getRightBound() >= opnd->getLeftBound()) || \
    (opnd->getLeftBound() <= origopnd->getLeftBound() && opnd->getRightBound() >= origopnd->getLeftBound())))

            if (potentialRedef->inst &&
                potentialRedef->inst->getDst() &&
                potentialRedef->inst->getDst()->getTopDcl() == dstTopDcl &&
                IS_VAR_REDEFINED(dst, potentialRedef->inst->getDst()))
            {
                potentialRedef->active = false;

                for (auto use : potentialRedef->uses)
                {
                    use->active = false;
                }
            }
            else
            {
                for (unsigned int i = 0; i < G4_MAX_SRCS; i++)
                {
                    if (potentialRedef->inst &&
                        potentialRedef->inst->getSrc(i) &&
                        potentialRedef->inst->getSrc(i)->getTopDcl() == dstTopDcl &&
                        IS_VAR_REDEFINED(dst, potentialRedef->inst->getSrc(i)))
                    {
                        potentialRedef->active = false;

                        for (auto use : potentialRedef->uses)
                        {
                            use->active = false;
                        }
                    }
                }
            }

            second++;
        }
    }

    if (dst->getTopDcl()->getAddressed())
    {
        // Iterate over entire LVN table and remove any instruction with
        // src indirect operand that point to current dst. For eg,
        // A0 = &V10
        // V20 = r[A0] <-- inst1
        // ...
        // V10 = 0 <-- Current instruction - Invalidate inst1
        // V30 = r[A0] <-- inst1 != this inst
        for (auto dcls : lvnTable)
        {
            for (auto second = dcls.second.begin();
                second != dcls.second.end();
                )
            {
                auto lvnItems = (*second);
                auto lvnItemsInst = lvnItems->inst;

                for (unsigned int i = 0; i < G4_MAX_SRCS; i++)
                {
                    if (lvnItemsInst &&
                        lvnItemsInst->getSrc(i) &&
                        lvnItemsInst->getSrc(i)->isSrcRegRegion() &&
                        lvnItemsInst->getSrc(i)->asSrcRegRegion()->isIndirect())
                    {
                        if (p2a.isPresentInPointsTo(lvnItemsInst->getSrc(i)->asSrcRegRegion()->getTopDcl()->getRegVar(), dst->getTopDcl()->getRegVar()))
                        {
                            lvnItems->active = false;
                            second = dcls.second.erase(second);

                            for (auto use : lvnItems->uses)
                            {
                                use->active = false;
                            }
                            continue;
                        }
                    }
                }
                if (second != dcls.second.end())
                {
                    // Increment iterator only if dcls.second is not empty.
                    // Erase operation in earlier loop can result in this.
                    second++;
                }
            }
        }
    }
}

bool LVN::sameGRFRef(G4_Declare* dcl1, G4_Declare* dcl2)
{
    bool overlap = false;

    unsigned int dst1LowGRF = dcl1->getRegVar()->getPhyReg()->asGreg()->getRegNum();
    unsigned int dst1HighGRF = dst1LowGRF + (dcl1->getNumRows());

    unsigned int dst2LowGRF = dcl2->getRegVar()->getPhyReg()->asGreg()->getRegNum();
    unsigned int dst2HighGRF = dst2LowGRF + (dcl2->getNumRows());

    if (dst1LowGRF <= dst2HighGRF &&
        dst1HighGRF >= dst2LowGRF)
    {
        overlap = true;
    }

    return overlap;
}

// Function assumes that dst's G4_RegVar has pre-defined
// physical register. We remove all those entries from
// LVN table that refer to same GRF.
void LVN::removePhysicalVarRedefs(G4_DstRegRegion* dst)
{
    G4_Declare* topdcl = dst->getTopDcl();
    for (auto all : lvnTable)
    {
        for (auto it = all.second.begin();
            it != all.second.end();
            )
        {
            auto item = (*it);
            bool erase = false;

            auto dstTopDcl = item->inst->getDst() ? item->inst->getDst()->getTopDcl() : nullptr;
            if (dstTopDcl->getRegVar()->isGreg())
            {
                if (sameGRFRef(topdcl, dstTopDcl))
                {
                    item->active = false;
                    erase = true;
                }
            }

            for (auto i = 0; i < item->inst->getNumSrc(); i++)
            {
                auto srcTopDcl = item->inst->getSrc(i)->getTopDcl();
                if (srcTopDcl &&
                    srcTopDcl->getRegVar()->isGreg())
                {
                    // Check if both physical registers have an overlap
                    if (sameGRFRef(topdcl, srcTopDcl))
                    {
                        item->active = false;
                        erase = true;
                    }
                }
            }

            if (erase)
            {
                it = all.second.erase(it);
                continue;
            }

            it++;
        }
    }
}

bool LVN::checkIfInPointsTo(const G4_RegVar* addr, const G4_RegVar* var) const
{
    // Check whether var is present in points2 of addr
    auto ptrToAllPointsTo = p2a.getAllInPointsTo(addr);
    if (ptrToAllPointsTo)
    {
        const G4_RegVar* topRegVar = var->getDeclare()->getRootDeclare()->getRegVar();
        auto it = std::find_if(ptrToAllPointsTo->begin(), ptrToAllPointsTo->end(),
            [&topRegVar](const pointInfo& element) {return element.var == topRegVar && element.off == 0; });

        return it != ptrToAllPointsTo->end();
    }

    return false;
}

void LVN::removeAliases(G4_INST* inst)
{
    // inst uses indirect dst operand
    MUST_BE_TRUE(inst->getDst()->isIndirect(), "Expecting to see indirect operand in dst");

    auto dstPointsToPtr = p2a.getAllInPointsTo(inst->getDst()->getTopDcl()->getRegVar());
    if (!dstPointsToPtr)
        return;

    for (auto item : *dstPointsToPtr)
    {
        auto dcl = item.var->getDeclare()->getRootDeclare();
        auto it = dclValueTable.find(dcl);
        if (it == dclValueTable.end())
            continue;

        for (auto d : it->second)
        {
            d->active = false;
#ifdef DEBUG_VERBOSE_ON
            DEBUG_VERBOSE("Removing inst from LVN table for indirect dst conflict:");
            d->inst->emit(std::cerr);
            DEBUG_VERBOSE(" #" << d->inst->getLineNo() << ":$" << d->inst->getCISAOff() << std::endl);
            DEBUG_VERBOSE("Due to indirect dst in:" << std::endl);
            inst->emit(std::cerr);
            DEBUG_VERBOSE(" #" << inst->getLineNo() << ":$" << inst->getCISAOff() << std::endl << std::endl);
#endif
        }
    }
}

void LVN::removeRedefs(G4_INST* inst)
{
    G4_DstRegRegion* dst = inst->getDst();
    if (dst)
    {
        if (dst->isIndirect())
        {
            removeAliases(inst);
        }

        G4_DstRegRegion* dstRegRegion = dst;
        removeVirtualVarRedefs(dstRegRegion);

        G4_Declare* dstTopDcl = dstRegRegion->getTopDcl();
        if (dstTopDcl &&
            dstTopDcl->getRegVar()->isGreg())
        {
            removePhysicalVarRedefs(dstRegRegion);
        }
    }
}

int64_t LVN::getNegativeRepresentation(int64_t imm, G4_Type type)
{
    union
    {
        double ddata;
        float fdata;
        char bdata;
        short wdata;
        int dwdata;
        int64_t lldata;
    } d;
    d.lldata = imm;

    if (type == Type_B || type == Type_UB)
    {
        d.bdata = -d.bdata;
    }
    else if (type == Type_W || type == Type_UW)
    {
        d.wdata = -d.wdata;
    }
    else if (type == Type_D || type == Type_UD)
    {
        d.dwdata = -d.dwdata;
    }
    else if (type == Type_Q || type == Type_UQ)
    {
        d.lldata = -d.lldata;
    }
    else if (type == Type_F)
    {
        d.fdata = -d.fdata;
    }
    else if (type == Type_DF)
    {
        d.ddata = -d.ddata;
    }
    else if (type == Type_HF)
    {
        d.wdata = d.wdata ^ (1 << 15);
    }

    return d.lldata;
}

const char* LVN::getModifierStr(G4_SrcModifier srcMod)
{
    if (srcMod == Mod_src_undef)
    {
        return "";
    }
    else if (srcMod == Mod_Minus)
    {
        return "NEG";
    }
    else if (srcMod == Mod_Abs)
    {
        return "ABS";
    }
    else if (srcMod == Mod_Minus_Abs)
    {
        return "NEGABS";
    }
    else if (srcMod == Mod_Not)
    {
        return "NOT";
    }
    else
    {
        return "";
    }
}

void LVN::getValue(int64_t imm, G4_Operand* opnd, Value& value)
{
    value.hash = imm;
    value.inst = nullptr;
}

LVNItemInfo* LVN::getOpndValue(G4_Operand* opnd, bool create)
{
    auto topdcl = opnd->getTopDcl();
    if (!topdcl)
        return nullptr;

    auto lb = opnd->getLeftBound();
    auto rb = opnd->getRightBound();
    uint16_t hs = 0;
    bool isScalar = false;
    bool isSingleStride = false;
    if (opnd->isSrcRegRegion())
    {
        isScalar = opnd->asSrcRegRegion()->getRegion()->isScalar();
        if (!isScalar)
        {
            isSingleStride = opnd->asSrcRegRegion()->getRegion()->isSingleStride(opnd->getInst()->getExecSize(), hs);
            if (!isSingleStride)
                hs = 0;
        }
    }
    else if (opnd->isDstRegRegion())
    {
        isScalar = opnd->getInst()->getExecSize() == g4::SIMD1;
        if (!isScalar)
        {
            hs = opnd->asDstRegRegion()->getHorzStride();
            isSingleStride = true;
        }
    }

    auto it = dclValueTable.find(topdcl);
    if (it == dclValueTable.end())
    {
        if (!create)
        {
            return nullptr;
        }
    }

    if (it != dclValueTable.end())
    {
        for (auto& item : (*it).second)
        {
            if (!item->active)
                continue;

            if (item->lb == lb && item->rb == rb && item->isScalar == isScalar &&
                item->constHStride == isSingleStride && item->hstride == hs)
                return item;
        }
    }

    if (create)
    {
        // create instance of LVNItemInfo since it doesnt exist
        LVNItemInfo* item = createLVNItemInfo();
        item->constHStride = isSingleStride;
        item->hstride = hs;
        item->inst = opnd->getInst();
        item->opnd = opnd;
        item->isScalar = isScalar;
        item->lb = lb;
        item->rb = rb;

        return item;
    }

    return nullptr;
}

void LVN::getValue(G4_SrcRegRegion* src, G4_INST* inst, Value& value)
{
    G4_Declare* topdcl = src->getTopDcl();

    // check whether operand is present in value table
    auto lvnItemInfo = getOpndValue(src);
    if (lvnItemInfo->value.isValueEmpty())
    {
        lvnItemInfo->value.hash = topdcl->getDeclId() + src->getLeftBound() + src->getRightBound() + getActualHStride(src) + dclValueTable.size();
        lvnItemInfo->value.inst = inst;
        perInstValueCache.push_back(std::make_pair(topdcl, lvnItemInfo));
    }
    value = lvnItemInfo->value;
}

void LVN::getValue(G4_DstRegRegion* dst, G4_INST* inst, Value& value)
{
    G4_Declare* topdcl = dst->getTopDcl();

    auto lvnItemInfo = getOpndValue(dst);
    if (lvnItemInfo->value.isValueEmpty())
    {
        lvnItemInfo->value.hash = topdcl->getDeclId() + dst->getLeftBound() + dst->getRightBound() + dst->getHorzStride() + dclValueTable.size();
        lvnItemInfo->value.inst = inst;
        perInstValueCache.push_back(std::make_pair(topdcl, lvnItemInfo));
    }
    value = lvnItemInfo->value;
}

void LVN::invalidateOldDstValue(G4_INST* inst)
{
    // invalidate old values
    if (!inst->getDst())
        return;
    auto topdcl = inst->getDst()->getTopDcl();
    if (!topdcl)
        return;
    auto it = dclValueTable.find(topdcl);
    if (it == dclValueTable.end())
        return;

    auto lb = inst->getDst()->getLeftBound();
    auto rb = inst->getDst()->getRightBound();
    for (auto& item : (*it).second)
    {
        if (!item->active)
            continue;

        if (item->lb < rb && item->rb >= rb)
            item->active = false;

        if (lb < item->rb && rb >= item->rb)
            item->active = false;
    }
}

void LVN::getValue(G4_INST* inst, Value& value)
{
    value.inst = inst;
    value.hash = (unsigned int)inst->opcode();
    for (unsigned int i = 0; i != inst->getNumSrc(); i++)
    {
        auto opnd = inst->getSrc(i);

        Value v;
        if (opnd->isSrcRegRegion())
        {
            getValue(opnd->asSrcRegRegion(), inst, v);
            value.hash += v.hash;
        }
        else if (opnd->isImm())
        {
            getValue(opnd->asImm()->getInt(), opnd, v);
            value.hash += v.hash;
        }
    }
}

template<class T, class K>
bool LVN::opndsMatch(T* opnd1, K* opnd2)
{
    bool match = true;

    // T,K can either be G4_SrcRegRegion/G4_DstRegRegion
    G4_Declare* topdcl1 = opnd1->getTopDcl();
    G4_Declare* topdcl2 = opnd2->getTopDcl();

    G4_INST* inst1 = opnd1->getInst();
    G4_INST* inst2 = opnd2->getInst();
    // Compare emask for opnd1, opnd2 instructions
    if (!bb->isAllLaneActive())
    {
        if (inst1->isWriteEnableInst() != inst2->isWriteEnableInst())
        {
            match = false;
        }
        else
        {
            if (inst1->getMaskOffset() != inst2->getMaskOffset() &&
                !inst1->isWriteEnableInst())
            {
                match = false;
            }
        }
    }
    else
    {
        // Not in SIMD CF so dont care
    }

    if (match)
    {
        const char* mod1 = getModifierStr(Mod_src_undef);
        const char* mod2 = mod1;
        if (opnd1->isSrcRegRegion())
        {
            mod1 = getModifierStr(opnd1->asSrcRegRegion()->getModifier());
        }

        if (opnd2->isSrcRegRegion())
        {
            mod2 = getModifierStr(opnd2->asSrcRegRegion()->getModifier());
        }

        if (mod1 != mod2)
        {
            match = false;
        }
    }

    if (match)
    {
        if (opnd1->isIndirect() != opnd2->isIndirect())
        {
            match = false;
        }
        else
        {
            // In this branch, both operands are either direct or indirect
            if (opnd1->isIndirect())
            {
                if (opnd1->isSrcRegRegion() &&
                    opnd2->isSrcRegRegion())
                {
                    G4_SrcRegRegion* src1 = opnd1->asSrcRegRegion();
                    G4_SrcRegRegion* src2 = opnd2->asSrcRegRegion();

                    if (topdcl1 != topdcl2 ||
                        src1->getRegOff() != src2->getRegOff() ||
                        src1->getSubRegOff() != src2->getSubRegOff() ||
                        src1->getAddrImm() != src2->getAddrImm() ||
                        getActualHStride(src1) != getActualHStride(src2) ||
                        src1->getInst()->getExecSize() != src2->getInst()->getExecSize() ||
                        src1->getType() != src2->getType())
                    {
                        match = false;
                    }
                }
                else
                {
                    // opnd1 is a src region and opnd2 is dst region or
                    // vice-versa.
                    match = false;
                }
            }
            else
            {
                // opnd1/opnd2 can be either src/dst regions and may not of the same type.
                if (topdcl1 != topdcl2 || opnd1->getType() != opnd2->getType())
                {
                    match = false;
                }
                else
                {
                    int op1lb = 0, op2lb = 0, op1rb = 0, op2rb = 0, op1hs = 0, op2hs = 0;
                    if (opnd1->isSrcRegRegion())
                    {
                        G4_SrcRegRegion* src1 = opnd1->asSrcRegRegion();
                        op1lb = src1->getLeftBound();
                        op1rb = src1->getRightBound();
                        op1hs = getActualHStride(src1);
                    }
                    else if (opnd1->isDstRegRegion())
                    {
                        G4_DstRegRegion* dst1 = opnd1->asDstRegRegion();
                        op1lb = dst1->getLeftBound();
                        op1rb = dst1->getRightBound();
                        op1hs = dst1->getHorzStride();
                    }

                    if (opnd2->isSrcRegRegion())
                    {
                        G4_SrcRegRegion* src2 = opnd2->asSrcRegRegion();
                        op2lb = src2->getLeftBound();
                        op2rb = src2->getRightBound();
                        op2hs = getActualHStride(src2);
                    }
                    else if (opnd2->isDstRegRegion())
                    {
                        G4_DstRegRegion* dst2 = opnd2->asDstRegRegion();
                        op2lb = dst2->getLeftBound();
                        op2rb = dst2->getRightBound();
                        op2hs = dst2->getHorzStride();
                    }

                    if (op1lb != op2lb ||
                        op1rb != op2rb ||
                        op1hs != op2hs)
                    {
                        match = false;
                    }
                }
            }
        }
    }

    return match;
}

// Given a G4_Operand, this function returns false if,
// operand is a src region that is either scalar <0;1,0> or
// has contiguous region like <8;8,1>. Otherwise, if
// src region is uniform like <8;4,2> then function returns
// false. Only in case of non-uniform region the function
// returns true. Non-uniform regions are regions that
// have overlapping elements or hstride between elements
// in not uniform.
//
// Examples of non-uniform region are
// (4) <1;2,2>, (8) <2;4,1>, (8) <8;4,1>.
// The last one <8;4,1> is actually used in FRC.
bool isNonUniformSrcRegion(G4_SrcRegRegion* srcRgn)
{
    auto execSize = srcRgn->getInst()->getExecSize();
    return !srcRgn->getRegion()->isSingleStride(execSize);
}

bool LVN::addValue(G4_INST* inst)
{
    auto isAllowedOpcode = [](G4_INST* inst)
    {
        if (inst->opcode() == G4_mov ||
            inst->opcode() == G4_shl ||
            inst->opcode() == G4_shr)
            return true;
        return false;
    };

    if (!isAllowedOpcode(inst) ||
        inst->getSaturate() ||
        inst->getDst()->isIndirect() ||
        inst->getPredicate() != NULL ||
        inst->getCondMod() != NULL)
    {
        return false;
    }

    G4_Operand* dst = inst->getDst();
    if (!dst->getBase() ||
        !dst->getBase()->isRegVar() ||
        dst->getBase()->asRegVar()->getDeclare()->getRegFile() != G4_GRF ||
        dst->getTopDcl()->getAddressed())
    {
        return false;
    }

    if (dst->getBase() &&
        dst->getTopDcl()->isOutput())
    {
        return false;
    }

    auto srcValid = [](G4_Operand* src)
    {
        if (src->isImm())
        {
            if (src->isRelocImm())
            {
                return false;
            }
            return true;
        }

        if (src->isSrcRegRegion())
        {
            G4_SrcRegRegion* srcRgn = src->asSrcRegRegion();
            if (!srcRgn->getBase() ||
                !srcRgn->getBase()->isRegVar() ||
                (!srcRgn->getBase()->asRegVar()->getDeclare()->useGRF() && !srcRgn->isIndirect()))
            {
                return false;
            }

            if (srcRgn->isIndirect())
            {
                return false;
            }

            if (srcRgn->getBase() &&
                srcRgn->getTopDcl()->isOutput())
            {
                return false;
            }
            return true;
        }

        return true;
    };

    bool allSrcsValid = true;
    for (unsigned int i = 0; i != inst->getNumSrc(); i++)
    {
        auto src = inst->getSrc(i);
        allSrcsValid &= srcValid(src);
    }

    return allSrcsValid;

}

bool LVN::computeValue(G4_INST* inst, bool negate, bool& canNegate, bool& isGlobal, int64_t& tmpPosImm, bool posValValid, Value& value)
{
    canNegate = false;
    isGlobal = false;
    value.initializeEmptyValue();

    G4_Operand* src = inst->getSrc(0);
    LVNItemInfo* item = nullptr;

    if (inst->opcode() == G4_mov && src->isImm())
    {
        if (src->isImm())
        {
            int64_t dstImm = 0;
            G4_Type srcType = src->asImm()->getType();
            G4_Type dstType = inst->getDst()->getType();

            if (negate && posValValid)
            {
                dstImm = getNegativeRepresentation(tmpPosImm, inst->getDst()->getType());
            }
            else
            {
                G4_Imm* srcImmOpnd = src->asImm();
                int64_t srcImmData = srcImmOpnd->getImm();
                bool success = getDstData(srcImmData, srcType, dstImm, dstType, canNegate);
                tmpPosImm = dstImm;
                if (success == false)
                {
                    // This means mov was unsupported for optimization,
                    // eg, a type-conversion mov.
                    return false;
                }

                if (negate == true)
                {
                    dstImm = getNegativeRepresentation(dstImm, dstType);
                }
            }

            getValue(dstImm, src, value);
            if (!negate)
            {
                value.inst = inst;

                // Substituting negative imm values is an optimization case
                item = createLVNItemInfo();
                item->isImm = true;
                item->constHStride = true;
                item->hstride = inst->getDst()->getHorzStride();
                item->inst = inst;
                item->opnd = inst->getDst();
                item->isScalar = inst->getExecSize() == g4::SIMD1;
                item->lb = inst->getDst()->getLeftBound();
                item->rb = inst->getDst()->getRightBound();
                item->value = value;
            }
        }
    }
    else
    {
        getValue(inst, value);
        item = createLVNItemInfo();
        item->constHStride = true;
        item->hstride = inst->getDst()->getHorzStride();
        item->inst = inst;
        item->opnd = inst->getDst();
        item->isScalar = inst->getExecSize() == g4::SIMD1;
        item->lb = inst->getDst()->getLeftBound();
        item->rb = inst->getDst()->getRightBound();
        item->value = value;
    }

    if (item &&
        inst->getDst() && inst->getDst()->getTopDcl())
    {
        perInstValueCache.push_back(std::make_pair(inst->getDst()->getTopDcl(), item));
    }

    if (inst->getDst() && inst->getDst()->getTopDcl())
    {
        // Compute value for globals so we can insert it in LVN table.
        // But we dont want to apply optimization on such instructions.
        isGlobal = fg.globalOpndHT.isOpndGlobal(inst->getDst());
        isGlobal |= inst->getDst()->getTopDcl()->isOutput();
        isGlobal |= inst->getDst()->getTopDcl()->isInput();
    }

    return true;
}

// Ordering of val1 and val2 parameters is important.
// val1 is current instruction in top-down order.
// val2 is earlier instruction having operand with same value.
// Ordering is important because we allow optimization when
// val2 instruction uses NoMask and val1 instruction uses
// a subset.
bool LVN::valuesMatch(Value& val1, Value& val2, bool checkNegImm)
{
    auto inst1 = val1.inst;
    auto inst2 = val2.inst;

    if (inst1->opcode() != inst2->opcode())
        return false;

    bool match = false;

    for (unsigned int i = 0; i != G4_MAX_SRCS; i++)
    {
        G4_Operand* opnd1 = inst1->getSrc(i);
        G4_Operand* opnd2 = inst2->getSrc(i);

        if ((!opnd1 && opnd2) || (opnd1 && !opnd2))
            return false;

        if (!opnd1 && !opnd2)
            continue;

        if (opnd1->isImm() &&
            opnd2->isImm())
        {
            match = true;
            G4_Type type1 = opnd1->getType();
            G4_Type type2 = opnd2->getType();

            if ((type1 == Type_UV && type2 != Type_UV) || (type1 != Type_UV && type2 == Type_UV))
            {
                return false;
            }
            else if ((type1 == Type_V && type2 != Type_V) || (type1 != Type_V && type2 == Type_V))
            {
                return false;
            }
            else if ((type1 == Type_VF && type2 != Type_VF) || (type1 != Type_VF && type2 == Type_VF))
            {
                return false;
            }

            if (match && !bb->isAllLaneActive())
            {
                G4_INST* val1Inst = val1.inst;
                G4_INST* val2Inst = val2.inst;

                if (val1Inst->isWriteEnableInst() != val2Inst->isWriteEnableInst() ||
                    val1Inst->getMaskOption() != val2Inst->getMaskOption())
                {
                    return false;
                }
            }

            if (!checkNegImm)
            {
                if (!opnd1->asImm()->isEqualTo(opnd2->asImm()))
                {
                    return false;
                }
            }
            else
            {
                auto imm1 = opnd1->asImm()->getImm();
                auto imm2 = opnd2->asImm()->getImm();
                auto bitSize = TypeBitSize(type1);
                // Check all bits except for sign-bit.
                // Assume sign-bit is always the MSB of type.
                for (unsigned int i = 0; i != bitSize - 1; i++)
                {
                    if (((imm1 >> i) & 0x1) != ((imm2 >> i) & 0x1))
                    {
                        return false;
                    }
                }
            }
        }
        else if (opnd1->isSrcRegRegion())
        {
            if (opnd2->isSrcRegRegion())
            {
                match = opndsMatch(opnd1->asSrcRegRegion(), opnd2->asSrcRegRegion());
            }
        }

        if (!match)
            return false;
    }

    return match;
}

bool LVN::isSameValue(Value& val1, Value& val2, bool negImmVal)
{
    if (val1.isEqualValueHash(val2))
    {
        // Do a detailed check whether values really match
        if (valuesMatch(val1, val2, negImmVal))
        {
            return true;
        }
    }

    return false;
}

LVNItemInfo* LVN::isValueInTable(Value& value, bool negate)
{
    int64_t hash = value.hash;

    auto bucket = lvnTable.find(hash);
    if (bucket != lvnTable.end())
    {
        for (auto it = bucket->second.rbegin();
            it != bucket->second.rend();
            )
        {
            auto lvnItem = (*it);

            if (lvnItem->active &&
                isSameValue(value, lvnItem->value, negate))
            {
                return lvnItem;
            }

            it++;
        }
    }

    return nullptr;
}

void LVN::addValueToTable(G4_INST* inst, Value& oldValue)
{
    auto findLVNItemInfo = [this](G4_INST* inst, G4_Operand* opnd)
    {
        auto it = dclValueTable.find(opnd->getTopDcl());
        MUST_BE_TRUE(it != dclValueTable.end(), "Value not added");
        LVNItemInfo* lvnItem = nullptr;
        for (auto item : (*it).second)
        {
            if (item->inst == inst &&
                opnd->getInst() == item->inst)
            {
                return item;
            }
        }
        MUST_BE_TRUE(lvnItem != nullptr, "Not expecting nullptr");

        return lvnItem;
    };

    auto insertInLvnTable = [this](int64_t key, LVNItemInfo* itemToIns)
    {
        auto it = lvnTable.find(key);
        if (it != lvnTable.end())
        {
            it->second.push_back(itemToIns);
        }
        else
        {
            std::list<vISA::LVNItemInfo*> second;
            second.push_back(itemToIns);
            lvnTable.insert(make_pair(key, second));
        }
        itemToIns->active = true;
    };

    auto attachUses = [this](G4_INST* inst, LVNItemInfo* lvnItem)
    {
        // scan all dcls of lvnItem->inst and link
        // all their values in to this lvnItem
        // this is required because G4 IR is not SSA so
        // a redef needs to invalidate earlier computed
        // values that use the older def. for eg,
        // V10 = ... <-- value1
        // V11 = V10 <-- value2
        // V10 = V10 + 1  <-- value3 (invalidate value2)
        // V12 = V10 <-- needs to use value3, not value2
        for (unsigned int i = 0; i != inst->getNumSrc(); i++)
        {
            auto src = inst->getSrc(i);
            if (src && src->isSrcRegRegion())
            {
                auto srcLb = src->getLeftBound();
                auto srcRb = src->getRightBound();
                auto it = dclValueTable.find(src->getTopDcl());
                if (it != dclValueTable.end())
                {
                    for (auto l : (*it).second)
                    {
                        if (l->active)
                        {
                            auto lb = l->lb;
                            auto rb = l->rb;
                            if (lb <= srcRb && rb >= srcLb)
                            {
                                l->uses.push_back(lvnItem);
                            }
                        }
                    }
                }
            }
        }
    };


    for (auto& item : perInstValueCache)
    {
        auto it = dclValueTable.find(item.first);
        item.second->active = true;
        dclValueTable[item.first].push_back(item.second);
    }

    auto lvnItem = findLVNItemInfo(inst, inst->getDst());
    insertInLvnTable(oldValue.hash, lvnItem);
    attachUses(inst, lvnItem);

    MUST_BE_TRUE(lvnItem->inst == lvnItem->value.inst, "Missing inst ptr in value");
}

void LVN::addUse(G4_DstRegRegion* dst, G4_INST* use, unsigned int srcIndex)
{
    Gen4_Operand_Number srcPos = Opnd_dst;
    if (srcIndex == 0)
    {
        srcPos = Opnd_src0;
    }
    else if (srcIndex == 1)
    {
        srcPos = Opnd_src1;
    }
    else if (srcIndex == 2)
    {
        srcPos = Opnd_src2;
    }

    UseInfo useInst = { use, srcPos };
    auto it = defUse.find(dst->getInst());

    if (it == defUse.end())
    {
        UseList uses = { useInst };

        defUse.insert(std::make_pair(dst->getInst(), uses));
    }
    else
    {
        (*it).second.push_back(useInst);
    }

    auto srcOpnd = use->getSrc(srcIndex);
    auto itUD = useDef.find(srcOpnd);
    if (itUD == useDef.end())
    {
        DefList defs = { dst->getInst() };
        useDef.insert(std::make_pair(srcOpnd, defs));
    }
    else
    {
        (*itUD).second.push_back(dst->getInst());
    }
}

void LVN::removeAddrTaken(G4_AddrExp* opnd)
{
    G4_Declare* opndTopDcl = opnd->getRegVar()->getDeclare()->getRootDeclare();

    auto range_it = activeDefs.equal_range(opndTopDcl->getDeclId());
    for (auto it = range_it.first;
        it != range_it.second;
        )
    {
        auto prev_it = it;
        (*prev_it).second.second->getInst()->removeAllUses();
        it++;
        activeDefs.erase(prev_it);
    }
}

void LVN::populateDuTable(INST_LIST_ITER inst_it)
{
    duTablePopulated = true;
    // Populate duTable from inst_it position
    ActiveDefMMap activeDefs;
    G4_INST* startInst = (*inst_it);
    G4_Operand* startInstDst = startInst->getDst();
    INST_LIST_ITER lastInstIt = bb->end();
    unsigned int numEdgesAdded = 0;

    while (inst_it != lastInstIt)
    {
        G4_INST* curInst = (*inst_it);
        // First scan src operands and check if their def has been
        // added to activeDefs table already. If so, link them.
        for (unsigned int i = 0; i < G4_MAX_SRCS; i++)
        {
            G4_Operand* opnd = curInst->getSrc(i);
            if (opnd == NULL)
            {
                continue;
            }

            if (opnd->isAddrExp())
            {
                // Since this operand is address taken,
                // remove any defs corresponding to it
                // in DU and activeDefs tables.
                removeAddrTaken(opnd->asAddrExp());
            }

            if (opnd->isSrcRegRegion())
            {
                G4_Declare* topdcl = opnd->getTopDcl();
                if (topdcl != NULL)
                {
                    auto range_it = activeDefs.equal_range(topdcl->getDeclId());
                    if (range_it.first == range_it.second)
                    {
                        // No match found so move on to next src opnd
                        continue;
                    }

                    unsigned int lb = opnd->getLeftBound();
                    unsigned int rb = opnd->getRightBound();
                    unsigned int hs = getActualHStride(opnd->asSrcRegRegion());

                    auto start_it = range_it.second;
                    start_it--;
                    bool done = false;
                    for (auto it = start_it;
                        ;
                        )
                    {
                        G4_DstRegRegion* activeDst = (*it).second.second;

                        unsigned int lb_dst = activeDst->getLeftBound();
                        unsigned int rb_dst = activeDst->getRightBound();
                        unsigned int hs_dst = activeDst->getHorzStride();

                        if (lb_dst <= rb && rb_dst >= lb)
                        {
                            // Early return case
                            // This function is entered when an existing
                            // value is found in LVN table. startInst is
                            // the instruction that is expected to be
                            // optimized away. However, if it so happens
                            // that startInst defs a partial subset of
                            // data flowing in to a src operand, we can
                            // postpone doing DU for the BB. This helps
                            // large BBs with false starts for du
                            // computation.
                            if (activeDst == startInstDst &&
                                numEdgesAdded == 0)
                            {
                                if (!(lb_dst <= lb && rb_dst >= rb))
                                {
                                    duTablePopulated = false;
                                    return;
                                }
                            }

                            addUse(activeDst, curInst, i);
                            numEdgesAdded++;

                            if (activeDst->getInst()->getPredicate() == NULL &&
                                (lb_dst <= lb && rb_dst >= rb &&
                                (hs_dst == hs ||
                                    (hs_dst == 1 && hs == 0 && activeDst->getInst()->getExecSize() == g4::SIMD1))))
                            {
                                // Active def (in bottom-up order)
                                // fully defines use of current inst. So no
                                // need to analyze earlier appearing defs.
                                break;
                            }
                        }

                        if (done ||
                            it == range_it.first)
                        {
                            // Last match reached
                            break;
                        }
                        it--;
                    }
                }
            }
        }

        G4_DstRegRegion* dst = curInst->getDst();
        if (dst)
        {
            G4_Declare* curDstTopDcl = dst->getTopDcl();
            if (curDstTopDcl != NULL)
            {
                // Check if already an overlapping dst region is active
                auto range_it = activeDefs.equal_range(curDstTopDcl->getDeclId());
                for (auto it = range_it.first;
                    it != range_it.second;
                    )
                {
                    G4_DstRegRegion* activeDst = (*it).second.second;

                    unsigned int lb = dst->getLeftBound();
                    unsigned int rb = dst->getRightBound();
                    unsigned int hs = dst->getHorzStride();

                    unsigned int lb_dst = activeDst->getLeftBound();
                    unsigned int rb_dst = activeDst->getRightBound();
                    unsigned int hs_dst = activeDst->getHorzStride();

                    if ((lb <= lb_dst && rb >= rb_dst) &&
                        hs == hs_dst)
                    {
                        if (curInst->getPredicate() == NULL)
                        {
                            // Current dst completely overlaps
                            // earlier def so retire earlier
                            // active def.
                            auto prev_it = it;
                            it++;
                            activeDefs.erase(prev_it);
                            continue;
                        }
                    }

                    it++;
                }

                bool addValueToDU = false;
                if (addValue(curInst))
                {
                    addValueToDU = true;
                }

                if (addValueToDU ||
                    (activeDefs.find(curDstTopDcl->getDeclId()) != activeDefs.end()))
                {
                    // mov (8) V10(0,0):d     r0.0:d - 1
                    // shr (1) V10(0,1):d     0x1:d  - 2
                    // send ... V10 ...              - 3
                    //
                    // Since inst 1 is a valid LVN candidate it will be inserted
                    // in activeDefs table. Inst 2 is not an LVN candidate but
                    // it clobbers def of inst 1, hence we have to insert it in
                    // the LVN table too. If first instruction wasnt an LVN
                    // candidate by itself, we wouldnt insert it or inst 2 in
                    // active def table.
                    ActiveDef newActiveDef;
                    newActiveDef.first = curDstTopDcl;
                    newActiveDef.second = dst;
                    activeDefs.emplace(curDstTopDcl->getDeclId(), newActiveDef);
                }
            }
        }

        inst_it++;
    }
}

void LVN::invalidate()
{
    // invalidate all values cached in value table
    for (auto& bucket : lvnTable)
    {
        for (auto& lvnItem : bucket.second)
        {
            lvnItem->active = false;
        }
    }
}

void LVN::doLVN()
{
    bb->resetLocalIds();
    for (INST_LIST_ITER inst_it = bb->begin(), inst_end_it = bb->end();
        inst_it != inst_end_it;
        inst_it++)
    {
        G4_INST* inst = (*inst_it);
        bool negMatch = false;

        if (inst->isOptBarrier())
        {
            // disallow optimization across opt barrier
            invalidate();
            continue;
        }

        if (inst->getDst() == NULL)
        {
            continue;
        }

        Value value, oldValue;
        bool canNegate = false, isGlobal = false;
        G4_INST* lvnInst = NULL;
        LVNItemInfo* lvnItem = NULL;
        int64_t posVal = 0;
        bool addGlobalValueToTable = false;

        bool canAdd = addValue(inst);
        bool success = false;
        if (canAdd)
        {
            perInstValueCache.clear();

            // Compute value of current instruction
            // success is false when there is a float type-conversion mov
            // that we dont implement, eg :hf->:f interpretation.
            success = computeValue(inst, false, canNegate, isGlobal, posVal, false, value);
            value.inst = inst;
            oldValue = value;

            if (isGlobal || value.isValueEmpty())
            {
                if (isGlobal && !value.isValueEmpty())
                {
                    // If variable is global, we want to add it to LVN table.
                    addGlobalValueToTable = true;
                }

                // If a value is global or is empty we cannot eliminate
                // the definition just because another variable holds
                // same data. So the logic of replacing operands is
                // skipped with canAdd == false.
                canAdd = false;
            }

            if (canAdd && success)
            {
                // Check if value exists in table
                lvnItem = isValueInTable(value, false);

                // If value doesnt exist, search for value's
                // negative representation (only for imm).

                if (canNegate &&
                    lvnItem == NULL)
                {
                    computeValue(inst, true, canNegate, isGlobal, posVal, true, value);
                    value.inst = inst;
                    negMatch = true;

                    lvnItem = isValueInTable(value, true);
                }

                if (lvnItem != NULL)
                {
                    lvnInst = lvnItem->inst;
                    if (inst->getLocalId() - lvnInst->getLocalId() > LVN::MaxLVNDistance)
                    {
                        // do not do LVN to avoid register pressure increase
                        // removeRedef should get rid of this lvnInst later
                    }
                    else
                    {
                        auto dstRegionMatch = [](G4_DstRegRegion* dst1, G4_DstRegRegion* dst2)
                        {
                            return dst1->getType() == dst2->getType() && dst1->getRegOff() == dst2->getRegOff() &&
                                dst1->getSubRegOff() == dst2->getSubRegOff() && dst1->getHorzStride() == dst2->getHorzStride();

                        };
                        // if this and lvnInst has identical dst regions, we can replace partial uses as well
                        bool hasSameDstRegion = dstRegionMatch(inst->getDst(), lvnInst->getDst());
                        // How can duTablePopulated be false here? I don't understand the other comment???
                        if (duTablePopulated == false)
                        {
                            populateDuTable(inst_it);
                        }

                        bool removeInst = false;
                        if (duTablePopulated)
                        {
                            UseList uses;
                            bool defFound = getAllUses(inst, uses);
                            if (defFound)
                            {
                                bool canReplaceAllUses = canReplaceUses(inst_it, uses, lvnInst, negMatch, !hasSameDstRegion);

                                if (canReplaceAllUses)
                                {
                                    replaceAllUses(inst, negMatch, uses, lvnInst, hasSameDstRegion);
                                    removeInst = true;
                                }
                            }
                        }

                        if (removeInst)
                        {
                            INST_LIST_ITER prev_it = inst_it;
                            inst_it--;
                            bb->erase(prev_it);

                            numInstsRemoved++;
                            continue;
                        }
                    }
                }
            }
        }

        // Scan LVN table and remove all those var entries
        // that have same variable as current inst's dst.
        removeRedefs(inst);

        if (success &&
            (canAdd ||
                addGlobalValueToTable))
        {
            addValueToTable(inst, oldValue);
        }
    }
}

// This is a static method. It can be called to cleanup after any optimization.
unsigned int LVN::removeRedundantSamplerMovs(G4_Kernel& kernel, G4_BB* bb)
{
    // Remove all redundant writes to rx.2 where x is samplerHeader dcl
    auto samplerDcl = kernel.fg.builder->getBuiltinSamplerHeader();
    if (!samplerDcl)
        return 0;

    G4_INST* lastSamplerDclDef = nullptr;
    unsigned int numInstsRemoved = 0;

    for (auto instIt = bb->begin(); instIt != bb->end();)
    {
        auto inst = (*instIt);
        auto dst = inst->getDst();
        if (dst && dst->getTopDcl() == samplerDcl)
        {
            if (!lastSamplerDclDef)
            {
                // Seeing def for first time in BB
                lastSamplerDclDef = inst;
                ++instIt;
                continue;
            }
            else if (lastSamplerDclDef->opcode() == G4_mov &&
                inst->opcode() == G4_mov &&
                lastSamplerDclDef->getDst()->getType() == dst->getType() &&
                lastSamplerDclDef->getDst()->getLeftBound() == dst->getLeftBound() &&
                lastSamplerDclDef->getDst()->getRightBound() == dst->getRightBound() &&
                lastSamplerDclDef->getSrc(0)->isImm() &&
                inst->getSrc(0)->isImm() &&
                lastSamplerDclDef->getSrc(0)->asImm()->isEqualTo(inst->getSrc(0)->asImm()))
            {
                // Redundant mov found, erase it
                instIt = bb->erase(instIt);
                numInstsRemoved++;
                continue;
            }
            else
            {
                lastSamplerDclDef = inst;
            }
        }
        ++instIt;
    }

    return numInstsRemoved;
}

LVN::~LVN()
{
    for (auto d : toDtor)
    {
        d->~LVNItemInfo();
    }
}
