Skip to content

Commit 60ba4e6

Browse files
committed
record runtime assumptions for parametric expressions
1 parent 0eae7f0 commit 60ba4e6

File tree

6 files changed

+114
-102
lines changed

6 files changed

+114
-102
lines changed

llvm/include/llvm/Analysis/DependenceAnalysis.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ namespace llvm {
5252
class ScalarEvolution;
5353
class SCEV;
5454
class SCEVConstant;
55+
class SCEVPredicate;
56+
class SCEVUnionPredicate;
5557
class raw_ostream;
5658

5759
/// Dependence - This class represents a dependence between two memory
@@ -349,12 +351,14 @@ namespace llvm {
349351
const SCEV *getSplitIteration(const Dependence &Dep, unsigned Level);
350352

351353
Function *getFunction() const { return F; }
354+
SCEVUnionPredicate getRuntimeAssumptions();
352355

353356
private:
354357
AAResults *AA;
355358
ScalarEvolution *SE;
356359
LoopInfo *LI;
357360
Function *F;
361+
SmallVector<const SCEVPredicate *, 4> Assumptions;
358362

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

llvm/include/llvm/Analysis/ScalarEvolution.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,12 +1044,10 @@ class ScalarEvolution {
10441044
bool isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero = false,
10451045
bool OrNegative = false);
10461046

1047-
/// Check that memory access offsets in V are multiples of array element size
1048-
/// EltSize. Param records the first parametric expression. If the scalar
1049-
/// evolution V contains two or more parameters, we check that the subsequent
1050-
/// parametric expressions are multiples of the first parametric expression
1051-
/// Param.
1052-
bool isKnownMultipleOf(const SCEV *V, const SCEV *&Param, uint64_t EltSize);
1047+
/// Check that memory access offsets in S are multiples of M. Assumptions
1048+
/// records the runtime predicates under which S is a multiple of M.
1049+
bool isKnownMultipleOf(const SCEV *S, uint64_t M,
1050+
SmallVectorImpl<const SCEVPredicate *> &Assumptions);
10531051

10541052
/// Splits SCEV expression \p S into two SCEVs. One of them is obtained from
10551053
/// \p S by substitution of all AddRec sub-expression related to loop \p L

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ static void dumpExampleDependence(raw_ostream &OS, DependenceInfo *DA,
206206
}
207207
}
208208
}
209+
SCEVUnionPredicate Assumptions = DA->getRuntimeAssumptions();
210+
if (!Assumptions.isAlwaysTrue()) {
211+
OS << "Runtime Assumptions:\n";
212+
Assumptions.print(OS, 0);
213+
}
209214
}
210215

211216
void DependenceAnalysisWrapperPass::print(raw_ostream &OS,
@@ -3569,6 +3574,10 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
35693574
Inv.invalidate<LoopAnalysis>(F, PA);
35703575
}
35713576

3577+
SCEVUnionPredicate DependenceInfo::getRuntimeAssumptions() {
3578+
return SCEVUnionPredicate(Assumptions, *SE);
3579+
}
3580+
35723581
// depends -
35733582
// Returns NULL if there is no dependence.
35743583
// Otherwise, return a Dependence with as many details as possible.
@@ -3644,12 +3653,11 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
36443653

36453654
const SCEV *SrcEv = SE->getMinusSCEV(SrcSCEV, SrcBase);
36463655
const SCEV *DstEv = SE->getMinusSCEV(DstSCEV, DstBase);
3647-
const SCEV *Param = nullptr;
36483656

