1 //===- AffineAnalysis.cpp - Affine structures analysis routines -----------===//
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 affine structures
10 // (expressions, maps, sets), and other utilities relying on such analysis.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h"
15 #include "mlir/Analysis/SliceAnalysis.h"
16 #include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h"
17 #include "mlir/Dialect/Affine/Analysis/Utils.h"
18 #include "mlir/Dialect/Affine/IR/AffineOps.h"
19 #include "mlir/Dialect/Affine/IR/AffineValueMap.h"
20 #include "mlir/Dialect/Func/IR/FuncOps.h"
21 #include "mlir/IR/AffineExprVisitor.h"
22 #include "mlir/IR/BuiltinOps.h"
23 #include "mlir/IR/IntegerSet.h"
24 #include "mlir/Interfaces/ViewLikeInterface.h"
25 #include "llvm/ADT/TypeSwitch.h"
26 #include "llvm/Support/Debug.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 #define DEBUG_TYPE "affine-analysis"
30 
31 using namespace mlir;
32 
33 /// Get the value that is being reduced by `pos`-th reduction in the loop if
34 /// such a reduction can be performed by affine parallel loops. This assumes
35 /// floating-point operations are commutative. On success, `kind` will be the
36 /// reduction kind suitable for use in affine parallel loop builder. If the
37 /// reduction is not supported, returns null.
38 static Value getSupportedReduction(AffineForOp forOp, unsigned pos,
39                                    arith::AtomicRMWKind &kind) {
40   SmallVector<Operation *> combinerOps;
41   Value reducedVal =
42       matchReduction(forOp.getRegionIterArgs(), pos, combinerOps);
43   if (!reducedVal)
44     return nullptr;
45 
46   // Expected only one combiner operation.
47   if (combinerOps.size() > 1)
48     return nullptr;
49 
50   Operation *combinerOp = combinerOps.back();
51   Optional<arith::AtomicRMWKind> maybeKind =
52       TypeSwitch<Operation *, Optional<arith::AtomicRMWKind>>(combinerOp)
53           .Case([](arith::AddFOp) { return arith::AtomicRMWKind::addf; })
54           .Case([](arith::MulFOp) { return arith::AtomicRMWKind::mulf; })
55           .Case([](arith::AddIOp) { return arith::AtomicRMWKind::addi; })
56           .Case([](arith::AndIOp) { return arith::AtomicRMWKind::andi; })
57           .Case([](arith::OrIOp) { return arith::AtomicRMWKind::ori; })
58           .Case([](arith::MulIOp) { return arith::AtomicRMWKind::muli; })
59           .Case([](arith::MinFOp) { return arith::AtomicRMWKind::minf; })
60           .Case([](arith::MaxFOp) { return arith::AtomicRMWKind::maxf; })
61           .Case([](arith::MinSIOp) { return arith::AtomicRMWKind::mins; })
62           .Case([](arith::MaxSIOp) { return arith::AtomicRMWKind::maxs; })
63           .Case([](arith::MinUIOp) { return arith::AtomicRMWKind::minu; })
64           .Case([](arith::MaxUIOp) { return arith::AtomicRMWKind::maxu; })
65           .Default([](Operation *) -> Optional<arith::AtomicRMWKind> {
66             // TODO: AtomicRMW supports other kinds of reductions this is
67             // currently not detecting, add those when the need arises.
68             return llvm::None;
69           });
70   if (!maybeKind)
71     return nullptr;
72 
73   kind = *maybeKind;
74   return reducedVal;
75 }
76 
77 /// Populate `supportedReductions` with descriptors of the supported reductions.
78 void mlir::getSupportedReductions(
79     AffineForOp forOp, SmallVectorImpl<LoopReduction> &supportedReductions) {
80   unsigned numIterArgs = forOp.getNumIterOperands();
81   if (numIterArgs == 0)
82     return;
83   supportedReductions.reserve(numIterArgs);
84   for (unsigned i = 0; i < numIterArgs; ++i) {
85     arith::AtomicRMWKind kind;
86     if (Value value = getSupportedReduction(forOp, i, kind))
87       supportedReductions.emplace_back(LoopReduction{kind, i, value});
88   }
89 }
90 
91 /// Returns true if `forOp' is a parallel loop. If `parallelReductions` is
92 /// provided, populates it with descriptors of the parallelizable reductions and
93 /// treats them as not preventing parallelization.
94 bool mlir::isLoopParallel(AffineForOp forOp,
95                           SmallVectorImpl<LoopReduction> *parallelReductions) {
96   unsigned numIterArgs = forOp.getNumIterOperands();
97 
98   // Loop is not parallel if it has SSA loop-carried dependences and reduction
99   // detection is not requested.
100   if (numIterArgs > 0 && !parallelReductions)
101     return false;
102 
103   // Find supported reductions of requested.
104   if (parallelReductions) {
105     getSupportedReductions(forOp, *parallelReductions);
106     // Return later to allow for identifying all parallel reductions even if the
107     // loop is not parallel.
108     if (parallelReductions->size() != numIterArgs)
109       return false;
110   }
111 
112   // Check memory dependences.
113   return isLoopMemoryParallel(forOp);
114 }
115 
116 /// Returns true if `op` is an alloc-like op, i.e., one allocating memrefs.
117 static bool isAllocLikeOp(Operation *op) {
118   auto memEffects = dyn_cast<MemoryEffectOpInterface>(op);
119   return memEffects && memEffects.hasEffect<MemoryEffects::Allocate>();
120 }
121 
122 /// Returns true if `v` is allocated locally to `enclosingOp` -- i.e., it is
123 /// allocated by an operation nested within `enclosingOp`.
124 static bool isLocallyDefined(Value v, Operation *enclosingOp) {
125   Operation *defOp = v.getDefiningOp();
126   if (!defOp)
127     return false;
128 
129   if (isAllocLikeOp(defOp) && enclosingOp->isProperAncestor(defOp))
130     return true;
131 
132   // Aliasing ops.
133   auto viewOp = dyn_cast<ViewLikeOpInterface>(defOp);
134   return viewOp && isLocallyDefined(viewOp.getViewSource(), enclosingOp);
135 }
136 
137 bool mlir::isLoopMemoryParallel(AffineForOp forOp) {
138   // Any memref-typed iteration arguments are treated as serializing.
139   if (llvm::any_of(forOp.getResultTypes(),
140                    [](Type type) { return type.isa<BaseMemRefType>(); }))
141     return false;
142 
143   // Collect all load and store ops in loop nest rooted at 'forOp'.
144   SmallVector<Operation *, 8> loadAndStoreOps;
145   auto walkResult = forOp.walk([&](Operation *op) -> WalkResult {
146     if (auto readOp = dyn_cast<AffineReadOpInterface>(op)) {
147       // Memrefs that are allocated inside `forOp` need not be considered.
148       if (!isLocallyDefined(readOp.getMemRef(), forOp))
149         loadAndStoreOps.push_back(op);
150     } else if (auto writeOp = dyn_cast<AffineWriteOpInterface>(op)) {
151       // Filter out stores the same way as above.
152       if (!isLocallyDefined(writeOp.getMemRef(), forOp))
153         loadAndStoreOps.push_back(op);
154     } else if (!isa<AffineForOp, AffineYieldOp, AffineIfOp>(op) &&
155                !isAllocLikeOp(op) &&
156                !MemoryEffectOpInterface::hasNoEffect(op)) {
157       // Alloc-like ops inside `forOp` are fine (they don't impact parallelism)
158       // as long as they don't escape the loop (which has been checked above).
159       return WalkResult::interrupt();
160     }
161 
162     return WalkResult::advance();
163   });
164 
165   // Stop early if the loop has unknown ops with side effects.
166   if (walkResult.wasInterrupted())
167     return false;
168 
169   // Dep check depth would be number of enclosing loops + 1.
170   unsigned depth = getNestingDepth(forOp) + 1;
171 
172   // Check dependences between all pairs of ops in 'loadAndStoreOps'.
173   for (auto *srcOp : loadAndStoreOps) {
174     MemRefAccess srcAccess(srcOp);
175     for (auto *dstOp : loadAndStoreOps) {
176       MemRefAccess dstAccess(dstOp);
177       FlatAffineValueConstraints dependenceConstraints;
178       DependenceResult result = checkMemrefAccessDependence(
179           srcAccess, dstAccess, depth, &dependenceConstraints,
180           /*dependenceComponents=*/nullptr);
181       if (result.value != DependenceResult::NoDependence)
182         return false;
183     }
184   }
185   return true;
186 }
187 
188 /// Returns the sequence of AffineApplyOp Operations operation in
189 /// 'affineApplyOps', which are reachable via a search starting from 'operands',
190 /// and ending at operands which are not defined by AffineApplyOps.
191 // TODO: Add a method to AffineApplyOp which forward substitutes the
192 // AffineApplyOp into any user AffineApplyOps.
193 void mlir::getReachableAffineApplyOps(
194     ArrayRef<Value> operands, SmallVectorImpl<Operation *> &affineApplyOps) {
195   struct State {
196     // The ssa value for this node in the DFS traversal.
197     Value value;
198     // The operand index of 'value' to explore next during DFS traversal.
199     unsigned operandIndex;
200   };
201   SmallVector<State, 4> worklist;
202   for (auto operand : operands) {
203     worklist.push_back({operand, 0});
204   }
205 
206   while (!worklist.empty()) {
207     State &state = worklist.back();
208     auto *opInst = state.value.getDefiningOp();
209     // Note: getDefiningOp will return nullptr if the operand is not an
210     // Operation (i.e. block argument), which is a terminator for the search.
211     if (!isa_and_nonnull<AffineApplyOp>(opInst)) {
212       worklist.pop_back();
213       continue;
214     }
215 
216     if (state.operandIndex == 0) {
217       // Pre-Visit: Add 'opInst' to reachable sequence.
218       affineApplyOps.push_back(opInst);
219     }
220     if (state.operandIndex < opInst->getNumOperands()) {
221       // Visit: Add next 'affineApplyOp' operand to worklist.
222       // Get next operand to visit at 'operandIndex'.
223       auto nextOperand = opInst->getOperand(state.operandIndex);
224       // Increment 'operandIndex' in 'state'.
225       ++state.operandIndex;
226       // Add 'nextOperand' to worklist.
227       worklist.push_back({nextOperand, 0});
228     } else {
229       // Post-visit: done visiting operands AffineApplyOp, pop off stack.
230       worklist.pop_back();
231     }
232   }
233 }
234 
235 // Builds a system of constraints with dimensional identifiers corresponding to
236 // the loop IVs of the forOps appearing in that order. Any symbols founds in
237 // the bound operands are added as symbols in the system. Returns failure for
238 // the yet unimplemented cases.
239 // TODO: Handle non-unit steps through local variables or stride information in
240 // FlatAffineValueConstraints. (For eg., by using iv - lb % step = 0 and/or by
241 // introducing a method in FlatAffineValueConstraints
242 // setExprStride(ArrayRef<int64_t> expr, int64_t stride)
243 LogicalResult mlir::getIndexSet(MutableArrayRef<Operation *> ops,
244                                 FlatAffineValueConstraints *domain) {
245   SmallVector<Value, 4> indices;
246   SmallVector<AffineForOp, 8> forOps;
247 
248   for (Operation *op : ops) {
249     assert((isa<AffineForOp, AffineIfOp>(op)) &&
250            "ops should have either AffineForOp or AffineIfOp");
251     if (AffineForOp forOp = dyn_cast<AffineForOp>(op))
252       forOps.push_back(forOp);
253   }
254   extractForInductionVars(forOps, &indices);
255   // Reset while associated Values in 'indices' to the domain.
256   domain->reset(forOps.size(), /*numSymbols=*/0, /*numLocals=*/0, indices);
257   for (Operation *op : ops) {
258     // Add constraints from forOp's bounds.
259     if (AffineForOp forOp = dyn_cast<AffineForOp>(op)) {
260       if (failed(domain->addAffineForOpDomain(forOp)))
261         return failure();
262     } else if (AffineIfOp ifOp = dyn_cast<AffineIfOp>(op)) {
263       domain->addAffineIfOpDomain(ifOp);
264     }
265   }
266   return success();
267 }
268 
269 /// Computes the iteration domain for 'op' and populates 'indexSet', which
270 /// encapsulates the constraints involving loops surrounding 'op' and
271 /// potentially involving any Function symbols. The dimensional identifiers in
272 /// 'indexSet' correspond to the loops surrounding 'op' from outermost to
273 /// innermost.
274 static LogicalResult getOpIndexSet(Operation *op,
275                                    FlatAffineValueConstraints *indexSet) {
276   SmallVector<Operation *, 4> ops;
277   getEnclosingAffineForAndIfOps(*op, &ops);
278   return getIndexSet(ops, indexSet);
279 }
280 
281 // Returns the number of outer loop common to 'src/dstDomain'.
282 // Loops common to 'src/dst' domains are added to 'commonLoops' if non-null.
283 static unsigned
284 getNumCommonLoops(const FlatAffineValueConstraints &srcDomain,
285                   const FlatAffineValueConstraints &dstDomain,
286                   SmallVectorImpl<AffineForOp> *commonLoops = nullptr) {
287   // Find the number of common loops shared by src and dst accesses.
288   unsigned minNumLoops =
289       std::min(srcDomain.getNumDimIds(), dstDomain.getNumDimIds());
290   unsigned numCommonLoops = 0;
291   for (unsigned i = 0; i < minNumLoops; ++i) {
292     if (!isForInductionVar(srcDomain.getValue(i)) ||
293         !isForInductionVar(dstDomain.getValue(i)) ||
294         srcDomain.getValue(i) != dstDomain.getValue(i))
295       break;
296     if (commonLoops != nullptr)
297       commonLoops->push_back(getForInductionVarOwner(srcDomain.getValue(i)));
298     ++numCommonLoops;
299   }
300   if (commonLoops != nullptr)
301     assert(commonLoops->size() == numCommonLoops);
302   return numCommonLoops;
303 }
304 
305 /// Returns Block common to 'srcAccess.opInst' and 'dstAccess.opInst'.
306 static Block *getCommonBlock(const MemRefAccess &srcAccess,
307                              const MemRefAccess &dstAccess,
308                              const FlatAffineValueConstraints &srcDomain,
309                              unsigned numCommonLoops) {
310   // Get the chain of ancestor blocks to the given `MemRefAccess` instance. The
311   // search terminates when either an op with the `AffineScope` trait or
312   // `endBlock` is reached.
313   auto getChainOfAncestorBlocks = [&](const MemRefAccess &access,
314                                       SmallVector<Block *, 4> &ancestorBlocks,
315                                       Block *endBlock = nullptr) {
316     Block *currBlock = access.opInst->getBlock();
317     // Loop terminates when the currBlock is nullptr or equals to the endBlock,
318     // or its parent operation holds an affine scope.
319     while (currBlock && currBlock != endBlock &&
320            !currBlock->getParentOp()->hasTrait<OpTrait::AffineScope>()) {
321       ancestorBlocks.push_back(currBlock);
322       currBlock = currBlock->getParentOp()->getBlock();
323     }
324   };
325 
326   if (numCommonLoops == 0) {
327     Block *block = srcAccess.opInst->getBlock();
328     while (!llvm::isa<FuncOp>(block->getParentOp())) {
329       block = block->getParentOp()->getBlock();
330     }
331     return block;
332   }
333   Value commonForIV = srcDomain.getValue(numCommonLoops - 1);
334   AffineForOp forOp = getForInductionVarOwner(commonForIV);
335   assert(forOp && "commonForValue was not an induction variable");
336 
337   // Find the closest common block including those in AffineIf.
338   SmallVector<Block *, 4> srcAncestorBlocks, dstAncestorBlocks;
339   getChainOfAncestorBlocks(srcAccess, srcAncestorBlocks, forOp.getBody());
340   getChainOfAncestorBlocks(dstAccess, dstAncestorBlocks, forOp.getBody());
341 
342   Block *commonBlock = forOp.getBody();
343   for (int i = srcAncestorBlocks.size() - 1, j = dstAncestorBlocks.size() - 1;
344        i >= 0 && j >= 0 && srcAncestorBlocks[i] == dstAncestorBlocks[j];
345        i--, j--)
346     commonBlock = srcAncestorBlocks[i];
347 
348   return commonBlock;
349 }
350 
351 // Returns true if the ancestor operation of 'srcAccess' appears before the
352 // ancestor operation of 'dstAccess' in the common ancestral block. Returns
353 // false otherwise.
354 // Note that because 'srcAccess' or 'dstAccess' may be nested in conditionals,
355 // the function is named 'srcAppearsBeforeDstInCommonBlock'. Note that
356 // 'numCommonLoops' is the number of contiguous surrounding outer loops.
357 static bool srcAppearsBeforeDstInAncestralBlock(
358     const MemRefAccess &srcAccess, const MemRefAccess &dstAccess,
359     const FlatAffineValueConstraints &srcDomain, unsigned numCommonLoops) {
360   // Get Block common to 'srcAccess.opInst' and 'dstAccess.opInst'.
361   auto *commonBlock =
362       getCommonBlock(srcAccess, dstAccess, srcDomain, numCommonLoops);
363   // Check the dominance relationship between the respective ancestors of the
364   // src and dst in the Block of the innermost among the common loops.
365   auto *srcInst = commonBlock->findAncestorOpInBlock(*srcAccess.opInst);
366   assert(srcInst != nullptr);
367   auto *dstInst = commonBlock->findAncestorOpInBlock(*dstAccess.opInst);
368   assert(dstInst != nullptr);
369 
370   // Determine whether dstInst comes after srcInst.
371   return srcInst->isBeforeInBlock(dstInst);
372 }
373 
374 // Adds ordering constraints to 'dependenceDomain' based on number of loops
375 // common to 'src/dstDomain' and requested 'loopDepth'.
376 // Note that 'loopDepth' cannot exceed the number of common loops plus one.
377 // EX: Given a loop nest of depth 2 with IVs 'i' and 'j':
378 // *) If 'loopDepth == 1' then one constraint is added: i' >= i + 1
379 // *) If 'loopDepth == 2' then two constraints are added: i == i' and j' > j + 1
380 // *) If 'loopDepth == 3' then two constraints are added: i == i' and j == j'
381 static void
382 addOrderingConstraints(const FlatAffineValueConstraints &srcDomain,
383                        const FlatAffineValueConstraints &dstDomain,
384                        unsigned loopDepth,
385                        FlatAffineValueConstraints *dependenceDomain) {
386   unsigned numCols = dependenceDomain->getNumCols();
387   SmallVector<int64_t, 4> eq(numCols);
388   unsigned numSrcDims = srcDomain.getNumDimIds();
389   unsigned numCommonLoops = getNumCommonLoops(srcDomain, dstDomain);
390   unsigned numCommonLoopConstraints = std::min(numCommonLoops, loopDepth);
391   for (unsigned i = 0; i < numCommonLoopConstraints; ++i) {
392     std::fill(eq.begin(), eq.end(), 0);
393     eq[i] = -1;
394     eq[i + numSrcDims] = 1;
395     if (i == loopDepth - 1) {
396       eq[numCols - 1] = -1;
397       dependenceDomain->addInequality(eq);
398     } else {
399       dependenceDomain->addEquality(eq);
400     }
401   }
402 }
403 
404 // Computes distance and direction vectors in 'dependences', by adding
405 // variables to 'dependenceDomain' which represent the difference of the IVs,
406 // eliminating all other variables, and reading off distance vectors from
407 // equality constraints (if possible), and direction vectors from inequalities.
408 static void computeDirectionVector(
409     const FlatAffineValueConstraints &srcDomain,
410     const FlatAffineValueConstraints &dstDomain, unsigned loopDepth,
411     FlatAffineValueConstraints *dependenceDomain,
412     SmallVector<DependenceComponent, 2> *dependenceComponents) {
413   // Find the number of common loops shared by src and dst accesses.
414   SmallVector<AffineForOp, 4> commonLoops;
415   unsigned numCommonLoops =
416       getNumCommonLoops(srcDomain, dstDomain, &commonLoops);
417   if (numCommonLoops == 0)
418     return;
419   // Compute direction vectors for requested loop depth.
420   unsigned numIdsToEliminate = dependenceDomain->getNumIds();
421   // Add new variables to 'dependenceDomain' to represent the direction
422   // constraints for each shared loop.
423   dependenceDomain->insertDimId(/*pos=*/0, /*num=*/numCommonLoops);
424 
425   // Add equality constraints for each common loop, setting newly introduced
426   // variable at column 'j' to the 'dst' IV minus the 'src IV.
427   SmallVector<int64_t, 4> eq;
428   eq.resize(dependenceDomain->getNumCols());
429   unsigned numSrcDims = srcDomain.getNumDimIds();
430   // Constraint variables format:
431   // [num-common-loops][num-src-dim-ids][num-dst-dim-ids][num-symbols][constant]
432   for (unsigned j = 0; j < numCommonLoops; ++j) {
433     std::fill(eq.begin(), eq.end(), 0);
434     eq[j] = 1;
435     eq[j + numCommonLoops] = 1;
436     eq[j + numCommonLoops + numSrcDims] = -1;
437     dependenceDomain->addEquality(eq);
438   }
439 
440   // Eliminate all variables other than the direction variables just added.
441   dependenceDomain->projectOut(numCommonLoops, numIdsToEliminate);
442 
443   // Scan each common loop variable column and set direction vectors based
444   // on eliminated constraint system.
445   dependenceComponents->resize(numCommonLoops);
446   for (unsigned j = 0; j < numCommonLoops; ++j) {
447     (*dependenceComponents)[j].op = commonLoops[j].getOperation();
448     auto lbConst =
449         dependenceDomain->getConstantBound(FlatAffineConstraints::LB, j);
450     (*dependenceComponents)[j].lb =
451         lbConst.getValueOr(std::numeric_limits<int64_t>::min());
452     auto ubConst =
453         dependenceDomain->getConstantBound(FlatAffineConstraints::UB, j);
454     (*dependenceComponents)[j].ub =
455         ubConst.getValueOr(std::numeric_limits<int64_t>::max());
456   }
457 }
458 
459 LogicalResult MemRefAccess::getAccessRelation(FlatAffineRelation &rel) const {
460   // Create set corresponding to domain of access.
461   FlatAffineValueConstraints domain;
462   if (failed(getOpIndexSet(opInst, &domain)))
463     return failure();
464 
465   // Get access relation from access map.
466   AffineValueMap accessValueMap;
467   getAccessMap(&accessValueMap);
468   if (failed(getRelationFromMap(accessValueMap, rel)))
469     return failure();
470 
471   FlatAffineRelation domainRel(rel.getNumDomainDims(), /*numRangeDims=*/0,
472                                domain);
473 
474   // Merge and align domain ids of `ret` and ids of `domain`. Since the domain
475   // of the access map is a subset of the domain of access, the domain ids of
476   // `ret` are guranteed to be a subset of ids of `domain`.
477   for (unsigned i = 0, e = domain.getNumDimIds(); i < e; ++i) {
478     unsigned loc;
479     if (rel.findId(domain.getValue(i), &loc)) {
480       rel.swapId(i, loc);
481     } else {
482       rel.insertDomainId(i);
483       rel.setValue(i, domain.getValue(i));
484     }
485   }
486 
487   // Append domain constraints to `rel`.
488   domainRel.appendRangeId(rel.getNumRangeDims());
489   domainRel.mergeSymbolIds(rel);
490   domainRel.mergeLocalIds(rel);
491   rel.append(domainRel);
492 
493   return success();
494 }
495 
496 // Populates 'accessMap' with composition of AffineApplyOps reachable from
497 // indices of MemRefAccess.
498 void MemRefAccess::getAccessMap(AffineValueMap *accessMap) const {
499   // Get affine map from AffineLoad/Store.
500   AffineMap map;
501   if (auto loadOp = dyn_cast<AffineReadOpInterface>(opInst))
502     map = loadOp.getAffineMap();
503   else
504     map = cast<AffineWriteOpInterface>(opInst).getAffineMap();
505 
506   SmallVector<Value, 8> operands(indices.begin(), indices.end());
507   fullyComposeAffineMapAndOperands(&map, &operands);
508   map = simplifyAffineMap(map);
509   canonicalizeMapAndOperands(&map, &operands);
510   accessMap->reset(map, operands);
511 }
512 
513 // Builds a flat affine constraint system to check if there exists a dependence
514 // between memref accesses 'srcAccess' and 'dstAccess'.
515 // Returns 'NoDependence' if the accesses can be definitively shown not to
516 // access the same element.
517 // Returns 'HasDependence' if the accesses do access the same element.
518 // Returns 'Failure' if an error or unsupported case was encountered.
519 // If a dependence exists, returns in 'dependenceComponents' a direction
520 // vector for the dependence, with a component for each loop IV in loops
521 // common to both accesses (see Dependence in AffineAnalysis.h for details).
522 //
523 // The memref access dependence check is comprised of the following steps:
524 // *) Build access relation for each access. An access relation maps elements
525 //    of an iteration domain to the element(s) of an array domain accessed by
526 //    that iteration of the associated statement through some array reference.
527 // *) Compute the dependence relation by composing access relation of
528 //    `srcAccess` with the inverse of access relation of `dstAccess`.
529 //    Doing this builds a relation between iteration domain of `srcAccess`
530 //    to the iteration domain of `dstAccess` which access the same memory
531 //    location.
532 // *) Add ordering constraints for `srcAccess` to be accessed before
533 //    `dstAccess`.
534 //
535 // This method builds a constraint system with the following column format:
536 //
537 //  [src-dim-identifiers, dst-dim-identifiers, symbols, constant]
538 //
539 // For example, given the following MLIR code with "source" and "destination"
540 // accesses to the same memref label, and symbols %M, %N, %K:
541 //
542 //   affine.for %i0 = 0 to 100 {
543 //     affine.for %i1 = 0 to 50 {
544 //       %a0 = affine.apply
545 //         (d0, d1) -> (d0 * 2 - d1 * 4 + s1, d1 * 3 - s0) (%i0, %i1)[%M, %N]
546 //       // Source memref access.
547 //       store %v0, %m[%a0#0, %a0#1] : memref<4x4xf32>
548 //     }
549 //   }
550 //
551 //   affine.for %i2 = 0 to 100 {
552 //     affine.for %i3 = 0 to 50 {
553 //       %a1 = affine.apply
554 //         (d0, d1) -> (d0 * 7 + d1 * 9 - s1, d1 * 11 + s0) (%i2, %i3)[%K, %M]
555 //       // Destination memref access.
556 //       %v1 = load %m[%a1#0, %a1#1] : memref<4x4xf32>
557 //     }
558 //   }
559 //
560 // The access relation for `srcAccess` would be the following:
561 //
562 //   [src_dim0, src_dim1, mem_dim0, mem_dim1,  %N,   %M,  const]
563 //       2        -4       -1         0         1     0     0     = 0
564 //       0         3        0        -1         0    -1     0     = 0
565 //       1         0        0         0         0     0     0    >= 0
566 //      -1         0        0         0         0     0     100  >= 0
567 //       0         1        0         0         0     0     0    >= 0
568 //       0        -1        0         0         0     0     50   >= 0
569 //
570 //  The access relation for `dstAccess` would be the following:
571 //
572 //   [dst_dim0, dst_dim1, mem_dim0, mem_dim1,  %M,   %K,  const]
573 //       7         9       -1         0        -1     0     0     = 0
574 //       0         11       0        -1         0    -1     0     = 0
575 //       1         0        0         0         0     0     0    >= 0
576 //      -1         0        0         0         0     0     100  >= 0
577 //       0         1        0         0         0     0     0    >= 0
578 //       0        -1        0         0         0     0     50   >= 0
579 //
580 //  The equalities in the above relations correspond to the access maps while
581 //  the inequalities corresspond to the iteration domain constraints.
582 //
583 // The dependence relation formed:
584 //
585 //   [src_dim0, src_dim1, dst_dim0, dst_dim1,  %M,   %N,   %K,  const]
586 //      2         -4        -7        -9        1     1     0     0    = 0
587 //      0          3         0        -11      -1     0     1     0    = 0
588 //       1         0         0         0        0     0     0     0    >= 0
589 //      -1         0         0         0        0     0     0     100  >= 0
590 //       0         1         0         0        0     0     0     0    >= 0
591 //       0        -1         0         0        0     0     0     50   >= 0
592 //       0         0         1         0        0     0     0     0    >= 0
593 //       0         0        -1         0        0     0     0     100  >= 0
594 //       0         0         0         1        0     0     0     0    >= 0
595 //       0         0         0        -1        0     0     0     50   >= 0
596 //
597 //
598 // TODO: Support AffineExprs mod/floordiv/ceildiv.
599 DependenceResult mlir::checkMemrefAccessDependence(
600     const MemRefAccess &srcAccess, const MemRefAccess &dstAccess,
601     unsigned loopDepth, FlatAffineValueConstraints *dependenceConstraints,
602     SmallVector<DependenceComponent, 2> *dependenceComponents, bool allowRAR) {
603   LLVM_DEBUG(llvm::dbgs() << "Checking for dependence at depth: "
604                           << Twine(loopDepth) << " between:\n";);
605   LLVM_DEBUG(srcAccess.opInst->dump(););
606   LLVM_DEBUG(dstAccess.opInst->dump(););
607 
608   // Return 'NoDependence' if these accesses do not access the same memref.
609   if (srcAccess.memref != dstAccess.memref)
610     return DependenceResult::NoDependence;
611 
612   // Return 'NoDependence' if one of these accesses is not an
613   // AffineWriteOpInterface.
614   if (!allowRAR && !isa<AffineWriteOpInterface>(srcAccess.opInst) &&
615       !isa<AffineWriteOpInterface>(dstAccess.opInst))
616     return DependenceResult::NoDependence;
617 
618   // Create access relation from each MemRefAccess.
619   FlatAffineRelation srcRel, dstRel;
620   if (failed(srcAccess.getAccessRelation(srcRel)))
621     return DependenceResult::Failure;
622   if (failed(dstAccess.getAccessRelation(dstRel)))
623     return DependenceResult::Failure;
624 
625   FlatAffineValueConstraints srcDomain = srcRel.getDomainSet();
626   FlatAffineValueConstraints dstDomain = dstRel.getDomainSet();
627 
628   // Return 'NoDependence' if loopDepth > numCommonLoops and if the ancestor
629   // operation of 'srcAccess' does not properly dominate the ancestor
630   // operation of 'dstAccess' in the same common operation block.
631   // Note: this check is skipped if 'allowRAR' is true, because because RAR
632   // deps can exist irrespective of lexicographic ordering b/w src and dst.
633   unsigned numCommonLoops = getNumCommonLoops(srcDomain, dstDomain);
634   assert(loopDepth <= numCommonLoops + 1);
635   if (!allowRAR && loopDepth > numCommonLoops &&
636       !srcAppearsBeforeDstInAncestralBlock(srcAccess, dstAccess, srcDomain,
637                                            numCommonLoops)) {
638     return DependenceResult::NoDependence;
639   }
640 
641   // Compute the dependence relation by composing `srcRel` with the inverse of
642   // `dstRel`. Doing this builds a relation between iteration domain of
643   // `srcAccess` to the iteration domain of `dstAccess` which access the same
644   // memory locations.
645   dstRel.inverse();
646   dstRel.compose(srcRel);
647   *dependenceConstraints = dstRel;
648 
649   // Add 'src' happens before 'dst' ordering constraints.
650   addOrderingConstraints(srcDomain, dstDomain, loopDepth,
651                          dependenceConstraints);
652 
653   // Return 'NoDependence' if the solution space is empty: no dependence.
654   if (dependenceConstraints->isEmpty())
655     return DependenceResult::NoDependence;
656 
657   // Compute dependence direction vector and return true.
658   if (dependenceComponents != nullptr)
659     computeDirectionVector(srcDomain, dstDomain, loopDepth,
660                            dependenceConstraints, dependenceComponents);
661 
662   LLVM_DEBUG(llvm::dbgs() << "Dependence polyhedron:\n");
663   LLVM_DEBUG(dependenceConstraints->dump());
664   return DependenceResult::HasDependence;
665 }
666 
667 /// Gathers dependence components for dependences between all ops in loop nest
668 /// rooted at 'forOp' at loop depths in range [1, maxLoopDepth].
669 void mlir::getDependenceComponents(
670     AffineForOp forOp, unsigned maxLoopDepth,
671     std::vector<SmallVector<DependenceComponent, 2>> *depCompsVec) {
672   // Collect all load and store ops in loop nest rooted at 'forOp'.
673   SmallVector<Operation *, 8> loadAndStoreOps;
674   forOp->walk([&](Operation *op) {
675     if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op))
676       loadAndStoreOps.push_back(op);
677   });
678 
679   unsigned numOps = loadAndStoreOps.size();
680   for (unsigned d = 1; d <= maxLoopDepth; ++d) {
681     for (unsigned i = 0; i < numOps; ++i) {
682       auto *srcOp = loadAndStoreOps[i];
683       MemRefAccess srcAccess(srcOp);
684       for (unsigned j = 0; j < numOps; ++j) {
685         auto *dstOp = loadAndStoreOps[j];
686         MemRefAccess dstAccess(dstOp);
687 
688         FlatAffineValueConstraints dependenceConstraints;
689         SmallVector<DependenceComponent, 2> depComps;
690         // TODO: Explore whether it would be profitable to pre-compute and store
691         // deps instead of repeatedly checking.
692         DependenceResult result = checkMemrefAccessDependence(
693             srcAccess, dstAccess, d, &dependenceConstraints, &depComps);
694         if (hasDependence(result))
695           depCompsVec->push_back(depComps);
696       }
697     }
698   }
699 }
700