diff options
author | Philip Reames <preames@rivosinc.com> | 2023-05-01 10:16:32 -0700 |
---|---|---|
committer | Philip Reames <listmail@philipreames.com> | 2023-05-01 10:21:02 -0700 |
commit | 4343534a670f74e87841c0ca03d3189b72c04b1d (patch) | |
tree | 33cec847e0a34106dc3d64532fa26bf7e6b9bba2 /llvm/lib/Analysis/LoopAccessAnalysis.cpp | |
parent | d38d6ca1798b339a8d292e91f8e0708c334769ff (diff) | |
download | llvm-4343534a670f74e87841c0ca03d3189b72c04b1d.zip llvm-4343534a670f74e87841c0ca03d3189b72c04b1d.tar.gz llvm-4343534a670f74e87841c0ca03d3189b72c04b1d.tar.bz2 |
[LAA] Rework overflow checking in getPtrStride [nfc]
The previous code structure and comments were exceedingly confusing. I have multiple times looked at this code and suspected a bug. This time, I decided to take the time to reflow the code and comment out why it is correct.
The only suspect (to me) case left is that an underaligned access with a unit stride (in terms of the access type) might miss the undefined null pointer when wrapping. This is unlikely to be an issue for C/C++ code with real page sizes, so I'm not bothering to fully convince myself whether that case is correct or not.
Diffstat (limited to 'llvm/lib/Analysis/LoopAccessAnalysis.cpp')
-rw-r--r-- | llvm/lib/Analysis/LoopAccessAnalysis.cpp | 84 |
1 files changed, 35 insertions, 49 deletions
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp index 2707d0e..c2cdd3c 100644 --- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp +++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp @@ -1312,20 +1312,18 @@ void AccessAnalysis::processMemAccesses() { } } -static bool isInBoundsGep(Value *Ptr) { - if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr)) - return GEP->isInBounds(); - return false; -} - /// Return true if an AddRec pointer \p Ptr is unsigned non-wrapping, /// i.e. monotonically increasing/decreasing. static bool isNoWrapAddRec(Value *Ptr, const SCEVAddRecExpr *AR, PredicatedScalarEvolution &PSE, const Loop *L) { + // FIXME: This should probably only return true for NUW. if (AR->getNoWrapFlags(SCEV::NoWrapMask)) return true; + if (PSE.hasNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW)) + return true; + // Scalar evolution does not propagate the non-wrapping flags to values that // are derived from a non-wrapping induction variable because non-wrapping // could be flow-sensitive. @@ -1400,35 +1398,6 @@ std::optional<int64_t> llvm::getPtrStride(PredicatedScalarEvolution &PSE, return std::nullopt; } - // The address calculation must not wrap. Otherwise, a dependence could be - // inverted. - // An inbounds getelementptr that is a AddRec with a unit stride - // cannot wrap per definition. The unit stride requirement is checked later. - // An getelementptr without an inbounds attribute and unit stride would have - // to access the pointer value "0" which is undefined behavior in address - // space 0, therefore we can also vectorize this case. - unsigned AddrSpace = Ty->getPointerAddressSpace(); - bool IsInBoundsGEP = isInBoundsGep(Ptr); - bool IsNoWrapAddRec = !ShouldCheckWrap || - PSE.hasNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW) || - isNoWrapAddRec(Ptr, AR, PSE, Lp); - if (!IsNoWrapAddRec && !IsInBoundsGEP && - NullPointerIsDefined(Lp->getHeader()->getParent(), AddrSpace)) { - if (!Assume) { - LLVM_DEBUG( - dbgs() << "LAA: Bad stride - Pointer may wrap in the address space " - << *Ptr << " SCEV: " << *AR << "\n"); - return std::nullopt; - } - - PSE.setNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW); - IsNoWrapAddRec = true; - LLVM_DEBUG(dbgs() << "LAA: Pointer may wrap in the address space:\n" - << "LAA: Pointer: " << *Ptr << "\n" - << "LAA: SCEV: " << *AR << "\n" - << "LAA: Added an overflow assumption\n"); - } - // Check the step is constant. const SCEV *Step = AR->getStepRecurrence(*PSE.getSE()); @@ -1457,25 +1426,42 @@ std::optional<int64_t> llvm::getPtrStride(PredicatedScalarEvolution &PSE, if (Rem) return std::nullopt; - // If the SCEV could wrap but we have an inbounds gep with a unit stride we - // know we can't "wrap around the address space". In case of address space - // zero we know that this won't happen without triggering undefined behavior. - if (!IsNoWrapAddRec && Stride != 1 && Stride != -1 && - (IsInBoundsGEP || !NullPointerIsDefined(Lp->getHeader()->getParent(), - AddrSpace))) { - if (!Assume) - return std::nullopt; + if (!ShouldCheckWrap) + return Stride; + + // The address calculation must not wrap. Otherwise, a dependence could be + // inverted. + if (isNoWrapAddRec(Ptr, AR, PSE, Lp)) + return Stride; + + // An inbounds getelementptr that is a AddRec with a unit stride + // cannot wrap per definition. If it did, the result would be poison + // and any memory access dependent on it would be immediate UB + // when executed. + if (auto *GEP = dyn_cast<GetElementPtrInst>(Ptr); + GEP && GEP->isInBounds() && (Stride == 1 || Stride == -1)) + return Stride; + + // If the null pointer is undefined, then a access sequence which would + // otherwise access it can be assumed not to unsigned wrap. Note that this + // assumes the object in memory is aligned to the natural alignment. + unsigned AddrSpace = Ty->getPointerAddressSpace(); + if (!NullPointerIsDefined(Lp->getHeader()->getParent(), AddrSpace) && + (Stride == 1 || Stride == -1)) + return Stride; - // We can avoid this case by adding a run-time check. - LLVM_DEBUG(dbgs() << "LAA: Non unit strided pointer which is not either " - << "inbounds or in address space 0 may wrap:\n" + if (Assume) { + PSE.setNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW); + LLVM_DEBUG(dbgs() << "LAA: Pointer may wrap:\n" << "LAA: Pointer: " << *Ptr << "\n" << "LAA: SCEV: " << *AR << "\n" << "LAA: Added an overflow assumption\n"); - PSE.setNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW); + return Stride; } - - return Stride; + LLVM_DEBUG( + dbgs() << "LAA: Bad stride - Pointer may wrap in the address space " + << *Ptr << " SCEV: " << *AR << "\n"); + return std::nullopt; } std::optional<int> llvm::getPointersDiff(Type *ElemTyA, Value *PtrA, |