Skip to content

Commit b285b33

Browse files
omarahmed1111jdoerfert
authored andcommitted
[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
1 parent 5a5a075 commit b285b33

File tree

5 files changed

+313
-39
lines changed

5 files changed

+313
-39
lines changed

llvm/include/llvm/Analysis/MustExecute.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ class ICFLoopSafetyInfo: public LoopSafetyInfo {
176176
virtual ~ICFLoopSafetyInfo() {};
177177
};
178178

179+
bool mayContainIrreducibleControl(const Function &F, const LoopInfo *LI);
180+
179181
struct MustBeExecutedContextExplorer;
180182

181183
/// Enum that allows us to spell out the direction.

llvm/lib/Analysis/MustExecute.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,13 +484,13 @@ static bool maybeEndlessLoop(const Loop &L) {
484484
return true;
485485
}
486486

487-
static bool mayContainIrreducibleControl(const Function &F, const LoopInfo *LI) {
487+
bool llvm::mayContainIrreducibleControl(const Function &F, const LoopInfo *LI) {
488488
if (!LI)
489489
return false;
490490
using RPOTraversal = ReversePostOrderTraversal<const Function *>;
491491
RPOTraversal FuncRPOT(&F);
492-
return !containsIrreducibleCFG<const BasicBlock *, const RPOTraversal,
493-
const LoopInfo>(FuncRPOT, *LI);
492+
return containsIrreducibleCFG<const BasicBlock *, const RPOTraversal,
493+
const LoopInfo>(FuncRPOT, *LI);
494494
}
495495

496496
/// Lookup \p Key in \p Map and return the result, potentially after

llvm/lib/Transforms/IPO/Attributor.cpp

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "llvm/Analysis/LazyValueInfo.h"
2929
#include "llvm/Analysis/Loads.h"
3030
#include "llvm/Analysis/MemoryBuiltins.h"
31+
#include "llvm/Analysis/MustExecute.h"
3132
#include "llvm/Analysis/ScalarEvolution.h"
3233
#include "llvm/Analysis/ValueTracking.h"
3334
#include "llvm/IR/Argument.h"
@@ -36,9 +37,9 @@
3637
#include "llvm/IR/IRBuilder.h"
3738
#include "llvm/IR/InstIterator.h"
3839
#include "llvm/IR/IntrinsicInst.h"
40+
#include "llvm/IR/NoFolder.h"
3941
#include "llvm/IR/Verifier.h"
4042
#include "llvm/InitializePasses.h"
41-
#include "llvm/IR/NoFolder.h"
4243
#include "llvm/Support/CommandLine.h"
4344
#include "llvm/Support/Debug.h"
4445
#include "llvm/Support/raw_ostream.h"
@@ -2491,28 +2492,34 @@ struct AAUndefinedBehaviorFunction final : AAUndefinedBehaviorImpl {
24912492

24922493
/// ------------------------ Will-Return Attributes ----------------------------
24932494

2494-
// Helper function that checks whether a function has any cycle.
2495-
// TODO: Replace with more efficent code
2496-
static bool containsCycle(Function &F) {
2497-
SmallPtrSet<BasicBlock *, 32> Visited;
2498-
2499-
// Traverse BB by dfs and check whether successor is already visited.
2500-
for (BasicBlock *BB : depth_first(&F)) {
2501-
Visited.insert(BB);
2502-
for (auto *SuccBB : successors(BB)) {
2503-
if (Visited.count(SuccBB))
2495+
// Helper function that checks whether a function has any cycle which we don't
2496+
// know if it is bounded or not.
2497+
// Loops with maximum trip count are considered bounded, any other cycle not.
2498+
static bool mayContainUnboundedCycle(Function &F, Attributor &A) {
2499+
ScalarEvolution *SE =
2500+
A.getInfoCache().getAnalysisResultForFunction<ScalarEvolutionAnalysis>(F);
2501+
LoopInfo *LI = A.getInfoCache().getAnalysisResultForFunction<LoopAnalysis>(F);
2502+
// If either SCEV or LoopInfo is not available for the function then we assume
2503+
// any cycle to be unbounded cycle.
2504+
// We use scc_iterator which uses Tarjan algorithm to find all the maximal
2505+
// SCCs.To detect if there's a cycle, we only need to find the maximal ones.
2506+
if (!SE || !LI) {
2507+
for (scc_iterator<Function *> SCCI = scc_begin(&F); !SCCI.isAtEnd(); ++SCCI)
2508+
if (SCCI.hasCycle())
25042509
return true;
2505-
}
2510+
return false;
25062511
}
2507-
return false;
2508-
}
25092512

2510-
// Helper function that checks the function have a loop which might become an
2511-
// endless loop
2512-
// FIXME: Any cycle is regarded as endless loop for now.
2513-
// We have to allow some patterns.
2514-
static bool containsPossiblyEndlessLoop(Function *F) {
2515-
return containsCycle(*F);
2513+
// If there's irreducible control, the function may contain non-loop cycles.
2514+
if (mayContainIrreducibleControl(F, LI))
2515+
return true;
2516+
2517+
// Any loop that does not have a max trip count is considered unbounded cycle.
2518+
for (auto *L : LI->getLoopsInPreorder()) {
2519+
if (!SE->getSmallConstantMaxTripCount(L))
2520+
return true;
2521+
}
2522+
return false;
25162523
}
25172524

25182525
struct AAWillReturnImpl : public AAWillReturn {
@@ -2523,7 +2530,7 @@ struct AAWillReturnImpl : public AAWillReturn {
25232530
AAWillReturn::initialize(A);
25242531

25252532
Function *F = getAssociatedFunction();
2526-
if (!F || !A.isFunctionIPOAmendable(*F) || containsPossiblyEndlessLoop(F))
2533+
if (!F || !A.isFunctionIPOAmendable(*F) || mayContainUnboundedCycle(*F, A))
25272534
indicatePessimisticFixpoint();
25282535
}
25292536

llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ return: ; preds = %if.end, %if.then
101101
ret i32* %retval.0
102102
}
103103

104-
; CHECK: Function Attrs: argmemonly nofree norecurse nosync nounwind
104+
; CHECK: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn
105105
; 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)
106106
define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
107107
entry:
@@ -160,6 +160,7 @@ entry:
160160
;
161161
; CHECK-NOT: attributes #
162162
; CHECK: attributes #{{.*}} = { argmemonly nofree nosync nounwind }
163-
; CHECK: attributes #{{.*}} = { argmemonly nofree norecurse nosync nounwind }
163+
; CHECK: attributes #{{.*}} = { argmemonly nofree norecurse nosync nounwind willreturn }
164164
; CHECK: attributes #{{.*}} = { nosync nounwind }
165+
; CHECK: attributes #{{.*}} = { norecurse nosync nounwind willreturn }
165166
; CHECK-NOT: attributes #

0 commit comments

Comments
 (0)