#include "live_variable_analysis.h"
#include "RD_subst.h"
#include "DataFlow/backward_data_flow.h"

#include <string>
#include <vector>
#include <map>
#include <set>
#include <list>
#include <unordered_map>
#include <algorithm>

using std::string;
using std::pair;
using std::vector;
using std::map;
using std::set;
using std::unordered_map;
using std::list;

using LIVE_VARIABLES::LiveDeadVarsForCall;

namespace SAPFOR
{
    bool BasicBlock::addLive(const map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>>& to_add, bool in) {
        std::map<SAPFOR::Argument*, std::vector<SAPFOR::BasicBlock*>>& current_set = (in ? live_in : live_out);
        std::map<SAPFOR::Argument*, std::vector<SAPFOR::BasicBlock*>>& opposite_set = (!in ? live_in : live_out);

        bool inserted = false;
        for (const auto& byNew : to_add)
        {
            const vector<SAPFOR::BasicBlock*>& add_in_live = byNew.second;
            vector<SAPFOR::BasicBlock*> new_in_live = {};

            auto current_set_iter = current_set.find(byNew.first);

            if (current_set_iter == current_set.end())
                new_in_live = add_in_live;
            else
                std::set_difference(add_in_live.begin(), add_in_live.end(), current_set_iter->second.begin(), current_set_iter->second.end(), std::inserter(new_in_live, new_in_live.begin()));

            auto opposite_set_iter = opposite_set.find(byNew.first);
            auto both_set_iter = live_inout.find(byNew.first);

            if (new_in_live.size() != 0)
            {
                inserted = true;

                bool current_set_has_argument = current_set_iter != current_set.end();
                bool opposite_set_has_argument = opposite_set_iter != opposite_set.end();
                bool both_set_has_argument = both_set_iter != live_inout.end();

                for (SAPFOR::BasicBlock* bb : new_in_live)
                {
                    bool insert_to_both = false;
                    if (opposite_set_has_argument)
                    {
                        auto& usages_from_opposite = opposite_set_iter->second;
                        auto find_bb_from_opposite = std::lower_bound(usages_from_opposite.begin(), usages_from_opposite.end(), bb);

                        if (find_bb_from_opposite != usages_from_opposite.end() && *find_bb_from_opposite == bb)
                        {
                            insert_to_both = true;
                            usages_from_opposite.erase(find_bb_from_opposite);

                            if (usages_from_opposite.size() == 0) {
                                opposite_set.erase(opposite_set_iter);
                                opposite_set_has_argument = false;
                            }
                        }
                    }

                    if (insert_to_both)
                    {
                        if (!both_set_has_argument) {
                            both_set_iter = live_inout.insert({ byNew.first, {} }).first;
                            both_set_has_argument = true;
                        }
                    }
                    else
                    {
                        if (!current_set_has_argument) {
                            current_set_iter = current_set.insert({ byNew.first, {} }).first;
                            current_set_has_argument = true;
                        }
                    }

                    vector<SAPFOR::BasicBlock*>& dest = insert_to_both ? both_set_iter->second : current_set_iter->second;

                    auto find_bb_from_dest = std::lower_bound(dest.begin(), dest.end(), bb);

                    if (find_bb_from_dest == dest.end() || *find_bb_from_dest != bb)
                        dest.insert(find_bb_from_dest, bb);
                }
            }
        }

        return inserted;
    }

    bool BasicBlock::removeLive(SAPFOR::Argument* to_remove, bool in)
    {
        std::map<SAPFOR::Argument*, std::vector<SAPFOR::BasicBlock*>>& current_set = (in ? live_in : live_out);
        std::map<SAPFOR::Argument*, std::vector<SAPFOR::BasicBlock*>>& opposite_set = (!in ? live_in : live_out);

        bool removed = false;

        removed |= (current_set.erase(to_remove) != 0);

        auto it = live_inout.find(to_remove);

        if (it != live_inout.end())
        {
            auto& dest = opposite_set[to_remove];
            for (SAPFOR::BasicBlock* bb : it->second)
            {
                auto find_bb_from_dest = std::lower_bound(dest.begin(), dest.end(), bb);

                if (find_bb_from_dest == dest.end() || *find_bb_from_dest != bb)
                    dest.insert(find_bb_from_dest, bb);
            }

            live_inout.erase(to_remove);
            removed = true;
        }

        return removed;
    }

