#include "leak_detector.h" #include #include #include #include #include #include #include #include #include "errors.h" #include "graph_calls.h" #include "../Distribution/Distribution.h" #include "utils.h" #include "../ParallelizationRegions/ParRegions.h" using std::vector; using std::map; using std::string; using std::wstring; using std::set; using std::to_string; using std::tuple; using std::pair; #define DEB 0 void createMapOfFunc(const vector &allFuncInfo, map, FuncInfo*> &mapFuncInfo) { for (auto &func : allFuncInfo) mapFuncInfo[make_pair(func->fileName, func->linesNum.first)] = func; } void createMapOfFunc(const vector &allFuncInfo, map &mapFuncInfo) { for (auto &func : allFuncInfo) mapFuncInfo[func->funcName] = func; } void createMapOfFunc(const map> &allFuncInfo, map &mapFuncInfo) { for (auto it = allFuncInfo.begin(); it != allFuncInfo.end(); ++it) for (int k = 0; k < it->second.size(); ++k) mapFuncInfo[it->second[k]->funcName] = it->second[k]; } FuncInfo* getFuncInfo(const map &funcMap, const string &funcName) { auto it = funcMap.find(funcName); if (it == funcMap.end()) return NULL; return it->second; } string removeString(const string &toRemove, const string &inStr) { string outStr(inStr); const size_t found = outStr.find(toRemove); if (found != string::npos) outStr.erase(found, toRemove.length()); return outStr; } void updateFuncInfo(const map> &allFuncInfo) // const here { bool changesDone = false; map mapFuncInfo; createMapOfFunc(allFuncInfo, mapFuncInfo); do { changesDone = false; for (auto &it : mapFuncInfo) { FuncInfo *currInfo = it.second; for(auto &funcCall : currInfo->funcsCalledFromThis) { // Find pointer to info of called function auto itCalledFunc = mapFuncInfo.find(funcCall.CalledFuncName); if (itCalledFunc != mapFuncInfo.end() && !itCalledFunc->second->isInterface) { FuncInfo *calledFunc = itCalledFunc->second; // check for pureness if (!calledFunc->isPure && currInfo->isPure) { currInfo->isPure = false; changesDone = true; } // check for using parameter as index // Iterate through all pars of the call for (int parNo = 0; parNo < funcCall.NoOfParamUsedForCall.size(); ++parNo) { auto& parOfCalled = funcCall.NoOfParamUsedForCall[parNo]; // If this par of called func is used as index change if (calledFunc->isParamUsedAsIndex[parNo]) { // Then pars of calling func which are used in this par of called // are also used as index for (auto &parOfCalling : parOfCalled) { if (!currInfo->isParamUsedAsIndex[parOfCalling]) { changesDone = true; currInfo->isParamUsedAsIndex[parOfCalling] = true; } } } } // propagate inout types for (int parNo = 0; parNo < funcCall.NoOfParamUsedForCall.size(); ++parNo) { auto& parOfCalled = funcCall.NoOfParamUsedForCall[parNo]; if (parOfCalled.size() == 0) continue; if (calledFunc->funcParams.isArgOut(parNo)) { for (auto& num : parOfCalled) { if (!currInfo->funcParams.isArgOut(num)) { currInfo->funcParams.inout_types[num] |= OUT_BIT; changesDone = true; } } } if (calledFunc->funcParams.isArgIn(parNo)) { for (auto& num : parOfCalled) { if (!currInfo->funcParams.isArgIn(num)) { currInfo->funcParams.inout_types[num] |= IN_BIT; changesDone = true; } } } } } } } } while (changesDone); // check for io usage do { changesDone = false; for (auto &it : mapFuncInfo) { FuncInfo *currInfo = it.second; for (auto &callInfo : currInfo->callsFromDetailed) { auto& funcCall = callInfo.detailCallsFrom; auto itCalledFunc = mapFuncInfo.find(funcCall.first); if (itCalledFunc != mapFuncInfo.end()) { FuncInfo *calledFunc = itCalledFunc->second; if (calledFunc->linesOfIO.size()) { const int lineOfCall = funcCall.second; auto it = currInfo->linesOfIO.find(lineOfCall); if (it == currInfo->linesOfIO.end()) { currInfo->linesOfIO.insert(lineOfCall); changesDone = true; } } } } } } while (changesDone); //fill all pars IN, if they have NONE status for (auto& it : mapFuncInfo) { FuncInfo* currInfo = it.second; currInfo->funcParams.completeParams(); } } int CreateCallGraphViz(const char *fileName, const map> &funcByFile, map &V, vector &E) { map allFuncs; createMapOfFunc(funcByFile, allFuncs); string graph = ""; graph += "digraph G{\n"; auto it = funcByFile.begin(); int fileNum = 0; set inCluster; set unknownCluster; char buf[1024]; while (it != funcByFile.end()) { sprintf(buf, "subgraph cluster%d {\n", fileNum); graph += buf; const int dimSize = (int)it->second.size(); set uniqNames; for (int k = 0; k < dimSize; ++k) { const string currfunc = it->second[k]->funcName; auto it = uniqNames.find(currfunc); if (it == uniqNames.end()) { uniqNames.insert(it, currfunc); inCluster.insert(currfunc); sprintf(buf, "\"%s\"\n", currfunc.c_str()); graph += buf; } } sprintf(buf, "label = \"file <%s>\"\n", removeString(".\\", it->first).c_str()); graph += buf; graph += "}\n"; fileNum++; it++; } it = funcByFile.begin(); while (it != funcByFile.end()) { const char *formatString = "\"%s\" -> \"%s\" [minlen=2.0];\n"; const int dimSize = (int)it->second.size(); for (int k = 0; k < dimSize; ++k) { const string &callFrom = it->second[k]->funcName; const FuncInfo *callFromP = it->second[k]; for (auto &callItem : it->second[k]->callsFrom) { sprintf(buf, formatString, callFrom.c_str(), callItem.c_str()); graph += buf; if (inCluster.find(callFrom) == inCluster.end()) unknownCluster.insert(callFrom); if (inCluster.find(callItem) == inCluster.end()) unknownCluster.insert(callItem); if (V.find(callFrom) == V.end()) { V[callFrom] = CallV(callFromP->funcName, callFromP->fileName, callFromP->isMain); V[callFrom].inRegion = callFromP->inRegion; } if (V.find(callItem) == V.end()) { auto it = allFuncs.find(callItem); if (it == allFuncs.end()) V[callItem] = CallV(callItem); else { auto currF = it->second; V[callItem] = CallV(callItem, currF->fileName, currF->isMain); V[callItem].inRegion = currF->inRegion; } } E.push_back(callFrom); E.push_back(callItem); } } it++; } if (unknownCluster.size() > 0) { sprintf(buf, "subgraph cluster%d {\n", fileNum); graph += buf; for (auto &func : unknownCluster) { sprintf(buf, "\"%s\"\n", func.c_str()); graph += buf; } sprintf(buf, "label = \"file \"\n"); graph += buf; graph += "}\n"; } graph += "overlap=false\n"; graph += "}\n"; if (fileName) { FILE *out = fopen(fileName, "w"); if (out == NULL) { __spf_print(1, "can not open file %s\n", fileName); return -1; } fprintf(out, graph.c_str()); fclose(out); } return 0; } int CreateFuncInfo(const char *fileName, const map> &funcByFile) { string funcOut = ""; for (auto &byFile : funcByFile) { funcOut += "FILE " + byFile.first + ":\n"; for (auto &func : byFile.second) { funcOut += (func->isInterface ? ("INTERFACE OF ") : "") + string(" FUNCTION '") + func->funcName + "' " + (func->isPure ? " is PURE" : "is IMPURE") + "\n"; char buf[256]; sprintf(buf, " LINES [%d, %d] \n", func->linesNum.first, func->linesNum.second); funcOut += buf; sprintf(buf, " PARAMETERS %d:\n", func->funcParams.countOfPars); funcOut += buf; for (int z = 0; z < func->funcParams.countOfPars; ++z) { bool in = func->funcParams.isArgIn(z); bool out = func->funcParams.isArgOut(z); const char *inout = ""; if (in && out) inout = "IN/OUT"; else if (in) inout = "IN"; else if (out) inout = "OUT"; sprintf(buf, " %s: type '%s', %s\n", func->funcParams.identificators[z].c_str(), paramNames[func->funcParams.parametersT[z]], inout); funcOut += buf; } } } if (fileName) { FILE *out = fopen(fileName, "w"); if (out == NULL) { __spf_print(1, "can not open file %s\n", fileName); return -1; } fprintf(out, funcOut.c_str()); fclose(out); } return 0; } extern map> allFuncInfo; // file -> Info FuncInfo* isUserFunctionInProject(const string &func) { FuncInfo *ret = NULL; for (auto &it : allFuncInfo) { for (auto &currF : it.second) { if (currF->funcName == func) { ret = currF; break; } } if (ret) break; } return ret; } // Find dead functions and fill callTo / callFrom information void findDeadFunctionsAndFillCalls(map> &allFuncInfo, map> &allMessages, bool noPrint) { map mapFuncInfo; createMapOfFunc(allFuncInfo, mapFuncInfo); set allExternalCalls; set allChildCalls; for (auto &it : mapFuncInfo) { FuncInfo *currInfo = it.second; allChildCalls.insert(currInfo->callsFrom.begin(), currInfo->callsFrom.end()); allExternalCalls.insert(currInfo->externalCalls.begin(), currInfo->externalCalls.end()); } for (auto &it : mapFuncInfo) { FuncInfo *currInfo = it.second; if (allChildCalls.find(it.first) == allChildCalls.end()) if (!currInfo->isMain && allExternalCalls.find(currInfo->funcName) == allExternalCalls.end()) currInfo->deadFunction = currInfo->doNotAnalyze = true; } if (!noPrint) { for (auto &it : allFuncInfo) { const string &currF = it.first; auto itM = allMessages.find(currF); if (itM == allMessages.end()) itM = allMessages.insert(itM, make_pair(currF, vector())); for (auto& func : it.second) if (func->deadFunction) itM->second.push_back(Messages(WARR, func->linesNum.first, R47, L"This function is not called in current project", 1015)); } } for (auto &it : mapFuncInfo) { FuncInfo *currInfo = it.second; for (auto &k : currInfo->callsFrom) { auto itFound = mapFuncInfo.find(k); if (itFound != mapFuncInfo.end()) { FuncInfo *callFrom = itFound->second; callFrom->callsTo.push_back(currInfo); currInfo->callsFromV.insert(callFrom); } } } FuncInfo* main = NULL; for (auto& it : mapFuncInfo) if (it.second->isMain) main = it.second; checkNull(main, convertFileName(__FILE__).c_str(), __LINE__); set liveFunctions; liveFunctions.insert(main); for (auto& callFrom : main->callsFromV) liveFunctions.insert(callFrom); //find live functions bool changes = true; while (changes) { changes = false; for (auto& currInfo : liveFunctions) { for (auto& callFrom : currInfo->callsFromV) { if (liveFunctions.find(callFrom) == liveFunctions.end()) { changes = true; liveFunctions.insert(callFrom); } } } } // propagate 'deadFunction' status for all 'CallsFrom' from dead functions changes = true; while (changes) { changes = false; for (auto& it : mapFuncInfo) { FuncInfo* currInfo = it.second; if (currInfo->deadFunction == false) continue; for (auto& callFrom : currInfo->callsFrom) { auto itFrom = mapFuncInfo.find(callFrom); if (itFrom != mapFuncInfo.end()) { auto func = itFrom->second; if (!func->deadFunction && liveFunctions.find(func) == liveFunctions.end()) { changes = true; func->deadFunction = func->doNotAnalyze = true; } } } } } } static inline void addLinks(const FuncParam &actual, const FuncParam &formal, map> &arrayLinksByFuncCalls) { if (actual.parameters.size() != formal.parameters.size()) return; else { for (int i = 0; i < actual.parameters.size(); ++i) if (actual.parametersT[i] == formal.parametersT[i] && formal.parametersT[i] == ARRAY_T) { //printf("add lhs %s -> rhs %s\n", ((DIST::Array*)formal.parameters[i])->GetName().c_str(), ((DIST::Array*)actual.parameters[i])->GetName().c_str()); if (((DIST::Array*)formal.parameters[i]) == ((DIST::Array*)actual.parameters[i])) printInternalError(convertFileName(__FILE__).c_str(), __LINE__); arrayLinksByFuncCalls[(DIST::Array*)formal.parameters[i]].insert((DIST::Array*)actual.parameters[i]); } } } static bool propagateUp(DIST::Array *from, set to, DIST::distFlag flag, bool &change, map> &SPF_messages) { bool globalChange = false; if (from->GetDistributeFlagVal() == flag) { for (auto &realRef : to) { auto val = realRef->GetDistributeFlagVal(); if (val != flag) { //exclude this case if (flag == DIST::IO_PRIV && val == DIST::SPF_PRIV || flag == DIST::NO_DISTR && val == DIST::SPF_PRIV || flag == DIST::NO_DISTR && val == DIST::IO_PRIV) ; else { realRef->SetDistributeFlag(flag); if (flag == DIST::IO_PRIV) { wstring messageE, messageR; __spf_printToLongBuf(messageE, L"Array '%s' can not be distributed because of DVM's I/O constraints", to_wstring(realRef->GetShortName()).c_str()); __spf_printToLongBuf(messageR, R68, to_wstring(realRef->GetShortName()).c_str()); auto places = realRef->GetDeclInfo(); for (auto& place : places) getObjectForFileFromMap(place.first.c_str(), SPF_messages).push_back(Messages(WARR, place.second, messageR, messageE, 1037)); } #if DEB printf("up: set %d %s\n", flag, realRef->GetName().c_str()); #endif change = true; globalChange = true; } } } } return globalChange; } static bool propagateFlag(bool isDown, const map> &arrayLinksByFuncCalls, const map, pair> &declaredArrays, map> &SPF_messages) { bool globalChange = false; bool change = true; while (change) { change = false; for (auto &array : declaredArrays) { set realArrayRefs; getRealArrayRefs(array.second.first, array.second.first, realArrayRefs, arrayLinksByFuncCalls); bool allNonDistr = true; bool allDistr = true; bool nonDistrSpfPriv = false; bool nonDistrIOPriv = false; bool init = false; // propagate SPF to down calls for (auto &realRef : realArrayRefs) { if (realRef != array.second.first) { bool nonDistr = realRef->IsNotDistribute(); if (realRef->GetDistributeFlagVal() == DIST::SPF_PRIV) nonDistrSpfPriv = true; else if (realRef->GetDistributeFlagVal() == DIST::IO_PRIV) nonDistrIOPriv = true; allNonDistr = allNonDistr && nonDistr; allDistr = allDistr && !nonDistr; init = true; } } if (init) { if (allNonDistr && array.second.first->IsNotDistribute() == false) { if (isDown) { if (nonDistrSpfPriv) { array.second.first->SetDistributeFlag(DIST::SPF_PRIV); if (DEB) printf("down: set %d %s\n", DIST::SPF_PRIV, array.second.first->GetName().c_str()); } else if (nonDistrIOPriv) { array.second.first->SetDistributeFlag(DIST::IO_PRIV); if (DEB) printf("down: set %d %s\n", DIST::IO_PRIV, array.second.first->GetName().c_str()); } else { array.second.first->SetDistributeFlag(DIST::NO_DISTR); if (DEB) printf("down: set %d %s\n", DIST::NO_DISTR, array.second.first->GetName().c_str()); } change = true; globalChange = true; } } else { if (!isDown) { bool ret = propagateUp(array.second.first, realArrayRefs, DIST::SPF_PRIV, change, SPF_messages); globalChange = globalChange || ret; ret = propagateUp(array.second.first, realArrayRefs, DIST::IO_PRIV, change, SPF_messages); globalChange = globalChange || ret; ret = propagateUp(array.second.first, realArrayRefs, DIST::NO_DISTR, change, SPF_messages); globalChange = globalChange || ret; } } } } } return globalChange; } void propagateArrayFlags(const map> &arrayLinksByFuncCalls, const map, pair> &declaredArrays, map> &SPF_messages) { bool change = true; while (change) { bool changeD = propagateFlag(true, arrayLinksByFuncCalls, declaredArrays, SPF_messages); bool changeU = propagateFlag(false, arrayLinksByFuncCalls, declaredArrays, SPF_messages); change = changeD || changeU; } } static void aggregateUsedArrays(map &funcByName, const map> &arrayLinksByFuncCalls) { //change to real refs for (auto &func : funcByName) { { set curr = func.second->allUsedArrays; set newRefs; for (auto &array : curr) getRealArrayRefs(array, array, newRefs, arrayLinksByFuncCalls); func.second->allUsedArrays.clear(); for (auto &newArray : newRefs) if (newArray->IsNotDistribute() == false) func.second->allUsedArrays.insert(newArray); } { set curr = func.second->usedArraysWrite; set newRefs; for (auto &array : curr) getRealArrayRefs(array, array, newRefs, arrayLinksByFuncCalls); func.second->usedArraysWrite.clear(); for (auto &newArray : newRefs) if (newArray->IsNotDistribute() == false) func.second->usedArraysWrite.insert(newArray); } } bool changed = true; while (changed) { changed = false; for (auto &func : funcByName) { for (auto &callsFrom : func.second->callsFrom) { auto itF = funcByName.find(callsFrom); if (itF != funcByName.end()) { for (auto &usedArray : itF->second->allUsedArrays) { auto itA = func.second->allUsedArrays.find(usedArray); if (itA == func.second->allUsedArrays.end()) { changed = true; func.second->allUsedArrays.insert(usedArray); } } } } } } changed = true; while (changed) { changed = false; for (auto &func : funcByName) { for (auto &callsFrom : func.second->callsFrom) { auto itF = funcByName.find(callsFrom); if (itF != funcByName.end()) { for (auto &usedArray : itF->second->usedArraysWrite) { auto itA = func.second->usedArraysWrite.find(usedArray); if (itA == func.second->usedArraysWrite.end()) { changed = true; func.second->usedArraysWrite.insert(usedArray); } } } } } } } void createLinksBetweenFormalAndActualParams(map> &allFuncInfo, map> &arrayLinksByFuncCalls, const map, pair> &declaredArrays, map> &SPF_messages, bool keepFiles) { for (auto &funcsOnFile : allFuncInfo) { for (auto &func : funcsOnFile.second) { //printf("func %s :\n", func->funcName.c_str()); const string &name = func->funcName; for (auto &caller : func->callsTo) for (int i = 0; i < caller->callsFromDetailed.size(); ++i) if (caller->callsFromDetailed[i].detailCallsFrom.first == name) addLinks(caller->callsFromDetailed[i].actualParams, func->funcParams, arrayLinksByFuncCalls); } } #if DEB printf("BEFORE\n"); //debug dump for (auto& elem : declaredArrays) { auto array = elem.second.first; auto flag = array->GetDistributeFlagVal(); // int { DISTR = 0, NO_DISTR, SPF_PRIV, IO_PRIV } distFlagType; string flagS = ""; if (flag == DIST::DISTR) flagS = "DISTR"; else if (flag == DIST::NO_DISTR) flagS = "NO_DISTR"; else if (flag == DIST::SPF_PRIV) flagS = "SPF_PRIV"; else if (flag == DIST::IO_PRIV) flagS = "IO_PRIV"; printf("%s %s flag %s\n", array->GetShortName(), array->GetName(), flagS.c_str()); } #endif if (keepFiles) { FILE *file = fopen("_arrayLinksByCalls.txt", "w"); for (auto &elem : arrayLinksByFuncCalls) { fprintf(file, "%s -> ", elem.first->GetName().c_str()); for (auto &rhs : elem.second) fprintf(file, " %s ", rhs->GetName().c_str()); fprintf(file, "\n"); } fclose(file); } propagateArrayFlags(arrayLinksByFuncCalls, declaredArrays, SPF_messages); //propagate distr state bool change = true; while (change) { change = false; for (auto &array : declaredArrays) { set realArrayRefs; getRealArrayRefs(array.second.first, array.second.first, realArrayRefs, arrayLinksByFuncCalls); if (realArrayRefs.size() && (*realArrayRefs.begin()) != array.second.first && !(*realArrayRefs.begin())->IsNotDistribute() && array.second.first->IsNotDistribute()) { array.second.first->SetDistributeFlag(DIST::DISTR); change = true; } } } map funcByName; createMapOfFunc(allFuncInfo, funcByName); aggregateUsedArrays(funcByName, arrayLinksByFuncCalls); #if DEB printf("AFTER\n"); //debug dump for (auto &elem : declaredArrays) { auto array = elem.second.first; auto flag = array->GetDistributeFlagVal(); // int { DISTR = 0, NO_DISTR, SPF_PRIV, IO_PRIV } distFlagType; string flagS = ""; if (flag == DIST::DISTR) flagS = "DISTR"; else if (flag == DIST::NO_DISTR) flagS = "NO_DISTR"; else if (flag == DIST::SPF_PRIV) flagS = "SPF_PRIV"; else if (flag == DIST::IO_PRIV) flagS = "IO_PRIV"; printf("%s %s flag %s\n", array->GetShortName(), array->GetName(), flagS.c_str()); } printf(""); #endif } bool detectMpiCalls(const map>& allFuncInfo, map>& SPF_messages) { bool retVal = false; map funcByName; createMapOfFunc(allFuncInfo, funcByName); for (auto& byFile : allFuncInfo) { for (auto& func : byFile.second) { for (auto& callInfo: func->callsFromDetailed) { auto& callsFromThis = callInfo.detailCallsFrom; if (isMpiFunction(callsFromThis.first) && funcByName.find(callsFromThis.first) == funcByName.end()) { retVal = true; wstring messageE, messageR; __spf_printToLongBuf(messageE, L"Detected mpi call, turn on special regime of paralyzing"); __spf_printToLongBuf(messageR, R148); SPF_messages[byFile.first].push_back(Messages(NOTE, callsFromThis.second, messageR, messageE, 1051)); } } } } return retVal; } void excludeArraysFromDistribution(const map>& arrayLinksByFuncCalls, const map, pair> declaredArrays, map>& loopGraph, vector parallelRegions, map>& SPF_messages, map, DIST::Array*>& createdArrays, int sharedMemoryParallelization) { checkArraysMapping(loopGraph, SPF_messages, arrayLinksByFuncCalls); propagateArrayFlags(arrayLinksByFuncCalls, declaredArrays, SPF_messages); if (sharedMemoryParallelization == 0) { for (int z = 0; z < parallelRegions.size(); ++z) filterArrayInCSRGraph(loopGraph, allFuncInfo, parallelRegions[z], arrayLinksByFuncCalls, SPF_messages); propagateArrayFlags(arrayLinksByFuncCalls, declaredArrays, SPF_messages); } for (auto& loopByFile : loopGraph) { for (auto& loop : loopByFile.second) { loop->removeNonDistrArrays(); loop->removeGraphData(); } } for (auto& funcByFile : allFuncInfo) for (auto& func : funcByFile.second) func->removeNonDistrArrays(); //restore for (int z = 0; z < parallelRegions.size(); ++z) { parallelRegions[z]->GetAllArraysToModify().cleanData(); parallelRegions[z]->GetGraphToModify().ClearGraphCSR(); parallelRegions[z]->GetReducedGraphToModify().ClearGraphCSR(); } createdArrays.clear(); } void compliteArrayUsage(DIST::Arrays& allArraysForRegion, map, DIST::Array*>& createdArrays, const map>& arrayLinksByFuncCalls, const map>& tableOfUniqNamesByArray) { set usedArraysLocal; usedArraysLocal.insert(allArraysForRegion.GetArrays().begin(), allArraysForRegion.GetArrays().end()); //add array that linked with used for (auto& used : usedArraysLocal) { set allArrayRefs; getAllArrayRefs(used, used, allArrayRefs, arrayLinksByFuncCalls); for (auto& array : allArrayRefs) { if (usedArraysLocal.find(array) == usedArraysLocal.end()) { auto key = tableOfUniqNamesByArray.find(array); if (key != tableOfUniqNamesByArray.end()) { createdArrays.insert(make_pair(key->second, key->first)); allArraysForRegion.AddArrayToGraph(array); } } } } } void remoteNotUsedArrays(map, DIST::Array*>& createdArrays, const set& usedArraysAcrossRegions, const map>& arrayLinksByFuncCalls) { //remove arrays that is not used map, DIST::Array*> createdArraysNew; for (auto it = createdArrays.begin(); it != createdArrays.end(); ++it) { if (usedArraysAcrossRegions.find(it->second) != usedArraysAcrossRegions.end()) createdArraysNew.insert(*it); else { set realArrayRefs; getRealArrayRefs(it->second, it->second, realArrayRefs, arrayLinksByFuncCalls); for (auto& array : realArrayRefs) { if (array == it->second) continue; if (usedArraysAcrossRegions.find(array) != usedArraysAcrossRegions.end()) createdArraysNew.insert(*it); } } } createdArrays = createdArraysNew; }