[LoopInfo] Limit the iterations to check whether a loop has dedicated exits
authorWei Mi <wmi@google.com>
Thu, 26 Sep 2019 15:36:25 +0000 (15:36 +0000)
committerWei Mi <wmi@google.com>
Thu, 26 Sep 2019 15:36:25 +0000 (15:36 +0000)
for extreme large case.

We had a case that a single loop which has 4000 exits and the average number
of predecessors of each exit is > 1000, and we found compiling the case spent
a significant amount of time on checking whether a loop has dedicated exits.
This patch adds a limit for the iterations to the check. With the patch, the
time to compile our testcase reduced from 1000s to 200s (clang release build).

Differential Revision: https://reviews.llvm.org/D67359

llvm-svn: 372990

llvm/include/llvm/Analysis/LoopInfo.h
llvm/include/llvm/Analysis/LoopInfoImpl.h
llvm/lib/Analysis/LoopInfo.cpp
llvm/test/Transforms/LICM/loop-max-dedicated-exit-iterations.ll [new file with mode: 0644]

index abf3863..8fe8158 100644 (file)
@@ -269,6 +269,9 @@ public:
 
   /// Return true if no exit block for the loop has a predecessor that is
   /// outside the loop.
+  /// Return false if a non-dedicated successor is found or it cannot finish
+  /// the check within MaxDedicateExitIterations iterations so return false
+  /// conservatively.
   bool hasDedicatedExits() const;
 
   /// Return all unique successor blocks of this loop.
