[llvm] b285b33 - [Attributor] Detect possibly unbounded cycles in functions

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 13 09:18:31 PDT 2020


Author: omarahmed1111
Date: 2020-03-13T11:17:33-05:00
New Revision: b285b333dcd04dc79cb864e7178d56b74815caec

URL: https://github.com/llvm/llvm-project/commit/b285b333dcd04dc79cb864e7178d56b74815caec
DIFF: https://github.com/llvm/llvm-project/commit/b285b333dcd04dc79cb864e7178d56b74815caec.diff

LOG: [Attributor] Detect possibly unbounded cycles in functions

This patch add mayContainUnboundedCycle helper function which checks whether a function has any cycle which we don't know if it is bounded or not.
Loops with maximum trip count are considered bounded, any other cycle not.
It also contains some fixed tests and some added tests contain bounded and
unbounded loops and non-loop cycles.

Reviewed By: jdoerfert, uenoku, baziotis

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

Added: 
    

Modified: 
    llvm/include/llvm/Analysis/MustExecute.h
    llvm/lib/Analysis/MustExecute.cpp
    llvm/lib/Transforms/IPO/Attributor.cpp
    llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll
    llvm/test/Transforms/Attributor/willreturn.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/MustExecute.h b/llvm/include/llvm/Analysis/MustExecute.h
index 587ebcbfb8f8..7a8c3722df5e 100644
--- a/llvm/include/llvm/Analysis/MustExecute.h
+++ b/llvm/include/llvm/Analysis/MustExecute.h
@@ -176,6 +176,8 @@ class ICFLoopSafetyInfo: public LoopSafetyInfo {
   virtual ~ICFLoopSafetyInfo() {};
 };
 
+bool mayContainIrreducibleControl(const Function &F, const LoopInfo *LI);
+
 struct MustBeExecutedContextExplorer;
 
 /// Enum that allows us to spell out the direction.

diff  --git a/llvm/lib/Analysis/MustExecute.cpp b/llvm/lib/Analysis/MustExecute.cpp
index ec285c51f160..bea973e8a94e 100644
--- a/llvm/lib/Analysis/MustExecute.cpp
+++ b/llvm/lib/Analysis/MustExecute.cpp
@@ -484,13 +484,13 @@ static bool maybeEndlessLoop(const Loop &L) {
   return true;
 }
 
