[llvm] [SimplifyCFG] Simplify switch with implicit default (PR #95665)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 16 04:22:45 PDT 2024
https://github.com/YanWQ-monad updated https://github.com/llvm/llvm-project/pull/95665
>From 24e531ca4f2405eac815407417347452bbc91d52 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sat, 15 Jun 2024 23:24:11 +0800
Subject: [PATCH 1/9] [SimplifyCFG] Pre-commit: add tests
---
.../switch-with-implicit-default.ll | 374 ++++++++++++++++++
1 file changed, 374 insertions(+)
create mode 100644 llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
new file mode 100644
index 0000000000000..867f28c0242e2
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
@@ -0,0 +1,374 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
+
+declare void @func0()
+declare void @func1()
+declare void @func2()
+declare void @unreachable()
+
+define void @positive(i32 %x) {
+; CHECK-LABEL: define void @positive(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 3
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 1
+; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 1, label %[[CASE1:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[DEFAULT]]:
+; CHECK-NEXT: tail call void @unreachable()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i32 %x, 3
+ %v = select i1 %c, i32 %x, i32 1
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 1, label %case1
+ i32 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
+define void @positive_manual_default_out_of_cases(i32 %x) {
+; CHECK-LABEL: define void @positive_manual_default_out_of_cases(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 3
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 7
+; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 1, label %[[CASE1:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[DEFAULT]]:
+; CHECK-NEXT: tail call void @unreachable()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i32 %x, 3
+ %v = select i1 %c, i32 %x, i32 7
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 1, label %case1
+ i32 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
+
+define void @wrong_icmp(i32 %x) {
+; CHECK-LABEL: define void @wrong_icmp(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[X]], 2
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 1
+; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 1, label %[[CASE1:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[DEFAULT]]:
+; CHECK-NEXT: tail call void @unreachable()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ugt i32 %x, 2
+ %v = select i1 %c, i32 %x, i32 1
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 1, label %case1
+ i32 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
+define void @outer_icmp_too_wide(i32 %x) {
+; CHECK-LABEL: define void @outer_icmp_too_wide(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 4
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 1
+; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 1, label %[[CASE1:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[DEFAULT]]:
+; CHECK-NEXT: tail call void @unreachable()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i32 %x, 4
+ %v = select i1 %c, i32 %x, i32 1
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 1, label %case1
+ i32 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
+define void @outer_icmp_too_narrow(i32 %x) {
+; CHECK-LABEL: define void @outer_icmp_too_narrow(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 2
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 1
+; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 1, label %[[CASE1:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[DEFAULT]]:
+; CHECK-NEXT: tail call void @unreachable()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i32 %x, 2
+ %v = select i1 %c, i32 %x, i32 1
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 1, label %case1
+ i32 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
+define void @switch_cases_not_continuous_1(i32 %x) {
+; CHECK-LABEL: define void @switch_cases_not_continuous_1(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 3
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 2
+; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[DEFAULT]]:
+; CHECK-NEXT: tail call void @unreachable()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i32 %x, 3
+ %v = select i1 %c, i32 %x, i32 2
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
+define void @switch_cases_not_continuous_2(i32 %x) {
+; CHECK-LABEL: define void @switch_cases_not_continuous_2(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 3
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 2
+; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: i32 3, label %[[CASE1:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[DEFAULT]]:
+; CHECK-NEXT: tail call void @unreachable()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i32 %x, 3
+ %v = select i1 %c, i32 %x, i32 2
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 2, label %case2
+ i32 3, label %case1
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
>From 9eff08dba645864fe31f40af6ae3221e26188323 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sat, 15 Jun 2024 23:26:40 +0800
Subject: [PATCH 2/9] [SimplifyCFG] Simplify switch with implicit default
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 45 +++++++++++++++++++
.../switch-with-implicit-default.ll | 10 ++---
2 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index bccfb80f2b6cb..7a16b714e5324 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7104,6 +7104,48 @@ static bool simplifySwitchOfPowersOfTwo(SwitchInst *SI, IRBuilder<> &Builder,
return true;
}
+// Try to fold switch with manually selecting default branch
+// For example, for the switch
+// switch (v) { case 0: case 1: case 2: ... case MaxC: default: }
+// (continuous cases value from 0 to MaxC)
+// and, v = select (x u<= MaxC), x, AnotherC.
+// so "AnotherC" is the de-facto default branch, and it will be folded to
+// switch (x) { case 0: case 1: case 2: ... case MaxC: default -> AnotherC: }
+static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
+ Value *Cond;
+ ConstantInt *Bound, *DefaultCase;
+ ICmpInst::Predicate Pred;
+ if (!(match(SI->getCondition(),
+ m_Select(m_ICmp(Pred, m_Value(Cond), m_ConstantInt(Bound)),
+ m_Deferred(Cond), m_ConstantInt(DefaultCase))) &&
+ Pred == CmpInst::ICMP_ULT))
+ return false;
+
+ // In an ideal situation, the range of switch cases is continuous,
+ // and should match the range of ICmp. That is,
+ // MaxCase (declared below) + 1 = SI->getNumCases() = OuterRange.Upper.
+ // Checking it partially here can be a fast-fail path
+ if (Bound->getValue() != SI->getNumCases())
+ return false;
+
+ SmallVector<uint64_t, 4> Values;
+ for (const auto &Case : SI->cases()) {
+ uint64_t CaseValue = Case.getCaseValue()->getValue().getZExtValue();
+ Values.push_back(CaseValue);
+ }
+ llvm::sort(Values);
+
+ // The cases should be continuous from 0 to some value
+ if (!(*Values.begin() == 0 && *Values.rbegin() == SI->getNumCases() - 1))
+ return false;
+
+ SI->setCondition(Cond);
+ if (auto It = SI->findCaseValue(DefaultCase); It != SI->case_end())
+ SI->setDefaultDest(It->getCaseSuccessor());
+
+ return true;
+}
+
bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
BasicBlock *BB = SI->getParent();
@@ -7142,6 +7184,9 @@ bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
if (Options.ForwardSwitchCondToPhi && ForwardSwitchConditionToPHI(SI))
return requestResimplify();
+ if (simplifySwitchWithImplicitDefault(SI))
+ return requestResimplify();
+
// The conversion from switch to lookup tables results in difficult-to-analyze
// code and makes pruning branches much harder. This is a problem if the
// switch expression itself can still be restricted as a result of inlining or
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
index 867f28c0242e2..0107cdc70c95d 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
@@ -11,17 +11,13 @@ define void @positive(i32 %x) {
; CHECK-SAME: i32 [[X:%.*]]) {
; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 3
; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 1
-; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: switch i32 [[X]], label %[[CASE1:.*]] [
; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
-; CHECK-NEXT: i32 1, label %[[CASE1:.*]]
; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
; CHECK-NEXT: ]
-; CHECK: [[DEFAULT]]:
-; CHECK-NEXT: tail call void @unreachable()
-; CHECK-NEXT: br label %[[END:.*]]
; CHECK: [[CASE0]]:
; CHECK-NEXT: tail call void @func0()
-; CHECK-NEXT: br label %[[END]]
+; CHECK-NEXT: br label %[[END:.*]]
; CHECK: [[CASE1]]:
; CHECK-NEXT: tail call void @func1()
; CHECK-NEXT: br label %[[END]]
@@ -64,7 +60,7 @@ define void @positive_manual_default_out_of_cases(i32 %x) {
; CHECK-SAME: i32 [[X:%.*]]) {
; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 3
; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 7
-; CHECK-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [
+; CHECK-NEXT: switch i32 [[X]], label %[[DEFAULT:.*]] [
; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
; CHECK-NEXT: i32 1, label %[[CASE1:.*]]
; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
>From 753c1c3bf4b3e1bd3617a605ed86fb34d3863172 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sun, 16 Jun 2024 00:44:17 +0800
Subject: [PATCH 3/9] Fix typo
---
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 7a16b714e5324..2a0e1f64a398c 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7104,7 +7104,7 @@ static bool simplifySwitchOfPowersOfTwo(SwitchInst *SI, IRBuilder<> &Builder,
return true;
}
-// Try to fold switch with manually selecting default branch
+// Try to fold switch with manually selected default branch.
// For example, for the switch
// switch (v) { case 0: case 1: case 2: ... case MaxC: default: }
// (continuous cases value from 0 to MaxC)
>From e69f264d41e02bee1804050146116c4cda7a23c8 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sun, 16 Jun 2024 00:45:39 +0800
Subject: [PATCH 4/9] Refactor `if` with de morgan
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 2a0e1f64a398c..a0c2d6ea90c94 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7115,10 +7115,10 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
Value *Cond;
ConstantInt *Bound, *DefaultCase;
ICmpInst::Predicate Pred;
- if (!(match(SI->getCondition(),
- m_Select(m_ICmp(Pred, m_Value(Cond), m_ConstantInt(Bound)),
- m_Deferred(Cond), m_ConstantInt(DefaultCase))) &&
- Pred == CmpInst::ICMP_ULT))
+ if (!match(SI->getCondition(),
+ m_Select(m_ICmp(Pred, m_Value(Cond), m_ConstantInt(Bound)),
+ m_Deferred(Cond), m_ConstantInt(DefaultCase))) ||
+ Pred != CmpInst::ICMP_ULT)
return false;
// In an ideal situation, the range of switch cases is continuous,
@@ -7136,7 +7136,7 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
llvm::sort(Values);
// The cases should be continuous from 0 to some value
- if (!(*Values.begin() == 0 && *Values.rbegin() == SI->getNumCases() - 1))
+ if (*Values.begin() != 0 || *Values.rbegin() != SI->getNumCases() - 1)
return false;
SI->setCondition(Cond);
>From 7e92dd6940238bea5f6330ae45b1fd54bf79c9fa Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sun, 16 Jun 2024 00:55:52 +0800
Subject: [PATCH 5/9] [SimplifyCFG] Use `APInt` to handle `i128` properly
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 13 +++--
.../switch-with-implicit-default.ll | 49 +++++++++++++++++++
2 files changed, 55 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index a0c2d6ea90c94..67fd22bf09cb1 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7128,15 +7128,14 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
if (Bound->getValue() != SI->getNumCases())
return false;
- SmallVector<uint64_t, 4> Values;
- for (const auto &Case : SI->cases()) {
- uint64_t CaseValue = Case.getCaseValue()->getValue().getZExtValue();
- Values.push_back(CaseValue);
- }
- llvm::sort(Values);
+ SmallVector<APInt, 4> Values;
+ for (const auto &Case : SI->cases())
+ Values.push_back(Case.getCaseValue()->getValue());
+ llvm::sort(Values,
+ [](const APInt &lhs, const APInt &rhs) { return lhs.ult(rhs); });
// The cases should be continuous from 0 to some value
- if (*Values.begin() != 0 || *Values.rbegin() != SI->getNumCases() - 1)
+ if (!Values.begin()->isZero() || *Values.rbegin() != SI->getNumCases() - 1)
return false;
SI->setCondition(Cond);
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
index 0107cdc70c95d..a9aa6f814b59c 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
@@ -55,6 +55,55 @@ end:
ret void
}
+define void @positive_with_i128(i128 %x) {
+; CHECK-LABEL: define void @positive_with_i128(
+; CHECK-SAME: i128 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i128 [[X]], 3
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i128 [[X]], i128 1
+; CHECK-NEXT: switch i128 [[X]], label %[[CASE1:.*]] [
+; CHECK-NEXT: i128 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i128 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i128 %x, 3
+ %v = select i1 %c, i128 %x, i128 1
+ switch i128 %v, label %default [
+ i128 0, label %case0
+ i128 1, label %case1
+ i128 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
define void @positive_manual_default_out_of_cases(i32 %x) {
; CHECK-LABEL: define void @positive_manual_default_out_of_cases(
; CHECK-SAME: i32 [[X:%.*]]) {
>From 7bdf41dc089d2f9e16a1a1e60312cfa092193afd Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sun, 16 Jun 2024 14:30:32 +0800
Subject: [PATCH 6/9] [SimplifyCFG] Don't use `llvm::sort` to get min/max value
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 67fd22bf09cb1..289be2808195b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7128,14 +7128,17 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
if (Bound->getValue() != SI->getNumCases())
return false;
- SmallVector<APInt, 4> Values;
- for (const auto &Case : SI->cases())
- Values.push_back(Case.getCaseValue()->getValue());
- llvm::sort(Values,
- [](const APInt &lhs, const APInt &rhs) { return lhs.ult(rhs); });
+ APInt Min = SI->case_begin()->getCaseValue()->getValue(), Max = Min;
+ for (const auto &Case : SI->cases()) {
+ const APInt &CaseVal = Case.getCaseValue()->getValue();
+ if (CaseVal.ult(Min))
+ Min = CaseVal;
+ if (CaseVal.ugt(Max))
+ Max = CaseVal;
+ }
// The cases should be continuous from 0 to some value
- if (!Values.begin()->isZero() || *Values.rbegin() != SI->getNumCases() - 1)
+ if (!Min.isZero() || Max != SI->getNumCases() - 1)
return false;
SI->setCondition(Cond);
>From 7c0ea4de1487deb7dd992c2b89027bc72dd250b3 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sun, 16 Jun 2024 14:40:55 +0800
Subject: [PATCH 7/9] [SimplifyCFG] Also match `icmp ule`
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 10 ++--
.../switch-with-implicit-default.ll | 49 +++++++++++++++++++
2 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 289be2808195b..2ceb5677eb124 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7117,15 +7117,19 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
ICmpInst::Predicate Pred;
if (!match(SI->getCondition(),
m_Select(m_ICmp(Pred, m_Value(Cond), m_ConstantInt(Bound)),
- m_Deferred(Cond), m_ConstantInt(DefaultCase))) ||
- Pred != CmpInst::ICMP_ULT)
+ m_Deferred(Cond), m_ConstantInt(DefaultCase))))
+ return false;
+
+ ConstantRange OuterRange =
+ ConstantRange::makeExactICmpRegion(Pred, Bound->getValue());
+ if (!OuterRange.getLower().isZero())
return false;
// In an ideal situation, the range of switch cases is continuous,
// and should match the range of ICmp. That is,
// MaxCase (declared below) + 1 = SI->getNumCases() = OuterRange.Upper.
// Checking it partially here can be a fast-fail path
- if (Bound->getValue() != SI->getNumCases())
+ if (OuterRange.getUpper() != SI->getNumCases())
return false;
APInt Min = SI->case_begin()->getCaseValue()->getValue(), Max = Min;
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
index a9aa6f814b59c..11645a9396ee1 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
@@ -55,6 +55,55 @@ end:
ret void
}
+define void @positive_with_ule(i32 %x) {
+; CHECK-LABEL: define void @positive_with_ule(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ule i32 [[X]], 2
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 1
+; CHECK-NEXT: switch i32 [[X]], label %[[CASE1:.*]] [
+; CHECK-NEXT: i32 0, label %[[CASE0:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[CASE0]]:
+; CHECK-NEXT: tail call void @func0()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ule i32 %x, 2
+ %v = select i1 %c, i32 %x, i32 1
+ switch i32 %v, label %default [
+ i32 0, label %case0
+ i32 1, label %case1
+ i32 2, label %case2
+ ]
+
+default:
+ tail call void @unreachable()
+ br label %end
+
+case0:
+ tail call void @func0()
+ br label %end
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+end:
+ ret void
+}
+
define void @positive_with_i128(i128 %x) {
; CHECK-LABEL: define void @positive_with_i128(
; CHECK-SAME: i128 [[X:%.*]]) {
>From 4ef626c4a8c2ad92309c9ae8effe94860c9847f2 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sun, 16 Jun 2024 15:04:36 +0800
Subject: [PATCH 8/9] [SimplifyCFG] Check unreachable default for more
flexibility
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 27 ++++++----
.../switch-with-implicit-default.ll | 49 +++++++++++++++++++
2 files changed, 66 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 2ceb5677eb124..c1ba45f86702a 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7125,11 +7125,14 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
if (!OuterRange.getLower().isZero())
return false;
- // In an ideal situation, the range of switch cases is continuous,
- // and should match the range of ICmp. That is,
+ bool HasDefault =
+ !isa<UnreachableInst>(SI->getDefaultDest()->getFirstNonPHIOrDbg());
+
+ // In an ideal situation, if default is reachable, the range of switch cases
+ // should be continuous, and should match the range of ICmp. That is,
// MaxCase (declared below) + 1 = SI->getNumCases() = OuterRange.Upper.
// Checking it partially here can be a fast-fail path
- if (OuterRange.getUpper() != SI->getNumCases())
+ if (HasDefault && OuterRange.getUpper() != SI->getNumCases())
return false;
APInt Min = SI->case_begin()->getCaseValue()->getValue(), Max = Min;
@@ -7141,15 +7144,19 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
Max = CaseVal;
}
- // The cases should be continuous from 0 to some value
- if (!Min.isZero() || Max != SI->getNumCases() - 1)
- return false;
+ // If the switch has default, the cases should be continuous from 0 to some
+ // value. Otherwise, check whether all the cases satisfy the predicate, that
+ // is, all the cases are in the range `OuterRange`.
+ if ((HasDefault && Min.isZero() && Max == SI->getNumCases() - 1) ||
+ (!HasDefault && Max.ult(OuterRange.getUpper()))) {
+ SI->setCondition(Cond);
+ if (auto It = SI->findCaseValue(DefaultCase); It != SI->case_end())
+ SI->setDefaultDest(It->getCaseSuccessor());
- SI->setCondition(Cond);
- if (auto It = SI->findCaseValue(DefaultCase); It != SI->case_end())
- SI->setDefaultDest(It->getCaseSuccessor());
+ return true;
+ }
- return true;
+ return false;
}
bool SimplifyCFGOpt::simplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
diff --git a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
index 11645a9396ee1..d8844bca557f3 100644
--- a/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
+++ b/llvm/test/Transforms/SimplifyCFG/switch-with-implicit-default.ll
@@ -4,6 +4,7 @@
declare void @func0()
declare void @func1()
declare void @func2()
+declare void @func3()
declare void @unreachable()
define void @positive(i32 %x) {
@@ -55,6 +56,54 @@ end:
ret void
}
+define void @positive_with_hole(i32 %x) {
+; CHECK-LABEL: define void @positive_with_hole(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[X]], 5
+; CHECK-NEXT: [[V:%.*]] = select i1 [[C]], i32 [[X]], i32 1
+; CHECK-NEXT: switch i32 [[X]], label %[[CASE1:.*]] [
+; CHECK-NEXT: i32 3, label %[[CASE3:.*]]
+; CHECK-NEXT: i32 2, label %[[CASE2:.*]]
+; CHECK-NEXT: ]
+; CHECK: [[CASE1]]:
+; CHECK-NEXT: tail call void @func1()
+; CHECK-NEXT: br label %[[END:.*]]
+; CHECK: [[CASE2]]:
+; CHECK-NEXT: tail call void @func2()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[CASE3]]:
+; CHECK-NEXT: tail call void @func3()
+; CHECK-NEXT: br label %[[END]]
+; CHECK: [[END]]:
+; CHECK-NEXT: ret void
+;
+ %c = icmp ult i32 %x, 5
+ %v = select i1 %c, i32 %x, i32 1
+ switch i32 %v, label %default [
+ i32 1, label %case1
+ i32 2, label %case2
+ i32 3, label %case3
+ ]
+
+default:
+ unreachable
+
+case1:
+ tail call void @func1()
+ br label %end
+
+case2:
+ tail call void @func2()
+ br label %end
+
+case3:
+ tail call void @func3()
+ br label %end
+
+end:
+ ret void
+}
+
define void @positive_with_ule(i32 %x) {
; CHECK-LABEL: define void @positive_with_ule(
; CHECK-SAME: i32 [[X:%.*]]) {
>From e259ac543fceeb876fa1ff1741ee5e96d0b2fd85 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sun, 16 Jun 2024 19:22:15 +0800
Subject: [PATCH 9/9] [SimplifyCFG] Use `defaultDestUndefined`
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index c1ba45f86702a..a5b1fbac49fac 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7125,8 +7125,7 @@ static bool simplifySwitchWithImplicitDefault(SwitchInst *SI) {
if (!OuterRange.getLower().isZero())
return false;
- bool HasDefault =
- !isa<UnreachableInst>(SI->getDefaultDest()->getFirstNonPHIOrDbg());
+ bool HasDefault = !SI->defaultDestUndefined();
// In an ideal situation, if default is reachable, the range of switch cases
// should be continuous, and should match the range of ICmp. That is,
More information about the llvm-commits
mailing list