Merge pull request 'select_array_dim_conf_pass_xnp' (#6) from select_array_dim_conf_pass_xnp into master
Reviewed-on: Alexander_KS/SAPFOR/pulls/6
This commit was merged in pull request #6.
This commit is contained in:
@@ -285,7 +285,9 @@ set(EXPR_TRANSFORM _src/ExpressionTransform/control_flow_graph_part.cpp
|
|||||||
set(GR_CALL _src/GraphCall/graph_calls.cpp
|
set(GR_CALL _src/GraphCall/graph_calls.cpp
|
||||||
_src/GraphCall/graph_calls.h
|
_src/GraphCall/graph_calls.h
|
||||||
_src/GraphCall/graph_calls_base.cpp
|
_src/GraphCall/graph_calls_base.cpp
|
||||||
_src/GraphCall/graph_calls_func.h)
|
_src/GraphCall/graph_calls_func.h
|
||||||
|
_src/GraphCall/select_array_conf.cpp
|
||||||
|
_src/GraphCall/select_array_conf.h)
|
||||||
|
|
||||||
set(GR_LOOP _src/GraphLoop/graph_loops_base.cpp
|
set(GR_LOOP _src/GraphLoop/graph_loops_base.cpp
|
||||||
_src/GraphLoop/graph_loops.cpp
|
_src/GraphLoop/graph_loops.cpp
|
||||||
|
|||||||
231
sapfor/experts/Sapfor_2017/_src/GraphCall/select_array_conf.cpp
Normal file
231
sapfor/experts/Sapfor_2017/_src/GraphCall/select_array_conf.cpp
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
#include "select_array_conf.h"
|
||||||
|
|
||||||
|
using std::map;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::set;
|
||||||
|
using std::pair;
|
||||||
|
|
||||||
|
using std::inserter;
|
||||||
|
using std::copy;
|
||||||
|
|
||||||
|
bool IsSetsIntersect(const set<DIST::Array*>& lhs, const set<DIST::Array*>& rhs) {
|
||||||
|
if (lhs.empty() || rhs.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lhs.size() * 100 < rhs.size())
|
||||||
|
{
|
||||||
|
for (const auto& x : lhs)
|
||||||
|
if (rhs.find(x) != rhs.end())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhs.size() * 100 < lhs.size())
|
||||||
|
{
|
||||||
|
for (const auto& x : rhs)
|
||||||
|
if (lhs.find(x) != lhs.end())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto l_it = lhs.begin(), l_end = lhs.end();
|
||||||
|
auto r_it = rhs.begin(), r_end = rhs.end();
|
||||||
|
|
||||||
|
while (l_it != l_end && r_it != r_end)
|
||||||
|
{
|
||||||
|
if (*l_it < *r_it)
|
||||||
|
{
|
||||||
|
l_it = lhs.lower_bound(*r_it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*r_it < *l_it)
|
||||||
|
{
|
||||||
|
r_it = rhs.lower_bound(*l_it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void findUsedArraysInParallelLoops(LoopGraph* loop, set<DIST::Array*>& res) {
|
||||||
|
if(loop->directive)
|
||||||
|
copy(loop->usedArraysAll.begin(), loop->usedArraysAll.end(), inserter(res, res.end()));
|
||||||
|
else
|
||||||
|
for(LoopGraph* child : loop->children)
|
||||||
|
findUsedArraysInParallelLoops(child, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void preventLoopsFromParallelizations(LoopGraph* loop, const set<DIST::Array*>& prevent,
|
||||||
|
vector<Directive*>& createdDirectives) {
|
||||||
|
if (loop->directive)
|
||||||
|
{
|
||||||
|
if (IsSetsIntersect(prevent, loop->usedArraysAll))
|
||||||
|
{
|
||||||
|
// prevent this loop
|
||||||
|
int loopLine = loop->lineNum;
|
||||||
|
|
||||||
|
for (auto dir_it = createdDirectives.begin(); dir_it != createdDirectives.end(); dir_it++)
|
||||||
|
{
|
||||||
|
if ((*dir_it)->line == loopLine)
|
||||||
|
{
|
||||||
|
delete *dir_it;
|
||||||
|
dir_it = createdDirectives.erase(dir_it);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete loop->directive;
|
||||||
|
loop->directive = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (LoopGraph* child : loop->children)
|
||||||
|
preventLoopsFromParallelizations(child, prevent, createdDirectives);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectArrayConfForParallelization(SgProject* proj, map<string, vector<FuncInfo*>>& funcByFile,
|
||||||
|
const map<string, vector<LoopGraph*>>& loopGraph, map<std::string, vector<Directive*>>& createdDirectives,
|
||||||
|
map<string, vector<Messages>>& allMessages, const map<DIST::Array*, set<DIST::Array*>>& arrayLinksByFuncCalls,
|
||||||
|
const vector<ParallelRegion*>& regions)
|
||||||
|
{
|
||||||
|
map<string, FuncInfo*> funcByName;
|
||||||
|
for (const auto& byFile : funcByFile)
|
||||||
|
for(const auto& byFunc : byFile.second)
|
||||||
|
funcByName[byFunc->funcName] = byFunc;
|
||||||
|
|
||||||
|
// array(real ref) dims func array in func
|
||||||
|
map<DIST::Array*, map<int, map<FuncInfo*, set<DIST::Array*>>>> usedArrays;
|
||||||
|
|
||||||
|
for (const auto& byFile : loopGraph)
|
||||||
|
{
|
||||||
|
SgFile::switchToFile(byFile.first);
|
||||||
|
|
||||||
|
auto& loops = byFile.second;
|
||||||
|
|
||||||
|
auto file_funcs_it = funcByFile.find(byFile.first);
|
||||||
|
|
||||||
|
if(file_funcs_it == funcByFile.end())
|
||||||
|
printInternalError(convertFileName(__FILE__).c_str(), __LINE__); // no such file in funcByFile
|
||||||
|
|
||||||
|
map<FuncInfo*, set<DIST::Array*>> usedInLoops;
|
||||||
|
|
||||||
|
for (const auto& loop : loops)
|
||||||
|
{
|
||||||
|
SgStatement* search_func = loop->loop->GetOriginal();
|
||||||
|
|
||||||
|
while (search_func && (!isSgProgHedrStmt(search_func)))
|
||||||
|
search_func = search_func->controlParent();
|
||||||
|
|
||||||
|
if (!search_func)
|
||||||
|
printInternalError(convertFileName(__FILE__).c_str(), __LINE__); //loop statement outside any function statement
|
||||||
|
|
||||||
|
bool loop_analyzed = false;
|
||||||
|
for (const auto& byFunc : file_funcs_it->second)
|
||||||
|
{
|
||||||
|
if (byFunc->funcPointer->GetOriginal() == search_func)
|
||||||
|
{
|
||||||
|
if(!loop->usedArraysAll.empty())
|
||||||
|
findUsedArraysInParallelLoops(loop, usedInLoops[byFunc]);
|
||||||
|
|
||||||
|
loop_analyzed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loop_analyzed)
|
||||||
|
printInternalError(convertFileName(__FILE__).c_str(), __LINE__); //no func found for loop
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& byFunc : usedInLoops)
|
||||||
|
{
|
||||||
|
for (DIST::Array* arr : byFunc.second)
|
||||||
|
{
|
||||||
|
set<DIST::Array*> realRefs;
|
||||||
|
getRealArrayRefs(arr, arr, realRefs, arrayLinksByFuncCalls);
|
||||||
|
|
||||||
|
int num_of_dims = arr->GetDimSize();
|
||||||
|
for(DIST::Array* ref : realRefs)
|
||||||
|
usedArrays[ref][num_of_dims][byFunc.first].insert(arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map<FuncInfo*, set<DIST::Array*>> preventFromParallelization;
|
||||||
|
|
||||||
|
for (const auto& byRealRef : usedArrays)
|
||||||
|
{
|
||||||
|
DIST::Array* realRef = byRealRef.first;
|
||||||
|
auto& refferedDims = byRealRef.second;
|
||||||
|
|
||||||
|
// prevent from parallelization all arrays wich refers realRef and has smaller number of dims
|
||||||
|
auto bySmallerDimSize = refferedDims.rbegin();
|
||||||
|
if(bySmallerDimSize != refferedDims.rend())
|
||||||
|
{
|
||||||
|
bySmallerDimSize++;
|
||||||
|
for (; bySmallerDimSize != refferedDims.rend(); bySmallerDimSize++)
|
||||||
|
{
|
||||||
|
for (const auto& byFunc : bySmallerDimSize->second)
|
||||||
|
{
|
||||||
|
FuncInfo* f = byFunc.first;
|
||||||
|
auto& to_prevent = byFunc.second;
|
||||||
|
|
||||||
|
auto& destSet = preventFromParallelization[f];
|
||||||
|
copy(to_prevent.begin(), to_prevent.end(), inserter(destSet, destSet.end()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& byFile : loopGraph)
|
||||||
|
{
|
||||||
|
auto dirs_it = createdDirectives.find(byFile.first);
|
||||||
|
if (dirs_it != createdDirectives.end())
|
||||||
|
{
|
||||||
|
SgFile::switchToFile(byFile.first);
|
||||||
|
|
||||||
|
auto& loops = byFile.second;
|
||||||
|
|
||||||
|
auto file_funcs_it = funcByFile.find(byFile.first);
|
||||||
|
|
||||||
|
if (file_funcs_it == funcByFile.end())
|
||||||
|
printInternalError(convertFileName(__FILE__).c_str(), __LINE__); // no such file in funcByFile
|
||||||
|
|
||||||
|
map<FuncInfo*, set<DIST::Array*>> usedInLoops;
|
||||||
|
|
||||||
|
for (const auto& loop : loops)
|
||||||
|
{
|
||||||
|
SgStatement* search_func = loop->loop->GetOriginal();
|
||||||
|
|
||||||
|
while (search_func && (!isSgProgHedrStmt(search_func)))
|
||||||
|
search_func = search_func->controlParent();
|
||||||
|
|
||||||
|
if (!search_func)
|
||||||
|
printInternalError(convertFileName(__FILE__).c_str(), __LINE__); //loop statement outside any function statement
|
||||||
|
|
||||||
|
bool loop_analyzed = false;
|
||||||
|
for (const auto& byFunc : file_funcs_it->second)
|
||||||
|
{
|
||||||
|
if (byFunc->funcPointer->GetOriginal() == search_func)
|
||||||
|
{
|
||||||
|
auto prevent_it = preventFromParallelization.find(byFunc);
|
||||||
|
if (prevent_it != preventFromParallelization.end())
|
||||||
|
preventLoopsFromParallelizations(loop, prevent_it->second, dirs_it->second);
|
||||||
|
|
||||||
|
loop_analyzed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loop_analyzed)
|
||||||
|
printInternalError(convertFileName(__FILE__).c_str(), __LINE__); //no func found for loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<map>
|
||||||
|
#include<string>
|
||||||
|
#include<vector>
|
||||||
|
#include<set>
|
||||||
|
|
||||||
|
#include "../Distribution/Distribution.h"
|
||||||
|
#include "../GraphLoop/graph_loops.h"
|
||||||
|
#include "../ParallelizationRegions/ParRegions.h"
|
||||||
|
|
||||||
|
|
||||||
|
void SelectArrayConfForParallelization(SgProject* proj, std::map<std::string, std::vector<FuncInfo*>>& funcByFile,
|
||||||
|
const std::map<std::string, std::vector<LoopGraph*>>& loopGraph,
|
||||||
|
std::map<std::string, std::vector<Directive*>>& createdDirectives,
|
||||||
|
std::map<std::string, std::vector<Messages>>& allMessages,
|
||||||
|
const std::map<DIST::Array*, std::set<DIST::Array*>>& arrayLinksByFuncCalls, const std::vector<ParallelRegion*>& regions);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "Utils/leak_detector.h"
|
#include "Utils/leak_detector.h"
|
||||||
|
|
||||||
#pragma comment(linker, "/STACK:536870912") // 512 МБ
|
#pragma comment(linker, "/STACK:536870912") // 512 МБ
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "LoopAnalyzer/loop_analyzer_nodist.h"
|
#include "LoopAnalyzer/loop_analyzer_nodist.h"
|
||||||
|
|
||||||
#include "GraphCall/graph_calls_func.h"
|
#include "GraphCall/graph_calls_func.h"
|
||||||
|
#include "GraphCall/select_array_conf.h"
|
||||||
#include "GraphLoop/graph_loops_func.h"
|
#include "GraphLoop/graph_loops_func.h"
|
||||||
#include "DynamicAnalysis/gCov_parser_func.h"
|
#include "DynamicAnalysis/gCov_parser_func.h"
|
||||||
#include "DynamicAnalysis/createParallelRegions.h"
|
#include "DynamicAnalysis/createParallelRegions.h"
|
||||||
@@ -2174,6 +2175,11 @@ static bool runAnalysis(SgProject &project, const int curr_regime, const bool ne
|
|||||||
runPrivateVariableAnalysis(loopGraph, fullIR, commonBlocks, SPF_messages);
|
runPrivateVariableAnalysis(loopGraph, fullIR, commonBlocks, SPF_messages);
|
||||||
else if (curr_regime == FIX_COMMON_BLOCKS)
|
else if (curr_regime == FIX_COMMON_BLOCKS)
|
||||||
fixCommonBlocks(allFuncInfo, commonBlocks, &project);
|
fixCommonBlocks(allFuncInfo, commonBlocks, &project);
|
||||||
|
else if (curr_regime == SELECT_ARRAY_DIM_CONF) {
|
||||||
|
map<string, vector<Messages>> localMessages;
|
||||||
|
|
||||||
|
SelectArrayConfForParallelization(&project, allFuncInfo, loopGraph, createdDirectives, localMessages, arrayLinksByFuncCalls, parallelRegions);
|
||||||
|
}
|
||||||
|
|
||||||
const float elapsed = duration_cast<milliseconds>(high_resolution_clock::now() - timeForPass).count() / 1000.;
|
const float elapsed = duration_cast<milliseconds>(high_resolution_clock::now() - timeForPass).count() / 1000.;
|
||||||
const float elapsedGlobal = duration_cast<milliseconds>(high_resolution_clock::now() - globalTime).count() / 1000.;
|
const float elapsedGlobal = duration_cast<milliseconds>(high_resolution_clock::now() - globalTime).count() / 1000.;
|
||||||
@@ -2318,7 +2324,7 @@ static void findFunctionsToInclude(bool needToAddErrors)
|
|||||||
|
|
||||||
static bool dvmInited = false;
|
static bool dvmInited = false;
|
||||||
|
|
||||||
void runPass(const int curr_regime, const char *proj_name, const char *folderName)
|
void runPass(const int curr_regime, const char *proj_name, const char *folderName, bool root_call)
|
||||||
{
|
{
|
||||||
// create full dependency graph
|
// create full dependency graph
|
||||||
switch (curr_regime)
|
switch (curr_regime)
|
||||||
@@ -2330,10 +2336,14 @@ void runPass(const int curr_regime, const char *proj_name, const char *folderNam
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init math functions of FORTRAN
|
// init math functions of FORTRAN
|
||||||
initIntrinsicFunctionNames();
|
if(root_call)
|
||||||
initTags();
|
{
|
||||||
InitPassesDependencies(passesDependencies, passesIgnoreStateDone);
|
initIntrinsicFunctionNames();
|
||||||
setPassValues();
|
initTags();
|
||||||
|
InitPassesDependencies(passesDependencies, passesIgnoreStateDone);
|
||||||
|
removalsFromPassesDependencies(passesDependencies, curr_regime);
|
||||||
|
setPassValues();
|
||||||
|
}
|
||||||
|
|
||||||
if (dvmInited == false)
|
if (dvmInited == false)
|
||||||
{
|
{
|
||||||
@@ -2360,7 +2370,7 @@ void runPass(const int curr_regime, const char *proj_name, const char *folderNam
|
|||||||
if (itDep != passesDependencies.end())
|
if (itDep != passesDependencies.end())
|
||||||
for (int k = 0; k < itDep->second.size(); ++k)
|
for (int k = 0; k < itDep->second.size(); ++k)
|
||||||
runPass(itDep->second[k], proj_name, folderName);
|
runPass(itDep->second[k], proj_name, folderName);
|
||||||
|
|
||||||
switch (curr_regime)
|
switch (curr_regime)
|
||||||
{
|
{
|
||||||
case FIND_FUNC_TO_INCLUDE:
|
case FIND_FUNC_TO_INCLUDE:
|
||||||
@@ -2384,6 +2394,8 @@ void runPass(const int curr_regime, const char *proj_name, const char *folderNam
|
|||||||
|
|
||||||
case INSERT_PARALLEL_DIRS_NODIST:
|
case INSERT_PARALLEL_DIRS_NODIST:
|
||||||
{
|
{
|
||||||
|
mpiProgram = 1;
|
||||||
|
|
||||||
string additionalName = (consoleMode && folderName == NULL) ? "__shared" : "";
|
string additionalName = (consoleMode && folderName == NULL) ? "__shared" : "";
|
||||||
|
|
||||||
runAnalysis(*project, INSERT_PARALLEL_DIRS_NODIST, false);
|
runAnalysis(*project, INSERT_PARALLEL_DIRS_NODIST, false);
|
||||||
@@ -2391,6 +2403,10 @@ void runPass(const int curr_regime, const char *proj_name, const char *folderNam
|
|||||||
runPass(REVERT_SUBST_EXPR_RD, proj_name, folderName);
|
runPass(REVERT_SUBST_EXPR_RD, proj_name, folderName);
|
||||||
|
|
||||||
runPass(CONVERT_LOOP_TO_ASSIGN, proj_name, folderName);
|
runPass(CONVERT_LOOP_TO_ASSIGN, proj_name, folderName);
|
||||||
|
|
||||||
|
runPass(SELECT_ARRAY_DIM_CONF, proj_name, folderName);
|
||||||
|
|
||||||
|
runPass(REVERT_SUBST_EXPR_RD, proj_name, folderName);
|
||||||
|
|
||||||
runAnalysis(*project, INSERT_PARALLEL_DIRS, false, consoleMode ? additionalName.c_str() : NULL, folderName);
|
runAnalysis(*project, INSERT_PARALLEL_DIRS, false, consoleMode ? additionalName.c_str() : NULL, folderName);
|
||||||
|
|
||||||
@@ -2922,9 +2938,9 @@ int main(int argc, char **argv)
|
|||||||
if (curr_regime == EMPTY_PASS)
|
if (curr_regime == EMPTY_PASS)
|
||||||
printHelp(passNames, printPasses ? EMPTY_PASS : -1);
|
printHelp(passNames, printPasses ? EMPTY_PASS : -1);
|
||||||
|
|
||||||
runPass(curr_regime, proj_name, folderName);
|
runPass(curr_regime, proj_name, folderName, true);
|
||||||
if (printText)
|
if (printText)
|
||||||
runPass(UNPARSE_FILE, proj_name, folderName);
|
runPass(UNPARSE_FILE, proj_name, folderName, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ enum passes {
|
|||||||
CHECK_ARGS_DECL,
|
CHECK_ARGS_DECL,
|
||||||
|
|
||||||
FIND_FUNC_TO_INCLUDE,
|
FIND_FUNC_TO_INCLUDE,
|
||||||
|
SELECT_ARRAY_DIM_CONF,
|
||||||
ONLY_ARRAY_GRAPH,
|
ONLY_ARRAY_GRAPH,
|
||||||
|
|
||||||
PRIVATE_ANALYSIS_SPF,
|
PRIVATE_ANALYSIS_SPF,
|
||||||
@@ -225,6 +226,7 @@ static void setPassValues()
|
|||||||
passNames[VERIFY_COMMON] = "VERIFY_COMMON";
|
passNames[VERIFY_COMMON] = "VERIFY_COMMON";
|
||||||
passNames[VERIFY_OPERATORS] = "VERIFY_OPERATORS";
|
passNames[VERIFY_OPERATORS] = "VERIFY_OPERATORS";
|
||||||
passNames[FIND_FUNC_TO_INCLUDE] = "FIND_FUNC_TO_INCLUDE";
|
passNames[FIND_FUNC_TO_INCLUDE] = "FIND_FUNC_TO_INCLUDE";
|
||||||
|
passNames[SELECT_ARRAY_DIM_CONF] = "SELECT_ARRAY_DIM_CONF";
|
||||||
passNames[CREATE_DISTR_DIRS] = "CREATE_DISTR_DIRS";
|
passNames[CREATE_DISTR_DIRS] = "CREATE_DISTR_DIRS";
|
||||||
passNames[CREATE_PARALLEL_DIRS] = "CREATE_PARALLEL_DIRS";
|
passNames[CREATE_PARALLEL_DIRS] = "CREATE_PARALLEL_DIRS";
|
||||||
passNames[INSERT_PARALLEL_DIRS] = "INSERT_PARALLEL_DIRS";
|
passNames[INSERT_PARALLEL_DIRS] = "INSERT_PARALLEL_DIRS";
|
||||||
@@ -348,4 +350,4 @@ static int getPassCode(const std::string& passName)
|
|||||||
return z;
|
return z;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
void runPass(const int curr_regime, const char *proj_name = "dvm.proj", const char *folderName = NULL);
|
void runPass(const int curr_regime, const char *proj_name = "dvm.proj", const char *folderName = NULL, bool root_call = false);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using std::set;
|
|||||||
#define list vector<Pass>
|
#define list vector<Pass>
|
||||||
|
|
||||||
static map<passes, vector<passes>> *passDeps;
|
static map<passes, vector<passes>> *passDeps;
|
||||||
|
static map<passes, set<passes>> passRemovals;
|
||||||
|
|
||||||
class Pass
|
class Pass
|
||||||
{
|
{
|
||||||
@@ -56,7 +57,7 @@ public:
|
|||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend const list& operator<<(const list &left_vec, const list &right_vec)
|
friend const list& operator<=(const list &left_vec, const list &right_vec)
|
||||||
{
|
{
|
||||||
for (int k = 0; k < right_vec.size(); ++k)
|
for (int k = 0; k < right_vec.size(); ++k)
|
||||||
{
|
{
|
||||||
@@ -69,6 +70,63 @@ public:
|
|||||||
return right_vec;
|
return right_vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend void operator-=(const list& left_vec, const list& right_vec)
|
||||||
|
{
|
||||||
|
/* erase each pass in right_vec from dependency tree for each pass in left_vec */
|
||||||
|
|
||||||
|
for(const auto& left : left_vec)
|
||||||
|
{
|
||||||
|
auto& eraseFrom = passRemovals[left.name];
|
||||||
|
|
||||||
|
for(const auto& right : right_vec)
|
||||||
|
eraseFrom.insert(right.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend void operator-=(const list& left_vec, const Pass& right)
|
||||||
|
{
|
||||||
|
left_vec -= list({right});
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator-=(const list& right_vec) const
|
||||||
|
{
|
||||||
|
list({ *this }) -= right_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator-=(const Pass& right) const
|
||||||
|
{
|
||||||
|
list({ *this }) -= list({ right });
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyRemovals() {
|
||||||
|
set<passes> to_process = { name }, processed;
|
||||||
|
|
||||||
|
while (!to_process.empty())
|
||||||
|
{
|
||||||
|
set<passes> to_process_next;
|
||||||
|
|
||||||
|
for (const auto& pass : to_process) {
|
||||||
|
if (processed.insert(pass).second) {
|
||||||
|
auto removals_it = passRemovals.find(pass);
|
||||||
|
auto deps_it = passDeps->find(pass);
|
||||||
|
|
||||||
|
if (removals_it != passRemovals.end() && deps_it != passDeps->end()) {
|
||||||
|
auto& removals = removals_it->second;
|
||||||
|
auto& deps = deps_it->second;
|
||||||
|
for (auto dep_it = deps.begin(); dep_it != deps.end();) {
|
||||||
|
if (removals.find(*dep_it) == removals.end())
|
||||||
|
to_process_next.insert(*(dep_it++));
|
||||||
|
else
|
||||||
|
dep_it = deps.erase(dep_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_process = to_process_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Pass(passes name) : name(name) { }
|
Pass(passes name) : name(name) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,20 +151,19 @@ static void depsToGraphViz(const map<passes, vector<passes>> &passDepsIn)
|
|||||||
|
|
||||||
void InitPassesDependencies(map<passes, vector<passes>> &passDepsIn, set<passes> &passesIgnoreStateDone, bool printTree = false)
|
void InitPassesDependencies(map<passes, vector<passes>> &passDepsIn, set<passes> &passesIgnoreStateDone, bool printTree = false)
|
||||||
{
|
{
|
||||||
if (passDepsIn.size() != 0)
|
passDepsIn.clear();
|
||||||
return;
|
|
||||||
|
|
||||||
passDeps = &passDepsIn;
|
passDeps = &passDepsIn;
|
||||||
|
|
||||||
Pass(PREPROC_SPF) <= Pass(CREATE_INTER_TREE);
|
Pass(PREPROC_SPF) <= Pass(CREATE_INTER_TREE);
|
||||||
|
|
||||||
list({ CREATE_INTER_TREE, CORRECT_VAR_DECL }) << list({ GCOV_PARSER, PREDICT_SCHEME, INSERT_INTER_TREE });
|
list({ CREATE_INTER_TREE, CORRECT_VAR_DECL }) <= list({ GCOV_PARSER, PREDICT_SCHEME, INSERT_INTER_TREE });
|
||||||
|
|
||||||
list({ FILE_LINE_INFO, BUILD_INCLUDE_DEPENDENCIES }) <= Pass(CORRECT_VAR_DECL);
|
list({ FILE_LINE_INFO, BUILD_INCLUDE_DEPENDENCIES }) <= Pass(CORRECT_VAR_DECL);
|
||||||
|
|
||||||
Pass(DEF_USE_STAGE1) <= Pass(DEF_USE_STAGE2);
|
Pass(DEF_USE_STAGE1) <= Pass(DEF_USE_STAGE2);
|
||||||
|
|
||||||
list({ VERIFY_DVM_DIRS, PRIVATE_CALL_GRAPH_STAGE1, PRIVATE_CALL_GRAPH_STAGE2, MACRO_EXPANSION, CONVERT_ASSIGN_TO_LOOP, DEF_USE_STAGE1, DEF_USE_STAGE2, FILL_PARALLEL_REG_IR, VERIFY_COMMON, FILL_COMMON_BLOCKS, CALL_GRAPH_IR }) << list({ SUBST_EXPR, SUBST_EXPR_RD, BUILD_IR });
|
list({ VERIFY_DVM_DIRS, PRIVATE_CALL_GRAPH_STAGE1, PRIVATE_CALL_GRAPH_STAGE2, MACRO_EXPANSION, CONVERT_ASSIGN_TO_LOOP, DEF_USE_STAGE1, DEF_USE_STAGE2, FILL_PARALLEL_REG_IR, VERIFY_COMMON, FILL_COMMON_BLOCKS, CALL_GRAPH_IR }) <= list({ SUBST_EXPR, SUBST_EXPR_RD, BUILD_IR });
|
||||||
|
|
||||||
Pass(BUILD_IR) <= Pass(SUBST_EXPR) <= Pass(SUBST_EXPR_AND_UNPARSE);
|
Pass(BUILD_IR) <= Pass(SUBST_EXPR) <= Pass(SUBST_EXPR_AND_UNPARSE);
|
||||||
|
|
||||||
@@ -118,17 +175,19 @@ void InitPassesDependencies(map<passes, vector<passes>> &passDepsIn, set<passes>
|
|||||||
|
|
||||||
Pass(GET_ALL_ARRAY_DECL) <= Pass(CALL_GRAPH_IR);
|
Pass(GET_ALL_ARRAY_DECL) <= Pass(CALL_GRAPH_IR);
|
||||||
|
|
||||||
Pass(LOOP_GRAPH) <= Pass(PRIVATE_CALL_GRAPH_STAGE3) <= Pass(PRIVATE_ANALYSIS_IR) <= list({ LOOP_ANALYZER_DATA_DIST_S0, LOOP_ANALYZER_DATA_DIST_S1, ONLY_ARRAY_GRAPH, LOOP_ANALYZER_ALIGNS, LOOP_ANALYZER_NODIST });
|
Pass(LOOP_GRAPH) <= Pass(PRIVATE_CALL_GRAPH_STAGE3) <= list(FIND_FUNC_TO_INCLUDE, PRIVATE_ANALYSIS_IR) <= list({ LOOP_ANALYZER_DATA_DIST_S0, LOOP_ANALYZER_DATA_DIST_S1, ONLY_ARRAY_GRAPH, LOOP_ANALYZER_ALIGNS });
|
||||||
|
|
||||||
|
Pass(PRIVATE_ANALYSIS_IR) <= Pass(LOOP_ANALYZER_NODIST);
|
||||||
|
|
||||||
Pass(CORRECT_VAR_DECL) <= list({ VERIFY_DVM_DIRS, PREPROC_SPF, VERIFY_ENDDO, VERIFY_INCLUDE, PREPROC_ALLOCATES, CHECK_FUNC_TO_INCLUDE, FILL_PAR_REGIONS_LINES, CONVERT_ASSIGN_TO_LOOP, VERIFY_COMMON, VERIFY_EQUIVALENCE, PRINT_PAR_REGIONS_ERRORS, VERIFY_OPERATORS, VERIFY_FORMAT }) <= Pass(CODE_CHECKER_PASSES);
|
Pass(CORRECT_VAR_DECL) <= list({ VERIFY_DVM_DIRS, PREPROC_SPF, VERIFY_ENDDO, VERIFY_INCLUDE, PREPROC_ALLOCATES, CHECK_FUNC_TO_INCLUDE, FILL_PAR_REGIONS_LINES, CONVERT_ASSIGN_TO_LOOP, VERIFY_COMMON, VERIFY_EQUIVALENCE, PRINT_PAR_REGIONS_ERRORS, VERIFY_OPERATORS, VERIFY_FORMAT }) <= Pass(CODE_CHECKER_PASSES);
|
||||||
|
|
||||||
list({ VERIFY_ENDDO, VERIFY_INCLUDE, PREPROC_SPF, PREPROC_ALLOCATES, GET_ALL_ARRAY_DECL, GCOV_PARSER }) << list({ CALL_GRAPH, MACRO_EXPANSION, DEF_USE_STAGE1 });
|
list({ VERIFY_ENDDO, VERIFY_INCLUDE, PREPROC_SPF, PREPROC_ALLOCATES, GET_ALL_ARRAY_DECL, GCOV_PARSER }) <= list({ CALL_GRAPH, MACRO_EXPANSION, DEF_USE_STAGE1 });
|
||||||
|
|
||||||
list({ VERIFY_ENDDO, VERIFY_INCLUDE, PREPROC_ALLOCATES, FILL_PARALLEL_REG_IR }) << list({ GET_ALL_ARRAY_DECL, FILL_COMMON_BLOCKS }) <= Pass(PREPROC_SPF);
|
list({ VERIFY_ENDDO, VERIFY_INCLUDE, PREPROC_ALLOCATES, FILL_PARALLEL_REG_IR }) <= list({ GET_ALL_ARRAY_DECL, FILL_COMMON_BLOCKS }) <= Pass(PREPROC_SPF);
|
||||||
|
|
||||||
Pass(CHECK_PAR_REG_DIR) <= Pass(FILL_PARALLEL_REG_IR);
|
Pass(CHECK_PAR_REG_DIR) <= Pass(FILL_PARALLEL_REG_IR);
|
||||||
|
|
||||||
list({ GET_ALL_ARRAY_DECL, CALL_GRAPH2, CODE_CHECKER_PASSES, SUBST_EXPR_RD, ARRAY_ACCESS_ANALYSIS_FOR_CORNER }) << list({ LOOP_ANALYZER_NODIST, LOOP_ANALYZER_DATA_DIST_S0, LOOP_ANALYZER_DATA_DIST_S1, ONLY_ARRAY_GRAPH });
|
list({ GET_ALL_ARRAY_DECL, CALL_GRAPH2, CODE_CHECKER_PASSES, SUBST_EXPR_RD, ARRAY_ACCESS_ANALYSIS_FOR_CORNER }) <= list({ LOOP_ANALYZER_NODIST, LOOP_ANALYZER_DATA_DIST_S0, LOOP_ANALYZER_DATA_DIST_S1, ONLY_ARRAY_GRAPH });
|
||||||
|
|
||||||
Pass(LOOP_ANALYZER_NODIST) <= Pass(INSERT_PARALLEL_DIRS_NODIST);
|
Pass(LOOP_ANALYZER_NODIST) <= Pass(INSERT_PARALLEL_DIRS_NODIST);
|
||||||
|
|
||||||
@@ -146,9 +205,9 @@ void InitPassesDependencies(map<passes, vector<passes>> &passDepsIn, set<passes>
|
|||||||
|
|
||||||
Pass(LOOP_ANALYZER_DATA_DIST_S2) <= Pass(CREATE_NESTED_LOOPS);
|
Pass(LOOP_ANALYZER_DATA_DIST_S2) <= Pass(CREATE_NESTED_LOOPS);
|
||||||
|
|
||||||
list({ CORRECT_VAR_DECL, PREPROC_SPF }) << list({ LOOP_GRAPH, CALL_GRAPH, CALL_GRAPH2 });
|
list({ CORRECT_VAR_DECL, PREPROC_SPF }) <= list({ LOOP_GRAPH, CALL_GRAPH, CALL_GRAPH2 });
|
||||||
|
|
||||||
list({ PREPROC_SPF, CALL_GRAPH2 }) <= Pass(FILL_PAR_REGIONS_LINES) <= list({ EXPAND_EXTRACT_PAR_REGION, CHECK_FUNC_TO_INCLUDE, FIND_FUNC_TO_INCLUDE, CHECK_ARGS_DECL });
|
list({ PREPROC_SPF, CALL_GRAPH2 }) <= Pass(FILL_PAR_REGIONS_LINES) <= list({ EXPAND_EXTRACT_PAR_REGION, CHECK_FUNC_TO_INCLUDE, FIND_FUNC_TO_INCLUDE, CHECK_ARGS_DECL, SELECT_ARRAY_DIM_CONF});
|
||||||
|
|
||||||
list({ PREPROC_SPF, CALL_GRAPH2, FILL_PAR_REGIONS_LINES }) <= Pass(FILL_PAR_REGIONS) <= Pass(RESOLVE_PAR_REGIONS);
|
list({ PREPROC_SPF, CALL_GRAPH2, FILL_PAR_REGIONS_LINES }) <= Pass(FILL_PAR_REGIONS) <= Pass(RESOLVE_PAR_REGIONS);
|
||||||
|
|
||||||
@@ -166,29 +225,29 @@ void InitPassesDependencies(map<passes, vector<passes>> &passDepsIn, set<passes>
|
|||||||
|
|
||||||
Pass(CALL_GRAPH2) <= list({ PURE_SAVE_TO_PARAMS, PURE_MODULE_TO_PARAMS, PURE_COMMON_TO_PARAMS, PURE_INTENT_INSERT });
|
Pass(CALL_GRAPH2) <= list({ PURE_SAVE_TO_PARAMS, PURE_MODULE_TO_PARAMS, PURE_COMMON_TO_PARAMS, PURE_INTENT_INSERT });
|
||||||
|
|
||||||
list({ REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) << list({ PURE_SAVE_TO_PARAMS, PURE_MODULE_TO_PARAMS, PURE_COMMON_TO_PARAMS, PURE_INTENT_INSERT });
|
list({ REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) <= list({ PURE_SAVE_TO_PARAMS, PURE_MODULE_TO_PARAMS, PURE_COMMON_TO_PARAMS, PURE_INTENT_INSERT });
|
||||||
|
|
||||||
list({ CORRECT_VAR_DECL, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD, VERIFY_INCLUDE }) << list({ CONVERT_TO_ENDDO, CORRECT_CODE_STYLE, REMOVE_DVM_DIRS, REMOVE_DVM_DIRS_TO_COMMENTS, REMOVE_DVM_INTERVALS });
|
list({ CORRECT_VAR_DECL, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD, VERIFY_INCLUDE }) <= list({ CONVERT_TO_ENDDO, CORRECT_CODE_STYLE, REMOVE_DVM_DIRS, REMOVE_DVM_DIRS_TO_COMMENTS, REMOVE_DVM_INTERVALS });
|
||||||
|
|
||||||
list({ CALL_GRAPH2, CONVERT_LOOP_TO_ASSIGN, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD, RESTORE_LOOP_FROM_ASSIGN }) <= Pass(INLINE_PROCEDURES);
|
list({ CALL_GRAPH2, CONVERT_LOOP_TO_ASSIGN, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD, RESTORE_LOOP_FROM_ASSIGN }) <= Pass(INLINE_PROCEDURES);
|
||||||
|
|
||||||
list({ CONVERT_LOOP_TO_ASSIGN, CORRECT_FORMAT_PLACE }) << list({ CONVERT_TO_ENDDO, CORRECT_CODE_STYLE, INSERT_INCLUDES, REMOVE_DVM_DIRS, REMOVE_DVM_DIRS_TO_COMMENTS, REMOVE_DVM_INTERVALS });
|
list({ CONVERT_LOOP_TO_ASSIGN, CORRECT_FORMAT_PLACE }) <= list({ CONVERT_TO_ENDDO, CORRECT_CODE_STYLE, INSERT_INCLUDES, REMOVE_DVM_DIRS, REMOVE_DVM_DIRS_TO_COMMENTS, REMOVE_DVM_INTERVALS });
|
||||||
|
|
||||||
list({ CORRECT_VAR_DECL, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) << list({ INSERT_INCLUDES, UNPARSE_FILE, SET_TO_ALL_DECL_INIT_ZERO });
|
list({ CORRECT_VAR_DECL, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) <= list({ INSERT_INCLUDES, UNPARSE_FILE, SET_TO_ALL_DECL_INIT_ZERO });
|
||||||
|
|
||||||
Pass(CALL_GRAPH2) <= Pass(PRIVATE_ARRAYS_SHRINKING_ANALYSIS) <= Pass(PRIVATE_ARRAYS_SHRINKING);
|
Pass(CALL_GRAPH2) <= Pass(PRIVATE_ARRAYS_SHRINKING_ANALYSIS) <= Pass(PRIVATE_ARRAYS_SHRINKING);
|
||||||
|
|
||||||
list({ CALL_GRAPH2, LOOP_ANALYZER_ALIGNS, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) << list({ PRIVATE_ARRAYS_EXPANSION, PRIVATE_ARRAYS_SHRINKING });
|
list({ CALL_GRAPH2, LOOP_ANALYZER_ALIGNS, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) <= list({ PRIVATE_ARRAYS_EXPANSION, PRIVATE_ARRAYS_SHRINKING });
|
||||||
|
|
||||||
list({ GCOV_PARSER, CREATE_INTER_TREE, CALL_GRAPH, CALL_GRAPH2 }) <= Pass(CREATE_PARALLEL_REGIONS);
|
list({ GCOV_PARSER, CREATE_INTER_TREE, CALL_GRAPH, CALL_GRAPH2 }) <= Pass(CREATE_PARALLEL_REGIONS);
|
||||||
|
|
||||||
list({ PRIVATE_CALL_GRAPH_STAGE1, PRIVATE_CALL_GRAPH_STAGE2, MACRO_EXPANSION, CONVERT_ASSIGN_TO_LOOP, DEF_USE_STAGE1, DEF_USE_STAGE2, LOOP_GRAPH, CALL_GRAPH, PRIVATE_ANALYSIS_IR }) <= Pass(INSERT_REGIONS);
|
list({ PRIVATE_CALL_GRAPH_STAGE1, PRIVATE_CALL_GRAPH_STAGE2, MACRO_EXPANSION, CONVERT_ASSIGN_TO_LOOP, DEF_USE_STAGE1, DEF_USE_STAGE2, LOOP_GRAPH, CALL_GRAPH, PRIVATE_ANALYSIS_IR, FIND_FUNC_TO_INCLUDE }) <= Pass(INSERT_REGIONS);
|
||||||
|
|
||||||
list({ LOOP_ANALYZER_DATA_DIST_S1, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) << list({ LOOPS_SPLITTER, LOOPS_COMBINER, UNROLL_LOOPS, INSERT_REGIONS });
|
list({ LOOP_ANALYZER_DATA_DIST_S1, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD }) <= list({ LOOPS_SPLITTER, LOOPS_COMBINER, UNROLL_LOOPS, INSERT_REGIONS });
|
||||||
|
|
||||||
list({ CALL_GRAPH2, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD, CONVERT_LOOP_TO_ASSIGN, RESTORE_LOOP_FROM_ASSIGN }) << list({ DUPLICATE_FUNCTIONS, REMOVE_UNUSED_FUNCTIONS });
|
list({ CALL_GRAPH2, REVERT_SUBST_EXPR, REVERT_SUBST_EXPR_RD, CONVERT_LOOP_TO_ASSIGN, RESTORE_LOOP_FROM_ASSIGN }) <= list({ DUPLICATE_FUNCTIONS, REMOVE_UNUSED_FUNCTIONS });
|
||||||
|
|
||||||
list({ CONVERT_LOOP_TO_ASSIGN, RESTORE_LOOP_FROM_ASSIGN }) << list({ LOOPS_SPLITTER, LOOPS_COMBINER, PRIVATE_ARRAYS_EXPANSION, PRIVATE_ARRAYS_SHRINKING, CREATE_PARALLEL_REGIONS, PURE_SAVE_TO_PARAMS, PURE_MODULE_TO_PARAMS, PURE_COMMON_TO_PARAMS, PURE_INTENT_INSERT });
|
list({ CONVERT_LOOP_TO_ASSIGN, RESTORE_LOOP_FROM_ASSIGN }) <= list({ LOOPS_SPLITTER, LOOPS_COMBINER, PRIVATE_ARRAYS_EXPANSION, PRIVATE_ARRAYS_SHRINKING, CREATE_PARALLEL_REGIONS, PURE_SAVE_TO_PARAMS, PURE_MODULE_TO_PARAMS, PURE_COMMON_TO_PARAMS, PURE_INTENT_INSERT });
|
||||||
|
|
||||||
list({ GET_ALL_ARRAY_DECL, FILL_PARALLEL_REG_IR }) <= Pass(CONVERT_ASSIGN_TO_LOOP);
|
list({ GET_ALL_ARRAY_DECL, FILL_PARALLEL_REG_IR }) <= Pass(CONVERT_ASSIGN_TO_LOOP);
|
||||||
|
|
||||||
@@ -196,7 +255,7 @@ void InitPassesDependencies(map<passes, vector<passes>> &passDepsIn, set<passes>
|
|||||||
|
|
||||||
list({ BUILD_IR, CALL_GRAPH }) <= Pass(LIVE_ANALYSIS_IR);
|
list({ BUILD_IR, CALL_GRAPH }) <= Pass(LIVE_ANALYSIS_IR);
|
||||||
|
|
||||||
list({ BUILD_IR, LOOP_GRAPH, LIVE_ANALYSIS_IR, FIND_FUNC_TO_INCLUDE }) <= Pass(PRIVATE_ANALYSIS_IR);
|
list({ BUILD_IR, LOOP_GRAPH, LIVE_ANALYSIS_IR }) <= Pass(PRIVATE_ANALYSIS_IR);
|
||||||
|
|
||||||
Pass(CALL_GRAPH2) <= Pass(FIX_COMMON_BLOCKS);
|
Pass(CALL_GRAPH2) <= Pass(FIX_COMMON_BLOCKS);
|
||||||
|
|
||||||
@@ -214,4 +273,13 @@ void InitPassesDependencies(map<passes, vector<passes>> &passDepsIn, set<passes>
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void removalsFromPassesDependencies(map<passes, vector<passes>>& passDepsIn, const int curr_regime)
|
||||||
|
{
|
||||||
|
passDeps = &passDepsIn;
|
||||||
|
|
||||||
|
Pass(INSERT_PARALLEL_DIRS_NODIST) -= list({ FIND_FUNC_TO_INCLUDE, CHECK_FUNC_TO_INCLUDE });
|
||||||
|
|
||||||
|
Pass(passes(curr_regime)).applyRemovals();
|
||||||
|
}
|
||||||
#undef list
|
#undef list
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ static void runPassesLoop(const vector<passes> &passesToRun, const char *prName,
|
|||||||
if (showDebug)
|
if (showDebug)
|
||||||
printf("SAPFOR: run pass %d -> %s\n", passesToRun[i], passNames[passesToRun[i]]);
|
printf("SAPFOR: run pass %d -> %s\n", passesToRun[i], passNames[passesToRun[i]]);
|
||||||
__spf_print(1, "SAPFOR: run pass %d -> %s\n", passesToRun[i], passNames[passesToRun[i]]);
|
__spf_print(1, "SAPFOR: run pass %d -> %s\n", passesToRun[i], passNames[passesToRun[i]]);
|
||||||
runPass(passesToRun[i], prName, folderNameChar);
|
runPass(passesToRun[i], prName, folderNameChar, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if __BOOST
|
#if __BOOST
|
||||||
|
|||||||
Reference in New Issue
Block a user