MLIR  17.0.0git
LoopFusionUtils.h
Go to the documentation of this file.
1 //===- LoopFusionUtils.h - Loop fusion 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 fusion 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_LOOPFUSIONUTILS_H
16 #define MLIR_DIALECT_AFFINE_LOOPFUSIONUTILS_H
17 
18 #include "mlir/IR/Value.h"
19 #include "mlir/Support/LLVM.h"
20 #include "llvm/ADT/DenseMap.h"
21 #include "llvm/ADT/SmallVector.h"
22 
23 namespace mlir {
24 class Operation;
25 
26 namespace affine {
27 class AffineForOp;
28 struct ComputationSliceState;
29 
30 // TODO: Extend this module to include utility functions for querying fusion
31 // cost/storage reduction, and for performing the loop fusion transformation.
32 
33 struct FusionResult {
34  enum ResultEnum {
36  FailPrecondition, // Failed precondition for fusion. (e.g. same block).
37  FailBlockDependence, // Fusion would violate another dependence in block.
38  FailFusionDependence, // Fusion would reverse dependences between loops.
39  FailComputationSlice, // Unable to compute src loop computation slice.
40  FailIncorrectSlice, // Slice is computed, but it is incorrect.
41  } value;
43 };
44 
45 /// Describes the fusion strategy to be used in the Affine loop fusion
46 /// utilities. Currently, it is used to specialized the loop fusion utilities
47 /// with the assumptions made in the AffineLoopFusion pass for producer-consumer
48 /// and sibling fusion, while sharing a single implementation. The latter
49 /// strategies are also limited to scenarios where a single memref is involved
50 /// in the producer-consume or sibling relationship between the candidate
51 /// loops. We use 'memref' to keep track of such a memref.
52 // TODO: Remove 'memref' when we support more generic scenarios.
53 // TODO: Generalize utilities so that producer-consumer and sibling fusion
54 // strategies can be used without the assumptions made in the AffineLoopFusion
55 // pass.
57 public:
58  enum StrategyEnum {
59  // Generic loop fusion: Arbitrary loops are considered for fusion. No
60  // assumptions about a specific fusion strategy from AffineLoopFusion pass
61  // are made.
62  // TODO: Generic fusion is not fully implemented by fusion utilities yet.
63  // It should only be used for testing.
65  // Producer-consumer fusion: Only loops with a producer-consumer
66  // memref dependence are considered for fusion. Currently, assumptions from
67  // the producer-consumer fusion implementation in AffineLoopFusion pass are
68  // made. See pass for specific details.
70  // Sibling fusion: Only sibling loops with no producer-consumer memref
71  // dependences are considered for fusion. Memref reuse is taken into account
72  // for profitability. Currently, assumptions from the sibling fusion
73  // implementation in AffineLoopFusion pass are made. See pass for specific
74  // details.
75  Sibling
76  };
77 
78  /// Construct a generic or producer-consumer fusion strategy.
79  FusionStrategy(StrategyEnum strategy) : strategy(strategy) {
80  assert(strategy != Sibling &&
81  "Sibling fusion strategy requires a specific memref");
82  }
83 
84  /// Construct a sibling fusion strategy targeting 'memref'. This construct
85  /// should only be used for sibling fusion.
86  FusionStrategy(Value memref) : strategy(Sibling), memref(memref) {}
87 
88  /// Returns the fusion strategy.
89  StrategyEnum getStrategy() const { return strategy; };
90 
91  /// Returns the memref attached to this sibling fusion strategy.
93  assert(strategy == Sibling && "Memref is only valid for sibling fusion");
94  return memref;
95  }
96 
97 private:
98  /// Fusion strategy.
99  StrategyEnum strategy;
100 
101  /// Target memref for this fusion transformation. Only used for sibling
102  /// fusion.
103  Value memref;
104 };
105 
106 /// Checks the feasibility of fusing the loop nest rooted at 'srcForOp' into the
107 /// loop nest rooted at 'dstForOp' at 'dstLoopDepth'. Returns FusionResult
108 /// 'Success' if fusion of the src/dst loop nests is feasible (i.e. they are
109 /// in the same block and dependences would not be violated). Otherwise
110 /// returns a FusionResult explaining why fusion is not feasible.
111 /// NOTE: This function is not feature complete and should only be used in
112 /// testing.
113 /// TODO: Update comments when this function is fully implemented.
114 FusionResult
115 canFuseLoops(AffineForOp srcForOp, AffineForOp dstForOp, unsigned dstLoopDepth,
116  ComputationSliceState *srcSlice,
117  FusionStrategy fusionStrategy = FusionStrategy::Generic);
118 
119 /// Fuses 'srcForOp' into 'dstForOp' with destination loop block insertion
120 /// point and source slice loop bounds specified in 'srcSlice'.
121 /// `isInnermostSiblingInsertionFusion` enables cleanup of `srcForOp that is a
122 /// single-iteration reduction loop being sibling-fused into a 'dstForOp'.
123 void fuseLoops(AffineForOp srcForOp, AffineForOp dstForOp,
124  const ComputationSliceState &srcSlice,
125  bool isInnermostSiblingInsertionFusion = false);
126 
127 /// LoopNestStats aggregates various per-loop statistics (eg. loop trip count
128 /// and operation count) for a loop nest up until (and including) the innermost
129 /// loop body.
131  /// Map from AffineForOp to immediate child AffineForOps in its loop body.
133  /// Map from AffineForOp to count of operations in its loop body.
135  /// Map from AffineForOp to its constant trip count.
137 };
138 
139 /// Collect loop nest statistics (eg. loop trip count and operation count)
140 /// in 'stats' for loop nest rooted at 'forOp'. Returns true on success,
141 /// returns false otherwise.
142 // TODO: Consider moving this to LoopUtils.
143 bool getLoopNestStats(AffineForOp forOp, LoopNestStats *stats);
144 
145 /// Computes the total cost of the loop nest rooted at 'forOp' using 'stats'.
146 /// Currently, the total cost is computed by counting the total operation
147 /// instance count (i.e. total number of operations in the loop body * loop
148 /// trip count) for the entire loop nest.
149 // TODO: Improve this cost model.
150 int64_t getComputeCost(AffineForOp forOp, LoopNestStats &stats);
151 
152 /// Computes and returns in 'computeCost', the total compute cost of fusing the
153 /// 'slice' of the loop nest rooted at 'srcForOp' into 'dstForOp'. Currently,
154 /// the total cost is computed by counting the total operation instance count
155 /// (i.e. total number of operations in the loop body * loop trip count) for
156 /// the entire loop nest.
157 /// Returns true on success, failure otherwise (e.g. non-constant trip counts).
158 // TODO: Improve this cost model.
159 bool getFusionComputeCost(AffineForOp srcForOp, LoopNestStats &srcStats,
160  AffineForOp dstForOp, LoopNestStats &dstStats,
161  const ComputationSliceState &slice,
162  int64_t *computeCost);
163 
164 /// Returns in 'producerConsumerMemrefs' the memrefs involved in a
165 /// producer-consumer dependence between write ops in 'srcOps' and read ops in
166 /// 'dstOps'.
168  ArrayRef<Operation *> dstOps,
169  DenseSet<Value> &producerConsumerMemrefs);
170 
171 } // namespace affine
172 } // namespace mlir
173 
174 #endif // MLIR_DIALECT_AFFINE_LOOPFUSIONUTILS_H
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:93
Describes the fusion strategy to be used in the Affine loop fusion utilities.
StrategyEnum getStrategy() const
Returns the fusion strategy.
FusionStrategy(StrategyEnum strategy)
Construct a generic or producer-consumer fusion strategy.
FusionStrategy(Value memref)
Construct a sibling fusion strategy targeting 'memref'.
Value getSiblingFusionMemRef() const
Returns the memref attached to this sibling fusion strategy.
bool getFusionComputeCost(AffineForOp srcForOp, LoopNestStats &srcStats, AffineForOp dstForOp, LoopNestStats &dstStats, const ComputationSliceState &slice, int64_t *computeCost)
Computes and returns in 'computeCost', the total compute cost of fusing the 'slice' of the loop nest ...
void gatherProducerConsumerMemrefs(ArrayRef< Operation * > srcOps, ArrayRef< Operation * > dstOps, DenseSet< Value > &producerConsumerMemrefs)
Returns in 'producerConsumerMemrefs' the memrefs involved in a producer-consumer dependence between w...
int64_t getComputeCost(AffineForOp forOp, LoopNestStats &stats)
Computes the total cost of the loop nest rooted at 'forOp' using 'stats'.
void fuseLoops(AffineForOp srcForOp, AffineForOp dstForOp, const ComputationSliceState &srcSlice, bool isInnermostSiblingInsertionFusion=false)
Fuses 'srcForOp' into 'dstForOp' with destination loop block insertion point and source slice loop bo...
bool getLoopNestStats(AffineForOp forOp, LoopNestStats *stats)
Collect loop nest statistics (eg.
FusionResult canFuseLoops(AffineForOp srcForOp, AffineForOp dstForOp, unsigned dstLoopDepth, ComputationSliceState *srcSlice, FusionStrategy fusionStrategy=FusionStrategy::Generic)
Checks the feasibility of fusing the loop nest rooted at 'srcForOp' into the loop nest rooted at 'dst...
This header declares functions that assit transformations in the MemRef dialect.
ComputationSliceState aggregates loop IVs, loop bound AffineMaps and their associated operands for a ...
Definition: Utils.h:260
enum mlir::affine::FusionResult::ResultEnum value
LoopNestStats aggregates various per-loop statistics (eg.
DenseMap< Operation *, uint64_t > opCountMap
Map from AffineForOp to count of operations in its loop body.
DenseMap< Operation *, SmallVector< AffineForOp, 2 > > loopMap
Map from AffineForOp to immediate child AffineForOps in its loop body.
DenseMap< Operation *, uint64_t > tripCountMap
Map from AffineForOp to its constant trip count.