Skip to content

Commit 2c6b239

Browse files
sebpopMeinersburkasuga-fj
authored
[DA] handle memory accesses with different offsets and strides (#123436)
This patch corrects the behavior of the Dependence Analysis for memory accesses that do not start at the same offset or do not have similar strides. When offsets or strides cannot be disambiguated at compile time, DA collects a set of runtime assumptions under which the dependence test becomes valid. The default remains the same as before the patch: DA rejects the dependence test as undecidable instead of collecting runtime assumptions. --------- Co-authored-by: Michael Kruse <[email protected]> Co-authored-by: Ryotaro Kasuga <[email protected]>
1 parent 91a7085 commit 2c6b239

File tree

7 files changed

+397
-35
lines changed

7 files changed

+397
-35
lines changed

llvm/include/llvm/Analysis/DependenceAnalysis.h

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#define LLVM_ANALYSIS_DEPENDENCEANALYSIS_H
4141

4242
#include "llvm/ADT/SmallBitVector.h"
43+
#include "llvm/Analysis/ScalarEvolution.h"
4344
#include "llvm/IR/Instructions.h"
4445
#include "llvm/IR/PassManager.h"
4546
#include "llvm/Pass.h"
@@ -49,8 +50,6 @@ namespace llvm {
4950
template <typename T> class ArrayRef;
5051
class Loop;
5152
class LoopInfo;
52-
class ScalarEvolution;
53-
class SCEV;
5453
class SCEVConstant;
5554
class raw_ostream;
5655

@@ -74,8 +73,9 @@ namespace llvm {
7473
Dependence &operator=(Dependence &&) = default;
7574

7675
public:
77-
Dependence(Instruction *Source, Instruction *Destination)
78-
: Src(Source), Dst(Destination) {}
76+
Dependence(Instruction *Source, Instruction *Destination,
77+
const SCEVUnionPredicate &A)
78+
: Src(Source), Dst(Destination), Assumptions(A) {}
7979
virtual ~Dependence() = default;
8080

8181
/// Dependence::DVEntry - Each level in the distance/direction vector
@@ -203,6 +203,10 @@ namespace llvm {
203203
/// field.
204204
void setNextSuccessor(const Dependence *succ) { NextSuccessor = succ; }
205205

206+
/// getRuntimeAssumptions - Returns the runtime assumptions under which this
207+
/// Dependence relation is valid.
208+
SCEVUnionPredicate getRuntimeAssumptions() const { return Assumptions; }
209+
206210
/// dump - For debugging purposes, dumps a dependence to OS.
207211
///
208212
void dump(raw_ostream &OS) const;
@@ -211,6 +215,7 @@ namespace llvm {
211215
Instruction *Src, *Dst;
212216

213217
private:
218+
SCEVUnionPredicate Assumptions;
214219
const Dependence *NextPredecessor = nullptr, *NextSuccessor = nullptr;
215220
friend class DependenceInfo;
216221
};
@@ -225,8 +230,9 @@ namespace llvm {
225230
/// input dependences are unordered.
226231
class FullDependence final : public Dependence {
227232
public:
228-
FullDependence(Instruction *Src, Instruction *Dst, bool LoopIndependent,
229-
unsigned Levels);
233+
FullDependence(Instruction *Source, Instruction *Destination,
234+
const SCEVUnionPredicate &Assumes,
235+
bool PossiblyLoopIndependent, unsigned Levels);
230236

231237
/// isLoopIndependent - Returns true if this is a loop-independent
232238
/// dependence.
@@ -302,9 +308,13 @@ namespace llvm {
302308

303309
/// depends - Tests for a dependence between the Src and Dst instructions.
304310
/// Returns NULL if no dependence; otherwise, returns a Dependence (or a
305-
/// FullDependence) with as much information as can be gleaned.
306-
std::unique_ptr<Dependence> depends(Instruction *Src,
307-
Instruction *Dst);
311+
/// FullDependence) with as much information as can be gleaned. By default,
312+
/// the dependence test collects a set of runtime assumptions that cannot be
313+
/// solved at compilation time. By default UnderRuntimeAssumptions is false
314+
/// for a safe approximation of the dependence relation that does not
315+
/// require runtime checks.
316+
std::unique_ptr<Dependence> depends(Instruction *Src, Instruction *Dst,
317+
bool UnderRuntimeAssumptions = false);
308318

309319
/// getSplitIteration - Give a dependence that's splittable at some
310320
/// particular level, return the iteration that should be used to split
@@ -350,11 +360,16 @@ namespace llvm {
350360

351361
Function *getFunction() const { return F; }
352362

363+
/// getRuntimeAssumptions - Returns all the runtime assumptions under which
364+
/// the dependence test is valid.
365+
SCEVUnionPredicate getRuntimeAssumptions() const;
366+
353367
private:
354368
AAResults *AA;
355369
ScalarEvolution *SE;
356370
LoopInfo *LI;
357371
Function *F;
372+
SmallVector<const SCEVPredicate *, 4> Assumptions;
358373

359374
/// Subscript - This private struct represents a pair of subscripts from
360375
/// a pair of potentially multi-dimensional array references. We use a

llvm/include/llvm/Analysis/ScalarEvolution.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,13 @@ class ScalarEvolution {
10441044
bool isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero = false,
10451045
bool OrNegative = false);
10461046

1047+
/// Check that \p S is a multiple of \p M. When \p S is an AddRecExpr, \p S is
1048+
/// a multiple of \p M if \p S starts with a multiple of \p M and at every
1049+
/// iteration step \p S only adds multiples of \p M. \p Assumptions records
1050+
/// the runtime predicates under which \p S is a multiple of \p M.
1051+
bool isKnownMultipleOf(const SCEV *S, uint64_t M,
1052+
SmallVectorImpl<const SCEVPredicate *> &Assumptions);
1053+
10471054
/// Splits SCEV expression \p S into two SCEVs. One of them is obtained from
10481055
/// \p S by substitution of all AddRec sub-expression related to loop \p L
10491056
/// with initial value of that SCEV. The second is obtained from \p S by

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA,
185185
if (DstI->mayReadOrWriteMemory()) {
186186
OS << "Src:" << *SrcI << " --> Dst:" << *DstI << "\n";
187187
OS << " da analyze - ";
188-
if (auto D = DA->depends(&*SrcI, &*DstI)) {
188+
if (auto D = DA->depends(&*SrcI, &*DstI,
189+
/*UnderRuntimeAssumptions=*/true)) {
189190
// Normalize negative direction vectors if required by clients.
190191
if (NormalizeResults && D->normalize(&SE))
191192
OS << "normalized - ";
@@ -197,13 +198,17 @@ static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA,
197198
OS << "!\n";
198199
}
199200
}
200-
}
201-
else
201+
} else
202202
OS << "none!\n";
203203
}
204204
}
205205
}
206206
}
207+
SCEVUnionPredicate Assumptions = DA->getRuntimeAssumptions();
208+
if (!Assumptions.isAlwaysTrue()) {
209+
OS << "Runtime Assumptions:\n";
210+
Assumptions.print(OS, 0);
211+
}
207212
}
208213

209214
void DependenceAnalysisWrapperPass::print(raw_ostream &OS,
@@ -262,9 +267,10 @@ bool Dependence::isScalar(unsigned level) const {
262267
// FullDependence methods
263268

264269
FullDependence::FullDependence(Instruction *Source, Instruction *Destination,
270+
const SCEVUnionPredicate &Assumes,
265271
bool PossiblyLoopIndependent,
266272
unsigned CommonLevels)
267-
: Dependence(Source, Destination), Levels(CommonLevels),
273+
: Dependence(Source, Destination, Assumes), Levels(CommonLevels),
268274
LoopIndependent(PossiblyLoopIndependent) {
269275
Consistent = true;
270276
if (CommonLevels)
@@ -704,6 +710,12 @@ void Dependence::dump(raw_ostream &OS) const {
704710
OS << " splitable";
705711
}
706712
OS << "!\n";
713+
714+
SCEVUnionPredicate Assumptions = getRuntimeAssumptions();
715+
if (!Assumptions.isAlwaysTrue()) {
716+
OS << " Runtime Assumptions:\n";
717+
Assumptions.print(OS, 2);
718+
}
707719
}
708720

709721
// Returns NoAlias/MayAliass/MustAlias for two memory locations based upon their
@@ -3567,6 +3579,10 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
35673579
Inv.invalidate<LoopAnalysis>(F, PA);
35683580
}
35693581

3582+
SCEVUnionPredicate DependenceInfo::getRuntimeAssumptions() const {
3583+
return SCEVUnionPredicate(Assumptions, *SE);
3584+
}
3585+
35703586
// depends -
35713587
// Returns NULL if there is no dependence.
35723588
// Otherwise, return a Dependence with as many details as possible.
@@ -3579,7 +3595,9 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
35793595
// Care is required to keep the routine below, getSplitIteration(),
35803596
// up to date with respect to this routine.
35813597
std::unique_ptr<Dependence>
3582-
DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
3598+
DependenceInfo::depends(Instruction *Src, Instruction *Dst,
3599+
bool UnderRuntimeAssumptions) {
3600+
SmallVector<const SCEVPredicate *, 4> Assume;
35833601
bool PossiblyLoopIndependent = true;
35843602
if (Src == Dst)
35853603
PossiblyLoopIndependent = false;
@@ -3591,22 +3609,20 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
35913609
if (!isLoadOrStore(Src) || !isLoadOrStore(Dst)) {
35923610
// can only analyze simple loads and stores, i.e., no calls, invokes, etc.
35933611
LLVM_DEBUG(dbgs() << "can only handle simple loads and stores\n");
3594-
return std::make_unique<Dependence>(Src, Dst);
3612+
return std::make_unique<Dependence>(Src, Dst,
3613+
SCEVUnionPredicate(Assume, *SE));
35953614
}
35963615

3597-
assert(isLoadOrStore(Src) && "instruction is not load or store");
3598-
assert(isLoadOrStore(Dst) && "instruction is not load or store");
3599-
Value *SrcPtr = getLoadStorePointerOperand(Src);
3600-
Value *DstPtr = getLoadStorePointerOperand(Dst);
3616+
const MemoryLocation &DstLoc = MemoryLocation::get(Dst);
3617+
const MemoryLocation &SrcLoc = MemoryLocation::get(Src);
36013618

3602-
switch (underlyingObjectsAlias(AA, F->getDataLayout(),
3603-
MemoryLocation::get(Dst),
3604-
MemoryLocation::get(Src))) {
3619+
switch (underlyingObjectsAlias(AA, F->getDataLayout(), DstLoc, SrcLoc)) {
36053620
case AliasResult::MayAlias:
36063621
case AliasResult::PartialAlias:
36073622
// cannot analyse objects if we don't understand their aliasing.
36083623
LLVM_DEBUG(dbgs() << "can't analyze may or partial alias\n");
3609-
return std::make_unique<Dependence>(Src, Dst);
3624+
return std::make_unique<Dependence>(Src, Dst,
3625+
SCEVUnionPredicate(Assume, *SE));
36103626
case AliasResult::NoAlias:
36113627
// If the objects noalias, they are distinct, accesses are independent.
36123628
LLVM_DEBUG(dbgs() << "no alias\n");
@@ -3615,30 +3631,75 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
36153631
break; // The underlying objects alias; test accesses for dependence.
36163632
}
36173633

3618-
// establish loop nesting levels
3619-
establishNestingLevels(Src, Dst);
3620-
LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
3621-
LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n");
3622-
3623-
FullDependence Result(Src, Dst, PossiblyLoopIndependent, CommonLevels);
3624-
++TotalArrayPairs;
3634+
if (DstLoc.Size != SrcLoc.Size || !DstLoc.Size.isPrecise() ||
3635+
!SrcLoc.Size.isPrecise()) {
3636+
// The dependence test gets confused if the size of the memory accesses
3637+
// differ.
3638+
LLVM_DEBUG(dbgs() << "can't analyze must alias with different sizes\n");
3639+
return std::make_unique<Dependence>(Src, Dst,
3640+
SCEVUnionPredicate(Assume, *SE));
3641+
}
36253642

3626-
unsigned Pairs = 1;
3627-
SmallVector<Subscript, 2> Pair(Pairs);
3643+
Value *SrcPtr = getLoadStorePointerOperand(Src);
3644+
Value *DstPtr = getLoadStorePointerOperand(Dst);
36283645
const SCEV *SrcSCEV = SE->getSCEV(SrcPtr);
36293646
const SCEV *DstSCEV = SE->getSCEV(DstPtr);
36303647
LLVM_DEBUG(dbgs() << " SrcSCEV = " << *SrcSCEV << "\n");
36313648
LLVM_DEBUG(dbgs() << " DstSCEV = " << *DstSCEV << "\n");
3632-
if (SE->getPointerBase(SrcSCEV) != SE->getPointerBase(DstSCEV)) {
3649+
const SCEV *SrcBase = SE->getPointerBase(SrcSCEV);
3650+
const SCEV *DstBase = SE->getPointerBase(DstSCEV);
3651+
if (SrcBase != DstBase) {
36333652
// If two pointers have different bases, trying to analyze indexes won't
36343653
// work; we can't compare them to each other. This can happen, for example,
36353654
// if one is produced by an LCSSA PHI node.
36363655
//
36373656
// We check this upfront so we don't crash in cases where getMinusSCEV()
36383657
// returns a SCEVCouldNotCompute.
36393658
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different pointer base\n");
3640-
return std::make_unique<Dependence>(Src, Dst);
3659+
return std::make_unique<Dependence>(Src, Dst,
3660+
SCEVUnionPredicate(Assume, *SE));
3661+
}
3662+
3663+
uint64_t EltSize = SrcLoc.Size.toRaw();
3664+
const SCEV *SrcEv = SE->getMinusSCEV(SrcSCEV, SrcBase);
3665+
const SCEV *DstEv = SE->getMinusSCEV(DstSCEV, DstBase);
3666+
3667+
if (Src != Dst) {
3668+
// Check that memory access offsets are multiples of element sizes.
3669+
if (!SE->isKnownMultipleOf(SrcEv, EltSize, Assume) ||
3670+
!SE->isKnownMultipleOf(DstEv, EltSize, Assume)) {
3671+
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n");
3672+
return std::make_unique<Dependence>(Src, Dst,
3673+
SCEVUnionPredicate(Assume, *SE));
3674+
}
3675+
}
3676+
3677+
if (!Assume.empty()) {
3678+
if (!UnderRuntimeAssumptions)
3679+
return std::make_unique<Dependence>(Src, Dst,
3680+
SCEVUnionPredicate(Assume, *SE));
3681+
// Add non-redundant assumptions.
3682+
unsigned N = Assumptions.size();
3683+
for (const SCEVPredicate *P : Assume) {
3684+
bool Implied = false;
3685+
for (unsigned I = 0; I != N && !Implied; I++)
3686+
if (Assumptions[I]->implies(P, *SE))
3687+
Implied = true;
3688+
if (!Implied)
3689+
Assumptions.push_back(P);
3690+
}
36413691
}
3692+
3693+
establishNestingLevels(Src, Dst);
3694+
LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
3695+
LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n");
3696+
3697+
FullDependence Result(Src, Dst, SCEVUnionPredicate(Assume, *SE),
3698+
PossiblyLoopIndependent, CommonLevels);
3699+
++TotalArrayPairs;
3700+
3701+
unsigned Pairs = 1;
3702+
SmallVector<Subscript, 2> Pair(Pairs);
36423703
Pair[0].Src = SrcSCEV;
36433704
Pair[0].Dst = DstSCEV;
36443705

@@ -4032,7 +4093,7 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,
40324093
// establish loop nesting levels
40334094
establishNestingLevels(Src, Dst);
40344095

4035-
FullDependence Result(Src, Dst, false, CommonLevels);
4096+
FullDependence Result(Src, Dst, Dep.Assumptions, false, CommonLevels);
40364097

40374098
unsigned Pairs = 1;
40384099
SmallVector<Subscript, 2> Pair(Pairs);

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10963,6 +10963,56 @@ bool ScalarEvolution::isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero,
1096310963
return all_of(Mul->operands(), NonRecursive) && (OrZero || isKnownNonZero(S));
1096410964
}
1096510965

10966+
bool ScalarEvolution::isKnownMultipleOf(
10967+
const SCEV *S, uint64_t M,
10968+
SmallVectorImpl<const SCEVPredicate *> &Assumptions) {
10969+
if (M == 0)
10970+
return false;
10971+
if (M == 1)
10972+
return true;
10973+
10974+
// Recursively check AddRec operands. An AddRecExpr S is a multiple of M if S
10975+
// starts with a multiple of M and at every iteration step S only adds
10976+
// multiples of M.
10977+
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(S))
10978+
return isKnownMultipleOf(AddRec->getStart(), M, Assumptions) &&
10979+
isKnownMultipleOf(AddRec->getStepRecurrence(*this), M, Assumptions);
10980+
10981+
// For a constant, check that "S % M == 0".
10982+
if (auto *Cst = dyn_cast<SCEVConstant>(S)) {
10983+
APInt C = Cst->getAPInt();
10984+
return C.urem(M) == 0;
10985+
}
10986+
10987+
// TODO: Also check other SCEV expressions, i.e., SCEVAddRecExpr, etc.
10988+
10989+
// Basic tests have failed.
10990+
// Check "S % M == 0" at compile time and record runtime Assumptions.
10991+
auto *STy = dyn_cast<IntegerType>(S->getType());
10992+
const SCEV *SmodM =
10993+
getURemExpr(S, getConstant(ConstantInt::get(STy, M, false)));
10994+
const SCEV *Zero = getZero(STy);
10995+
10996+
// Check whether "S % M == 0" is known at compile time.
10997+
if (isKnownPredicate(ICmpInst::ICMP_EQ, SmodM, Zero))
10998+
return true;
10999+
11000+
// Check whether "S % M != 0" is known at compile time.
11001+
if (isKnownPredicate(ICmpInst::ICMP_NE, SmodM, Zero))
11002+
return false;
11003+
11004+
const SCEVPredicate *P = getComparePredicate(ICmpInst::ICMP_EQ, SmodM, Zero);
11005+
11006+
// Detect redundant predicates.
11007+
for (auto *A : Assumptions)
11008+
if (A->implies(P, *this))
11009+
return true;
11010+
11011+
// Only record non-redundant predicates.
11012+
Assumptions.push_back(P);
11013+
return true;
11014+
}
11015+
1096611016
std::pair<const SCEV *, const SCEV *>
1096711017
ScalarEvolution::SplitIntoInitAndPostInc(const Loop *L, const SCEV *S) {
1096811018
// Compute SCEV on entry of loop L.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 of different sizes: i32 and i64.
6+
; Bug 16183 - https://github.com/llvm/llvm-project/issues/16183
7+
8+
define i64 @bug16183_alias(ptr nocapture %A) {
9+
; CHECK-LABEL: 'bug16183_alias'
10+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 4 --> Dst: store i32 2, ptr %arrayidx, align 4
11+
; CHECK-NEXT: da analyze - none!
12+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 4 --> Dst: %0 = load i64, ptr %A, align 8
13+
; CHECK-NEXT: da analyze - confused!
14+
; CHECK-NEXT: Src: %0 = load i64, ptr %A, align 8 --> Dst: %0 = load i64, ptr %A, align 8
15+
; CHECK-NEXT: da analyze - none!
16+
;
17+
entry:
18+
%arrayidx = getelementptr inbounds i32, ptr %A, i64 1
19+
store i32 2, ptr %arrayidx, align 4
20+
%0 = load i64, ptr %A, align 8
21+
ret i64 %0
22+
}

0 commit comments

Comments
 (0)