Skip to content

Ensure collectTransitivePredecessors returns Pred only from the Loop. #113831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 31, 2024

Conversation

Nirhar
Copy link
Contributor

@Nirhar Nirhar commented Oct 27, 2024

It's possible that we encounter Irreducible control flow, due to which, we may find that a few predecessors of BB are not a part of the CurLoop. Currently we crash in the function for such cases. This patch ensures that we only return Predecessors that are a part of CurLoop and gracefully ignore other Predecessors.

For example, consider Irreducible IR of this form:

define i64 @baz() {
bb:
  br label %bb1

bb1:                                              ; preds = %bb3, %bb
  br label %bb3

bb2:                                              ; No predecessors!
  br label %bb3

bb3:                                              ; preds = %bb2, %bb1
  %load = load ptr addrspace(1), ptr addrspace(1) null, align 8
  br label %bb1
}

This crashes when collectTransitivePredecessors is called on the %bb1<Header>, %bb3<latch> loop, because the loop body has a predecessor %bb2 which is not a part of the loop.

See https://godbolt.org/z/E9fM1q3cT for the crash

@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Oct 27, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 27, 2024

@llvm/pr-subscribers-llvm-analysis

Author: Manish Kausik H (Nirhar)

Changes

It's possible that we encounter Irreducible control flow, due to which, we may find that a few predecessors of BB are not a part of the CurLoop. Currently we crash in the function for such cases. This patch ensures that we only return Predecessors that are a part of CurLoop and gracefully ignore other Predecessors.

For example, consider Irreducible IR of this form:

source_filename = "reduced.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2"
target triple = "x86_64-unknown-linux-gnu"

declare ptr addrspace(1) @<!-- -->widget(ptr addrspace(1), ptr addrspace(1))

define i64 @<!-- -->baz() {
bb:
  br label %bb1

bb1:                                              ; preds = %bb3, %bb
  br label %bb3

bb2:                                              ; No predecessors!
  br label %bb3

bb3:                                              ; preds = %bb2, %bb1
  %load = load ptr addrspace(1), ptr addrspace(1) null, align 8
  %call = call ptr addrspace(1) @<!-- -->widget(ptr addrspace(1) %load, ptr addrspace(1) null)
  br label %bb1
}

This crashes when collectTransitivePredecessors is called on the %bb1&lt;Header&gt;, %bb3&lt;latch&gt; loop, because the loop body has a predecessor %bb2 which is not a part of the loop.


Full diff: https://github.com/llvm/llvm-project/pull/113831.diff

1 Files Affected:

  • (modified) llvm/lib/Analysis/MustExecute.cpp (+6-1)
