Skip to content

Commit 99ddd77

Browse files
modikingnikic
andauthored
[LoopUnroll] Introduce PragmaUnrollFullMaxIterations as a hard cap on how many iterations we try to unroll (#78648)
Fixes [PR77842](#77842) where UBSAN causes pragma full unroll to try and unroll INT_MAX times. This sets a cap to make sure we don't attempt this and crash the compiler. Testing: ninja check-all with new test --------- Co-authored-by: Nikita Popov <[email protected]>
1 parent c329125 commit 99ddd77

File tree

2 files changed

+69
-2
lines changed

2 files changed

+69
-2
lines changed

llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ static cl::opt<unsigned>
173173
cl::desc("Default threshold (max size of unrolled "
174174
"loop), used in all but O3 optimizations"));
175175

176+
static cl::opt<unsigned> PragmaUnrollFullMaxIterations(
177+
"pragma-unroll-full-max-iterations", cl::init(1'000'000), cl::Hidden,
178+
cl::desc("Maximum allowed iterations to unroll under pragma unroll full."));
179+
176180
/// A magic value for use with the Threshold parameter to indicate
177181
/// that the loop unroll should be performed regardless of how much
178182
/// code expansion would result.
@@ -776,8 +780,17 @@ shouldPragmaUnroll(Loop *L, const PragmaInfo &PInfo,
776780
return PInfo.PragmaCount;
777781
}
778782

779-
if (PInfo.PragmaFullUnroll && TripCount != 0)
783+
if (PInfo.PragmaFullUnroll && TripCount != 0) {
784+
// Certain cases with UBSAN can cause trip count to be calculated as
785+
// INT_MAX, Block full unrolling at a reasonable limit so that the compiler
786+
// doesn't hang trying to unroll the loop. See PR77842
787+
if (TripCount > PragmaUnrollFullMaxIterations) {
788+
LLVM_DEBUG(dbgs() << "Won't unroll; trip count is too large\n");
789+
return std::nullopt;
790+
}
791+
780792
return TripCount;
793+
}
781794

782795
if (PInfo.PragmaEnableUnroll && !TripCount && MaxTripCount &&
783796
MaxTripCount <= UP.MaxUpperBound)
@@ -1282,7 +1295,7 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
12821295
}
12831296

12841297
// Do not attempt partial/runtime unrolling in FullLoopUnrolling
1285-
if (OnlyFullUnroll && !(UP.Count >= MaxTripCount)) {
1298+
if (OnlyFullUnroll && (UP.Count < TripCount || UP.Count < MaxTripCount)) {
12861299
LLVM_DEBUG(
12871300
dbgs() << "Not attempting partial/runtime unroll in FullLoopUnroll.\n");
12881301
return LoopUnrollResult::Unmodified;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt -passes=loop-unroll-full -S %s | FileCheck %s
3+
4+
; Validate that loop unroll full doesn't try to fully unroll values whose trip counts are too large.
5+
6+
define void @foo(i64 %end) {
7+
; CHECK-LABEL: define void @foo(
8+
; CHECK-SAME: i64 [[END:%.*]]) {
9+
; CHECK-NEXT: entry:
10+
; CHECK-NEXT: br label [[LOOPHEADER:%.*]]
11+
; CHECK: loopheader:
12+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEW:%.*]], [[BACKEDGE:%.*]] ]
13+
; CHECK-NEXT: [[EXIT:%.*]] = icmp eq i64 [[IV]], [[END]]
14+
; CHECK-NEXT: br i1 [[EXIT]], label [[FOR_COND_CLEANUP_LOOPEXIT:%.*]], label [[CONT23:%.*]]
15+
; CHECK: for.cond.cleanup.loopexit:
16+
; CHECK-NEXT: ret void
17+
; CHECK: cont23:
18+
; CHECK-NEXT: [[EXITCOND241:%.*]] = icmp eq i64 [[IV]], 2147483647
19+
; CHECK-NEXT: br i1 [[EXITCOND241]], label [[HANDLER_ADD_OVERFLOW:%.*]], label [[BACKEDGE]]
20+
; CHECK: handler.add_overflow:
21+
; CHECK-NEXT: unreachable
22+
; CHECK: backedge:
23+
; CHECK-NEXT: [[IV_NEW]] = add i64 [[IV]], 1
24+
; CHECK-NEXT: br label [[LOOPHEADER]], !llvm.loop [[LOOP0:![0-9]+]]
25+
;
26+
entry:
27+
br label %loopheader
28+
29+
loopheader:
30+
%iv = phi i64 [ 0, %entry ], [ %iv_new, %backedge ]
31+
%exit = icmp eq i64 %iv, %end
32+
br i1 %exit, label %for.cond.cleanup.loopexit, label %cont23
33+
34+
for.cond.cleanup.loopexit:
35+
ret void
36+
37+
cont23:
38+
%exitcond241 = icmp eq i64 %iv, 2147483647
39+
br i1 %exitcond241, label %handler.add_overflow, label %backedge
40+
41+
handler.add_overflow:
42+
unreachable
43+
44+
backedge: ; preds = %cont23
45+
%iv_new = add i64 %iv, 1
46+
br label %loopheader, !llvm.loop !0
47+
}
48+
49+
!0 = distinct !{!0, !1}
50+
!1 = !{!"llvm.loop.unroll.full"}
51+
;.
52+
; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]]}
53+
; CHECK: [[META1]] = !{!"llvm.loop.unroll.full"}
54+
;.

0 commit comments

Comments
 (0)