    map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>> BasicBlock::getLive(bool in) const {
        auto& current_set = in ? live_in : live_out;

        map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>> res;

        for (auto& by_source : { current_set, live_inout })
        {
            for (auto& by_pair : by_source)
            {
                auto& dest = res[by_pair.first];
                auto dest_copy = dest;

                dest.resize(dest_copy.size() + by_pair.second.size());

                set_union(dest_copy.begin(), dest_copy.end(), by_pair.second.begin(), by_pair.second.end(), dest.begin());
            }
        }

        return res;
    }

    void BasicBlock::compressLives() {
        for (auto& bySrc : { &live_in, &live_out, &live_inout })
            for (auto& byArg : *bySrc)
                byArg.second.shrink_to_fit();
    }
}

bool LiveDeadVarsForCall::tryInsert(set<SAPFOR::BasicBlock*>& dest, SAPFOR::BasicBlock* b)
{
    if (b == block || dest.find(block) == dest.end())
    {
        dest.insert(b);
        return true;
    }

    return false;
}

LiveDeadVarsForCall::LiveDeadVarsForCall(FuncInfo* f, SAPFOR::BasicBlock* b, const vector<SAPFOR::Argument*>& p)
{
    block = b;
    func = f;

    int param_size = p.size();
    params = vector<SAPFOR::Argument*>(param_size, NULL);

    for (int i = 0; i < param_size; i++)
        if (f->funcParams.isArgOut(i))
            params[i] = p[i];
}

void LiveDeadVarsForCall::make_live(SAPFOR::Argument* arg, SAPFOR::BasicBlock* b)
{
    if (arg->getMemType() == SAPFOR::CFG_MEM_TYPE::COMMON_ || arg->getMemType() == SAPFOR::CFG_MEM_TYPE::MODULE_)
    {
        if (commons_dead_after.find(arg) == commons_dead_after.end())
            tryInsert(commons_live_after[arg], b);
    }

    auto it = find(params.begin(), params.end(), arg);
    if (it != params.end())
    {
        int idx = it - params.begin();
        if (dead_after.find(idx) == dead_after.end())
            tryInsert(live_after[idx], b);
    }
}

void LiveDeadVarsForCall::make_dead(SAPFOR::Argument* arg)
{
    if (arg->getMemType() == SAPFOR::CFG_MEM_TYPE::COMMON_ || arg->getMemType() == SAPFOR::CFG_MEM_TYPE::MODULE_)
    {
        if (commons_live_after.find(arg) == commons_live_after.end())
            commons_dead_after.insert(arg);
    }

    auto it = find(params.begin(), params.end(), arg);
    if (it != params.end())
    {
        int idx = it - params.begin();
        if (live_after.find(idx) == live_after.end())
            dead_after.insert(idx);
    }
}

void LiveDeadVarsForCall::updateFromOut()
{
    for (const auto& p : block->getLiveOut())
        for (auto b : p.second)
            make_live(p.first, b);
}

static bool getLiveDead(const vector<SAPFOR::Argument*>& params, const string& func_name,
                        set<SAPFOR::Argument*>& live, set<SAPFOR::Argument*>& dead);

static void buildUseDef(SAPFOR::BasicBlock* block, set<SAPFOR::Argument*>& use, set<SAPFOR::Argument*>& def,
                        vector<SAPFOR::Argument*>& formal_parameters, vector<LiveDeadVarsForCall>& fcalls,
                        const map<string, FuncInfo*>& funcByName, bool interprocedural);

class LiveVarAnalysisNode : public DataFlowAnalysisNode<map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>>> {
private:
    set<SAPFOR::Argument*> live, dead;
public:
    map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>> getIn()
    {
        return getBlock()->getLiveOut();
    }

    map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>> getOut()
    {
        return getBlock()->getLiveIn();
    }

    bool addIn(const map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>>& data)
    {
        return getBlock()->addLiveOut(data);
    }

    bool addOut(const map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>>& data)
    {
        return getBlock()->addLiveIn(data);
    }

    DATA_FLOW_UPD_STATUS forwardData(const map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>>& data)
    {
        bool inserted = false;

        for (const auto& byArg : data)
            if (live.find(byArg.first) == live.end() && dead.find(byArg.first) == dead.end())
                inserted |= getBlock()->addLiveIn({ byArg });

        return inserted ? DATA_FLOW_UPD_STATUS::PROPAGATED : DATA_FLOW_UPD_STATUS::NO_CHANGE;
    }