diff --git a/llvm/lib/Analysis/MustExecute.cpp b/llvm/lib/Analysis/MustExecute.cpp
index caed62679a683c..8e233a5c8d7a54 100644
--- a/llvm/lib/Analysis/MustExecute.cpp
+++ b/llvm/lib/Analysis/MustExecute.cpp
@@ -162,6 +162,9 @@ static bool CanProveNotTakenFirstIteration(const BasicBlock *ExitBlock,
 /// Collect all blocks from \p CurLoop which lie on all possible paths from
 /// the header of \p CurLoop (inclusive) to BB (exclusive) into the set
 /// \p Predecessors. If \p BB is the header, \p Predecessors will be empty.
+/// Note: It's possible that we encounter Irreducible control flow, due to
+/// which, we may find that a few predecessors of \p BB are not a part of the
+/// \p CurLoop. We only return Predecessors that are a part of \p CurLoop.
 static void collectTransitivePredecessors(
     const Loop *CurLoop, const BasicBlock *BB,
     SmallPtrSetImpl<const BasicBlock *> &Predecessors) {
@@ -171,6 +174,8 @@ static void collectTransitivePredecessors(
     return;
   SmallVector<const BasicBlock *, 4> WorkList;
   for (const auto *Pred : predecessors(BB)) {
+    if (!CurLoop->contains(Pred))
+      continue;
     Predecessors.insert(Pred);
     WorkList.push_back(Pred);
   }
@@ -187,7 +192,7 @@ static void collectTransitivePredecessors(
     // We can ignore backedge of all loops containing BB to get a sligtly more
     // optimistic result.
     for (const auto *PredPred : predecessors(Pred))
-      if (Predecessors.insert(PredPred).second)
+      if (CurLoop->contains(PredPred) && Predecessors.insert(PredPred).second)
         WorkList.push_back(PredPred);
   }
 }

@Nirhar Nirhar force-pushed the irreducible-control-flow-bug branch 2 times, most recently from 9bb2c5e to e9731bd Compare October 27, 2024 19:08
@Nirhar
Copy link
Contributor Author

Nirhar commented Oct 27, 2024

@nikic @arsenm

@arsenm
Copy link
Contributor

arsenm commented Oct 27, 2024

Needs tests

@Nirhar
Copy link
Contributor Author

Nirhar commented Oct 28, 2024

I'm not sure how I can write a lit test for this, reason being, most of the loop transformation passes run loop-simplify which removes the irreducibility from the above example. I don't know how to invoke collectTransitivePredecessors without using any of the loop passes.

@arsenm
Copy link
Contributor

arsenm commented Oct 28, 2024

I'm not sure how I can write a lit test for this, reason being, most of the loop transformation passes run loop-simplify which removes the irreducibility from the above example. I don't know how to invoke collectTransitivePredecessors without using any of the loop passes.

You can directly run the must-execute analysis printer and check the output. Tests ideally run in isolation, the context where it is used in the pass pipeline should not matter

It's possible that we encounter Irreducible control flow, due to
which, we may find that a few predecessors of BB are not a part of the
CurLoop. Currently we crash in the function for such cases. This patch
ensures that we only return Predecessors that are a part of CurLoop and
gracefully ignore other Predecessors.

For example, consider Irreducible IR of this form:
```
define i64 @baz() {
bb:
  br label %bb1

bb1:                                              ; preds = %bb3, %bb
  br label %bb3

bb2:                                              ; No predecessors!
  br label %bb3

bb3:                                              ; preds = %bb2, %bb1
  %load = load ptr addrspace(1), ptr addrspace(1) null, align 8
  br label %bb1
}
```
This crashes when `collectTransitivePredecessors` is called on the
`%bb1<Header>, %bb3<latch>` loop, because the loop body has a predecessor
`%bb2` which is not a part of the loop.
@Nirhar Nirhar force-pushed the irreducible-control-flow-bug branch from e9731bd to 3ab467b Compare October 29, 2024 16:29
@Nirhar
Copy link
Contributor Author

Nirhar commented Oct 29, 2024

@arsenm Thanks for the idea! Have added a lit test.

@arsenm arsenm requested review from ssahasra and fhahn October 30, 2024 17:10
@Nirhar
Copy link
Contributor Author

Nirhar commented Oct 31, 2024

@arsenm Can you merge it? I don't have merge permissions. Thanks!

@arsenm arsenm merged commit 0856592 into llvm:main Oct 31, 2024
6 of 8 checks passed
smallp-o-p pushed a commit to smallp-o-p/llvm-project that referenced this pull request Nov 3, 2024
…p. (llvm#113831)

It's possible that we encounter Irreducible control flow, due to which,
we may find that a few predecessors of BB are not a part of the CurLoop.
Currently we crash in the function for such cases. This patch ensures
that we only return Predecessors that are a part of CurLoop and
gracefully ignore other Predecessors.

For example, consider Irreducible IR of this form:
```
define i64 @baz() {
bb:
  br label %bb1

bb1:                                              ; preds = %bb3, %bb
  br label %bb3

bb2:                                              ; No predecessors!
  br label %bb3

bb3:                                              ; preds = %bb2, %bb1
  %load = load ptr addrspace(1), ptr addrspace(1) null, align 8
  br label %bb1
}
```

This crashes when `collectTransitivePredecessors` is called on the
`%bb1<Header>, %bb3<latch>` loop, because the loop body has a
predecessor `%bb2` which is not a part of the loop.

See https://godbolt.org/z/E9fM1q3cT for the crash
NoumanAmir657 pushed a commit to NoumanAmir657/llvm-project that referenced this pull request Nov 4, 2024
…p. (llvm#113831)

It's possible that we encounter Irreducible control flow, due to which,
we may find that a few predecessors of BB are not a part of the CurLoop.
Currently we crash in the function for such cases. This patch ensures
that we only return Predecessors that are a part of CurLoop and
gracefully ignore other Predecessors.

For example, consider Irreducible IR of this form:
```
define i64 @baz() {
bb:
  br label %bb1

bb1:                                              ; preds = %bb3, %bb
  br label %bb3

bb2:                                              ; No predecessors!
  br label %bb3

bb3:                                              ; preds = %bb2, %bb1
  %load = load ptr addrspace(1), ptr addrspace(1) null, align 8
  br label %bb1
}
```

This crashes when `collectTransitivePredecessors` is called on the
`%bb1<Header>, %bb3<latch>` loop, because the loop body has a
predecessor `%bb2` which is not a part of the loop.

See https://godbolt.org/z/E9fM1q3cT for the crash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:analysis Includes value tracking, cost tables and constant folding
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants