[llvm] [SimplifyCFG] Simplify switch with implicit default (PR #95665)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 15 10:00:59 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/5] [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/5] [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/5] 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/5] 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/5] [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:%.*]]) {



More information about the llvm-commits mailing list