Files
SAPFOR/src/CFGraph/CFGraph.cpp

1305 lines
45 KiB
C++
Raw Normal View History

2023-09-14 19:43:13 +03:00
#define _LEAK_
#include "leak_detector.h"
2023-09-14 19:43:13 +03:00
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <set>
#include <chrono>
#include <functional>
2025-06-04 13:08:38 +03:00
#include "SgUtils.h"
#include "CommonBlock.h"
#include "graph_calls.h"
2023-09-14 19:43:13 +03:00
#include "dvm.h"
#include "IR.h"
#include "CFGraph.h"
#define DEB_PRINT 1
using namespace std;
using namespace SAPFOR;
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
typedef SAPFOR::BasicBlock BBlock;
int BBlock::lastNumBlock = 0;
BBlock::BasicBlock(IR_Block* item)
{
num = lastNumBlock++;
instructions.push_back(item);
item->setBasicBlock(this);
}
BBlock::BasicBlock(const BBlock& copyFrom)
{
num = lastNumBlock++;
for (auto& ir : copyFrom.getInstructions())
{
instructions.push_back(new IR_Block(*ir));
instructions.back()->setBasicBlock(this);
}
//need to replace with actual BB
next = copyFrom.next;
prev = copyFrom.prev;
}
2025-05-30 17:06:21 +03:00
void BBlock::addInstruction(IR_Block* item, bool pushFront)
2023-09-14 19:43:13 +03:00
{
2025-05-30 17:06:21 +03:00
if (pushFront)
instructions.insert(instructions.begin(), item);
else
instructions.push_back(item);
2023-09-14 19:43:13 +03:00
item->setBasicBlock(this);
}
2025-05-30 17:06:21 +03:00
void BBlock::addInstructionBefore(IR_Block* item, Instruction* before)
2024-12-19 15:37:34 +03:00
{
2025-05-30 17:06:21 +03:00
checkNull(before, convertFileName(__FILE__).c_str(), __LINE__);
checkNull(item, convertFileName(__FILE__).c_str(), __LINE__);
2024-12-19 15:37:34 +03:00
2024-12-26 01:58:15 +03:00
for (auto it = instructions.begin(); it != instructions.end(); ++it) {
2025-05-30 17:06:21 +03:00
if ((*it)->getInstruction() == before) {
2025-04-22 16:14:29 +03:00
instructions.insert(it, item);
2024-12-26 01:58:15 +03:00
item->setBasicBlock(this);
return;
}
}
2025-05-30 17:06:21 +03:00
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
2024-12-26 01:58:15 +03:00
}
2024-01-30 19:40:14 +03:00
int BBlock::removePrev(BBlock* removed)
{
auto it = std::remove(prev.begin(), prev.end(), removed);
auto r = prev.end() - it;
prev.erase(it, prev.end());
return r;
}
int BBlock::removeNext(BBlock* removed)
{
auto it = std::remove(next.begin(), next.end(), removed);
auto r = next.end() - it;
next.erase(it, next.end());
return r;
}
2023-09-14 19:43:13 +03:00
BBlock::~BasicBlock()
{
for (auto& instr : instructions)
delete instr;
}
static void dumpSets(const map<SAPFOR::Argument*, set<int>>& sets)
{
map<string, set<int>> byName;
map<string, string> types;
for (auto& elem : sets)
{
/*if (elem.first->getType() == CFG_ARG_TYPE::REG)
continue;*/
byName[elem.first->getValue()] = elem.second;
types[elem.first->getValue()] = elem.first->getMemTypeStr();
}
if (byName.size() != sets.size())
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
for (auto& elem : byName)
{
printf(" %s (%s): ", elem.first.c_str(), types[elem.first].c_str());
for (auto& d : elem.second)
printf("%d ", d);
printf("\n");
}
}
static void debPrint(BBlock* curr, bool withRD)
{
printf(" -> [next BB]: ");
for (auto& next : curr->getNext())
printf("%d ", next->getNumber());
printf("\n");
printf(" -> [prev BB]: ");
for (auto& next : curr->getPrev())
printf("%d ", next->getNumber());
printf("\n");
if (withRD)
{
printf("IN sets:\n");
dumpSets(curr->getRD_In());
printf("OUT sets:\n");
dumpSets(curr->getRD_Out());
}
else
{
printf("IN sets %d\n", (int)curr->getRD_In().size());
printf("OUT sets %d\n", (int)curr->getRD_Out().size());
}
}
static void debPrint(const string& fName, const vector<BBlock*>& bblocks, const vector<IR_Block*>& blocks, bool withRD)
{
printf("============================================\n");
printf("IR and CFG for '%s' function\n", fName.c_str());
printf("============================================\n");
BBlock* curr = blocks[0]->getBasicBlock();
for (auto& block : blocks)
{
auto instr = block->getInstruction();
auto oper = instr->getOperator();
if (curr != block->getBasicBlock())
{
debPrint(curr, withRD);
printf("\n");
curr = block->getBasicBlock();
}
printf("[%d] [line %d] [BB %d] %s\n", instr->getNumber(), oper ? oper->lineNumber() : -1, block->getBasicBlock()->getNumber(), instr->dump().c_str());
}
debPrint(curr, withRD); // for last block
printf("\n");
}
vector<pair<const Variable*, CommonBlock*>> getCommonsByFunction(SgFile* file, SgStatement* function, const map<string, CommonBlock*>& commonBlocks)
{
vector<pair<const Variable*, CommonBlock*>> retVal;
for (auto& block : commonBlocks)
{
auto vars = block.second->getVariables(file, function);
for (auto& var : vars)
retVal.push_back(make_pair(var, block.second));
}
return retVal;
}
template<typename T>
bool intersectAndAdd(set<T>& s1, const set<T>& s2)
{
bool retVal = false;
set<T> intersect;
set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), inserter(intersect, intersect.begin()));
if (intersect.size() != s2.size())
retVal = true;
s1.insert(s2.begin(), s2.end());
return retVal;
}
template bool intersectAndAdd<DIST::Array*>(set<DIST::Array*>&, const set<DIST::Array*>&);
static void addToSet(map<SAPFOR::Argument*, set<int>>& addTo,
SAPFOR::Argument* fromArg, const set<int>& fromSet, bool& changeLoc)
{
if (addTo.find(fromArg) == addTo.end())
{
addTo[fromArg] = fromSet;
changeLoc = true;
}
else
{
bool res = intersectAndAdd(addTo[fromArg], fromSet);
changeLoc = res || changeLoc;
}
}
static bool bb_cmp(const BBlock* a, const BBlock* b)
{
return a->getInstructions()[0]->getNumber() < b->getInstructions()[0]->getNumber();
}
static void fillGenKillForGlobal(const pair<SAPFOR::Argument*, set<int>>& arg,
map<SAPFOR::Argument*, set<int>>& gen_local,
map<SAPFOR::Argument*, set<int>>& kill_local)
{
bool notInited = false;
for (auto& ir : arg.second)
if (ir == SAPFOR::CFG_VAL::UNINIT)
notInited = true;
if (gen_local.count(arg.first))
{
if (notInited)
{
for (auto& ir : arg.second)
if (ir != SAPFOR::CFG_VAL::UNINIT)
gen_local[arg.first].insert(ir);
}
else
{
kill_local[arg.first].insert(SAPFOR::CFG_VAL::KILL_ALL);
gen_local[arg.first].clear();
gen_local[arg.first] = arg.second;
}
}
else
{
for (auto& ir : arg.second)
if (ir != SAPFOR::CFG_VAL::UNINIT)
gen_local[arg.first].insert(ir);
if (!notInited)
kill_local[arg.first].insert(SAPFOR::CFG_VAL::KILL_ALL);
}
}
void buildGenKillForCFG(const vector<BBlock*>& CFG, const map<string, FuncInfo*>& funcByName,
const map<FuncInfo*, map<SAPFOR::Argument*, set<int>>>& outForFunc, // actually, RD_out of last IR instruction of function
map<BBlock*, map<SAPFOR::Argument*, set<int>>>& gen,
map<BBlock*, map<SAPFOR::Argument*, set<int>>>& kill,
map<Instruction*, map<SAPFOR::Argument*, set<int>>>* genForIR,
map<Instruction*, map<SAPFOR::Argument*, set<int>>>* killForIR,
map<BBlock*, set<SAPFOR::Argument*>>& notInitedGlobals,
const CFG_Settings settings)
{
for (auto& block : CFG)
{
map<SAPFOR::Argument*, set<int>> kill_local;
map<SAPFOR::Argument*, set<int>> gen_local;
vector<Instruction*> lastParamRef;
for (auto& instr : block->getInstructions())
{
auto currInstr = instr->getInstruction();
auto arg = currInstr->getResult();
if (currInstr->getOperation() == CFG_OP::ASSIGN ||
currInstr->getOperation() == CFG_OP::LOAD)
{
if (arg->getType() != CFG_ARG_TYPE::REG ||
arg->getType() == CFG_ARG_TYPE::REG && settings.withRegisters)
{
kill_local[arg].insert(SAPFOR::CFG_VAL::KILL_ALL);
gen_local[arg].clear();
gen_local[arg].insert(currInstr->getNumber());
}
}
else if (currInstr->getOperation() == CFG_OP::F_CALL)
{
int count = stoi(currInstr->getArg2()->getValue());
if (lastParamRef.size() != count)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
map<SAPFOR::Argument*, set<int>> onlyGlobal;
bool hasOut = false;
const string& fName = currInstr->getArg1()->getValue();
auto itF = funcByName.find(fName);
if (itF != funcByName.end())
{
auto itOut = outForFunc.find(itF->second);
if (itOut != outForFunc.end())
{
hasOut = true;
for (auto& out : itOut->second)
if (out.first->isMemGlobal() || out.first->isParameter())
onlyGlobal[out.first] = out.second;
}
}
// TODO: do it better for user's functions! function not found in project
else if (!isIntrinsicFunctionName(fName.c_str()) &&
currInstr->getArg1()->getValue() != "_WRITE" &&
currInstr->getArg1()->getValue() != "_PRINT")
{
set<int> whereDef = { SAPFOR::CFG_VAL::UNINIT, currInstr->getNumber() }; // not inited and out
for (auto& par : lastParamRef)
{
auto arg = par->getArg1();
if (arg->getType() == CFG_ARG_TYPE::VAR)
fillGenKillForGlobal(make_pair(arg, whereDef), gen_local, kill_local);
}
}
// return of function always REG
if (arg && settings.withRegisters)
{
kill_local[arg].insert(SAPFOR::CFG_VAL::KILL_ALL);
gen_local[arg].clear();
}
if (hasOut)
{
bool hasFoundRes = false;
for (auto& out : onlyGlobal)
{
if (out.first->getMemType() == CFG_MEM_TYPE::FUNC_RES_)
{
if (arg)
{
hasFoundRes = true;
gen_local[arg].insert(out.second.begin(), out.second.end());
}
}
else if (out.first->isMemGlobal())
fillGenKillForGlobal(out, gen_local, kill_local);
else if (out.first->getMemType() == CFG_MEM_TYPE::FUNC_PARAM_)
{
int num = getParamIndex(out.first, lastParamRef.size());
auto arg = lastParamRef[num]->getArg1();
if (arg->getType() == CFG_ARG_TYPE::VAR)
fillGenKillForGlobal(make_pair(arg, out.second), gen_local, kill_local);
}
}
if (arg && !hasFoundRes)
gen_local[arg].insert(currInstr->getNumber());
}
else if (currInstr->getArg1()->getValue() == "_READ")
{
for (auto& arg: lastParamRef) // all is out
if (arg->getArg1()->getType() == CFG_ARG_TYPE::VAR)
fillGenKillForGlobal(make_pair(arg->getArg1(), set<int>{ currInstr->getNumber()}), gen_local, kill_local);
}
}
else if (currInstr->getOperation() == CFG_OP::PARAM)
lastParamRef.push_back(currInstr);
if (currInstr->getOperation() != CFG_OP::PARAM)
lastParamRef.clear();
if (genForIR)
(*genForIR)[currInstr] = gen_local;
if (killForIR)
(*killForIR)[currInstr] = kill_local;
}
for (auto& gen : gen_local)
if (gen.first->isMemGlobal() || gen.first->getMemType() == CFG_MEM_TYPE::FUNC_PARAM_)
if (gen.second.count(SAPFOR::CFG_VAL::UNINIT))
notInitedGlobals[block].insert(gen.first);
gen[block] = gen_local;
kill[block] = kill_local;
}
}
static int buildReachingDefs(const vector<BBlock*>& CFG, const FuncInfo* currF,
map<SAPFOR::Argument*, set<int>>& outForCurrFunc,
const map<FuncInfo*, map<SAPFOR::Argument*, set<int>>>& outForFunc,
const map<string, FuncInfo*>& funcByName, const CFG_Settings settings)
{
// create gen kill for blocks
map<BBlock*, map<SAPFOR::Argument*, set<int>>> gen, kill;
map<BBlock*, set<SAPFOR::Argument*>> notInitedGlobals;
buildGenKillForCFG(CFG, funcByName, outForFunc, gen, kill, NULL, NULL, notInitedGlobals, settings);
//create not inited vars: U AllGen
set<SAPFOR::Argument*> allGen;
for (auto& byBlock : gen)
for (auto& genB : byBlock.second)
allGen.insert(genB.first);
// sort blocks in IR order
vector<BBlock*> sortedByIR = CFG;
sort(sortedByIR.begin(), sortedByIR.end(), bb_cmp);
//set to first block all not inited vars to IN set
if (sortedByIR.size())
{
int firstN = 0;
auto stF = currF->funcPointer->GetOriginal();
if (stF->variant() == ENTRY_STAT)
{
int z = 0;
for (auto& block : sortedByIR)
{
for (auto& ir : block->getInstructions())
if (ir->getInstruction()->getOperation() == CFG_OP::ENTRY && ir->getInstruction()->getOperator() == stF)
firstN = z;
z++;
}
if (sortedByIR[firstN]->getInstructions()[0]->getInstruction()->getOperation() != CFG_OP::ENTRY && z == 0)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}
auto& firstIn = sortedByIR[firstN]->getModRD_In();
for (auto& noInited : allGen)
firstIn[noInited] = set<int>{ SAPFOR::CFG_VAL::UNINIT };
}
bool changed = true;
int iter = 0;
while (changed)
{
changed = false;
//update IN = U OUTp
//update OUT = GEN U (IN \ KILL)
for (const auto& block : sortedByIR)
{
bool changeLoc = false;
map<SAPFOR::Argument*, set<int>>& in_loc = block->getModRD_In();
const auto& prevBlocks = block->getPrev();
// IN = U OUTp
for (auto& prev : prevBlocks)
{
const auto& currOut = prev->getRD_Out();
for (auto& out : currOut)
{
bool res = intersectAndAdd(in_loc[out.first], out.second);
changeLoc = res || changeLoc;
}
}
if (changeLoc)
changed = true;
map<SAPFOR::Argument*, set<int>>& out_loc = block->getModRD_Out();
changeLoc = false;
const auto& currIn = block->getRD_In();
const auto& currKill = kill[block];
const auto& currGen = gen[block];
// OUT = GEN
for (auto& gen : currGen)
{
bool genUpdated = false;
if (gen.first->isMemGlobal() || gen.first->getMemType() == CFG_MEM_TYPE::FUNC_PARAM_)
{
if (notInitedGlobals[block].count(gen.first))
{
auto inLoc = in_loc.find(gen.first);
if (inLoc != in_loc.end())
{
genUpdated = true;
set<int> copy;
for (auto& elem : gen.second)
{
if (elem == SAPFOR::CFG_VAL::UNINIT)
copy.insert(inLoc->second.begin(), inLoc->second.end());
else
copy.insert(elem);
}
addToSet(out_loc, gen.first, copy, changeLoc);
}
}
}
if (!genUpdated)
addToSet(out_loc, gen.first, gen.second, changeLoc);
}
// OUT = GEN U (IN \ KILL)
for (auto& in : currIn)
{
if (currKill.count(in.first))
{
auto s1 = in.second;
const auto& s2 = currKill.find(in.first)->second;
if (s2.size() && (*s2.begin()) == SAPFOR::CFG_VAL::KILL_ALL)
;// nothing to add, s1 \ s2 = empty
else
{
set<int> diff;
set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), inserter(diff, diff.begin()));
addToSet(out_loc, in.first, diff, changeLoc);
}
}
else
addToSet(out_loc, in.first, in.second, changeLoc);
}
if (changeLoc)
changed = true;
}
iter++;
}
if (sortedByIR.size())
outForCurrFunc = sortedByIR.back()->getRD_Out();
return iter;
}
2025-05-30 17:06:21 +03:00
//Kosaraju-Sharir algorithm
2023-09-14 19:43:13 +03:00
static vector<int> getStronglyConnectedComps(vector<vector<int>>& g) {
// 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
auto size = g.size();
vector<bool> vis(size); // all false by default
vector<int> l(size); // all zero by default
auto x = size; // index for filling l in reverse order
vector<vector<int>> t(size); // transpose graph
// Recursive subroutine 'visit':
function<void(int)> visit;
visit = [&](int u)
{
if (!vis[u])
{
vis[u] = true;
for (auto v : g[u])
{
visit(v);
t[v].push_back(u); // construct transpose
}
l[--x] = u;
}
};
// 2. For each vertex u of the graph do visit(u)
for (int i = 0; i < g.size(); ++i)
visit(i);
vector<int> c(size); // used for component assignment
// Recursive subroutine 'assign':
function<void(int, int)> assign;
assign = [&](int u, int root) {
if (vis[u]) { // repurpose vis to mean 'unassigned'
vis[u] = false;
c[u] = root;
for (auto v : t[u]) {
assign(v, root);
}
}
};
// 3: For each element u of l in order, do assign(u, u)
for (auto u : l)
assign(u, u);
return c;
}
static int getFuncNum(FuncInfo* call, map<FuncInfo*, int>& funcToInt, map<int, FuncInfo*>& intTofunc, int &fnum)
{
auto it = funcToInt.find(call);
if (funcToInt.find(call) == funcToInt.end())
{
it = funcToInt.insert(it, make_pair(call, fnum));
intTofunc[fnum] = call;
fnum++;
}
return it->second;
}
//return also strongly connected components if has recursive chains call
vector<set<FuncInfo*>> groupByCallDependencies(const map<FuncInfo*, set<FuncInfo*>>& callDeps, vector<set<FuncInfo*>>& scc)
{
vector<vector<int>> funcGraph;
funcGraph.resize(callDeps.size());
map<FuncInfo*, int> funcToInt;
map<int, FuncInfo*> intTofunc;
int fnum = 0;
for (auto& funcPair : callDeps)
{
FuncInfo* call = funcPair.first;
const set<FuncInfo*>& callTo = funcPair.second;
int callN = getFuncNum(call, funcToInt, intTofunc, fnum);
for (auto& to : callTo)
{
int callToN = getFuncNum(to, funcToInt, intTofunc, fnum);
if (funcGraph.size() < fnum)
funcGraph.resize(fnum);
funcGraph[callN].push_back(callToN);
}
}
vector<int> scc_v = getStronglyConnectedComps(funcGraph);
map<int, vector<int>> components;
for (int f = 0; f < scc_v.size(); ++f)
components[scc_v[f]].push_back(f);
vector<vector<int>> funcWithRecursion;
for (auto& elem : components)
if (elem.second.size() > 1)
funcWithRecursion.push_back(elem.second);
int countWithRec = funcWithRecursion.size();
set<FuncInfo*> added;
size_t added_prev_size = 0;
size_t added_new_size = 0;
set<int> doneRec;
vector<set<FuncInfo*>> runMapForRD;
while (added.size() != callDeps.size())
{
set<FuncInfo*> newLvl;
if (added.size() == 0)
{
for (auto& func : callDeps)
{
if (func.second.size() == 0)
newLvl.insert(func.first);
else
{
bool ifHasDep = false;
for (auto& dep : func.second)
if (callDeps.count(dep))
ifHasDep = true;
if (!ifHasDep)
newLvl.insert(func.first);
}
}
}
else
{
for (auto& func : callDeps)
{
bool ifHasDep = false;
for (auto& dep : func.second)
if (callDeps.count(dep) && added.count(dep) == 0)
ifHasDep = true;
if (!ifHasDep && added.count(func.first) == 0)
newLvl.insert(func.first);
}
}
added.insert(newLvl.begin(), newLvl.end());
added_new_size = added.size();
if (added_new_size == added_prev_size)
{
if (countWithRec != 0)
{
for (int z = 0; z < funcWithRecursion.size(); ++z)
{
if (doneRec.find(z) != doneRec.end())
continue;
set<FuncInfo*> candidate;
for (int f = 0; f < funcWithRecursion[z].size(); ++f)
candidate.insert(intTofunc[funcWithRecursion[z][f]]);
bool ok = true;
for (auto& elem : candidate)
{
if (candidate.find(elem) != candidate.end())
continue;
if (callDeps.count(elem))
for (auto& dep : callDeps.at(elem))
if (added.find(dep) == added.end())
ok = false;
}
if (!ok)
continue;
else
{
newLvl = candidate;
added.insert(newLvl.begin(), newLvl.end());
added_new_size = added.size();
--countWithRec;
doneRec.insert(z);
scc.push_back(candidate);
}
}
if (newLvl.size() == 0)
{
__spf_print(1, "recursive call was found, can not resolve dependencies\n");
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}
}
}
added_prev_size = added_new_size;
runMapForRD.push_back(newLvl);
}
return runMapForRD;
}
static void buildReachingDefs(const map<FuncInfo*, vector<BBlock*>>& CFG, const CFG_Settings& settings)
{
map<FuncInfo*, set<FuncInfo*>> callDeps;
map<string, FuncInfo*> funcByName;
map<FuncInfo*, map<SAPFOR::Argument*, set<int>>> outForFunc;
for (auto& byFunc : CFG)
{
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*>> callLvlsForRD = groupByCallDependencies(callDeps, scc);
//TODO: take into account ssc structure
2025-01-13 18:16:11 +03:00
__spf_print(DEB_PRINT, " count of functions %d, count of lvls %d\n", (int)CFG.size(), (int)callLvlsForRD.size());
2023-09-14 19:43:13 +03:00
for (auto& byLvl : callLvlsForRD)
{
for (auto& byFunc : byLvl)
{
2025-01-13 18:16:11 +03:00
__spf_print(DEB_PRINT, " RD time for '%s' function", byFunc->funcName.c_str());
2023-09-14 19:43:13 +03:00
auto t = high_resolution_clock::now();
auto itCFG = CFG.find(byFunc);
if (itCFG == CFG.end())
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
map<SAPFOR::Argument*, set<int>> outForCurr;
int iter = buildReachingDefs(itCFG->second, byFunc, outForCurr, outForFunc, funcByName, settings);
if (outForFunc.count(byFunc))
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
outForFunc[byFunc] = outForCurr;
auto msec = duration_cast<milliseconds>(high_resolution_clock::now() - t).count();
__spf_print(DEB_PRINT, " is %.3f sec, iters %d\n", msec / 1000., iter);
}
__spf_print(DEB_PRINT, "\n");
}
}
static void createBlock(IR_Block* forIR, map<IR_Block*, BBlock*>& createdBlocks,
vector<BBlock*>& bblocks,
const vector<IR_Block*>& instructions)
{
if (createdBlocks.count(forIR) == 0)
{
bblocks.push_back(new BBlock(forIR));
createdBlocks[forIR] = bblocks.back();
}
}
static vector<BBlock*> buildCFG(const vector<IR_Block*>& instructions, bool withCalls)
{
vector<BBlock*> bblocks;
map<IR_Block*, BBlock*> createdBlocks;
map<Instruction*, IR_Block*> instrToIr;
map<int, IR_Block*> instrNumToIr;
for (auto& ir : instructions)
{
instrToIr[ir->getInstruction()] = ir;
instrNumToIr[ir->getNumber()] = ir;
}
if (instructions.size())
{
bblocks.push_back(new BBlock(instructions[0]));
createdBlocks[instructions[0]] = bblocks.back();
}
for (int z = 0; z < instructions.size(); ++z)
{
if (instructions[z]->getInstruction()->getOperation() == CFG_OP::JUMP ||
instructions[z]->getInstruction()->getOperation() == CFG_OP::JUMP_IF)
{
auto irJump = instrToIr[instructions[z]->getJump()];
if (irJump == NULL)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
createBlock(irJump, createdBlocks, bblocks, instructions);
if (z + 1 < instructions.size())
createBlock(instructions[z + 1], createdBlocks, bblocks, instructions);
}
else if (instructions[z]->getInstruction()->getOperation() == CFG_OP::ENTRY)
{
createBlock(instructions[z], createdBlocks, bblocks, instructions);
if (z + 1 != instructions.size() - 1) // TODO: check this
createBlock(instructions[z + 1], createdBlocks, bblocks, instructions);
}
else if (instructions[z]->getInstruction()->getOperation() == CFG_OP::DVM_DIR ||
(instructions[z]->getInstruction()->getOperation() == CFG_OP::EXIT && withCalls))
{
createBlock(instructions[z], createdBlocks, bblocks, instructions);
if (z + 1 < instructions.size())
createBlock(instructions[z + 1], createdBlocks, bblocks, instructions);
}
else if (instructions[z]->getInstruction()->getOperation() == CFG_OP::F_CALL && withCalls)
{
int prev = z - 1;
while (prev >= 0 && instructions[prev]->getInstruction()->getOperation() == CFG_OP::PARAM)
--prev;
createBlock(instructions[prev + 1], createdBlocks, bblocks, instructions);
if (z + 1 < instructions.size())
createBlock(instructions[z + 1], createdBlocks, bblocks, instructions);
}
}
BBlock* currBlock = NULL;
for (auto& ir : instructions)
{
if (createdBlocks.count(ir) != 0)
currBlock = createdBlocks[ir];
else
{
if (currBlock == NULL)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
currBlock->addInstruction(ir);
}
}
for (auto& bblock : bblocks)
{
auto lastInstr = bblock->getInstructions().back();
BBlock* fromJump = NULL;
if (lastInstr->getJump())
{
fromJump = instrToIr[lastInstr->getJump()]->getBasicBlock();
bblock->addNext(fromJump);
}
if (lastInstr->getInstruction()->getOperation() != CFG_OP::JUMP)
{
int next = lastInstr->getNumber() + 1;
if (instrNumToIr.count(next) != 0)
{
auto toAdd = instrNumToIr[next]->getBasicBlock();
if (fromJump != toAdd)
bblock->addNext(toAdd);
}
}
}
for (auto& bblock : bblocks)
for (auto& next : bblock->getNext())
next->addPrev(bblock);
auto checkIR = getAllIR(bblocks);
if (checkIR.size() != instructions.size())
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
return bblocks;
}
static void relaceArgInEntry(SAPFOR::Instruction* ir, const string& entryName, const vector<string>& params)
{
for (int z = 0; z < 3; ++z)
{
SAPFOR::Argument* arg = NULL;
if (z == 0)
arg = ir->getArg1();
else if (z == 1)
arg = ir->getArg2();
else if (z == 2)
arg = ir->getResult();
if (arg == NULL)
continue;
if (arg->getMemType() == SAPFOR::CFG_MEM_TYPE::FUNC_PARAM_)
{
vector<string> splited;
splitString(arg->getValue(), '%', splited);
if (splited.size() != 3)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
if (splited[0] != entryName)
{
int p = -1;
for (int t = 0; t < params.size(); ++t)
if (params[t] == splited[1])
p = t;
SAPFOR::Argument* newArg = NULL;
if (p < 0)
newArg = createArg(splited[0] + "%" + splited[1], splited[1], SAPFOR::CFG_MEM_TYPE::LOCAL_);
else
newArg = createArg(entryName + "%" + splited[1] + "%" + to_string(p), splited[1], SAPFOR::CFG_MEM_TYPE::FUNC_PARAM_);
if (z == 0)
ir->setArg1(newArg);
else if (z == 1)
ir->setArg2(newArg);
else if (z == 2)
ir->setResult(newArg);
}
}
else if (arg->getMemType() == SAPFOR::CFG_MEM_TYPE::LOCAL_ &&
arg->getType() == SAPFOR::CFG_ARG_TYPE::VAR)
{
vector<string> splited;
splitString(arg->getValue(), '%', splited);
if (splited.size() != 2)
continue;
if (splited[0] != entryName)
{
int p = -1;
for (int t = 0; t < params.size(); ++t)
if (params[t] == splited[1])
p = t;
if (p >= 0)
{
auto newArg = createArg(entryName + "%" + splited[1] + "%" + to_string(p), splited[1], SAPFOR::CFG_MEM_TYPE::FUNC_PARAM_);
if (z == 0)
ir->setArg1(newArg);
else if (z == 1)
ir->setArg2(newArg);
else if (z == 2)
ir->setResult(newArg);
}
}
}
}
}
static void filterEntryArgs(const vector<BBlock*>& bblocks, const string& entryName, const vector<string>& params)
{
/*set<BBlock*> checked;
std::stack<BBlock*> q;
q.push(bblocks[0]);
checked.insert(bblocks[0]);
while (!q.empty())
{
BBlock* curr = q.top();
checked.insert(curr);
q.pop();
for (auto& elem : curr->getNext())
if (checked.count(elem) == 0)
q.push(elem);
}*/
for (auto& block : bblocks)
{
//if (checked.count(block) == 0) // not checked
for (auto& ir : block->getInstructions())
relaceArgInEntry(ir->getInstruction(), "_" + entryName, params);
}
}
map<FuncInfo*, vector<BBlock*>> buildCFG(const map<string, CommonBlock*>& commonBlocks, const map<string, vector<FuncInfo*>>& allFuncInfo, const CFG_Settings settings)
{
map<FuncInfo*, vector<BBlock*>> result;
string oldFile = current_file->filename();
for (auto& byFile : allFuncInfo)
{
if (SgFile::switchToFile(byFile.first) == -1)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
map<SgStatement*, FuncInfo*> byPointer;
for (auto& func : byFile.second)
byPointer[func->funcPointer->GetOriginal()] = func;
for (auto& byFunc : byFile.second)
{
FuncInfo* currF = byFunc;
SgStatement* function = currF->funcPointer->GetOriginal();
if (function->variant() == ENTRY_STAT)
continue;
if (currF->isInterface)
continue;
if (!isSgProgHedrStmt(function)) {
__spf_print(1, "var %d on file %s and line %d\n", function->variant(), function->fileName(), function->lineNumber());
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
}
vector<IR_Block*> blocks = buildIR(function, currF, getCommonsByFunction(current_file, function, commonBlocks), settings);
if (blocks.size())
blocks[0]->setHeader();
vector<BBlock*> CFG_forFunc = buildCFG(blocks, settings.withCallsInBlocks);
if (result.find(currF) != result.end())
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
result[currF] = CFG_forFunc;
}
map<SAPFOR::Argument*, SAPFOR::Argument*> argsInc;
//duplicate graph of parent func for ENTRY points
for (auto& byFunc : byFile.second)
{
FuncInfo* currF = byFunc;
SgStatement* function = currF->funcPointer->GetOriginal();
if (function->variant() != ENTRY_STAT)
continue;
auto parent = function->controlParent();
if (!isSgProgHedrStmt(parent))
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
if (!byPointer.count(parent))
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
FuncInfo* parentF = byPointer[parent];
if (!result.count(parentF))
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
if (result.count(currF))
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
const auto& cfg = result[parentF];
vector<BBlock*> copy;
map<BBlock*, BBlock*> oldToNewBB;
map<Instruction*, Instruction*> oldToNewIR;
for (auto& block : cfg)
{
copy.push_back(new BBlock(*block));
oldToNewBB[block] = copy.back();
const auto& newIR = copy.back()->getInstructions();
const auto& oldIR = block->getInstructions();
if (newIR.size() != oldIR.size())
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
for (int z = 0; z < oldIR.size(); ++z)
oldToNewIR[oldIR[z]->getInstruction()] = newIR[z]->getInstruction();
}
for (auto& block : copy)
{
block->replacePrevNext(oldToNewBB);
for (auto& ir : block->getInstructions())
{
auto jump = ir->getJump();
if (jump)
{
auto it = oldToNewIR.find(jump);
if (it == oldToNewIR.end())
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
ir->setJump(it->second);
}
}
}
//need to add goto to ENTRY
map<FuncInfo*, vector<BBlock*>> tmpInfo;
tmpInfo[parentF] = copy;
auto position = getInstructionAndBlockByStatement(tmpInfo, function);
if (position.first == NULL || position.second == NULL)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
if (position.first->getOperation() != CFG_OP::ENTRY)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
const int lastIrNum = SAPFOR::Instruction::getNextInstrNum();
int maxNum = 0;
//shift all IR numbers by lastIrNum + 1
for (auto& block : copy)
{
for (auto& instr : block->getInstructions())
{
maxNum = std::max(instr->getInstruction()->getNumber(), maxNum);
instr->getInstruction()->shiftNumber(lastIrNum + 1);
auto ir = instr->getInstruction();
auto type = ir->getOperation();
if (type == CFG_OP::JUMP || type == CFG_OP::JUMP_IF)
{
if (argsInc.find((type == CFG_OP::JUMP) ? ir->getArg1() : ir->getArg2()) == argsInc.end())
{
int jumpTo = atoi((type == CFG_OP::JUMP) ? ir->getArg1()->getValue().c_str() : ir->getArg2()->getValue().c_str());
jumpTo += (lastIrNum + 1);
SAPFOR::Argument* newArg = new SAPFOR::Argument((type == CFG_OP::JUMP) ? *(ir->getArg1()) : *(ir->getArg2()));
newArg->setValue(to_string(jumpTo));
if (type == CFG_OP::JUMP)
{
ir->setArg1(newArg);
argsInc[ir->getArg1()] = newArg;
}
else
{
ir->setArg2(newArg);
argsInc[ir->getArg2()] = newArg;
}
}
else
{
if (type == CFG_OP::JUMP)
ir->setArg1(argsInc[ir->getArg1()]);
else
ir->setArg2(argsInc[ir->getArg2()]);
}
}
}
}
SAPFOR::Instruction::shiftNextInstrNum(maxNum + 1);
// goto to ENTRY
BBlock* firstBlock = new BBlock();
IR_Block* newGoto = new IR_Block(new Instruction(CFG_OP::JUMP, lastIrNum, new SAPFOR::Argument(CFG_ARG_TYPE::INSTR, to_string(position.first->getNumber()))));
newGoto->setJump(position.first);
newGoto->setHeader();
firstBlock->addInstruction(newGoto);
firstBlock->addNext(position.second);
position.second->addPrev(firstBlock);
copy.insert(copy.begin(), firstBlock);
filterEntryArgs(copy, function->symbol()->identifier(), currF->funcParams.identificators);
result[currF] = copy;
}
}
if (settings.withRD)
buildReachingDefs(result, settings);
if (settings.withDominators)
{
auto t = high_resolution_clock::now();
for (auto& [func, bblocks] : result)
SAPFOR::buildDominatorTree(bblocks);
auto msec = duration_cast<milliseconds>(high_resolution_clock::now() - t).count();
2025-06-11 15:07:06 +03:00
__spf_print(1, " dominator build time is %.3f sec\n", msec / 1000.);
}
2023-09-14 19:43:13 +03:00
if (SgFile::switchToFile(oldFile) == -1)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
for (auto& [func, blocks] : result)
removedUnreachableBlocks(blocks);
2023-09-14 19:43:13 +03:00
return result;
}
static bool ir_cmp(const IR_Block* a, const IR_Block* b)
{
return a->getNumber() < b->getNumber();
}
vector<IR_Block*> getAllIR(const vector<BBlock*>& blocks)
{
vector<IR_Block*> intsrs;
for (auto& block : blocks)
for (auto& instr : block->getInstructions())
intsrs.push_back(instr);
sort(intsrs.begin(), intsrs.end(), ir_cmp);
return intsrs;
}
void dumpCFG(const map<FuncInfo*, vector<BBlock*>>& blocks, bool withRD)
{
map<int, map<FuncInfo*, tuple<FuncInfo*, vector<BBlock*>, vector<IR_Block*>>>> toPrint; // sort by IR number
for (auto& byFunc : blocks)
{
vector<IR_Block*> IR = getAllIR(byFunc.second);
toPrint[IR[0]->getNumber()][byFunc.first] = make_tuple(byFunc.first, byFunc.second, IR);
}
for (auto& dump : toPrint)
for (auto& byFunc : dump.second)
debPrint(get<0>(byFunc.second)->funcName, get<1>(byFunc.second), get<2>(byFunc.second), withRD);
}
// buildCFGforCurrentFunc builds and returns CFG for current function, which contains 'stmt'
// and for functions, called from current function.
// CFG is built with reaching definitions analysis
// and with assupmtion, that every loop executes at least one iteration
map<FuncInfo*, vector<SAPFOR::BasicBlock*>>
buildCFGforCurrentFunc(SgStatement* stmt, const SAPFOR::CFG_Settings settings,
const map<string, CommonBlock*>& commonBlocks,
const map<string, vector<FuncInfo*>>& allFuncInfo)
{
checkNull(stmt, convertFileName(__FILE__).c_str(), __LINE__);
string file = stmt->fileName();
SgProgHedrStmt* curFunc = isSgProgHedrStmt(getFuncStat(stmt));
checkNull(curFunc, convertFileName(__FILE__).c_str(), __LINE__);
string curFuncName = curFunc->nameWithContains();
if (allFuncInfo.count(file) == 0)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
const vector<FuncInfo*>& funcInFile = allFuncInfo.at(file);
FuncInfo* current = NULL;
for (auto& func : funcInFile)
{
if (func->funcName == curFuncName)
{
current = func;
break;
}
}
checkNull(current, convertFileName(__FILE__).c_str(), __LINE__);
map<string, vector<FuncInfo*>> fileFuncInfoMap;
fileFuncInfoMap[current->fileName].push_back(current);
if (settings.withCallFrom)
for (auto& callFrom : current->callsFromV)
fileFuncInfoMap[callFrom->fileName].push_back(callFrom);
return buildCFG(commonBlocks, fileFuncInfoMap, settings);
2025-06-18 16:26:43 +03:00
}
void removedUnreachableBlocks(vector<SAPFOR::BasicBlock*>& blocks)
{
set<SAPFOR::BasicBlock*> reachable;
for (auto& b : blocks)
if (b->getInstructions().front()->isHeader() ||
b->getInstructions().front()->getInstruction()->getOperation() == SAPFOR::CFG_OP::ENTRY)
reachable.insert(b);
set<SAPFOR::BasicBlock*> worklist = reachable;
while (worklist.size() != 0)
{
set<SAPFOR::BasicBlock*> to_insert;
for (auto& b : worklist)
for (auto& next : b->getNext())
if (reachable.insert(next).second)
to_insert.insert(next);
worklist = to_insert;
}
auto remove_unreachable_it = remove_if(blocks.begin(), blocks.end(),
[&reachable](SAPFOR::BasicBlock* b)
{
if (reachable.find(b) == reachable.end())
{
for (auto& next : b->getNext())
if (reachable.find(next) != reachable.end())
next->removePrev(b);
delete b;
return true;
}
return false;
}
);
reachable.clear();
blocks.erase(remove_unreachable_it, blocks.end());
2023-09-14 19:43:13 +03:00
}