Skip to content

Commit

Permalink
[LoopDeletion] Allows deletion of possibly infinite side-effect free …
Browse files Browse the repository at this point in the history
…loops

From C11 and C++11 onwards, a forward-progress requirement has been
introduced for both languages. In the case of C, loops with non-constant
conditionals that do not have any observable side-effects (as defined by
6.8.5p6) can be assumed by the implementation to terminate, and in the
case of C++, this assumption extends to all functions. The clang
frontend will emit the `mustprogress` function attribute for C++
functions (D86233, D85393, D86841) and emit the loop metadata
`llvm.loop.mustprogress` for every loop in C11 or later that has a
non-constant conditional.

This patch modifies LoopDeletion so that only loops with
the `llvm.loop.mustprogress` metadata or loops contained in functions
that are required to make progress (`mustprogress` or `willreturn`) are
checked for observable side-effects. If these loops do not have an
observable side-effect, then we delete them.

Loops without observable side-effects that do not satisfy the above
conditions will not be deleted.

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D86844
  • Loading branch information
atmnpatel committed Nov 7, 2020
1 parent 0cab911 commit 0b17c6e
Show file tree
Hide file tree
Showing 19 changed files with 274 additions and 30 deletions.
4 changes: 2 additions & 2 deletions clang/test/Misc/loop-opt-setup.c
Expand Up @@ -25,6 +25,6 @@ void Helper() {

// Check br i1 to make sure the loop is gone, there will still be a label branch for the infinite loop.
// CHECK-LABEL: Helper
// CHECK: br label
// CHECK: entry:
// CHECK-NOT: br i1
// CHECK: br label
// CHECK-NEXT: ret void
3 changes: 3 additions & 0 deletions llvm/include/llvm/Transforms/Utils/LoopUtils.h
Expand Up @@ -229,6 +229,9 @@ bool hasDisableAllTransformsHint(const Loop *L);
/// Look for the loop attribute that disables the LICM transformation heuristics.
bool hasDisableLICMTransformsHint(const Loop *L);

/// Look for the loop attribute that requires progress within the loop.
bool hasMustProgress(const Loop *L);

/// The mode sets how eager a transformation should be applied.
enum TransformationMode {
/// The pass can use heuristics to determine whether a transformation should
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Transforms/Scalar/LoopDeletion.cpp
Expand Up @@ -210,8 +210,10 @@ static LoopDeletionResult deleteLoopIfDead(Loop *L, DominatorTree &DT,
// Don't remove loops for which we can't solve the trip count.
// They could be infinite, in which case we'd be changing program behavior.
const SCEV *S = SE.getConstantMaxBackedgeTakenCount(L);
if (isa<SCEVCouldNotCompute>(S)) {
LLVM_DEBUG(dbgs() << "Could not compute SCEV MaxBackedgeTakenCount.\n");
if (isa<SCEVCouldNotCompute>(S) &&
!L->getHeader()->getParent()->mustProgress() && !hasMustProgress(L)) {
LLVM_DEBUG(dbgs() << "Could not compute SCEV MaxBackedgeTakenCount and was "
"not required to make progress.\n");
return Changed ? LoopDeletionResult::Modified
: LoopDeletionResult::Unmodified;
}
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Transforms/Utils/LoopUtils.cpp
Expand Up @@ -63,6 +63,7 @@ static cl::opt<bool> ForceReductionIntrinsic(

static const char *LLVMLoopDisableNonforced = "llvm.loop.disable_nonforced";
static const char *LLVMLoopDisableLICM = "llvm.licm.disable";
static const char *LLVMLoopMustProgress = "llvm.loop.mustprogress";

bool llvm::formDedicatedExitBlocks(Loop *L, DominatorTree *DT, LoopInfo *LI,
MemorySSAUpdater *MSSAU,
Expand Down Expand Up @@ -404,6 +405,10 @@ bool llvm::hasDisableLICMTransformsHint(const Loop *L) {
return getBooleanLoopAttribute(L, LLVMLoopDisableLICM);
}

bool llvm::hasMustProgress(const Loop *L) {
return getBooleanLoopAttribute(L, LLVMLoopMustProgress);
}

TransformationMode llvm::hasUnrollTransformation(Loop *L) {
if (getBooleanLoopAttribute(L, "llvm.loop.unroll.disable"))
return TM_SuppressedByUser;
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Other/loop-deletion-printer.ll
Expand Up @@ -14,7 +14,7 @@
; DELETED-BUT-PRINTED: IR Dump {{.*}}LoopDeletionPass {{.*invalidated:}}
; DELETED-BUT-PRINTED-NOT: IR Dump {{.*}}LoopInstSimplifyPass

define void @deleteme() {
define void @deleteme() willreturn {
entry:
br label %loop
loop:
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Other/loop-pm-invalidation.ll
Expand Up @@ -227,7 +227,7 @@ exit:
ret void
}

define void @dead_loop() {
define void @dead_loop() willreturn {
; CHECK-LOOP-INV: Starting {{.*}}Function pass manager run
; CHECK-LOOP-INV-NEXT: Starting {{.*}}Function pass manager run
; CHECK-LOOP-INV-NEXT: Running pass: LoopSimplifyPass
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/LICM/2003-02-27-PreheaderProblem.ll
Expand Up @@ -6,7 +6,7 @@
; RUN: opt < %s -licm -loop-deletion -simplifycfg -S | \
; RUN: not grep "br "

define i32 @main(i32 %argc) {
define i32 @main(i32 %argc) willreturn {
; <label>:0
br label %bb5
bb5: ; preds = %bb5, %0
Expand Down
Expand Up @@ -15,7 +15,7 @@
; DT: [3] %for.body
; DT: [4] %for.cond3.loopexit

define i32 @fn1() {
define i32 @fn1() willreturn {
entry:
br label %for.cond

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/LoopDeletion/basic-remark.ll
Expand Up @@ -10,7 +10,7 @@
; CHECK-NEXT: Args:
; CHECK-NEXT: - String: Loop deleted because it is invariant
; CHECK-NEXT: ...
define i32 @main() local_unnamed_addr #0 {
define i32 @main() local_unnamed_addr willreturn {
entry:
br label %for.cond, !dbg !9

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/LoopDeletion/diundef.ll
Expand Up @@ -5,7 +5,7 @@ target triple = "x86_64-apple-macosx10.14.0"

@a = common local_unnamed_addr global i32 0, align 4, !dbg !0

define i32 @b() local_unnamed_addr !dbg !12 {
define i32 @b() local_unnamed_addr willreturn !dbg !12 {
entry:
call void @llvm.dbg.value(metadata i32 0, metadata !16, metadata !DIExpression()), !dbg !17
br label %for.cond, !dbg !18
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/LoopDeletion/invalidation.ll
Expand Up @@ -8,7 +8,7 @@
; RUN: | FileCheck %s --check-prefixes=CHECK,AFTER


define void @foo(i64 %n, i64 %m) nounwind {
define void @foo(i64 %n, i64 %m) nounwind willreturn {
; CHECK-LABEL: @foo(

entry:
Expand Down
Expand Up @@ -11,7 +11,7 @@
; CHECK: return:
; CHECK-NEXT: ret void

define void @foo(i64 %n, i64 %m) nounwind {
define void @foo(i64 %n, i64 %m) nounwind willreturn {
entry:
br label %bb

Expand Down
8 changes: 5 additions & 3 deletions llvm/test/Transforms/LoopDeletion/multiple-exits.ll
Expand Up @@ -10,7 +10,7 @@
; RUN: opt < %s -passes=loop-deletion -S | FileCheck %s --check-prefixes=CHECK,AFTER


define void @foo(i64 %n, i64 %m) nounwind {
define void @foo(i64 %n, i64 %m) #0 {
; CHECK-LABEL: @foo(

entry:
Expand Down Expand Up @@ -43,7 +43,7 @@ return:
; CHECK-NEXT: ret void
}

define i64 @bar(i64 %n, i64 %m, i64 %maybe_zero) nounwind {
define i64 @bar(i64 %n, i64 %m, i64 %maybe_zero) #0 {
; CHECK-LABEL: @bar(

entry:
Expand Down Expand Up @@ -95,7 +95,7 @@ return:

; This function has a loop which looks like @bar's but that cannot be deleted
; because which path we exit through determines which value is selected.
define i64 @baz(i64 %n, i64 %m, i64 %maybe_zero) nounwind {
define i64 @baz(i64 %n, i64 %m, i64 %maybe_zero) #0 {
; CHECK-LABEL: @baz(

entry:
Expand Down Expand Up @@ -136,3 +136,5 @@ return:
; CHECK-NEXT: %[[X:.*]] = phi i64 [ 12, %bb ], [ 10, %bb2 ], [ 10, %bb3 ]
; CHECK-NEXT: ret i64 %[[X]]
}

attributes #0 = { nounwind willreturn }
237 changes: 237 additions & 0 deletions llvm/test/Transforms/LoopDeletion/mustprogress.ll
@@ -0,0 +1,237 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
; RUN: opt < %s -loop-deletion -S | FileCheck %s

;; Original C Code:
;; void unknown_tripcount_mustprogress_attr_mustprogress_loopmd(int a, int b) {
;; for (; a < b;) ;
;; for (;;) ;
;; }

define void @unknown_tripcount_mustprogress_attr_mustprogress_loopmd(i32 %a, i32 %b) #0 {
; CHECK: Function Attrs: mustprogress
; CHECK-LABEL: define {{[^@]+}}@unknown_tripcount_mustprogress_attr_mustprogress_loopmd
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) [[ATTR0:#.*]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_END:%.*]]
; CHECK: for.end:
; CHECK-NEXT: unreachable
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond, !llvm.loop !2
for.end:
br label %for.cond1
for.cond1:
br label %for.cond1
}

;; Original C Code:
;; void unknown_tripcount_mustprogress_attr_no_mustprogress_loopmd(int a, int b) {
;; for (; a < b;) ;
;; for (;;) ;
;; }
;; => Removed mustprogress loop attribute

define void @unknown_tripcount_mustprogress_attr_no_mustprogess_loopmd(i32 %a, i32 %b) #0 {
; CHECK: Function Attrs: mustprogress
; CHECK-LABEL: define {{[^@]+}}@unknown_tripcount_mustprogress_attr_no_mustprogess_loopmd
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) [[ATTR0]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_END:%.*]]
; CHECK: for.end:
; CHECK-NEXT: unreachable
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
br label %for.cond1
for.cond1:
br label %for.cond1
}

;; Original C Code:
;; void known_tripcount_no_mustprogress_attr_no_mustprogress_loopmd() {
;; for (int i = 0; i < 5; i++) ;
;; }

define void @known_tripcount_no_mustprogress_attr_no_mustprogress_loopmd() {
; CHECK-LABEL: define {{[^@]+}}@known_tripcount_no_mustprogress_attr_no_mustprogress_loopmd() {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_END:%.*]]
; CHECK: for.end:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
%cmp = icmp slt i32 %i.0, 5
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.inc
for.inc:
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end:
ret void
}

;; Original C Code:
;; void known_tripcount_no_mustprogress_attr_mustprogress_loopmd() {
;; for (int i = 0; i < 5; i++) ;
;; }
;; => Added mustprogress loop attribute

define void @known_tripcount_no_mustprogress_attr_mustprogress_loopmd() {
; CHECK-LABEL: define {{[^@]+}}@known_tripcount_no_mustprogress_attr_mustprogress_loopmd() {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_END:%.*]]
; CHECK: for.end:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
%cmp = icmp slt i32 %i.0, 5
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.inc
for.inc:
%inc = add nsw i32 %i.0, 1
br label %for.cond, !llvm.loop !4
for.end:
ret void
}

;; Original C Code:
;; void known_tripcount_mustprogress_attr_no_mustprogress_loopmd() {
;; for (int i = 0; i < 5; i++) ;
;; }
;; => Added mustprogress function attribute

define void @known_tripcount_mustprogress_attr_no_mustprogress_loopmd() #0 {
; CHECK: Function Attrs: mustprogress
; CHECK-LABEL: define {{[^@]+}}@known_tripcount_mustprogress_attr_no_mustprogress_loopmd
; CHECK-SAME: () [[ATTR0]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_END:%.*]]
; CHECK: for.end:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
%cmp = icmp slt i32 %i.0, 5
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.inc
for.inc:
%inc = add nsw i32 %i.0, 1
br label %for.cond
for.end:
ret void
}

;; Original C Code:
;; void known_tripcount_mustprogress_attr_mustprogress_loopmd() {
;; for (int i = 0; i < 5; i++) ;
;; }
;; => Added mustprogress function and mustprogress loop attribute

define void @known_tripcount_mustprogress_attr_mustprogress_loopmd() #0 {
; CHECK: Function Attrs: mustprogress
; CHECK-LABEL: define {{[^@]+}}@known_tripcount_mustprogress_attr_mustprogress_loopmd
; CHECK-SAME: () [[ATTR0]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_END:%.*]]
; CHECK: for.end:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
%cmp = icmp slt i32 %i.0, 5
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.inc
for.inc:
%inc = add nsw i32 %i.0, 1
br label %for.cond, !llvm.loop !5
for.end:
ret void
}

;; Original C Code:
;; void unknown_tripcount_no_mustprogress_attr_mustprogress_loopmd(int a, int b) {
;; for (; a < b;) ;
;; }
;; => Added mustprogress loop attribute

define void @unknown_tripcount_no_mustprogress_attr_mustprogress_loopmd(i32 %a, i32 %b) {
; CHECK-LABEL: define {{[^@]+}}@unknown_tripcount_no_mustprogress_attr_mustprogress_loopmd
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_END:%.*]]
; CHECK: for.end:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond, !llvm.loop !6
for.end:
ret void
}

;; Original C Code:
;; void unknown_tripcount_no_mustprogress_attr_no_mustprogress_loopmd(int a, int b) {
;; for (; a < b;) ;
;; }

define void @unknown_tripcount_no_mustprogress_attr_no_mustprogress_loopmd(i32 %a, i32 %b) {
; CHECK-LABEL: define {{[^@]+}}@unknown_tripcount_no_mustprogress_attr_no_mustprogress_loopmd
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR_COND:%.*]]
; CHECK: for.cond:
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
; CHECK: for.body:
; CHECK-NEXT: br label [[FOR_COND]]
; CHECK: for.end:
; CHECK-NEXT: ret void
;
entry:
br label %for.cond
for.cond:
%cmp = icmp slt i32 %a, %b
br i1 %cmp, label %for.body, label %for.end
for.body:
br label %for.cond
for.end:
ret void
}

; CHECK: attributes [[ATTR0]] = { mustprogress }

attributes #0 = { mustprogress }
!2 = distinct !{!2, !3}
!3 = !{!"llvm.loop.mustprogress"}
!4 = distinct !{!4, !3}
!5 = distinct !{!5, !3}
!6 = distinct !{!6, !3}

0 comments on commit 0b17c6e

Please sign in to comment.