123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825 |
- //===- IslAst.cpp - isl code generator interface --------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // The isl code generator interface takes a Scop and generates an isl_ast. This
- // ist_ast can either be returned directly or it can be pretty printed to
- // stdout.
- //
- // A typical isl_ast output looks like this:
- //
- // for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) {
- // bb2(c2);
- // }
- //
- // An in-depth discussion of our AST generation approach can be found in:
- //
- // Polyhedral AST generation is more than scanning polyhedra
- // Tobias Grosser, Sven Verdoolaege, Albert Cohen
- // ACM Transactions on Programming Languages and Systems (TOPLAS),
- // 37(4), July 2015
- // http://www.grosser.es/#pub-polyhedral-AST-generation
- //
- //===----------------------------------------------------------------------===//
- #include "polly/CodeGen/IslAst.h"
- #include "polly/CodeGen/CodeGeneration.h"
- #include "polly/DependenceInfo.h"
- #include "polly/LinkAllPasses.h"
- #include "polly/Options.h"
- #include "polly/ScopDetection.h"
- #include "polly/ScopInfo.h"
- #include "polly/ScopPass.h"
- #include "polly/Support/GICHelper.h"
- #include "llvm/ADT/Statistic.h"
- #include "llvm/IR/Function.h"
- #include "llvm/Support/Debug.h"
- #include "llvm/Support/raw_ostream.h"
- #include "isl/aff.h"
- #include "isl/ast.h"
- #include "isl/ast_build.h"
- #include "isl/id.h"
- #include "isl/isl-noexceptions.h"
- #include "isl/printer.h"
- #include "isl/schedule.h"
- #include "isl/set.h"
- #include "isl/union_map.h"
- #include "isl/val.h"
- #include <cassert>
- #include <cstdlib>
- #define DEBUG_TYPE "polly-ast"
- using namespace llvm;
- using namespace polly;
- using IslAstUserPayload = IslAstInfo::IslAstUserPayload;
- static cl::opt<bool>
- PollyParallel("polly-parallel",
- cl::desc("Generate thread parallel code (isl codegen only)"),
- cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
- static cl::opt<bool> PrintAccesses("polly-ast-print-accesses",
- cl::desc("Print memory access functions"),
- cl::init(false), cl::ZeroOrMore,
- cl::cat(PollyCategory));
- static cl::opt<bool> PollyParallelForce(
- "polly-parallel-force",
- cl::desc(
- "Force generation of thread parallel code ignoring any cost model"),
- cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
- static cl::opt<bool> UseContext("polly-ast-use-context",
- cl::desc("Use context"), cl::Hidden,
- cl::init(true), cl::ZeroOrMore,
- cl::cat(PollyCategory));
- static cl::opt<bool> DetectParallel("polly-ast-detect-parallel",
- cl::desc("Detect parallelism"), cl::Hidden,
- cl::init(false), cl::ZeroOrMore,
- cl::cat(PollyCategory));
- STATISTIC(ScopsProcessed, "Number of SCoPs processed");
- STATISTIC(ScopsBeneficial, "Number of beneficial SCoPs");
- STATISTIC(BeneficialAffineLoops, "Number of beneficial affine loops");
- STATISTIC(BeneficialBoxedLoops, "Number of beneficial boxed loops");
- STATISTIC(NumForLoops, "Number of for-loops");
- STATISTIC(NumParallel, "Number of parallel for-loops");
- STATISTIC(NumInnermostParallel, "Number of innermost parallel for-loops");
- STATISTIC(NumOutermostParallel, "Number of outermost parallel for-loops");
- STATISTIC(NumReductionParallel, "Number of reduction-parallel for-loops");
- STATISTIC(NumExecutedInParallel, "Number of for-loops executed in parallel");
- STATISTIC(NumIfConditions, "Number of if-conditions");
- namespace polly {
- /// Temporary information used when building the ast.
- struct AstBuildUserInfo {
- /// Construct and initialize the helper struct for AST creation.
- AstBuildUserInfo() = default;
- /// The dependence information used for the parallelism check.
- const Dependences *Deps = nullptr;
- /// Flag to indicate that we are inside a parallel for node.
- bool InParallelFor = false;
- /// Flag to indicate that we are inside an SIMD node.
- bool InSIMD = false;
- /// The last iterator id created for the current SCoP.
- isl_id *LastForNodeId = nullptr;
- };
- } // namespace polly
- /// Free an IslAstUserPayload object pointed to by @p Ptr.
- static void freeIslAstUserPayload(void *Ptr) {
- delete ((IslAstInfo::IslAstUserPayload *)Ptr);
- }
- /// Print a string @p str in a single line using @p Printer.
- static isl_printer *printLine(__isl_take isl_printer *Printer,
- const std::string &str,
- __isl_keep isl_pw_aff *PWA = nullptr) {
- Printer = isl_printer_start_line(Printer);
- Printer = isl_printer_print_str(Printer, str.c_str());
- if (PWA)
- Printer = isl_printer_print_pw_aff(Printer, PWA);
- return isl_printer_end_line(Printer);
- }
- /// Return all broken reductions as a string of clauses (OpenMP style).
- static const std::string getBrokenReductionsStr(const isl::ast_node &Node) {
- IslAstInfo::MemoryAccessSet *BrokenReductions;
- std::string str;
- BrokenReductions = IslAstInfo::getBrokenReductions(Node);
- if (!BrokenReductions || BrokenReductions->empty())
- return "";
- // Map each type of reduction to a comma separated list of the base addresses.
- std::map<MemoryAccess::ReductionType, std::string> Clauses;
- for (MemoryAccess *MA : *BrokenReductions)
- if (MA->isWrite())
- Clauses[MA->getReductionType()] +=
- ", " + MA->getScopArrayInfo()->getName();
- // Now print the reductions sorted by type. Each type will cause a clause
- // like: reduction (+ : sum0, sum1, sum2)
- for (const auto &ReductionClause : Clauses) {
- str += " reduction (";
- str += MemoryAccess::getReductionOperatorStr(ReductionClause.first);
- // Remove the first two symbols (", ") to make the output look pretty.
- str += " : " + ReductionClause.second.substr(2) + ")";
- }
- return str;
- }
- /// Callback executed for each for node in the ast in order to print it.
- static isl_printer *cbPrintFor(__isl_take isl_printer *Printer,
- __isl_take isl_ast_print_options *Options,
- __isl_keep isl_ast_node *Node, void *) {
- isl::pw_aff DD =
- IslAstInfo::getMinimalDependenceDistance(isl::manage_copy(Node));
- const std::string BrokenReductionsStr =
- getBrokenReductionsStr(isl::manage_copy(Node));
- const std::string KnownParallelStr = "#pragma known-parallel";
- const std::string DepDisPragmaStr = "#pragma minimal dependence distance: ";
- const std::string SimdPragmaStr = "#pragma simd";
- const std::string OmpPragmaStr = "#pragma omp parallel for";
- if (!DD.is_null())
- Printer = printLine(Printer, DepDisPragmaStr, DD.get());
- if (IslAstInfo::isInnermostParallel(isl::manage_copy(Node)))
- Printer = printLine(Printer, SimdPragmaStr + BrokenReductionsStr);
- if (IslAstInfo::isExecutedInParallel(isl::manage_copy(Node)))
- Printer = printLine(Printer, OmpPragmaStr);
- else if (IslAstInfo::isOutermostParallel(isl::manage_copy(Node)))
- Printer = printLine(Printer, KnownParallelStr + BrokenReductionsStr);
- return isl_ast_node_for_print(Node, Printer, Options);
- }
- /// Check if the current scheduling dimension is parallel.
- ///
- /// In case the dimension is parallel we also check if any reduction
- /// dependences is broken when we exploit this parallelism. If so,
- /// @p IsReductionParallel will be set to true. The reduction dependences we use
- /// to check are actually the union of the transitive closure of the initial
- /// reduction dependences together with their reversal. Even though these
- /// dependences connect all iterations with each other (thus they are cyclic)
- /// we can perform the parallelism check as we are only interested in a zero
- /// (or non-zero) dependence distance on the dimension in question.
- static bool astScheduleDimIsParallel(const isl::ast_build &Build,
- const Dependences *D,
- IslAstUserPayload *NodeInfo) {
- if (!D->hasValidDependences())
- return false;
- isl::union_map Schedule = Build.get_schedule();
- isl::union_map Dep = D->getDependences(
- Dependences::TYPE_RAW | Dependences::TYPE_WAW | Dependences::TYPE_WAR);
- if (!D->isParallel(Schedule.get(), Dep.release())) {
- isl::union_map DepsAll =
- D->getDependences(Dependences::TYPE_RAW | Dependences::TYPE_WAW |
- Dependences::TYPE_WAR | Dependences::TYPE_TC_RED);
- // TODO: We will need to change isParallel to stop the unwrapping
- isl_pw_aff *MinimalDependenceDistanceIsl = nullptr;
- D->isParallel(Schedule.get(), DepsAll.release(),
- &MinimalDependenceDistanceIsl);
- NodeInfo->MinimalDependenceDistance =
- isl::manage(MinimalDependenceDistanceIsl);
- return false;
- }
- isl::union_map RedDeps = D->getDependences(Dependences::TYPE_TC_RED);
- if (!D->isParallel(Schedule.get(), RedDeps.release()))
- NodeInfo->IsReductionParallel = true;
- if (!NodeInfo->IsReductionParallel)
- return true;
- for (const auto &MaRedPair : D->getReductionDependences()) {
- if (!MaRedPair.second)
- continue;
- isl::union_map MaRedDeps = isl::manage_copy(MaRedPair.second);
- if (!D->isParallel(Schedule.get(), MaRedDeps.release()))
- NodeInfo->BrokenReductions.insert(MaRedPair.first);
- }
- return true;
- }
- // This method is executed before the construction of a for node. It creates
- // an isl_id that is used to annotate the subsequently generated ast for nodes.
- //
- // In this function we also run the following analyses:
- //
- // - Detection of openmp parallel loops
- //
- static __isl_give isl_id *astBuildBeforeFor(__isl_keep isl_ast_build *Build,
- void *User) {
- AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
- IslAstUserPayload *Payload = new IslAstUserPayload();
- isl_id *Id = isl_id_alloc(isl_ast_build_get_ctx(Build), "", Payload);
- Id = isl_id_set_free_user(Id, freeIslAstUserPayload);
- BuildInfo->LastForNodeId = Id;
- Payload->IsParallel = astScheduleDimIsParallel(isl::manage_copy(Build),
- BuildInfo->Deps, Payload);
- // Test for parallelism only if we are not already inside a parallel loop
- if (!BuildInfo->InParallelFor && !BuildInfo->InSIMD)
- BuildInfo->InParallelFor = Payload->IsOutermostParallel =
- Payload->IsParallel;
- return Id;
- }
- // This method is executed after the construction of a for node.
- //
- // It performs the following actions:
- //
- // - Reset the 'InParallelFor' flag, as soon as we leave a for node,
- // that is marked as openmp parallel.
- //
- static __isl_give isl_ast_node *
- astBuildAfterFor(__isl_take isl_ast_node *Node, __isl_keep isl_ast_build *Build,
- void *User) {
- isl_id *Id = isl_ast_node_get_annotation(Node);
- assert(Id && "Post order visit assumes annotated for nodes");
- IslAstUserPayload *Payload = (IslAstUserPayload *)isl_id_get_user(Id);
- assert(Payload && "Post order visit assumes annotated for nodes");
- AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
- assert(Payload->Build.is_null() && "Build environment already set");
- Payload->Build = isl::manage_copy(Build);
- Payload->IsInnermost = (Id == BuildInfo->LastForNodeId);
- Payload->IsInnermostParallel =
- Payload->IsInnermost && (BuildInfo->InSIMD || Payload->IsParallel);
- if (Payload->IsOutermostParallel)
- BuildInfo->InParallelFor = false;
- isl_id_free(Id);
- return Node;
- }
- static isl_stat astBuildBeforeMark(__isl_keep isl_id *MarkId,
- __isl_keep isl_ast_build *Build,
- void *User) {
- if (!MarkId)
- return isl_stat_error;
- AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
- if (strcmp(isl_id_get_name(MarkId), "SIMD") == 0)
- BuildInfo->InSIMD = true;
- return isl_stat_ok;
- }
- static __isl_give isl_ast_node *
- astBuildAfterMark(__isl_take isl_ast_node *Node,
- __isl_keep isl_ast_build *Build, void *User) {
- assert(isl_ast_node_get_type(Node) == isl_ast_node_mark);
- AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
- auto *Id = isl_ast_node_mark_get_id(Node);
- if (strcmp(isl_id_get_name(Id), "SIMD") == 0)
- BuildInfo->InSIMD = false;
- isl_id_free(Id);
- return Node;
- }
- static __isl_give isl_ast_node *AtEachDomain(__isl_take isl_ast_node *Node,
- __isl_keep isl_ast_build *Build,
- void *User) {
- assert(!isl_ast_node_get_annotation(Node) && "Node already annotated");
- IslAstUserPayload *Payload = new IslAstUserPayload();
- isl_id *Id = isl_id_alloc(isl_ast_build_get_ctx(Build), "", Payload);
- Id = isl_id_set_free_user(Id, freeIslAstUserPayload);
- Payload->Build = isl::manage_copy(Build);
- return isl_ast_node_set_annotation(Node, Id);
- }
- // Build alias check condition given a pair of minimal/maximal access.
- static isl::ast_expr buildCondition(Scop &S, isl::ast_build Build,
- const Scop::MinMaxAccessTy *It0,
- const Scop::MinMaxAccessTy *It1) {
- isl::pw_multi_aff AFirst = It0->first;
- isl::pw_multi_aff ASecond = It0->second;
- isl::pw_multi_aff BFirst = It1->first;
- isl::pw_multi_aff BSecond = It1->second;
- isl::id Left = AFirst.get_tuple_id(isl::dim::set);
- isl::id Right = BFirst.get_tuple_id(isl::dim::set);
- isl::ast_expr True =
- isl::ast_expr::from_val(isl::val::int_from_ui(Build.ctx(), 1));
- isl::ast_expr False =
- isl::ast_expr::from_val(isl::val::int_from_ui(Build.ctx(), 0));
- const ScopArrayInfo *BaseLeft =
- ScopArrayInfo::getFromId(Left)->getBasePtrOriginSAI();
- const ScopArrayInfo *BaseRight =
- ScopArrayInfo::getFromId(Right)->getBasePtrOriginSAI();
- if (BaseLeft && BaseLeft == BaseRight)
- return True;
- isl::set Params = S.getContext();
- isl::ast_expr NonAliasGroup, MinExpr, MaxExpr;
- // In the following, we first check if any accesses will be empty under
- // the execution context of the scop and do not code generate them if this
- // is the case as isl will fail to derive valid AST expressions for such
- // accesses.
- if (!AFirst.intersect_params(Params).domain().is_empty() &&
- !BSecond.intersect_params(Params).domain().is_empty()) {
- MinExpr = Build.access_from(AFirst).address_of();
- MaxExpr = Build.access_from(BSecond).address_of();
- NonAliasGroup = MaxExpr.le(MinExpr);
- }
- if (!BFirst.intersect_params(Params).domain().is_empty() &&
- !ASecond.intersect_params(Params).domain().is_empty()) {
- MinExpr = Build.access_from(BFirst).address_of();
- MaxExpr = Build.access_from(ASecond).address_of();
- isl::ast_expr Result = MaxExpr.le(MinExpr);
- if (!NonAliasGroup.is_null())
- NonAliasGroup = isl::manage(
- isl_ast_expr_or(NonAliasGroup.release(), Result.release()));
- else
- NonAliasGroup = Result;
- }
- if (NonAliasGroup.is_null())
- NonAliasGroup = True;
- return NonAliasGroup;
- }
- isl::ast_expr IslAst::buildRunCondition(Scop &S, const isl::ast_build &Build) {
- isl::ast_expr RunCondition;
- // The conditions that need to be checked at run-time for this scop are
- // available as an isl_set in the runtime check context from which we can
- // directly derive a run-time condition.
- auto PosCond = Build.expr_from(S.getAssumedContext());
- if (S.hasTrivialInvalidContext()) {
- RunCondition = std::move(PosCond);
- } else {
- auto ZeroV = isl::val::zero(Build.ctx());
- auto NegCond = Build.expr_from(S.getInvalidContext());
- auto NotNegCond =
- isl::ast_expr::from_val(std::move(ZeroV)).eq(std::move(NegCond));
- RunCondition =
- isl::manage(isl_ast_expr_and(PosCond.release(), NotNegCond.release()));
- }
- // Create the alias checks from the minimal/maximal accesses in each alias
- // group which consists of read only and non read only (read write) accesses.
- // This operation is by construction quadratic in the read-write pointers and
- // linear in the read only pointers in each alias group.
- for (const Scop::MinMaxVectorPairTy &MinMaxAccessPair : S.getAliasGroups()) {
- auto &MinMaxReadWrite = MinMaxAccessPair.first;
- auto &MinMaxReadOnly = MinMaxAccessPair.second;
- auto RWAccEnd = MinMaxReadWrite.end();
- for (auto RWAccIt0 = MinMaxReadWrite.begin(); RWAccIt0 != RWAccEnd;
- ++RWAccIt0) {
- for (auto RWAccIt1 = RWAccIt0 + 1; RWAccIt1 != RWAccEnd; ++RWAccIt1)
- RunCondition = isl::manage(isl_ast_expr_and(
- RunCondition.release(),
- buildCondition(S, Build, RWAccIt0, RWAccIt1).release()));
- for (const Scop::MinMaxAccessTy &ROAccIt : MinMaxReadOnly)
- RunCondition = isl::manage(isl_ast_expr_and(
- RunCondition.release(),
- buildCondition(S, Build, RWAccIt0, &ROAccIt).release()));
- }
- }
- return RunCondition;
- }
- /// Simple cost analysis for a given SCoP.
- ///
- /// TODO: Improve this analysis and extract it to make it usable in other
- /// places too.
- /// In order to improve the cost model we could either keep track of
- /// performed optimizations (e.g., tiling) or compute properties on the
- /// original as well as optimized SCoP (e.g., #stride-one-accesses).
- static bool benefitsFromPolly(Scop &Scop, bool PerformParallelTest) {
- if (PollyProcessUnprofitable)
- return true;
- // Check if nothing interesting happened.
- if (!PerformParallelTest && !Scop.isOptimized() &&
- Scop.getAliasGroups().empty())
- return false;
- // The default assumption is that Polly improves the code.
- return true;
- }
- /// Collect statistics for the syntax tree rooted at @p Ast.
- static void walkAstForStatistics(const isl::ast_node &Ast) {
- assert(!Ast.is_null());
- isl_ast_node_foreach_descendant_top_down(
- Ast.get(),
- [](__isl_keep isl_ast_node *Node, void *User) -> isl_bool {
- switch (isl_ast_node_get_type(Node)) {
- case isl_ast_node_for:
- NumForLoops++;
- if (IslAstInfo::isParallel(isl::manage_copy(Node)))
- NumParallel++;
- if (IslAstInfo::isInnermostParallel(isl::manage_copy(Node)))
- NumInnermostParallel++;
- if (IslAstInfo::isOutermostParallel(isl::manage_copy(Node)))
- NumOutermostParallel++;
- if (IslAstInfo::isReductionParallel(isl::manage_copy(Node)))
- NumReductionParallel++;
- if (IslAstInfo::isExecutedInParallel(isl::manage_copy(Node)))
- NumExecutedInParallel++;
- break;
- case isl_ast_node_if:
- NumIfConditions++;
- break;
- default:
- break;
- }
- // Continue traversing subtrees.
- return isl_bool_true;
- },
- nullptr);
- }
- IslAst::IslAst(Scop &Scop) : S(Scop), Ctx(Scop.getSharedIslCtx()) {}
- IslAst::IslAst(IslAst &&O)
- : S(O.S), Ctx(O.Ctx), RunCondition(std::move(O.RunCondition)),
- Root(std::move(O.Root)) {}
- void IslAst::init(const Dependences &D) {
- bool PerformParallelTest = PollyParallel || DetectParallel ||
- PollyVectorizerChoice != VECTORIZER_NONE;
- auto ScheduleTree = S.getScheduleTree();
- // Skip AST and code generation if there was no benefit achieved.
- if (!benefitsFromPolly(S, PerformParallelTest))
- return;
- auto ScopStats = S.getStatistics();
- ScopsBeneficial++;
- BeneficialAffineLoops += ScopStats.NumAffineLoops;
- BeneficialBoxedLoops += ScopStats.NumBoxedLoops;
- auto Ctx = S.getIslCtx();
- isl_options_set_ast_build_atomic_upper_bound(Ctx.get(), true);
- isl_options_set_ast_build_detect_min_max(Ctx.get(), true);
- isl_ast_build *Build;
- AstBuildUserInfo BuildInfo;
- if (UseContext)
- Build = isl_ast_build_from_context(S.getContext().release());
- else
- Build = isl_ast_build_from_context(
- isl_set_universe(S.getParamSpace().release()));
- Build = isl_ast_build_set_at_each_domain(Build, AtEachDomain, nullptr);
- if (PerformParallelTest) {
- BuildInfo.Deps = &D;
- BuildInfo.InParallelFor = false;
- BuildInfo.InSIMD = false;
- Build = isl_ast_build_set_before_each_for(Build, &astBuildBeforeFor,
- &BuildInfo);
- Build =
- isl_ast_build_set_after_each_for(Build, &astBuildAfterFor, &BuildInfo);
- Build = isl_ast_build_set_before_each_mark(Build, &astBuildBeforeMark,
- &BuildInfo);
- Build = isl_ast_build_set_after_each_mark(Build, &astBuildAfterMark,
- &BuildInfo);
- }
- RunCondition = buildRunCondition(S, isl::manage_copy(Build));
- Root = isl::manage(
- isl_ast_build_node_from_schedule(Build, S.getScheduleTree().release()));
- walkAstForStatistics(Root);
- isl_ast_build_free(Build);
- }
- IslAst IslAst::create(Scop &Scop, const Dependences &D) {
- IslAst Ast{Scop};
- Ast.init(D);
- return Ast;
- }
- isl::ast_node IslAst::getAst() { return Root; }
- isl::ast_expr IslAst::getRunCondition() { return RunCondition; }
- isl::ast_node IslAstInfo::getAst() { return Ast.getAst(); }
- isl::ast_expr IslAstInfo::getRunCondition() { return Ast.getRunCondition(); }
- IslAstUserPayload *IslAstInfo::getNodePayload(const isl::ast_node &Node) {
- isl::id Id = Node.get_annotation();
- if (Id.is_null())
- return nullptr;
- IslAstUserPayload *Payload = (IslAstUserPayload *)Id.get_user();
- return Payload;
- }
- bool IslAstInfo::isInnermost(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload && Payload->IsInnermost;
- }
- bool IslAstInfo::isParallel(const isl::ast_node &Node) {
- return IslAstInfo::isInnermostParallel(Node) ||
- IslAstInfo::isOutermostParallel(Node);
- }
- bool IslAstInfo::isInnermostParallel(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload && Payload->IsInnermostParallel;
- }
- bool IslAstInfo::isOutermostParallel(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload && Payload->IsOutermostParallel;
- }
- bool IslAstInfo::isReductionParallel(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload && Payload->IsReductionParallel;
- }
- bool IslAstInfo::isExecutedInParallel(const isl::ast_node &Node) {
- if (!PollyParallel)
- return false;
- // Do not parallelize innermost loops.
- //
- // Parallelizing innermost loops is often not profitable, especially if
- // they have a low number of iterations.
- //
- // TODO: Decide this based on the number of loop iterations that will be
- // executed. This can possibly require run-time checks, which again
- // raises the question of both run-time check overhead and code size
- // costs.
- if (!PollyParallelForce && isInnermost(Node))
- return false;
- return isOutermostParallel(Node) && !isReductionParallel(Node);
- }
- isl::union_map IslAstInfo::getSchedule(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload ? Payload->Build.get_schedule() : isl::union_map();
- }
- isl::pw_aff
- IslAstInfo::getMinimalDependenceDistance(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload ? Payload->MinimalDependenceDistance : isl::pw_aff();
- }
- IslAstInfo::MemoryAccessSet *
- IslAstInfo::getBrokenReductions(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload ? &Payload->BrokenReductions : nullptr;
- }
- isl::ast_build IslAstInfo::getBuild(const isl::ast_node &Node) {
- IslAstUserPayload *Payload = getNodePayload(Node);
- return Payload ? Payload->Build : isl::ast_build();
- }
- static std::unique_ptr<IslAstInfo> runIslAst(
- Scop &Scop,
- function_ref<const Dependences &(Dependences::AnalysisLevel)> GetDeps) {
- // Skip SCoPs in case they're already handled by PPCGCodeGeneration.
- if (Scop.isToBeSkipped())
- return {};
- ScopsProcessed++;
- const Dependences &D = GetDeps(Dependences::AL_Statement);
- if (D.getSharedIslCtx() != Scop.getSharedIslCtx()) {
- LLVM_DEBUG(
- dbgs() << "Got dependence analysis for different SCoP/isl_ctx\n");
- return {};
- }
- std::unique_ptr<IslAstInfo> Ast = std::make_unique<IslAstInfo>(Scop, D);
- LLVM_DEBUG({
- if (Ast)
- Ast->print(dbgs());
- });
- return Ast;
- }
- IslAstInfo IslAstAnalysis::run(Scop &S, ScopAnalysisManager &SAM,
- ScopStandardAnalysisResults &SAR) {
- auto GetDeps = [&](Dependences::AnalysisLevel Lvl) -> const Dependences & {
- return SAM.getResult<DependenceAnalysis>(S, SAR).getDependences(Lvl);
- };
- return std::move(*runIslAst(S, GetDeps));
- }
- 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) {
- isl::ast_node_user AstNode = isl::manage_copy(Node).as<isl::ast_node_user>();
- isl::ast_expr NodeExpr = AstNode.expr();
- isl::ast_expr CallExpr = NodeExpr.get_op_arg(0);
- isl::id CallExprId = CallExpr.get_id();
- ScopStmt *AccessStmt = (ScopStmt *)CallExprId.get_user();
- P = isl_printer_start_line(P);
- P = isl_printer_print_str(P, AccessStmt->getBaseName());
- P = isl_printer_print_str(P, "(");
- P = isl_printer_end_line(P);
- P = isl_printer_indent(P, 2);
- for (MemoryAccess *MemAcc : *AccessStmt) {
- P = isl_printer_start_line(P);
- if (MemAcc->isRead())
- P = isl_printer_print_str(P, "/* read */ &");
- else
- P = isl_printer_print_str(P, "/* write */ ");
- isl::ast_build Build = IslAstInfo::getBuild(isl::manage_copy(Node));
- if (MemAcc->isAffine()) {
- isl_pw_multi_aff *PwmaPtr =
- MemAcc->applyScheduleToAccessRelation(Build.get_schedule()).release();
- isl::pw_multi_aff Pwma = isl::manage(PwmaPtr);
- isl::ast_expr AccessExpr = Build.access_from(Pwma);
- P = isl_printer_print_ast_expr(P, AccessExpr.get());
- } else {
- P = isl_printer_print_str(
- P, MemAcc->getLatestScopArrayInfo()->getName().c_str());
- P = isl_printer_print_str(P, "[*]");
- }
- P = isl_printer_end_line(P);
- }
- P = isl_printer_indent(P, -2);
- P = isl_printer_start_line(P);
- P = isl_printer_print_str(P, ");");
- P = isl_printer_end_line(P);
- isl_ast_print_options_free(O);
- return P;
- }
- void IslAstInfo::print(raw_ostream &OS) {
- isl_ast_print_options *Options;
- isl::ast_node RootNode = Ast.getAst();
- Function &F = S.getFunction();
- OS << ":: isl ast :: " << F.getName() << " :: " << S.getNameStr() << "\n";
- if (RootNode.is_null()) {
- OS << ":: isl ast generation and code generation was skipped!\n\n";
- OS << ":: This is either because no useful optimizations could be applied "
- "(use -polly-process-unprofitable to enforce code generation) or "
- "because earlier passes such as dependence analysis timed out (use "
- "-polly-dependences-computeout=0 to set dependence analysis timeout "
- "to infinity)\n\n";
- return;
- }
- isl::ast_expr RunCondition = Ast.getRunCondition();
- char *RtCStr, *AstStr;
- Options = isl_ast_print_options_alloc(S.getIslCtx().get());
- if (PrintAccesses)
- Options =
- isl_ast_print_options_set_print_user(Options, cbPrintUser, nullptr);
- Options = isl_ast_print_options_set_print_for(Options, cbPrintFor, nullptr);
- isl_printer *P = isl_printer_to_str(S.getIslCtx().get());
- P = isl_printer_set_output_format(P, ISL_FORMAT_C);
- P = isl_printer_print_ast_expr(P, RunCondition.get());
- RtCStr = isl_printer_get_str(P);
- P = isl_printer_flush(P);
- P = isl_printer_indent(P, 4);
- P = isl_ast_node_print(RootNode.get(), P, Options);
- AstStr = isl_printer_get_str(P);
- LLVM_DEBUG({
- dbgs() << S.getContextStr() << "\n";
- dbgs() << stringFromIslObj(S.getScheduleTree(), "null");
- });
- OS << "\nif (" << RtCStr << ")\n\n";
- OS << AstStr << "\n";
- OS << "else\n";
- OS << " { /* original code */ }\n\n";
- free(RtCStr);
- free(AstStr);
- isl_printer_free(P);
- }
- AnalysisKey IslAstAnalysis::Key;
- PreservedAnalyses IslAstPrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
- ScopStandardAnalysisResults &SAR,
- SPMUpdater &U) {
- auto &Ast = SAM.getResult<IslAstAnalysis>(S, SAR);
- Ast.print(OS);
- return PreservedAnalyses::all();
- }
- void IslAstInfoWrapperPass::releaseMemory() { Ast.reset(); }
- bool IslAstInfoWrapperPass::runOnScop(Scop &Scop) {
- auto GetDeps = [this](Dependences::AnalysisLevel Lvl) -> const Dependences & {
- return getAnalysis<DependenceInfo>().getDependences(Lvl);
- };
- Ast = runIslAst(Scop, GetDeps);
- return false;
- }
- void IslAstInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
- // Get the Common analysis usage of ScopPasses.
- ScopPass::getAnalysisUsage(AU);
- AU.addRequiredTransitive<ScopInfoRegionPass>();
- AU.addRequired<DependenceInfo>();
- AU.addPreserved<DependenceInfo>();
- }
- void IslAstInfoWrapperPass::printScop(raw_ostream &OS, Scop &S) const {
- OS << "Printing analysis 'Polly - Generate an AST of the SCoP (isl)'"
- << S.getName() << "' in function '" << S.getFunction().getName() << "':\n";
- if (Ast)
- Ast->print(OS);
- }
- char IslAstInfoWrapperPass::ID = 0;
- Pass *polly::createIslAstInfoWrapperPassPass() {
- return new IslAstInfoWrapperPass();
- }
- INITIALIZE_PASS_BEGIN(IslAstInfoWrapperPass, "polly-ast",
- "Polly - Generate an AST of the SCoP (isl)", false,
- false);
- INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass);
- INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
- INITIALIZE_PASS_END(IslAstInfoWrapperPass, "polly-ast",
- "Polly - Generate an AST from the SCoP (isl)", false, false)
|