36493657
if (Src != Dst) {
36503658
// Check that memory access offsets are multiples of element sizes.
3651-
if (!SE->isKnownMultipleOf(SrcEv, Param, EltSize) ||
3652-
!SE->isKnownMultipleOf(DstEv, Param, EltSize)) {
3659+
if (!SE->isKnownMultipleOf(SrcEv, EltSize, Assumptions) ||
3660+
!SE->isKnownMultipleOf(DstEv, EltSize, Assumptions)) {
36533661
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n");
36543662
return std::make_unique<Dependence>(Src, Dst);
36553663
}

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 49 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -10971,107 +10971,64 @@ bool ScalarEvolution::isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero,
1097110971
return all_of(Mul->operands(), NonRecursive) && (OrZero || isKnownNonZero(S));
1097210972
}
1097310973

10974-
bool ScalarEvolution::isKnownMultipleOf(const SCEV *V, const SCEV *&Param,
10975-
uint64_t EltSize) {
10976-
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V))
10977-
return isKnownMultipleOf(AddRec->getStart(), Param, EltSize) &&
10978-
isKnownMultipleOf(AddRec->getStepRecurrence(*this), Param, EltSize);
10974+
bool ScalarEvolution::isKnownMultipleOf(
10975+
const SCEV *S, uint64_t M,
10976+
SmallVectorImpl<const SCEVPredicate *> &Assumptions) {
10977+
if (M == 0)
10978+
return false;
10979+
if (M == 1)
10980+
return true;
10981+
10982+
// Recursively check AddRec operands.
10983+
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(S))
10984+
return isKnownMultipleOf(AddRec->getStart(), M, Assumptions) &&
10985+
isKnownMultipleOf(AddRec->getStepRecurrence(*this), M, Assumptions);
1097910986

