blob: c5a8da63295681841febd0469df6f61b57d96b2d [file] [log] [blame]
Ehsan Toosi0f03b2b2020-06-25 15:02:111//===- CopyRemoval.cpp - Removing the redundant copies --------------------===//
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#include "mlir/Interfaces/CopyOpInterface.h"
10#include "mlir/Interfaces/SideEffectInterfaces.h"
11#include "mlir/Pass/Pass.h"
12#include "mlir/Transforms/Passes.h"
13
14using namespace mlir;
15using namespace MemoryEffects;
16
17namespace {
18
19//===----------------------------------------------------------------------===//
20// CopyRemovalPass
21//===----------------------------------------------------------------------===//
Ehsan Toosi486d2752020-07-24 13:43:5922
Ehsan Toosi0f03b2b2020-06-25 15:02:1123/// This pass removes the redundant Copy operations. Additionally, it
24/// removes the leftover definition and deallocation operations by erasing the
25/// copy operation.
26class CopyRemovalPass : public PassWrapper<CopyRemovalPass, OperationPass<>> {
Ehsan Toosi486d2752020-07-24 13:43:5927public:
28 void runOnOperation() override {
29 getOperation()->walk([&](CopyOpInterface copyOp) {
30 reuseCopySourceAsTarget(copyOp);
31 reuseCopyTargetAsSource(copyOp);
32 });
Ehsan Toosi4e9f4d02020-08-24 11:19:5033 for (std::pair<Value, Value> &pair : replaceList)
34 pair.first.replaceAllUsesWith(pair.second);
Ehsan Toosi486d2752020-07-24 13:43:5935 for (Operation *op : eraseList)
36 op->erase();
37 }
38
Ehsan Toosi0f03b2b2020-06-25 15:02:1139private:
40 /// List of operations that need to be removed.
Ehsan Toosi4e9f4d02020-08-24 11:19:5041 llvm::SmallPtrSet<Operation *, 4> eraseList;
42
43 /// List of values that need to be replaced with their counterparts.
44 llvm::SmallDenseSet<std::pair<Value, Value>, 4> replaceList;
45
46 /// Returns the allocation operation for `value` in `block` if it exists.
47 /// nullptr otherwise.
48 Operation *getAllocationOpInBlock(Value value, Block *block) {
49 assert(block && "Block cannot be null");
50 Operation *op = value.getDefiningOp();
51 if (op && op->getBlock() == block) {
52 auto effects = dyn_cast<MemoryEffectOpInterface>(op);
53 if (effects && effects.hasEffect<Allocate>())
54 return op;
55 }
56 return nullptr;
57 }
Ehsan Toosi0f03b2b2020-06-25 15:02:1158
59 /// Returns the deallocation operation for `value` in `block` if it exists.
Ehsan Toosi4e9f4d02020-08-24 11:19:5060 /// nullptr otherwise.
61 Operation *getDeallocationOpInBlock(Value value, Block *block) {
Ehsan Toosi486d2752020-07-24 13:43:5962 assert(block && "Block cannot be null");
Ehsan Toosi0f03b2b2020-06-25 15:02:1163 auto valueUsers = value.getUsers();
64 auto it = llvm::find_if(valueUsers, [&](Operation *op) {
65 auto effects = dyn_cast<MemoryEffectOpInterface>(op);
66 return effects && op->getBlock() == block && effects.hasEffect<Free>();
67 });
68 return (it == valueUsers.end() ? nullptr : *it);
69 }
70
71 /// Returns true if an operation between start and end operations has memory
72 /// effect.
73 bool hasMemoryEffectOpBetween(Operation *start, Operation *end) {
Ehsan Toosi486d2752020-07-24 13:43:5974 assert((start || end) && "Start and end operations cannot be null");
Ehsan Toosi0f03b2b2020-06-25 15:02:1175 assert(start->getBlock() == end->getBlock() &&
76 "Start and end operations should be in the same block.");
77 Operation *op = start->getNextNode();
78 while (op->isBeforeInBlock(end)) {
Ehsan Toosi486d2752020-07-24 13:43:5979 if (isa<MemoryEffectOpInterface>(op))
Ehsan Toosi0f03b2b2020-06-25 15:02:1180 return true;
81 op = op->getNextNode();
82 }
83 return false;
84 };
85
86 /// Returns true if `val` value has at least a user between `start` and
87 /// `end` operations.
88 bool hasUsersBetween(Value val, Operation *start, Operation *end) {
Ehsan Toosi486d2752020-07-24 13:43:5989 assert((start || end) && "Start and end operations cannot be null");
Ehsan Toosi0f03b2b2020-06-25 15:02:1190 Block *block = start->getBlock();
91 assert(block == end->getBlock() &&
92 "Start and end operations should be in the same block.");
93 return llvm::any_of(val.getUsers(), [&](Operation *op) {
94 return op->getBlock() == block && start->isBeforeInBlock(op) &&
95 op->isBeforeInBlock(end);
96 });
97 };
98
99 bool areOpsInTheSameBlock(ArrayRef<Operation *> operations) {
Ehsan Toosi486d2752020-07-24 13:43:59100 assert(!operations.empty() &&
101 "The operations list should contain at least a single operation");
102 Block *block = operations.front()->getBlock();
103 return llvm::none_of(
104 operations, [&](Operation *op) { return block != op->getBlock(); });
Ehsan Toosi0f03b2b2020-06-25 15:02:11105 }
106
107 /// Input:
108 /// func(){
109 /// %from = alloc()
110 /// write_to(%from)
111 /// %to = alloc()
112 /// copy(%from,%to)
113 /// dealloc(%from)
114 /// return %to
115 /// }
116 ///
117 /// Output:
118 /// func(){
119 /// %from = alloc()
120 /// write_to(%from)
121 /// return %from
122 /// }
123 /// Constraints:
124 /// 1) %to, copy and dealloc must all be defined and lie in the same block.
125 /// 2) This transformation cannot be applied if there is a single user/alias
126 /// of `to` value between the defining operation of `to` and the copy
127 /// operation.
128 /// 3) This transformation cannot be applied if there is a single user/alias
129 /// of `from` value between the copy operation and the deallocation of `from`.
130 /// TODO: Alias analysis is not available at the moment. Currently, we check
131 /// if there are any operations with memory effects between copy and
132 /// deallocation operations.
Ehsan Toosi486d2752020-07-24 13:43:59133 void reuseCopySourceAsTarget(CopyOpInterface copyOp) {
Ehsan Toosi0f03b2b2020-06-25 15:02:11134 if (eraseList.count(copyOp))
135 return;
136
137 Value from = copyOp.getSource();
138 Value to = copyOp.getTarget();
139
140 Operation *copy = copyOp.getOperation();
Ehsan Toosi4e9f4d02020-08-24 11:19:50141 Block *copyBlock = copy->getBlock();
Ehsan Toosi0f03b2b2020-06-25 15:02:11142 Operation *fromDefiningOp = from.getDefiningOp();
Ehsan Toosi4e9f4d02020-08-24 11:19:50143 Operation *fromFreeingOp = getDeallocationOpInBlock(from, copyBlock);
144 Operation *toDefiningOp = getAllocationOpInBlock(to, copyBlock);
Ehsan Toosi0f03b2b2020-06-25 15:02:11145 if (!fromDefiningOp || !fromFreeingOp || !toDefiningOp ||
146 !areOpsInTheSameBlock({fromFreeingOp, toDefiningOp, copy}) ||
147 hasUsersBetween(to, toDefiningOp, copy) ||
148 hasUsersBetween(from, copy, fromFreeingOp) ||
149 hasMemoryEffectOpBetween(copy, fromFreeingOp))
150 return;
151
Ehsan Toosi4e9f4d02020-08-24 11:19:50152 replaceList.insert({to, from});
Ehsan Toosi0f03b2b2020-06-25 15:02:11153 eraseList.insert(copy);
154 eraseList.insert(toDefiningOp);
155 eraseList.insert(fromFreeingOp);
156 }
157
158 /// Input:
159 /// func(){
160 /// %to = alloc()
161 /// %from = alloc()
162 /// write_to(%from)
163 /// copy(%from,%to)
164 /// dealloc(%from)
165 /// return %to
166 /// }
167 ///
168 /// Output:
169 /// func(){
170 /// %to = alloc()
171 /// write_to(%to)
172 /// return %to
173 /// }
174 /// Constraints:
175 /// 1) %from, copy and dealloc must all be defined and lie in the same block.
176 /// 2) This transformation cannot be applied if there is a single user/alias
177 /// of `to` value between the defining operation of `from` and the copy
178 /// operation.
179 /// 3) This transformation cannot be applied if there is a single user/alias
180 /// of `from` value between the copy operation and the deallocation of `from`.
181 /// TODO: Alias analysis is not available at the moment. Currently, we check
182 /// if there are any operations with memory effects between copy and
183 /// deallocation operations.
Ehsan Toosi486d2752020-07-24 13:43:59184 void reuseCopyTargetAsSource(CopyOpInterface copyOp) {
Ehsan Toosi0f03b2b2020-06-25 15:02:11185 if (eraseList.count(copyOp))
186 return;
187
188 Value from = copyOp.getSource();
189 Value to = copyOp.getTarget();
190
191 Operation *copy = copyOp.getOperation();
Ehsan Toosi4e9f4d02020-08-24 11:19:50192 Block *copyBlock = copy->getBlock();
193 Operation *fromDefiningOp = getAllocationOpInBlock(from, copyBlock);
194 Operation *fromFreeingOp = getDeallocationOpInBlock(from, copyBlock);
Ehsan Toosi0f03b2b2020-06-25 15:02:11195 if (!fromDefiningOp || !fromFreeingOp ||
196 !areOpsInTheSameBlock({fromFreeingOp, fromDefiningOp, copy}) ||
197 hasUsersBetween(to, fromDefiningOp, copy) ||
198 hasUsersBetween(from, copy, fromFreeingOp) ||
199 hasMemoryEffectOpBetween(copy, fromFreeingOp))
200 return;
201
Ehsan Toosi4e9f4d02020-08-24 11:19:50202 replaceList.insert({from, to});
Ehsan Toosi0f03b2b2020-06-25 15:02:11203 eraseList.insert(copy);
204 eraseList.insert(fromDefiningOp);
205 eraseList.insert(fromFreeingOp);
206 }
Ehsan Toosi0f03b2b2020-06-25 15:02:11207};
208
209} // end anonymous namespace
210
211//===----------------------------------------------------------------------===//
212// CopyRemovalPass construction
213//===----------------------------------------------------------------------===//
Ehsan Toosi486d2752020-07-24 13:43:59214
Ehsan Toosi0f03b2b2020-06-25 15:02:11215std::unique_ptr<Pass> mlir::createCopyRemovalPass() {
216 return std::make_unique<CopyRemovalPass>();
217}