[llvm] [InstCombine] Fold `switch(zext/sext(X))` into `switch(X)` (PR #76988)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 5 10:41:43 PST 2024


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/76988

>From d3e89f1e22743c9d033cd2c32ce8a99a5681f38d Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 5 Jan 2024 23:19:44 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 .../InstCombine/switch-zext-sext.ll           | 123 ++++++++++++++++++
 .../Transforms/PhaseOrdering/switch-sext.ll   |  58 +++++++++
 2 files changed, 181 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/switch-zext-sext.ll
 create mode 100644 llvm/test/Transforms/PhaseOrdering/switch-sext.ll

diff --git a/llvm/test/Transforms/InstCombine/switch-zext-sext.ll b/llvm/test/Transforms/InstCombine/switch-zext-sext.ll
new file mode 100644
index 00000000000000..5e5303a1109e9c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/switch-zext-sext.ll
@@ -0,0 +1,123 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt %s -passes=instcombine -S | FileCheck %s
+
+define i1 @test_switch_with_zext(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_zext(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT:      i16 37, label [[SW_BB:%.*]]
+; CHECK-NEXT:      i16 38, label [[SW_BB]]
+; CHECK-NEXT:      i16 39, label [[SW_BB]]
+; CHECK-NEXT:    ]
+; CHECK:       sw.bb:
+; CHECK-NEXT:    ret i1 [[B]]
+; CHECK:       sw.default:
+; CHECK-NEXT:    ret i1 [[C]]
+;
+entry:
+  %a.ext = zext i16 %a to i32
+  switch i32 %a.ext, label %sw.default [
+  i32 37, label %sw.bb
+  i32 38, label %sw.bb
+  i32 39, label %sw.bb
+  ]
+
+sw.bb:
+  ret i1 %b
+sw.default:
+  ret i1 %c
+}
+
+define i1 @test_switch_with_sext(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_sext(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_EXT:%.*]] = sext i16 [[A]] to i32
+; CHECK-NEXT:    switch i32 [[A_EXT]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 37, label [[SW_BB:%.*]]
+; CHECK-NEXT:      i32 38, label [[SW_BB]]
+; CHECK-NEXT:      i32 39, label [[SW_BB]]
+; CHECK-NEXT:    ]
+; CHECK:       sw.bb:
+; CHECK-NEXT:    ret i1 [[B]]
+; CHECK:       sw.default:
+; CHECK-NEXT:    ret i1 [[C]]
+;
+entry:
+  %a.ext = sext i16 %a to i32
+  switch i32 %a.ext, label %sw.default [
+  i32 37, label %sw.bb
+  i32 38, label %sw.bb
+  i32 39, label %sw.bb
+  ]
+
+sw.bb:
+  ret i1 %b
+sw.default:
+  ret i1 %c
+}
+
+; Negative tests
+
+define i1 @test_switch_with_zext_unreachable_case(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_zext_unreachable_case(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_EXT:%.*]] = zext i16 [[A]] to i32
+; CHECK-NEXT:    switch i32 [[A_EXT]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 37, label [[SW_BB:%.*]]
+; CHECK-NEXT:      i32 38, label [[SW_BB]]
+; CHECK-NEXT:      i32 39, label [[SW_BB]]
+; CHECK-NEXT:      i32 65537, label [[SW_BB]]
+; CHECK-NEXT:    ]
+; CHECK:       sw.bb:
+; CHECK-NEXT:    ret i1 [[B]]
+; CHECK:       sw.default:
+; CHECK-NEXT:    ret i1 [[C]]
+;
+entry:
+  %a.ext = zext i16 %a to i32
+  switch i32 %a.ext, label %sw.default [
+  i32 37, label %sw.bb
+  i32 38, label %sw.bb
+  i32 39, label %sw.bb
+  i32 65537, label %sw.bb
+  ]
+
+sw.bb:
+  ret i1 %b
+sw.default:
+  ret i1 %c
+}
+
+define i1 @test_switch_with_sext_unreachable_case(i16 %a, i1 %b, i1 %c) {
+; CHECK-LABEL: define i1 @test_switch_with_sext_unreachable_case(
+; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_EXT:%.*]] = sext i16 [[A]] to i32
+; CHECK-NEXT:    switch i32 [[A_EXT]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 37, label [[SW_BB:%.*]]
+; CHECK-NEXT:      i32 38, label [[SW_BB]]
+; CHECK-NEXT:      i32 39, label [[SW_BB]]
+; CHECK-NEXT:      i32 -65537, label [[SW_BB]]
+; CHECK-NEXT:    ]
+; CHECK:       sw.bb:
+; CHECK-NEXT:    ret i1 [[B]]
+; CHECK:       sw.default:
+; CHECK-NEXT:    ret i1 [[C]]
+;
+entry:
+  %a.ext = sext i16 %a to i32
+  switch i32 %a.ext, label %sw.default [
+  i32 37, label %sw.bb
+  i32 38, label %sw.bb
+  i32 39, label %sw.bb
+  i32 -65537, label %sw.bb
+  ]
+
+sw.bb:
+  ret i1 %b
+sw.default:
+  ret i1 %c
+}
diff --git a/llvm/test/Transforms/PhaseOrdering/switch-sext.ll b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll
new file mode 100644
index 00000000000000..764f26f11adb4f
--- /dev/null
+++ b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll
@@ -0,0 +1,58 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -S -passes='default<O3>' < %s | FileCheck %s
+
+define i8 @test_switch_with_sext_phi(i8 %code) {
+; CHECK-LABEL: define i8 @test_switch_with_sext_phi(
+; CHECK-SAME: i8 [[CODE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CONV:%.*]] = sext i8 [[CODE]] to i32
+; CHECK-NEXT:    switch i32 [[CONV]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT:      i32 105, label [[SW_EPILOG:%.*]]
+; CHECK-NEXT:      i32 73, label [[SW_BB1:%.*]]
+; CHECK-NEXT:      i32 108, label [[SW_BB2:%.*]]
+; CHECK-NEXT:      i32 76, label [[SW_BB3:%.*]]
+; CHECK-NEXT:      i32 63, label [[SW_BB4:%.*]]
+; CHECK-NEXT:    ]
+; CHECK:       sw.bb1:
+; CHECK-NEXT:    br label [[SW_EPILOG]]
+; CHECK:       sw.bb2:
+; CHECK-NEXT:    br label [[SW_EPILOG]]
+; CHECK:       sw.bb3:
+; CHECK-NEXT:    br label [[SW_EPILOG]]
+; CHECK:       sw.bb4:
+; CHECK-NEXT:    br label [[SW_EPILOG]]
+; CHECK:       sw.default:
+; CHECK-NEXT:    br label [[SW_EPILOG]]
+; CHECK:       sw.epilog:
+; CHECK-NEXT:    [[PEP_CODE:%.*]] = phi i8 [ [[CODE]], [[SW_DEFAULT]] ], [ 63, [[SW_BB4]] ], [ 81, [[SW_BB3]] ], [ 113, [[SW_BB2]] ], [ 73, [[SW_BB1]] ], [ 105, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    ret i8 [[PEP_CODE]]
+;
+entry:
+  %conv = sext i8 %code to i32
+  switch i32 %conv, label %sw.default [
+  i32 105, label %sw.epilog
+  i32 73, label %sw.bb1
+  i32 108, label %sw.bb2
+  i32 76, label %sw.bb3
+  i32 63, label %sw.bb4
+  ]
+
+sw.bb1:
+  br label %sw.epilog
+
+sw.bb2:
+  br label %sw.epilog
+
+sw.bb3:
+  br label %sw.epilog
+
+sw.bb4:
+  br label %sw.epilog
+
+sw.default:
+  br label %sw.epilog
+
+sw.epilog:
+  %pep_code = phi i8 [ %code, %sw.default ], [ 63, %sw.bb4 ], [ 81, %sw.bb3 ], [ 113, %sw.bb2 ], [ 73, %sw.bb1 ], [ 105, %entry ]
+  ret i8 %pep_code
+}

>From bd90b60fd2108c154fc00482ef73cb35507c9c29 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 5 Jan 2024 23:33:21 +0800
Subject: [PATCH 2/2] [InstCombine] Canonicalize `switch(zext/sext(X))` to
 `switch(X)`

---
 .../InstCombine/InstructionCombining.cpp      | 19 ++++++++++++
 llvm/test/Transforms/InstCombine/phi.ll       | 30 ++++++++-----------
 .../InstCombine/switch-zext-sext.ll           |  9 +++---
 .../Transforms/PhaseOrdering/switch-sext.ll   | 20 ++++---------
 4 files changed, 41 insertions(+), 37 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index e845bf38df2dfd..7f2018b3a19958 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3247,6 +3247,25 @@ Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) {
     }
   }
 