    LiveVarAnalysisNode(SAPFOR::BasicBlock* block, vector<SAPFOR::Argument*>& formal_parameters,
                        vector<LiveDeadVarsForCall>& fcalls, const map<string, FuncInfo*>& funcByName)
    {
        setBlock(block);

        buildUseDef(getBlock(), live, dead, formal_parameters, fcalls, funcByName, true);

        for (SAPFOR::Argument* arg : live)
            getBlock()->addLiveIn({ { arg, { getBlock() } } });
    }
};

class LiveVarAnalysis : public BackwardDataFlowAnalysis<LiveVarAnalysisNode> {
protected:
    vector<SAPFOR::Argument*>& formal_parameters;
    vector<LiveDeadVarsForCall>& fcalls;
    const map<string, FuncInfo*>& funcByName;

    LiveVarAnalysisNode* createNode(SAPFOR::BasicBlock* block) override {
        return new LiveVarAnalysisNode(block, formal_parameters, fcalls, funcByName);
    }
public:
    LiveVarAnalysis(vector<SAPFOR::Argument*>& formal_parameters, vector<LiveDeadVarsForCall>& fcalls,
        const map<string, FuncInfo*>& funcByName) : formal_parameters(formal_parameters), fcalls(fcalls), funcByName(funcByName)
    { }
};

void getUseDefForInstruction(SAPFOR::BasicBlock* block, SAPFOR::Instruction* instr,
                             set<SAPFOR::Argument*>& use, set<SAPFOR::Argument*>& def,
                             vector<SAPFOR::Argument*>& formal_parameters, vector<LiveDeadVarsForCall>& fcalls,
                             vector<SAPFOR::Argument*>& arg_stack, int& arg_stack_index, int& arg_stack_size,
                             bool& last_stack_op,
                             string& fName, const map<string, FuncInfo*>& funcByName, bool interprocedural)
{
    for (auto arg : { instr->getArg1(), instr->getArg2(), instr->getResult() })
        if (arg && arg->getMemType() == SAPFOR::CFG_MEM_TYPE::FUNC_PARAM_)
            formal_parameters[getParamIndex(arg, formal_parameters.size())] = arg;

    last_stack_op = false;
    SAPFOR::Argument* res_arg = NULL;

    static const set<SAPFOR::CFG_OP> skip = { SAPFOR::CFG_OP::ENTRY };
    SAPFOR::CFG_OP instr_operation = instr->getOperation();
    if (instr_operation == SAPFOR::CFG_OP::F_CALL ||
        instr_operation == SAPFOR::CFG_OP::STORE ||
        instr_operation == SAPFOR::CFG_OP::LOAD)
    {
        res_arg = (instr_operation == SAPFOR::CFG_OP::STORE ? instr->getArg1() : instr->getResult());
        if(instr_operation != SAPFOR::CFG_OP::F_CALL)
            use.insert(instr_operation != SAPFOR::CFG_OP::STORE ? instr->getArg1() : instr->getResult());

        arg_stack_size = stoi(instr->getArg2()->getValue());

        arg_stack.clear();
        arg_stack.resize(arg_stack_size);

        arg_stack_index = arg_stack_size - 1;

        if(instr_operation == SAPFOR::CFG_OP::F_CALL)
            fName = instr->getArg1()->getValue();
    }
    else if (hasStoreStructure(instr_operation))
    {
        res_arg = instr->getArg1();
        set<SAPFOR::Argument*> instr_args = { instr->getResult(), instr->getArg2() };
        use.insert(instr_args.begin(), instr_args.end());
    }
    else if (instr_operation == SAPFOR::CFG_OP::PARAM || instr_operation == SAPFOR::CFG_OP::REF)
    {
        arg_stack[arg_stack_index--] = instr->getArg1();
    }
    else if (skip.find(instr_operation) == skip.end())
    {
        //default
        res_arg = instr->getResult();
        std::set<SAPFOR::Argument*> intr_args = { instr->getArg1(), instr->getArg2() };
        use.insert(intr_args.begin(), intr_args.end());
    }
    else
    {
        //skip
        return;
    }

    if (arg_stack_index < 0) {
        if(instr_operation == SAPFOR::CFG_OP::PARAM || instr_operation == SAPFOR::CFG_OP::F_CALL)
        {
            auto func_it = funcByName.find(fName);
            if (interprocedural && func_it != funcByName.end())
            {
                fcalls.push_back(LiveDeadVarsForCall(func_it->second, block, arg_stack));

                auto r_it = fcalls.rbegin();
                auto r_end = fcalls.rend();

                for (auto e : def)
                    r_it->make_dead(e);

                for (auto e : use)
                    r_it->make_live(e, block);
            }

            set<SAPFOR::Argument*> make_live, make_dead;
            if (fName == "_READ")
                def.insert(arg_stack.begin(), arg_stack.end());
            else if (interprocedural && getLiveDead(arg_stack, fName, make_live, make_dead))
            {
                use.insert(make_live.begin(), make_live.end());
                def.insert(make_dead.begin(), make_dead.end());
            }
            else if (func_it != funcByName.end())
            {
                int arg_num = arg_stack.size();
                for (int i = 0; i < arg_num; i++)
                {
                    if(func_it->second->funcParams.isArgOut(i))
                        def.insert(arg_stack[i]);

                    if (func_it->second->funcParams.isArgIn(i))
                        use.insert(arg_stack[i]);
                }
            }
            else
                use.insert(arg_stack.begin(), arg_stack.end());

            fName = "";
        }
        else 
        {
            use.insert(arg_stack.begin(), arg_stack.end());
        }

        arg_stack_index = 0;
        arg_stack_size = 0;

        arg_stack.clear();

        last_stack_op = true;
    }

    if (res_arg)
        def.insert(res_arg);
}

