MLIR 24.0.0git
LoopUtils.h
Go to the documentation of this file.
1//===- LoopUtils.h - Loop transformation utilities --------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This header file defines prototypes for various loop transformation utility
10// methods: these are not passes by themselves but are used either by passes,
11// optimization sequences, or in turn by other transformation utilities.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef MLIR_DIALECT_AFFINE_LOOPUTILS_H
16#define MLIR_DIALECT_AFFINE_LOOPUTILS_H
17
18#include "mlir/IR/Block.h"
19#include "mlir/Support/LLVM.h"
21#include <optional>
22
23namespace mlir {
24class AffineMap;
25class LoopLikeOpInterface;
26class OpBuilder;
27class Value;
28class ValueRange;
29
30namespace func {
31class FuncOp;
32} // namespace func
33
34namespace scf {
35class ForOp;
36class ParallelOp;
37} // namespace scf
38
39namespace affine {
40class AffineForOp;
41struct MemRefRegion;
42
43/// Unrolls this for operation completely if the trip count is known to be
44/// constant. Returns failure otherwise.
45LogicalResult loopUnrollFull(AffineForOp forOp);
46
47/// Unrolls this for operation by the specified unroll factor. Returns failure
48/// if the loop cannot be unrolled either due to restrictions or due to invalid
49/// unroll factors. Requires positive loop bounds and step. If specified,
50/// annotates the Ops in each unrolled iteration by applying `annotateFn`.
51/// When `cleanUpUnroll` is true, we can ensure the cleanup loop is unrolled
52/// regardless of the unroll factor.
53LogicalResult loopUnrollByFactor(
54 AffineForOp forOp, uint64_t unrollFactor,
55 function_ref<void(unsigned, Operation *, OpBuilder)> annotateFn = nullptr,
56 bool cleanUpUnroll = false);
57
58/// Unrolls this loop by the specified unroll factor or its trip count,
59/// whichever is lower.
60LogicalResult loopUnrollUpToFactor(AffineForOp forOp, uint64_t unrollFactor);
61
62/// Returns true if `loops` is a perfectly nested loop nest, where loops appear
63/// in it from outermost to innermost.
64[[maybe_unused]] bool isPerfectlyNested(ArrayRef<AffineForOp> loops);
65
66/// Get perfectly nested sequence of loops starting at root of loop nest
67/// (the first op being another AffineFor, and the second op - a terminator).
68/// A loop is perfectly nested iff: the first op in the loop's body is another
69/// AffineForOp, and the second op is a terminator).
70void getPerfectlyNestedLoops(SmallVectorImpl<AffineForOp> &nestedLoops,
71 AffineForOp root);
72
73/// Unrolls and jams this loop by the specified factor. `forOp` can be a loop
74/// with iteration arguments performing supported reductions and its inner loops
75/// can have iteration arguments. Returns success if the loop is successfully
76/// unroll-jammed.
77LogicalResult loopUnrollJamByFactor(AffineForOp forOp,
78 uint64_t unrollJamFactor);
79
80/// Unrolls and jams this loop by the specified factor or by the trip count (if
81/// constant), whichever is lower.
82LogicalResult loopUnrollJamUpToFactor(AffineForOp forOp,
83 uint64_t unrollJamFactor);
84
85/// Promotes the loop body of a AffineForOp to its containing block if the loop
86/// was known to have a single iteration.
87LogicalResult promoteIfSingleIteration(AffineForOp forOp);
88
89/// Skew the operations in an affine.for's body with the specified
90/// operation-wise shifts. The shifts are with respect to the original execution
91/// order, and are multiplied by the loop 'step' before being applied. If
92/// `unrollPrologueEpilogue` is set, fully unroll the prologue and epilogue
93/// loops when possible.
94LogicalResult affineForOpBodySkew(AffineForOp forOp, ArrayRef<uint64_t> shifts,
95 bool unrollPrologueEpilogue = false);
96
97/// Tiles the specified band of perfectly nested loops creating tile-space loops
98/// and intra-tile loops. A band is a contiguous set of loops. This utility
99/// doesn't check for the validity of tiling itself, but just performs it.
100LogicalResult
101tilePerfectlyNested(MutableArrayRef<AffineForOp> input,
102 ArrayRef<unsigned> tileSizes,
103 SmallVectorImpl<AffineForOp> *tiledNest = nullptr);
104
105/// Tiles the specified band of perfectly nested loops creating tile-space
106/// loops and intra-tile loops, using SSA values as tiling parameters. A band
107/// is a contiguous set of loops.
109 MutableArrayRef<AffineForOp> input, ArrayRef<Value> tileSizes,
110 SmallVectorImpl<AffineForOp> *tiledNest = nullptr);
111
112/// Performs loop interchange on 'forOpA' and 'forOpB'. Requires that 'forOpA'
113/// and 'forOpB' are part of a perfectly nested sequence of loops.
114void interchangeLoops(AffineForOp forOpA, AffineForOp forOpB);
115
116/// Checks if the loop interchange permutation 'loopPermMap', of the perfectly
117/// nested sequence of loops in 'loops', would violate dependences (loop 'i' in
118/// 'loops' is mapped to location 'j = 'loopPermMap[i]' in the interchange).
119bool isValidLoopInterchangePermutation(ArrayRef<AffineForOp> loops,
120 ArrayRef<unsigned> loopPermMap);
121
122/// Performs a loop permutation on a perfectly nested loop nest `inputNest`
123/// (where the contained loops appear from outer to inner) as specified by the
124/// permutation `permMap`: loop 'i' in `inputNest` is mapped to location
125/// 'loopPermMap[i]', where positions 0, 1, ... are from the outermost position
126/// to inner. Returns the position in `inputNest` of the AffineForOp that
127/// becomes the new outermost loop of this nest. This method always succeeds,
128/// asserts out on invalid input / specifications.
129unsigned permuteLoops(ArrayRef<AffineForOp> inputNest,
130 ArrayRef<unsigned> permMap);
131
132// Sinks all sequential loops to the innermost levels (while preserving
133// relative order among them) and moves all parallel loops to the
134// outermost (while again preserving relative order among them).
135// Returns AffineForOp of the root of the new loop nest after loop interchanges.
136AffineForOp sinkSequentialLoops(AffineForOp forOp);
137
138/// Performs tiling fo imperfectly nested loops (with interchange) by
139/// strip-mining the `forOps` by `sizes` and sinking them, in their order of
140/// occurrence in `forOps`, under each of the `targets`.
141/// Returns the new AffineForOps, one per each of (`forOps`, `targets`) pair,
142/// nested immediately under each of `targets`.
143SmallVector<SmallVector<AffineForOp, 8>, 8> tile(ArrayRef<AffineForOp> forOps,
144 ArrayRef<uint64_t> sizes,
145 ArrayRef<AffineForOp> targets);
146
147/// Performs tiling (with interchange) by strip-mining the `forOps` by `sizes`
148/// and sinking them, in their order of occurrence in `forOps`, under `target`.
149/// Returns the new AffineForOps, one per `forOps`, nested immediately under
150/// `target`.
151SmallVector<AffineForOp, 8> tile(ArrayRef<AffineForOp> forOps,
152 ArrayRef<uint64_t> sizes, AffineForOp target);
153
154/// Explicit copy / DMA generation options for mlir::affineDataCopyGenerate.
156 // True if DMAs should be generated instead of point-wise copies.
158 // The slower memory space from which data is to be moved.
160 // Memory space of the faster one (typically a scratchpad).
162 // Memory space to place tags in: only meaningful for DMAs.
164 // Capacity of the fast memory space in bytes.
166};
167
168/// Performs explicit copying for the contiguous sequence of operations in the
169/// block iterator range [`begin', `end'), where `end' can't be past the
170/// terminator of the block (since additional operations are potentially
171/// inserted right before `end`. `copyOptions` provides various parameters, and
172/// the output argument `copyNests` is the set of all copy nests inserted, each
173/// represented by its root affine.for. Since we generate alloc's and dealloc's
174/// for all fast buffers (before and after the range of operations resp. or at a
175/// hoisted position), all of the fast memory capacity is assumed to be
176/// available for processing this block range. When 'filterMemRef' is specified,
177/// copies are only generated for the provided MemRef. Returns success if the
178/// explicit copying succeeded for all memrefs on which affine load/stores were
179/// encountered. For memrefs for whose element types a size in bytes can't be
180/// computed (`index` type), their capacity is not accounted for and the
181/// `fastMemCapacityBytes` copy option would be non-functional in such cases.
182LogicalResult affineDataCopyGenerate(Block::iterator begin, Block::iterator end,
183 const AffineCopyOptions &copyOptions,
184 std::optional<Value> filterMemRef,
185 DenseSet<Operation *> &copyNests);
186
187/// A convenience version of affineDataCopyGenerate for all ops in the body of
188/// an AffineForOp.
189LogicalResult affineDataCopyGenerate(AffineForOp forOp,
190 const AffineCopyOptions &copyOptions,
191 std::optional<Value> filterMemRef,
192 DenseSet<Operation *> &copyNests);
193
194/// Result for calling generateCopyForMemRegion.
195struct CopyGenerateResult {
196 // Number of bytes used by alloc.
197 uint64_t sizeInBytes;
198
199 // The newly created buffer allocation.
200 Operation *alloc;
201
202 // Generated loop nest for copying data between the allocated buffer and the
203 // original memref.
204 Operation *copyNest;
205};
206
207/// generateCopyForMemRegion is similar to affineDataCopyGenerate, but works
208/// with a single memref region. `memrefRegion` is supposed to contain analysis
209/// information within analyzedOp. The generated prologue and epilogue always
210/// surround `analyzedOp`.
211///
212/// Note that `analyzedOp` is a single op for API convenience, and the
213/// [begin, end) version can be added as needed.
214///
215/// Also note that certain options in `copyOptions` aren't looked at anymore,
216/// like slowMemorySpace.
217LogicalResult generateCopyForMemRegion(const MemRefRegion &memrefRegion,
218 Operation *analyzedOp,
219 const AffineCopyOptions &copyOptions,
220 CopyGenerateResult &result);
221
222/// Replace a perfect nest of "for" loops with a single linearized loop. Assumes
223/// `loops` contains a list of perfectly nested loops outermost to innermost
224/// that are normalized (step one and lower bound of zero) and with bounds and
225/// steps independent of any loop induction variable involved in the nest.
226/// Coalescing affine.for loops is not always possible, i.e., the result may not
227/// be representable using affine.for.
229
230/// Maps `forOp` for execution on a parallel grid of virtual `processorIds` of
231/// size given by `numProcessors`. This is achieved by embedding the SSA values
232/// corresponding to `processorIds` and `numProcessors` into the bounds and step
233/// of the `forOp`. No check is performed on the legality of the rewrite, it is
234/// the caller's responsibility to ensure legality.
235///
236/// Requires that `processorIds` and `numProcessors` have the same size and that
237/// for each idx, `processorIds`[idx] takes, at runtime, all values between 0
238/// and `numProcessors`[idx] - 1. This corresponds to traditional use cases for:
239/// 1. GPU (threadIdx, get_local_id(), ...)
240/// 2. MPI (MPI_Comm_rank)
241/// 3. OpenMP (omp_get_thread_num)
242///
243/// Example:
244/// Assuming a 2-d grid with processorIds = [blockIdx.x, threadIdx.x] and
245/// numProcessors = [gridDim.x, blockDim.x], the loop:
246///
247/// ```
248/// scf.for %i = %lb to %ub step %step {
249/// ...
250/// }
251/// ```
252///
253/// is rewritten into a version resembling the following pseudo-IR:
254///
255/// ```
256/// scf.for %i = %lb + %step * (threadIdx.x + blockIdx.x * blockDim.x)
257/// to %ub step %gridDim.x * blockDim.x * %step {
258/// ...
259/// }
260/// ```
261void mapLoopToProcessorIds(scf::ForOp forOp, ArrayRef<Value> processorId,
262 ArrayRef<Value> numProcessors);
263
264/// Gathers all AffineForOps in 'func.func' grouped by loop depth.
265void gatherLoops(func::FuncOp func,
266 std::vector<SmallVector<AffineForOp, 2>> &depthToLoops);
267
268/// Creates an AffineForOp while ensuring that the lower and upper bounds are
269/// canonicalized, i.e., unused and duplicate operands are removed, any constant
270/// operands propagated/folded in, and duplicate bound maps dropped.
271AffineForOp createCanonicalizedAffineForOp(OpBuilder b, Location loc,
272 ValueRange lbOperands,
273 AffineMap lbMap,
274 ValueRange ubOperands,
275 AffineMap ubMap, int64_t step = 1);
276
277/// Separates full tiles from partial tiles for a perfect nest `nest` by
278/// generating a conditional guard that selects between the full tile version
279/// and the partial tile version using an AffineIfOp. The original loop nest
280/// is replaced by this guarded two version form.
281///
282/// affine.if (cond)
283/// // full_tile
284/// else
285/// // partial tile
286///
287LogicalResult
288separateFullTiles(MutableArrayRef<AffineForOp> nest,
289 SmallVectorImpl<AffineForOp> *fullTileNest = nullptr);
290
291/// Walk an affine.for to find a band to coalesce.
292LogicalResult coalescePerfectlyNestedAffineLoops(AffineForOp op);
293
294/// Count the number of loops surrounding `operand` such that operand could be
295/// hoisted above.
296/// Stop counting at the first loop over which the operand cannot be hoisted.
297/// This counts any LoopLikeOpInterface, not just affine.for.
299} // namespace affine
300} // namespace mlir
301
302#endif // MLIR_DIALECT_AFFINE_LOOPUTILS_H
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
OpListType::iterator iterator
Definition Block.h:164
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class helps build Operations.
Definition Builders.h:210
This class represents an operand of an operation.
Definition Value.h:254
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
LogicalResult loopUnrollFull(AffineForOp forOp)
Unrolls this for operation completely if the trip count is known to be constant.
LogicalResult promoteIfSingleIteration(AffineForOp forOp)
Promotes the loop body of a AffineForOp to its containing block if the loop was known to have a singl...
LogicalResult loopUnrollJamUpToFactor(AffineForOp forOp, uint64_t unrollJamFactor)
Unrolls and jams this loop by the specified factor or by the trip count (if constant),...
LogicalResult loopUnrollByFactor(AffineForOp forOp, uint64_t unrollFactor, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn=nullptr, bool cleanUpUnroll=false)
Unrolls this for operation by the specified unroll factor.
void getPerfectlyNestedLoops(SmallVectorImpl< AffineForOp > &nestedLoops, AffineForOp root)
Get perfectly nested sequence of loops starting at root of loop nest (the first op being another Affi...
LogicalResult affineForOpBodySkew(AffineForOp forOp, ArrayRef< uint64_t > shifts, bool unrollPrologueEpilogue=false)
Skew the operations in an affine.for's body with the specified operation-wise shifts.
bool isValidLoopInterchangePermutation(ArrayRef< AffineForOp > loops, ArrayRef< unsigned > loopPermMap)
Checks if the loop interchange permutation 'loopPermMap', of the perfectly nested sequence of loops i...
LogicalResult loopUnrollUpToFactor(AffineForOp forOp, uint64_t unrollFactor)
Unrolls this loop by the specified unroll factor or its trip count, whichever is lower.
unsigned permuteLoops(ArrayRef< AffineForOp > inputNest, ArrayRef< unsigned > permMap)
Performs a loop permutation on a perfectly nested loop nest inputNest (where the contained loops appe...
LogicalResult loopUnrollJamByFactor(AffineForOp forOp, uint64_t unrollJamFactor)
Unrolls and jams this loop by the specified factor.
LogicalResult tilePerfectlyNestedParametric(MutableArrayRef< AffineForOp > input, ArrayRef< Value > tileSizes, SmallVectorImpl< AffineForOp > *tiledNest=nullptr)
Tiles the specified band of perfectly nested loops creating tile-space loops and intra-tile loops,...
bool isPerfectlyNested(ArrayRef< AffineForOp > loops)
Returns true if loops is a perfectly nested loop nest, where loops appear in it from outermost to inn...
int64_t numEnclosingInvariantLoops(OpOperand &operand)
Performs explicit copying for the contiguous sequence of operations in the block iterator range [‘beg...
SmallVector< SmallVector< AffineForOp, 8 >, 8 > tile(ArrayRef< AffineForOp > forOps, ArrayRef< uint64_t > sizes, ArrayRef< AffineForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
AffineForOp sinkSequentialLoops(AffineForOp forOp)
LogicalResult tilePerfectlyNested(MutableArrayRef< AffineForOp > input, ArrayRef< unsigned > tileSizes, SmallVectorImpl< AffineForOp > *tiledNest=nullptr)
Tiles the specified band of perfectly nested loops creating tile-space loops and intra-tile loops.
void interchangeLoops(AffineForOp forOpA, AffineForOp forOpB)
Performs loop interchange on 'forOpA' and 'forOpB'.
Include the generated interface declarations.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:122
LogicalResult coalesceLoops(MutableArrayRef< scf::ForOp > loops)
Replace a perfect nest of "for" loops with a single linearized loop.
Definition Utils.cpp:1034
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
Explicit copy / DMA generation options for mlir::affineDataCopyGenerate.
Definition LoopUtils.h:155