Skip to content

Commit c38d16c

Browse files
committed
[SCEV] Iteratively compute ranges for deeply nested expressions.
At the moment, getRangeRef may overflow the stack for very deeply nested expressions. This patch introduces a new getRangeRefIter function, which first builds a worklist of N-ary expressions and phi nodes, followed by their operands iteratively. getRangeRef has been extended to also take a Depth argument and it switches to use getRangeRefIter once the depth reaches a certain threshold. This ensures compile-time is not impacted in general. Note that the iterative algorithm may lead to a slightly different evaluation order, which could result in slightly worse ranges for cyclic phis. https://llvm-compile-time-tracker.com/compare.php?from=23c3eb7cdf3478c9db86f6cb5115821a8f0f5f40&to=e0e09fa338e77e53242bfc846e1484350ad79773&stat=instructions Fixes llvm#49579. Reviewed By: mkazantsev Differential Revision: https://reviews.llvm.org/D130728
1 parent fcaaa82 commit c38d16c

File tree

4 files changed

+174
-20
lines changed

4 files changed

+174
-20
lines changed

llvm/include/llvm/Analysis/ScalarEvolution.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,9 @@ class ScalarEvolution {
12831283
/// Mark SCEVUnknown Phis currently being processed by getRangeRef.
12841284
SmallPtrSet<const PHINode *, 6> PendingPhiRanges;
12851285

1286+
/// Mark SCEVUnknown Phis currently being processed by getRangeRefIter.
1287+
SmallPtrSet<const PHINode *, 6> PendingPhiRangesIter;
1288+
12861289
// Mark SCEVUnknown Phis currently being processed by isImpliedViaMerge.
12871290
SmallPtrSet<const PHINode *, 6> PendingMerges;
12881291

@@ -1560,7 +1563,12 @@ class ScalarEvolution {
15601563
/// Determine the range for a particular SCEV.
15611564
/// NOTE: This returns a reference to an entry in a cache. It must be
15621565
/// copied if its needed for longer.
1563-
const ConstantRange &getRangeRef(const SCEV *S, RangeSignHint Hint);
1566+
const ConstantRange &getRangeRef(const SCEV *S, RangeSignHint Hint,
1567+
unsigned Depth = 0);
1568+
1569+
/// Determine the range for a particular SCEV, but evaluates ranges for
1570+
/// operands iteratively first.
1571+
const ConstantRange &getRangeRefIter(const SCEV *S, RangeSignHint Hint);
15641572

15651573
/// Determines the range for the affine SCEVAddRecExpr {\p Start,+,\p Step}.
15661574
/// Helper for \c getRange.

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ static cl::opt<unsigned>
220220
cl::desc("Size of the expression which is considered huge"),
221221
cl::init(4096));
222222

223+
static cl::opt<unsigned> RangeIterThreshold(
224+
"scev-range-iter-threshold", cl::Hidden,
225+
cl::desc("Threshold for switching to iteratively computing SCEV ranges"),
226+
cl::init(32));
227+
223228
static cl::opt<bool>
224229
ClassifyExpressions("scalar-evolution-classify-expressions",
225230
cl::Hidden, cl::init(true),
@@ -6425,18 +6430,78 @@ getRangeForUnknownRecurrence(const SCEVUnknown *U) {
64256430
return FullSet;
64266431
}
64276432

6433+
const ConstantRange &
6434+
ScalarEvolution::getRangeRefIter(const SCEV *S,
6435+
ScalarEvolution::RangeSignHint SignHint) {
6436+
DenseMap<const SCEV *, ConstantRange> &Cache =
6437+
SignHint == ScalarEvolution::HINT_RANGE_UNSIGNED ? UnsignedRanges
6438+
: SignedRanges;
6439+
SmallVector<const SCEV *> WorkList;
6440+
SmallPtrSet<const SCEV *, 8> Seen;
6441+
6442+
// Add Expr to the worklist, if Expr is either an N-ary expression or a
6443+
// SCEVUnknown PHI node.
6444+
auto AddToWorklist = [&WorkList, &Seen, &Cache](const SCEV *Expr) {
6445+
if (!Seen.insert(Expr).second)
6446+
return;
6447+
if (Cache.find(Expr) != Cache.end())
6448+
return;
6449+
if (isa<SCEVNAryExpr>(Expr) || isa<SCEVUDivExpr>(Expr))
6450+
WorkList.push_back(Expr);
6451+
else if (auto *UnknownS = dyn_cast<SCEVUnknown>(Expr))
6452+
if (isa<PHINode>(UnknownS->getValue()))
6453+
WorkList.push_back(Expr);
6454+
};
6455+
AddToWorklist(S);
6456+
6457+
// Build worklist by queuing operands of N-ary expressions and phi nodes.
6458+
for (unsigned I = 0; I != WorkList.size(); ++I) {
6459+
const SCEV *P = WorkList[I];
6460+
if (auto *NaryS = dyn_cast<SCEVNAryExpr>(P)) {
6461+
for (const SCEV *Op : NaryS->operands())
6462+
AddToWorklist(Op);
6463+
} else if (auto *UDiv = dyn_cast<SCEVUDivExpr>(P)) {
6464+
AddToWorklist(UDiv->getLHS());
6465+
AddToWorklist(UDiv->getRHS());
6466+
} else {
6467+
auto *UnknownS = cast<SCEVUnknown>(P);
6468+
if (const PHINode *P = dyn_cast<PHINode>(UnknownS->getValue())) {
6469+
if (!PendingPhiRangesIter.insert(P).second)
6470+
continue;
6471+
for (auto &Op : reverse(P->operands()))
6472+
AddToWorklist(getSCEV(Op));
6473+
}
6474+
}
6475+
}
6476+
6477+
if (!WorkList.empty()) {
6478+
// Use getRangeRef to compute ranges for items in the worklist in reverse
6479+
// order. This will force ranges for earlier operands to be computed before
6480+
// their users in most cases.
6481+
for (const SCEV *P :
6482+
reverse(make_range(WorkList.begin() + 1, WorkList.end()))) {
6483+
getRangeRef(P, SignHint);
6484+
6485+
if (auto *UnknownS = dyn_cast<SCEVUnknown>(P))
6486+
if (const PHINode *P = dyn_cast<PHINode>(UnknownS->getValue()))
6487+
PendingPhiRangesIter.erase(P);
6488+
}
6489+
}
6490+
6491+
return getRangeRef(S, SignHint, 0);
6492+
}
6493+
64286494
/// Determine the range for a particular SCEV. If SignHint is
64296495
/// HINT_RANGE_UNSIGNED (resp. HINT_RANGE_SIGNED) then getRange prefers ranges
64306496
/// with a "cleaner" unsigned (resp. signed) representation.
6431-
const ConstantRange &
6432-
ScalarEvolution::getRangeRef(const SCEV *S,
6433-
ScalarEvolution::RangeSignHint SignHint) {
6497+
const ConstantRange &ScalarEvolution::getRangeRef(
6498+
const SCEV *S, ScalarEvolution::RangeSignHint SignHint, unsigned Depth) {
64346499
DenseMap<const SCEV *, ConstantRange> &Cache =
64356500
SignHint == ScalarEvolution::HINT_RANGE_UNSIGNED ? UnsignedRanges
64366501
: SignedRanges;
64376502
ConstantRange::PreferredRangeType RangeType =
6438-
SignHint == ScalarEvolution::HINT_RANGE_UNSIGNED
6439-
? ConstantRange::Unsigned : ConstantRange::Signed;
6503+
SignHint == ScalarEvolution::HINT_RANGE_UNSIGNED ? ConstantRange::Unsigned
6504+
: ConstantRange::Signed;
64406505

64416506
// See if we've computed this range already.
64426507
DenseMap<const SCEV *, ConstantRange>::iterator I = Cache.find(S);
@@ -6446,6 +6511,11 @@ ScalarEvolution::getRangeRef(const SCEV *S,
64466511
if (const SCEVConstant *C = dyn_cast<SCEVConstant>(S))
64476512
return setRange(C, SignHint, ConstantRange(C->getAPInt()));
64486513

6514+
// Switch to iteratively computing the range for S, if it is part of a deeply
6515+
// nested expression.
6516+
if (Depth > RangeIterThreshold)
6517+
return getRangeRefIter(S, SignHint);
6518+
64496519
unsigned BitWidth = getTypeSizeInBits(S->getType());
64506520
ConstantRange ConservativeResult(BitWidth, /*isFullSet=*/true);
64516521
using OBO = OverflowingBinaryOperator;
@@ -6465,23 +6535,23 @@ ScalarEvolution::getRangeRef(const SCEV *S,
64656535
}
64666536

64676537
if (const SCEVAddExpr *Add = dyn_cast<SCEVAddExpr>(S)) {
6468-
ConstantRange X = getRangeRef(Add->getOperand(0), SignHint);
6538+
ConstantRange X = getRangeRef(Add->getOperand(0), SignHint, Depth + 1);
64696539
unsigned WrapType = OBO::AnyWrap;
64706540
if (Add->hasNoSignedWrap())
64716541
WrapType |= OBO::NoSignedWrap;
64726542
if (Add->hasNoUnsignedWrap())
64736543
WrapType |= OBO::NoUnsignedWrap;
64746544
for (unsigned i = 1, e = Add->getNumOperands(); i != e; ++i)
6475-
X = X.addWithNoWrap(getRangeRef(Add->getOperand(i), SignHint),
6545+
X = X.addWithNoWrap(getRangeRef(Add->getOperand(i), SignHint, Depth + 1),
64766546
WrapType, RangeType);
64776547
return setRange(Add, SignHint,
64786548
ConservativeResult.intersectWith(X, RangeType));
64796549
}
64806550

64816551
if (const SCEVMulExpr *Mul = dyn_cast<SCEVMulExpr>(S)) {
6482-
ConstantRange X = getRangeRef(Mul->getOperand(0), SignHint);
6552+
ConstantRange X = getRangeRef(Mul->getOperand(0), SignHint, Depth + 1);
64836553
for (unsigned i = 1, e = Mul->getNumOperands(); i != e; ++i)
6484-
X = X.multiply(getRangeRef(Mul->getOperand(i), SignHint));
6554+
X = X.multiply(getRangeRef(Mul->getOperand(i), SignHint, Depth + 1));
64856555
return setRange(Mul, SignHint,
64866556
ConservativeResult.intersectWith(X, RangeType));
64876557
}
@@ -6507,41 +6577,42 @@ ScalarEvolution::getRangeRef(const SCEV *S,
65076577
}
65086578

65096579
const auto *NAry = cast<SCEVNAryExpr>(S);
6510-
ConstantRange X = getRangeRef(NAry->getOperand(0), SignHint);
6580+
ConstantRange X = getRangeRef(NAry->getOperand(0), SignHint, Depth + 1);
65116581
for (unsigned i = 1, e = NAry->getNumOperands(); i != e; ++i)
6512-
X = X.intrinsic(ID, {X, getRangeRef(NAry->getOperand(i), SignHint)});
6582+
X = X.intrinsic(
6583+
ID, {X, getRangeRef(NAry->getOperand(i), SignHint, Depth + 1)});
65136584
return setRange(S, SignHint,
65146585
ConservativeResult.intersectWith(X, RangeType));
65156586
}
65166587

65176588
if (const SCEVUDivExpr *UDiv = dyn_cast<SCEVUDivExpr>(S)) {
6518-
ConstantRange X = getRangeRef(UDiv->getLHS(), SignHint);
6519-
ConstantRange Y = getRangeRef(UDiv->getRHS(), SignHint);
6589+
ConstantRange X = getRangeRef(UDiv->getLHS(), SignHint, Depth + 1);
6590+
ConstantRange Y = getRangeRef(UDiv->getRHS(), SignHint, Depth + 1);
65206591
return setRange(UDiv, SignHint,
65216592
ConservativeResult.intersectWith(X.udiv(Y), RangeType));
65226593
}
65236594

65246595
if (const SCEVZeroExtendExpr *ZExt = dyn_cast<SCEVZeroExtendExpr>(S)) {
6525-
ConstantRange X = getRangeRef(ZExt->getOperand(), SignHint);
6596+
ConstantRange X = getRangeRef(ZExt->getOperand(), SignHint, Depth + 1);
65266597
return setRange(ZExt, SignHint,
65276598
ConservativeResult.intersectWith(X.zeroExtend(BitWidth),
65286599
RangeType));
65296600
}
65306601

65316602
if (const SCEVSignExtendExpr *SExt = dyn_cast<SCEVSignExtendExpr>(S)) {
6532-
ConstantRange X = getRangeRef(SExt->getOperand(), SignHint);
6603+
ConstantRange X = getRangeRef(SExt->getOperand(), SignHint, Depth + 1);
65336604
return setRange(SExt, SignHint,
65346605
ConservativeResult.intersectWith(X.signExtend(BitWidth),
65356606
RangeType));
65366607
}
65376608

65386609
if (const SCEVPtrToIntExpr *PtrToInt = dyn_cast<SCEVPtrToIntExpr>(S)) {
6539-
ConstantRange X = getRangeRef(PtrToInt->getOperand(), SignHint);
6610+
ConstantRange X = getRangeRef(PtrToInt->getOperand(), SignHint, Depth + 1);
65406611
return setRange(PtrToInt, SignHint, X);
65416612
}
65426613

65436614
if (const SCEVTruncateExpr *Trunc = dyn_cast<SCEVTruncateExpr>(S)) {
6544-
ConstantRange X = getRangeRef(Trunc->getOperand(), SignHint);
6615+
ConstantRange X = getRangeRef(Trunc->getOperand(), SignHint, Depth + 1);
65456616
return setRange(Trunc, SignHint,
65466617
ConservativeResult.intersectWith(X.truncate(BitWidth),
65476618
RangeType));
@@ -6671,12 +6742,13 @@ ScalarEvolution::getRangeRef(const SCEV *S,
66716742
RangeType);
66726743

66736744
// A range of Phi is a subset of union of all ranges of its input.
6674-
if (const PHINode *Phi = dyn_cast<PHINode>(U->getValue())) {
6745+
if (PHINode *Phi = dyn_cast<PHINode>(U->getValue())) {
66756746
// Make sure that we do not run over cycled Phis.
66766747
if (PendingPhiRanges.insert(Phi).second) {
66776748
ConstantRange RangeFromOps(BitWidth, /*isFullSet=*/false);
6749+
66786750
for (const auto &Op : Phi->operands()) {
6679-
auto OpRange = getRangeRef(getSCEV(Op), SignHint);
6751+
auto OpRange = getRangeRef(getSCEV(Op), SignHint, Depth + 1);
66806752
RangeFromOps = RangeFromOps.unionWith(OpRange);
66816753
// No point to continue if we already have a full set.
66826754
if (RangeFromOps.isFullSet())

llvm/test/Analysis/ScalarEvolution/ranges.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
22
; RUN: opt < %s -disable-output "-passes=print<scalar-evolution>" 2>&1 | FileCheck %s
3+
; RUN: opt < %s -disable-output "-passes=print<scalar-evolution>" -scev-range-iter-threshold=1 2>&1 | FileCheck %s
34

45
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64"
56

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
; RUN: opt -passes=indvars -S %s | FileCheck --check-prefix=COMMON --check-prefix=DEFAULT %s
2+
; RUN: opt -passes=indvars -scev-range-iter-threshold=1 -S %s | FileCheck --check-prefix=COMMON --check-prefix=LIMIT %s
3+
4+
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
5+
6+
define i32 @test(i1 %c.0, i32 %m) {
7+
; COMMON-LABEL: @test(
8+
; COMMON-NEXT: entry:
9+
; COMMON-NEXT: br label [[OUTER_HEADER:%.*]]
10+
; COMMON: outer.header:
11+
; DEFAULT-NEXT: [[INDVARS_IV:%.*]] = phi i32 [ [[INDVARS_IV_NEXT:%.*]], [[OUTER_LATCH:%.*]] ], [ 2, [[ENTRY:%.*]] ]
12+
; COMMON-NEXT: [[IV_1:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_1_NEXT:%.*]], [[OUTER_LATCH:%.*]] ]
13+
; COMMON-NEXT: [[MAX_0:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[MAX_1:%.*]], [[OUTER_LATCH]] ]
14+
; COMMON-NEXT: [[TMP0:%.*]] = sext i32 [[IV_1]] to i64
15+
; COMMON-NEXT: br label [[INNER_1:%.*]]
16+
; COMMON: inner.1:
17+
; COMMON-NEXT: [[C_1:%.*]] = icmp slt i64 0, [[TMP0]]
18+
; COMMON-NEXT: br i1 [[C_1]], label [[INNER_1]], label [[INNER_2_HEADER_PREHEADER:%.*]]
19+
; COMMON: inner.2.header.preheader:
20+
; COMMON-NEXT: br label [[INNER_2_HEADER:%.*]]
21+
; COMMON: inner.2.header:
22+
; COMMON-NEXT: [[IV_3:%.*]] = phi i32 [ [[IV_3_NEXT:%.*]], [[INNER_2_LATCH:%.*]] ], [ 0, [[INNER_2_HEADER_PREHEADER]] ]
23+
; COMMON-NEXT: br i1 [[C_0:%.*]], label [[OUTER_LATCH]], label [[INNER_2_LATCH]]
24+
; COMMON: inner.2.latch:
25+
; COMMON-NEXT: [[IV_3_NEXT]] = add i32 [[IV_3]], 1
26+
; DEFAULT-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[IV_3_NEXT]], [[INDVARS_IV]]
27+
; LIMIT-NEXT: [[EXITCOND:%.*]] = icmp ugt i32 [[IV_3]], [[IV_1]]
28+
; COMMON-NEXT: br i1 [[EXITCOND]], label [[OUTER_LATCH]], label [[INNER_2_HEADER]]
29+
; COMMON: outer.latch:
30+
; COMMON-NEXT: [[MAX_1]] = phi i32 [ [[M:%.*]], [[INNER_2_LATCH]] ], [ 0, [[INNER_2_HEADER]] ]
31+
; COMMON-NEXT: [[IV_1_NEXT]] = add nuw i32 [[IV_1]], 1
32+
; COMMON-NEXT: [[C_3:%.*]] = icmp ugt i32 [[IV_1]], [[MAX_0]]
33+
; DEFAULT-NEXT: [[INDVARS_IV_NEXT]] = add i32 [[INDVARS_IV]], 1
34+
; COMMON-NEXT: br i1 [[C_3]], label [[EXIT:%.*]], label [[OUTER_HEADER]], !llvm.loop [[LOOP0:![0-9]+]]
35+
; COMMON: exit:
36+
; COMMON-NEXT: ret i32 0
37+
;
38+
entry:
39+
br label %outer.header
40+
41+
outer.header:
42+
%iv.1 = phi i32 [ 0, %entry ], [ %iv.1.next, %outer.latch ]
43+
%iv.2 = phi i32 [ 0, %entry ], [ %iv.2.next , %outer.latch ]
44+
%max.0 = phi i32 [ 0, %entry ], [ %max.1, %outer.latch ]
45+
%0 = sext i32 %iv.1 to i64
46+
br label %inner.1
47+
48+
inner.1:
49+
%c.1 = icmp slt i64 0, %0
50+
br i1 %c.1, label %inner.1, label %inner.2.header
51+
52+
inner.2.header:
53+
%iv.3 = phi i32 [ 0, %inner.1 ], [ %iv.3.next, %inner.2.latch ]
54+
br i1 %c.0, label %outer.latch, label %inner.2.latch
55+
56+
inner.2.latch:
57+
%iv.3.next = add i32 %iv.3, 1
58+
%c.2 = icmp ugt i32 %iv.3, %iv.2
59+
br i1 %c.2, label %outer.latch, label %inner.2.header
60+
61+
outer.latch:
62+
%max.1 = phi i32 [ %m, %inner.2.latch ], [ %iv.3, %inner.2.header ]
63+
%iv.1.next = add i32 %iv.1, 1
64+
%iv.2.next = add i32 %iv.2, 1
65+
%c.3 = icmp ugt i32 %iv.2, %max.0
66+
br i1 %c.3, label %exit, label %outer.header, !llvm.loop !0
67+
68+
exit:
69+
ret i32 0
70+
}
71+
72+
!0 = distinct !{!0, !1}
73+
!1 = !{!"llvm.loop.mustprogress"}

0 commit comments

Comments
 (0)