1 | //===- AffineLoopInvariantCodeMotion.cpp - Code to perform loop fusion-----===// |
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 file implements loop invariant code motion. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "mlir/Dialect/Affine/Passes.h" |
14 | |
15 | #include "mlir/Analysis/SliceAnalysis.h" |
16 | #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h" |
17 | #include "mlir/Dialect/Affine/Analysis/AffineStructures.h" |
18 | #include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h" |
19 | #include "mlir/Dialect/Affine/Analysis/Utils.h" |
20 | #include "mlir/Dialect/Affine/IR/AffineOps.h" |
21 | #include "mlir/Dialect/Affine/LoopUtils.h" |
22 | #include "mlir/Dialect/Affine/Utils.h" |
23 | #include "mlir/Dialect/Arith/IR/Arith.h" |
24 | #include "mlir/Dialect/Func/IR/FuncOps.h" |
25 | #include "mlir/IR/AffineExpr.h" |
26 | #include "mlir/IR/AffineMap.h" |
27 | #include "mlir/IR/Builders.h" |
28 | #include "mlir/IR/Matchers.h" |
29 | #include "mlir/Interfaces/SideEffectInterfaces.h" |
30 | #include "llvm/ADT/DenseMap.h" |
31 | #include "llvm/ADT/DenseSet.h" |
32 | #include "llvm/ADT/SmallPtrSet.h" |
33 | #include "llvm/Support/CommandLine.h" |
34 | #include "llvm/Support/Debug.h" |
35 | #include "llvm/Support/raw_ostream.h" |
36 | |
37 | namespace mlir { |
38 | namespace affine { |
39 | #define GEN_PASS_DEF_AFFINELOOPINVARIANTCODEMOTION |
40 | #include "mlir/Dialect/Affine/Passes.h.inc" |
41 | } // namespace affine |
42 | } // namespace mlir |
43 | |
44 | #define DEBUG_TYPE "licm" |
45 | |
46 | using namespace mlir; |
47 | using namespace mlir::affine; |
48 | |
49 | namespace { |
50 | |
51 | /// Affine loop invariant code motion (LICM) pass. |
52 | /// TODO: The pass is missing zero-trip tests. |
53 | /// TODO: This code should be removed once the new LICM pass can handle its |
54 | /// uses. |
55 | struct LoopInvariantCodeMotion |
56 | : public affine::impl::AffineLoopInvariantCodeMotionBase< |
57 | LoopInvariantCodeMotion> { |
58 | void runOnOperation() override; |
59 | void runOnAffineForOp(AffineForOp forOp); |
60 | }; |
61 | } // namespace |
62 | |
63 | static bool |
64 | checkInvarianceOfNestedIfOps(AffineIfOp ifOp, Value indVar, ValueRange iterArgs, |
65 | SmallPtrSetImpl<Operation *> &opsWithUsers, |
66 | SmallPtrSetImpl<Operation *> &opsToHoist); |
67 | static bool isOpLoopInvariant(Operation &op, Value indVar, ValueRange iterArgs, |
68 | SmallPtrSetImpl<Operation *> &opsWithUsers, |
69 | SmallPtrSetImpl<Operation *> &opsToHoist); |
70 | |
71 | static bool |
72 | areAllOpsInTheBlockListInvariant(Region &blockList, Value indVar, |
73 | ValueRange iterArgs, |
74 | SmallPtrSetImpl<Operation *> &opsWithUsers, |
75 | SmallPtrSetImpl<Operation *> &opsToHoist); |
76 | |
77 | // Returns true if the individual op is loop invariant. |
78 | static bool isOpLoopInvariant(Operation &op, Value indVar, ValueRange iterArgs, |
79 | SmallPtrSetImpl<Operation *> &opsWithUsers, |
80 | SmallPtrSetImpl<Operation *> &opsToHoist) { |
81 | LLVM_DEBUG(llvm::dbgs() << "iterating on op: " << op;); |
82 | |
83 | if (auto ifOp = dyn_cast<AffineIfOp>(op)) { |
84 | if (!checkInvarianceOfNestedIfOps(ifOp, indVar, iterArgs, opsWithUsers, |
85 | opsToHoist)) |
86 | return false; |
87 | } else if (auto forOp = dyn_cast<AffineForOp>(op)) { |
88 | if (!areAllOpsInTheBlockListInvariant(forOp.getRegion(), indVar, iterArgs, |
89 | opsWithUsers, opsToHoist)) |
90 | return false; |
91 | } else if (auto parOp = dyn_cast<AffineParallelOp>(op)) { |
92 | if (!areAllOpsInTheBlockListInvariant(parOp.getRegion(), indVar, iterArgs, |
93 | opsWithUsers, opsToHoist)) |
94 | return false; |
95 | } else if (!isMemoryEffectFree(&op) && |
96 | !isa<AffineReadOpInterface, AffineWriteOpInterface, |
97 | AffinePrefetchOp>(&op)) { |
98 | // Check for side-effecting ops. Affine read/write ops are handled |
99 | // separately below. |
100 | return false; |
101 | } else if (!matchPattern(op: &op, pattern: m_Constant())) { |
102 | // Register op in the set of ops that have users. |
103 | opsWithUsers.insert(Ptr: &op); |
104 | if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) { |
105 | auto read = dyn_cast<AffineReadOpInterface>(op); |
106 | Value memref = read ? read.getMemRef() |
107 | : cast<AffineWriteOpInterface>(op).getMemRef(); |
108 | for (auto *user : memref.getUsers()) { |
109 | // If this memref has a user that is a DMA, give up because these |
110 | // operations write to this memref. |
111 | if (isa<AffineDmaStartOp, AffineDmaWaitOp>(Val: user)) |
112 | return false; |
113 | // If the memref used by the load/store is used in a store elsewhere in |
114 | // the loop nest, we do not hoist. Similarly, if the memref used in a |
115 | // load is also being stored too, we do not hoist the load. |
116 | if (isa<AffineWriteOpInterface>(user) || |
117 | (isa<AffineReadOpInterface>(user) && |
118 | isa<AffineWriteOpInterface>(op))) { |
119 | if (&op != user) { |
120 | SmallVector<AffineForOp, 8> userIVs; |
121 | getAffineForIVs(*user, &userIVs); |
122 | // Check that userIVs don't contain the for loop around the op. |
123 | if (llvm::is_contained(userIVs, getForInductionVarOwner(indVar))) |
124 | return false; |
125 | } |
126 | } |
127 | } |
128 | } |
129 | |
130 | if (op.getNumOperands() == 0 && !isa<AffineYieldOp>(op)) { |
131 | LLVM_DEBUG(llvm::dbgs() << "Non-constant op with 0 operands\n" ); |
132 | return false; |
133 | } |
134 | } |
135 | |
136 | // Check operands. |
137 | for (unsigned int i = 0; i < op.getNumOperands(); ++i) { |
138 | auto *operandSrc = op.getOperand(idx: i).getDefiningOp(); |
139 | |
140 | LLVM_DEBUG( |
141 | op.getOperand(i).print(llvm::dbgs() << "Iterating on operand\n" )); |
142 | |
143 | // If the loop IV is the operand, this op isn't loop invariant. |
144 | if (indVar == op.getOperand(idx: i)) { |
145 | LLVM_DEBUG(llvm::dbgs() << "Loop IV is the operand\n" ); |
146 | return false; |
147 | } |
148 | |
149 | // If the one of the iter_args is the operand, this op isn't loop invariant. |
150 | if (llvm::is_contained(Range&: iterArgs, Element: op.getOperand(idx: i))) { |
151 | LLVM_DEBUG(llvm::dbgs() << "One of the iter_args is the operand\n" ); |
152 | return false; |
153 | } |
154 | |
155 | if (operandSrc) { |
156 | LLVM_DEBUG(llvm::dbgs() << *operandSrc << "Iterating on operand src\n" ); |
157 | |
158 | // If the value was defined in the loop (outside of the if/else region), |
159 | // and that operation itself wasn't meant to be hoisted, then mark this |
160 | // operation loop dependent. |
161 | if (opsWithUsers.count(Ptr: operandSrc) && opsToHoist.count(Ptr: operandSrc) == 0) |
162 | return false; |
163 | } |
164 | } |
165 | |
166 | // If no operand was loop variant, mark this op for motion. |
167 | opsToHoist.insert(Ptr: &op); |
168 | return true; |
169 | } |
170 | |
171 | // Checks if all ops in a region (i.e. list of blocks) are loop invariant. |
172 | static bool |
173 | areAllOpsInTheBlockListInvariant(Region &blockList, Value indVar, |
174 | ValueRange iterArgs, |
175 | SmallPtrSetImpl<Operation *> &opsWithUsers, |
176 | SmallPtrSetImpl<Operation *> &opsToHoist) { |
177 | |
178 | for (auto &b : blockList) { |
179 | for (auto &op : b) { |
180 | if (!isOpLoopInvariant(op, indVar, iterArgs, opsWithUsers, opsToHoist)) |
181 | return false; |
182 | } |
183 | } |
184 | |
185 | return true; |
186 | } |
187 | |
188 | // Returns true if the affine.if op can be hoisted. |
189 | static bool |
190 | checkInvarianceOfNestedIfOps(AffineIfOp ifOp, Value indVar, ValueRange iterArgs, |
191 | SmallPtrSetImpl<Operation *> &opsWithUsers, |
192 | SmallPtrSetImpl<Operation *> &opsToHoist) { |
193 | if (!areAllOpsInTheBlockListInvariant(ifOp.getThenRegion(), indVar, iterArgs, |
194 | opsWithUsers, opsToHoist)) |
195 | return false; |
196 | |
197 | if (!areAllOpsInTheBlockListInvariant(ifOp.getElseRegion(), indVar, iterArgs, |
198 | opsWithUsers, opsToHoist)) |
199 | return false; |
200 | |
201 | return true; |
202 | } |
203 | |
204 | void LoopInvariantCodeMotion::runOnAffineForOp(AffineForOp forOp) { |
205 | auto *loopBody = forOp.getBody(); |
206 | auto indVar = forOp.getInductionVar(); |
207 | ValueRange iterArgs = forOp.getRegionIterArgs(); |
208 | |
209 | // This is the place where hoisted instructions would reside. |
210 | OpBuilder b(forOp.getOperation()); |
211 | |
212 | SmallPtrSet<Operation *, 8> opsToHoist; |
213 | SmallVector<Operation *, 8> opsToMove; |
214 | SmallPtrSet<Operation *, 8> opsWithUsers; |
215 | |
216 | for (auto &op : *loopBody) { |
217 | // Register op in the set of ops that have users. This set is used |
218 | // to prevent hoisting ops that depend on these ops that are |
219 | // not being hoisted. |
220 | if (!op.use_empty()) |
221 | opsWithUsers.insert(&op); |
222 | if (!isa<AffineYieldOp>(op)) { |
223 | if (isOpLoopInvariant(op, indVar, iterArgs, opsWithUsers, opsToHoist)) { |
224 | opsToMove.push_back(&op); |
225 | } |
226 | } |
227 | } |
228 | |
229 | // For all instructions that we found to be invariant, place sequentially |
230 | // right before the for loop. |
231 | for (auto *op : opsToMove) { |
232 | op->moveBefore(forOp); |
233 | } |
234 | |
235 | LLVM_DEBUG(forOp->print(llvm::dbgs() << "Modified loop\n" )); |
236 | } |
237 | |
238 | void LoopInvariantCodeMotion::runOnOperation() { |
239 | // Walk through all loops in a function in innermost-loop-first order. This |
240 | // way, we first LICM from the inner loop, and place the ops in |
241 | // the outer loop, which in turn can be further LICM'ed. |
242 | getOperation().walk([&](AffineForOp op) { |
243 | LLVM_DEBUG(op->print(llvm::dbgs() << "\nOriginal loop\n" )); |
244 | runOnAffineForOp(forOp: op); |
245 | }); |
246 | } |
247 | |
248 | std::unique_ptr<OperationPass<func::FuncOp>> |
249 | mlir::affine::createAffineLoopInvariantCodeMotionPass() { |
250 | return std::make_unique<LoopInvariantCodeMotion>(); |
251 | } |
252 | |