From b6ceadf3b663427f3cc233bbfdb5e35017cabd9e Mon Sep 17 00:00:00 2001 From: Uday Bondhugula Date: Wed, 14 Dec 2022 23:54:31 +0530 Subject: [PATCH] [MLIR] NFC. Split out code from hasNoInterveningEffect The `hasNoInterveningEffect` utility is too long with too deeply nested logic. Split out a part into a helper. NFC. Reviewed By: springerm Differential Revision: https://reviews.llvm.org/D139795 --- mlir/lib/Dialect/Affine/Utils/Utils.cpp | 89 +++++++++++++++++---------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/mlir/lib/Dialect/Affine/Utils/Utils.cpp b/mlir/lib/Dialect/Affine/Utils/Utils.cpp index 739588f..e93455a 100644 --- a/mlir/lib/Dialect/Affine/Utils/Utils.cpp +++ b/mlir/lib/Dialect/Affine/Utils/Utils.cpp @@ -643,6 +643,41 @@ LogicalResult mlir::normalizeAffineFor(AffineForOp op, bool promoteSingleIter) { return success(); } +/// Returns true if `srcMemOp` may have an effect on `destMemOp` within the +/// scope of the outermost `minSurroundingLoops` loops that surround them. +/// `srcMemOp` and `destMemOp` are expected to be affine read/write ops. +static bool mayHaveEffect(Operation *srcMemOp, Operation *destMemOp, + unsigned minSurroundingLoops) { + MemRefAccess srcAccess(srcMemOp); + MemRefAccess destAccess(destMemOp); + + // Affine dependence analysis here is applicable only if both ops operate on + // the same memref and if `srcMemOp` and `destMemOp` are in the same + // AffineScope. Also, we can only check if our affine scope is isolated from + // above; otherwise, values can from outside of the affine scope that the + // check below cannot analyze. + Region *srcScope = getAffineScope(srcMemOp); + if (srcAccess.memref == destAccess.memref && + srcScope == getAffineScope(destMemOp)) { + unsigned nsLoops = getNumCommonSurroundingLoops(*srcMemOp, *destMemOp); + FlatAffineValueConstraints dependenceConstraints; + for (unsigned d = nsLoops + 1; d > minSurroundingLoops; d--) { + DependenceResult result = checkMemrefAccessDependence( + srcAccess, destAccess, d, &dependenceConstraints, + /*dependenceComponents=*/nullptr); + // A dependence failure or the presence of a dependence implies a + // side effect. + if (!noDependence(result)) + return true; + } + // No side effect was seen. + return false; + } + // TODO: Check here if the memrefs alias: there is no side effect if + // `srcAccess.memref` and `destAccess.memref` don't alias. + return true; +} + template bool mlir::hasNoInterveningEffect(Operation *start, T memOp) { auto isLocallyAllocated = [](Value memref) { @@ -687,49 +722,19 @@ bool mlir::hasNoInterveningEffect(Operation *start, T memOp) { // If the side effect comes from an affine read or write, try to // prove the side effecting `op` cannot reach `memOp`. if (isa(op)) { - MemRefAccess srcAccess(op); - MemRefAccess destAccess(memOp); - // Affine dependence analysis here is applicable only if both ops - // operate on the same memref and if `op`, `memOp`, and `start` are in - // the same AffineScope. - if (srcAccess.memref == destAccess.memref && - getAffineScope(op) == getAffineScope(memOp) && - getAffineScope(op) == getAffineScope(start)) { - // Number of loops containing the start op and the ending operation. - unsigned minSurroundingLoops = - getNumCommonSurroundingLoops(*start, *memOp); - - // Number of loops containing the operation `op` which has the - // potential memory side effect and can occur on a path between - // `start` and `memOp`. - unsigned nsLoops = getNumCommonSurroundingLoops(*op, *memOp); - - // For ease, let's consider the case that `op` is a store and we're - // looking for other potential stores (e.g `op`) that overwrite memory - // after `start`, and before being read in `memOp`. In this case, we - // only need to consider other potential stores with depth > - // minSurrounding loops since `start` would overwrite any store with a - // smaller number of surrounding loops before. - unsigned d; - FlatAffineValueConstraints dependenceConstraints; - for (d = nsLoops + 1; d > minSurroundingLoops; d--) { - DependenceResult result = checkMemrefAccessDependence( - srcAccess, destAccess, d, &dependenceConstraints, - /*dependenceComponents=*/nullptr); - // A dependence failure or the presence of a dependence implies a - // side effect. - if (!noDependence(result)) { - hasSideEffect = true; - return; - } - } - - // No side effect was seen, simply return. - return; - } - // TODO: Check here if the memrefs alias: there is no side effect if - // `srcAccess.memref` and `destAccess.memref` don't alias. + // For ease, let's consider the case that `op` is a store and + // we're looking for other potential stores that overwrite memory after + // `start`, and before being read in `memOp`. In this case, we only + // need to consider other potential stores with depth > + // minSurroundingLoops since `start` would overwrite any store with a + // smaller number of surrounding loops before. + unsigned minSurroundingLoops = + getNumCommonSurroundingLoops(*start, *memOp); + if (mayHaveEffect(op, memOp, minSurroundingLoops)) + hasSideEffect = true; + return; } + // We have an op with a memory effect and we cannot prove if it // intervenes. hasSideEffect = true; -- 2.7.4