Skip to content

Bug fixes for Array Dependence Analysis #116389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 69 additions & 27 deletions llvm/lib/Analysis/DependenceAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,10 +712,66 @@ void Dependence::dump(raw_ostream &OS) const {
// tbaa, non-overlapping regions etc), then it is known there is no dependecy.
// Otherwise the underlying objects are checked to see if they point to
// different identifiable objects.
static AliasResult underlyingObjectsAlias(AAResults *AA,
const DataLayout &DL,
const MemoryLocation &LocA,
const MemoryLocation &LocB) {
static AliasResult underlyingObjectsAlias(AAResults *AA, LoopInfo *LI,
ScalarEvolution *SE, Instruction *A,
Instruction *B) {
const MemoryLocation &LocA = MemoryLocation::get(A);
const MemoryLocation &LocB = MemoryLocation::get(B);

// Check the underlying objects are the same
const Value *AObj = getUnderlyingObject(LocA.Ptr);
const Value *BObj = getUnderlyingObject(LocB.Ptr);

if (AObj == BObj) {
// The dependence test gets confused if the size of the memory accesses
// differ.
if (LocA.Size != LocB.Size)
return AliasResult::MayAlias;

// If the underlying objects are the same, they must alias.
return AliasResult::MustAlias;
}

if (auto *APhi = dyn_cast<PHINode>(AObj)) {
if (auto *BPhi = dyn_cast<PHINode>(BObj)) {
Loop *ALoop = LI->getLoopFor(APhi->getParent());
Loop *BLoop = LI->getLoopFor(BPhi->getParent());
if (ALoop == BLoop) {
auto *SCEVa = SE->getSCEV(const_cast<Value *>(AObj));
auto *SCEVb = SE->getSCEV(const_cast<Value *>(BObj));

// If the SCEVs are the same, they must alias.
if (SCEVa == SCEVb)
return AliasResult::MustAlias;

// If SCEV cannot analyze one of the values, then they may alias.
if (isa<SCEVUnknown>(SCEVa) || isa<SCEVUnknown>(SCEVb))
return AliasResult::MayAlias;

// Check whether the start values alias.
const SCEV *StartA = SCEVa;
while (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(StartA))
StartA = AR->getStart();

const SCEV *StartB = SCEVb;
while (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(StartB))
StartB = AR->getStart();

if (const SCEVUnknown *UA = dyn_cast<SCEVUnknown>(StartA)) {
if (const SCEVUnknown *UB = dyn_cast<SCEVUnknown>(StartB)) {
MemoryLocation LocAS =
MemoryLocation::getBeforeOrAfter(UA->getValue());
MemoryLocation LocBS =
MemoryLocation::getBeforeOrAfter(UB->getValue());
if (AA->isNoAlias(LocAS, LocBS))
return AliasResult::NoAlias;
}
}
return AliasResult::MayAlias;
}
}
}

// Check the original locations (minus size) for noalias, which can happen for
// tbaa, incompatible underlying object locations, etc.
MemoryLocation LocAS =
Expand All @@ -725,14 +781,6 @@ static AliasResult underlyingObjectsAlias(AAResults *AA,
if (AA->isNoAlias(LocAS, LocBS))
return AliasResult::NoAlias;

// Check the underlying objects are the same
const Value *AObj = getUnderlyingObject(LocA.Ptr);
const Value *BObj = getUnderlyingObject(LocB.Ptr);

// If the underlying objects are the same, they must alias
if (AObj == BObj)
return AliasResult::MustAlias;

// We may have hit the recursion limit for underlying objects, or have
// underlying objects where we don't know they will alias.
if (!isIdentifiedObject(AObj) || !isIdentifiedObject(BObj))
Expand All @@ -743,7 +791,6 @@ static AliasResult underlyingObjectsAlias(AAResults *AA,
return AliasResult::NoAlias;
}


// Returns true if the load or store can be analyzed. Atomic and volatile
// operations have properties which this analysis does not understand.
static
Expand Down Expand Up @@ -3116,11 +3163,9 @@ const SCEV *DependenceInfo::addToCoefficient(const SCEV *Expr,
const Loop *TargetLoop,
const SCEV *Value) const {
const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(Expr);
if (!AddRec) // create a new addRec
return SE->getAddRecExpr(Expr,
Value,
TargetLoop,
SCEV::FlagAnyWrap); // Worst case, with no info.
if (!AddRec)
return SE->getAddRecExpr(Expr, Value, TargetLoop, SCEV::FlagNSW);

if (AddRec->getLoop() == TargetLoop) {
const SCEV *Sum = SE->getAddExpr(AddRec->getStepRecurrence(*SE), Value);
if (Sum->isZero())
Expand All @@ -3131,7 +3176,7 @@ const SCEV *DependenceInfo::addToCoefficient(const SCEV *Expr,
AddRec->getNoWrapFlags());
}
if (SE->isLoopInvariant(AddRec, TargetLoop))
return SE->getAddRecExpr(AddRec, Value, TargetLoop, SCEV::FlagAnyWrap);
return SE->getAddRecExpr(AddRec, Value, TargetLoop, SCEV::FlagNSW);
return SE->getAddRecExpr(
addToCoefficient(AddRec->getStart(), TargetLoop, Value),
AddRec->getStepRecurrence(*SE), AddRec->getLoop(),
Expand Down Expand Up @@ -3606,9 +3651,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
Value *SrcPtr = getLoadStorePointerOperand(Src);
Value *DstPtr = getLoadStorePointerOperand(Dst);

switch (underlyingObjectsAlias(AA, F->getDataLayout(),
MemoryLocation::get(Dst),
MemoryLocation::get(Src))) {
switch (underlyingObjectsAlias(AA, LI, SE, Dst, Src)) {
case AliasResult::MayAlias:
case AliasResult::PartialAlias:
// cannot analyse objects if we don't understand their aliasing.
Expand Down Expand Up @@ -4030,11 +4073,8 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,
assert(Dst->mayReadFromMemory() || Dst->mayWriteToMemory());
assert(isLoadOrStore(Src));
assert(isLoadOrStore(Dst));
Value *SrcPtr = getLoadStorePointerOperand(Src);
Value *DstPtr = getLoadStorePointerOperand(Dst);
assert(underlyingObjectsAlias(
AA, F->getDataLayout(), MemoryLocation::get(Dst),
MemoryLocation::get(Src)) == AliasResult::MustAlias);
assert(underlyingObjectsAlias(AA, LI, SE, Dst, Src) ==
AliasResult::MustAlias);

// establish loop nesting levels
establishNestingLevels(Src, Dst);
Expand All @@ -4043,6 +4083,8 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,

unsigned Pairs = 1;
SmallVector<Subscript, 2> Pair(Pairs);
Value *SrcPtr = getLoadStorePointerOperand(Src);
Value *DstPtr = getLoadStorePointerOperand(Dst);
const SCEV *SrcSCEV = SE->getSCEV(SrcPtr);
const SCEV *DstSCEV = SE->getSCEV(DstPtr);
Pair[0].Src = SrcSCEV;
Expand Down
15 changes: 15 additions & 0 deletions llvm/test/Analysis/DependenceAnalysis/DifferentAccessSize.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
; RUN: opt < %s -disable-output "-passes=print<da>" -aa-pipeline=basic-aa 2>&1 \
; RUN: | FileCheck %s

; The dependence test does not handle array accesses of different sizes: i32 and i64.
; Bug 16183 - https://github.com/llvm/llvm-project/issues/16183
; CHECK-LABEL: bug16183_alias
; CHECK: da analyze - confused!

define i64 @bug16183_alias(i32* nocapture %A) {
entry:
%arrayidx = getelementptr inbounds i32, ptr %A, i64 1
store i32 2, ptr %arrayidx, align 4
%0 = load i64, ptr %A, align 8
ret i64 %0
}
136 changes: 136 additions & 0 deletions llvm/test/Analysis/DependenceAnalysis/FlipFlopBaseAddress.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
; RUN: opt < %s -disable-output "-passes=print<da>" -aa-pipeline=basic-aa 2>&1 \
; RUN: | FileCheck %s

; Check that dependence analysis correctly handles flip-flop of base addresses.
; Bug 41488 - https://github.com/llvm/llvm-project/issues/41488

; CHECK-LABEL: bug41488_test1
; CHECK-NOT: da analyze - none!

define float @bug41488_test1() {
entry:
%g = alloca float, align 4
%h = alloca float, align 4
br label %for.body

for.body:
%p = phi float* [ %g, %entry ], [ %q, %for.body ]
%q = phi float* [ %h, %entry ], [ %p, %for.body ]
%0 = load float, float* %p, align 4
store float undef, float* %q, align 4
%branch_cond = fcmp ugt float %0, 0.0
br i1 %branch_cond, label %for.cond.cleanup, label %for.body

for.cond.cleanup:
ret float undef
}

; CHECK-LABEL: bug41488_test2
; CHECK-NOT: da analyze - none!

define void @bug41488_test2(i32 %n) {
entry:
%g = alloca float, align 4
%h = alloca float, align 4
br label %for.body

for.body:
%i = phi i32 [0, %entry ], [ %inc, %for.body ]
%p = phi float* [ %g, %entry ], [ %q, %for.body ]
%q = phi float* [ %h, %entry ], [ %p, %for.body ]
%0 = load float, float* %p, align 4
store float 0.0, float* %q, align 4
%inc = add nuw i32 %i, 1
%branch_cond = icmp ult i32 %i, %n
br i1 %branch_cond, label %for.body, label %for.cond.cleanup

for.cond.cleanup:
ret void
}

; Bug 53942 - https://github.com/llvm/llvm-project/issues/53942
; CHECK-LABEL: bug53942_foo
; CHECK-NOT: da analyze - none!

define void @bug53942_foo(i32 noundef %n, ptr noalias nocapture noundef writeonly %A, ptr noalias nocapture noundef %B) {
entry:
%cmp8 = icmp sgt i32 %n, 1
br i1 %cmp8, label %for.body.preheader, label %for.cond.cleanup

for.body.preheader: ; preds = %entry
%wide.trip.count = zext nneg i32 %n to i64
br label %for.body

for.cond.cleanup: ; preds = %for.body, %entry
ret void

for.body: ; preds = %for.body.preheader, %for.body
%indvars.iv = phi i64 [ 1, %for.body.preheader ], [ %indvars.iv.next, %for.body ]
%ptr1.011 = phi ptr [ %A, %for.body.preheader ], [ %ptr2.09, %for.body ]
%ptr2.09 = phi ptr [ %B, %for.body.preheader ], [ %ptr1.011, %for.body ]
%.pre = load double, ptr %B, align 8
%arrayidx2 = getelementptr inbounds double, ptr %ptr1.011, i64 %indvars.iv
store double %.pre, ptr %arrayidx2, align 8
%indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
%exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count
br i1 %exitcond.not, label %for.cond.cleanup, label %for.body
}


; Bug 53942 - https://github.com/llvm/llvm-project/issues/53942
; CHECK-LABEL: bug53942_bar
; CHECK-NOT: da analyze - none!

define void @bug53942_bar(i32 noundef %n, ptr noalias noundef %A, ptr noalias noundef %B) {
entry:
br label %for.cond

for.cond: ; preds = %for.inc, %entry
%i.0 = phi i32 [ 1, %entry ], [ %inc, %for.inc ]
%cmp = icmp slt i32 %i.0, %n
br i1 %cmp, label %for.body, label %for.cond.cleanup

for.cond.cleanup: ; preds = %for.cond
br label %for.end

for.body: ; preds = %for.cond
%and = and i32 %i.0, 2
%tobool.not = icmp eq i32 %and, 0
br i1 %tobool.not, label %cond.false, label %cond.true

cond.true: ; preds = %for.body
br label %cond.end

cond.false: ; preds = %for.body
br label %cond.end

cond.end: ; preds = %cond.false, %cond.true
%cond = phi ptr [ %A, %cond.true ], [ %B, %cond.false ]
%and1 = and i32 %i.0, 2
%tobool2.not = icmp eq i32 %and1, 0
br i1 %tobool2.not, label %cond.false4, label %cond.true3

cond.true3: ; preds = %cond.end
br label %cond.end5

cond.false4: ; preds = %cond.end
br label %cond.end5

cond.end5: ; preds = %cond.false4, %cond.true3
%cond6 = phi ptr [ %B, %cond.true3 ], [ %A, %cond.false4 ]
%sub = add nsw i32 %i.0, -1
%idxprom = sext i32 %sub to i64
%arrayidx = getelementptr inbounds double, ptr %cond6, i64 %idxprom
%0 = load double, ptr %arrayidx, align 8
%idxprom7 = zext nneg i32 %i.0 to i64
%arrayidx8 = getelementptr inbounds double, ptr %cond, i64 %idxprom7
store double %0, ptr %arrayidx8, align 8
br label %for.inc

for.inc: ; preds = %cond.end5
%inc = add nuw nsw i32 %i.0, 1
br label %for.cond

for.end: ; preds = %for.cond.cleanup
ret void
}
67 changes: 67 additions & 0 deletions llvm/test/Analysis/DependenceAnalysis/PR31848.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
; RUN: opt < %s -disable-output "-passes=print<da>" -aa-pipeline=basic-aa 2>&1 \
; RUN: | FileCheck %s

define void @foo(i32* nocapture %A, i32 %n) {
entry:
%n.cmp = icmp sgt i32 %n, 0
br i1 %n.cmp, label %for.j.header, label %exit

for.j.header:
%j= phi i32 [ %j.inc, %for.j.latch ], [ 0, %entry ]
br label %for.i.header

for.i.header:
%i = phi i32 [ %i.inc, %for.i.latch ], [ 0, %for.j.header ]
br label %for.di.header

for.di.header:
%di = phi i32 [ -1, %for.i.header ], [ %di.inc, %for.di.latch ]
%di.add = add nsw i32 %di, %i
br label %for.dj.header

for.dj.header:
%dj = phi i32 [ -1, %for.di.header ], [ %dj.inc, %body ]
%dj.add = add nsw i32 %dj, %j
br label %while.x

while.x:
%x = phi i32 [ %di.add, %for.dj.header ], [ %x.inc, %while.x ]
%x.cmp = icmp slt i32 %x, 0
%x.inc = add nsw i32 %x, %n
br i1 %x.cmp, label %while.x, label %while.y

while.y:
%y = phi i32 [ %y.inc, %while.y ], [ %dj.add, %while.x ]
%y.cmp = icmp slt i32 %y, 0
%y.inc = add nsw i32 %y, %n
br i1 %y.cmp, label %while.y, label %body

body:
%mul = mul nsw i32 %y, %n
%add = add nsw i32 %mul, %x
%idxprom = sext i32 %add to i64
%arrayidx = getelementptr inbounds i32, i32* %A, i64 %idxprom
; CHECK: da analyze - output [* * * *]
store i32 7, i32* %arrayidx, align 4
%dj.inc = add nsw i32 %dj, 1
%dj.exitcond = icmp eq i32 %dj.inc, 2
br i1 %dj.exitcond, label %for.di.latch, label %for.dj.header

for.di.latch:
%di.inc = add nsw i32 %di, 1
%di.exitcond = icmp eq i32 %di.inc, 2
br i1 %di.exitcond, label %for.i.latch, label %for.di.header

for.i.latch:
%i.inc = add nuw nsw i32 %i, 1
%i.exitcond = icmp eq i32 %i.inc, %n
br i1 %i.exitcond, label %for.j.latch, label %for.i.header

for.j.latch:
%j.inc = add nuw nsw i32 %j, 1
%j.exitcond = icmp eq i32 %j.inc, %n
br i1 %j.exitcond, label %exit, label %for.j.header

exit:
ret void
}
Loading
Loading