diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -662,6 +662,11 @@ return hasFnAttribute(Attribute::OptimizeForSize) || hasMinSize(); } + /// Determine if the function is permitted to not make progress. + bool doesNotMakeProgress() const { + return hasFnAttribute(Attribute::NoProgress); + } + /// copyAttributesFrom - copy all additional attributes (those not needed to /// create a Function) from the Function Src to this one. void copyAttributesFrom(const Function *Src); 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 hasNeedsProgress(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,11 @@ 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 hasNeedsProgress(L) || + !L->getHeader()->getParent()->doesNotMakeProgress(); } /// This function returns true if there is no viable path from the @@ -202,7 +206,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()->doesNotMakeProgress()) { 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 *LLVMLoopNeedsProgress = "llvm.loop.needs_progress"; bool llvm::formDedicatedExitBlocks(Loop *L, DominatorTree *DT, LoopInfo *LI, MemorySSAUpdater *MSSAU, @@ -404,6 +405,10 @@ return getBooleanLoopAttribute(L, LLVMLoopDisableLICM); } +bool llvm::hasNeedsProgress(const Loop *L) { + return getBooleanLoopAttribute(L, LLVMLoopNeedsProgress); +} + TransformationMode llvm::hasUnrollTransformation(Loop *L) { if (getBooleanLoopAttribute(L, "llvm.loop.unroll.disable")) return TM_SuppressedByUser; diff --git a/llvm/test/Transforms/LoopDeletion/noprogress.ll b/llvm/test/Transforms/LoopDeletion/noprogress.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/LoopDeletion/noprogress.ll @@ -0,0 +1,130 @@ +; RUN: opt < %s -loop-deletion -S | FileCheck %s + +; 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 @F(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 +} + +; 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 @G(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 +} + +; CHECK: define void [[FUNC3:@.*]]() { +; CHECK: entry: +; CHECK: br label %[[FOR_END:.*]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @H() { +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 +} + +; CHECK: define void [[FUNC4:@.*]]() { +; CHECK: entry: +; CHECK: br label %[[FOR_END:.*]] +; CHECK: [[FOR_END]]: +; CHECK: ret void +; CHECK: } + +define void @I() { +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 +} + +; CHECK: define void [[FUNC5:@.*]](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 @J(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 +} + +; CHECK: attributes [[ATTR0]] = { noprogress } +; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[GEN3:!.*]]} +; CHECK: [[GEN3]] = !{!"llvm.loop.needs_progress"} + +attributes #0 = { noprogress } +!2 = distinct !{!2, !3} +!3 = !{!"llvm.loop.needs_progress"}