• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &region : 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