static void updateUseDefForInstruction(SAPFOR::BasicBlock* block, SAPFOR::Instruction* instr,
                                       set<SAPFOR::Argument*>& use, set<SAPFOR::Argument*>& def,
                                       vector<SAPFOR::Argument*>& formal_parameters, vector<LiveDeadVarsForCall>& fcalls,
                                       vector<SAPFOR::Argument*>& arg_stack, int& arg_stack_index, int& arg_stack_size,
                                       string& fName, const map<string, FuncInfo*>& funcByName, bool interprocedural)
{
    set<SAPFOR::Argument*> res, args;
    bool last_stack_op;
    getUseDefForInstruction(block, instr,
        args, res,
        formal_parameters, fcalls,
        arg_stack, arg_stack_index, arg_stack_size,
        last_stack_op,
        fName, funcByName,
        interprocedural
    );

    for (auto e : res)
    {
        if (e && e->getType() == SAPFOR::CFG_ARG_TYPE::VAR)
        {
            def.insert(e);
            use.erase(e);
        }
    }

    for (auto e : args)
    {
        if (e && e->getType() == SAPFOR::CFG_ARG_TYPE::VAR)
        {
            use.insert(e);
            def.erase(e);
        }
    }
}

//Build use and def sets of block. Result are stored in use and def
static void buildUseDef(SAPFOR::BasicBlock* block, set<SAPFOR::Argument*>& use, set<SAPFOR::Argument*>& def,
                        vector<SAPFOR::Argument*>& formal_parameters, vector<LiveDeadVarsForCall>& fcalls,
                        const map<string, FuncInfo*>& funcByName, bool interprocedural)
{
    vector<SAPFOR::Argument*> arg_stack;
    int arg_stack_index = 0, arg_stack_size = 0;
    string fName;

    const auto& instructions = block->getInstructions();
    auto ir_block_it = instructions.rbegin(), ir_block_end = instructions.rend();

    for (; ir_block_it != ir_block_end; ir_block_it++)
    {
        updateUseDefForInstruction(block, (*ir_block_it)->getInstruction(),
            use, def,
            formal_parameters, fcalls,
            arg_stack, arg_stack_index, arg_stack_size,
            fName, funcByName,
            interprocedural
        );
    }
}

// prints info about live variables
void doDumpLive(const map<FuncInfo*, vector<SAPFOR::BasicBlock*>>& CFGraph_for_project)
{
    for (const auto& byFunc : CFGraph_for_project)
    {
        __spf_print(1, "============================================\n");
        __spf_print(1, "Live variables '%s' function\n", byFunc.first->funcName.c_str());
        __spf_print(1, "============================================\n");
        for (auto byBB : byFunc.second)
        {
            __spf_print(1, "[BB %d]\n", byBB->getNumber());
            __spf_print(1, "  IN:\n");
            for (const auto& live : byBB->getLiveIn())
            {
                __spf_print(1, "    %s:", live.first->getValue().c_str());
                for (auto use : live.second)
                    __spf_print(1, " %d", use->getNumber());
                __spf_print(1, "\n");
            }

            __spf_print(1, "  OUT:\n");
            for (const auto& live : byBB->getLiveOut())
            {
                __spf_print(1, "    %s:", live.first->getValue().c_str());
                for (auto use : live.second)
                    __spf_print(1, " %d", use->getNumber());
                __spf_print(1, "\n");
            }
        }
    }
}

