#include "live_variable_analysis.h"
#include "RD_subst.h"

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

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

namespace SAPFOR
{
    bool BasicBlock::addLive(const map<SAPFOR::Argument*, set<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 set<SAPFOR::BasicBlock*>& add_in_live = byNew.second;
            set<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;
    };

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

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

        for (auto& by_source : { current_set, live_inout })
            for (auto& by_pair : by_source)
                res[by_pair.first].insert(by_pair.second.begin(), by_pair.second.end());

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

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

        return false;
    }

public:
    FuncInfo* func;
    
    map<int, set<SAPFOR::BasicBlock*>> live_after;
    set<int> dead_after;

    map<SAPFOR::Argument*, set<SAPFOR::BasicBlock*>> commons_live_after;
    set<SAPFOR::Argument*> commons_dead_after;

    vector<SAPFOR::Argument*> params;
    SAPFOR::BasicBlock* block;

    fcall(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 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 make_dead(SAPFOR::Argument* arg)
    {
        if (arg->getMemType() == SAPFOR::CFG_MEM_TYPE::COMMON_ || arg->getMemType() == SAPFOR::CFG_MEM_TYPE::MODULE_)
            commons_dead_after.insert(arg);

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

    void 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<fcall>& fcalls, 
                        const map<string, FuncInfo*>& funcByName);

enum
{
    CNT_NOTINIT = 0
};

struct BasicBlockNode
{
    SAPFOR::BasicBlock* bb;
    set<SAPFOR::Argument*> live, dead;
    int in_cnt, out_cnt;
    set<BasicBlockNode*> next_blocks;

    set<vector<BasicBlockNode*>::reverse_iterator> rollback;
    set<vector<BasicBlockNode*>::reverse_iterator> ignore_rollback;

    BasicBlockNode(SAPFOR::BasicBlock* block, vector<SAPFOR::Argument*>& formal_parameters,
                   vector<fcall>& fcalls, const map<string, FuncInfo*>& funcByName)
    {
        bb = block;
        out_cnt = in_cnt = CNT_NOTINIT;

        buildUseDef(bb, live, dead, formal_parameters, fcalls, funcByName);
        for (SAPFOR::Argument* arg : live)
            bb->addLiveIn({ { arg, { bb } } });

        rollback = {};
        ignore_rollback = {};
        next_blocks = {};
    }

    void updateLive()
    {
        bool in_changed = false, out_changed = false;
        int in_max_cnt = CNT_NOTINIT, out_max_cnt = CNT_NOTINIT;
        for (auto next : next_blocks)
        {
            if (out_cnt < next->in_cnt)
            {
                for (const auto& byArg : next->bb->getLiveIn())
                {
                    bool inserted = bb->addLiveOut({ byArg });
                    out_changed |= inserted;

                    if (inserted)
                    {
                        if (next->in_cnt > out_max_cnt)
                            out_max_cnt = next->in_cnt;

                        if (live.find(byArg.first) == live.end() && dead.find(byArg.first) == dead.end())
                        {
                            inserted = bb->addLiveIn({ byArg });
                            if (inserted && next->in_cnt > in_max_cnt)
                            {
                                in_max_cnt = next->in_cnt;
                                in_changed = true;
                            }
                        }
                    }
                }
            }
        }

        bool was_notinit = (in_cnt == CNT_NOTINIT);
        
        if (in_max_cnt != CNT_NOTINIT)
            in_cnt = in_max_cnt;

        if (out_max_cnt != CNT_NOTINIT)
            out_cnt = out_max_cnt;

        // TODO: fix counter overflow
        if (was_notinit)
        {
            out_cnt++;
            in_cnt++;
        }
    }
    
    bool newerThan(const BasicBlockNode* block) const { return in_cnt > block->out_cnt; }
};

//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<fcall>& fcalls, 
                        const map<string, FuncInfo*>& funcByName)
{
    set<SAPFOR::Argument*> tmp_use, tmp_def;

    vector<SAPFOR::Argument*> lastParamRef;
    bool fcall_added;

    for (auto ir_block : block->getInstructions())
    {
        fcall_added = false;
        SAPFOR::Instruction* instr = ir_block->getInstruction();
        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;

        set<SAPFOR::Argument*> res = {}, args = {};
        SAPFOR::Argument* res_arg = NULL;

        static const set<SAPFOR::CFG_OP> skip = { SAPFOR::CFG_OP::ENTRY };
        SAPFOR::CFG_OP instr_operation = instr->getOperation();
        if (hasStoreStructure(instr_operation)) {
            res_arg = instr->getArg1();
            std::set<SAPFOR::Argument*> instr_args = { instr->getResult(), instr->getArg2()};
            insertIfVar(instr_args.begin(), instr_args.end(), args);
        }
        else if (instr_operation == SAPFOR::CFG_OP::PARAM)
            lastParamRef.push_back(instr->getArg1());
        else if (instr_operation == SAPFOR::CFG_OP::F_CALL)
        {
            res_arg = instr->getResult();

            int count = stoi(instr->getArg2()->getValue());
            if (lastParamRef.size() != count)
                printInternalError(convertFileName(__FILE__).c_str(), __LINE__);

            const string& fName = instr->getArg1()->getValue();
            auto func_it = funcByName.find(fName);
            if (func_it != funcByName.end())
            {
                fcalls.push_back(fcall(func_it->second, block, lastParamRef));
                fcall_added = true;
            }

            set<SAPFOR::Argument*> make_live, make_dead;
            if (fName == "_READ")
                res.insert(lastParamRef.begin(), lastParamRef.end());
            else if (getLiveDead(lastParamRef, fName, make_live, make_dead))
            {
                insertIfVar(make_live.begin(), make_live.end(), args);
                insertIfVar(make_dead.begin(), make_dead.end(), res);
            }
            else
                insertIfVar(lastParamRef.begin(), lastParamRef.end(), args);

            lastParamRef.clear();
        }
        else if(skip.find(instr_operation) == skip.end())
        {
            //default
            res_arg = instr->getResult();
            std::set<SAPFOR::Argument*> intr_args = { instr->getArg1(), instr->getArg2() };
            insertIfVar(intr_args.begin(), intr_args.end(), args);
        }
        else {
            //skip
            continue;
        }
    
        if (res_arg && res_arg->getType() == SAPFOR::CFG_ARG_TYPE::VAR)
            res.insert(res_arg);

        for (auto e : args)
            if (!tmp_def.count(e))
                tmp_use.insert(e);
        
        for(auto e : res)
            if (!tmp_use.count(e))
                tmp_def.insert(e);

        auto r_it = fcalls.rbegin();
        auto r_end = fcalls.rend();
        if (fcall_added && r_it != r_end)
            r_it++;

        while (r_it != r_end && r_it->block == block)
        {
            for (auto e : args)
                r_it->make_live(e, block);

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

            r_it++;
        }
    }

    use = tmp_use;
    def = tmp_def;
}

