Skip to content

Commit ca28604

Browse files
committed
[DA] Simplify runtime predicate collection and extend to all dependence tests
Previously, predicates were collected using a local `Assume` vector. This patch removes local `Assume` vector, uses class member `Assumptions` instead, and extends predicate collection to all dependence tests.
1 parent 879f40a commit ca28604

File tree

4 files changed

+69
-41
lines changed

4 files changed

+69
-41
lines changed

llvm/include/llvm/Analysis/DependenceAnalysis.h

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ class LLVM_ABI FullDependence final : public Dependence {
285285
class DependenceInfo {
286286
public:
287287
DependenceInfo(Function *F, AAResults *AA, ScalarEvolution *SE, LoopInfo *LI)
288-
: AA(AA), SE(SE), LI(LI), F(F) {}
288+
: AA(AA), SE(SE), LI(LI), F(F), Assumptions({}, *SE),
289+
UnderRuntimeAssumptions(false) {}
289290

290291
/// Handle transitive invalidation when the cached analysis results go away.
291292
LLVM_ABI bool invalidate(Function &F, const PreservedAnalyses &PA,
@@ -355,7 +356,33 @@ class DependenceInfo {
355356
ScalarEvolution *SE;
356357
LoopInfo *LI;
357358
Function *F;
358-
SmallVector<const SCEVPredicate *, 4> Assumptions;
359+
360+
/// Runtime assumptions collected during dependence analysis.
361+
///
362+
/// The dependence analysis employs a cascade of tests from simple to complex:
363+
/// ZIV -> SIV (Strong/Weak-Crossing/Weak-Zero) -> RDIV -> MIV -> Banerjee.
364+
/// Each test attempts to characterize the dependence with increasing
365+
/// precision.
366+
///
367+
/// Assumption Management Strategy:
368+
/// - Each test may require runtime assumptions (e.g., "coefficient != 0")
369+
/// to provide precise analysis.
370+
/// - If UnderRuntimeAssumptions=true: tests can add assumptions and continue.
371+
/// - If UnderRuntimeAssumptions=false: tests that need assumptions fail
372+
/// gracefully, allowing more complex tests to attempt analysis.
373+
/// - Only assumptions from successful tests contribute to the final result.
374+
/// - SCEVUnionPredicate automatically deduplicates redundant assumptions.
375+
///
376+
/// This design ensures:
377+
/// 1. Simpler tests get priority (better performance).
378+
/// 2. Complex tests serve as fallbacks when simple tests fail.
379+
/// 3. No unnecessary runtime checks from failed test attempts.
380+
/// 4. Maintains the intended cascade behavior of the dependence analysis.
381+
SCEVUnionPredicate Assumptions;
382+
383+
/// Indicates whether runtime assumptions are collected during the analysis.
384+
/// When false, dependence tests that would require runtime assumptions fail.
385+
bool UnderRuntimeAssumptions;
359386

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

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3567,7 +3567,7 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
35673567
}
35683568

35693569
SCEVUnionPredicate DependenceInfo::getRuntimeAssumptions() const {
3570-
return SCEVUnionPredicate(Assumptions, *SE);
3570+
return Assumptions;
35713571
}
35723572

35733573
// depends -
@@ -3584,7 +3584,12 @@ SCEVUnionPredicate DependenceInfo::getRuntimeAssumptions() const {
35843584
std::unique_ptr<Dependence>
35853585
DependenceInfo::depends(Instruction *Src, Instruction *Dst,
35863586
bool UnderRuntimeAssumptions) {
3587-
SmallVector<const SCEVPredicate *, 4> Assume;
3587+
// Set the flag for whether we're allowed to add runtime assumptions.
3588+
this->UnderRuntimeAssumptions = UnderRuntimeAssumptions;
3589+
3590+
// Clear any previous assumptions
3591+
Assumptions = SCEVUnionPredicate({}, *SE);
3592+
35883593
bool PossiblyLoopIndependent = true;
35893594
if (Src == Dst)
35903595
PossiblyLoopIndependent = false;
@@ -3596,8 +3601,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
35963601
if (!isLoadOrStore(Src) || !isLoadOrStore(Dst)) {
35973602
// can only analyze simple loads and stores, i.e., no calls, invokes, etc.
35983603
LLVM_DEBUG(dbgs() << "can only handle simple loads and stores\n");
3599-
return std::make_unique<Dependence>(Src, Dst,
3600-
SCEVUnionPredicate(Assume, *SE));
3604+
return std::make_unique<Dependence>(Src, Dst, getRuntimeAssumptions());
36013605
}
36023606

36033607
const MemoryLocation &DstLoc = MemoryLocation::get(Dst);
@@ -3608,8 +3612,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
36083612
case AliasResult::PartialAlias:
36093613
// cannot analyse objects if we don't understand their aliasing.
36103614
LLVM_DEBUG(dbgs() << "can't analyze may or partial alias\n");
3611-
return std::make_unique<Dependence>(Src, Dst,
3612-
SCEVUnionPredicate(Assume, *SE));
3615+
return std::make_unique<Dependence>(Src, Dst, getRuntimeAssumptions());
36133616
case AliasResult::NoAlias:
36143617
// If the objects noalias, they are distinct, accesses are independent.
36153618
LLVM_DEBUG(dbgs() << "no alias\n");
@@ -3623,8 +3626,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
36233626
// The dependence test gets confused if the size of the memory accesses
36243627
// differ.
36253628
LLVM_DEBUG(dbgs() << "can't analyze must alias with different sizes\n");
3626-
return std::make_unique<Dependence>(Src, Dst,
3627-
SCEVUnionPredicate(Assume, *SE));
3629+
return std::make_unique<Dependence>(Src, Dst, getRuntimeAssumptions());
36283630
}
36293631

36303632
Value *SrcPtr = getLoadStorePointerOperand(Src);
@@ -3643,8 +3645,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
36433645
// We check this upfront so we don't crash in cases where getMinusSCEV()
36443646
// returns a SCEVCouldNotCompute.
36453647
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different pointer base\n");
3646-
return std::make_unique<Dependence>(Src, Dst,
3647-
SCEVUnionPredicate(Assume, *SE));
3648+
return std::make_unique<Dependence>(Src, Dst, getRuntimeAssumptions());
36483649
}
36493650

36503651
// Even if the base pointers are the same, they may not be loop-invariant. It
@@ -3656,44 +3657,48 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
36563657
if (!isLoopInvariant(SrcBase, SrcLoop) ||
36573658
!isLoopInvariant(DstBase, DstLoop)) {
36583659
LLVM_DEBUG(dbgs() << "The base pointer is not loop invariant.\n");
3659-
return std::make_unique<Dependence>(Src, Dst,
3660-
SCEVUnionPredicate(Assume, *SE));
3660+
return std::make_unique<Dependence>(Src, Dst, getRuntimeAssumptions());
36613661
}
36623662

36633663
uint64_t EltSize = SrcLoc.Size.toRaw();
36643664
const SCEV *SrcEv = SE->getMinusSCEV(SrcSCEV, SrcBase);
36653665
const SCEV *DstEv = SE->getMinusSCEV(DstSCEV, DstBase);
36663666

36673667
// Check that memory access offsets are multiples of element sizes.
3668-
if (!SE->isKnownMultipleOf(SrcEv, EltSize, Assume) ||
3669-
!SE->isKnownMultipleOf(DstEv, EltSize, Assume)) {
3668+
SmallVector<const SCEVPredicate *, 4> TempAssumptions;
3669+
if (!SE->isKnownMultipleOf(SrcEv, EltSize, TempAssumptions) ||
3670+
!SE->isKnownMultipleOf(DstEv, EltSize, TempAssumptions)) {
36703671
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n");
3671-
return std::make_unique<Dependence>(Src, Dst,
3672-
SCEVUnionPredicate(Assume, *SE));
3672+
return std::make_unique<Dependence>(Src, Dst, getRuntimeAssumptions());
36733673
}
36743674

3675-
if (!Assume.empty()) {
3676-
if (!UnderRuntimeAssumptions)
3677-
return std::make_unique<Dependence>(Src, Dst,
3678-
SCEVUnionPredicate(Assume, *SE));
3679-
// Add non-redundant assumptions.
3680-
unsigned N = Assumptions.size();
3681-
for (const SCEVPredicate *P : Assume) {
3682-
bool Implied = false;
3683-
for (unsigned I = 0; I != N && !Implied; I++)
3684-
if (Assumptions[I]->implies(P, *SE))
3685-
Implied = true;
3686-
if (!Implied)
3687-
Assumptions.push_back(P);
3675+
// Add any new assumptions from the isKnownMultipleOf calls
3676+
if (!TempAssumptions.empty()) {
3677+
if (UnderRuntimeAssumptions) {
3678+
SmallVector<const SCEVPredicate *, 4> NewPreds(
3679+
Assumptions.getPredicates());
3680+
NewPreds.append(TempAssumptions.begin(), TempAssumptions.end());
3681+
const_cast<DependenceInfo *>(this)->Assumptions =
3682+
SCEVUnionPredicate(NewPreds, *SE);
3683+
} else {
3684+
// Runtime assumptions needed but not allowed.
3685+
// Return confused dependence since we cannot proceed with precise
3686+
// analysis.
3687+
LLVM_DEBUG(dbgs() << "Runtime assumptions needed for offset analysis but "
3688+
"not allowed\n");
3689+
return std::make_unique<Dependence>(Src, Dst, getRuntimeAssumptions());
36883690
}
36893691
}
36903692

3693+
// Assert that we haven't added runtime assumptions when not allowed
3694+
assert(UnderRuntimeAssumptions || Assumptions.isAlwaysTrue());
3695+
36913696
establishNestingLevels(Src, Dst);
36923697
LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
36933698
LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n");
36943699

3695-
FullDependence Result(Src, Dst, SCEVUnionPredicate(Assume, *SE),
3696-
PossiblyLoopIndependent, CommonLevels);
3700+
FullDependence Result(Src, Dst, Assumptions, PossiblyLoopIndependent,
3701+
CommonLevels);
36973702
++TotalArrayPairs;
36983703

36993704
unsigned Pairs = 1;
@@ -4036,6 +4041,10 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
40364041
return nullptr;
40374042
}
40384043

4044+
// Assert that we haven't added runtime assumptions when not allowed
4045+
assert(UnderRuntimeAssumptions || Assumptions.isAlwaysTrue());
4046+
4047+
Result.Assumptions = getRuntimeAssumptions();
40394048
return std::make_unique<FullDependence>(std::move(Result));
40404049
}
40414050

llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ define i32 @alias_with_parametric_offset(ptr nocapture %A, i64 %n) {
3434
; CHECK-NEXT: Equal predicate: (zext i2 (trunc i64 %n to i2) to i64) == 0
3535
; CHECK-NEXT: Src: %0 = load i32, ptr %A, align 1 --> Dst: %0 = load i32, ptr %A, align 1
3636
; CHECK-NEXT: da analyze - none!
37-
; CHECK-NEXT: Runtime Assumptions:
38-
; CHECK-NEXT: Equal predicate: (zext i2 (trunc i64 %n to i2) to i64) == 0
3937
;
4038
entry:
4139
%arrayidx = getelementptr inbounds i8, ptr %A, i64 %n
@@ -56,7 +54,6 @@ define i32 @alias_with_parametric_expr(ptr nocapture %A, i64 %n, i64 %m) {
5654
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 1 --> Dst: %0 = load i32, ptr %arrayidx1, align 1
5755
; CHECK-NEXT: da analyze - none!
5856
; CHECK-NEXT: Runtime Assumptions:
59-
; CHECK-NEXT: Equal predicate: (zext i2 ((trunc i64 %m to i2) + (-2 * (trunc i64 %n to i2))) to i64) == 0
6057
; CHECK-NEXT: Equal predicate: (zext i2 (-2 + (trunc i64 %m to i2)) to i64) == 0
6158
;
6259
entry:
@@ -81,8 +78,6 @@ define i32 @gep_i8_vs_i32(ptr nocapture %A, i64 %n, i64 %m) {
8178
; CHECK-NEXT: Equal predicate: (zext i2 (trunc i64 %n to i2) to i64) == 0
8279
; CHECK-NEXT: Src: store i32 42, ptr %arrayidx1, align 4 --> Dst: store i32 42, ptr %arrayidx1, align 4
8380
; CHECK-NEXT: da analyze - none!
84-
; CHECK-NEXT: Runtime Assumptions:
85-
; CHECK-NEXT: Equal predicate: (zext i2 (trunc i64 %n to i2) to i64) == 0
8681
;
8782
entry:
8883
%arrayidx0 = getelementptr inbounds i8, ptr %A, i64 %n

llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ define void @test(ptr %A, ptr %B, i1 %arg, i32 %n, i32 %m) #0 align 2 {
5050
; CHECK-NEXT: Equal predicate: (8 * (zext i4 (trunc i32 %v1 to i4) to i32))<nuw><nsw> == 0
5151
; CHECK-NEXT: Src: %v32 = load <32 x i32>, ptr %v30, align 128 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
5252
; CHECK-NEXT: da analyze - consistent input [0 S S]!
53-
; CHECK-NEXT: Runtime Assumptions:
54-
; CHECK-NEXT: Equal predicate: (zext i7 (4 * (trunc i32 %v1 to i7) * (1 + (trunc i32 %n to i7))) to i32) == 0
55-
; CHECK-NEXT: Equal predicate: (8 * (zext i4 (trunc i32 %v1 to i4) to i32))<nuw><nsw> == 0
5653
;
5754
entry:
5855
%v1 = load i32, ptr %B, align 4

0 commit comments

Comments
 (0)