// sets for the next function
static map<string, pair<set<int>, set<SAPFOR::Argument*>>> live_by_func, dead_by_func;

// fill sets of arguments wich becomes live or dead after call of this function
static void fillLiveDeadArgs(const FuncInfo* func, const vector<SAPFOR::BasicBlock*>& blocks)
{
    if (blocks.size() == 0)
        return;

    SAPFOR::BasicBlock* entrypoint = NULL;
    int entrypoint_first_instr = 0;
    set<SAPFOR::BasicBlock*> exits = {};

    for (auto block : blocks)
    {
        if (block->getNext().size() == 0)
            exits.insert(block);

        if (block->getInstructions().front()->isHeader())
        {
            if (!entrypoint || block->getInstructions()[0]->getNumber() < entrypoint_first_instr)
            {
                entrypoint_first_instr = block->getInstructions()[0]->getNumber();
                entrypoint = block;
            }
        }
    }

    if (!entrypoint)
        printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

    set<int> live, dead;
    set<SAPFOR::Argument*> common_live, common_dead;

    for (const auto& arg : entrypoint->getLiveIn())
    {
        switch (arg.first->getMemType())
        {
        case SAPFOR::CFG_MEM_TYPE::COMMON_:
        case SAPFOR::CFG_MEM_TYPE::MODULE_:
            common_live.insert(arg.first);
            break;
        case SAPFOR::CFG_MEM_TYPE::FUNC_PARAM_:
        {
            int num = getParamIndex(arg.first, func->funcParams.countOfPars);
            if (func->funcParams.isArgIn(num))
                live.insert(num);
            break;
        }
        }
    }

    for (auto byExit : exits)
    {
        for (const auto& byRd : byExit->getRD_Out())
        {
            auto out_arg = byRd.first;
            if (!(byRd.second.size() == 1 && *(byRd.second.begin()) < 0))
            {
                switch (out_arg->getMemType())
                {
                case SAPFOR::CFG_MEM_TYPE::COMMON_:
                case SAPFOR::CFG_MEM_TYPE::MODULE_:
                    if (!common_live.count(out_arg))
                        common_dead.insert(out_arg);
                    break;
                case SAPFOR::CFG_MEM_TYPE::FUNC_PARAM_:
                {
                    int num = getParamIndex(out_arg, func->funcParams.countOfPars);

                    if (!live.count(num) && func->funcParams.isArgOut(num))
                        dead.insert(num);
                    break;
                }
                }
            }
        }
    }

    live_by_func[func->funcName] = { live, common_live };
    dead_by_func[func->funcName] = { dead, common_dead };
}

// unite global arguments and actual parameters with given indexes for function
// stores the result in the last argument
bool joinGlobalsWithParameters(const vector<SAPFOR::Argument*>& params,
    const map<string, pair<set<int>, set<SAPFOR::Argument*>>>& params_and_globals,
    const string& func_name, set<SAPFOR::Argument*>& result)
{
    auto globals_it = params_and_globals.find(func_name);

    if (globals_it == params_and_globals.end())
        return false;

    const auto& param_indexes = globals_it->second.first;
    const auto& globals = globals_it->second.second;

    int params_size = params.size();

    for (int idx : param_indexes)
    {
        if (idx < params_size) {
            if (params[idx] && params[idx]->getType() == SAPFOR::CFG_ARG_TYPE::VAR)
                result.insert(params[idx]);
        }
        else
            printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
    }

    result.insert(globals.begin(), globals.end());
    return true;
}

// fill sets of arguments wich becomes live or dead after call with parameters params
static bool getLiveDead(const vector<SAPFOR::Argument*>& params, const string& func_name,
    set<SAPFOR::Argument*>& live, set<SAPFOR::Argument*>& dead)
{
    return joinGlobalsWithParameters(params, live_by_func, func_name, live) &&
        joinGlobalsWithParameters(params, dead_by_func, func_name, dead);
}

