Skip to content

Commit 7f1423e

Browse files
authored
[DA][Delinearization] Move validation logic into Delinearization (llvm#169047)
This patch moves the validation logic of delinearization results from DA to Delinearization. Also call it in `printDelinearization` to test its behavior. The motivation is as follows: - Almost the same code exists in `tryDelinearizeFixedSize` and `tryDelinearizeParametricSize`. Consolidating it in Delinearization avoids code duplication. - Currently this validation logic is not well tested. Moving it to Delinearization allows us to write regression tests easily. This patch changes the test outputs and debug messages, but otherwise NFCI.
1 parent 8871e9e commit 7f1423e

19 files changed

+150
-130
lines changed

llvm/include/llvm/Analysis/Delinearization.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define LLVM_ANALYSIS_DELINEARIZATION_H
1818

1919
#include "llvm/IR/PassManager.h"
20+
#include "llvm/IR/Value.h"
2021

2122
namespace llvm {
2223
class raw_ostream;
@@ -140,6 +141,15 @@ bool delinearizeFixedSizeArray(ScalarEvolution &SE, const SCEV *Expr,
140141
SmallVectorImpl<const SCEV *> &Sizes,
141142
const SCEV *ElementSize);
142143

144+
/// Check that each subscript in \p Subscripts is within the corresponding size
145+
/// in \p Sizes. For the outermost dimension, the subscript being negative is
146+
/// allowed. If \p Ptr is not nullptr, it may be used to get information from
147+
/// the IR pointer value, which may help in the validation.
148+
bool validateDelinearizationResult(ScalarEvolution &SE,
149+
ArrayRef<const SCEV *> Sizes,
150+
ArrayRef<const SCEV *> Subscripts,
151+
const Value *Ptr = nullptr);
152+
143153
/// Gathers the individual index expressions from a GEP instruction.
144154
///
145155
/// This function optimistically assumes the GEP references into a fixed size

llvm/lib/Analysis/Delinearization.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,108 @@ bool llvm::delinearizeFixedSizeArray(ScalarEvolution &SE, const SCEV *Expr,
656656
return !Subscripts.empty();
657657
}
658658

659+
static bool isKnownNonNegative(ScalarEvolution *SE, const SCEV *S,
660+
const Value *Ptr) {
661+
bool Inbounds = false;
662+
if (auto *SrcGEP = dyn_cast<GetElementPtrInst>(Ptr))
663+
Inbounds = SrcGEP->isInBounds();
664+
if (Inbounds) {
665+
if (const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(S)) {
666+
if (AddRec->isAffine()) {
667+
// We know S is for Ptr, the operand on a load/store, so doesn't wrap.
668+
// If both parts are NonNegative, the end result will be NonNegative
669+
if (SE->isKnownNonNegative(AddRec->getStart()) &&
670+
SE->isKnownNonNegative(AddRec->getOperand(1)))
671+
return true;
672+
}
673+
}
674+
}
675+
676+
return SE->isKnownNonNegative(S);
677+
}
678+
679+
/// Compare to see if S is less than Size, using
680+
///
681+
/// isKnownNegative(S - Size)
682+
///
683+
/// with some extra checking if S is an AddRec and we can prove less-than using
684+
/// the loop bounds.
685+
static bool isKnownLessThan(ScalarEvolution *SE, const SCEV *S,
686+
const SCEV *Size) {
687+
// First unify to the same type
688+
auto *SType = dyn_cast<IntegerType>(S->getType());
689+
auto *SizeType = dyn_cast<IntegerType>(Size->getType());
690+
if (!SType || !SizeType)
691+
return false;
692+
Type *MaxType =
693+
(SType->getBitWidth() >= SizeType->getBitWidth()) ? SType : SizeType;
694+
S = SE->getTruncateOrZeroExtend(S, MaxType);
695+
Size = SE->getTruncateOrZeroExtend(Size, MaxType);
696+
697+
auto CollectUpperBound = [&](const Loop *L, Type *T) -> const SCEV * {
698+
if (SE->hasLoopInvariantBackedgeTakenCount(L)) {
699+
const SCEV *UB = SE->getBackedgeTakenCount(L);
700+
return SE->getTruncateOrZeroExtend(UB, T);
701+
}
702+
return nullptr;
703+
};
704+
705+
auto CheckAddRecBECount = [&]() {
706+
const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(S);
707+
if (!AddRec || !AddRec->isAffine() || !AddRec->hasNoSignedWrap())
708+
return false;
709+
const SCEV *BECount = CollectUpperBound(AddRec->getLoop(), MaxType);
710+
// If the BTC cannot be computed, check the base case for S.
711+
if (!BECount || isa<SCEVCouldNotCompute>(BECount))
712+
return false;
713+
const SCEV *Start = AddRec->getStart();
714+
const SCEV *Step = AddRec->getStepRecurrence(*SE);
715+
const SCEV *End = AddRec->evaluateAtIteration(BECount, *SE);
716+
const SCEV *Diff0 = SE->getMinusSCEV(Start, Size);
717+
const SCEV *Diff1 = SE->getMinusSCEV(End, Size);
718+
719+
// If the value of Step is non-negative and the AddRec is non-wrap, it
720+
// reaches its maximum at the last iteration. So it's enouth to check
721+
// whether End - Size is negative.
722+
if (SE->isKnownNonNegative(Step) && SE->isKnownNegative(Diff1))
723+
return true;
724+
725+
// If the value of Step is non-positive and the AddRec is non-wrap, the
726+
// initial value is its maximum.
727+
if (SE->isKnownNonPositive(Step) && SE->isKnownNegative(Diff0))
728+
return true;
729+
730+
// Even if we don't know the sign of Step, either Start or End must be
731+
// the maximum value of the AddRec since it is non-wrap.
732+
if (SE->isKnownNegative(Diff0) && SE->isKnownNegative(Diff1))
733+
return true;
734+
735+
return false;
736+
};
737+
738+
if (CheckAddRecBECount())
739+
return true;
740+
741+
// Check using normal isKnownNegative
742+
const SCEV *LimitedBound = SE->getMinusSCEV(S, Size);
743+
return SE->isKnownNegative(LimitedBound);
744+
}
745+
746+
bool llvm::validateDelinearizationResult(ScalarEvolution &SE,
747+
ArrayRef<const SCEV *> Sizes,
748+
ArrayRef<const SCEV *> Subscripts,
749+
const Value *Ptr) {
750+
for (size_t I = 1; I < Sizes.size(); ++I) {
751+
const SCEV *Size = Sizes[I - 1];
752+
const SCEV *Subscript = Subscripts[I];
753+
if (!isKnownNonNegative(&SE, Subscript, Ptr))
754+
return false;
755+
if (!isKnownLessThan(&SE, Subscript, Size))
756+
return false;
757+
}
758+
return true;
759+
}
760+
659761
bool llvm::getIndexExpressionsFromGEP(ScalarEvolution &SE,
660762
const GetElementPtrInst *GEP,
661763
SmallVectorImpl<const SCEV *> &Subscripts,
@@ -766,6 +868,11 @@ void printDelinearization(raw_ostream &O, Function *F, LoopInfo *LI,
766868
for (int i = 0; i < Size; i++)
767869
O << "[" << *Subscripts[i] << "]";
768870
O << "\n";
871+
872+
bool IsValid = validateDelinearizationResult(
873+
*SE, Sizes, Subscripts, getLoadStorePointerOperand(&Inst));
874+
O << "Delinearization validation: " << (IsValid ? "Succeeded" : "Failed")
875+
<< "\n";
769876
}
770877
}
771878

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 4 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,83 +1176,6 @@ bool DependenceInfo::isKnownPredicate(ICmpInst::Predicate Pred, const SCEV *X,
11761176
}
11771177
}
11781178

1179-
/// Compare to see if S is less than Size, using
1180-
///
1181-
/// isKnownNegative(S - Size)
1182-
///
1183-
/// with some extra checking if S is an AddRec and we can prove less-than using
1184-
/// the loop bounds.
1185-
bool DependenceInfo::isKnownLessThan(const SCEV *S, const SCEV *Size) const {
1186-
// First unify to the same type
1187-
auto *SType = dyn_cast<IntegerType>(S->getType());
1188-
auto *SizeType = dyn_cast<IntegerType>(Size->getType());
1189-
if (!SType || !SizeType)
1190-
return false;
1191-
Type *MaxType =
1192-
(SType->getBitWidth() >= SizeType->getBitWidth()) ? SType : SizeType;
1193-
S = SE->getTruncateOrZeroExtend(S, MaxType);
1194-
Size = SE->getTruncateOrZeroExtend(Size, MaxType);
1195-
1196-
auto CheckAddRecBECount = [&]() {
1197-
const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(S);
1198-
if (!AddRec || !AddRec->isAffine() || !AddRec->hasNoSignedWrap())
1199-
return false;
1200-
const SCEV *BECount = collectUpperBound(AddRec->getLoop(), MaxType);
1201-
// If the BTC cannot be computed, check the base case for S.
1202-
if (!BECount || isa<SCEVCouldNotCompute>(BECount))
1203-
return false;
1204-
const SCEV *Start = AddRec->getStart();
1205-
const SCEV *Step = AddRec->getStepRecurrence(*SE);
1206-
const SCEV *End = AddRec->evaluateAtIteration(BECount, *SE);
1207-
const SCEV *Diff0 = SE->getMinusSCEV(Start, Size);
1208-
const SCEV *Diff1 = SE->getMinusSCEV(End, Size);
1209-
1210-
// If the value of Step is non-negative and the AddRec is non-wrap, it
1211-
// reaches its maximum at the last iteration. So it's enouth to check
1212-
// whether End - Size is negative.
1213-
if (SE->isKnownNonNegative(Step) && SE->isKnownNegative(Diff1))
1214-
return true;
1215-
1216-
// If the value of Step is non-positive and the AddRec is non-wrap, the
1217-
// initial value is its maximum.
1218-
if (SE->isKnownNonPositive(Step) && SE->isKnownNegative(Diff0))
1219-
return true;
1220-
1221-
// Even if we don't know the sign of Step, either Start or End must be
1222-
// the maximum value of the AddRec since it is non-wrap.
1223-
if (SE->isKnownNegative(Diff0) && SE->isKnownNegative(Diff1))
1224-
return true;
1225-
1226-
return false;
1227-
};
1228-
1229-
if (CheckAddRecBECount())
1230-
return true;
1231-
1232-
// Check using normal isKnownNegative
1233-
const SCEV *LimitedBound = SE->getMinusSCEV(S, Size);
1234-
return SE->isKnownNegative(LimitedBound);
1235-
}
1236-
1237-
bool DependenceInfo::isKnownNonNegative(const SCEV *S, const Value *Ptr) const {
1238-
bool Inbounds = false;
1239-
if (auto *SrcGEP = dyn_cast<GetElementPtrInst>(Ptr))
1240-
Inbounds = SrcGEP->isInBounds();
1241-
if (Inbounds) {
1242-
if (const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(S)) {
1243-
if (AddRec->isAffine()) {
1244-
// We know S is for Ptr, the operand on a load/store, so doesn't wrap.
1245-
// If both parts are NonNegative, the end result will be NonNegative
1246-
if (SE->isKnownNonNegative(AddRec->getStart()) &&
1247-
SE->isKnownNonNegative(AddRec->getOperand(1)))
1248-
return true;
1249-
}
1250-
}
1251-
}
1252-
1253-
return SE->isKnownNonNegative(S);
1254-
}
1255-
12561179
// All subscripts are all the same type.
12571180
// Loop bound may be smaller (e.g., a char).
12581181
// Should zero extend loop bound, since it's always >= 0.
@@ -3360,35 +3283,8 @@ bool DependenceInfo::tryDelinearizeFixedSize(
33603283
// iff the subscripts are positive and are less than the range of the
33613284
// dimension.
33623285
if (!DisableDelinearizationChecks) {
3363-
auto AllIndicesInRange = [&](ArrayRef<const SCEV *> DimensionSizes,
3364-
SmallVectorImpl<const SCEV *> &Subscripts,
3365-
Value *Ptr) {
3366-
size_t SSize = Subscripts.size();
3367-
for (size_t I = 1; I < SSize; ++I) {
3368-
const SCEV *S = Subscripts[I];
3369-
if (!isKnownNonNegative(S, Ptr)) {
3370-
LLVM_DEBUG({
3371-
dbgs() << "Check failed: !isKnownNonNegative(S, Ptr)\n";
3372-
dbgs() << " S: " << *S << "\n" << " Ptr: " << *Ptr << "\n";
3373-
});
3374-
return false;
3375-
}
3376-
const SCEV *Range = DimensionSizes[I - 1];
3377-
if (!isKnownLessThan(S, Range)) {
3378-
LLVM_DEBUG({
3379-
dbgs() << "Check failed: !isKnownLessThan(S, Range)\n";
3380-
dbgs() << " S: " << *S << "\n"
3381-
<< " Range: " << *Range << "\n";
3382-
});
3383-
return false;
3384-
}
3385-
}
3386-
return true;
3387-
};
3388-
3389-
if (!AllIndicesInRange(SrcSizes, SrcSubscripts, SrcPtr) ||
3390-
!AllIndicesInRange(DstSizes, DstSubscripts, DstPtr)) {
3391-
LLVM_DEBUG(dbgs() << "Check failed: AllIndicesInRange.\n");
3286+
if (!validateDelinearizationResult(*SE, SrcSizes, SrcSubscripts, SrcPtr) ||
3287+
!validateDelinearizationResult(*SE, DstSizes, DstSubscripts, DstPtr)) {
33923288
SrcSubscripts.clear();
33933289
DstSubscripts.clear();
33943290
return false;
@@ -3446,38 +3342,16 @@ bool DependenceInfo::tryDelinearizeParametricSize(
34463342
SrcSubscripts.size() != DstSubscripts.size())
34473343
return false;
34483344

3449-
size_t Size = SrcSubscripts.size();
3450-
34513345
// Statically check that the array bounds are in-range. The first subscript we
34523346
// don't have a size for and it cannot overflow into another subscript, so is
34533347
// always safe. The others need to be 0 <= subscript[i] < bound, for both src
34543348
// and dst.
34553349
// FIXME: It may be better to record these sizes and add them as constraints
34563350
// to the dependency checks.
34573351
if (!DisableDelinearizationChecks)
3458-
for (size_t I = 1; I < Size; ++I) {
3459-
bool SNN = isKnownNonNegative(SrcSubscripts[I], SrcPtr);
3460-
bool DNN = isKnownNonNegative(DstSubscripts[I], DstPtr);
3461-
bool SLT = isKnownLessThan(SrcSubscripts[I], Sizes[I - 1]);
3462-
bool DLT = isKnownLessThan(DstSubscripts[I], Sizes[I - 1]);
3463-
if (SNN && DNN && SLT && DLT)
3464-
continue;
3465-
3466-
LLVM_DEBUG({
3467-
dbgs() << "Delinearization checks failed: can't prove the following\n";
3468-
if (!SNN)
3469-
dbgs() << " isKnownNonNegative(" << *SrcSubscripts[I] << ")\n";
3470-
if (!DNN)
3471-
dbgs() << " isKnownNonNegative(" << *DstSubscripts[I] << ")\n";
3472-
if (!SLT)
3473-
dbgs() << " isKnownLessThan(" << *SrcSubscripts[I] << ", "
3474-
<< *Sizes[I - 1] << ")\n";
3475-
if (!DLT)
3476-
dbgs() << " isKnownLessThan(" << *DstSubscripts[I] << ", "
3477-
<< *Sizes[I - 1] << ")\n";
3478-
});
3352+
if (!validateDelinearizationResult(*SE, Sizes, SrcSubscripts, SrcPtr) ||
3353+
!validateDelinearizationResult(*SE, Sizes, DstSubscripts, DstPtr))
34793354
return false;
3480-
}
34813355

34823356
return true;
34833357
}

llvm/test/Analysis/Delinearization/a.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ define void @foo(i64 %n, i64 %m, i64 %o, ptr nocapture %A) #0 {
1515
; CHECK-NEXT: Base offset: %A
1616
; CHECK-NEXT: ArrayDecl[UnknownSize][%m][%o] with elements of 4 bytes.
1717
; CHECK-NEXT: ArrayRef[{3,+,2}<nuw><%for.i>][{-4,+,3}<nw><%for.j>][{7,+,5}<nw><%for.k>]
18+
; CHECK-NEXT: Delinearization validation: Failed
1819
;
1920
entry:
2021
%cmp32 = icmp sgt i64 %n, 0

llvm/test/Analysis/Delinearization/constant_functions_multi_dim.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ define void @mat_mul(ptr %C, ptr %A, ptr %B, i64 %N) !kernel_arg_addr_space !2 !
1111
; CHECK-NEXT: Base offset: %A
1212
; CHECK-NEXT: ArrayDecl[UnknownSize][%N] with elements of 4 bytes.
1313
; CHECK-NEXT: ArrayRef[%call][{0,+,1}<nuw><nsw><%for.inc>]
14+
; CHECK-NEXT: Delinearization validation: Succeeded
1415
; CHECK-EMPTY:
1516
; CHECK-NEXT: Inst: %tmp5 = load float, ptr %arrayidx4, align 4
1617
; CHECK-NEXT: AccessFunction: {(4 * %call1),+,(4 * %N)}<%for.inc>
1718
; CHECK-NEXT: Base offset: %B
1819
; CHECK-NEXT: ArrayDecl[UnknownSize][%N] with elements of 4 bytes.
1920
; CHECK-NEXT: ArrayRef[{0,+,1}<nuw><nsw><%for.inc>][%call1]
21+
; CHECK-NEXT: Delinearization validation: Failed
2022
;
2123
entry:
2224
br label %entry.split

llvm/test/Analysis/Delinearization/divide_by_one.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ define void @test(ptr nocapture %dst, i32 %stride, i32 %bs) {
1818
; CHECK-NEXT: Base offset: %dst
1919
; CHECK-NEXT: ArrayDecl[UnknownSize][%stride] with elements of 1 bytes.
2020
; CHECK-NEXT: ArrayRef[{(1 + %bs),+,-1}<nw><%for.cond1.preheader>][{-1,+,1}<nw><%for.body3>]
21+
; CHECK-NEXT: Delinearization validation: Failed
2122
; CHECK-EMPTY:
2223
; CHECK-NEXT: Inst: store i8 %0, ptr %arrayidx7, align 1
2324
; CHECK-NEXT: AccessFunction: {{\{\{}}(%stride * %bs),+,(-1 * %stride)}<%for.cond1.preheader>,+,1}<nsw><%for.body3>
2425
; CHECK-NEXT: Base offset: %dst
2526
; CHECK-NEXT: ArrayDecl[UnknownSize][%stride] with elements of 1 bytes.
2627
; CHECK-NEXT: ArrayRef[{%bs,+,-1}<nsw><%for.cond1.preheader>][{0,+,1}<nuw><nsw><%for.body3>]
28+
; CHECK-NEXT: Delinearization validation: Failed
2729
;
2830
entry:
2931
%cmp20 = icmp sgt i32 %bs, -1

0 commit comments

Comments
 (0)