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

Copyright (c) 2017 Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


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

#include "Compiler/CISACodeGen/LivenessAnalysis.hpp"
#include "Compiler/CISACodeGen/ShaderCodeGen.hpp"
#include "Compiler/CISACodeGen/LiveVars.hpp"
#include "Compiler/CodeGenPublic.h"
#include "Compiler/IGCPassSupport.h"
#include "common/debug/Debug.hpp"
#include "common/igc_regkeys.hpp"

#include "common/LLVMWarningsPush.hpp"
#include "llvm/ADT/SparseBitVector.h"
#include <llvm/IR/CFG.h>
#include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/SetVector.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/Support/MathExtras.h>
#include "common/LLVMWarningsPop.hpp"

using namespace llvm;
using namespace IGC;

char LivenessAnalysis::ID = 0;

std::string LivenessAnalysis::getllvmValueName(Value *V)
{
    if (V->hasName())
    {
        return V->getName();
    }
    // For unamed value, try the following:
    std::string Str;
    raw_string_ostream aOS(Str);
    aOS << *V;
    Str = aOS.str();
    // Name starting with %
    int bid = Str.find_first_of('%');
    int end = Str.find_first_of(' ', bid);
    std::string vname = Str.substr(bid, end - bid);
    return vname;
}

// Register pass to igc-opt
#define PASS_FLAG "igc-livenessanalysis"
#define PASS_DESCRIPTION "Calculate liveness based on LiveVars"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS true
IGC_INITIALIZE_PASS_BEGIN(LivenessAnalysis, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
//IGC_INITIALIZE_PASS_DEPENDENCY(LiveVarsAnalysis)
IGC_INITIALIZE_PASS_END(LivenessAnalysis, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)

// runOnFunction set up Map only. calculate() computes Liveness.
bool LivenessAnalysis::runOnFunction(Function &F)
{
    m_F = &F;

    // Pre-allocate memory to avoid many small alocations.
    // todo: reuse it from LiveVars
    size_t nVals = m_F->arg_size();
    for (auto &BB : *m_F) {
        nVals += BB.size();
    }

    // We will allocate a bit more (~10%) in case it needs expansion.
    // Also, as llvm::DenseMap will resize if the Map's capacity is 75% full,
    // allocate even more to avoid such automatic resizing.
    uint32_t mapCap1 = int_cast<uint32_t>((size_t)(nVals * 1.40f));
    uint32_t vecCap1 = int_cast<uint32_t>((size_t)(nVals * 1.10f));
    ValueIds.grow(mapCap1);
    IdValues.reserve(vecCap1);

    initValueIds();

    return false;
}

LivenessAnalysis::~LivenessAnalysis()
{
    clear();
}

void LivenessAnalysis::clear()
{
    KillInsts.clear();
    ValueIds.clear();
    IdValues.clear();
    BBLiveIns.clear();

    delete m_LV;
    m_LV = nullptr;
}

void LivenessAnalysis::initValueIds()
{
    int ix = 0;
    for (Function::arg_iterator AI = m_F->arg_begin(), AE = m_F->arg_end();
        AI != AE; ++AI)
    {
        Value* Val = &*AI;
        ValueIds.insert(std::make_pair(Val, ix));
        IdValues.push_back(Val);
        ++ix;
    }

    for (inst_iterator II = inst_begin(m_F), IE = inst_end(m_F);
        II != IE; ++II)
    {
        Instruction *Inst = &*II;
        ValueIds.insert(std::make_pair(Inst, ix));
        IdValues.push_back(Inst);
        ++ix;
    }
}

void LivenessAnalysis::setLiveIn(BasicBlock *BB, Value *V)
{
    ValueToIntMap::iterator I = ValueIds.find(V);
    if (I == ValueIds.end())
    {
        return;
    }
    int id = I->second;
    setLiveIn(BB, id);
}

inline void LivenessAnalysis::setLiveIn(BasicBlock *BB, int ValueID)
{
    SBitVector &BV = BBLiveIns[BB];
    BV.set(ValueID);
}

inline void LivenessAnalysis::setKillInsts(Value *V, Instruction *kill)
{
    ValueVec& VS = KillInsts[kill];
    VS.push_back(V);
}

bool LivenessAnalysis::isInstLastUseOfValue(Value *V, Instruction *I)
{
    ValueVec& VS = KillInsts[I];
    for (int i = 0, e = (int)VS.size(); i < e; ++I)
    {
        if (VS[i] == V)
        {
            return true;
        }
    }
    return false;
}

bool LivenessAnalysis::isBefore(Instruction *A, Instruction *B)
{
    assert(A->getParent() == B->getParent() &&
           "A and B must be within the same BB!");

    int nA = (int)getDistance(A);
    int nB = (int)getDistance(B);
    return nA < nB;
}

void LivenessAnalysis::calculate(Function *F)
{
    // If m_LV is not nullptr, it means that the full liveness has been
    // computed. No need to recompute and just return.
    if (m_LV)
    {
        return;
    }

    // Since we re-calculate, make sure to begin with a clean
    // state. Note that ValueIds are not cleared here, as we
    // want to make sure ValueIds are valid as long as this
    // analysis is valid.  If we re-calculate value ids, a value
    // might be assigned a different id than the one assigned
    // by runOnFunction.
    KillInsts.clear();
    BBLiveIns.clear();

    m_F = F;
    m_WIA = getAnalysisIfAvailable<WIAnalysis>();

    // todo: might use LiveVars as a pass
    m_LV = new LiveVars();
    m_LV->Calculate(F, m_WIA);


    // Pre-allocate memory to avoid many small alocations.
    size_t nVals = IdValues.size();

    // We will allocate a bit more (~10%) in case it needs expansion.
    // Also, as llvm::DenseMap will resize if the Map's capacity is 75% full,
    // allocate even more to avoid such automatic resizing.
    uint32_t mapCap1 = int_cast<uint32_t>((size_t)(nVals * 1.40f));
    uint32_t mapCap2 = int_cast<uint32_t>((size_t)(m_F->size() * 1.40f));
    BBLiveIns.grow(mapCap2);
    KillInsts.grow(mapCap1);

    for (LiveVars::iterator LVI = m_LV->begin(), LVE = m_LV->end();
         LVI != LVE; ++LVI)
    {
        Value *V = LVI->first;
        LiveVars::LVInfo* lvi = LVI->second;
        ValueToIntMap::iterator VI = ValueIds.find(V);
        if (VI == ValueIds.end())
        {
            // Only value in ValueIds are considered.
            continue;
        }
        int valID = VI->second;

        // V is either an instruction or an argument. If defBB is nullptr,
        // V is an argument; otherwise, it is the defining BB.
        BasicBlock *defBB = nullptr;
        if (Instruction *defInst = dyn_cast<Instruction>(V))
        {
            defBB = defInst->getParent();
        }

        for (SmallPtrSet<BasicBlock*, 8>::iterator II = lvi->AliveBlocks.begin(),
                                                   IE = lvi->AliveBlocks.end();
             II != IE; ++II)
        {
            BasicBlock *BB = *II;
            setLiveIn(BB, valID);
        }

        for (std::vector<Instruction*>::iterator II = lvi->Kills.begin(),
                                                 IE = lvi->Kills.end();
             II != IE; ++II)
        {
            Instruction *inst = *II;
            setKillInsts(V, inst);

            // If inst's BB isn't "V"'s defBB, V must be live into this BB.
            // This condition check also works when "V" is an argument.
            BasicBlock *useBB = inst->getParent();
            if (defBB != useBB)
            {
                setLiveIn(useBB, valID);
            }
        }
    }

    if (IGC_IS_FLAG_ENABLED(EnableLivenessDump))
    {
        print(errs());
    }
}

void LivenessAnalysis::print_livein(raw_ostream& OS, BasicBlock *BB)
{
    SBitVector& BitVec = BBLiveIns[BB];
    OS << "    Live-In-Values (#values = " << BitVec.count() << " ):\n";

    SBitVector::iterator I, E;
    int nVals = 0;
    for (I = BitVec.begin(), E = BitVec.end(); I != E; ++I)
    {
        int id = *I;
        Value *V = IdValues[id];
        assert(V && "Value should be in Value Map!");
        if (nVals == 0) {
            OS << "      ";
        }
        if (V->hasName())
        {
            OS << V->getName() << ",  ";
        }
        else
        {
            OS << getllvmValueName(V) << ",  ";
        }
        ++nVals;
        if (nVals == 8)
        {
            // 8 values per line
            OS << "\n";
            nVals = 0;
        }
    }
    OS << "\n\n";
}

void LivenessAnalysis::print(raw_ostream& OS)
{
    if (!m_F) return;

    std::stringstream ss;
    ss << "LivenessAnalysis: " << m_F->getName().str();
    Debug::Banner(OS, ss.str());

    for (Function::iterator I = m_F->begin(), E = m_F->end(); I != E; ++I) {
        BasicBlock *BB = &*I;
        OS << "BB:";
        if (BB->hasName())
            OS << " " << BB->getName();
        OS << "       ; preds =";
        bool isFirst = true;
        for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE; ++PI) {
            BasicBlock *pred = *PI;
            OS << ((isFirst) ? " " : ", ");
            OS << (pred->hasName() ? pred->getName() : "Unamed(wrong)");
            isFirst = false;
        }
        OS << "\n";
        print_livein(OS, BB);
        for (BasicBlock::iterator it = BB->begin(), ie = BB->end(); it != ie; ++it) {
            Instruction *I = &*it;
            OS << *I << '\n';
        }
        OS << "\n";
    }
}

#if defined( _DEBUG )

void LivenessAnalysis::dump()
{
    print(dbgs());

}

void LivenessAnalysis::dump_livein(BasicBlock *BB)
{
    print_livein(dbgs(), BB);
}

void LivenessAnalysis::dump_livein()
{
    if (!m_F) {
        return;
    }

    for (auto& BI : *m_F) {
        BasicBlock& BB = BI;
        print_livein(dbgs(), &BB);
    }
}

#endif