1 //===- Utils.cpp ---- Misc utilities for analysis -------------------------===//
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 miscellaneous analysis routines for non-loop IR
10 // structures.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "mlir/Dialect/Affine/Analysis/Utils.h"
15 #include "mlir/Analysis/Presburger/PresburgerSet.h"
16 #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h"
17 #include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h"
18 #include "mlir/Dialect/Affine/IR/AffineOps.h"
19 #include "mlir/Dialect/Affine/IR/AffineValueMap.h"
20 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
21 #include "mlir/Dialect/StandardOps/IR/Ops.h"
22 #include "mlir/IR/IntegerSet.h"
23 #include "llvm/ADT/SmallPtrSet.h"
24 #include "llvm/Support/Debug.h"
25 #include "llvm/Support/raw_ostream.h"
26 
27 #define DEBUG_TYPE "analysis-utils"
28 
29 using namespace mlir;
30 using namespace presburger;
31 
32 using llvm::SmallDenseMap;
33 
34 /// Populates 'loops' with IVs of the loops surrounding 'op' ordered from
35 /// the outermost 'affine.for' operation to the innermost one.
36 void mlir::getLoopIVs(Operation &op, SmallVectorImpl<AffineForOp> *loops) {
37   auto *currOp = op.getParentOp();
38   AffineForOp currAffineForOp;
39   // Traverse up the hierarchy collecting all 'affine.for' operation while
40   // skipping over 'affine.if' operations.
41   while (currOp) {
42     if (AffineForOp currAffineForOp = dyn_cast<AffineForOp>(currOp))
43       loops->push_back(currAffineForOp);
44     currOp = currOp->getParentOp();
45   }
46   std::reverse(loops->begin(), loops->end());
47 }
48 
49 /// Populates 'ops' with IVs of the loops surrounding `op`, along with
50 /// `affine.if` operations interleaved between these loops, ordered from the
51 /// outermost `affine.for` operation to the innermost one.
52 void mlir::getEnclosingAffineForAndIfOps(Operation &op,
53                                          SmallVectorImpl<Operation *> *ops) {
54   ops->clear();
55   Operation *currOp = op.getParentOp();
56 
57   // Traverse up the hierarchy collecting all `affine.for` and `affine.if`
58   // operations.
59   while (currOp) {
60     if (isa<AffineIfOp, AffineForOp>(currOp))
61       ops->push_back(currOp);
62     currOp = currOp->getParentOp();
63   }
64   std::reverse(ops->begin(), ops->end());
65 }
66 
67 // Populates 'cst' with FlatAffineValueConstraints which represent original
68 // domain of the loop bounds that define 'ivs'.
69 LogicalResult
70 ComputationSliceState::getSourceAsConstraints(FlatAffineValueConstraints &cst) {
71   assert(!ivs.empty() && "Cannot have a slice without its IVs");
72   cst.reset(/*numDims=*/ivs.size(), /*numSymbols=*/0, /*numLocals=*/0, ivs);
73   for (Value iv : ivs) {
74     AffineForOp loop = getForInductionVarOwner(iv);
75     assert(loop && "Expected affine for");
76     if (failed(cst.addAffineForOpDomain(loop)))
77       return failure();
78   }
79   return success();
80 }
81 
82 // Populates 'cst' with FlatAffineValueConstraints which represent slice bounds.
83 LogicalResult
84 ComputationSliceState::getAsConstraints(FlatAffineValueConstraints *cst) {
85   assert(!lbOperands.empty());
86   // Adds src 'ivs' as dimension identifiers in 'cst'.
87   unsigned numDims = ivs.size();
88   // Adds operands (dst ivs and symbols) as symbols in 'cst'.
89   unsigned numSymbols = lbOperands[0].size();
90 
91   SmallVector<Value, 4> values(ivs);
92   // Append 'ivs' then 'operands' to 'values'.
93   values.append(lbOperands[0].begin(), lbOperands[0].end());
94   cst->reset(numDims, numSymbols, 0, values);
95 
96   // Add loop bound constraints for values which are loop IVs of the destination
97   // of fusion and equality constraints for symbols which are constants.
98   for (unsigned i = numDims, end = values.size(); i < end; ++i) {
99     Value value = values[i];
100     assert(cst->containsId(value) && "value expected to be present");
101     if (isValidSymbol(value)) {
102       // Check if the symbol is a constant.
103       if (auto cOp = value.getDefiningOp<arith::ConstantIndexOp>())
104         cst->addBound(FlatAffineConstraints::EQ, value, cOp.value());
105     } else if (auto loop = getForInductionVarOwner(value)) {
106       if (failed(cst->addAffineForOpDomain(loop)))
107         return failure();
108     }
109   }
110 
111   // Add slices bounds on 'ivs' using maps 'lbs'/'ubs' with 'lbOperands[0]'
112   LogicalResult ret = cst->addSliceBounds(ivs, lbs, ubs, lbOperands[0]);
113   assert(succeeded(ret) &&
114          "should not fail as we never have semi-affine slice maps");
115   (void)ret;
116   return success();
117 }
118 
119 // Clears state bounds and operand state.
120 void ComputationSliceState::clearBounds() {
121   lbs.clear();
122   ubs.clear();
123   lbOperands.clear();
124   ubOperands.clear();
125 }
126 
127 void ComputationSliceState::dump() const {
128   llvm::errs() << "\tIVs:\n";
129   for (Value iv : ivs)
130     llvm::errs() << "\t\t" << iv << "\n";
131 
132   llvm::errs() << "\tLBs:\n";
133   for (auto &en : llvm::enumerate(lbs)) {
134     llvm::errs() << "\t\t" << en.value() << "\n";
135     llvm::errs() << "\t\tOperands:\n";
136     for (Value lbOp : lbOperands[en.index()])
137       llvm::errs() << "\t\t\t" << lbOp << "\n";
138   }
139 
140   llvm::errs() << "\tUBs:\n";
141   for (auto &en : llvm::enumerate(ubs)) {
142     llvm::errs() << "\t\t" << en.value() << "\n";
143     llvm::errs() << "\t\tOperands:\n";
144     for (Value ubOp : ubOperands[en.index()])
145       llvm::errs() << "\t\t\t" << ubOp << "\n";
146   }
147 }
148 
149 /// Fast check to determine if the computation slice is maximal. Returns true if
150 /// each slice dimension maps to an existing dst dimension and both the src
151 /// and the dst loops for those dimensions have the same bounds. Returns false
152 /// if both the src and the dst loops don't have the same bounds. Returns
153 /// llvm::None if none of the above can be proven.
154 Optional<bool> ComputationSliceState::isSliceMaximalFastCheck() const {
155   assert(lbs.size() == ubs.size() && !lbs.empty() && !ivs.empty() &&
156          "Unexpected number of lbs, ubs and ivs in slice");
157 
158   for (unsigned i = 0, end = lbs.size(); i < end; ++i) {
159     AffineMap lbMap = lbs[i];
160     AffineMap ubMap = ubs[i];
161 
162     // Check if this slice is just an equality along this dimension.
163     if (!lbMap || !ubMap || lbMap.getNumResults() != 1 ||
164         ubMap.getNumResults() != 1 ||
165         lbMap.getResult(0) + 1 != ubMap.getResult(0) ||
166         // The condition above will be true for maps describing a single
167         // iteration (e.g., lbMap.getResult(0) = 0, ubMap.getResult(0) = 1).
168         // Make sure we skip those cases by checking that the lb result is not
169         // just a constant.
170         lbMap.getResult(0).isa<AffineConstantExpr>())
171       return llvm::None;
172 
173     // Limited support: we expect the lb result to be just a loop dimension for
174     // now.
175     AffineDimExpr result = lbMap.getResult(0).dyn_cast<AffineDimExpr>();
176     if (!result)
177       return llvm::None;
178 
179     // Retrieve dst loop bounds.
180     AffineForOp dstLoop =
181         getForInductionVarOwner(lbOperands[i][result.getPosition()]);
182     if (!dstLoop)
183       return llvm::None;
184     AffineMap dstLbMap = dstLoop.getLowerBoundMap();
185     AffineMap dstUbMap = dstLoop.getUpperBoundMap();
186 
187     // Retrieve src loop bounds.
188     AffineForOp srcLoop = getForInductionVarOwner(ivs[i]);
189     assert(srcLoop && "Expected affine for");
190     AffineMap srcLbMap = srcLoop.getLowerBoundMap();
191     AffineMap srcUbMap = srcLoop.getUpperBoundMap();
192 
193     // Limited support: we expect simple src and dst loops with a single
194     // constant component per bound for now.
195     if (srcLbMap.getNumResults() != 1 || srcUbMap.getNumResults() != 1 ||
196         dstLbMap.getNumResults() != 1 || dstUbMap.getNumResults() != 1)
197       return llvm::None;
198 
199     AffineExpr srcLbResult = srcLbMap.getResult(0);
200     AffineExpr dstLbResult = dstLbMap.getResult(0);
201     AffineExpr srcUbResult = srcUbMap.getResult(0);
202     AffineExpr dstUbResult = dstUbMap.getResult(0);
203     if (!srcLbResult.isa<AffineConstantExpr>() ||
204         !srcUbResult.isa<AffineConstantExpr>() ||
205         !dstLbResult.isa<AffineConstantExpr>() ||
206         !dstUbResult.isa<AffineConstantExpr>())
207       return llvm::None;
208 
209     // Check if src and dst loop bounds are the same. If not, we can guarantee
210     // that the slice is not maximal.
211     if (srcLbResult != dstLbResult || srcUbResult != dstUbResult ||
212         srcLoop.getStep() != dstLoop.getStep())
213       return false;
214   }
215 
216   return true;
217 }
218 
219 /// Returns true if it is deterministically verified that the original iteration
220 /// space of the slice is contained within the new iteration space that is
221 /// created after fusing 'this' slice into its destination.
222 Optional<bool> ComputationSliceState::isSliceValid() {
223   // Fast check to determine if the slice is valid. If the following conditions
224   // are verified to be true, slice is declared valid by the fast check:
225   // 1. Each slice loop is a single iteration loop bound in terms of a single
226   //    destination loop IV.
227   // 2. Loop bounds of the destination loop IV (from above) and those of the
228   //    source loop IV are exactly the same.
229   // If the fast check is inconclusive or false, we proceed with a more
230   // expensive analysis.
231   // TODO: Store the result of the fast check, as it might be used again in
232   // `canRemoveSrcNodeAfterFusion`.
233   Optional<bool> isValidFastCheck = isSliceMaximalFastCheck();
234   if (isValidFastCheck.hasValue() && isValidFastCheck.getValue())
235     return true;
236 
237   // Create constraints for the source loop nest using which slice is computed.
238   FlatAffineValueConstraints srcConstraints;
239   // TODO: Store the source's domain to avoid computation at each depth.
240   if (failed(getSourceAsConstraints(srcConstraints))) {
241     LLVM_DEBUG(llvm::dbgs() << "Unable to compute source's domain\n");
242     return llvm::None;
243   }
244   // As the set difference utility currently cannot handle symbols in its
245   // operands, validity of the slice cannot be determined.
246   if (srcConstraints.getNumSymbolIds() > 0) {
247     LLVM_DEBUG(llvm::dbgs() << "Cannot handle symbols in source domain\n");
248     return llvm::None;
249   }
250   // TODO: Handle local ids in the source domains while using the 'projectOut'
251   // utility below. Currently, aligning is not done assuming that there will be
252   // no local ids in the source domain.
253   if (srcConstraints.getNumLocalIds() != 0) {
254     LLVM_DEBUG(llvm::dbgs() << "Cannot handle locals in source domain\n");
255     return llvm::None;
256   }
257 
258   // Create constraints for the slice loop nest that would be created if the
259   // fusion succeeds.
260   FlatAffineValueConstraints sliceConstraints;
261   if (failed(getAsConstraints(&sliceConstraints))) {
262     LLVM_DEBUG(llvm::dbgs() << "Unable to compute slice's domain\n");
263     return llvm::None;
264   }
265 
266   // Projecting out every dimension other than the 'ivs' to express slice's
267   // domain completely in terms of source's IVs.
268   sliceConstraints.projectOut(ivs.size(),
269                               sliceConstraints.getNumIds() - ivs.size());
270 
271   LLVM_DEBUG(llvm::dbgs() << "Domain of the source of the slice:\n");
272   LLVM_DEBUG(srcConstraints.dump());
273   LLVM_DEBUG(llvm::dbgs() << "Domain of the slice if this fusion succeeds "
274                              "(expressed in terms of its source's IVs):\n");
275   LLVM_DEBUG(sliceConstraints.dump());
276 
277   // TODO: Store 'srcSet' to avoid recalculating for each depth.
278   PresburgerSet srcSet(srcConstraints);
279   PresburgerSet sliceSet(sliceConstraints);
280   PresburgerSet diffSet = sliceSet.subtract(srcSet);
281 
282   if (!diffSet.isIntegerEmpty()) {
283     LLVM_DEBUG(llvm::dbgs() << "Incorrect slice\n");
284     return false;
285   }
286   return true;
287 }
288 
289 /// Returns true if the computation slice encloses all the iterations of the
290 /// sliced loop nest. Returns false if it does not. Returns llvm::None if it
291 /// cannot determine if the slice is maximal or not.
292 Optional<bool> ComputationSliceState::isMaximal() const {
293   // Fast check to determine if the computation slice is maximal. If the result
294   // is inconclusive, we proceed with a more expensive analysis.
295   Optional<bool> isMaximalFastCheck = isSliceMaximalFastCheck();
296   if (isMaximalFastCheck.hasValue())
297     return isMaximalFastCheck;
298 
299   // Create constraints for the src loop nest being sliced.
300   FlatAffineValueConstraints srcConstraints;
301   srcConstraints.reset(/*numDims=*/ivs.size(), /*numSymbols=*/0,
302                        /*numLocals=*/0, ivs);
303   for (Value iv : ivs) {
304     AffineForOp loop = getForInductionVarOwner(iv);
305     assert(loop && "Expected affine for");
306     if (failed(srcConstraints.addAffineForOpDomain(loop)))
307       return llvm::None;
308   }
309 
310   // Create constraints for the slice using the dst loop nest information. We
311   // retrieve existing dst loops from the lbOperands.
312   SmallVector<Value, 8> consumerIVs;
313   for (Value lbOp : lbOperands[0])
314     if (getForInductionVarOwner(lbOp))
315       consumerIVs.push_back(lbOp);
316 
317   // Add empty IV Values for those new loops that are not equalities and,
318   // therefore, are not yet materialized in the IR.
319   for (int i = consumerIVs.size(), end = ivs.size(); i < end; ++i)
320     consumerIVs.push_back(Value());
321 
322   FlatAffineValueConstraints sliceConstraints;
323   sliceConstraints.reset(/*numDims=*/consumerIVs.size(), /*numSymbols=*/0,
324                          /*numLocals=*/0, consumerIVs);
325 
326   if (failed(sliceConstraints.addDomainFromSliceMaps(lbs, ubs, lbOperands[0])))
327     return llvm::None;
328 
329   if (srcConstraints.getNumDimIds() != sliceConstraints.getNumDimIds())
330     // Constraint dims are different. The integer set difference can't be
331     // computed so we don't know if the slice is maximal.
332     return llvm::None;
333 
334   // Compute the difference between the src loop nest and the slice integer
335   // sets.
336   PresburgerSet srcSet(srcConstraints);
337   PresburgerSet sliceSet(sliceConstraints);
338   PresburgerSet diffSet = srcSet.subtract(sliceSet);
339   return diffSet.isIntegerEmpty();
340 }
341 
342 unsigned MemRefRegion::getRank() const {
343   return memref.getType().cast<MemRefType>().getRank();
344 }
345 
346 Optional<int64_t> MemRefRegion::getConstantBoundingSizeAndShape(
347     SmallVectorImpl<int64_t> *shape, std::vector<SmallVector<int64_t, 4>> *lbs,
348     SmallVectorImpl<int64_t> *lbDivisors) const {
349   auto memRefType = memref.getType().cast<MemRefType>();
350   unsigned rank = memRefType.getRank();
351   if (shape)
352     shape->reserve(rank);
353 
354   assert(rank == cst.getNumDimIds() && "inconsistent memref region");
355 
356   // Use a copy of the region constraints that has upper/lower bounds for each
357   // memref dimension with static size added to guard against potential
358   // over-approximation from projection or union bounding box. We may not add
359   // this on the region itself since they might just be redundant constraints
360   // that will need non-trivials means to eliminate.
361   FlatAffineConstraints cstWithShapeBounds(cst);
362   for (unsigned r = 0; r < rank; r++) {
363     cstWithShapeBounds.addBound(FlatAffineConstraints::LB, r, 0);
364     int64_t dimSize = memRefType.getDimSize(r);
365     if (ShapedType::isDynamic(dimSize))
366       continue;
367     cstWithShapeBounds.addBound(FlatAffineConstraints::UB, r, dimSize - 1);
368   }
369 
370   // Find a constant upper bound on the extent of this memref region along each
371   // dimension.
372   int64_t numElements = 1;
373   int64_t diffConstant;
374   int64_t lbDivisor;
375   for (unsigned d = 0; d < rank; d++) {
376     SmallVector<int64_t, 4> lb;
377     Optional<int64_t> diff =
378         cstWithShapeBounds.getConstantBoundOnDimSize(d, &lb, &lbDivisor);
379     if (diff.hasValue()) {
380       diffConstant = diff.getValue();
381       assert(diffConstant >= 0 && "Dim size bound can't be negative");
382       assert(lbDivisor > 0);
383     } else {
384       // If no constant bound is found, then it can always be bound by the
385       // memref's dim size if the latter has a constant size along this dim.
386       auto dimSize = memRefType.getDimSize(d);
387       if (dimSize == -1)
388         return None;
389       diffConstant = dimSize;
390       // Lower bound becomes 0.
391       lb.resize(cstWithShapeBounds.getNumSymbolIds() + 1, 0);
392       lbDivisor = 1;
393     }
394     numElements *= diffConstant;
395     if (lbs) {
396       lbs->push_back(lb);
397       assert(lbDivisors && "both lbs and lbDivisor or none");
398       lbDivisors->push_back(lbDivisor);
399     }
400     if (shape) {
401       shape->push_back(diffConstant);
402     }
403   }
404   return numElements;
405 }
406 
407 void MemRefRegion::getLowerAndUpperBound(unsigned pos, AffineMap &lbMap,
408                                          AffineMap &ubMap) const {
409   assert(pos < cst.getNumDimIds() && "invalid position");
410   auto memRefType = memref.getType().cast<MemRefType>();
411   unsigned rank = memRefType.getRank();
412 
413   assert(rank == cst.getNumDimIds() && "inconsistent memref region");
414 
415   auto boundPairs = cst.getLowerAndUpperBound(
416       pos, /*offset=*/0, /*num=*/rank, cst.getNumDimAndSymbolIds(),
417       /*localExprs=*/{}, memRefType.getContext());
418   lbMap = boundPairs.first;
419   ubMap = boundPairs.second;
420   assert(lbMap && "lower bound for a region must exist");
421   assert(ubMap && "upper bound for a region must exist");
422   assert(lbMap.getNumInputs() == cst.getNumDimAndSymbolIds() - rank);
423   assert(ubMap.getNumInputs() == cst.getNumDimAndSymbolIds() - rank);
424 }
425 
426 LogicalResult MemRefRegion::unionBoundingBox(const MemRefRegion &other) {
427   assert(memref == other.memref);
428   return cst.unionBoundingBox(*other.getConstraints());
429 }
430 
431 /// Computes the memory region accessed by this memref with the region
432 /// represented as constraints symbolic/parametric in 'loopDepth' loops
433 /// surrounding opInst and any additional Function symbols.
434 //  For example, the memref region for this load operation at loopDepth = 1 will
435 //  be as below:
436 //
437 //    affine.for %i = 0 to 32 {
438 //      affine.for %ii = %i to (d0) -> (d0 + 8) (%i) {
439 //        load %A[%ii]
440 //      }
441 //    }
442 //
443 // region:  {memref = %A, write = false, {%i <= m0 <= %i + 7} }
444 // The last field is a 2-d FlatAffineConstraints symbolic in %i.
445 //
446 // TODO: extend this to any other memref dereferencing ops
447 // (dma_start, dma_wait).
448 LogicalResult MemRefRegion::compute(Operation *op, unsigned loopDepth,
449                                     const ComputationSliceState *sliceState,
450                                     bool addMemRefDimBounds) {
451   assert((isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) &&
452          "affine read/write op expected");
453 
454   MemRefAccess access(op);
455   memref = access.memref;
456   write = access.isStore();
457 
458   unsigned rank = access.getRank();
459 
460   LLVM_DEBUG(llvm::dbgs() << "MemRefRegion::compute: " << *op
461                           << "depth: " << loopDepth << "\n";);
462 
463   // 0-d memrefs.
464   if (rank == 0) {
465     SmallVector<AffineForOp, 4> ivs;
466     getLoopIVs(*op, &ivs);
467     assert(loopDepth <= ivs.size() && "invalid 'loopDepth'");
468     // The first 'loopDepth' IVs are symbols for this region.
469     ivs.resize(loopDepth);
470     SmallVector<Value, 4> regionSymbols;
471     extractForInductionVars(ivs, &regionSymbols);
472     // A 0-d memref has a 0-d region.
473     cst.reset(rank, loopDepth, /*numLocals=*/0, regionSymbols);
474     return success();
475   }
476 
477   // Build the constraints for this region.
478   AffineValueMap accessValueMap;
479   access.getAccessMap(&accessValueMap);
480   AffineMap accessMap = accessValueMap.getAffineMap();
481 
482   unsigned numDims = accessMap.getNumDims();
483   unsigned numSymbols = accessMap.getNumSymbols();
484   unsigned numOperands = accessValueMap.getNumOperands();
485   // Merge operands with slice operands.
486   SmallVector<Value, 4> operands;
487   operands.resize(numOperands);
488   for (unsigned i = 0; i < numOperands; ++i)
489     operands[i] = accessValueMap.getOperand(i);
490 
491   if (sliceState != nullptr) {
492     operands.reserve(operands.size() + sliceState->lbOperands[0].size());
493     // Append slice operands to 'operands' as symbols.
494     for (auto extraOperand : sliceState->lbOperands[0]) {
495       if (!llvm::is_contained(operands, extraOperand)) {
496         operands.push_back(extraOperand);
497         numSymbols++;
498       }
499     }
500   }
501   // We'll first associate the dims and symbols of the access map to the dims
502   // and symbols resp. of cst. This will change below once cst is
503   // fully constructed out.
504   cst.reset(numDims, numSymbols, 0, operands);
505 
506   // Add equality constraints.
507   // Add inequalities for loop lower/upper bounds.
508   for (unsigned i = 0; i < numDims + numSymbols; ++i) {
509     auto operand = operands[i];
510     if (auto loop = getForInductionVarOwner(operand)) {
511       // Note that cst can now have more dimensions than accessMap if the
512       // bounds expressions involve outer loops or other symbols.
513       // TODO: rewrite this to use getInstIndexSet; this way
514       // conditionals will be handled when the latter supports it.
515       if (failed(cst.addAffineForOpDomain(loop)))
516         return failure();
517     } else {
518       // Has to be a valid symbol.
519       auto symbol = operand;
520       assert(isValidSymbol(symbol));
521       // Check if the symbol is a constant.
522       if (auto *op = symbol.getDefiningOp()) {
523         if (auto constOp = dyn_cast<arith::ConstantIndexOp>(op)) {
524           cst.addBound(FlatAffineConstraints::EQ, symbol, constOp.value());
525         }
526       }
527     }
528   }
529 
530   // Add lower/upper bounds on loop IVs using bounds from 'sliceState'.
531   if (sliceState != nullptr) {
532     // Add dim and symbol slice operands.
533     for (auto operand : sliceState->lbOperands[0]) {
534       cst.addInductionVarOrTerminalSymbol(operand);
535     }
536     // Add upper/lower bounds from 'sliceState' to 'cst'.
537     LogicalResult ret =
538         cst.addSliceBounds(sliceState->ivs, sliceState->lbs, sliceState->ubs,
539                            sliceState->lbOperands[0]);
540     assert(succeeded(ret) &&
541            "should not fail as we never have semi-affine slice maps");
542     (void)ret;
543   }
544 
545   // Add access function equalities to connect loop IVs to data dimensions.
546   if (failed(cst.composeMap(&accessValueMap))) {
547     op->emitError("getMemRefRegion: compose affine map failed");
548     LLVM_DEBUG(accessValueMap.getAffineMap().dump());
549     return failure();
550   }
551 
552   // Set all identifiers appearing after the first 'rank' identifiers as
553   // symbolic identifiers - so that the ones corresponding to the memref
554   // dimensions are the dimensional identifiers for the memref region.
555   cst.setDimSymbolSeparation(cst.getNumDimAndSymbolIds() - rank);
556 
557   // Eliminate any loop IVs other than the outermost 'loopDepth' IVs, on which
558   // this memref region is symbolic.
559   SmallVector<AffineForOp, 4> enclosingIVs;
560   getLoopIVs(*op, &enclosingIVs);
561   assert(loopDepth <= enclosingIVs.size() && "invalid loop depth");
562   enclosingIVs.resize(loopDepth);
563   SmallVector<Value, 4> ids;
564   cst.getValues(cst.getNumDimIds(), cst.getNumDimAndSymbolIds(), &ids);
565   for (auto id : ids) {
566     AffineForOp iv;
567     if ((iv = getForInductionVarOwner(id)) &&
568         !llvm::is_contained(enclosingIVs, iv)) {
569       cst.projectOut(id);
570     }
571   }
572 
573   // Project out any local variables (these would have been added for any
574   // mod/divs).
575   cst.projectOut(cst.getNumDimAndSymbolIds(), cst.getNumLocalIds());
576 
577   // Constant fold any symbolic identifiers.
578   cst.constantFoldIdRange(/*pos=*/cst.getNumDimIds(),
579                           /*num=*/cst.getNumSymbolIds());
580 
581   assert(cst.getNumDimIds() == rank && "unexpected MemRefRegion format");
582 
583   // Add upper/lower bounds for each memref dimension with static size
584   // to guard against potential over-approximation from projection.
585   // TODO: Support dynamic memref dimensions.
586   if (addMemRefDimBounds) {
587     auto memRefType = memref.getType().cast<MemRefType>();
588     for (unsigned r = 0; r < rank; r++) {
589       cst.addBound(FlatAffineConstraints::LB, /*pos=*/r, /*value=*/0);
590       if (memRefType.isDynamicDim(r))
591         continue;
592       cst.addBound(FlatAffineConstraints::UB, /*pos=*/r,
593                    memRefType.getDimSize(r) - 1);
594     }
595   }
596   cst.removeTrivialRedundancy();
597 
598   LLVM_DEBUG(llvm::dbgs() << "Memory region:\n");
599   LLVM_DEBUG(cst.dump());
600   return success();
601 }
602 
603 static unsigned getMemRefEltSizeInBytes(MemRefType memRefType) {
604   auto elementType = memRefType.getElementType();
605 
606   unsigned sizeInBits;
607   if (elementType.isIntOrFloat()) {
608     sizeInBits = elementType.getIntOrFloatBitWidth();
609   } else {
610     auto vectorType = elementType.cast<VectorType>();
611     sizeInBits =
612         vectorType.getElementTypeBitWidth() * vectorType.getNumElements();
613   }
614   return llvm::divideCeil(sizeInBits, 8);
615 }
616 
617 // Returns the size of the region.
618 Optional<int64_t> MemRefRegion::getRegionSize() {
619   auto memRefType = memref.getType().cast<MemRefType>();
620 
621   if (!memRefType.getLayout().isIdentity()) {
622     LLVM_DEBUG(llvm::dbgs() << "Non-identity layout map not yet supported\n");
623     return false;
624   }
625 
626   // Indices to use for the DmaStart op.
627   // Indices for the original memref being DMAed from/to.
628   SmallVector<Value, 4> memIndices;
629   // Indices for the faster buffer being DMAed into/from.
630   SmallVector<Value, 4> bufIndices;
631 
632   // Compute the extents of the buffer.
633   Optional<int64_t> numElements = getConstantBoundingSizeAndShape();
634   if (!numElements.hasValue()) {
635     LLVM_DEBUG(llvm::dbgs() << "Dynamic shapes not yet supported\n");
636     return None;
637   }
638   return getMemRefEltSizeInBytes(memRefType) * numElements.getValue();
639 }
640 
641 /// Returns the size of memref data in bytes if it's statically shaped, None
642 /// otherwise.  If the element of the memref has vector type, takes into account
643 /// size of the vector as well.
644 //  TODO: improve/complete this when we have target data.
645 Optional<uint64_t> mlir::getMemRefSizeInBytes(MemRefType memRefType) {
646   if (!memRefType.hasStaticShape())
647     return None;
648   auto elementType = memRefType.getElementType();
649   if (!elementType.isIntOrFloat() && !elementType.isa<VectorType>())
650     return None;
651 
652   uint64_t sizeInBytes = getMemRefEltSizeInBytes(memRefType);
653   for (unsigned i = 0, e = memRefType.getRank(); i < e; i++) {
654     sizeInBytes = sizeInBytes * memRefType.getDimSize(i);
655   }
656   return sizeInBytes;
657 }
658 
659 template <typename LoadOrStoreOp>
660 LogicalResult mlir::boundCheckLoadOrStoreOp(LoadOrStoreOp loadOrStoreOp,
661                                             bool emitError) {
662   static_assert(llvm::is_one_of<LoadOrStoreOp, AffineReadOpInterface,
663                                 AffineWriteOpInterface>::value,
664                 "argument should be either a AffineReadOpInterface or a "
665                 "AffineWriteOpInterface");
666 
667   Operation *op = loadOrStoreOp.getOperation();
668   MemRefRegion region(op->getLoc());
669   if (failed(region.compute(op, /*loopDepth=*/0, /*sliceState=*/nullptr,
670                             /*addMemRefDimBounds=*/false)))
671     return success();
672 
673   LLVM_DEBUG(llvm::dbgs() << "Memory region");
674   LLVM_DEBUG(region.getConstraints()->dump());
675 
676   bool outOfBounds = false;
677   unsigned rank = loadOrStoreOp.getMemRefType().getRank();
678 
679   // For each dimension, check for out of bounds.
680   for (unsigned r = 0; r < rank; r++) {
681     FlatAffineConstraints ucst(*region.getConstraints());
682 
683     // Intersect memory region with constraint capturing out of bounds (both out
684     // of upper and out of lower), and check if the constraint system is
685     // feasible. If it is, there is at least one point out of bounds.
686     SmallVector<int64_t, 4> ineq(rank + 1, 0);
687     int64_t dimSize = loadOrStoreOp.getMemRefType().getDimSize(r);
688     // TODO: handle dynamic dim sizes.
689     if (dimSize == -1)
690       continue;
691 
692     // Check for overflow: d_i >= memref dim size.
693     ucst.addBound(FlatAffineConstraints::LB, r, dimSize);
694     outOfBounds = !ucst.isEmpty();
695     if (outOfBounds && emitError) {
696       loadOrStoreOp.emitOpError()
697           << "memref out of upper bound access along dimension #" << (r + 1);
698     }
699 
700     // Check for a negative index.
701     FlatAffineConstraints lcst(*region.getConstraints());
702     std::fill(ineq.begin(), ineq.end(), 0);
703     // d_i <= -1;
704     lcst.addBound(FlatAffineConstraints::UB, r, -1);
705     outOfBounds = !lcst.isEmpty();
706     if (outOfBounds && emitError) {
707       loadOrStoreOp.emitOpError()
708           << "memref out of lower bound access along dimension #" << (r + 1);
709     }
710   }
711   return failure(outOfBounds);
712 }
713 
714 // Explicitly instantiate the template so that the compiler knows we need them!
715 template LogicalResult
716 mlir::boundCheckLoadOrStoreOp(AffineReadOpInterface loadOp, bool emitError);
717 template LogicalResult
718 mlir::boundCheckLoadOrStoreOp(AffineWriteOpInterface storeOp, bool emitError);
719 
720 // Returns in 'positions' the Block positions of 'op' in each ancestor
721 // Block from the Block containing operation, stopping at 'limitBlock'.
722 static void findInstPosition(Operation *op, Block *limitBlock,
723                              SmallVectorImpl<unsigned> *positions) {
724   Block *block = op->getBlock();
725   while (block != limitBlock) {
726     // FIXME: This algorithm is unnecessarily O(n) and should be improved to not
727     // rely on linear scans.
728     int instPosInBlock = std::distance(block->begin(), op->getIterator());
729     positions->push_back(instPosInBlock);
730     op = block->getParentOp();
731     block = op->getBlock();
732   }
733   std::reverse(positions->begin(), positions->end());
734 }
735 
736 // Returns the Operation in a possibly nested set of Blocks, where the
737 // position of the operation is represented by 'positions', which has a
738 // Block position for each level of nesting.
739 static Operation *getInstAtPosition(ArrayRef<unsigned> positions,
740                                     unsigned level, Block *block) {
741   unsigned i = 0;
742   for (auto &op : *block) {
743     if (i != positions[level]) {
744       ++i;
745       continue;
746     }
747     if (level == positions.size() - 1)
748       return &op;
749     if (auto childAffineForOp = dyn_cast<AffineForOp>(op))
750       return getInstAtPosition(positions, level + 1,
751                                childAffineForOp.getBody());
752 
753     for (auto &region : op.getRegions()) {
754       for (auto &b : region)
755         if (auto *ret = getInstAtPosition(positions, level + 1, &b))
756           return ret;
757     }
758     return nullptr;
759   }
760   return nullptr;
761 }
762 
763 // Adds loop IV bounds to 'cst' for loop IVs not found in 'ivs'.
764 static LogicalResult addMissingLoopIVBounds(SmallPtrSet<Value, 8> &ivs,
765                                             FlatAffineValueConstraints *cst) {
766   for (unsigned i = 0, e = cst->getNumDimIds(); i < e; ++i) {
767     auto value = cst->getValue(i);
768     if (ivs.count(value) == 0) {
769       assert(isForInductionVar(value));
770       auto loop = getForInductionVarOwner(value);
771       if (failed(cst->addAffineForOpDomain(loop)))
772         return failure();
773     }
774   }
775   return success();
776 }
777 
778 /// Returns the innermost common loop depth for the set of operations in 'ops'.
779 // TODO: Move this to LoopUtils.
780 unsigned mlir::getInnermostCommonLoopDepth(
781     ArrayRef<Operation *> ops, SmallVectorImpl<AffineForOp> *surroundingLoops) {
782   unsigned numOps = ops.size();
783   assert(numOps > 0 && "Expected at least one operation");
784 
785   std::vector<SmallVector<AffineForOp, 4>> loops(numOps);
786   unsigned loopDepthLimit = std::numeric_limits<unsigned>::max();
787   for (unsigned i = 0; i < numOps; ++i) {
788     getLoopIVs(*ops[i], &loops[i]);
789     loopDepthLimit =
790         std::min(loopDepthLimit, static_cast<unsigned>(loops[i].size()));
791   }
792 
793   unsigned loopDepth = 0;
794   for (unsigned d = 0; d < loopDepthLimit; ++d) {
795     unsigned i;
796     for (i = 1; i < numOps; ++i) {
797       if (loops[i - 1][d] != loops[i][d])
798         return loopDepth;
799     }
800     if (surroundingLoops)
801       surroundingLoops->push_back(loops[i - 1][d]);
802     ++loopDepth;
803   }
804   return loopDepth;
805 }
806 
807 /// Computes in 'sliceUnion' the union of all slice bounds computed at
808 /// 'loopDepth' between all dependent pairs of ops in 'opsA' and 'opsB', and
809 /// then verifies if it is valid. Returns 'SliceComputationResult::Success' if
810 /// union was computed correctly, an appropriate failure otherwise.
811 SliceComputationResult
812 mlir::computeSliceUnion(ArrayRef<Operation *> opsA, ArrayRef<Operation *> opsB,
813                         unsigned loopDepth, unsigned numCommonLoops,
814                         bool isBackwardSlice,
815                         ComputationSliceState *sliceUnion) {
816   // Compute the union of slice bounds between all pairs in 'opsA' and
817   // 'opsB' in 'sliceUnionCst'.
818   FlatAffineValueConstraints sliceUnionCst;
819   assert(sliceUnionCst.getNumDimAndSymbolIds() == 0);
820   std::vector<std::pair<Operation *, Operation *>> dependentOpPairs;
821   for (auto *i : opsA) {
822     MemRefAccess srcAccess(i);
823     for (auto *j : opsB) {
824       MemRefAccess dstAccess(j);
825       if (srcAccess.memref != dstAccess.memref)
826         continue;
827       // Check if 'loopDepth' exceeds nesting depth of src/dst ops.
828       if ((!isBackwardSlice && loopDepth > getNestingDepth(i)) ||
829           (isBackwardSlice && loopDepth > getNestingDepth(j))) {
830         LLVM_DEBUG(llvm::dbgs() << "Invalid loop depth\n");
831         return SliceComputationResult::GenericFailure;
832       }
833 
834       bool readReadAccesses = isa<AffineReadOpInterface>(srcAccess.opInst) &&
835                               isa<AffineReadOpInterface>(dstAccess.opInst);
836       FlatAffineValueConstraints dependenceConstraints;
837       // Check dependence between 'srcAccess' and 'dstAccess'.
838       DependenceResult result = checkMemrefAccessDependence(
839           srcAccess, dstAccess, /*loopDepth=*/numCommonLoops + 1,
840           &dependenceConstraints, /*dependenceComponents=*/nullptr,
841           /*allowRAR=*/readReadAccesses);
842       if (result.value == DependenceResult::Failure) {
843         LLVM_DEBUG(llvm::dbgs() << "Dependence check failed\n");
844         return SliceComputationResult::GenericFailure;
845       }
846       if (result.value == DependenceResult::NoDependence)
847         continue;
848       dependentOpPairs.emplace_back(i, j);
849 
850       // Compute slice bounds for 'srcAccess' and 'dstAccess'.
851       ComputationSliceState tmpSliceState;
852       mlir::getComputationSliceState(i, j, &dependenceConstraints, loopDepth,
853                                      isBackwardSlice, &tmpSliceState);
854 
855       if (sliceUnionCst.getNumDimAndSymbolIds() == 0) {
856         // Initialize 'sliceUnionCst' with the bounds computed in previous step.
857         if (failed(tmpSliceState.getAsConstraints(&sliceUnionCst))) {
858           LLVM_DEBUG(llvm::dbgs()
859                      << "Unable to compute slice bound constraints\n");
860           return SliceComputationResult::GenericFailure;
861         }
862         assert(sliceUnionCst.getNumDimAndSymbolIds() > 0);
863         continue;
864       }
865 
866       // Compute constraints for 'tmpSliceState' in 'tmpSliceCst'.
867       FlatAffineValueConstraints tmpSliceCst;
868       if (failed(tmpSliceState.getAsConstraints(&tmpSliceCst))) {
869         LLVM_DEBUG(llvm::dbgs()
870                    << "Unable to compute slice bound constraints\n");
871         return SliceComputationResult::GenericFailure;
872       }
873 
874       // Align coordinate spaces of 'sliceUnionCst' and 'tmpSliceCst' if needed.
875       if (!sliceUnionCst.areIdsAlignedWithOther(tmpSliceCst)) {
876 
877         // Pre-constraint id alignment: record loop IVs used in each constraint
878         // system.
879         SmallPtrSet<Value, 8> sliceUnionIVs;
880         for (unsigned k = 0, l = sliceUnionCst.getNumDimIds(); k < l; ++k)
881           sliceUnionIVs.insert(sliceUnionCst.getValue(k));
882         SmallPtrSet<Value, 8> tmpSliceIVs;
883         for (unsigned k = 0, l = tmpSliceCst.getNumDimIds(); k < l; ++k)
884           tmpSliceIVs.insert(tmpSliceCst.getValue(k));
885 
886         sliceUnionCst.mergeAndAlignIdsWithOther(/*offset=*/0, &tmpSliceCst);
887 
888         // Post-constraint id alignment: add loop IV bounds missing after
889         // id alignment to constraint systems. This can occur if one constraint
890         // system uses an loop IV that is not used by the other. The call
891         // to unionBoundingBox below expects constraints for each Loop IV, even
892         // if they are the unsliced full loop bounds added here.
893         if (failed(addMissingLoopIVBounds(sliceUnionIVs, &sliceUnionCst)))
894           return SliceComputationResult::GenericFailure;
895         if (failed(addMissingLoopIVBounds(tmpSliceIVs, &tmpSliceCst)))
896           return SliceComputationResult::GenericFailure;
897       }
898       // Compute union bounding box of 'sliceUnionCst' and 'tmpSliceCst'.
899       if (sliceUnionCst.getNumLocalIds() > 0 ||
900           tmpSliceCst.getNumLocalIds() > 0 ||
901           failed(sliceUnionCst.unionBoundingBox(tmpSliceCst))) {
902         LLVM_DEBUG(llvm::dbgs()
903                    << "Unable to compute union bounding box of slice bounds\n");
904         return SliceComputationResult::GenericFailure;
905       }
906     }
907   }
908 
909   // Empty union.
910   if (sliceUnionCst.getNumDimAndSymbolIds() == 0)
911     return SliceComputationResult::GenericFailure;
912 
913   // Gather loops surrounding ops from loop nest where slice will be inserted.
914   SmallVector<Operation *, 4> ops;
915   for (auto &dep : dependentOpPairs) {
916     ops.push_back(isBackwardSlice ? dep.second : dep.first);
917   }
918   SmallVector<AffineForOp, 4> surroundingLoops;
919   unsigned innermostCommonLoopDepth =
920       getInnermostCommonLoopDepth(ops, &surroundingLoops);
921   if (loopDepth > innermostCommonLoopDepth) {
922     LLVM_DEBUG(llvm::dbgs() << "Exceeds max loop depth\n");
923     return SliceComputationResult::GenericFailure;
924   }
925 
926   // Store 'numSliceLoopIVs' before converting dst loop IVs to dims.
927   unsigned numSliceLoopIVs = sliceUnionCst.getNumDimIds();
928 
929   // Convert any dst loop IVs which are symbol identifiers to dim identifiers.
930   sliceUnionCst.convertLoopIVSymbolsToDims();
931   sliceUnion->clearBounds();
932   sliceUnion->lbs.resize(numSliceLoopIVs, AffineMap());
933   sliceUnion->ubs.resize(numSliceLoopIVs, AffineMap());
934 
935   // Get slice bounds from slice union constraints 'sliceUnionCst'.
936   sliceUnionCst.getSliceBounds(/*offset=*/0, numSliceLoopIVs,
937                                opsA[0]->getContext(), &sliceUnion->lbs,
938                                &sliceUnion->ubs);
939 
940   // Add slice bound operands of union.
941   SmallVector<Value, 4> sliceBoundOperands;
942   sliceUnionCst.getValues(numSliceLoopIVs,
943                           sliceUnionCst.getNumDimAndSymbolIds(),
944                           &sliceBoundOperands);
945 
946   // Copy src loop IVs from 'sliceUnionCst' to 'sliceUnion'.
947   sliceUnion->ivs.clear();
948   sliceUnionCst.getValues(0, numSliceLoopIVs, &sliceUnion->ivs);
949 
950   // Set loop nest insertion point to block start at 'loopDepth'.
951   sliceUnion->insertPoint =
952       isBackwardSlice
953           ? surroundingLoops[loopDepth - 1].getBody()->begin()
954           : std::prev(surroundingLoops[loopDepth - 1].getBody()->end());
955 
956   // Give each bound its own copy of 'sliceBoundOperands' for subsequent
957   // canonicalization.
958   sliceUnion->lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
959   sliceUnion->ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
960 
961   // Check if the slice computed is valid. Return success only if it is verified
962   // that the slice is valid, otherwise return appropriate failure status.
963   Optional<bool> isSliceValid = sliceUnion->isSliceValid();
964   if (!isSliceValid.hasValue()) {
965     LLVM_DEBUG(llvm::dbgs() << "Cannot determine if the slice is valid\n");
966     return SliceComputationResult::GenericFailure;
967   }
968   if (!isSliceValid.getValue())
969     return SliceComputationResult::IncorrectSliceFailure;
970 
971   return SliceComputationResult::Success;
972 }
973 
974 // TODO: extend this to handle multiple result maps.
975 static Optional<uint64_t> getConstDifference(AffineMap lbMap, AffineMap ubMap) {
976   assert(lbMap.getNumResults() == 1 && "expected single result bound map");
977   assert(ubMap.getNumResults() == 1 && "expected single result bound map");
978   assert(lbMap.getNumDims() == ubMap.getNumDims());
979   assert(lbMap.getNumSymbols() == ubMap.getNumSymbols());
980   AffineExpr lbExpr(lbMap.getResult(0));
981   AffineExpr ubExpr(ubMap.getResult(0));
982   auto loopSpanExpr = simplifyAffineExpr(ubExpr - lbExpr, lbMap.getNumDims(),
983                                          lbMap.getNumSymbols());
984   auto cExpr = loopSpanExpr.dyn_cast<AffineConstantExpr>();
985   if (!cExpr)
986     return None;
987   return cExpr.getValue();
988 }
989 
990 // Builds a map 'tripCountMap' from AffineForOp to constant trip count for loop
991 // nest surrounding represented by slice loop bounds in 'slice'. Returns true
992 // on success, false otherwise (if a non-constant trip count was encountered).
993 // TODO: Make this work with non-unit step loops.
994 bool mlir::buildSliceTripCountMap(
995     const ComputationSliceState &slice,
996     llvm::SmallDenseMap<Operation *, uint64_t, 8> *tripCountMap) {
997   unsigned numSrcLoopIVs = slice.ivs.size();
998   // Populate map from AffineForOp -> trip count
999   for (unsigned i = 0; i < numSrcLoopIVs; ++i) {
1000     AffineForOp forOp = getForInductionVarOwner(slice.ivs[i]);
1001     auto *op = forOp.getOperation();
1002     AffineMap lbMap = slice.lbs[i];
1003     AffineMap ubMap = slice.ubs[i];
1004     // If lower or upper bound maps are null or provide no results, it implies
1005     // that source loop was not at all sliced, and the entire loop will be a
1006     // part of the slice.
1007     if (!lbMap || lbMap.getNumResults() == 0 || !ubMap ||
1008         ubMap.getNumResults() == 0) {
1009       // The iteration of src loop IV 'i' was not sliced. Use full loop bounds.
1010       if (forOp.hasConstantLowerBound() && forOp.hasConstantUpperBound()) {
1011         (*tripCountMap)[op] =
1012             forOp.getConstantUpperBound() - forOp.getConstantLowerBound();
1013         continue;
1014       }
1015       Optional<uint64_t> maybeConstTripCount = getConstantTripCount(forOp);
1016       if (maybeConstTripCount.hasValue()) {
1017         (*tripCountMap)[op] = maybeConstTripCount.getValue();
1018         continue;
1019       }
1020       return false;
1021     }
1022     Optional<uint64_t> tripCount = getConstDifference(lbMap, ubMap);
1023     // Slice bounds are created with a constant ub - lb difference.
1024     if (!tripCount.hasValue())
1025       return false;
1026     (*tripCountMap)[op] = tripCount.getValue();
1027   }
1028   return true;
1029 }
1030 
1031 // Return the number of iterations in the given slice.
1032 uint64_t mlir::getSliceIterationCount(
1033     const llvm::SmallDenseMap<Operation *, uint64_t, 8> &sliceTripCountMap) {
1034   uint64_t iterCount = 1;
1035   for (const auto &count : sliceTripCountMap) {
1036     iterCount *= count.second;
1037   }
1038   return iterCount;
1039 }
1040 
1041 const char *const kSliceFusionBarrierAttrName = "slice_fusion_barrier";
1042 // Computes slice bounds by projecting out any loop IVs from
1043 // 'dependenceConstraints' at depth greater than 'loopDepth', and computes slice
1044 // bounds in 'sliceState' which represent the one loop nest's IVs in terms of
1045 // the other loop nest's IVs, symbols and constants (using 'isBackwardsSlice').
1046 void mlir::getComputationSliceState(
1047     Operation *depSourceOp, Operation *depSinkOp,
1048     FlatAffineValueConstraints *dependenceConstraints, unsigned loopDepth,
1049     bool isBackwardSlice, ComputationSliceState *sliceState) {
1050   // Get loop nest surrounding src operation.
1051   SmallVector<AffineForOp, 4> srcLoopIVs;
1052   getLoopIVs(*depSourceOp, &srcLoopIVs);
1053   unsigned numSrcLoopIVs = srcLoopIVs.size();
1054 
1055   // Get loop nest surrounding dst operation.
1056   SmallVector<AffineForOp, 4> dstLoopIVs;
1057   getLoopIVs(*depSinkOp, &dstLoopIVs);
1058   unsigned numDstLoopIVs = dstLoopIVs.size();
1059 
1060   assert((!isBackwardSlice && loopDepth <= numSrcLoopIVs) ||
1061          (isBackwardSlice && loopDepth <= numDstLoopIVs));
1062 
1063   // Project out dimensions other than those up to 'loopDepth'.
1064   unsigned pos = isBackwardSlice ? numSrcLoopIVs + loopDepth : loopDepth;
1065   unsigned num =
1066       isBackwardSlice ? numDstLoopIVs - loopDepth : numSrcLoopIVs - loopDepth;
1067   dependenceConstraints->projectOut(pos, num);
1068 
1069   // Add slice loop IV values to 'sliceState'.
1070   unsigned offset = isBackwardSlice ? 0 : loopDepth;
1071   unsigned numSliceLoopIVs = isBackwardSlice ? numSrcLoopIVs : numDstLoopIVs;
1072   dependenceConstraints->getValues(offset, offset + numSliceLoopIVs,
1073                                    &sliceState->ivs);
1074 
1075   // Set up lower/upper bound affine maps for the slice.
1076   sliceState->lbs.resize(numSliceLoopIVs, AffineMap());
1077   sliceState->ubs.resize(numSliceLoopIVs, AffineMap());
1078 
1079   // Get bounds for slice IVs in terms of other IVs, symbols, and constants.
1080   dependenceConstraints->getSliceBounds(offset, numSliceLoopIVs,
1081                                         depSourceOp->getContext(),
1082                                         &sliceState->lbs, &sliceState->ubs);
1083 
1084   // Set up bound operands for the slice's lower and upper bounds.
1085   SmallVector<Value, 4> sliceBoundOperands;
1086   unsigned numDimsAndSymbols = dependenceConstraints->getNumDimAndSymbolIds();
1087   for (unsigned i = 0; i < numDimsAndSymbols; ++i) {
1088     if (i < offset || i >= offset + numSliceLoopIVs) {
1089       sliceBoundOperands.push_back(dependenceConstraints->getValue(i));
1090     }
1091   }
1092 
1093   // Give each bound its own copy of 'sliceBoundOperands' for subsequent
1094   // canonicalization.
1095   sliceState->lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1096   sliceState->ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1097 
1098   // Set destination loop nest insertion point to block start at 'dstLoopDepth'.
1099   sliceState->insertPoint =
1100       isBackwardSlice ? dstLoopIVs[loopDepth - 1].getBody()->begin()
1101                       : std::prev(srcLoopIVs[loopDepth - 1].getBody()->end());
1102 
1103   llvm::SmallDenseSet<Value, 8> sequentialLoops;
1104   if (isa<AffineReadOpInterface>(depSourceOp) &&
1105       isa<AffineReadOpInterface>(depSinkOp)) {
1106     // For read-read access pairs, clear any slice bounds on sequential loops.
1107     // Get sequential loops in loop nest rooted at 'srcLoopIVs[0]'.
1108     getSequentialLoops(isBackwardSlice ? srcLoopIVs[0] : dstLoopIVs[0],
1109                        &sequentialLoops);
1110   }
1111   auto getSliceLoop = [&](unsigned i) {
1112     return isBackwardSlice ? srcLoopIVs[i] : dstLoopIVs[i];
1113   };
1114   auto isInnermostInsertion = [&]() {
1115     return (isBackwardSlice ? loopDepth >= srcLoopIVs.size()
1116                             : loopDepth >= dstLoopIVs.size());
1117   };
1118   llvm::SmallDenseMap<Operation *, uint64_t, 8> sliceTripCountMap;
1119   auto srcIsUnitSlice = [&]() {
1120     return (buildSliceTripCountMap(*sliceState, &sliceTripCountMap) &&
1121             (getSliceIterationCount(sliceTripCountMap) == 1));
1122   };
1123   // Clear all sliced loop bounds beginning at the first sequential loop, or
1124   // first loop with a slice fusion barrier attribute..
1125 
1126   for (unsigned i = 0; i < numSliceLoopIVs; ++i) {
1127     Value iv = getSliceLoop(i).getInductionVar();
1128     if (sequentialLoops.count(iv) == 0 &&
1129         getSliceLoop(i)->getAttr(kSliceFusionBarrierAttrName) == nullptr)
1130       continue;
1131     // Skip reset of bounds of reduction loop inserted in the destination loop
1132     // that meets the following conditions:
1133     //    1. Slice is  single trip count.
1134     //    2. Loop bounds of the source and destination match.
1135     //    3. Is being inserted at the innermost insertion point.
1136     Optional<bool> isMaximal = sliceState->isMaximal();
1137     if (isLoopParallelAndContainsReduction(getSliceLoop(i)) &&
1138         isInnermostInsertion() && srcIsUnitSlice() && isMaximal.hasValue() &&
1139         isMaximal.getValue())
1140       continue;
1141     for (unsigned j = i; j < numSliceLoopIVs; ++j) {
1142       sliceState->lbs[j] = AffineMap();
1143       sliceState->ubs[j] = AffineMap();
1144     }
1145     break;
1146   }
1147 }
1148 
1149 /// Creates a computation slice of the loop nest surrounding 'srcOpInst',
1150 /// updates the slice loop bounds with any non-null bound maps specified in
1151 /// 'sliceState', and inserts this slice into the loop nest surrounding
1152 /// 'dstOpInst' at loop depth 'dstLoopDepth'.
1153 // TODO: extend the slicing utility to compute slices that
1154 // aren't necessarily a one-to-one relation b/w the source and destination. The
1155 // relation between the source and destination could be many-to-many in general.
1156 // TODO: the slice computation is incorrect in the cases
1157 // where the dependence from the source to the destination does not cover the
1158 // entire destination index set. Subtract out the dependent destination
1159 // iterations from destination index set and check for emptiness --- this is one
1160 // solution.
1161 AffineForOp
1162 mlir::insertBackwardComputationSlice(Operation *srcOpInst, Operation *dstOpInst,
1163                                      unsigned dstLoopDepth,
1164                                      ComputationSliceState *sliceState) {
1165   // Get loop nest surrounding src operation.
1166   SmallVector<AffineForOp, 4> srcLoopIVs;
1167   getLoopIVs(*srcOpInst, &srcLoopIVs);
1168   unsigned numSrcLoopIVs = srcLoopIVs.size();
1169 
1170   // Get loop nest surrounding dst operation.
1171   SmallVector<AffineForOp, 4> dstLoopIVs;
1172   getLoopIVs(*dstOpInst, &dstLoopIVs);
1173   unsigned dstLoopIVsSize = dstLoopIVs.size();
1174   if (dstLoopDepth > dstLoopIVsSize) {
1175     dstOpInst->emitError("invalid destination loop depth");
1176     return AffineForOp();
1177   }
1178 
1179   // Find the op block positions of 'srcOpInst' within 'srcLoopIVs'.
1180   SmallVector<unsigned, 4> positions;
1181   // TODO: This code is incorrect since srcLoopIVs can be 0-d.
1182   findInstPosition(srcOpInst, srcLoopIVs[0]->getBlock(), &positions);
1183 
1184   // Clone src loop nest and insert it a the beginning of the operation block
1185   // of the loop at 'dstLoopDepth' in 'dstLoopIVs'.
1186   auto dstAffineForOp = dstLoopIVs[dstLoopDepth - 1];
1187   OpBuilder b(dstAffineForOp.getBody(), dstAffineForOp.getBody()->begin());
1188   auto sliceLoopNest =
1189       cast<AffineForOp>(b.clone(*srcLoopIVs[0].getOperation()));
1190 
1191   Operation *sliceInst =
1192       getInstAtPosition(positions, /*level=*/0, sliceLoopNest.getBody());
1193   // Get loop nest surrounding 'sliceInst'.
1194   SmallVector<AffineForOp, 4> sliceSurroundingLoops;
1195   getLoopIVs(*sliceInst, &sliceSurroundingLoops);
1196 
1197   // Sanity check.
1198   unsigned sliceSurroundingLoopsSize = sliceSurroundingLoops.size();
1199   (void)sliceSurroundingLoopsSize;
1200   assert(dstLoopDepth + numSrcLoopIVs >= sliceSurroundingLoopsSize);
1201   unsigned sliceLoopLimit = dstLoopDepth + numSrcLoopIVs;
1202   (void)sliceLoopLimit;
1203   assert(sliceLoopLimit >= sliceSurroundingLoopsSize);
1204 
1205   // Update loop bounds for loops in 'sliceLoopNest'.
1206   for (unsigned i = 0; i < numSrcLoopIVs; ++i) {
1207     auto forOp = sliceSurroundingLoops[dstLoopDepth + i];
1208     if (AffineMap lbMap = sliceState->lbs[i])
1209       forOp.setLowerBound(sliceState->lbOperands[i], lbMap);
1210     if (AffineMap ubMap = sliceState->ubs[i])
1211       forOp.setUpperBound(sliceState->ubOperands[i], ubMap);
1212   }
1213   return sliceLoopNest;
1214 }
1215 
1216 // Constructs  MemRefAccess populating it with the memref, its indices and
1217 // opinst from 'loadOrStoreOpInst'.
1218 MemRefAccess::MemRefAccess(Operation *loadOrStoreOpInst) {
1219   if (auto loadOp = dyn_cast<AffineReadOpInterface>(loadOrStoreOpInst)) {
1220     memref = loadOp.getMemRef();
1221     opInst = loadOrStoreOpInst;
1222     auto loadMemrefType = loadOp.getMemRefType();
1223     indices.reserve(loadMemrefType.getRank());
1224     for (auto index : loadOp.getMapOperands()) {
1225       indices.push_back(index);
1226     }
1227   } else {
1228     assert(isa<AffineWriteOpInterface>(loadOrStoreOpInst) &&
1229            "Affine read/write op expected");
1230     auto storeOp = cast<AffineWriteOpInterface>(loadOrStoreOpInst);
1231     opInst = loadOrStoreOpInst;
1232     memref = storeOp.getMemRef();
1233     auto storeMemrefType = storeOp.getMemRefType();
1234     indices.reserve(storeMemrefType.getRank());
1235     for (auto index : storeOp.getMapOperands()) {
1236       indices.push_back(index);
1237     }
1238   }
1239 }
1240 
1241 unsigned MemRefAccess::getRank() const {
1242   return memref.getType().cast<MemRefType>().getRank();
1243 }
1244 
1245 bool MemRefAccess::isStore() const {
1246   return isa<AffineWriteOpInterface>(opInst);
1247 }
1248 
1249 /// Returns the nesting depth of this statement, i.e., the number of loops
1250 /// surrounding this statement.
1251 unsigned mlir::getNestingDepth(Operation *op) {
1252   Operation *currOp = op;
1253   unsigned depth = 0;
1254   while ((currOp = currOp->getParentOp())) {
1255     if (isa<AffineForOp>(currOp))
1256       depth++;
1257   }
1258   return depth;
1259 }
1260 
1261 /// Equal if both affine accesses are provably equivalent (at compile
1262 /// time) when considering the memref, the affine maps and their respective
1263 /// operands. The equality of access functions + operands is checked by
1264 /// subtracting fully composed value maps, and then simplifying the difference
1265 /// using the expression flattener.
1266 /// TODO: this does not account for aliasing of memrefs.
1267 bool MemRefAccess::operator==(const MemRefAccess &rhs) const {
1268   if (memref != rhs.memref)
1269     return false;
1270 
1271   AffineValueMap diff, thisMap, rhsMap;
1272   getAccessMap(&thisMap);
1273   rhs.getAccessMap(&rhsMap);
1274   AffineValueMap::difference(thisMap, rhsMap, &diff);
1275   return llvm::all_of(diff.getAffineMap().getResults(),
1276                       [](AffineExpr e) { return e == 0; });
1277 }
1278 
1279 /// Returns the number of surrounding loops common to 'loopsA' and 'loopsB',
1280 /// where each lists loops from outer-most to inner-most in loop nest.
1281 unsigned mlir::getNumCommonSurroundingLoops(Operation &a, Operation &b) {
1282   SmallVector<AffineForOp, 4> loopsA, loopsB;
1283   getLoopIVs(a, &loopsA);
1284   getLoopIVs(b, &loopsB);
1285 
1286   unsigned minNumLoops = std::min(loopsA.size(), loopsB.size());
1287   unsigned numCommonLoops = 0;
1288   for (unsigned i = 0; i < minNumLoops; ++i) {
1289     if (loopsA[i].getOperation() != loopsB[i].getOperation())
1290       break;
1291     ++numCommonLoops;
1292   }
1293   return numCommonLoops;
1294 }
1295 
1296 static Optional<int64_t> getMemoryFootprintBytes(Block &block,
1297                                                  Block::iterator start,
1298                                                  Block::iterator end,
1299                                                  int memorySpace) {
1300   SmallDenseMap<Value, std::unique_ptr<MemRefRegion>, 4> regions;
1301 
1302   // Walk this 'affine.for' operation to gather all memory regions.
1303   auto result = block.walk(start, end, [&](Operation *opInst) -> WalkResult {
1304     if (!isa<AffineReadOpInterface, AffineWriteOpInterface>(opInst)) {
1305       // Neither load nor a store op.
1306       return WalkResult::advance();
1307     }
1308 
1309     // Compute the memref region symbolic in any IVs enclosing this block.
1310     auto region = std::make_unique<MemRefRegion>(opInst->getLoc());
1311     if (failed(
1312             region->compute(opInst,
1313                             /*loopDepth=*/getNestingDepth(&*block.begin())))) {
1314       return opInst->emitError("error obtaining memory region\n");
1315     }
1316 
1317     auto it = regions.find(region->memref);
1318     if (it == regions.end()) {
1319       regions[region->memref] = std::move(region);
1320     } else if (failed(it->second->unionBoundingBox(*region))) {
1321       return opInst->emitWarning(
1322           "getMemoryFootprintBytes: unable to perform a union on a memory "
1323           "region");
1324     }
1325     return WalkResult::advance();
1326   });
1327   if (result.wasInterrupted())
1328     return None;
1329 
1330   int64_t totalSizeInBytes = 0;
1331   for (const auto &region : regions) {
1332     Optional<int64_t> size = region.second->getRegionSize();
1333     if (!size.hasValue())
1334       return None;
1335     totalSizeInBytes += size.getValue();
1336   }
1337   return totalSizeInBytes;
1338 }
1339 
1340 Optional<int64_t> mlir::getMemoryFootprintBytes(AffineForOp forOp,
1341                                                 int memorySpace) {
1342   auto *forInst = forOp.getOperation();
1343   return ::getMemoryFootprintBytes(
1344       *forInst->getBlock(), Block::iterator(forInst),
1345       std::next(Block::iterator(forInst)), memorySpace);
1346 }
1347 
1348 /// Returns whether a loop is parallel and contains a reduction loop.
1349 bool mlir::isLoopParallelAndContainsReduction(AffineForOp forOp) {
1350   SmallVector<LoopReduction> reductions;
1351   if (!isLoopParallel(forOp, &reductions))
1352     return false;
1353   return !reductions.empty();
1354 }
1355 
1356 /// Returns in 'sequentialLoops' all sequential loops in loop nest rooted
1357 /// at 'forOp'.
1358 void mlir::getSequentialLoops(AffineForOp forOp,
1359                               llvm::SmallDenseSet<Value, 8> *sequentialLoops) {
1360   forOp->walk([&](Operation *op) {
1361     if (auto innerFor = dyn_cast<AffineForOp>(op))
1362       if (!isLoopParallel(innerFor))
1363         sequentialLoops->insert(innerFor.getInductionVar());
1364   });
1365 }
1366 
1367 IntegerSet mlir::simplifyIntegerSet(IntegerSet set) {
1368   FlatAffineConstraints fac(set);
1369   if (fac.isEmpty())
1370     return IntegerSet::getEmptySet(set.getNumDims(), set.getNumSymbols(),
1371                                    set.getContext());
1372   fac.removeTrivialRedundancy();
1373 
1374   auto simplifiedSet = fac.getAsIntegerSet(set.getContext());
1375   assert(simplifiedSet && "guaranteed to succeed while roundtripping");
1376   return simplifiedSet;
1377 }
1378