// minimizes the number of blocks beween the ends of back edges
static vector<SAPFOR::BasicBlock*> reorderSequence(const vector<SAPFOR::BasicBlock*>& blocks, 
                                                   const set<SAPFOR::BasicBlock*> back_edge_sources)
{
    vector<SAPFOR::BasicBlock*> res = { };

    auto blocks_end = blocks.rend();
    for (auto it = blocks.rbegin(); it < blocks_end; it++)
    {
        SAPFOR::BasicBlock* curr = *it;
        auto res_end = res.end();
        auto inserter = res.begin();
        if (back_edge_sources.count(curr) == 0)
        {
            auto curr_next_begin = curr->getNext().begin();
            auto curr_next_end = curr->getNext().end();
            while (inserter < res_end && std::find(curr_next_begin, curr_next_end, *inserter) == curr_next_end)
                inserter++;
        }

        res.insert(inserter, curr);
    }

    return res;
}

// finds back edges, reorders and converts blocks into vector of BasicBlockNode*
// fills vector of formal parameters for given function
// fills info about arguments which becomes live after calls of functions
static vector<BasicBlockNode*> toBlocksWithCnt(const vector<SAPFOR::BasicBlock*>& blocks,
                                               vector<SAPFOR::Argument*>& formal_parameters,
                                               vector<fcall>& fcalls, const map<string, FuncInfo*>& funcByName)
{
    set<pair<SAPFOR::BasicBlock*, SAPFOR::BasicBlock*>> back_edges = {};

    bool returned = false;
    map<SAPFOR::BasicBlock*, set<SAPFOR::BasicBlock*>> back_edges_by_src;

    auto blocks_sorted = sortCfgNodes(blocks, &back_edges);

    set<SAPFOR::BasicBlock*> back_edge_sources;

    for (auto& edge : back_edges)
    {
        back_edges_by_src[edge.first].insert(edge.second);
        back_edge_sources.insert(edge.first);
    }

    back_edges.clear();

    blocks_sorted = reorderSequence(blocks_sorted, back_edge_sources);
    back_edge_sources.clear();

    vector<BasicBlockNode*> blocks_with_counters;
    map<SAPFOR::BasicBlock*, BasicBlockNode*> node_by_block;
    for (auto block : blocks_sorted)
    {
        BasicBlockNode* node = new BasicBlockNode(block, formal_parameters, fcalls, funcByName);
        blocks_with_counters.push_back(node);
        node_by_block[block] = node;
    }

    for (auto r_it = blocks_with_counters.rbegin(); r_it != blocks_with_counters.rend(); r_it++)
    {
        auto back_edges_by_src_it = back_edges_by_src.find((*r_it)->bb);
        if (back_edges_by_src_it != back_edges_by_src.end())
        {
            // This node is a source for back edge
            for (auto dest : back_edges_by_src_it->second)
            {
                auto node_by_block_it = node_by_block.find(dest);
                if (node_by_block_it != node_by_block.end())
                    node_by_block_it->second->rollback.insert(r_it);
            }
        }

        for (auto next : (*r_it)->bb->getNext())
        {
            auto node_by_block_it = node_by_block.find(next);
            if (node_by_block_it != node_by_block.end())
                (*r_it)->next_blocks.insert(node_by_block_it->second);
        }
    }

    return blocks_with_counters;
}