// entrypoint for live variable analysis pass
void runLiveVariableAnalysis(const map<FuncInfo*, vector<SAPFOR::BasicBlock*>>& CFGraph_for_project)
{
    /*
        Here we assume that there is no recursive (explicit or implicit) calls.
        So it is easy to process CALL instruction: just use live_in set of first block
        for this function (that has already been built).
    */

    map<FuncInfo*, set<FuncInfo*>> callDeps;
    map<string, FuncInfo*> funcByName;

    for (auto& byFunc : CFGraph_for_project)
    {
        callDeps[byFunc.first].insert(byFunc.first->callsFromV.begin(), byFunc.first->callsFromV.end());
        funcByName[byFunc.first->funcName] = byFunc.first;
    }

    vector<set<FuncInfo*>> scc;
    vector<set<FuncInfo*>> callLvls = groupByCallDependencies(callDeps, scc);

    map<string, LiveVarAnalysis*> func_to_analysis_object;
    map<string, vector<SAPFOR::Argument*>> func_to_parameters;

    list<vector<LiveDeadVarsForCall>> live_for_fcalls;

    //TODO: take into account ssc structure
    // main stage
    for (auto& byLvl : callLvls)
    {
        live_for_fcalls.push_front({});
        auto& curr_fcalls = live_for_fcalls.front();

        for (auto& byFunc : byLvl)
        {
            auto itCFG = CFGraph_for_project.find(byFunc);
            if (itCFG == CFGraph_for_project.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);


            auto& params = func_to_parameters[byFunc->funcName] = vector<SAPFOR::Argument*>(byFunc->funcParams.countOfPars, NULL);

            LiveVarAnalysis* analysis_object = (func_to_analysis_object[byFunc->funcName] = new LiveVarAnalysis(params, curr_fcalls, funcByName));
            analysis_object->fit(itCFG->second);
            analysis_object->analyze();

            fillLiveDeadArgs(byFunc, itCFG->second);
        }
    }

    // interprocedural analysis
    for (auto& calls_vector : live_for_fcalls)
    {
        map<FuncInfo*, LiveDeadVarsForCall> assembled_fcalls;
        for (auto& call : calls_vector)
        {
            call.updateFromOut();
            call.params.clear();
            call.commons_dead_after.clear();
            call.dead_after.clear();

            auto it = assembled_fcalls.find(call.func);
            if (it == assembled_fcalls.end())
                it = assembled_fcalls.insert({ call.func, LiveDeadVarsForCall(call.func, call.block, {}) }).first;

            for (const auto& p : call.live_after)
                it->second.live_after[p.first].insert(p.second.begin(), p.second.end());

            for (const auto& p : call.commons_live_after)
                it->second.commons_live_after[p.first].insert(p.second.begin(), p.second.end());
        }

        for (const auto& func : assembled_fcalls)
        {
            auto func_it = func_to_analysis_object.find(func.first->funcName);
            if (func_it == func_to_analysis_object.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            auto param_it = func_to_parameters.find(func.first->funcName);
            if (param_it == func_to_parameters.end())
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            const vector<SAPFOR::Argument*>& params = param_it->second;
            auto params_begin = params.begin(), params_end = params.end();

            map<SAPFOR::Argument*, set<SAPFOR::BasicBlock*>> live_after = func.second.commons_live_after;

            for (auto curr = params_begin; curr < params_end; curr++)
            {
                if (*curr)
                {
                    const auto& live_param_it = func.second.live_after.find(curr - params_begin);
                    if (live_param_it != func.second.live_after.end())
                        live_after[*curr].insert(live_param_it->second.begin(), live_param_it->second.end());
                }
            }

            set<LiveVarAnalysisNode*> exits;
            int max_cnt = LiveVarAnalysisNode::getStartCounter();
            for (auto block : func_it->second->getNodes())
            {
                if (block->getBlock()->getNext().size() == 0)
                    exits.insert(block);
                if (block->getInCnt() > max_cnt)
                    max_cnt = block->getInCnt();
            }

            max_cnt++;

            for (auto exit : exits)
            {
                for (const auto& byArg : live_after)
                {
                    map<SAPFOR::Argument*, vector<SAPFOR::BasicBlock*>> converted;
                    converted[byArg.first] = vector<SAPFOR::BasicBlock*>(byArg.second.begin(), byArg.second.end());
 
                    if (exit->addIn(converted))
                    {
                        exit->setInCnt(max_cnt);
                        if (exit->forwardData(converted) != DATA_FLOW_UPD_STATUS::NO_CHANGE)
                            exit->setOutCnt(max_cnt);
                    }
                }
            }

            // now we can update live sets in all blocks
            func_it->second->analyze();
        }
    }

    for (const auto& byFunc : func_to_analysis_object)
        delete byFunc.second;

    for (auto& byFunc : CFGraph_for_project)
        for (auto& byBlock : byFunc.second)
            byBlock->compressLives();

    live_by_func.clear();
    dead_by_func.clear();
}