1 //===- AffineAnalysis.h - analyses for affine structures --------*- 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 methods that perform analysis
10 // involving affine structures (AffineExprStorage, AffineMap, IntegerSet, etc.)
11 // and other IR structures that in turn use these.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #ifndef MLIR_DIALECT_AFFINE_ANALYSIS_AFFINEANALYSIS_H
16 #define MLIR_DIALECT_AFFINE_ANALYSIS_AFFINEANALYSIS_H
17
18 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
19 #include "mlir/IR/Value.h"
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/ADT/SmallVector.h"
22
23 namespace mlir {
24
25 class AffineApplyOp;
26 class AffineForOp;
27 class AffineValueMap;
28 class FlatAffineRelation;
29 class FlatAffineValueConstraints;
30 class Operation;
31
32 /// A description of a (parallelizable) reduction in an affine loop.
33 struct LoopReduction {
34 /// Reduction kind.
35 arith::AtomicRMWKind kind;
36
37 /// Position of the iteration argument that acts as accumulator.
38 unsigned iterArgPosition;
39
40 /// The value being reduced.
41 Value value;
42 };
43
44 /// Populate `supportedReductions` with descriptors of the supported reductions.
45 void getSupportedReductions(
46 AffineForOp forOp, SmallVectorImpl<LoopReduction> &supportedReductions);
47
48 /// Returns true if `forOp' is a parallel loop. If `parallelReductions` is
49 /// provided, populates it with descriptors of the parallelizable reductions and
50 /// treats them as not preventing parallelization.
51 bool isLoopParallel(
52 AffineForOp forOp,
53 SmallVectorImpl<LoopReduction> *parallelReductions = nullptr);
54
55 /// Returns true if `forOp' doesn't have memory dependences preventing
56 /// parallelization. Memrefs that are allocated inside `forOp` do not impact its
57 /// dependences and parallelism. This function does not check iter_args (for
58 /// values other than memref types) and should be used only as a building block
59 /// for complete parallelism-checking functions.
60 bool isLoopMemoryParallel(AffineForOp forOp);
61
62 /// Returns in `affineApplyOps`, the sequence of those AffineApplyOp
63 /// Operations that are reachable via a search starting from `operands` and
64 /// ending at those operands that are not the result of an AffineApplyOp.
65 void getReachableAffineApplyOps(ArrayRef<Value> operands,
66 SmallVectorImpl<Operation *> &affineApplyOps);
67
68 /// Builds a system of constraints with dimensional variables corresponding to
69 /// the loop IVs of the forOps and AffineIfOp's operands appearing in
70 /// that order. Bounds of the loop are used to add appropriate inequalities.
71 /// Constraints from the index sets of AffineIfOp are also added. Any symbols
72 /// founds in the bound operands are added as symbols in the system. Returns
73 /// failure for the yet unimplemented cases. `ops` accepts both AffineForOp and
74 /// AffineIfOp.
75 // TODO: handle non-unit strides.
76 LogicalResult getIndexSet(MutableArrayRef<Operation *> ops,
77 FlatAffineValueConstraints *domain);
78
79 /// Encapsulates a memref load or store access information.
80 struct MemRefAccess {
81 Value memref;
82 Operation *opInst;
83 SmallVector<Value, 4> indices;
84
85 /// Constructs a MemRefAccess from a load or store operation.
86 // TODO: add accessors to standard op's load, store, DMA op's to return
87 // MemRefAccess, i.e., loadOp->getAccess(), dmaOp->getRead/WriteAccess.
88 explicit MemRefAccess(Operation *opInst);
89
90 // Returns the rank of the memref associated with this access.
91 unsigned getRank() const;
92 // Returns true if this access is of a store op.
93 bool isStore() const;
94
95 /// Creates an access relation for the access. An access relation maps
96 /// elements of an iteration domain to the element(s) of an array domain
97 /// accessed by that iteration of the associated statement through some array
98 /// reference. For example, given the MLIR code:
99 ///
100 /// affine.for %i0 = 0 to 10 {
101 /// affine.for %i1 = 0 to 10 {
102 /// %a = affine.load %arr[%i0 + %i1, %i0 + 2 * %i1] : memref<100x100xf32>
103 /// }
104 /// }
105 ///
106 /// The access relation, assuming that the memory locations for %arr are
107 /// represented as %m0, %m1 would be:
108 ///
109 /// (%i0, %i1) -> (%m0, %m1)
110 /// %m0 = %i0 + %i1
111 /// %m1 = %i0 + 2 * %i1
112 /// 0 <= %i0 < 10
113 /// 0 <= %i1 < 10
114 ///
115 /// Returns failure for yet unimplemented/unsupported cases (see docs of
116 /// mlir::getIndexSet and mlir::getRelationFromMap for these cases).
117 LogicalResult getAccessRelation(FlatAffineRelation &accessRel) const;
118
119 /// Populates 'accessMap' with composition of AffineApplyOps reachable from
120 /// 'indices'.
121 void getAccessMap(AffineValueMap *accessMap) const;
122
123 /// Equal if both affine accesses can be proved to be equivalent at compile
124 /// time (considering the memrefs, their respective affine access maps and
125 /// operands). The equality of access functions + operands is checked by
126 /// subtracting fully composed value maps, and then simplifying the difference
127 /// using the expression flattener.
128 /// TODO: this does not account for aliasing of memrefs.
129 bool operator==(const MemRefAccess &rhs) const;
130 bool operator!=(const MemRefAccess &rhs) const { return !(*this == rhs); }
131 };
132
133 // DependenceComponent contains state about the direction of a dependence as an
134 // interval [lb, ub] for an AffineForOp.
135 // Distance vectors components are represented by the interval [lb, ub] with
136 // lb == ub.
137 // Direction vectors components are represented by the interval [lb, ub] with
138 // lb < ub. Note that ub/lb == None means unbounded.
139 struct DependenceComponent {
140 // The AffineForOp Operation associated with this dependence component.
141 Operation *op = nullptr;
142 // The lower bound of the dependence distance.
143 Optional<int64_t> lb;
144 // The upper bound of the dependence distance (inclusive).
145 Optional<int64_t> ub;
DependenceComponentDependenceComponent146 DependenceComponent() : lb(llvm::None), ub(llvm::None) {}
147 };
148
149 /// Checks whether two accesses to the same memref access the same element.
150 /// Each access is specified using the MemRefAccess structure, which contains
151 /// the operation, indices and memref associated with the access. Returns
152 /// 'NoDependence' if it can be determined conclusively that the accesses do not
153 /// access the same memref element. If 'allowRAR' is true, will consider
154 /// read-after-read dependences (typically used by applications trying to
155 /// optimize input reuse).
156 // TODO: Wrap 'dependenceConstraints' and 'dependenceComponents' into a single
157 // struct.
158 // TODO: Make 'dependenceConstraints' optional arg.
159 struct DependenceResult {
160 enum ResultEnum {
161 HasDependence, // A dependence exists between 'srcAccess' and 'dstAccess'.
162 NoDependence, // No dependence exists between 'srcAccess' and 'dstAccess'.
163 Failure, // Dependence check failed due to unsupported cases.
164 } value;
DependenceResultDependenceResult165 DependenceResult(ResultEnum v) : value(v) {}
166 };
167
168 DependenceResult checkMemrefAccessDependence(
169 const MemRefAccess &srcAccess, const MemRefAccess &dstAccess,
170 unsigned loopDepth, FlatAffineValueConstraints *dependenceConstraints,
171 SmallVector<DependenceComponent, 2> *dependenceComponents,
172 bool allowRAR = false);
173
174 /// Utility function that returns true if the provided DependenceResult
175 /// corresponds to a dependence result.
hasDependence(DependenceResult result)176 inline bool hasDependence(DependenceResult result) {
177 return result.value == DependenceResult::HasDependence;
178 }
179
180 /// Returns in 'depCompsVec', dependence components for dependences between all
181 /// load and store ops in loop nest rooted at 'forOp', at loop depths in range
182 /// [1, maxLoopDepth].
183 void getDependenceComponents(
184 AffineForOp forOp, unsigned maxLoopDepth,
185 std::vector<SmallVector<DependenceComponent, 2>> *depCompsVec);
186
187 } // namespace mlir
188
189 #endif // MLIR_DIALECT_AFFINE_ANALYSIS_AFFINEANALYSIS_H
190