-static bool mayContainIrreducibleControl(const Function &F, const LoopInfo *LI) {
+bool llvm::mayContainIrreducibleControl(const Function &F, const LoopInfo *LI) {
   if (!LI)
     return false;
   using RPOTraversal = ReversePostOrderTraversal<const Function *>;
   RPOTraversal FuncRPOT(&F);
-  return !containsIrreducibleCFG<const BasicBlock *, const RPOTraversal,
-                                 const LoopInfo>(FuncRPOT, *LI);
+  return containsIrreducibleCFG<const BasicBlock *, const RPOTraversal,
+                                const LoopInfo>(FuncRPOT, *LI);
 }
 
 /// Lookup \p Key in \p Map and return the result, potentially after

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index b8f6b217f03a..a3ee90acc166 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -28,6 +28,7 @@
 #include "llvm/Analysis/LazyValueInfo.h"
 #include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
+#include "llvm/Analysis/MustExecute.h"
 #include "llvm/Analysis/ScalarEvolution.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/Argument.h"
@@ -36,9 +37,9 @@
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/NoFolder.h"
 #include "llvm/IR/Verifier.h"
 #include "llvm/InitializePasses.h"
-#include "llvm/IR/NoFolder.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
@@ -2491,28 +2492,34 @@ struct AAUndefinedBehaviorFunction final : AAUndefinedBehaviorImpl {
 
 /// ------------------------ Will-Return Attributes ----------------------------
 
-// Helper function that checks whether a function has any cycle.
-// TODO: Replace with more efficent code
-static bool containsCycle(Function &F) {
-  SmallPtrSet<BasicBlock *, 32> Visited;
-
-  // Traverse BB by dfs and check whether successor is already visited.
-  for (BasicBlock *BB : depth_first(&F)) {
-    Visited.insert(BB);
-    for (auto *SuccBB : successors(BB)) {
-      if (Visited.count(SuccBB))
+// Helper function that checks whether a function has any cycle which we don't
+// know if it is bounded or not.
+// Loops with maximum trip count are considered bounded, any other cycle not.
+static bool mayContainUnboundedCycle(Function &F, Attributor &A) {
+  ScalarEvolution *SE =
+      A.getInfoCache().getAnalysisResultForFunction<ScalarEvolutionAnalysis>(F);
+  LoopInfo *LI = A.getInfoCache().getAnalysisResultForFunction<LoopAnalysis>(F);
+  // If either SCEV or LoopInfo is not available for the function then we assume
+  // any cycle to be unbounded cycle.
+  // We use scc_iterator which uses Tarjan algorithm to find all the maximal
+  // SCCs.To detect if there's a cycle, we only need to find the maximal ones.
+  if (!SE || !LI) {
+    for (scc_iterator<Function *> SCCI = scc_begin(&F); !SCCI.isAtEnd(); ++SCCI)
+      if (SCCI.hasCycle())
         return true;
-    }
+    return false;
   }
-  return false;
-}
 
-// Helper function that checks the function have a loop which might become an
-// endless loop
-// FIXME: Any cycle is regarded as endless loop for now.
-//        We have to allow some patterns.
-static bool containsPossiblyEndlessLoop(Function *F) {
-  return containsCycle(*F);
+  // If there's irreducible control, the function may contain non-loop cycles.
+  if (mayContainIrreducibleControl(F, LI))
+    return true;
+
+  // Any loop that does not have a max trip count is considered unbounded cycle.
+  for (auto *L : LI->getLoopsInPreorder()) {
+    if (!SE->getSmallConstantMaxTripCount(L))
+      return true;
+  }
+  return false;
 }
 
 struct AAWillReturnImpl : public AAWillReturn {
@@ -2523,7 +2530,7 @@ struct AAWillReturnImpl : public AAWillReturn {
     AAWillReturn::initialize(A);
 
     Function *F = getAssociatedFunction();
-    if (!F || !A.isFunctionIPOAmendable(*F) || containsPossiblyEndlessLoop(F))
+    if (!F || !A.isFunctionIPOAmendable(*F) || mayContainUnboundedCycle(*F, A))
       indicatePessimisticFixpoint();
   }
 

diff  --git a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll
index 82da96e05d57..1ddde3a9bcb4 100644
--- a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll
+++ b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll
@@ -101,7 +101,7 @@ return:                                           ; preds = %if.end, %if.then
   ret i32* %retval.0
 }
 
-; CHECK: Function Attrs: argmemonly nofree norecurse nosync nounwind
+; CHECK: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn
 ; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* nofree readnone %n0, i32* nocapture nofree readonly %r0, i32* nofree returned writeonly "no-capture-maybe-returned" %w0)
 define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
@@ -160,6 +160,7 @@ entry:
 ;
 ; CHECK-NOT: attributes #
 ; CHECK: attributes #{{.*}} = { argmemonly nofree nosync nounwind }
-; CHECK: attributes #{{.*}} = { argmemonly nofree norecurse nosync nounwind }
+; CHECK: attributes #{{.*}} = { argmemonly nofree norecurse nosync nounwind willreturn }
 ; CHECK: attributes #{{.*}} = { nosync nounwind }
+; CHECK: attributes #{{.*}} = { norecurse nosync nounwind willreturn }
 ; CHECK-NOT: attributes #

diff  --git a/llvm/test/Transforms/Attributor/willreturn.ll b/llvm/test/Transforms/Attributor/willreturn.ll
index b1b68b3f8807..f521293e5a97 100644
--- a/llvm/test/Transforms/Attributor/willreturn.ll
+++ b/llvm/test/Transforms/Attributor/willreturn.ll
@@ -87,9 +87,8 @@ define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr #0 {
 ;   return ans;
 ; }
 
-; FIXME: missing willreturn
-; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable
-; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
 ; ATTRIBUTOR-NEXT: define i32 @fact_loop(i32 %0) local_unnamed_addr
 define i32 @fact_loop(i32 %0) local_unnamed_addr #0 {
   %2 = icmp slt i32 %0, 1
@@ -296,7 +295,7 @@ declare i32 @__gxx_personality_v0(...)
 
 
 ; TEST 11 (positive case)
-; counstant trip count
+; constant trip count
 ; int loop_constant_trip_count(int*p){
 ;    int ans = 0;
 ;    for(int i = 0;i<10;i++){
@@ -305,9 +304,8 @@ declare i32 @__gxx_personality_v0(...)
 ;    return ans;
 ; }
 
-; FIXME: missing willreturn
-; ATTRIBUTOR_MODULE: Function Attrs: argmemonly nofree noinline nosync nounwind readonly uwtable
-; ATTRIBUTOR_CGSCC: Function Attrs: argmemonly nofree noinline norecurse nosync nounwind readonly uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: argmemonly nofree noinline nosync nounwind readonly uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: argmemonly nofree noinline norecurse nosync nounwind readonly uwtable willreturn
 ; ATTRIBUTOR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture nofree readonly %0)
 define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 {
   br label %3
@@ -374,9 +372,8 @@ define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2,
 ;  }
 
 
-; FIXME: missing willreturn
-; ATTRIBUTOR_MODULE: Function Attrs: argmemonly nofree noinline nosync nounwind readonly uwtable
-; ATTRIBUTOR_CGSCC: Function Attrs: argmemonly nofree noinline norecurse nosync nounwind readonly uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: argmemonly nofree noinline nosync nounwind readonly uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: argmemonly nofree noinline norecurse nosync nounwind readonly uwtable willreturn
 ; ATTRIBUTOR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture nofree readonly %1) local_unnamed_addr
 
 define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr #0 {
@@ -434,9 +431,8 @@ unreachable_label:
   unreachable
 }
 
