| //===- polly/ScheduleTreeTransform.h ----------------------------*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Make changes to isl's schedule tree data structure. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef POLLY_SCHEDULETREETRANSFORM_H |
| #define POLLY_SCHEDULETREETRANSFORM_H |
| |
| #include "polly/Support/ISLTools.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "isl/isl-noexceptions.h" |
| #include <cassert> |
| |
| namespace polly { |
| struct BandAttr; |
| |
| /// This class defines a simple visitor class that may be used for |
| /// various schedule tree analysis purposes. |
| template <typename Derived, typename RetTy = void, typename... Args> |
| struct ScheduleTreeVisitor { |
| Derived &getDerived() { return *static_cast<Derived *>(this); } |
| const Derived &getDerived() const { |
| return *static_cast<const Derived *>(this); |
| } |
| |
| RetTy visit(isl::schedule_node Node, Args... args) { |
| assert(!Node.is_null()); |
| switch (isl_schedule_node_get_type(Node.get())) { |
| case isl_schedule_node_domain: |
| assert(isl_schedule_node_n_children(Node.get()) == 1); |
| return getDerived().visitDomain(Node.as<isl::schedule_node_domain>(), |
| std::forward<Args>(args)...); |
| case isl_schedule_node_band: |
| assert(isl_schedule_node_n_children(Node.get()) == 1); |
| return getDerived().visitBand(Node.as<isl::schedule_node_band>(), |
| std::forward<Args>(args)...); |
| case isl_schedule_node_sequence: |
| assert(isl_schedule_node_n_children(Node.get()) >= 2); |
| return getDerived().visitSequence(Node.as<isl::schedule_node_sequence>(), |
| std::forward<Args>(args)...); |
| case isl_schedule_node_set: |
| return getDerived().visitSet(Node.as<isl::schedule_node_set>(), |
| std::forward<Args>(args)...); |
| assert(isl_schedule_node_n_children(Node.get()) >= 2); |
| case isl_schedule_node_leaf: |
| assert(isl_schedule_node_n_children(Node.get()) == 0); |
| return getDerived().visitLeaf(Node.as<isl::schedule_node_leaf>(), |
| std::forward<Args>(args)...); |
| case isl_schedule_node_mark: |
| assert(isl_schedule_node_n_children(Node.get()) == 1); |
| return getDerived().visitMark(Node.as<isl::schedule_node_mark>(), |
| std::forward<Args>(args)...); |
| case isl_schedule_node_extension: |
| assert(isl_schedule_node_n_children(Node.get()) == 1); |
| return getDerived().visitExtension( |
| Node.as<isl::schedule_node_extension>(), std::forward<Args>(args)...); |
| case isl_schedule_node_filter: |
| assert(isl_schedule_node_n_children(Node.get()) == 1); |
| return getDerived().visitFilter(Node.as<isl::schedule_node_filter>(), |
| std::forward<Args>(args)...); |
| default: |
| llvm_unreachable("unimplemented schedule node type"); |
| } |
| } |
| |
| RetTy visitDomain(isl::schedule_node_domain Domain, Args... args) { |
| return getDerived().visitSingleChild(std::move(Domain), |
| std::forward<Args>(args)...); |
| } |
| |
| RetTy visitBand(isl::schedule_node_band Band, Args... args) { |
| return getDerived().visitSingleChild(std::move(Band), |
| std::forward<Args>(args)...); |
| } |
| |
| RetTy visitSequence(isl::schedule_node_sequence Sequence, Args... args) { |
| return getDerived().visitMultiChild(std::move(Sequence), |
| std::forward<Args>(args)...); |
| } |
| |
| RetTy visitSet(isl::schedule_node_set Set, Args... args) { |
| return getDerived().visitMultiChild(std::move(Set), |
| std::forward<Args>(args)...); |
| } |
| |
| RetTy visitLeaf(isl::schedule_node_leaf Leaf, Args... args) { |
| return getDerived().visitNode(std::move(Leaf), std::forward<Args>(args)...); |
| } |
| |
| RetTy visitMark(isl::schedule_node_mark Mark, Args... args) { |
| return getDerived().visitSingleChild(std::move(Mark), |
| std::forward<Args>(args)...); |
| } |
| |
| RetTy visitExtension(isl::schedule_node_extension Extension, Args... args) { |
| return getDerived().visitSingleChild(std::move(Extension), |
| std::forward<Args>(args)...); |
| } |
| |
| RetTy visitFilter(isl::schedule_node_filter Filter, Args... args) { |
| return getDerived().visitSingleChild(std::move(Filter), |
| std::forward<Args>(args)...); |
| } |
| |
| RetTy visitSingleChild(isl::schedule_node Node, Args... args) { |
| return getDerived().visitNode(std::move(Node), std::forward<Args>(args)...); |
| } |
| |
| RetTy visitMultiChild(isl::schedule_node Node, Args... args) { |
| return getDerived().visitNode(std::move(Node), std::forward<Args>(args)...); |
| } |
| |
| RetTy visitNode(isl::schedule_node Node, Args... args) { |
| llvm_unreachable("Unimplemented other"); |
| } |
| }; |
| |
| /// Recursively visit all nodes of a schedule tree. |
| template <typename Derived, typename RetTy = void, typename... Args> |
| struct RecursiveScheduleTreeVisitor |
| : public ScheduleTreeVisitor<Derived, RetTy, Args...> { |
| using BaseTy = ScheduleTreeVisitor<Derived, RetTy, Args...>; |
| BaseTy &getBase() { return *this; } |
| const BaseTy &getBase() const { return *this; } |
| Derived &getDerived() { return *static_cast<Derived *>(this); } |
| const Derived &getDerived() const { |
| return *static_cast<const Derived *>(this); |
| } |
| |
| /// When visiting an entire schedule tree, start at its root node. |
| RetTy visit(isl::schedule Schedule, Args... args) { |
| return getDerived().visit(Schedule.get_root(), std::forward<Args>(args)...); |
| } |
| |
| // Necessary to allow overload resolution with the added visit(isl::schedule) |
| // overload. |
| RetTy visit(isl::schedule_node Node, Args... args) { |
| return getBase().visit(Node, std::forward<Args>(args)...); |
| } |
| |
| /// By default, recursively visit the child nodes. |
| RetTy visitNode(isl::schedule_node Node, Args... args) { |
| for (unsigned i : rangeIslSize(0, Node.n_children())) |
| getDerived().visit(Node.child(i), std::forward<Args>(args)...); |
| return RetTy(); |
| } |
| }; |
| |
| /// Is this node the marker for its parent band? |
| bool isBandMark(const isl::schedule_node &Node); |
| |
| /// Extract the BandAttr from a band's wrapping marker. Can also pass the band |
| /// itself and this methods will try to find its wrapping mark. Returns nullptr |
| /// if the band has not BandAttr. |
| BandAttr *getBandAttr(isl::schedule_node MarkOrBand); |
| |
| /// Hoist all domains from extension into the root domain node, such that there |
| /// are no more extension nodes (which isl does not support for some |
| /// operations). This assumes that domains added by to extension nodes do not |
| /// overlap. |
| isl::schedule hoistExtensionNodes(isl::schedule Sched); |
| |
| /// Replace the AST band @p BandToUnroll by a sequence of all its iterations. |
| /// |
| /// The implementation enumerates all points in the partial schedule and creates |
| /// an ISL sequence node for each point. The number of iterations must be a |
| /// constant. |
| isl::schedule applyFullUnroll(isl::schedule_node BandToUnroll); |
| |
| /// Replace the AST band @p BandToUnroll by a partially unrolled equivalent. |
| isl::schedule applyPartialUnroll(isl::schedule_node BandToUnroll, int Factor); |
| |
| /// Loop-distribute the band @p BandToFission as much as possible. |
| isl::schedule applyMaxFission(isl::schedule_node BandToFission); |
| |
| /// Build the desired set of partial tile prefixes. |
| /// |
| /// We build a set of partial tile prefixes, which are prefixes of the vector |
| /// loop that have exactly VectorWidth iterations. |
| /// |
| /// 1. Drop all constraints involving the dimension that represents the |
| /// vector loop. |
| /// 2. Constrain the last dimension to get a set, which has exactly VectorWidth |
| /// iterations. |
| /// 3. Subtract loop domain from it, project out the vector loop dimension and |
| /// get a set that contains prefixes, which do not have exactly VectorWidth |
| /// iterations. |
| /// 4. Project out the vector loop dimension of the set that was build on the |
| /// first step and subtract the set built on the previous step to get the |
| /// desired set of prefixes. |
| /// |
| /// @param ScheduleRange A range of a map, which describes a prefix schedule |
| /// relation. |
| isl::set getPartialTilePrefixes(isl::set ScheduleRange, int VectorWidth); |
| |
| /// Create an isl::union_set, which describes the isolate option based on |
| /// IsolateDomain. |
| /// |
| /// @param IsolateDomain An isl::set whose @p OutDimsNum last dimensions should |
| /// belong to the current band node. |
| /// @param OutDimsNum A number of dimensions that should belong to |
| /// the current band node. |
| isl::union_set getIsolateOptions(isl::set IsolateDomain, unsigned OutDimsNum); |
| |
| /// Create an isl::union_set, which describes the specified option for the |
| /// dimension of the current node. |
| /// |
| /// @param Ctx An isl::ctx, which is used to create the isl::union_set. |
| /// @param Option The name of the option. |
| isl::union_set getDimOptions(isl::ctx Ctx, const char *Option); |
| |
| /// Tile a schedule node. |
| /// |
| /// @param Node The node to tile. |
| /// @param Identifier An name that identifies this kind of tiling and |
| /// that is used to mark the tiled loops in the |
| /// generated AST. |
| /// @param TileSizes A vector of tile sizes that should be used for |
| /// tiling. |
| /// @param DefaultTileSize A default tile size that is used for dimensions |
| /// that are not covered by the TileSizes vector. |
| isl::schedule_node tileNode(isl::schedule_node Node, const char *Identifier, |
| llvm::ArrayRef<int> TileSizes, int DefaultTileSize); |
| |
| /// Tile a schedule node and unroll point loops. |
| /// |
| /// @param Node The node to register tile. |
| /// @param TileSizes A vector of tile sizes that should be used for |
| /// tiling. |
| /// @param DefaultTileSize A default tile size that is used for dimensions |
| isl::schedule_node applyRegisterTiling(isl::schedule_node Node, |
| llvm::ArrayRef<int> TileSizes, |
| int DefaultTileSize); |
| |
| /// Apply greedy fusion. That is, fuse any loop that is possible to be fused |
| /// top-down. |
| /// |
| /// @param Sched Sched tree to fuse all the loops in. |
| /// @param Deps Validity constraints that must be preserved. |
| isl::schedule applyGreedyFusion(isl::schedule Sched, |
| const isl::union_map &Deps); |
| |
| } // namespace polly |
| |
| #endif // POLLY_SCHEDULETREETRANSFORM_H |