[llvm] [SimplifyCFG] Hoist common code when succ is unreachable block (PR #165570)
Kunqiu Chen via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 29 08:33:55 PDT 2025
https://github.com/Camsyn updated https://github.com/llvm/llvm-project/pull/165570
>From 29e07b8ef547879b4de855984d92cb4a5f6cf634 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 29 Oct 2025 21:43:11 +0800
Subject: [PATCH 1/4] Add a test before commit
---
.../SimplifyCFG/hoist-common-code.ll | 133 ++++++++++++++++++
1 file changed, 133 insertions(+)
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
index 8ce94d1cf5b4e..114e143502a00 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
@@ -185,8 +185,13 @@ else:
define void @test_icmp_complex(i1 %c, i32 %a, i32 %b) {
; CHECK-LABEL: @test_icmp_complex(
+; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
+; CHECK: if:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: br i1 [[CMP1]], label [[IF2:%.*]], label [[ELSE2:%.*]]
+; CHECK: else:
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[B]], [[A]]
+; CHECK-NEXT: br i1 [[CMP2]], label [[IF2]], label [[ELSE2]]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: if2:
@@ -486,3 +491,131 @@ else:
call void @bar()
ret float %op2
}
+
+define void @test_switch_with_unreachable_block_as_default(i1 %c, i32 %x, ptr %ptr) {
+; CHECK-LABEL: @test_switch_with_unreachable_block_as_default(
+; CHECK-NEXT: br i1 [[C:%.*]], label [[SW1:%.*]], label [[SW2:%.*]]
+; CHECK: sw1:
+; CHECK-NEXT: switch i32 [[X:%.*]], label [[UNREACHABLE:%.*]] [
+; CHECK-NEXT: i32 1, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i32 2, label [[BAR:%.*]]
+; CHECK-NEXT: ]
+; CHECK: sw2:
+; CHECK-NEXT: switch i32 [[X]], label [[UNREACHABLE]] [
+; CHECK-NEXT: i32 1, label [[BB1:%.*]]
+; CHECK-NEXT: i32 2, label [[BB2:%.*]]
+; CHECK-NEXT: i32 3, label [[BB3:%.*]]
+; CHECK-NEXT: ]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: bb1:
+; CHECK-NEXT: store i64 42, ptr [[PTR:%.*]], align 4
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: bb2:
+; CHECK-NEXT: store i64 42, ptr [[PTR]], align 4
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: bb3:
+; CHECK-NEXT: store i64 42, ptr [[PTR]], align 4
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: unreachable:
+; CHECK-NEXT: unreachable
+; CHECK: bar:
+; CHECK-NEXT: call void @bar()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+ br i1 %c, label %sw1, label %sw2
+
+sw1:
+ ; This switch only exists to have an %unreachable block with multiple predecessors.
+ switch i32 %x, label %unreachable [
+ i32 1, label %foo
+ i32 2, label %bar
+ ]
+
+sw2:
+ switch i32 %x, label %unreachable [
+ i32 1, label %bb1
+ i32 2, label %bb2
+ i32 3, label %bb3
+ ]
+
+bb1:
+ store i64 42, ptr %ptr
+ ret void
+
+bb2:
+ store i64 42, ptr %ptr
+ ret void
+
+bb3:
+ store i64 42, ptr %ptr
+ ret void
+
+unreachable:
+ unreachable
+
+foo:
+ ret void
+
+bar:
+ call void @bar()
+ ret void
+}
+
+define void @test_switch_with_unreachable_block_as_case(i1 %c, i32 %x, ptr %ptr) {
+; CHECK-LABEL: @test_switch_with_unreachable_block_as_case(
+; CHECK-NEXT: br i1 [[C:%.*]], label [[SW1:%.*]], label [[SW2:%.*]]
+; CHECK: sw1:
+; CHECK-NEXT: switch i32 [[X:%.*]], label [[UNREACHABLE:%.*]] [
+; CHECK-NEXT: i32 1, label [[COMMON_RET:%.*]]
+; CHECK-NEXT: i32 2, label [[BAR:%.*]]
+; CHECK-NEXT: ]
+; CHECK: sw2:
+; CHECK-NEXT: store i64 42, ptr [[PTR:%.*]], align 4
+; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
+; CHECK: unreachable:
+; CHECK-NEXT: unreachable
+; CHECK: bar:
+; CHECK-NEXT: call void @bar()
+; CHECK-NEXT: br label [[COMMON_RET]]
+;
+ br i1 %c, label %sw1, label %sw2
+
+sw1:
+ ; This switch only exists to have an %unreachable block with multiple predecessors.
+ switch i32 %x, label %unreachable [
+ i32 1, label %foo
+ i32 2, label %bar
+ ]
+
+sw2:
+ switch i32 %x, label %bb3 [
+ i32 1, label %bb1
+ i32 2, label %bb2
+ i32 3, label %unreachable
+ ]
+
+bb1:
+ store i64 42, ptr %ptr
+ ret void
+
+bb2:
+ store i64 42, ptr %ptr
+ ret void
+
+bb3:
+ store i64 42, ptr %ptr
+ ret void
+
+unreachable:
+ unreachable
+
+foo:
+ ret void
+
+bar:
+ call void @bar()
+ ret void
+}
>From cc6ca090a26e841246b89777b6f0bf4f84428ab0 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 29 Oct 2025 21:47:05 +0800
Subject: [PATCH 2/4] [SimplifyCFG] Hoist common code when succ is unreachable
block
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index d831c2737e5f8..7244ffbe344e6 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -1866,10 +1866,19 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
// If either of the blocks has it's address taken, then we can't do this fold,
// because the code we'd hoist would no longer run when we jump into the block
// by it's address.
- for (auto *Succ : successors(BB))
- if (Succ->hasAddressTaken() || !Succ->getSinglePredecessor())
+ for (auto *Succ : successors(BB)) {
+ if (Succ->hasAddressTaken())
return false;
-
+ if (Succ->getSinglePredecessor())
+ continue;
+ // If Succ has >1 predecessors, continue to check if the Succ is terminated
+ // by an `unreachable` inst. Since executing `unreachable` inst is an UB, we
+ // can relax the condition based on the assumptiom that the program would
+ // never enter Succ and trigger an UB.
+ if (isa<UnreachableInst>(Succ->getTerminator()))
+ continue;
+ return false;
+ }
// The second of pair is a SkipFlags bitmask.
using SuccIterPair = std::pair<BasicBlock::iterator, unsigned>;
SmallVector<SuccIterPair, 8> SuccIterPairs;
>From 779580aed02a17d7e8b979e54cfa2fe17b780df1 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 29 Oct 2025 21:47:52 +0800
Subject: [PATCH 3/4] Regenerate the test after commit
---
.../SimplifyCFG/hoist-common-code.ll | 21 ++-----------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
index 114e143502a00..98c0599ab209c 100644
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
+++ b/llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
@@ -185,13 +185,8 @@ else:
define void @test_icmp_complex(i1 %c, i32 %a, i32 %b) {
; CHECK-LABEL: @test_icmp_complex(
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
-; CHECK: if:
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: br i1 [[CMP1]], label [[IF2:%.*]], label [[ELSE2:%.*]]
-; CHECK: else:
-; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[B]], [[A]]
-; CHECK-NEXT: br i1 [[CMP2]], label [[IF2]], label [[ELSE2]]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: if2:
@@ -501,22 +496,10 @@ define void @test_switch_with_unreachable_block_as_default(i1 %c, i32 %x, ptr %p
; CHECK-NEXT: i32 2, label [[BAR:%.*]]
; CHECK-NEXT: ]
; CHECK: sw2:
-; CHECK-NEXT: switch i32 [[X]], label [[UNREACHABLE]] [
-; CHECK-NEXT: i32 1, label [[BB1:%.*]]
-; CHECK-NEXT: i32 2, label [[BB2:%.*]]
-; CHECK-NEXT: i32 3, label [[BB3:%.*]]
-; CHECK-NEXT: ]
-; CHECK: common.ret:
-; CHECK-NEXT: ret void
-; CHECK: bb1:
; CHECK-NEXT: store i64 42, ptr [[PTR:%.*]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: bb2:
-; CHECK-NEXT: store i64 42, ptr [[PTR]], align 4
-; CHECK-NEXT: br label [[COMMON_RET]]
-; CHECK: bb3:
-; CHECK-NEXT: store i64 42, ptr [[PTR]], align 4
-; CHECK-NEXT: br label [[COMMON_RET]]
+; CHECK: common.ret:
+; CHECK-NEXT: ret void
; CHECK: unreachable:
; CHECK-NEXT: unreachable
; CHECK: bar:
>From 4d51e3a45203c16c935b172d96c8f2a42df3b531 Mon Sep 17 00:00:00 2001
From: Camsyn <camsyn at foxmail.com>
Date: Wed, 29 Oct 2025 23:33:23 +0800
Subject: [PATCH 4/4] Check the first inst rather than the last one
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 7244ffbe344e6..9680e4fdbec8d 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -1875,7 +1875,7 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
// by an `unreachable` inst. Since executing `unreachable` inst is an UB, we
// can relax the condition based on the assumptiom that the program would
// never enter Succ and trigger an UB.
- if (isa<UnreachableInst>(Succ->getTerminator()))
+ if (isa<UnreachableInst>(Succ->getFirstNonPHIOrDbgOrLifetime()))
continue;
return false;
}
More information about the llvm-commits
mailing list