36#include "llvm/ADT/Statistic.h"
37#include "llvm/IR/Function.h"
38#include "llvm/Support/Debug.h"
39#include "llvm/Support/raw_ostream.h"
54#define DEBUG_TYPE "polly-ast"
63 cl::desc(
"Generate thread parallel code (isl codegen only)"),
67 cl::desc(
"Print memory access functions"),
71 "polly-parallel-force",
73 "Force generation of thread parallel code ignoring any cost model"),
77 cl::desc(
"Use context"), cl::Hidden,
81 cl::desc(
"Detect parallelism"), cl::Hidden,
86 cl::desc(
"Print the ISL abstract syntax tree"),
89STATISTIC(ScopsProcessed,
"Number of SCoPs processed");
90STATISTIC(ScopsBeneficial,
"Number of beneficial SCoPs");
91STATISTIC(BeneficialAffineLoops,
"Number of beneficial affine loops");
92STATISTIC(BeneficialBoxedLoops,
"Number of beneficial boxed loops");
95STATISTIC(NumParallel,
"Number of parallel for-loops");
96STATISTIC(NumInnermostParallel,
"Number of innermost parallel for-loops");
97STATISTIC(NumOutermostParallel,
"Number of outermost parallel for-loops");
98STATISTIC(NumReductionParallel,
"Number of reduction-parallel for-loops");
99STATISTIC(NumExecutedInParallel,
"Number of for-loops executed in parallel");
130 const std::string &
str,
145 if (!BrokenReductions || BrokenReductions->empty())
149 std::map<MemoryAccess::ReductionType, std::string> Clauses;
152 Clauses[MA->getReductionType()] +=
153 ", " + MA->getScopArrayInfo()->getName();
157 for (
const auto &ReductionClause : Clauses) {
158 str +=
" reduction (";
161 str +=
" : " + ReductionClause.second.substr(2) +
")";
173 const std::string BrokenReductionsStr =
175 const std::string KnownParallelStr =
"#pragma known-parallel";
176 const std::string DepDisPragmaStr =
"#pragma minimal dependence distance: ";
177 const std::string SimdPragmaStr =
"#pragma simd";
178 const std::string OmpPragmaStr =
"#pragma omp parallel for";
181 Printer =
printLine(Printer, DepDisPragmaStr, DD.
get());
184 Printer =
printLine(Printer, SimdPragmaStr + BrokenReductionsStr);
187 Printer =
printLine(Printer, OmpPragmaStr);
189 Printer =
printLine(Printer, KnownParallelStr + BrokenReductionsStr);
219 isl_pw_aff *MinimalDependenceDistanceIsl =
nullptr;
221 &MinimalDependenceDistanceIsl);
235 if (!MaRedPair.second)
260 BuildInfo->
Deps, Payload);
281 assert(Id &&
"Post order visit assumes annotated for nodes");
283 assert(Payload &&
"Post order visit assumes annotated for nodes");
319 BuildInfo->
InSIMD =
false;
360 if (BaseLeft && BaseLeft == BaseRight)
376 NonAliasGroup = MaxExpr.
le(MinExpr);
389 NonAliasGroup = Result;
393 NonAliasGroup = True;
395 return NonAliasGroup;
404 auto PosCond = Build.
expr_from(
S.getAssumedContext());
405 if (
S.hasTrivialInvalidContext()) {
409 auto NegCond = Build.
expr_from(
S.getInvalidContext());
421 auto &MinMaxReadWrite = MinMaxAccessPair.first;
422 auto &MinMaxReadOnly = MinMaxAccessPair.second;
423 auto RWAccEnd = MinMaxReadWrite.end();
425 for (
auto RWAccIt0 = MinMaxReadWrite.begin(); RWAccIt0 != RWAccEnd;
427 for (
auto RWAccIt1 = RWAccIt0 + 1; RWAccIt1 != RWAccEnd; ++RWAccIt1)
467 switch (isl_ast_node_get_type(Node)) {
468 case isl_ast_node_for:
470 if (IslAstInfo::isParallel(isl::manage_copy(Node)))
472 if (IslAstInfo::isInnermostParallel(isl::manage_copy(Node)))
473 NumInnermostParallel++;
474 if (IslAstInfo::isOutermostParallel(isl::manage_copy(Node)))
475 NumOutermostParallel++;
476 if (IslAstInfo::isReductionParallel(isl::manage_copy(Node)))
477 NumReductionParallel++;
478 if (IslAstInfo::isExecutedInParallel(isl::manage_copy(Node)))
479 NumExecutedInParallel++;
482 case isl_ast_node_if:
505 auto ScheduleTree =
S.getScheduleTree();
511 auto ScopStats =
S.getStatistics();
513 BeneficialAffineLoops += ScopStats.NumAffineLoops;
514 BeneficialBoxedLoops += ScopStats.NumBoxedLoops;
516 auto Ctx =
S.getIslCtx();
530 if (PerformParallelTest) {
651 dbgs() <<
"Got dependence analysis for different SCoP/isl_ctx\n");
655 std::unique_ptr<IslAstInfo> Ast = std::make_unique<IslAstInfo>(
Scop, D);
698 P,
MemAcc->getLatestScopArrayInfo()->getName().c_str());
716 Function &F =
S.getFunction();
718 OS <<
":: isl ast :: " << F.getName() <<
" :: " <<
S.getNameStr() <<
"\n";
721 OS <<
":: isl ast generation and code generation was skipped!\n\n";
722 OS <<
":: This is either because no useful optimizations could be applied "
723 "(use -polly-process-unprofitable to enforce code generation) or "
724 "because earlier passes such as dependence analysis timed out (use "
725 "-polly-dependences-computeout=0 to set dependence analysis timeout "
731 char *RtCStr, *AstStr;
750 dbgs() <<
S.getContextStr() <<
"\n";
751 dbgs() << stringFromIslObj(
S.getScheduleTree(),
"null");
753 OS <<
"\nif (" << RtCStr <<
")\n\n";
754 OS << AstStr <<
"\n";
756 OS <<
" { /* original code */ }\n\n";
764std::unique_ptr<IslAstInfo>
770 std::unique_ptr<IslAstInfo> Result =
runIslAst(
S, GetDeps);
772 outs() <<
"Printing analysis 'Polly - Generate an AST of the SCoP (isl)'"
773 <<
S.getName() <<
"' in function '" <<
S.getFunction().getName()
776 Result->print(llvm::outs());
static cl::opt< bool > UseContext("polly-ast-use-context", cl::desc("Use context"), cl::Hidden, cl::init(true), cl::cat(PollyCategory))
static cl::opt< bool > PollyPrintAst("polly-print-ast", cl::desc("Print the ISL abstract syntax tree"), cl::cat(PollyCategory))
static isl::ast_expr buildCondition(Scop &S, isl::ast_build Build, const Scop::MinMaxAccessTy *It0, const Scop::MinMaxAccessTy *It1)
static cl::opt< bool > DetectParallel("polly-ast-detect-parallel", cl::desc("Detect parallelism"), cl::Hidden, cl::cat(PollyCategory))
static isl_printer * printLine(__isl_take isl_printer *Printer, const std::string &str, __isl_keep isl_pw_aff *PWA=nullptr)
Print a string str in a single line using Printer.
static __isl_give isl_id * astBuildBeforeFor(__isl_keep isl_ast_build *Build, void *User)
static __isl_give isl_ast_node * astBuildAfterMark(__isl_take isl_ast_node *Node, __isl_keep isl_ast_build *Build, void *User)
IslAstInfo::IslAstUserPayload IslAstUserPayload
static void walkAstForStatistics(const isl::ast_node &Ast)
Collect statistics for the syntax tree rooted at Ast.
static cl::opt< bool > PollyParallelForce("polly-parallel-force", cl::desc("Force generation of thread parallel code ignoring any cost model"), cl::cat(PollyCategory))
static bool benefitsFromPolly(Scop &Scop, bool PerformParallelTest)
Simple cost analysis for a given SCoP.
static __isl_give isl_ast_node * astBuildAfterFor(__isl_take isl_ast_node *Node, __isl_keep isl_ast_build *Build, void *User)
STATISTIC(ScopsProcessed, "Number of SCoPs processed")
static __isl_give isl_ast_node * AtEachDomain(__isl_take isl_ast_node *Node, __isl_keep isl_ast_build *Build, void *User)
static bool astScheduleDimIsParallel(const isl::ast_build &Build, const Dependences *D, IslAstUserPayload *NodeInfo)
Check if the current scheduling dimension is parallel.
static isl_printer * cbPrintFor(__isl_take isl_printer *Printer, __isl_take isl_ast_print_options *Options, __isl_keep isl_ast_node *Node, void *)
Callback executed for each for node in the ast in order to print it.
static cl::opt< bool > PrintAccesses("polly-ast-print-accesses", cl::desc("Print memory access functions"), cl::cat(PollyCategory))
static std::string getBrokenReductionsStr(const isl::ast_node &Node)
Return all broken reductions as a string of clauses (OpenMP style).
static isl_stat astBuildBeforeMark(__isl_keep isl_id *MarkId, __isl_keep isl_ast_build *Build, void *User)
static __isl_give isl_printer * cbPrintUser(__isl_take isl_printer *P, __isl_take isl_ast_print_options *O, __isl_keep isl_ast_node *Node, void *User)
static cl::opt< bool > PollyParallel("polly-parallel", cl::desc("Generate thread parallel code (isl codegen only)"), cl::cat(PollyCategory))
static void freeIslAstUserPayload(void *Ptr)
Free an IslAstUserPayload object pointed to by Ptr.
static std::unique_ptr< IslAstInfo > runIslAst(Scop &Scop, function_ref< const Dependences &(Dependences::AnalysisLevel)> GetDeps)
llvm::cl::OptionCategory PollyCategory
__isl_give isl_printer * isl_printer_print_pw_aff(__isl_take isl_printer *p, __isl_keep isl_pw_aff *pwaff)
__isl_give isl_ast_node * isl_ast_node_set_annotation(__isl_take isl_ast_node *node, __isl_take isl_id *annotation)
__isl_give isl_printer * isl_ast_node_print(__isl_keep isl_ast_node *node, __isl_take isl_printer *p, __isl_take isl_ast_print_options *options)
isl_stat isl_ast_node_foreach_descendant_top_down(__isl_keep isl_ast_node *node, isl_bool(*fn)(__isl_keep isl_ast_node *node, void *user), void *user)
__isl_give isl_ast_print_options * isl_ast_print_options_alloc(isl_ctx *ctx)
__isl_give isl_ast_expr * isl_ast_expr_or(__isl_take isl_ast_expr *expr1, __isl_take isl_ast_expr *expr2)
__isl_give isl_ast_print_options * isl_ast_print_options_set_print_user(__isl_take isl_ast_print_options *options, __isl_give isl_printer *(*print_user)(__isl_take isl_printer *p, __isl_take isl_ast_print_options *options, __isl_keep isl_ast_node *node, void *user), void *user)
__isl_give isl_printer * isl_ast_node_for_print(__isl_keep isl_ast_node *node, __isl_take isl_printer *p, __isl_take isl_ast_print_options *options)
__isl_give isl_ast_print_options * isl_ast_print_options_set_print_for(__isl_take isl_ast_print_options *options, __isl_give isl_printer *(*print_for)(__isl_take isl_printer *p, __isl_take isl_ast_print_options *options, __isl_keep isl_ast_node *node, void *user), void *user)
__isl_export __isl_give isl_id * isl_ast_node_mark_get_id(__isl_keep isl_ast_node *node)
__isl_give isl_id * isl_ast_node_get_annotation(__isl_keep isl_ast_node *node)
__isl_null isl_ast_print_options * isl_ast_print_options_free(__isl_take isl_ast_print_options *options)
__isl_give isl_ast_expr * isl_ast_expr_and(__isl_take isl_ast_expr *expr1, __isl_take isl_ast_expr *expr2)
__isl_give isl_printer * isl_printer_print_ast_expr(__isl_take isl_printer *p, __isl_keep isl_ast_expr *expr)
__isl_give isl_ast_build * isl_ast_build_set_before_each_mark(__isl_take isl_ast_build *build, isl_stat(*fn)(__isl_keep isl_id *mark, __isl_keep isl_ast_build *build, void *user), void *user)
isl_stat isl_options_set_ast_build_detect_min_max(isl_ctx *ctx, int val)
__isl_give isl_ast_build * isl_ast_build_set_before_each_for(__isl_take isl_ast_build *build, __isl_give isl_id *(*fn)(__isl_keep isl_ast_build *build, void *user), void *user)
__isl_export __isl_give isl_ast_build * isl_ast_build_from_context(__isl_take isl_set *set)
isl_stat isl_options_set_ast_build_atomic_upper_bound(isl_ctx *ctx, int val)
__isl_give isl_ast_build * isl_ast_build_set_after_each_mark(__isl_take isl_ast_build *build, __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, __isl_keep isl_ast_build *build, void *user), void *user)
__isl_null isl_ast_build * isl_ast_build_free(__isl_take isl_ast_build *build)
__isl_overload __isl_give isl_ast_node * isl_ast_build_node_from_schedule(__isl_keep isl_ast_build *build, __isl_take isl_schedule *schedule)
__isl_export __isl_give isl_ast_build * isl_ast_build_set_at_each_domain(__isl_take isl_ast_build *build, __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, __isl_keep isl_ast_build *build, void *user), void *user)
isl_ctx * isl_ast_build_get_ctx(__isl_keep isl_ast_build *build)
__isl_give isl_ast_build * isl_ast_build_set_after_each_for(__isl_take isl_ast_build *build, __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node, __isl_keep isl_ast_build *build, void *user), void *user)
isl::ast_expr expr_from(isl::pw_aff pa) const
isl::union_map get_schedule() const
isl::ast_expr access_from(isl::multi_pw_aff mpa) const
isl::ast_expr le(isl::ast_expr expr2) const
static isl::ast_expr from_val(isl::val v)
isl::ast_expr get_op_arg(int pos) const
__isl_give isl_ast_expr * release()
isl::ast_expr eq(isl::ast_expr expr2) const
__isl_keep isl_ast_expr * get() const
isl::ast_expr address_of() const
isl::ast_expr expr() const
isl::id get_annotation() const
__isl_keep isl_ast_node * get() const
__isl_keep isl_pw_aff * get() const
isl::id get_tuple_id(isl::dim type) const
isl::pw_multi_aff intersect_params(isl::set set) const
__isl_give isl_union_map * release()
__isl_keep isl_union_map * get() const
static isl::val int_from_ui(isl::ctx ctx, unsigned long u)
static isl::val zero(isl::ctx ctx)
The accumulated dependence information for a SCoP.
bool isParallel(__isl_keep isl_union_map *Schedule, __isl_take isl_union_map *Deps, __isl_give isl_pw_aff **MinDistancePtr=nullptr) const
Check if a partial schedule is parallel wrt to Deps.
bool hasValidDependences() const
Report if valid dependences are available.
const std::shared_ptr< isl_ctx > & getSharedIslCtx() const
isl::union_map getDependences(int Kinds) const
Get the dependences of type Kinds.
__isl_give isl_map * getReductionDependences(MemoryAccess *MA) const
Return the reduction dependences caused by MA.
static bool isOutermostParallel(const isl::ast_node &Node)
Is this loop an outermost parallel loop?
static MemoryAccessSet * getBrokenReductions(const isl::ast_node &Node)
Get the nodes broken reductions or a nullptr if not available.
static bool isParallel(const isl::ast_node &Node)
Is this loop a parallel loop?
static isl::pw_aff getMinimalDependenceDistance(const isl::ast_node &Node)
Get minimal dependence distance or nullptr if not available.
static bool isInnermostParallel(const isl::ast_node &Node)
Is this loop an innermost parallel loop?
isl::ast_node getAst()
Return a copy of the AST root node.
SmallPtrSet< MemoryAccess *, 4 > MemoryAccessSet
static bool isExecutedInParallel(const isl::ast_node &Node)
Will the loop be run as thread parallel?
isl::ast_expr getRunCondition()
Get the run condition.
static bool isInnermost(const isl::ast_node &Node)
Is this loop an innermost loop?
static IslAstUserPayload * getNodePayload(const isl::ast_node &Node)
static isl::union_map getSchedule(const isl::ast_node &Node)
Get the nodes schedule or a nullptr if not available.
void print(raw_ostream &O)
static isl::ast_build getBuild(const isl::ast_node &Node)
Get the nodes build context or a nullptr if not available.
static bool isReductionParallel(const isl::ast_node &Node)
Is this loop a reduction parallel loop?
const std::shared_ptr< isl_ctx > getSharedIslCtx() const
static isl::ast_expr buildRunCondition(Scop &S, const isl::ast_build &Build)
Build run-time condition for scop.
isl::ast_expr getRunCondition()
Get the run-time conditions for the Scop.
IslAst(const IslAst &)=delete
std::shared_ptr< isl_ctx > Ctx
static IslAst create(Scop &Scop, const Dependences &D)
isl::ast_expr RunCondition
void init(const Dependences &D)
Represent memory accesses in statements.
std::string getReductionOperatorStr() const
Return a string representation of the access's reduction type.
A class to store information about arrays in the SCoP.
static const ScopArrayInfo * getFromId(isl::id Id)
Access the ScopArrayInfo associated with an isl Id.
const ScopArrayInfo * getBasePtrOriginSAI() const
For indirect accesses return the origin SAI of the BP, else null.
const char * getBaseName() const
const MinMaxVectorPairVectorTy & getAliasGroups() const
Return all alias groups for this SCoP.
std::pair< MinMaxVectorTy, MinMaxVectorTy > MinMaxVectorPairTy
Pair of minimal/maximal access vectors representing read write and read only accesses.
const std::shared_ptr< isl_ctx > & getSharedIslCtx() const
Directly return the shared_ptr of the context.
std::pair< isl::pw_multi_aff, isl::pw_multi_aff > MinMaxAccessTy
Type to represent a pair of minimal/maximal access to an array.
bool isOptimized() const
Check if the SCoP has been optimized by the scheduler.
__isl_export __isl_keep const char * isl_id_get_name(__isl_keep isl_id *id)
__isl_null isl_id * isl_id_free(__isl_take isl_id *id)
void * isl_id_get_user(__isl_keep isl_id *id)
__isl_give isl_id * isl_id_alloc(isl_ctx *ctx, __isl_keep const char *name, void *user)
__isl_give isl_id * isl_id_set_free_user(__isl_take isl_id *id, void(*free_user)(void *user))
enum isl_ast_node_type isl_ast_node_get_type(__isl_keep isl_ast_node *node)
aff manage_copy(__isl_keep isl_aff *ptr)
boolean manage(isl_bool val)
VectorizerChoice PollyVectorizerChoice
bool PollyProcessUnprofitable
std::unique_ptr< IslAstInfo > runIslAstGen(Scop &S, DependenceAnalysis::Result &DA)
__isl_null isl_printer * isl_printer_free(__isl_take isl_printer *printer)
__isl_give isl_printer * isl_printer_flush(__isl_take isl_printer *p)
__isl_give char * isl_printer_get_str(__isl_keep isl_printer *printer)
__isl_give isl_printer * isl_printer_print_str(__isl_take isl_printer *p, const char *s)
__isl_give isl_printer * isl_printer_start_line(__isl_take isl_printer *p)
__isl_give isl_printer * isl_printer_indent(__isl_take isl_printer *p, int indent)
__isl_give isl_printer * isl_printer_to_str(isl_ctx *ctx)
__isl_give isl_printer * isl_printer_end_line(__isl_take isl_printer *p)
__isl_give isl_printer * isl_printer_set_output_format(__isl_take isl_printer *p, int output_format)
__isl_export __isl_give isl_set * isl_set_universe(__isl_take isl_space *space)
Temporary information used when building the ast.
AstBuildUserInfo()=default
Construct and initialize the helper struct for AST creation.
bool InParallelFor
Flag to indicate that we are inside a parallel for node.
isl_id * LastForNodeId
The last iterator id created for the current SCoP.
const Dependences * Deps
The dependence information used for the parallelism check.
bool InSIMD
Flag to indicate that we are inside an SIMD node.
const Dependences & getDependences(Dependences::AnalysisLevel Level)
Return the dependence information for the current SCoP.
Payload information used to annotate an AST node.
bool IsInnermost
Flag to mark innermost loops.
bool IsOutermostParallel
Flag to mark outermost parallel loops.
bool IsParallel
Does the dependence analysis determine that there are no loop-carried dependencies?
MemoryAccessSet BrokenReductions
Set of accesses which break reduction dependences.
isl::pw_aff MinimalDependenceDistance
The minimal dependence distance for non parallel loops.
bool IsReductionParallel
Flag to mark parallel loops which break reductions.
bool IsInnermostParallel
Flag to mark innermost parallel loops.
isl::ast_build Build
The build environment at the time this node was constructed.