index 2f38dde..acea41a 100644 (file)
 
 namespace llvm {
 
+// Max number of loop iterations to check whether a loop is a dedicated
+// exit loop.
+extern cl::opt<uint64_t> MaxDedicateExitIterations;
+
 //===----------------------------------------------------------------------===//
 // APIs for simple analysis of the loop. See header notes.
 
@@ -87,10 +91,16 @@ bool LoopBase<BlockT, LoopT>::hasDedicatedExits() const {
   // within the loop.
   SmallVector<BlockT *, 4> ExitBlocks;
   getExitBlocks(ExitBlocks);
-  for (BlockT *EB : ExitBlocks)
-    for (BlockT *Predecessor : children<Inverse<BlockT *>>(EB))
+  uint64_t Iterations = 0;
+  for (BlockT *EB : ExitBlocks) {
+    for (BlockT *Predecessor : children<Inverse<BlockT *>>(EB)) {
       if (!contains(Predecessor))
         return false;
+      Iterations++;
+    }
+    if (Iterations > MaxDedicateExitIterations)
+      return false;
+  }
   // All the requirements are met.
   return true;
 }
index 419f013..5d481a4 100644 (file)
@@ -54,6 +54,13 @@ static cl::opt<bool, true>
     VerifyLoopInfoX("verify-loop-info", cl::location(VerifyLoopInfo),
                     cl::Hidden, cl::desc("Verify loop info (time consuming)"));
 
+namespace llvm {
+cl::opt<uint64_t> MaxDedicateExitIterations(
+    "max-dedicate-exit-iterations", cl::Hidden, cl::init(100000),
+    cl::desc("Max number of loop iterations to check whether a loop is a "
+             "dedicated exit loop. "));
+}
+
 //===----------------------------------------------------------------------===//
 // Loop implementation
 //
diff --git a/llvm/test/Transforms/LICM/loop-max-dedicated-exit-iterations.ll b/llvm/test/Transforms/LICM/loop-max-dedicated-exit-iterations.ll
new file mode 100644 (file)
index 0000000..421f2d1
--- /dev/null
@@ -0,0 +1,102 @@
+; RUN: opt -S -licm -max-dedicate-exit-iterations=5 < %s | FileCheck %s
+; RUN: opt -S -licm -max-dedicate-exit-iterations=100 < %s | FileCheck %s -check-prefixes=SINK
+; RUN: opt -S -passes='require<opt-remark-emit>,loop(licm)' -max-dedicate-exit-iterations=5 < %s | FileCheck %s
+; RUN: opt -S -passes='require<opt-remark-emit>,loop(licm)' -max-dedicate-exit-iterations=100 < %s | FileCheck %s -check-prefixes=SINK
+; Code sink in LICM requires the loop has dedicated exits. Use code sink
+; in LICM to verify max-dedicate-exit-iterations is functioning.
+
+@_ZL1m = internal global i64 0, align 8
+@.str = private unnamed_addr constant [13 x i8] c"hello = %ld\0A\00", align 1
+@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_2.cc, i8* null }]
+
+declare dso_local i64 @_Z3goov() local_unnamed_addr #0
+
+; Function Attrs: argmemonly nounwind willreturn
+declare {}* @llvm.invariant.start.p0i8(i64 immarg, i8* nocapture)
+
+; Function Attrs: uwtable
+define dso_local void @_Z3fool(i64 %a) local_unnamed_addr {
+entry:
+  %cmp18.old.old.old.old.old = icmp slt i64 %a, 5
+  %cmp14.old.old.old.old = icmp slt i64 %a, 4
+  %cmp10.old.old.old = icmp slt i64 %a, 3
+  %cmp6.old.old = icmp slt i64 %a, 2
+  %cmp2.old = icmp slt i64 %a, 1
+  %cmp = icmp slt i64 %a, 0
+; Check load cannot be sinked out of loop when max-dedicate-exit-iterations=5
+; and hasDedicatedExits return false, so it is hoisted out of loop.
+; CHECK: load i64, i64* @_ZL1m, align 8
+; CHECK: br label %do.body
+; CHECK: do.body:
+  br label %do.body
+
+do.body:                                          ; preds = %do.body.backedge, %entry
+  %t0 = load i64, i64* @_ZL1m, align 8, !tbaa !2
+  %call = tail call i64 @_Z3goov()
+  switch i64 %call, label %do.cond [
+    i64 0, label %sw.bb
+    i64 1, label %sw.bb1
+    i64 2, label %sw.bb5
+    i64 3, label %sw.bb9
+    i64 4, label %sw.bb13
+    i64 5, label %sw.bb17
+  ]
+
+sw.bb:                                            ; preds = %do.body
+  br i1 %cmp, label %Done, label %do.body.backedge
+
+sw.bb1:                                           ; preds = %do.body
+  br i1 %cmp2.old, label %Done, label %do.body.backedge
+
+sw.bb5:                                           ; preds = %do.body
+  br i1 %cmp6.old.old, label %Done, label %do.body.backedge
+
+sw.bb9:                                           ; preds = %do.body
+  br i1 %cmp10.old.old.old, label %Done, label %do.body.backedge
+
+sw.bb13:                                          ; preds = %do.body
+  br i1 %cmp14.old.old.old.old, label %Done, label %do.body.backedge
+
+sw.bb17:                                          ; preds = %do.body
+  br i1 %cmp18.old.old.old.old.old, label %Done, label %do.body.backedge
+
+do.cond:                                          ; preds = %do.body
+  %cmp21.old.old.old.old.old.old = icmp slt i64 %call, 10000
+  br i1 %cmp21.old.old.old.old.old.old, label %do.body.backedge, label %Done
+
+do.body.backedge:                                 ; preds = %do.cond, %sw.bb17, %sw.bb13, %sw.bb9, %sw.bb5, %sw.bb1, %sw.bb
+  br label %do.body
+
+Done:                                             ; preds = %sw.bb, %sw.bb1, %sw.bb5, %sw.bb9, %sw.bb13, %sw.bb17, %do.cond
+; Check load is sinked out of loop when max-dedicate-exit-iterations=100
+; and hasDedicatedExits return true.
+; SINK: Done:
+; SINK: load i64, i64* @_ZL1m, align 8
+; SINK-NEXT: tail call {{.*}} @printf
+  %call22 = tail call i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i64 0, i64 0), i64 %t0)
+  ret void
+}
+
+; Function Attrs: nofree nounwind
+declare dso_local i32 @printf(i8* nocapture readonly, ...) local_unnamed_addr
+
+; Function Attrs: uwtable
+define internal void @_GLOBAL__sub_I_2.cc() section ".text.startup" {
+entry:
+  %call.i = tail call i64 @_Z3goov()
+  store i64 %call.i, i64* @_ZL1m, align 8, !tbaa !2
+  %t0 = tail call {}* @llvm.invariant.start.p0i8(i64 8, i8* bitcast (i64* @_ZL1m to i8*))
+  ret void
+}
+
+attributes #0 = { readonly }
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{!"clang version 10.0.0 (trunk 372630) (llvm/trunk 372631)"}
+!2 = !{!3, !3, i64 0}
+!3 = !{!"long", !4, i64 0}
+!4 = !{!"omnipotent char", !5, i64 0}
+!5 = !{!"Simple C++ TBAA"}