Skip to content

Commit 4343534

Browse files
committed
[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.
1 parent d38d6ca commit 4343534

File tree

2 files changed

+39
-49
lines changed

2 files changed

+39
-49
lines changed

llvm/include/llvm/Analysis/LoopAccessAnalysis.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,10 @@ const SCEV *replaceSymbolicStrideSCEV(PredicatedScalarEvolution &PSE,
730730
/// to \p PtrToStride and therefore add further predicates to \p PSE.
731731
/// The \p Assume parameter indicates if we are allowed to make additional
732732
/// run-time assumptions.
733+
///
734+
/// Note that the analysis results are defined if-and-only-if the original
735+
/// memory access was defined. If that access was dead, or UB, then the
736+
/// result of this function is undefined.
733737
std::optional<int64_t>
734738
getPtrStride(PredicatedScalarEvolution &PSE, Type *AccessTy, Value *Ptr,
735739
const Loop *Lp,

llvm/lib/Analysis/LoopAccessAnalysis.cpp

Lines changed: 35 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,20 +1312,18 @@ void AccessAnalysis::processMemAccesses() {
13121312
}
13131313
}
13141314

1315-
static bool isInBoundsGep(Value *Ptr) {
1316-
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr))
1317-
return GEP->isInBounds();
1318-
return false;
1319-
}
1320-
13211315
/// Return true if an AddRec pointer \p Ptr is unsigned non-wrapping,
13221316
/// i.e. monotonically increasing/decreasing.
13231317
static bool isNoWrapAddRec(Value *Ptr, const SCEVAddRecExpr *AR,
13241318
PredicatedScalarEvolution &PSE, const Loop *L) {
1319+
13251320
// FIXME: This should probably only return true for NUW.
13261321
if (AR->getNoWrapFlags(SCEV::NoWrapMask))
13271322
return true;
13281323

1324+
if (PSE.hasNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW))
1325+
return true;
1326+
13291327
// Scalar evolution does not propagate the non-wrapping flags to values that
13301328
// are derived from a non-wrapping induction variable because non-wrapping
13311329
// could be flow-sensitive.
@@ -1400,35 +1398,6 @@ std::optional<int64_t> llvm::getPtrStride(PredicatedScalarEvolution &PSE,
14001398
return std::nullopt;
14011399
}
14021400

1403-
// The address calculation must not wrap. Otherwise, a dependence could be
1404-
// inverted.
1405-
// An inbounds getelementptr that is a AddRec with a unit stride
1406-
// cannot wrap per definition. The unit stride requirement is checked later.
1407-
// An getelementptr without an inbounds attribute and unit stride would have
1408-
// to access the pointer value "0" which is undefined behavior in address
1409-
// space 0, therefore we can also vectorize this case.
1410-
unsigned AddrSpace = Ty->getPointerAddressSpace();
1411-
bool IsInBoundsGEP = isInBoundsGep(Ptr);
1412-
bool IsNoWrapAddRec = !ShouldCheckWrap ||
1413-
PSE.hasNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW) ||
1414-
isNoWrapAddRec(Ptr, AR, PSE, Lp);
1415-
if (!IsNoWrapAddRec && !IsInBoundsGEP &&
1416-
NullPointerIsDefined(Lp->getHeader()->getParent(), AddrSpace)) {
1417-
if (!Assume) {
1418-
LLVM_DEBUG(
1419-
dbgs() << "LAA: Bad stride - Pointer may wrap in the address space "
1420-
<< *Ptr << " SCEV: " << *AR << "\n");
1421-
return std::nullopt;
1422-
}
1423-
1424-
PSE.setNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW);
1425-
IsNoWrapAddRec = true;
1426-
LLVM_DEBUG(dbgs() << "LAA: Pointer may wrap in the address space:\n"
1427-
<< "LAA: Pointer: " << *Ptr << "\n"
1428-
<< "LAA: SCEV: " << *AR << "\n"
1429-
<< "LAA: Added an overflow assumption\n");
1430-
}
1431-
14321401
// Check the step is constant.
14331402
const SCEV *Step = AR->getStepRecurrence(*PSE.getSE());
14341403

@@ -1457,25 +1426,42 @@ std::optional<int64_t> llvm::getPtrStride(PredicatedScalarEvolution &PSE,
14571426
if (Rem)
14581427
return std::nullopt;
14591428

1460-
// If the SCEV could wrap but we have an inbounds gep with a unit stride we
1461-
// know we can't "wrap around the address space". In case of address space
1462-
// zero we know that this won't happen without triggering undefined behavior.
1463-
if (!IsNoWrapAddRec && Stride != 1 && Stride != -1 &&
1464-
(IsInBoundsGEP || !NullPointerIsDefined(Lp->getHeader()->getParent(),
1465-
AddrSpace))) {
1466-
if (!Assume)
1467-
return std::nullopt;
1429+
if (!ShouldCheckWrap)
1430+
return Stride;
1431+
1432+
// The address calculation must not wrap. Otherwise, a dependence could be
1433+
// inverted.
1434+
if (isNoWrapAddRec(Ptr, AR, PSE, Lp))
1435+
return Stride;
1436+
1437+
// An inbounds getelementptr that is a AddRec with a unit stride
1438+
// cannot wrap per definition. If it did, the result would be poison
1439+
// and any memory access dependent on it would be immediate UB
1440+
// when executed.
1441+
if (auto *GEP = dyn_cast<GetElementPtrInst>(Ptr);
1442+
GEP && GEP->isInBounds() && (Stride == 1 || Stride == -1))
1443+
return Stride;
1444+
1445+
// If the null pointer is undefined, then a access sequence which would
1446+
// otherwise access it can be assumed not to unsigned wrap. Note that this
1447+
// assumes the object in memory is aligned to the natural alignment.
1448+
unsigned AddrSpace = Ty->getPointerAddressSpace();
1449+
if (!NullPointerIsDefined(Lp->getHeader()->getParent(), AddrSpace) &&
1450+
(Stride == 1 || Stride == -1))
1451+
return Stride;
14681452

1469-
// We can avoid this case by adding a run-time check.
1470-
LLVM_DEBUG(dbgs() << "LAA: Non unit strided pointer which is not either "
1471-
<< "inbounds or in address space 0 may wrap:\n"
1453+
if (Assume) {
1454+
PSE.setNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW);
1455+
LLVM_DEBUG(dbgs() << "LAA: Pointer may wrap:\n"
14721456
<< "LAA: Pointer: " << *Ptr << "\n"
14731457
<< "LAA: SCEV: " << *AR << "\n"
14741458
<< "LAA: Added an overflow assumption\n");
1475-
PSE.setNoOverflow(Ptr, SCEVWrapPredicate::IncrementNUSW);
1459+
return Stride;
14761460
}
1477-
1478-
return Stride;
1461+
LLVM_DEBUG(
1462+
dbgs() << "LAA: Bad stride - Pointer may wrap in the address space "
1463+
<< *Ptr << " SCEV: " << *AR << "\n");
1464+
return std::nullopt;
14791465
}
14801466

14811467
std::optional<int> llvm::getPointersDiff(Type *ElemTyA, Value *PtrA,

0 commit comments

Comments
 (0)