-; FIXME: missing willreturn
-; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable
-; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
 ; ATTRIBUTOR-NEXT: define i32 @unreachable_exit_positive2(i32 %0)
 define i32 @unreachable_exit_positive2(i32) local_unnamed_addr #0 {
   %2 = icmp slt i32 %0, 1
@@ -504,5 +500,273 @@ define void @call_longjmp(i8* nocapture readnone %0) local_unnamed_addr #0 {
 }
 
 
+; TEST 16 (negative case) 
+; int infinite_loop_inside_bounded_loop(int n) {
+;   int ans = 0;
+;   for (int i = 0; i < n; i++) {
+;     while (1)
+;       ans++;
+;   }
+;   return ans;
+; }
+
+; ATTRIBUTOR_MODULE: Function Attrs:  nofree nosync nounwind readnone
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree norecurse nosync nounwind readnone
+; ATTRIBUTOR-NOT: willreturn
+; ATTRIBUTOR-NEXT: define i32 @infinite_loop_inside_bounded_loop(i32 %n)
+define i32 @infinite_loop_inside_bounded_loop(i32 %n) {
+entry:
+  br label %for.cond
+
+for.cond:                                         ; preds = %for.inc, %entry
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %for.body, label %for.cond.cleanup
+
+for.cond.cleanup:                                 ; preds = %for.cond
+  br label %for.end
+
+for.body:                                         ; preds = %for.cond
+  br label %while.cond
+
+while.cond:                                       ; preds = %while.body, %for.body
+  br label %while.body
+
+while.body:                                       ; preds = %while.cond
+  br label %while.cond
+
+for.inc:                                          ; No predecessors!
+  br label %for.cond
+
+for.end:                                          ; preds = %for.cond.cleanup
+  ret i32 0
+}
+
+
+; TEST 17 (positive case)
+; Nested loops with constant max trip count
+; int bounded_nested_loops(int n) {
+;   int ans = 0;
+;   for (int i = 0; i < n; i++) {
+;     while (n--) {
+;       ans++;
+;     }
+;   }
+;   return ans;
+; }
+
+; ATTRIBUTOR_MODULE: Function Attrs:  nofree nosync nounwind readnone willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; ATTRIBUTOR-NEXT: define i32 @bounded_nested_loops(i32 %n)
+define i32 @bounded_nested_loops(i32 %n) {
+entry:
+  br label %for.cond
+
+for.cond:                                         ; preds = %for.inc, %entry
+  %i.0 = phi i32 [ 0, %entry ], [ %inc1, %for.inc ]
+  %ans.0 = phi i32 [ 0, %entry ], [ %tmp, %for.inc ]
+  %n.addr.0 = phi i32 [ %n, %entry ], [ -1, %for.inc ]
+  %cmp = icmp slt i32 %i.0, %n.addr.0
+  br i1 %cmp, label %for.body, label %for.cond.cleanup
+
+for.cond.cleanup:                                 ; preds = %for.cond
+  %ans.0.lcssa = phi i32 [ %ans.0, %for.cond ]
+  br label %for.end
+
+for.body:                                         ; preds = %for.cond
+  br label %while.cond
+
+while.cond:                                       ; preds = %while.body, %for.body
+  br i1 true, label %while.end, label %while.body
+
+while.body:                                       ; preds = %while.cond
+  br label %while.cond
+
+while.end:                                        ; preds = %while.cond
+  %tmp = add i32 %n.addr.0, %ans.0
+  br label %for.inc
+
+for.inc:                                          ; preds = %while.end
+  %inc1 = add nuw nsw i32 %i.0, 1
+  br label %for.cond
+
+for.end:                                          ; preds = %for.cond.cleanup
+  ret i32 %ans.0.lcssa
+}
+
+
+; TEST 18 (negative case)
+; int bounded_loop_inside_unbounded_loop(int n) {
+;   int ans = 0;
+;   while (n++) {
+;     for (int i = 0; i < n; i++) {
+;       ans++;
+;     }
+;   }
+;   return ans;
+; }
+
+; ATTRIBUTOR_MODULE: Function Attrs:  nofree nosync nounwind readnone
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree norecurse nosync nounwind readnone
+; ATTRIBUTOR-NOT: willreturn
+; ATTRIBUTOR-NEXT: define i32 @bounded_loop_inside_unbounded_loop(i32 %n)
+define i32 @bounded_loop_inside_unbounded_loop(i32 %n) {
+entry:
+  br label %while.cond
+
+while.cond:                                       ; preds = %for.end, %entry
+  %ans.0 = phi i32 [ 0, %entry ], [ %tmp2, %for.end ]
+  %n.addr.0 = phi i32 [ %n, %entry ], [ %inc, %for.end ]
+  %tmp = icmp sgt i32 %n.addr.0, -1
+  %smax = select i1 %tmp, i32 %n.addr.0, i32 -1
+  %inc = add nsw i32 %n.addr.0, 1
+  %tobool = icmp eq i32 %n.addr.0, 0
+  br i1 %tobool, label %while.end, label %while.body
+
+while.body:                                       ; preds = %while.cond
+  %tmp1 = add i32 %ans.0, 1
+  br label %for.cond
+
+for.cond:                                         ; preds = %for.inc, %while.body
+  br i1 true, label %for.cond.cleanup, label %for.body
+
+for.cond.cleanup:                                 ; preds = %for.cond
+  %tmp2 = add i32 %tmp1, %smax
+  br label %for.end
+
+for.body:                                         ; preds = %for.cond
+  br label %for.inc
+
+for.inc:                                          ; preds = %for.body
+  br label %for.cond
+
+for.end:                                          ; preds = %for.cond.cleanup
+  br label %while.cond
+
+while.end:                                        ; preds = %while.cond
+  %ans.0.lcssa = phi i32 [ %ans.0, %while.cond ]
+  ret i32 %ans.0.lcssa
+}
+
+
+; TEST 19 (negative case)
+; int nested_unbounded_loops(int n) {
+;   int ans = 0;
+;   while (n--) {
+;     while (n--) {
+;       ans++;
+;     }
+;     while (n--) {
+;       ans++;
+;     }
+;   }
+;   return ans;
+; }
+
+; ATTRIBUTOR_MODULE: Function Attrs:  nofree nosync nounwind readnone
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree norecurse nosync nounwind readnone
+; ATTRIBUTOR-NOT: willreturn
+; ATTRIBUTOR-NEXT: define i32 @nested_unbounded_loops(i32 %n)
+define i32 @nested_unbounded_loops(i32 %n) {
+entry:
+  br label %while.cond
+
+while.cond:                                       ; preds = %while.end10, %entry
+  %ans.0 = phi i32 [ 0, %entry ], [ %tmp1, %while.end10 ]
+  %n.addr.0 = phi i32 [ %n, %entry ], [ -1, %while.end10 ]
+  %tobool = icmp eq i32 %n.addr.0, 0
+  br i1 %tobool, label %while.end11, label %while.body
+
+while.body:                                       ; preds = %while.cond
+  br label %while.cond1
+
+while.cond1:                                      ; preds = %while.body4, %while.body
+  br i1 true, label %while.end, label %while.body4
+
+while.body4:                                      ; preds = %while.cond1
+  br label %while.cond1
+
+while.end:                                        ; preds = %while.cond1
+  %tmp = add i32 %n.addr.0, -2
+  br label %while.cond5
+
+while.cond5:                                      ; preds = %while.body8, %while.end
+  br i1 true, label %while.end10, label %while.body8
+
+while.body8:                                      ; preds = %while.cond5
+  br label %while.cond5
+
+while.end10:                                      ; preds = %while.cond5
+  %tmp1 = add i32 %tmp, %ans.0
+  br label %while.cond
+
+while.end11:                                      ; preds = %while.cond
+  %ans.0.lcssa = phi i32 [ %ans.0, %while.cond ]
+  ret i32 %ans.0.lcssa
+}
+
+
+; TEST 20 (negative case)
+;    void non_loop_cycle(int n) {
+;      if (fact_loop(n)>5)
+;        goto entry1;
+;      else
+;        goto entry2;
+;
+;    entry1:
+;      if (fact_loop(n)>5)
+;        goto exit;
+;      else
+;        goto entry2;
+;    entry2:
+;      if (fact_loop(n)>5)
+;        goto exit;
+;      else
+;        goto entry1;
+;    exit:
+;      return;
+;    }
+
+; ATTRIBUTOR_MODULE: Function Attrs:  nofree nosync nounwind readnone
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree norecurse nosync nounwind readnone
+; ATTRIBUTOR-NOT: willreturn
+; ATTRIBUTOR-NEXT: define void @non_loop_cycle(i32 %n)
+define void @non_loop_cycle(i32 %n) {
+entry:
+  %call = call i32 @fact_loop(i32 %n)
+  %cmp = icmp sgt i32 %call, 5
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  br label %entry1
+
+if.else:                                          ; preds = %entry
+  br label %entry2
+
+entry1:                                           ; preds = %if.else8, %if.then
+  %call1 = call i32 @fact_loop(i32 %n)
+  %cmp2 = icmp sgt i32 %call1, 5
+  br i1 %cmp2, label %if.then3, label %if.else4
+
+if.then3:                                         ; preds = %entry1
+  br label %exit
+
+if.else4:                                         ; preds = %entry1
+  br label %entry2
+
+entry2:                                           ; preds = %if.else4, %if.else
+  %call5 = call i32 @fact_loop(i32 %n)
+  %cmp6 = icmp sgt i32 %call5, 5
+  br i1 %cmp6, label %if.then7, label %if.else8
+
+if.then7:                                         ; preds = %entry2
+  br label %exit
+
+if.else8:                                         ; preds = %entry2
+  br label %entry1
+
+exit:                                             ; preds = %if.then7, %if.then3
+  ret void
+}
+
 attributes #0 = { nounwind uwtable noinline }
 attributes #1 = { uwtable noinline }


        


More information about the llvm-commits mailing list