#include "live_variable_analysis.h" #include "RD_subst.h" #include "DataFlow/backward_data_flow.h" #include #include #include #include #include #include #include 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>& to_add, bool in) { std::map>& current_set = (in ? live_in : live_out); std::map>& opposite_set = (!in ? live_in : live_out); bool inserted = false; for (const auto& byNew : to_add) { const vector& add_in_live = byNew.second; vector 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& 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>& current_set = (in ? live_in : live_out); std::map>& 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> BasicBlock::getLive(bool in) const { auto& current_set = in ? live_in : live_out; map> 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& 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& p) { block = b; func = f; int param_size = p.size(); params = vector(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& params, const string& func_name, set& live, set& dead); static void buildUseDef(SAPFOR::BasicBlock* block, set& use, set& def, vector& formal_parameters, vector& fcalls, const map& funcByName, bool interprocedural); class LiveVarAnalysisNode : public DataFlowAnalysisNode>> { private: set live, dead; public: map> getIn() { return getBlock()->getLiveOut(); } map> getOut() { return getBlock()->getLiveIn(); } bool addIn(const map>& data) { return getBlock()->addLiveOut(data); } bool addOut(const map>& data) { return getBlock()->addLiveIn(data); } DATA_FLOW_UPD_STATUS forwardData(const map>& 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& formal_parameters, vector& fcalls, const map& 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 { protected: vector& formal_parameters; vector& fcalls; const map& funcByName; LiveVarAnalysisNode* createNode(SAPFOR::BasicBlock* block) override { return new LiveVarAnalysisNode(block, formal_parameters, fcalls, funcByName); } public: LiveVarAnalysis(vector& formal_parameters, vector& fcalls, const map& funcByName) : formal_parameters(formal_parameters), fcalls(fcalls), funcByName(funcByName) { } }; void getUseDefForInstruction(SAPFOR::BasicBlock* block, SAPFOR::Instruction* instr, set& use, set& def, vector& formal_parameters, vector& fcalls, vector& arg_stack, int& arg_stack_index, int& arg_stack_size, bool& last_stack_op, string& fName, const map& 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 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 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 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 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& use, set& def, vector& formal_parameters, vector& fcalls, vector& arg_stack, int& arg_stack_index, int& arg_stack_size, string& fName, const map& funcByName, bool interprocedural) { set 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& use, set& def, vector& formal_parameters, vector& fcalls, const map& funcByName, bool interprocedural) { vector 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>& 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, set>> 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& blocks) { if (blocks.size() == 0) return; SAPFOR::BasicBlock* entrypoint = NULL; int entrypoint_first_instr = 0; set 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 live, dead; set 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& params, const map, set>>& params_and_globals, const string& func_name, set& 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& params, const string& func_name, set& live, set& 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>& 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> callDeps; map 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> scc; vector> callLvls = groupByCallDependencies(callDeps, scc); map func_to_analysis_object; map> func_to_parameters; list> 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(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 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& params = param_it->second; auto params_begin = params.begin(), params_end = params.end(); map> 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 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> converted; converted[byArg.first] = vector(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(); }