diff --git a/llvm/include/llvm/Analysis/DependenceAnalysis.h b/llvm/include/llvm/Analysis/DependenceAnalysis.h index f66c79d915665..0db8f5d8eca02 100644 --- a/llvm/include/llvm/Analysis/DependenceAnalysis.h +++ b/llvm/include/llvm/Analysis/DependenceAnalysis.h @@ -598,6 +598,12 @@ class DependenceInfo { /// returns NULL. const SCEVConstant *collectConstantUpperBound(const Loop *l, Type *T) const; + /// getNonRedundantAssumptions - Remove redundant assumptions from the + /// collection and return a SCEVUnionPredicate with unique assumptions. + /// This ensures that each assumption is only present once and that + /// stronger assumptions imply weaker ones. + SCEVUnionPredicate getNonRedundantAssumptions() const; + /// classifyPair - Examines the subscript pair (the Src and Dst SCEVs) /// and classifies it as either ZIV, SIV, RDIV, MIV, or Nonlinear. /// Collects the associated loops in a set. diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp index f33e04e804e3d..f94ff8c6a5f55 100644 --- a/llvm/lib/Analysis/DependenceAnalysis.cpp +++ b/llvm/lib/Analysis/DependenceAnalysis.cpp @@ -1282,7 +1282,28 @@ bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst, Result.DV[Level].Direction &= Dependence::DVEntry::EQ; ++StrongSIVsuccesses; } else if (Delta->isZero()) { - // since 0/X == 0 + // Check if coefficient could be zero. If so, 0/0 is undefined and we + // cannot conclude that only same-iteration dependencies exist. + // When coeff=0, all iterations access the same location. + if (isa(Coeff) && !SE->isKnownNonZero(Coeff)) { + // Use SCEV range analysis to prove coefficient > 0 in loop context. + const SCEV *Zero = SE->getZero(Coeff->getType()); + + // Ask SCEV's range analysis if it can prove Coeff > Zero + if (SE->isKnownPredicate(ICmpInst::ICMP_SGT, Coeff, Zero)) { + LLVM_DEBUG( + dbgs() + << "\t Coefficient proven positive by SCEV range analysis\n"); + } else { + // Cannot prove at compile time, add runtime assumption + const SCEVPredicate *Pred = + SE->getComparePredicate(ICmpInst::ICMP_SGT, Coeff, Zero); + const_cast(this)->Assumptions.push_back(Pred); + LLVM_DEBUG(dbgs() << "\t Added runtime assumption: " << *Coeff + << " > 0\n"); + } + } + // since 0/X == 0 (where X is known non-zero) Result.DV[Level].Distance = Delta; NewConstraint.setDistance(Delta, CurLoop); Result.DV[Level].Direction &= Dependence::DVEntry::EQ; @@ -3548,6 +3569,37 @@ SCEVUnionPredicate DependenceInfo::getRuntimeAssumptions() const { return SCEVUnionPredicate(Assumptions, *SE); } +// getNonRedundantAssumptions - Remove redundant assumptions from the collection +// and return a SCEVUnionPredicate with the unique assumptions. This ensures +// that each assumption is only present once and that stronger assumptions imply +// weaker ones, avoiding unnecessary runtime checks. +SCEVUnionPredicate DependenceInfo::getNonRedundantAssumptions() const { + SmallVector UniqueAssumptions; + + for (const SCEVPredicate *P : Assumptions) { + bool Implied = false; + for (const SCEVPredicate *Existing : UniqueAssumptions) { + if (Existing->implies(P, *SE)) { + Implied = true; + break; + } + } + if (!Implied) { + auto I = UniqueAssumptions.begin(); + while (I != UniqueAssumptions.end()) { + if (P->implies(*I, *SE)) { + I = UniqueAssumptions.erase(I); + } else { + ++I; + } + } + UniqueAssumptions.push_back(P); + } + } + + return SCEVUnionPredicate(UniqueAssumptions, *SE); +} + // depends - // Returns NULL if there is no dependence. // Otherwise, return a Dependence with as many details as possible. @@ -3562,7 +3614,6 @@ SCEVUnionPredicate DependenceInfo::getRuntimeAssumptions() const { std::unique_ptr DependenceInfo::depends(Instruction *Src, Instruction *Dst, bool UnderRuntimeAssumptions) { - SmallVector Assume; bool PossiblyLoopIndependent = true; if (Src == Dst) PossiblyLoopIndependent = false; @@ -3574,8 +3625,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, if (!isLoadOrStore(Src) || !isLoadOrStore(Dst)) { // can only analyze simple loads and stores, i.e., no calls, invokes, etc. LLVM_DEBUG(dbgs() << "can only handle simple loads and stores\n"); - return std::make_unique(Src, Dst, - SCEVUnionPredicate(Assume, *SE)); + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); } const MemoryLocation &DstLoc = MemoryLocation::get(Dst); @@ -3586,8 +3636,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, case AliasResult::PartialAlias: // cannot analyse objects if we don't understand their aliasing. LLVM_DEBUG(dbgs() << "can't analyze may or partial alias\n"); - return std::make_unique(Src, Dst, - SCEVUnionPredicate(Assume, *SE)); + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); case AliasResult::NoAlias: // If the objects noalias, they are distinct, accesses are independent. LLVM_DEBUG(dbgs() << "no alias\n"); @@ -3601,8 +3650,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, // The dependence test gets confused if the size of the memory accesses // differ. LLVM_DEBUG(dbgs() << "can't analyze must alias with different sizes\n"); - return std::make_unique(Src, Dst, - SCEVUnionPredicate(Assume, *SE)); + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); } Value *SrcPtr = getLoadStorePointerOperand(Src); @@ -3621,8 +3669,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, // We check this upfront so we don't crash in cases where getMinusSCEV() // returns a SCEVCouldNotCompute. LLVM_DEBUG(dbgs() << "can't analyze SCEV with different pointer base\n"); - return std::make_unique(Src, Dst, - SCEVUnionPredicate(Assume, *SE)); + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); } // Even if the base pointers are the same, they may not be loop-invariant. It @@ -3634,8 +3681,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, if (!isLoopInvariant(SrcBase, SrcLoop) || !isLoopInvariant(DstBase, DstLoop)) { LLVM_DEBUG(dbgs() << "The base pointer is not loop invariant.\n"); - return std::make_unique(Src, Dst, - SCEVUnionPredicate(Assume, *SE)); + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); } uint64_t EltSize = SrcLoc.Size.toRaw(); @@ -3643,34 +3689,22 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, const SCEV *DstEv = SE->getMinusSCEV(DstSCEV, DstBase); // Check that memory access offsets are multiples of element sizes. - if (!SE->isKnownMultipleOf(SrcEv, EltSize, Assume) || - !SE->isKnownMultipleOf(DstEv, EltSize, Assume)) { + if (!SE->isKnownMultipleOf(SrcEv, EltSize, Assumptions) || + !SE->isKnownMultipleOf(DstEv, EltSize, Assumptions)) { LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n"); - return std::make_unique(Src, Dst, - SCEVUnionPredicate(Assume, *SE)); + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); } - if (!Assume.empty()) { - if (!UnderRuntimeAssumptions) - return std::make_unique(Src, Dst, - SCEVUnionPredicate(Assume, *SE)); - // Add non-redundant assumptions. - unsigned N = Assumptions.size(); - for (const SCEVPredicate *P : Assume) { - bool Implied = false; - for (unsigned I = 0; I != N && !Implied; I++) - if (Assumptions[I]->implies(P, *SE)) - Implied = true; - if (!Implied) - Assumptions.push_back(P); - } - } + // If runtime assumptions were added but not allowed, return confused + // dependence. + if (!UnderRuntimeAssumptions && !Assumptions.empty()) + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); establishNestingLevels(Src, Dst); LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n"); LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n"); - FullDependence Result(Src, Dst, SCEVUnionPredicate(Assume, *SE), + FullDependence Result(Src, Dst, SCEVUnionPredicate(Assumptions, *SE), PossiblyLoopIndependent, CommonLevels); ++TotalArrayPairs; @@ -4014,6 +4048,12 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, return nullptr; } + // If runtime assumptions were added but not allowed, return confused + // dependence. + if (!UnderRuntimeAssumptions && !Assumptions.empty()) + return std::make_unique(Src, Dst, getNonRedundantAssumptions()); + + Result.Assumptions = getNonRedundantAssumptions(); return std::make_unique(std::move(Result)); } diff --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll index 8f94a455d3724..c7b9865329214 100644 --- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll +++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll @@ -649,8 +649,13 @@ define void @coeff_may_negative(ptr %a, i32 %k) { ; CHECK-NEXT: da analyze - none! ; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 ; CHECK-NEXT: da analyze - output [*|<]! +; CHECK-NEXT: Runtime Assumptions: +; CHECK-NEXT: Compare predicate: %k sgt) 0 ; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 ; CHECK-NEXT: da analyze - none! +; CHECK-NEXT: Runtime Assumptions: +; CHECK-NEXT: Compare predicate: %k sgt) 0 +; CHECK-NEXT: Compare predicate: %k sgt) 0 ; entry: br label %loop @@ -688,8 +693,13 @@ define void @coeff_positive(ptr %a, i32 %k) { ; CHECK-NEXT: da analyze - none! ; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 ; CHECK-NEXT: da analyze - output [*|<]! +; CHECK-NEXT: Runtime Assumptions: +; CHECK-NEXT: Compare predicate: %k sgt) 0 ; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 ; CHECK-NEXT: da analyze - none! +; CHECK-NEXT: Runtime Assumptions: +; CHECK-NEXT: Compare predicate: %k sgt) 0 +; CHECK-NEXT: Compare predicate: %k sgt) 0 ; entry: br label %loop diff --git a/llvm/test/Analysis/DependenceAnalysis/zero-coefficient.ll b/llvm/test/Analysis/DependenceAnalysis/zero-coefficient.ll new file mode 100644 index 0000000000000..d6ad924da722b --- /dev/null +++ b/llvm/test/Analysis/DependenceAnalysis/zero-coefficient.ll @@ -0,0 +1,30 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -disable-output "-passes=print" 2>&1 | FileCheck %s + +; Test case for GitHub issue #149991: Strong SIV test with symbolic coefficient +; that could be zero. Fixed using runtime assumptions: assume coefficient > 0. + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + +define void @test_zero_coefficient(ptr %a, i64 %k) { +; CHECK-LABEL: 'test_zero_coefficient' +; CHECK-NEXT: Src: store i8 42, ptr %idx, align 1 --> Dst: store i8 42, ptr %idx, align 1 +; CHECK-NEXT: da analyze - none! +; CHECK-NEXT: Runtime Assumptions: +; CHECK-NEXT: Compare predicate: %k sgt) 0 +; +entry: + br label %loop + +loop: + %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] + %subscript = mul i64 %i, %k ; When %k=0, all iterations access %a[0] + %idx = getelementptr i8, ptr %a, i64 %subscript + store i8 42, ptr %idx + %i.next = add i64 %i, 1 + %cond.exit = icmp eq i64 %i.next, 100 + br i1 %cond.exit, label %exit, label %loop + +exit: + ret void +}