Skip to content

Commit 5d51db7

Browse files
authored
[mlir][affine] Use alias analysis to redetermine intervening memory effects in affine-scalrep (#90859)
This fixes a TODO to use alias analysis to determine whether a read op intervenes between two write operations to the same memref. Signed-off-by: Asra <[email protected]>
1 parent ba66dfb commit 5d51db7

File tree

4 files changed

+54
-27
lines changed

4 files changed

+54
-27
lines changed

mlir/include/mlir/Dialect/Affine/Utils.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef MLIR_DIALECT_AFFINE_UTILS_H
1414
#define MLIR_DIALECT_AFFINE_UTILS_H
1515

16+
#include "mlir/Analysis/AliasAnalysis.h"
1617
#include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h"
1718
#include "mlir/Dialect/Affine/IR/AffineOps.h"
1819
#include "mlir/IR/OpDefinition.h"
@@ -106,7 +107,8 @@ struct VectorizationStrategy {
106107
/// loads and eliminate invariant affine loads; consequently, eliminate dead
107108
/// allocs.
108109
void affineScalarReplace(func::FuncOp f, DominanceInfo &domInfo,
109-
PostDominanceInfo &postDomInfo);
110+
PostDominanceInfo &postDomInfo,
111+
AliasAnalysis &analysis);
110112

111113
/// Vectorizes affine loops in 'loops' using the n-D vectorization factors in
112114
/// 'vectorSizes'. By default, each vectorization factor is applied
@@ -325,7 +327,8 @@ OpFoldResult linearizeIndex(ArrayRef<OpFoldResult> multiIndex,
325327
/// will check if there is no write to the memory between `start` and `memOp`
326328
/// that would change the read within `memOp`.
327329
template <typename EffectType, typename T>
328-
bool hasNoInterveningEffect(Operation *start, T memOp);
330+
bool hasNoInterveningEffect(Operation *start, T memOp,
331+
llvm::function_ref<bool(Value, Value)> mayAlias);
329332

330333
struct AffineValueExpr {
331334
explicit AffineValueExpr(AffineExpr e) : e(e) {}

mlir/lib/Dialect/Affine/Transforms/AffineScalarReplacement.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "mlir/Dialect/Affine/Passes.h"
1515

16+
#include "mlir/Analysis/AliasAnalysis.h"
1617
#include "mlir/Dialect/Affine/Utils.h"
1718
#include "mlir/Dialect/Func/IR/FuncOps.h"
1819
#include "mlir/IR/Dominance.h"
@@ -47,5 +48,6 @@ mlir::affine::createAffineScalarReplacementPass() {
4748

4849
void AffineScalarReplacement::runOnOperation() {
4950
affineScalarReplace(getOperation(), getAnalysis<DominanceInfo>(),
50-
getAnalysis<PostDominanceInfo>());
51+
getAnalysis<PostDominanceInfo>(),
52+
getAnalysis<AliasAnalysis>());
5153
}

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

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -678,12 +678,9 @@ static bool mayHaveEffect(Operation *srcMemOp, Operation *destMemOp,
678678
}
679679

680680
template <typename EffectType, typename T>
681-
bool mlir::affine::hasNoInterveningEffect(Operation *start, T memOp) {
682-
auto isLocallyAllocated = [](Value memref) {
683-
auto *defOp = memref.getDefiningOp();
684-
return defOp && hasSingleEffect<MemoryEffects::Allocate>(defOp, memref);
685-
};
686-
681+
bool mlir::affine::hasNoInterveningEffect(
682+
Operation *start, T memOp,
683+
llvm::function_ref<bool(Value, Value)> mayAlias) {
687684
// A boolean representing whether an intervening operation could have impacted
688685
// memOp.
689686
bool hasSideEffect = false;
@@ -704,11 +701,8 @@ bool mlir::affine::hasNoInterveningEffect(Operation *start, T memOp) {
704701
// If op causes EffectType on a potentially aliasing location for
705702
// memOp, mark as having the effect.
706703
if (isa<EffectType>(effect.getEffect())) {
707-
// TODO: This should be replaced with a check for no aliasing.
708-
// Aliasing information should be passed to this method.
709704
if (effect.getValue() && effect.getValue() != memref &&
710-
isLocallyAllocated(memref) &&
711-
isLocallyAllocated(effect.getValue()))
705+
!mayAlias(effect.getValue(), memref))
712706
continue;
713707
opMayHaveEffect = true;
714708
break;
@@ -832,10 +826,10 @@ bool mlir::affine::hasNoInterveningEffect(Operation *start, T memOp) {
832826
/// other operations will overwrite the memory loaded between the given load
833827
/// and store. If such a value exists, the replaced `loadOp` will be added to
834828
/// `loadOpsToErase` and its memref will be added to `memrefsToErase`.
835-
static void forwardStoreToLoad(AffineReadOpInterface loadOp,
836-
SmallVectorImpl<Operation *> &loadOpsToErase,
837-
SmallPtrSetImpl<Value> &memrefsToErase,
838-
DominanceInfo &domInfo) {
829+
static void forwardStoreToLoad(
830+
AffineReadOpInterface loadOp, SmallVectorImpl<Operation *> &loadOpsToErase,
831+
SmallPtrSetImpl<Value> &memrefsToErase, DominanceInfo &domInfo,
832+
llvm::function_ref<bool(Value, Value)> mayAlias) {
839833

840834
// The store op candidate for forwarding that satisfies all conditions
841835
// to replace the load, if any.
@@ -872,7 +866,8 @@ static void forwardStoreToLoad(AffineReadOpInterface loadOp,
872866

873867
// 4. Ensure there is no intermediate operation which could replace the
874868
// value in memory.
875-
if (!affine::hasNoInterveningEffect<MemoryEffects::Write>(storeOp, loadOp))
869+
if (!affine::hasNoInterveningEffect<MemoryEffects::Write>(storeOp, loadOp,
870+
mayAlias))
876871
continue;
877872

878873
// We now have a candidate for forwarding.
@@ -901,7 +896,8 @@ static void forwardStoreToLoad(AffineReadOpInterface loadOp,
901896
template bool
902897
mlir::affine::hasNoInterveningEffect<mlir::MemoryEffects::Read,
903898
affine::AffineReadOpInterface>(
904-
mlir::Operation *, affine::AffineReadOpInterface);
899+
mlir::Operation *, affine::AffineReadOpInterface,
900+
llvm::function_ref<bool(Value, Value)>);
905901

906902
// This attempts to find stores which have no impact on the final result.
907903
// A writing op writeA will be eliminated if there exists an op writeB if
@@ -910,7 +906,8 @@ mlir::affine::hasNoInterveningEffect<mlir::MemoryEffects::Read,
910906
// 3) There is no potential read between writeA and writeB.
911907
static void findUnusedStore(AffineWriteOpInterface writeA,
912908
SmallVectorImpl<Operation *> &opsToErase,
913-
PostDominanceInfo &postDominanceInfo) {
909+
PostDominanceInfo &postDominanceInfo,
910+
llvm::function_ref<bool(Value, Value)> mayAlias) {
914911

915912
for (Operation *user : writeA.getMemRef().getUsers()) {
916913
// Only consider writing operations.
@@ -939,7 +936,8 @@ static void findUnusedStore(AffineWriteOpInterface writeA,
939936

940937
// There cannot be an operation which reads from memory between
941938
// the two writes.
942-
if (!affine::hasNoInterveningEffect<MemoryEffects::Read>(writeA, writeB))
939+
if (!affine::hasNoInterveningEffect<MemoryEffects::Read>(writeA, writeB,
940+
mayAlias))
943941
continue;
944942

945943
opsToErase.push_back(writeA);
@@ -955,7 +953,8 @@ static void findUnusedStore(AffineWriteOpInterface writeA,
955953
// 3) There is no write between loadA and loadB.
956954
static void loadCSE(AffineReadOpInterface loadA,
957955
SmallVectorImpl<Operation *> &loadOpsToErase,
958-
DominanceInfo &domInfo) {
956+
DominanceInfo &domInfo,
957+
llvm::function_ref<bool(Value, Value)> mayAlias) {
959958
SmallVector<AffineReadOpInterface, 4> loadCandidates;
960959
for (auto *user : loadA.getMemRef().getUsers()) {
961960
auto loadB = dyn_cast<AffineReadOpInterface>(user);
@@ -976,7 +975,7 @@ static void loadCSE(AffineReadOpInterface loadA,
976975

977976
// 3. There should not be a write between loadA and loadB.
978977
if (!affine::hasNoInterveningEffect<MemoryEffects::Write>(
979-
loadB.getOperation(), loadA))
978+
loadB.getOperation(), loadA, mayAlias))
980979
continue;
981980

982981
// Check if two values have the same shape. This is needed for affine vector
@@ -1034,24 +1033,29 @@ static void loadCSE(AffineReadOpInterface loadA,
10341033
// than dealloc) remain.
10351034
//
10361035
void mlir::affine::affineScalarReplace(func::FuncOp f, DominanceInfo &domInfo,
1037-
PostDominanceInfo &postDomInfo) {
1036+
PostDominanceInfo &postDomInfo,
1037+
AliasAnalysis &aliasAnalysis) {
10381038
// Load op's whose results were replaced by those forwarded from stores.
10391039
SmallVector<Operation *, 8> opsToErase;
10401040

10411041
// A list of memref's that are potentially dead / could be eliminated.
10421042
SmallPtrSet<Value, 4> memrefsToErase;
10431043

1044+
auto mayAlias = [&](Value val1, Value val2) -> bool {
1045+
return !aliasAnalysis.alias(val1, val2).isNo();
1046+
};
1047+
10441048
// Walk all load's and perform store to load forwarding.
10451049
f.walk([&](AffineReadOpInterface loadOp) {
1046-
forwardStoreToLoad(loadOp, opsToErase, memrefsToErase, domInfo);
1050+
forwardStoreToLoad(loadOp, opsToErase, memrefsToErase, domInfo, mayAlias);
10471051
});
10481052
for (auto *op : opsToErase)
10491053
op->erase();
10501054
opsToErase.clear();
10511055

10521056
// Walk all store's and perform unused store elimination
10531057
f.walk([&](AffineWriteOpInterface storeOp) {
1054-
findUnusedStore(storeOp, opsToErase, postDomInfo);
1058+
findUnusedStore(storeOp, opsToErase, postDomInfo, mayAlias);
10551059
});
10561060
for (auto *op : opsToErase)
10571061
op->erase();
@@ -1084,7 +1088,7 @@ void mlir::affine::affineScalarReplace(func::FuncOp f, DominanceInfo &domInfo,
10841088
// stores. Otherwise, some stores are wrongly seen as having an intervening
10851089
// effect.
10861090
f.walk([&](AffineReadOpInterface loadOp) {
1087-
loadCSE(loadOp, opsToErase, domInfo);
1091+
loadCSE(loadOp, opsToErase, domInfo, mayAlias);
10881092
});
10891093
for (auto *op : opsToErase)
10901094
op->erase();

mlir/test/Dialect/Affine/scalrep.mlir

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,24 @@ func.func @redundant_store_elim(%out : memref<512xf32>) {
682682
// CHECK-NEXT: affine.store
683683
// CHECK-NEXT: }
684684

685+
// CHECK-LABEL: func @redundant_store_elim_nonintervening
686+
687+
func.func @redundant_store_elim_nonintervening(%in : memref<512xf32>) {
688+
%cf1 = arith.constant 1.0 : f32
689+
%out = memref.alloc() : memref<512xf32>
690+
affine.for %i = 0 to 16 {
691+
affine.store %cf1, %out[32*%i] : memref<512xf32>
692+
%0 = affine.load %in[32*%i] : memref<512xf32>
693+
affine.store %0, %out[32*%i] : memref<512xf32>
694+
}
695+
return
696+
}
697+
698+
// CHECK: affine.for
699+
// CHECK-NEXT: affine.load
700+
// CHECK-NEXT: affine.store
701+
// CHECK-NEXT: }
702+
685703
// CHECK-LABEL: func @redundant_store_elim_fail
686704

687705
func.func @redundant_store_elim_fail(%out : memref<512xf32>) {

0 commit comments

Comments
 (0)