Skip to content

Commit d4d4fc3

Browse files
authored
[MLIR][Affine] Fix scalrep and underlying analysis utility (#130251)
Fixes: #53034 Properly check the scopes of affine operations across which we are performing affine analysis. Fixes transforms and analyses in the presence of non-affine regions.
1 parent de1d351 commit d4d4fc3

File tree

6 files changed

+61
-11
lines changed

6 files changed

+61
-11
lines changed

mlir/include/mlir/Dialect/Affine/IR/AffineOps.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ bool isTopLevelValue(Value value, Region *region);
4646
/// trait `AffineScope`; `nullptr` if there is no such region.
4747
Region *getAffineScope(Operation *op);
4848

49+
/// Returns the closest region enclosing `op` that is held by a non-affine
50+
/// operation; `nullptr` if there is no such region. This method is meant to
51+
/// be used by affine analysis methods (e.g. dependence analysis) which are
52+
/// only meaningful when performed among/between operations from the same
53+
/// analysis scope.
54+
Region *getAffineAnalysisScope(Operation *op);
55+
4956
/// AffineDmaStartOp starts a non-blocking DMA operation that transfers data
5057
/// from a source memref to a destination memref. The source and destination
5158
/// memref need not be of the same dimensionality, but need to have the same

mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,8 @@ DependenceResult mlir::affine::checkMemrefAccessDependence(
626626

627627
// We can't analyze further if the ops lie in different affine scopes or have
628628
// no common block in an affine scope.
629-
if (getAffineScope(srcAccess.opInst) != getAffineScope(dstAccess.opInst))
629+
if (getAffineAnalysisScope(srcAccess.opInst) !=
630+
getAffineAnalysisScope(dstAccess.opInst))
630631
return DependenceResult::Failure;
631632
if (!getCommonBlockInAffineScope(srcAccess.opInst, dstAccess.opInst))
632633
return DependenceResult::Failure;

mlir/lib/Dialect/Affine/Analysis/Utils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,8 +2307,8 @@ FailureOr<AffineValueMap> mlir::affine::simplifyConstrainedMinMaxOp(
23072307

23082308
Block *mlir::affine::findInnermostCommonBlockInScope(Operation *a,
23092309
Operation *b) {
2310-
Region *aScope = mlir::affine::getAffineScope(a);
2311-
Region *bScope = mlir::affine::getAffineScope(b);
2310+
Region *aScope = getAffineAnalysisScope(a);
2311+
Region *bScope = getAffineAnalysisScope(b);
23122312
if (aScope != bScope)
23132313
return nullptr;
23142314

mlir/lib/Dialect/Affine/IR/AffineOps.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,16 @@ Region *mlir::affine::getAffineScope(Operation *op) {
270270
return nullptr;
271271
}
272272

273+
Region *mlir::affine::getAffineAnalysisScope(Operation *op) {
274+
Operation *curOp = op;
275+
while (auto *parentOp = curOp->getParentOp()) {
276+
if (!isa<AffineForOp, AffineIfOp, AffineParallelOp>(parentOp))
277+
return curOp->getParentRegion();
278+
curOp = parentOp;
279+
}
280+
return nullptr;
281+
}
282+
273283
// A Value can be used as a dimension id iff it meets one of the following
274284
// conditions:
275285
// *) It is valid as a symbol.

mlir/lib/Dialect/Affine/Utils/Utils.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,8 @@ static bool mustReachAtInnermost(const MemRefAccess &srcAccess,
636636
const MemRefAccess &destAccess) {
637637
// Affine dependence analysis is possible only if both ops in the same
638638
// AffineScope.
639-
if (getAffineScope(srcAccess.opInst) != getAffineScope(destAccess.opInst))
639+
if (getAffineAnalysisScope(srcAccess.opInst) !=
640+
getAffineAnalysisScope(destAccess.opInst))
640641
return false;
641642

642643
unsigned nsLoops =
@@ -659,9 +660,9 @@ static bool mayHaveEffect(Operation *srcMemOp, Operation *destMemOp,
659660
// AffineScope. Also, we can only check if our affine scope is isolated from
660661
// above; otherwise, values can from outside of the affine scope that the
661662
// check below cannot analyze.
662-
Region *srcScope = getAffineScope(srcMemOp);
663+
Region *srcScope = getAffineAnalysisScope(srcMemOp);
663664
if (srcAccess.memref == destAccess.memref &&
664-
srcScope == getAffineScope(destMemOp)) {
665+
srcScope == getAffineAnalysisScope(destMemOp)) {
665666
unsigned nsLoops = getNumCommonSurroundingLoops(*srcMemOp, *destMemOp);
666667
FlatAffineValueConstraints dependenceConstraints;
667668
for (unsigned d = nsLoops + 1; d > minSurroundingLoops; d--) {

mlir/test/Dialect/Affine/scalrep.mlir

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -738,12 +738,11 @@ func.func @with_inner_ops(%arg0: memref<?xf64>, %arg1: memref<?xf64>, %arg2: i1)
738738
return
739739
}
740740

741-
// CHECK: %[[pi:.+]] = arith.constant 3.140000e+00 : f64
742-
// CHECK: %{{.*}} = scf.if %arg2 -> (f64) {
743-
// CHECK: scf.yield %{{.*}} : f64
741+
// Semantics of non-affine region ops would be unknown.
742+
744743
// CHECK: } else {
745-
// CHECK: scf.yield %[[pi]] : f64
746-
// CHECK: }
744+
// CHECK-NEXT: %[[Y:.*]] = affine.load
745+
// CHECK-NEXT: scf.yield %[[Y]] : f64
747746

748747
// Check if scalar replacement works correctly when affine memory ops are in the
749748
// body of an scf.for.
@@ -952,3 +951,35 @@ func.func @consecutive_store() {
952951
}
953952
return
954953
}
954+
955+
// CHECK-LABEL: func @scf_for_if
956+
func.func @scf_for_if(%arg0: memref<?xi32>, %arg1: i32) -> i32 attributes {llvm.linkage = #llvm.linkage<external>} {
957+
%c1 = arith.constant 1 : index
958+
%c0 = arith.constant 0 : index
959+
%c0_i32 = arith.constant 0 : i32
960+
%c5_i32 = arith.constant 5 : i32
961+
%c10_i32 = arith.constant 10 : i32
962+
%0 = memref.alloca() : memref<1xi32>
963+
%1 = llvm.mlir.undef : i32
964+
affine.store %1, %0[0] : memref<1xi32>
965+
affine.store %c0_i32, %0[0] : memref<1xi32>
966+
%2 = arith.index_cast %arg1 : i32 to index
967+
scf.for %arg2 = %c0 to %2 step %c1 {
968+
%4 = memref.load %arg0[%arg2] : memref<?xi32>
969+
%5 = arith.muli %4, %c5_i32 : i32
970+
%6 = arith.cmpi sgt, %5, %c10_i32 : i32
971+
// CHECK: scf.if
972+
scf.if %6 {
973+
// No forwarding should happen here since we have an scf.for around and we
974+
// can't analyze the flow of values.
975+
// CHECK: affine.load
976+
%7 = affine.load %0[0] : memref<1xi32>
977+
%8 = arith.addi %5, %7 : i32
978+
// CHECK: affine.store
979+
affine.store %8, %0[0] : memref<1xi32>
980+
}
981+
}
982+
// CHECK: affine.load
983+
%3 = affine.load %0[0] : memref<1xi32>
984+
return %3 : i32
985+
}

0 commit comments

Comments
 (0)