diff --git a/llvm/include/llvm/Transforms/Utils/LoopUtils.h b/llvm/include/llvm/Transforms/Utils/LoopUtils.h --- a/llvm/include/llvm/Transforms/Utils/LoopUtils.h +++ b/llvm/include/llvm/Transforms/Utils/LoopUtils.h @@ -227,6 +227,9 @@ /// 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 diff --git a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp --- a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp +++ b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp @@ -87,7 +87,10 @@ for (auto &I : L->blocks()) if (any_of(*I, [](Instruction &I) { return I.mayHaveSideEffects(); })) return false; - return true; + + // If this loop was explicitly required to make progress or the function was + // not given permission to make no progress, we can remove this loop. + return hasMustProgress(L) || !L->getHeader()->getParent()->mayProgress(); } /// This function returns true if there is no viable path from the @@ -202,7 +205,8 @@ // 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(S)) { + if (isa(S) && + !L->getHeader()->getParent()->mayProgress()) { LLVM_DEBUG(dbgs() << "Could not compute SCEV MaxBackedgeTakenCount.\n"); return Changed ? LoopDeletionResult::Modified : LoopDeletionResult::Unmodified; diff --git a/llvm/lib/Transforms/Utils/LoopUtils.cpp b/llvm/lib/Transforms/Utils/LoopUtils.cpp --- a/llvm/lib/Transforms/Utils/LoopUtils.cpp +++ b/llvm/lib/Transforms/Utils/LoopUtils.cpp @@ -63,6 +63,7 @@ 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, @@ -404,6 +405,10 @@ 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; diff --git a/llvm/test/Transforms/LoopDeletion/mustprogress.ll b/llvm/test/Transforms/LoopDeletion/mustprogress.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/LoopDeletion/mustprogress.ll @@ -0,0 +1,247 @@ +; RUN: opt < %s -loop-deletion -S | FileCheck %s + +;; Original C Code: +;; void unknown_tripcount_mayprogess_attr_mustprogress_loopmd(int a, int b) { +;; for (; a < b;) ; +;; for (;;) ; +;; } + +; CHECK: void [[FUNC1:@.*]](i32 [[VAR1:%.*]], i32 [[VAR2:%.*]]) [[ATTR0:#.*]] { +; CHECK: entry: +; CHECK: br label %[[FOR_END:.*]] +; CHECK: [[FOR_END]]: +; CHECK: br label %[[FOR_COND1:.*]] +; CHECK: [[FOR_COND1]]: +; CHECK: br label %[[FOR_COND1]] +; CHECK: } + +define void @unknown_tripcount_mayprogess_attr_mustprogress_loopmd(i32 %a, i32 %b) #0 { +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_mayprogess_attr_no_mustprogress_loopmd(int a, int b) { +;; for (; a < b;) ; +;; for (;;) ; +;; } +;; => Removed mustprogress loop attribute + +; CHECK: void [[FUNC2:@.*]](i32 [[VAR1:%.*]], i32 [[VAR2:%.*]]) [[ATTR0]] { +; CHECK: entry: +; CHECK: br label %[[FOR_COND:.*]] +; CHECK: [[FOR_COND]]: +; CHECK: [[CMP:%.*]] = icmp slt i32 [[VAR1]], [[VAR2]] +; CHECK: br i1 [[CMP]], label %[[FOR_BODY:.*]], label %[[FOR_END:.*]] +; CHECK: [[FOR_BODY]]: +; CHECK: br label %[[FOR_COND]] +; CHECK: [[FOR_END]]: +; CHECK: br label %[[FOR_COND1:.*]] +; CHECK: [[FOR_COND1]]: +; CHECK: br label %[[FOR_COND1]] +; CHECK: } + +define void @unknown_tripcount_mayprogress_attr_no_mustprogess_loopmd(i32 %a, i32 %b) #0 { +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_mayprogess_attr_no_mustprogress_loopmd() { +;; for (int i = 0; i < 5; i++) ; +;; } + +; CHECK: define void [[FUNC3:@.*]]() { +; CHECK: entry: +; CHECK: br label %[[FOR_END:.*]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @known_tripcount_no_mayprogress_attr_no_mustprogress_loopmd() { +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_mayprogess_attr_mustprogress_loopmd() { +;; for (int i = 0; i < 5; i++) ; +;; } +;; => Added mustprogress loop attribute + +; CHECK: define void [[FUNC4:@.*]]() { +; CHECK: entry: +; CHECK: br label %[[FOR_END:.*]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @known_tripcount_no_mayprogress_attr_mustprogress_loopmd() { +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, !llvm.loop !2 +for.inc: + %inc = add nsw i32 %i.0, 1 + br label %for.cond +for.end: + ret void +} + +;; Original C Code: +;; void known_tripcount_mayprogess_attr_no_mustprogress_loopmd() { +;; for (int i = 0; i < 5; i++) ; +;; } +;; => Added mayprogress function attribute + +; CHECK: define void [[FUNC5:@.*]]() [[ATTR0]] { +; CHECK: entry: +; CHECK: br label %[[FOR_END:.*]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @known_tripcount_mayprogress_attr_no_mustprogress_loopmd() #0 { +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_mayprogess_attr_mustprogress_loopmd() { +;; for (int i = 0; i < 5; i++) ; +;; } +;; => Added mayprogress function and mustprogress loop attribute + +; CHECK: define void [[FUNC6:@.*]]() [[ATTR0]] { +; CHECK: entry: +; CHECK: br label %[[FOR_END:.*]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @known_tripcount_mayprogress_attr_mustprogress_loopmd() #0 { +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, !llvm.loop !2 +for.inc: + %inc = add nsw i32 %i.0, 1 + br label %for.cond +for.end: + ret void +} + +;; Original C Code: +;; void unknown_tripcount_no_mayprogess_attr_mustprogress_loopmd(int a, int b) { +;; for (; a < b;) ; +;; } +;; => Added mustprogress loop attribute + +; CHECK: define void [[FUNC7:@.*]](i32 [[VAR1:%.*]], i32 [[VAR2:%.*]]) { +; CHECK: entry: +; CHECK: br label %[[FOR_COND:.*]] +; CHECK: [[FOR_COND]]: +; CHECK: [[CMP:%.*]] = icmp slt i32 [[VAR1]], [[VAR2]] +; CHECK: br i1 [[CMP]], label %[[FOR_BODY:.*]], label %[[FOR_END:.*]] +; CHECK: [[FOR_BODY]]: +; CHECK: br label %[[FOR_COND]], !llvm.loop [[LOOP2:!.*]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @unknown_tripcount_no_mayprogress_attr_mustprogress_loopmd(i32 %a, i32 %b) { +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: + ret void +} + +;; Original C Code: +;; void unknown_tripcount_no_mayprogess_attr_no_mustprogress_loopmd(int a, int b) { +;; for (; a < b;) ; +;; } + +; CHECK: define void [[FUNC8:@.*]](i32 [[VAR1:%.*]], i32 [[VAR2:%.*]]) { +; CHECK: entry: +; CHECK: br label %[[FOR_COND:.*]] +; CHECK: [[FOR_COND]]: +; CHECK: [[CMP:%.*]] = icmp slt i32 [[VAR1]], [[VAR2]] +; CHECK: br i1 [[CMP]], label %[[FOR_BODY:.*]], label %[[FOR_END:.*]] +; CHECK: [[FOR_BODY]]: +; CHECK: br label %[[FOR_COND]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @unknown_tripcount_no_mayprogress_attr_no_mustprogress_loopmd(i32 %a, i32 %b) { +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]] = { mayprogress } +; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[GEN3:!.*]]} +; CHECK: [[GEN3]] = !{!"llvm.loop.mustprogress"} + +attributes #0 = { mayprogress } +!2 = distinct !{!2, !3} +!3 = !{!"llvm.loop.mustprogress"}