+  // Fold switch(zext/sext(X)) into switch(X) if possible.
+  if (match(Cond, m_ZExtOrSExt(m_Value(Op0)))) {
+    bool IsZExt = isa<ZExtInst>(Cond);
+    Type *SrcTy = Op0->getType();
+    unsigned NewWidth = SrcTy->getScalarSizeInBits();
+
+    if (all_of(SI.cases(), [&](const auto &Case) {
+          const APInt &CaseVal = Case.getCaseValue()->getValue();
+          return IsZExt ? CaseVal.isIntN(NewWidth)
+                        : CaseVal.isSignedIntN(NewWidth);
+        })) {
+      for (auto &Case : SI.cases()) {
+        APInt TruncatedCase = Case.getCaseValue()->getValue().trunc(NewWidth);
+        Case.setValue(ConstantInt::get(SI.getContext(), TruncatedCase));
+      }
+      return replaceOperand(SI, 0, Op0);
+    }
+  }
+
   KnownBits Known = computeKnownBits(Cond, 0, &SI);
   unsigned LeadingKnownZeros = Known.countMinLeadingZeros();
   unsigned LeadingKnownOnes = Known.countMinLeadingOnes();
diff --git a/llvm/test/Transforms/InstCombine/phi.ll b/llvm/test/Transforms/InstCombine/phi.ll
index 90818771675b2e..717f4c682a1530 100644
--- a/llvm/test/Transforms/InstCombine/phi.ll
+++ b/llvm/test/Transforms/InstCombine/phi.ll
@@ -996,10 +996,9 @@ done:
 define i1 @PR24766(i8 %x1, i8 %x2, i8 %condition) {
 ; CHECK-LABEL: @PR24766(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
-; CHECK-NEXT:    switch i32 [[CONV]], label [[EPILOG:%.*]] [
-; CHECK-NEXT:      i32 0, label [[SW1:%.*]]
-; CHECK-NEXT:      i32 1, label [[SW2:%.*]]
+; CHECK-NEXT:    switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
+; CHECK-NEXT:      i8 0, label [[SW1:%.*]]
+; CHECK-NEXT:      i8 1, label [[SW2:%.*]]
 ; CHECK-NEXT:    ]
 ; CHECK:       sw1:
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
@@ -1040,10 +1039,9 @@ epilog:
 define i1 @PR24766_no_constants(i8 %x1, i8 %x2, i8 %condition, i1 %another_condition) {
 ; CHECK-LABEL: @PR24766_no_constants(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
-; CHECK-NEXT:    switch i32 [[CONV]], label [[EPILOG:%.*]] [
-; CHECK-NEXT:      i32 0, label [[SW1:%.*]]
-; CHECK-NEXT:      i32 1, label [[SW2:%.*]]
+; CHECK-NEXT:    switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
+; CHECK-NEXT:      i8 0, label [[SW1:%.*]]
+; CHECK-NEXT:      i8 1, label [[SW2:%.*]]
 ; CHECK-NEXT:    ]
 ; CHECK:       sw1:
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
@@ -1085,10 +1083,9 @@ epilog:
 define i1 @PR24766_two_constants(i8 %x1, i8 %x2, i8 %condition) {
 ; CHECK-LABEL: @PR24766_two_constants(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
-; CHECK-NEXT:    switch i32 [[CONV]], label [[EPILOG:%.*]] [
-; CHECK-NEXT:      i32 0, label [[SW1:%.*]]
-; CHECK-NEXT:      i32 1, label [[SW2:%.*]]
+; CHECK-NEXT:    switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
+; CHECK-NEXT:      i8 0, label [[SW1:%.*]]
+; CHECK-NEXT:      i8 1, label [[SW2:%.*]]
 ; CHECK-NEXT:    ]
 ; CHECK:       sw1:
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
@@ -1128,11 +1125,10 @@ epilog:
 define i1 @PR24766_two_constants_two_var(i8 %x1, i8 %x2, i8 %condition) {
 ; CHECK-LABEL: @PR24766_two_constants_two_var(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
-; CHECK-NEXT:    switch i32 [[CONV]], label [[EPILOG:%.*]] [
-; CHECK-NEXT:      i32 0, label [[SW1:%.*]]
-; CHECK-NEXT:      i32 1, label [[SW2:%.*]]
-; CHECK-NEXT:      i32 2, label [[SW3:%.*]]
+; CHECK-NEXT:    switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
+; CHECK-NEXT:      i8 0, label [[SW1:%.*]]
+; CHECK-NEXT:      i8 1, label [[SW2:%.*]]
+; CHECK-NEXT:      i8 2, label [[SW3:%.*]]
 ; CHECK-NEXT:    ]
 ; CHECK:       sw1:
 ; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
diff --git a/llvm/test/Transforms/InstCombine/switch-zext-sext.ll b/llvm/test/Transforms/InstCombine/switch-zext-sext.ll
index 5e5303a1109e9c..c09441352acc0e 100644
--- a/llvm/test/Transforms/InstCombine/switch-zext-sext.ll
+++ b/llvm/test/Transforms/InstCombine/switch-zext-sext.ll
@@ -33,11 +33,10 @@ define i1 @test_switch_with_sext(i16 %a, i1 %b, i1 %c) {
 ; CHECK-LABEL: define i1 @test_switch_with_sext(
 ; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[A_EXT:%.*]] = sext i16 [[A]] to i32
-; CHECK-NEXT:    switch i32 [[A_EXT]], label [[SW_DEFAULT:%.*]] [
-; CHECK-NEXT:      i32 37, label [[SW_BB:%.*]]
-; CHECK-NEXT:      i32 38, label [[SW_BB]]
-; CHECK-NEXT:      i32 39, label [[SW_BB]]
+; CHECK-NEXT:    switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT:      i16 37, label [[SW_BB:%.*]]
+; CHECK-NEXT:      i16 38, label [[SW_BB]]
+; CHECK-NEXT:      i16 39, label [[SW_BB]]
 ; CHECK-NEXT:    ]
 ; CHECK:       sw.bb:
 ; CHECK-NEXT:    ret i1 [[B]]
diff --git a/llvm/test/Transforms/PhaseOrdering/switch-sext.ll b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll
index 764f26f11adb4f..e39771b629e013 100644
--- a/llvm/test/Transforms/PhaseOrdering/switch-sext.ll
+++ b/llvm/test/Transforms/PhaseOrdering/switch-sext.ll
@@ -2,29 +2,19 @@
 ; RUN: opt -S -passes='default<O3>' < %s | FileCheck %s
 
 define i8 @test_switch_with_sext_phi(i8 %code) {
-; CHECK-LABEL: define i8 @test_switch_with_sext_phi(
+; CHECK-LABEL: define noundef i8 @test_switch_with_sext_phi(
 ; CHECK-SAME: i8 [[CODE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CONV:%.*]] = sext i8 [[CODE]] to i32
-; CHECK-NEXT:    switch i32 [[CONV]], label [[SW_DEFAULT:%.*]] [
-; CHECK-NEXT:      i32 105, label [[SW_EPILOG:%.*]]
-; CHECK-NEXT:      i32 73, label [[SW_BB1:%.*]]
-; CHECK-NEXT:      i32 108, label [[SW_BB2:%.*]]
-; CHECK-NEXT:      i32 76, label [[SW_BB3:%.*]]
-; CHECK-NEXT:      i32 63, label [[SW_BB4:%.*]]
+; CHECK-NEXT:    switch i8 [[CODE]], label [[SW_EPILOG:%.*]] [
+; CHECK-NEXT:      i8 76, label [[SW_BB3:%.*]]
+; CHECK-NEXT:      i8 108, label [[SW_BB2:%.*]]
 ; CHECK-NEXT:    ]
-; CHECK:       sw.bb1:
-; CHECK-NEXT:    br label [[SW_EPILOG]]
 ; CHECK:       sw.bb2:
 ; CHECK-NEXT:    br label [[SW_EPILOG]]
 ; CHECK:       sw.bb3:
 ; CHECK-NEXT:    br label [[SW_EPILOG]]
-; CHECK:       sw.bb4:
-; CHECK-NEXT:    br label [[SW_EPILOG]]
-; CHECK:       sw.default:
-; CHECK-NEXT:    br label [[SW_EPILOG]]
 ; CHECK:       sw.epilog:
-; CHECK-NEXT:    [[PEP_CODE:%.*]] = phi i8 [ [[CODE]], [[SW_DEFAULT]] ], [ 63, [[SW_BB4]] ], [ 81, [[SW_BB3]] ], [ 113, [[SW_BB2]] ], [ 73, [[SW_BB1]] ], [ 105, [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[PEP_CODE:%.*]] = phi i8 [ 81, [[SW_BB3]] ], [ 113, [[SW_BB2]] ], [ [[CODE]], [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    ret i8 [[PEP_CODE]]
 ;
 entry:



More information about the llvm-commits mailing list