Skip to content

Commit c97a562

Browse files
committed
[DA] check memory offsets are multiples of elements size
Check that the offsets from memory base address are multiples of the array element size for a pair of memory accesses in a dependence test. The patch adds a testcase that cannot be disambiguated by the DA.
1 parent c3e9f44 commit c97a562

File tree

3 files changed

+123
-2
lines changed

3 files changed

+123
-2
lines changed

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3634,7 +3634,9 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
36343634
const SCEV *DstSCEV = SE->getSCEV(DstPtr);
36353635
LLVM_DEBUG(dbgs() << " SrcSCEV = " << *SrcSCEV << "\n");
36363636
LLVM_DEBUG(dbgs() << " DstSCEV = " << *DstSCEV << "\n");
3637-
if (SE->getPointerBase(SrcSCEV) != SE->getPointerBase(DstSCEV)) {
3637+
const SCEV *SrcBase = SE->getPointerBase(SrcSCEV);
3638+
const SCEV *DstBase = SE->getPointerBase(DstSCEV);
3639+
if (SrcBase != DstBase) {
36383640
// If two pointers have different bases, trying to analyze indexes won't
36393641
// work; we can't compare them to each other. This can happen, for example,
36403642
// if one is produced by an LCSSA PHI node.
@@ -3645,6 +3647,100 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
36453647
return std::make_unique<Dependence>(Src, Dst);
36463648
}
36473649

3650+
auto EltSize = SrcLoc.Size.toRaw();
3651+
assert(EltSize == DstLoc.Size.toRaw() && "Array element size differ");
3652+
3653+
// Check that memory access offsets in V are multiples of array EltSize.
3654+
std::function<bool(const SCEV *, const SCEV *&)> checkOffsets =
3655+
[&](const SCEV *V, const SCEV *&Param) -> bool {
3656+
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V)) {
3657+
if (!checkOffsets(AddRec->getStart(), Param))
3658+
return false;
3659+
return checkOffsets(AddRec->getStepRecurrence(*SE), Param);
3660+
}
3661+
if (auto *Cst = dyn_cast<SCEVConstant>(V)) {
3662+
APInt C = Cst->getAPInt();
3663+
return C.srem(EltSize) == 0;
3664+
}
3665+
3666+
auto checkParamsMultipleOfSize = [&](const SCEV *V,
3667+
const SCEV *&Param) -> bool {
3668+
if (EltSize == 1)
3669+
return true;
3670+
if (!Param) {
3671+
Param = V;
3672+
return true;
3673+
}
3674+
if (Param == V)
3675+
return true;
3676+
3677+
// Check whether "(Param - V) % Size == 0".
3678+
const SCEV *Diff = SE->getMinusSCEV(Param, V);
3679+
if (const SCEVConstant *Cst = dyn_cast<SCEVConstant>(Diff)) {
3680+
APInt Val = Cst->getAPInt();
3681+
if (Val.isZero())
3682+
return true;
3683+
auto Rem =
3684+
Val.srem(APInt(Val.getBitWidth(), EltSize, /*isSigned=*/true));
3685+
if (Rem.isZero())
3686+
return true;
3687+
LLVM_DEBUG(dbgs() << "SCEV with different offsets: " << *Param << " - "
3688+
<< *V << " = " << *Diff << " % " << EltSize << " = "
3689+
<< Rem << " != 0\n");
3690+
return false;
3691+
}
3692+
// Check if the symbolic difference is a multiple of Size.
3693+
const SCEV *Val = SE->getConstant(
3694+
APInt(Diff->getType()->getScalarSizeInBits(), EltSize));
3695+
3696+
// Check by using the remainder computation.
3697+
const SCEV *Remainder = SE->getURemExpr(Diff, Val);
3698+
if (const SCEVConstant *C = dyn_cast<SCEVConstant>(Remainder))
3699+
if (C->getValue()->isZero())
3700+
return true;
3701+
3702+
// Check by using the division computation.
3703+
const SCEV *Q = SE->getUDivExpr(Diff, Val);
3704+
const SCEV *Product = SE->getMulExpr(Q, Val);
3705+
if (Diff == Product)
3706+
return true;
3707+
LLVM_DEBUG(dbgs() << "SCEV with different offsets:\n"
3708+
<< *Param << " - " << *V << " = " << *Diff << "\n"
3709+
<< "Remainder = " << *Remainder << "\n"
3710+
<< "Q = " << *Q << " Product = " << *Product << "\n");
3711+
return false;
3712+
};
3713+
// Expressions like "n".
3714+
if (isa<SCEVUnknown>(V))
3715+
return checkParamsMultipleOfSize(V, Param);
3716+
// Expressions like "n + 1".
3717+
if (isa<SCEVAddExpr>(V))
3718+
return !SCEVExprContains(V, [](const SCEV *S) {
3719+
return isa<SCEVUnknown>(S);
3720+
}) || checkParamsMultipleOfSize(V, Param);
3721+
3722+
if (isa<SCEVMulExpr>(V))
3723+
return !SCEVExprContains(V, [](const SCEV *S) {
3724+
return isa<SCEVUnknown>(S);
3725+
}) || checkParamsMultipleOfSize(V, Param);
3726+
3727+
LLVM_DEBUG(dbgs() << "SCEV node not handled yet: " << *V << "\n");
3728+
return false;
3729+
};
3730+
3731+
// Check that memory access offsets are multiples of element sizes.
3732+
const SCEV *SrcEv = SE->getMinusSCEV(SrcSCEV, SrcBase);
3733+
const SCEV *DstEv = SE->getMinusSCEV(DstSCEV, DstBase);
3734+
const SCEV *Param = nullptr;
3735+
3736+
if (Src != Dst) {
3737+
// Check that memory access offsets are multiples of element sizes.
3738+
if (!checkOffsets(SrcEv, Param) || !checkOffsets(DstEv, Param)) {
3739+
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n");
3740+
return std::make_unique<Dependence>(Src, Dst);
3741+
}
3742+
}
3743+
36483744
// establish loop nesting levels
36493745
establishNestingLevels(Src, Dst);
36503746
LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -disable-output "-passes=print<da>" -aa-pipeline=basic-aa 2>&1 \
3+
; RUN: | FileCheck %s
4+
5+
; The dependence test does not handle array accesses with difference between array accesses
6+
; is not a multiple of the array element size.
7+
8+
; In this test, the element size is i32 = 4 bytes and the difference between the
9+
; load and the store is 2 bytes.
10+
11+
define i32 @alias_with_different_offsets(ptr nocapture %A) {
12+
; CHECK-LABEL: 'alias_with_different_offsets'
13+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: store i32 2, ptr %arrayidx, align 1
14+
; CHECK-NEXT: da analyze - none!
15+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: %0 = load i32, ptr %A, align 1
16+
; CHECK-NEXT: da analyze - confused!
17+
; CHECK-NEXT: Src: %0 = load i32, ptr %A, align 1 --> Dst: %0 = load i32, ptr %A, align 1
18+
; CHECK-NEXT: da analyze - none!
19+
;
20+
entry:
21+
%arrayidx = getelementptr inbounds i8, ptr %A, i64 2
22+
store i32 2, ptr %arrayidx, align 1
23+
%0 = load i32, ptr %A, align 1
24+
ret i32 %0
25+
}

llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ define void @test(ptr %A, ptr %B, i1 %arg, i32 %n, i32 %m) #0 align 2 {
4141
; CHECK-NEXT: Src: %v27 = load <32 x i32>, ptr %v25, align 256 --> Dst: %v27 = load <32 x i32>, ptr %v25, align 256
4242
; CHECK-NEXT: da analyze - consistent input [0 S S]!
4343
; CHECK-NEXT: Src: %v27 = load <32 x i32>, ptr %v25, align 256 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
44-
; CHECK-NEXT: da analyze - input [* S S|<]!
44+
; CHECK-NEXT: da analyze - confused!
4545
; CHECK-NEXT: Src: %v32 = load <32 x i32>, ptr %v30, align 128 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
4646
; CHECK-NEXT: da analyze - consistent input [0 S S]!
4747
;

0 commit comments

Comments
 (0)