// iterate over separated subset of blocks
static void analyzeSequence(const vector<BasicBlockNode*>& blocks_with_counters)
{
    auto curr = blocks_with_counters.rbegin();
    auto stop = blocks_with_counters.rend();

    while (curr != stop)
    {
        auto curr_bb = *curr;
        curr_bb->updateLive();

        const auto& jumps = curr_bb->rollback;
        if (jumps.size() != 0)
        {
            auto& ignored_jumps = curr_bb->ignore_rollback;
            
            bool jump = false;
            for (const auto& jump_to : jumps)
            {
                if (ignored_jumps.insert(jump_to).second && curr_bb->newerThan(*jump_to))
                {
                    jump = true;
                    curr = jump_to;
                    break;
                }
            }

            if (!jump)
                curr_bb->ignore_rollback.clear();
            else
                continue;
        }

        curr++;
    }
}

// delete all nodes from vector
static void freeBlocksWithCnt(const vector<BasicBlockNode*>& blocks_with_counters)
{
    for (auto to_free : blocks_with_counters)
        delete to_free;
}

// 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, vector<BasicBlockNode*>> func_to_blocks_with_cnt;
    map<string, vector<SAPFOR::Argument*>> func_to_parameters;

    list<vector<fcall>> 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);

            auto& blocks_with_cnt = (func_to_blocks_with_cnt[byFunc->funcName] = toBlocksWithCnt(itCFG->second, params, curr_fcalls, funcByName));
            analyzeSequence(blocks_with_cnt);

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

    // interprocedural analysis
    for (auto& calls_vector : live_for_fcalls)
    {
        map<FuncInfo*, fcall> 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, fcall(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_blocks_with_cnt.find(func.first->funcName);
            if (func_it == func_to_blocks_with_cnt.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<BasicBlockNode*> exits;
            int max_cnt = CNT_NOTINIT;
            for (auto block : func_it->second)
            {
                if (block->bb->getNext().size() == 0)
                    exits.insert(block);
                if (block->out_cnt > max_cnt)
                    max_cnt = block->out_cnt;
            }

            max_cnt++;

            for (auto exit : exits)
            {
                for (const auto& byArg : live_after)
                {
                    if (exit->bb->addLiveOut({ byArg }))
                    {
                        exit->out_cnt = max_cnt;
                        if (exit->live.find(byArg.first) == exit->live.end() && exit->dead.find(byArg.first) == exit->dead.end())
                            if (exit->bb->addLiveIn({ byArg }))
                                exit->in_cnt = max_cnt;
                    }
                }
            }
            
            // now we can update live sets in all blocks
            analyzeSequence(func_it->second);
        }
    }

    for (const auto& nodeByFunc : func_to_blocks_with_cnt)
        freeBlocksWithCnt(nodeByFunc.second);

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

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