10980-
if (auto *Cst = dyn_cast<SCEVConstant>(V)) {
10987+
// For a constant, check that "S % M == 0".
10988+
if (auto *Cst = dyn_cast<SCEVConstant>(S)) {
1098110989
APInt C = Cst->getAPInt();
10982-
// For example, alias_with_different_offsets in
10983-
// test/Analysis/DependenceAnalysis/DifferentOffsets.ll accesses "%A + 2":
10984-
// %arrayidx = getelementptr inbounds i8, ptr %A, i64 2
10985-
// store i32 42, ptr %arrayidx, align 1
10986-
// which is writing an i32, i.e., EltSize = 4 bytes, with an offset C = 2.
10987-
// isKnownMultipleOf returns false, as C=2 is not a multiple of 4.
10988-
return C.urem(EltSize) == 0;
10989-
}
10990-
10991-
// Use a lambda helper function to check V for parametric expressions.
10992-
// Param records the first parametric expression. If the scalar evolution V
10993-
// contains two or more parameters, we check that the subsequent parametric
10994-
// expressions are multiples of the first parametric expression Param.
10995-
auto checkParamsMultipleOfSize = [&](const SCEV *V,
10996-
const SCEV *&Param) -> bool {
10997-
if (EltSize == 1)
10998-
return true;
10999-
if (!Param) {
11000-
Param = V;
11001-
return true;
11002-
}
11003-
if (Param == V)
11004-
return true;
10990+
return C.urem(M) == 0;
10991+
}
1100510992

11006-
// Check whether "(Param - V) % Size == 0".
11007-
const SCEV *Diff = getMinusSCEV(Param, V);
11008-
if (auto *Cst = dyn_cast<SCEVConstant>(Diff)) {
11009-
APInt Val = Cst->getAPInt();
11010-
if (Val.isZero())
11011-
return true;
11012-
APInt Rem = Val.urem(APInt(Val.getBitWidth(), EltSize, /*isSigned=*/true));
11013-
if (Rem.isZero())
11014-
// For example in test/Analysis/DependenceAnalysis/Preliminary.ll
11015-
// SrcSCEV = ((4 * (sext i8 %n to i64))<nsw> + %A)
11016-
// DstSCEV = (4 + (4 * (sext i8 %n to i64))<nsw> + %A)
11017-
// Param = (4 * (sext i8 %n to i64))<nsw>
11018-
// V = 4 + (4 * (sext i8 %n to i64))<nsw>
11019-
// Diff = -4, Rem = 0, and so all offsets are multiple of 4.
11020-
return true;
11021-
LLVM_DEBUG(dbgs() << "SCEV with different offsets: " << *Param << " - "
11022-
<< *V << " = " << *Diff << " % " << EltSize << " = "
11023-
<< Rem << " != 0\n");
11024-
return false;
11025-
}
11026-
// Check if the symbolic difference is a multiple of Size.
11027-
const SCEV *Val =
11028-
getConstant(APInt(Diff->getType()->getScalarSizeInBits(), EltSize));
11029-
11030-
// Check by using the remainder computation.
11031-
const SCEV *Remainder = getURemExpr(Diff, Val);
11032-
if (const SCEVConstant *C = dyn_cast<SCEVConstant>(Remainder))
11033-
if (C->getValue()->isZero())
11034-
// For example test/Analysis/DependenceAnalysis/DADelin.ll
11035-
// SrcSCEV = {{{%A,+,(4 * %m * %o)}<%for.cond1.preheader>,+,(4 * %o)}
11036-
// DstSCEV = {{{%A,+,(4 * %m * %o)}<%for.cond1.preheader>,+,(4 * %o)}
11037-
// The strides '(4 * %m * %o)' and '(4 * %o)' are multiple of 4.
11038-
return true;
10993+
// Basic tests have failed.
10994+
// Record "S % M == 0" in the runtime Assumptions.
10995+
auto recordRuntimePredicate = [&](const SCEV *S) -> void {
10996+
auto *STy = dyn_cast<IntegerType>(S->getType());
10997+
const SCEV *SmodM =
10998+
getURemExpr(S, getConstant(ConstantInt::get(STy, M, false)));
10999+
const SCEV *Zero = getZero(STy);
1103911000

11040-
// Check by using the division computation.
11041-
const SCEV *Q = getUDivExpr(Diff, Val);
11042-
const SCEV *Product = getMulExpr(Q, Val);
11043-
if (Diff == Product)
11044-
return true;
11045-
LLVM_DEBUG(dbgs() << "SCEV with different offsets:\n"
11046-
<< *Param << " - " << *V << " = " << *Diff << "\n"
11047-
<< "Remainder = " << *Remainder << "\n"
11048-
<< "Q = " << *Q << " Product = " << *Product << "\n");
11049-
// For example in test/Analysis/DependenceAnalysis/MIVCheckConst.ll
11050-
// SrcSCEV = {(80640 + (4 * (1 + %n) * %v1) + %A),+,(8 * %v1)}<%bb13>
11051-
// DstSCEV = {(126720 + (128 * %m) + %A),+,256}<%bb13>
11052-
// We fail to prove that the offsets 80640 + (4 * (1 + %n) * %v1) and
11053-
// (8 * %v1) are multiples of 128.
11054-
// Param = 80640 + (4 * (1 + %n) * %v1)
11055-
// (80640 + (4 * (1 + %n) * %v1)) - (8 * %v1) =
11056-
// (80640 + ((-4 + (4 * %n)) * %v1))
11057-
// Remainder = (zext i7 ((trunc i32 %v1 to i7) *
11058-
// (-4 + (4 * (trunc i32 %n to i7)))) to i32)
11059-
// Q = ((80640 + ((-4 + (4 * %n)) * %v1)) /u 128)
11060-
// Product = (128 * ((80640 + ((-4 + (4 * %n)) * %v1)) /u 128))<nuw>
11061-
return false;
11001+
// Check whether "S % M == 0" is known at compile time.
11002+
if (isKnownPredicate(ICmpInst::ICMP_EQ, SmodM, Zero))
11003+
return;
11004+
11005+
const SCEVPredicate *P =
11006+
getComparePredicate(ICmpInst::ICMP_EQ, SmodM, Zero);
11007+
11008+
// Detect redundant predicates.
11009+
for (auto *A : Assumptions)
11010+
if (A->implies(P, *this))
11011+
return;
11012+
11013+
Assumptions.push_back(P);
11014+
return;
1106211015
};
1106311016

1106411017
// Expressions like "n".
11065-
if (isa<SCEVUnknown>(V))
11066-
return checkParamsMultipleOfSize(V, Param);
11018+
if (isa<SCEVUnknown>(S)) {
11019+
recordRuntimePredicate(S);
11020+
return true;
11021+
}
1106711022

11068-
// Expressions like "n + 1".
11069-
if (isa<SCEVAddExpr>(V) || isa<SCEVMulExpr>(V))
11070-
return !SCEVExprContains(V, [](const SCEV *S) {
11071-
return isa<SCEVUnknown>(S);
11072-
}) || checkParamsMultipleOfSize(V, Param);
11023+
// Expressions like "n + 1" and "n * 3".
11024+
if (isa<SCEVAddExpr>(S) || isa<SCEVMulExpr>(S)) {
11025+
if (SCEVExprContains(S, [](const SCEV *X) { return isa<SCEVUnknown>(X); }))
11026+
recordRuntimePredicate(S);
11027+
return true;
11028+
}
1107311029

11074-
LLVM_DEBUG(dbgs() << "SCEV node not handled yet: " << *V << "\n");
11030+
LLVM_DEBUG(dbgs() << "SCEV node not handled yet in isKnownMultipleOf: " << *S
11031+
<< "\n");
1107511032
return false;
1107611033
}
1107711034

llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,45 @@ entry:
2323
%0 = load i32, ptr %A, align 1
2424
ret i32 %0
2525
}
26+
27+
define i32 @alias_with_parametric_offset(ptr nocapture %A, i64 %n) {
28+
; CHECK-LABEL: 'alias_with_parametric_offset'
29+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: store i32 2, ptr %arrayidx, align 1
30+
; CHECK-NEXT: da analyze - none!
31+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: %0 = load i32, ptr %A, align 1
32+
; CHECK-NEXT: da analyze - flow [|<]!
33+
; CHECK-NEXT: Src: %0 = load i32, ptr %A, align 1 --> Dst: %0 = load i32, ptr %A, align 1
34+
; CHECK-NEXT: da analyze - none!
35+
; CHECK-NEXT: Runtime Assumptions:
36+
; CHECK-NEXT: Equal predicate: (zext i2 (trunc i64 %n to i2) to i64) == 0
37+
;
38+
entry:
39+
%arrayidx = getelementptr inbounds i8, ptr %A, i64 %n
40+
store i32 2, ptr %arrayidx, align 1
41+
%0 = load i32, ptr %A, align 1
42+
ret i32 %0
43+
}
44+
45+
define i32 @alias_with_parametric_expr(ptr nocapture %A, i64 %n, i64 %m) {
46+
; CHECK-LABEL: 'alias_with_parametric_expr'
47+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: store i32 2, ptr %arrayidx, align 1
48+
; CHECK-NEXT: da analyze - none!
49+
; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: %0 = load i32, ptr %arrayidx1, align 1
50+
; CHECK-NEXT: da analyze - flow [|<]!
51+
; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 1 --> Dst: %0 = load i32, ptr %arrayidx1, align 1
52+
; CHECK-NEXT: da analyze - none!
53+
; CHECK-NEXT: Runtime Assumptions:
54+
; CHECK-NEXT: Equal predicate: (zext i2 ((trunc i64 %m to i2) + (-2 * (trunc i64 %n to i2))) to i64) == 0
55+
; CHECK-NEXT: Equal predicate: (zext i2 (-2 + (trunc i64 %m to i2)) to i64) == 0
56+
;
57+
entry:
58+
%mul = mul nsw i64 %n, 10
59+
%add = add nsw i64 %mul, %m
60+
%arrayidx = getelementptr inbounds i8, ptr %A, i64 %add
61+
store i32 2, ptr %arrayidx, align 1
62+
63+
%add1 = add nsw i64 %m, 42
64+
%arrayidx1 = getelementptr inbounds i8, ptr %A, i64 %add1
65+
%0 = load i32, ptr %arrayidx1, align 1
66+
ret i32 %0
67+
}

llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@ define void @test(ptr %A, ptr %B, i1 %arg, i32 %n, i32 %m) #0 align 2 {
4141
; CHECK-NEXT: Src: %v27 = load <32 x i32>, ptr %v25, align 256 --> Dst: %v27 = load <32 x i32>, ptr %v25, align 256
4242
; CHECK-NEXT: da analyze - consistent input [0 S S]!
4343
; CHECK-NEXT: Src: %v27 = load <32 x i32>, ptr %v25, align 256 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
44-
; CHECK-NEXT: da analyze - confused!
44+
; CHECK-NEXT: da analyze - input [* S S|<]!
4545
; CHECK-NEXT: Src: %v32 = load <32 x i32>, ptr %v30, align 128 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
4646
; CHECK-NEXT: da analyze - consistent input [0 S S]!
47+
; CHECK-NEXT: Runtime Assumptions:
48+
; CHECK-NEXT: Equal predicate: (zext i7 (4 * (trunc i32 %v1 to i7) * (1 + (trunc i32 %n to i7))) to i32) == 0
49+
; CHECK-NEXT: Equal predicate: (8 * (zext i4 (trunc i32 %v1 to i4) to i32))<nuw><nsw> == 0
4750
;
4851
entry:
4952
%v1 = load i32, ptr %B, align 4

0 commit comments

Comments
 (0)