From 0b17c6e4479d62bd4ff05c48d6cdf340b198832f Mon Sep 17 00:00:00 2001 From: Atmn Patel Date: Sun, 25 Oct 2020 18:24:48 -0400 Subject: [PATCH] [LoopDeletion] Allows deletion of possibly infinite side-effect free 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 --- clang/test/Misc/loop-opt-setup.c | 4 +- .../include/llvm/Transforms/Utils/LoopUtils.h | 3 + llvm/lib/Transforms/Scalar/LoopDeletion.cpp | 6 +- llvm/lib/Transforms/Utils/LoopUtils.cpp | 5 + llvm/test/Other/loop-deletion-printer.ll | 2 +- llvm/test/Other/loop-pm-invalidation.ll | 2 +- .../LICM/2003-02-27-PreheaderProblem.ll | 2 +- .../LoopDeletion/2017-07-11-incremental-dt.ll | 2 +- .../Transforms/LoopDeletion/basic-remark.ll | 2 +- llvm/test/Transforms/LoopDeletion/diundef.ll | 2 +- .../Transforms/LoopDeletion/invalidation.ll | 2 +- .../LoopDeletion/multiple-exit-conditions.ll | 2 +- .../Transforms/LoopDeletion/multiple-exits.ll | 8 +- .../Transforms/LoopDeletion/mustprogress.ll | 237 ++++++++++++++++++ .../Transforms/LoopDeletion/no-exit-blocks.ll | 9 +- .../LoopDeletion/unreachable-loops.ll | 10 +- .../LoopDeletion/use-in-unreachable.ll | 2 +- llvm/test/Transforms/SCCP/calltest.ll | 2 +- .../Transforms/SimpleLoopUnswitch/pr37888.ll | 2 +- 19 files changed, 274 insertions(+), 30 deletions(-) create mode 100644 llvm/test/Transforms/LoopDeletion/mustprogress.ll diff --git a/clang/test/Misc/loop-opt-setup.c b/clang/test/Misc/loop-opt-setup.c index 322f5e0e6d4aa..43a9f85bca27f 100644 --- a/clang/test/Misc/loop-opt-setup.c +++ b/clang/test/Misc/loop-opt-setup.c @@ -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 diff --git a/llvm/include/llvm/Transforms/Utils/LoopUtils.h b/llvm/include/llvm/Transforms/Utils/LoopUtils.h index d741b5142e5bf..076ea2f4453bf 100644 --- a/llvm/include/llvm/Transforms/Utils/LoopUtils.h +++ b/llvm/include/llvm/Transforms/Utils/LoopUtils.h @@ -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 diff --git a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp index 065db647561ec..0db50d06ff26b 100644 --- a/llvm/lib/Transforms/Scalar/LoopDeletion.cpp +++ b/llvm/lib/Transforms/Scalar/LoopDeletion.cpp @@ -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(S)) { - LLVM_DEBUG(dbgs() << "Could not compute SCEV MaxBackedgeTakenCount.\n"); + if (isa(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; } diff --git a/llvm/lib/Transforms/Utils/LoopUtils.cpp b/llvm/lib/Transforms/Utils/LoopUtils.cpp index e10a2306547a7..4210cd66f3f6d 100644 --- a/llvm/lib/Transforms/Utils/LoopUtils.cpp +++ b/llvm/lib/Transforms/Utils/LoopUtils.cpp @@ -63,6 +63,7 @@ static cl::opt 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, @@ -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; diff --git a/llvm/test/Other/loop-deletion-printer.ll b/llvm/test/Other/loop-deletion-printer.ll index 6cb29ae374fbf..997905141982d 100644 --- a/llvm/test/Other/loop-deletion-printer.ll +++ b/llvm/test/Other/loop-deletion-printer.ll @@ -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: diff --git a/llvm/test/Other/loop-pm-invalidation.ll b/llvm/test/Other/loop-pm-invalidation.ll index 9deb17f124d6b..e6bd888ed32bd 100644 --- a/llvm/test/Other/loop-pm-invalidation.ll +++ b/llvm/test/Other/loop-pm-invalidation.ll @@ -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 diff --git a/llvm/test/Transforms/LICM/2003-02-27-PreheaderProblem.ll b/llvm/test/Transforms/LICM/2003-02-27-PreheaderProblem.ll index b54d520a91f76..8ff32219d4236 100644 --- a/llvm/test/Transforms/LICM/2003-02-27-PreheaderProblem.ll +++ b/llvm/test/Transforms/LICM/2003-02-27-PreheaderProblem.ll @@ -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 { ;