1 /* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "mlir-hlo/Analysis/userange_analysis.h"
17
18 #include <algorithm>
19 #include <utility>
20
21 #include "llvm/ADT/SetOperations.h"
22 #include "mlir/IR/Block.h"
23 #include "mlir/IR/Region.h"
24 #include "mlir/Interfaces/LoopLikeInterface.h"
25
26 using namespace mlir;
27
28 namespace {
29 /// Builds a userange information from the given value and its liveness. The
30 /// information includes all operations that are within the userange.
31 struct UserangeInfoBuilder {
32 using OperationListT = Liveness::OperationListT;
33 using ValueSetT = BufferViewFlowAnalysis::ValueSetT;
34
35 public:
36 /// Constructs an Userange builder.
UserangeInfoBuilder__anon99a3d9670111::UserangeInfoBuilder37 UserangeInfoBuilder(Liveness liveness, ValueSetT values,
38 OperationListT opList)
39 : values(std::move(values)),
40 opList(std::move(opList)),
41 liveness(std::move(liveness)) {}
42
43 /// Computes the userange of the current value by iterating over all of its
44 /// uses.
computeUserange__anon99a3d9670111::UserangeInfoBuilder45 Liveness::OperationListT computeUserange() {
46 Region *topRegion = findTopRegion();
47 // Iterate over all associated uses.
48 for (Operation *use : opList) {
49 // If one of the parents implements a LoopLikeOpInterface we need to add
50 // all operations inside of its regions to the userange.
51 Operation *loopParent = use->getParentOfType<LoopLikeOpInterface>();
52 if (loopParent && topRegion->isProperAncestor(use->getParentRegion()))
53 addAllOperationsInRegion(loopParent);
54
55 // Check if the parent block has already been processed.
56 Block *useBlock = findTopLiveBlock(use);
57 if (!startBlocks.insert(useBlock).second || visited.contains(useBlock))
58 continue;
59
60 // Add all operations inside the block that are within the userange.
61 findOperationsInUse(useBlock);
62 }
63 return currentUserange;
64 }
65
66 private:
67 /// Find the top most Region of all values stored in the values set.
findTopRegion__anon99a3d9670111::UserangeInfoBuilder68 Region *findTopRegion() const {
69 Region *topRegion = nullptr;
70 llvm::for_each(values, [&](Value v) {
71 Region *other = v.getParentRegion();
72 if (!topRegion || topRegion->isAncestor(other)) topRegion = other;
73 });
74 return topRegion;
75 }
76
77 /// Finds the highest level block that has the current value in its liveOut
78 /// set.
findTopLiveBlock__anon99a3d9670111::UserangeInfoBuilder79 Block *findTopLiveBlock(Operation *op) const {
80 Operation *topOp = op;
81 while (const LivenessBlockInfo *blockInfo =
82 liveness.getLiveness(op->getBlock())) {
83 if (llvm::any_of(values,
84 [&](Value v) { return blockInfo->isLiveOut(v); }))
85 topOp = op;
86 op = op->getParentOp();
87 }
88 return topOp->getBlock();
89 }
90
91 /// Adds all operations from start to end to the userange of the current
92 /// value. If an operation implements a nested region all operations inside of
93 /// it are included as well. If includeEnd is false the end operation is not
94 /// added.
addAllOperationsBetween__anon99a3d9670111::UserangeInfoBuilder95 void addAllOperationsBetween(Operation *start, Operation *end) {
96 currentUserange.push_back(start);
97 addAllOperationsInRegion(start);
98
99 while (start != end) {
100 start = start->getNextNode();
101 addAllOperationsInRegion(start);
102 currentUserange.push_back(start);
103 }
104 }
105
106 /// Adds all operations that are uses of the value in the given block to the
107 /// userange of the current value. Additionally iterate over all successors
108 /// where the value is live.
findOperationsInUse__anon99a3d9670111::UserangeInfoBuilder109 void findOperationsInUse(Block *block) {
110 SmallVector<Block *, 8> blocksToProcess;
111 addOperationsInBlockAndFindSuccessors(
112 block, block, getStartOperation(block), blocksToProcess);
113 while (!blocksToProcess.empty()) {
114 Block *toProcess = blocksToProcess.pop_back_val();
115 addOperationsInBlockAndFindSuccessors(
116 block, toProcess, &toProcess->front(), blocksToProcess);
117 }
118 }
119
120 /// Adds the operations between the given start operation and the computed end
121 /// operation to the userange. If the current value is live out, add all
122 /// successor blocks that have the value live in to the process queue. If we
123 /// find a loop, add the operations before the first use in block to the
124 /// userange (if any). The startBlock is the block where the iteration over
125 /// all successors started and is propagated further to find potential loops.
addOperationsInBlockAndFindSuccessors__anon99a3d9670111::UserangeInfoBuilder126 void addOperationsInBlockAndFindSuccessors(
127 const Block *startBlock, Block *toProcess, Operation *start,
128 SmallVector<Block *, 8> &blocksToProcess) {
129 const LivenessBlockInfo *blockInfo = liveness.getLiveness(toProcess);
130 Operation *end = getEndOperation(toProcess);
131
132 addAllOperationsBetween(start, end);
133
134 // If the value is live out we need to process all successors at which the
135 // value is live in.
136 if (!llvm::any_of(values, [&](Value v) { return blockInfo->isLiveOut(v); }))
137 return;
138 for (Block *successor : toProcess->getSuccessors()) {
139 // If the successor is the startBlock, we found a loop and only have to
140 // add the operations from the block front to the first use of the
141 // value.
142 if (!llvm::any_of(values, [&](Value v) {
143 return liveness.getLiveness(successor)->isLiveIn(v);
144 }))
145 continue;
146 if (successor == startBlock) {
147 start = &successor->front();
148 end = getStartOperation(successor);
149 if (start != end) addAllOperationsBetween(start, end->getPrevNode());
150 // Else we need to check if the value is live in and the successor
151 // has not been visited before. If so we also need to process it.
152 } else if (visited.insert(successor).second) {
153 blocksToProcess.push_back(successor);
154 }
155 }
156 }
157
158 /// Iterates over all regions of a given operation and adds all operations
159 /// inside those regions to the userange of the current value.
addAllOperationsInRegion__anon99a3d9670111::UserangeInfoBuilder160 void addAllOperationsInRegion(Operation *parentOp) {
161 // Iterate over all regions of the parentOp.
162 for (Region ®ion : parentOp->getRegions()) {
163 // Iterate over blocks inside the region.
164 for (Block &block : region) {
165 // If the blocks have been used as a startBlock before, we need to add
166 // all operations between the block front and the startOp of the value.
167 if (startBlocks.contains(&block)) {
168 Operation *start = &block.front();
169 Operation *end = getStartOperation(&block);
170 if (start != end) addAllOperationsBetween(start, end->getPrevNode());
171
172 // If the block has never been seen before, we need to add all
173 // operations inside.
174 } else if (visited.insert(&block).second) {
175 for (Operation &op : block) {
176 addAllOperationsInRegion(&op);
177 currentUserange.push_back(&op);
178 }
179 continue;
180 }
181 // If the block has either been visited before or was used as a
182 // startBlock, we need to add all operations between the endOp of the
183 // value and the end of the block.
184 Operation *end = getEndOperation(&block);
185 if (end == &block.back()) continue;
186 addAllOperationsBetween(end->getNextNode(), &block.back());
187 }
188 }
189 }
190
191 /// Find the start operation of the current value inside the given block.
getStartOperation__anon99a3d9670111::UserangeInfoBuilder192 Operation *getStartOperation(Block *block) {
193 Operation *startOperation = &block->back();
194 for (Operation *useOp : opList) {
195 // Find the associated operation in the current block (if any).
196 useOp = block->findAncestorOpInBlock(*useOp);
197 // Check whether the use is in our block and after the current end
198 // operation.
199 if (useOp && useOp->isBeforeInBlock(startOperation))
200 startOperation = useOp;
201 }
202 return startOperation;
203 }
204
205 /// Find the end operation of the current value inside the given block.
getEndOperation__anon99a3d9670111::UserangeInfoBuilder206 Operation *getEndOperation(Block *block) {
207 const LivenessBlockInfo *blockInfo = liveness.getLiveness(block);
208 if (llvm::any_of(values, [&](Value v) { return blockInfo->isLiveOut(v); }))
209 return &block->back();
210
211 Operation *endOperation = &block->front();
212 for (Operation *useOp : opList) {
213 // Find the associated operation in the current block (if any).
214 useOp = block->findAncestorOpInBlock(*useOp);
215 // Check whether the use is in our block and after the current end
216 // operation.
217 if (useOp && endOperation->isBeforeInBlock(useOp)) endOperation = useOp;
218 }
219 return endOperation;
220 }
221
222 /// The current Value.
223 ValueSetT values;
224
225 /// The list of all operations used by the values.
226 OperationListT opList;
227
228 /// The result list of the userange computation.
229 OperationListT currentUserange;
230
231 /// The set of visited blocks during the userange computation.
232 SmallPtrSet<Block *, 32> visited;
233
234 /// The set of blocks that the userange computation started from.
235 SmallPtrSet<Block *, 8> startBlocks;
236
237 /// The current liveness info.
238 Liveness liveness;
239 };
240 } // namespace
241
242 /// Empty UseInterval Constructor.
UseInterval()243 UseInterval::UseInterval()
244 : start(std::numeric_limits<size_t>::max()),
245 end(std::numeric_limits<size_t>::min()) {}
246
247 /// Performs an interval subtraction => A = A - B.
intervalSubtract(UseInterval::Vector & a,const UseInterval::Vector & b)248 void UseInterval::intervalSubtract(UseInterval::Vector &a,
249 const UseInterval::Vector &b) {
250 const auto *iterB = b.begin();
251 const auto *endB = b.end();
252 for (auto *iterA = a.begin(); iterA != a.end() && iterB != endB;) {
253 // iterA is strictly before iterB => increment iterA.
254 if (*iterA < *iterB) {
255 ++iterA;
256 // iterB is strictly before iterA => increment iterB.
257 } else if (*iterA > *iterB) {
258 ++iterB;
259 // iterB overlaps with the start of iterA, but iterA has some values that
260 // go beyond those of iterB. We have to set the start of iterA to the end
261 // of iterB + 1 and increment iterB. A(3, 100) - B(3, 5) => A(6,100)
262 } else if (iterA->start >= iterB->start && iterA->end > iterB->end) {
263 iterA->start = iterB->end + 1;
264 ++iterB;
265 // iterB overlaps with the end of iterA, but iterA has some values that
266 // come before iterB. We have to set the end of iterA to the start of
267 // iterB - 1 and increment iterA. A(4, 50) - B(40, 50) => A(4, 39)
268 } else if (iterA->end <= iterB->end && iterA->start < iterB->start) {
269 iterA->end = iterB->start - 1;
270 ++iterA;
271 // iterB is in the middle of iterA. We have to split iterA and increment
272 // iterB.
273 // A(2, 10) - B(5, 7) => (2, 4), (8, 10)
274 } else if (iterA->start < iterB->start && iterA->end > iterB->end) {
275 size_t endA = iterA->end;
276 iterA->end = iterB->start - 1;
277 iterA = a.insert(iterA, UseInterval(iterB->end + 1, endA));
278 ++iterB;
279 // Both intervals are equal. We have to erase the whole interval.
280 // A(5, 5) - B(5, 5) => {}
281 } else {
282 iterA = a.erase(iterA);
283 ++iterB;
284 }
285 }
286 }
287
288 /// Performs an interval intersection => A = A ^ B.
intervalIntersect(UseInterval::Vector & a,const UseInterval::Vector & b)289 void UseInterval::intervalIntersect(UseInterval::Vector &a,
290 const UseInterval::Vector &b) {
291 const auto *iterB = b.begin();
292 const auto *endB = b.end();
293 for (auto *iterA = a.begin(); iterA != a.end();) {
294 // iterB points to the end, therefore the remaining UseIntervals from A must
295 // be erased or iterA is strictly before iterB => erase iterA.
296 if (iterB == endB || *iterA < *iterB) {
297 iterA = a.erase(iterA);
298 // iterB is strictly before iterA => increment iterB.
299 } else if (*iterA > *iterB) {
300 ++iterB;
301 // iterB overlaps with iterA => reduce the interval to the overlap and
302 // insert the ending split-off to vector A again.
303 } else {
304 size_t currentEndA = iterA->end;
305 iterA->start = std::max(iterA->start, iterB->start);
306 iterA->end = std::min(currentEndA, iterB->end);
307 if (currentEndA > iterB->end) {
308 iterA = a.insert(std::next(iterA),
309 UseInterval(iterB->end + 1, currentEndA));
310 ++iterB;
311 } else {
312 ++iterA;
313 }
314 }
315 }
316 }
317
318 /// Performs an interval merge => A = A u B.
319 /// Note: All overlapping and contiguous UseIntervals are merged.
intervalMerge(UseInterval::Vector & a,const UseInterval::Vector & b)320 void UseInterval::intervalMerge(UseInterval::Vector &a,
321 const UseInterval::Vector &b) {
322 const auto *iterB = b.begin();
323 const auto *endB = b.end();
324 // Iterate over UseInterval::Vector a and b.
325 for (auto *iterA = a.begin(); iterA != a.end() && iterB != endB;) {
326 // Let A be the UseInterval of iterA and B the UseInterval of iterB.
327 // Check if A is before B.
328 if (*iterA < *iterB) {
329 // Check if A and B can be merged if they are contiguous. If the merge
330 // result contains the next elements of A, we can erase them.
331 if (iterA->isContiguous(*iterB)) {
332 mergeAndEraseContiguousIntervals(a, iterA, *iterB);
333 ++iterB;
334 }
335 ++iterA;
336 // Check if B is before A.
337 } else if (*iterA > *iterB) {
338 // Check if A and B can be merged if they are contiguous, else add B
339 // to the Vector of A.
340 if (iterB->isContiguous(*iterA))
341 iterA->mergeWith(*iterB);
342 else
343 iterA = a.insert(iterA, *iterB);
344 ++iterB;
345 // The UseIntervals interfere and must be merged.
346 } else {
347 mergeAndEraseContiguousIntervals(a, iterA, *iterB);
348 ++iterB;
349 }
350 }
351 // If there are remaining UseIntervals in b, add them to a.
352 if (iterB != endB) a.insert(a.end(), iterB, endB);
353 }
354
355 /// Merge the UseIntervals and erase overlapping and contiguouse UseIntervals
356 /// of the UseInterval::Vector.
mergeAndEraseContiguousIntervals(UseInterval::Vector & interval,UseInterval * iter,const UseInterval & toMerge)357 void UseInterval::mergeAndEraseContiguousIntervals(
358 UseInterval::Vector &interval, UseInterval *iter,
359 const UseInterval &toMerge) {
360 // Return if the iter points to the end.
361 if (iter == interval.end()) return;
362
363 // Merge the UseIntervals.
364 iter->mergeWith(toMerge);
365
366 // Find the next UseInterval from iter that is not contiguous with the merged
367 // iter.
368 UseInterval *next = std::next(iter);
369 while (next != interval.end() && iter->isContiguous(*next)) {
370 if (iter->end < next->end) iter->end = next->end;
371 ++next;
372 }
373 // Remove contiguous UseIntervals.
374 if (std::next(iter) != next) iter = interval.erase(std::next(iter), next);
375 }
376
UserangeAnalysis(Operation * op,const bufferization::BufferPlacementAllocs & allocs,const BufferViewFlowAnalysis & aliases)377 UserangeAnalysis::UserangeAnalysis(
378 Operation *op, const bufferization::BufferPlacementAllocs &allocs,
379 const BufferViewFlowAnalysis &aliases)
380 : liveness(op) {
381 // Walk over all operations and map them to an ID.
382 op->walk([&](Operation *operation) {
383 gatherMemoryEffects(operation);
384 operationIds.insert({operation, operationIds.size()});
385 operations.push_back(operation);
386 });
387
388 // Compute the use range for every allocValue and its aliases. Merge them
389 // and compute an interval. Add all computed intervals to the useIntervalMap.
390 for (const bufferization::BufferPlacementAllocs::AllocEntry &entry : allocs) {
391 Value allocValue = std::get<0>(entry);
392 const Value::use_range &allocUses = allocValue.getUses();
393 size_t dist = std::distance(allocUses.begin(), allocUses.end());
394 OperationListT useList;
395 useList.reserve(dist);
396 for (auto &use : allocUses) useList.push_back(use.getOwner());
397 computeUsePositions(allocValue);
398
399 UserangeInfoBuilder builder(liveness, {allocValue}, useList);
400 OperationListT liveOperations = builder.computeUserange();
401
402 // Sort the operation list by ids.
403 std::sort(liveOperations.begin(), liveOperations.end(),
404 [&](Operation *left, Operation *right) {
405 return operationIds[left] < operationIds[right];
406 });
407
408 UseInterval::Vector allocInterval =
409 computeInterval(allocValue, liveOperations);
410 // Iterate over all aliases and add their useranges to the userange of the
411 // current value. Also add the useInterval of each alias to the
412 // useIntervalMap.
413 ValueSetT aliasSet = aliases.resolve(allocValue);
414 for (Value alias : aliasSet) {
415 if (alias == allocValue) continue;
416 if (!aliasUseranges.count(alias)) {
417 OperationListT aliasOperations;
418 // If the alias is a BlockArgument then the value is live with the first
419 // operation inside that block. Otherwise the liveness analysis is
420 // sufficient for the use range.
421 if (alias.isa<BlockArgument>()) {
422 aliasOperations.push_back(&alias.getParentBlock()->front());
423 for (auto &use : alias.getUses())
424 aliasOperations.push_back(use.getOwner());
425 // Compute the use range for the alias and sort the operations
426 // afterwards.
427 UserangeInfoBuilder aliasBuilder(liveness, {alias}, aliasOperations);
428 aliasOperations = aliasBuilder.computeUserange();
429 std::sort(aliasOperations.begin(), aliasOperations.end(),
430 [&](Operation *left, Operation *right) {
431 return operationIds[left] < operationIds[right];
432 });
433 } else {
434 aliasOperations = liveness.resolveLiveness(alias);
435 }
436
437 aliasUseranges.insert({alias, aliasOperations});
438 useIntervalMap.insert(
439 {alias, computeInterval(alias, aliasUseranges[alias])});
440 computeUsePositions(alias);
441 }
442 UseInterval::intervalMerge(allocInterval, useIntervalMap[alias]);
443 mergeUsePositions(usePositionMap[allocValue], usePositionMap[alias]);
444 }
445 aliasCache.insert(std::make_pair(allocValue, aliasSet));
446
447 // Map the current allocValue to the computed useInterval.
448 useIntervalMap.insert(std::make_pair(allocValue, allocInterval));
449 }
450 }
451
452 /// Computes the doubled Id for the given value inside the operation based on
453 /// the program sequence. If the value has only read effects, the returning ID
454 /// will be even, otherwise odd.
computeId(Value v,Operation * op) const455 size_t UserangeAnalysis::computeId(Value v, Operation *op) const {
456 size_t doubledID = (operationIds.find(op)->second + 1) * 2 - 1;
457 auto mapIter = opReadWriteMap.find(op);
458 if (mapIter == opReadWriteMap.end()) return doubledID;
459 auto reads = mapIter->second.first;
460 auto writes = mapIter->second.second;
461 if (reads.contains(v) && !writes.contains(v)) return doubledID - 1;
462 return doubledID;
463 }
464
465 /// Computes the UsePositions of the given Value, sorts and inserts them into
466 /// the usePositionMap.
computeUsePositions(Value v)467 void UserangeAnalysis::computeUsePositions(Value v) {
468 // Get the uses of v.
469 const Value::use_range &uses = v.getUses();
470
471 // Create a UsePositionList.
472 UsePositionList usePosList;
473 size_t dist = std::distance(uses.begin(), uses.end());
474 usePosList.reserve(dist);
475
476 // Add all ids and Operations to the UsePositionList.
477 for (auto &use : uses) {
478 Operation *useOwner = use.getOwner();
479 usePosList.emplace_back(computeId(v, useOwner), useOwner);
480 }
481
482 // Sort the UsePositions by ascending Ids.
483 std::sort(usePosList.begin(), usePosList.end(),
484 [](const UsePosition &a, const UsePosition &b) {
485 return a.first < b.first;
486 });
487
488 // Insert the UsePositionList into the usePositionMap.
489 usePositionMap.insert(std::make_pair(v, usePosList));
490 }
491
492 /// Merges listB into listA, sorts the result and removes all duplicates.
mergeUsePositions(UsePositionList & listA,const UsePositionList & listB)493 void UserangeAnalysis::mergeUsePositions(UsePositionList &listA,
494 const UsePositionList &listB) {
495 // Insert listB into listA.
496 listA.insert(listA.end(), listB.begin(), listB.end());
497
498 // Sort the resulting listA.
499 std::sort(listA.begin(), listA.end(),
500 [](const UsePosition &a, const UsePosition &b) {
501 return a.first < b.first;
502 });
503
504 // Remove duplicates.
505 listA.erase(std::unique(listA.begin(), listA.end()), listA.end());
506 }
507
508 /// Checks if the use intervals of the given values interfere.
rangesInterfere(Value itemA,Value itemB) const509 bool UserangeAnalysis::rangesInterfere(Value itemA, Value itemB) const {
510 ValueSetT intersect = aliasCache.find(itemA)->second;
511 llvm::set_intersect(intersect, aliasCache.find(itemB)->second);
512 UseInterval::Vector tmpIntervalA = useIntervalMap.find(itemA)->second;
513 const UseInterval::Vector &intervalsB = useIntervalMap.find(itemB)->second;
514
515 // If the two values share a common alias, then the alias does not count as an
516 // interference and should be removed.
517 if (!intersect.empty()) {
518 for (Value alias : intersect) {
519 const UseInterval::Vector &aliasInterval =
520 useIntervalMap.find(alias)->second;
521 UseInterval::intervalSubtract(tmpIntervalA, aliasInterval);
522 }
523 }
524
525 // Iterate over both UseInterval::Vector and check if they interfere.
526 const auto *iterB = intervalsB.begin();
527 const auto *endB = intervalsB.end();
528 for (auto iterA = tmpIntervalA.begin(), endA = tmpIntervalA.end();
529 iterA != endA && iterB != endB;) {
530 if (*iterA < *iterB)
531 ++iterA;
532 else if (*iterA > *iterB)
533 ++iterB;
534 else
535 return true;
536 }
537 return false;
538 }
539
540 /// Merges the userange of itemB into the userange of itemA.
unionRanges(Value itemA,Value itemB)541 void UserangeAnalysis::unionRanges(Value itemA, Value itemB) {
542 UseInterval::intervalMerge(useIntervalMap[itemA], useIntervalMap[itemB]);
543 }
544
545 /// Builds an UseInterval::Vector corresponding to the given OperationList.
computeInterval(Value value,const Liveness::OperationListT & operationList)546 UseInterval::Vector UserangeAnalysis::computeInterval(
547 Value value, const Liveness::OperationListT &operationList) {
548 assert(!operationList.empty() && "Operation list must not be empty");
549 size_t start = computeId(value, *operationList.begin());
550 size_t last = start;
551 UseInterval::Vector intervals;
552 // Iterate over all operations in the operationList. If the gap between the
553 // respective operationIds is greater 1 create a new interval.
554 for (auto opIter = ++operationList.begin(), e = operationList.end();
555 opIter != e; ++opIter) {
556 size_t current = computeId(value, *opIter);
557 if (current - last > 2) {
558 intervals.emplace_back(start, last);
559 start = current;
560 }
561 last = current;
562 }
563 intervals.emplace_back(start, last);
564 return intervals;
565 }
566
567 /// Checks each operand within the operation for its memory effects and
568 /// separates them into read and write.
gatherMemoryEffects(Operation * op)569 void UserangeAnalysis::gatherMemoryEffects(Operation *op) {
570 if (OpTrait::hasElementwiseMappableTraits(op)) {
571 if (auto effectInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
572 SmallPtrSet<Value, 2> readEffectSet;
573 SmallPtrSet<Value, 2> writeEffectSet;
574 SmallVector<MemoryEffects::EffectInstance> effects;
575 for (auto operand : op->getOperands()) {
576 effects.clear();
577 effectInterface.getEffectsOnValue(operand, effects);
578 for (auto effect : effects) {
579 if (isa<MemoryEffects::Write>(effect.getEffect()))
580 writeEffectSet.insert(operand);
581 else if (isa<MemoryEffects::Read>(effect.getEffect()))
582 readEffectSet.insert(operand);
583 }
584 }
585 opReadWriteMap.insert(
586 {op, std::make_pair(readEffectSet, writeEffectSet)});
587 }
588 }
589 }
590
591 /// Computes the doubled Id back to the OperationId.
unwrapId(size_t id) const592 size_t UserangeAnalysis::unwrapId(size_t id) const { return id / 2; }
593
dump(raw_ostream & os)594 void UserangeAnalysis::dump(raw_ostream &os) {
595 os << "// ---- UserangeAnalysis -----\n";
596 llvm::SmallVector<Value> values;
597 values.reserve(useIntervalMap.size());
598 for (auto const &item : useIntervalMap) {
599 values.push_back(item.first);
600 }
601 std::sort(values.begin(), values.end(), [&](Value left, Value right) {
602 if (left.getDefiningOp()) {
603 if (right.getDefiningOp())
604 return operationIds[left.getDefiningOp()] <
605 operationIds[right.getDefiningOp()];
606 return true;
607 }
608 if (right.getDefiningOp()) return false;
609 return operationIds[&left.getParentBlock()->front()] <
610 operationIds[&right.getParentBlock()->front()];
611 });
612 for (auto value : values) {
613 os << "Value: " << value << (value.getDefiningOp() ? "\n" : "");
614 auto *rangeIt = useIntervalMap[value].begin();
615 os << "Userange: {(" << rangeIt->start << ", " << rangeIt->end << ")";
616 rangeIt++;
617 for (auto *e = useIntervalMap[value].end(); rangeIt != e; ++rangeIt) {
618 os << ", (" << rangeIt->start << ", " << rangeIt->end << ")";
619 }
620 os << "}\n";
621 }
622 os << "// ---------------------------\n";
623 }
624