[llvm] goldsteinn/helper for icmp eq ne (PR #86346)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Mar 22 14:47:49 PDT 2024
https://github.com/goldsteinn created https://github.com/llvm/llvm-project/pull/86346
- **Pre-commit: add test**
- **Fold 'switch(rol(x, C1))'**
- **[InstCombine] Add tests for simplifying Instruction w/ constants with eq/ne Constants; NFC**
- **[InstCombine] Add helper simplifying Instruction w/ constants with eq/ne Constants; NFC**
- **[InstCombine] Use `simplifyOpWithConstantEqConsts` in `foldICmpEquality`**
- **[InstCombine] Use `simplifyOpWithConstantEqConsts` in `visitSwitchInst`**
>From 1064f1fffab1dbd82c78108186b283aa43e954f0 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sat, 23 Mar 2024 00:47:03 +0800
Subject: [PATCH 1/6] Pre-commit: add test
---
.../test/Transforms/InstCombine/switch-rol.ll | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/switch-rol.ll
diff --git a/llvm/test/Transforms/InstCombine/switch-rol.ll b/llvm/test/Transforms/InstCombine/switch-rol.ll
new file mode 100644
index 00000000000000..cd0d066e3e0ce3
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/switch-rol.ll
@@ -0,0 +1,66 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @dummy()
+
+define i32 @switch_rol(i32 %a) #0 {
+; CHECK-LABEL: define i32 @switch_rol(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[ROL:%.*]] = call i32 @llvm.fshl.i32(i32 [[A]], i32 [[A]], i32 30)
+; CHECK-NEXT: switch i32 [[ROL]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[TRAP_EXIT:%.*]]
+; CHECK-NEXT: i32 5, label [[TRAP_EXIT]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: call void @dummy()
+; CHECK-NEXT: br label [[TRAP_EXIT]]
+; CHECK: trap.exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %rol = call i32 @llvm.fshl.i32(i32 %a, i32 %a, i32 30)
+ switch i32 %rol, label %default [
+ i32 0, label %trap.exit
+ i32 5, label %trap.exit
+ ]
+
+default:
+ call void @dummy()
+ br label %trap.exit
+
+trap.exit:
+ ret i32 0
+}
+
+define i32 @switch_rol_2(i32 %a) #0 {
+; CHECK-LABEL: define i32 @switch_rol_2(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[A]], -7
+; CHECK-NEXT: [[ROL:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP0]], i32 [[TMP0]], i32 30)
+; CHECK-NEXT: switch i32 [[ROL]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 0, label [[TRAP_EXIT:%.*]]
+; CHECK-NEXT: i32 5, label [[TRAP_EXIT]]
+; CHECK-NEXT: ]
+; CHECK: default:
+; CHECK-NEXT: call void @dummy()
+; CHECK-NEXT: br label [[TRAP_EXIT]]
+; CHECK: trap.exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %1 = sub i32 %a, 7
+ %rol = call i32 @llvm.fshl.i32(i32 %1, i32 %1, i32 30)
+ switch i32 %rol, label %default [
+ i32 0, label %trap.exit
+ i32 5, label %trap.exit
+ ]
+
+default:
+ call void @dummy()
+ br label %trap.exit
+
+trap.exit:
+ ret i32 0
+}
>From 450520b6d888181449e50f29fbaa5acdcebca4e4 Mon Sep 17 00:00:00 2001
From: YanWQ-monad <YanWQmonad at gmail.com>
Date: Sat, 23 Mar 2024 00:47:45 +0800
Subject: [PATCH 2/6] Fold 'switch(rol(x, C1))'
---
.../Transforms/InstCombine/InstructionCombining.cpp | 10 ++++++++++
llvm/test/Transforms/InstCombine/switch-rol.ll | 13 +++++--------
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 7c40fb4fc86082..b6611cfbbfc1f4 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3645,6 +3645,16 @@ Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) {
}
}
+ // Fold 'switch(rol(x, C1)) case C2:' to 'switch(x) case rol(C2, -C1):'
+ if (match(Cond,
+ m_FShl(m_Value(Op0), m_Deferred(Op0), m_ConstantInt(ShiftAmt)))) {
+ for (auto &Case : SI.cases()) {
+ const APInt NewCase = Case.getCaseValue()->getValue().rotr(ShiftAmt);
+ Case.setValue(ConstantInt::get(SI.getContext(), NewCase));
+ }
+ 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/switch-rol.ll b/llvm/test/Transforms/InstCombine/switch-rol.ll
index cd0d066e3e0ce3..7557ad5dfe276e 100644
--- a/llvm/test/Transforms/InstCombine/switch-rol.ll
+++ b/llvm/test/Transforms/InstCombine/switch-rol.ll
@@ -7,10 +7,9 @@ define i32 @switch_rol(i32 %a) #0 {
; CHECK-LABEL: define i32 @switch_rol(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[ROL:%.*]] = call i32 @llvm.fshl.i32(i32 [[A]], i32 [[A]], i32 30)
-; CHECK-NEXT: switch i32 [[ROL]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: switch i32 [[A]], label [[DEFAULT:%.*]] [
; CHECK-NEXT: i32 0, label [[TRAP_EXIT:%.*]]
-; CHECK-NEXT: i32 5, label [[TRAP_EXIT]]
+; CHECK-NEXT: i32 20, label [[TRAP_EXIT]]
; CHECK-NEXT: ]
; CHECK: default:
; CHECK-NEXT: call void @dummy()
@@ -37,11 +36,9 @@ define i32 @switch_rol_2(i32 %a) #0 {
; CHECK-LABEL: define i32 @switch_rol_2(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[A]], -7
-; CHECK-NEXT: [[ROL:%.*]] = call i32 @llvm.fshl.i32(i32 [[TMP0]], i32 [[TMP0]], i32 30)
-; CHECK-NEXT: switch i32 [[ROL]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i32 0, label [[TRAP_EXIT:%.*]]
-; CHECK-NEXT: i32 5, label [[TRAP_EXIT]]
+; CHECK-NEXT: switch i32 [[A]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i32 7, label [[TRAP_EXIT:%.*]]
+; CHECK-NEXT: i32 27, label [[TRAP_EXIT]]
; CHECK-NEXT: ]
; CHECK: default:
; CHECK-NEXT: call void @dummy()
>From 1e2e2b5ae4623fcefa844aef81ac2fb92c752198 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Fri, 22 Mar 2024 16:11:23 -0500
Subject: [PATCH 3/6] [InstCombine] Add tests for simplifying Instruction w/
constants with eq/ne Constants; NFC
---
.../Transforms/InstCombine/simplify-cmp-eq.ll | 1611 +++++++++++++++++
1 file changed, 1611 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
diff --git a/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll b/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
new file mode 100644
index 00000000000000..d5cd9061763945
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
@@ -0,0 +1,1611 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use.i8(i8)
+define <2 x i1> @simplify_cmp_or_disjoint(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_or_disjoint(
+; CHECK-NEXT: [[V:%.*]] = or disjoint <2 x i8> [[X:%.*]], <i8 12, i8 13>
+; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i8> [[V]], <i8 0, i8 45>
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = or disjoint <2 x i8> %x, <i8 12, i8 13>
+ %r = icmp ne <2 x i8> %v, <i8 0, i8 45>
+ ret <2 x i1> %r
+}
+
+define <2 x i1> @simplify_cmp_or_fail_missing_disjoint(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_or_fail_missing_disjoint(
+; CHECK-NEXT: [[V:%.*]] = or <2 x i8> [[X:%.*]], <i8 12, i8 13>
+; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i8> [[V]], <i8 0, i8 45>
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = or <2 x i8> %x, <i8 12, i8 13>
+ %r = icmp ne <2 x i8> %v, <i8 0, i8 45>
+ ret <2 x i1> %r
+}
+
+define i1 @simplify_cmp_add(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_add(
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[X:%.*]], 43
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = add i8 %x, 12
+ %r = icmp ne i8 %v, 55
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_add_fail_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_add_fail_multiuse(
+; CHECK-NEXT: [[V:%.*]] = add i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[V]], 55
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = add i8 %x, 12
+ call void @use.i8(i8 %v)
+ %r = icmp ne i8 %v, 55
+ ret i1 %r
+}
+
+define <2 x i1> @simplify_cmp_sub(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_sub(
+; CHECK-NEXT: [[V:%.*]] = sub <2 x i8> <i8 12, i8 13>, [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i8> [[V]], <i8 0, i8 55>
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = sub <2 x i8> <i8 12, i8 13>, %x
+ %r = icmp eq <2 x i8> %v, <i8 0, i8 55>
+ ret <2 x i1> %r
+}
+
+define i1 @simplify_cmp_xor(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_xor(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X:%.*]], 59
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = xor i8 %x, 12
+ %r = icmp eq i8 %v, 55
+ ret i1 %r
+}
+
+define <2 x i1> @simplify_cmp_mul_udiv(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_mul_udiv(
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i8> [[X:%.*]], <i8 4, i8 4>
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = mul nuw <2 x i8> %x, <i8 12, i8 12>
+ %r = icmp eq <2 x i8> %v, <i8 48, i8 48>
+ ret <2 x i1> %r
+}
+
+define <2 x i1> @simplify_cmp_mul_udiv_todo(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_mul_udiv_todo(
+; CHECK-NEXT: [[V:%.*]] = mul nuw <2 x i8> [[X:%.*]], <i8 12, i8 12>
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i8> [[V]], <i8 48, i8 60>
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = mul nuw <2 x i8> %x, <i8 12, i8 12>
+ %r = icmp eq <2 x i8> %v, <i8 48, i8 60>
+ ret <2 x i1> %r
+}
+
+define <2 x i1> @simplify_cmp_mul_udiv_fail_missing_nuw(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_mul_udiv_fail_missing_nuw(
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i8> [[X:%.*]], <i8 4, i8 4>
+; CHECK-NEXT: ret <2 x i1> [[R]]
+;
+ %v = mul nsw <2 x i8> %x, <i8 12, i8 12>
+ %r = icmp eq <2 x i8> %v, <i8 48, i8 48>
+ ret <2 x i1> %r
+}
+
+define <2 x i1> @simplify_cmp_mul_udiv_fail_bad_rem(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_mul_udiv_fail_bad_rem(
+; CHECK-NEXT: ret <2 x i1> zeroinitializer
+;
+ %v = mul nuw <2 x i8> %x, <i8 12, i8 12>
+ %r = icmp eq <2 x i8> %v, <i8 47, i8 47>
+ ret <2 x i1> %r
+}
+
+define <2 x i1> @simplify_cmp_mul_sdiv(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_mul_sdiv(
+; CHECK-NEXT: ret <2 x i1> zeroinitializer
+;
+ %v = mul nuw <2 x i8> %x, <i8 12, i8 12>
+ %r = icmp eq <2 x i8> %v, <i8 -48, i8 -48>
+ ret <2 x i1> %r
+}
+
+define <2 x i1> @simplify_cmp_mul_sdiv_fail_bad_rem(<2 x i8> %x) {
+; CHECK-LABEL: @simplify_cmp_mul_sdiv_fail_bad_rem(
+; CHECK-NEXT: ret <2 x i1> zeroinitializer
+;
+ %v = mul nuw <2 x i8> %x, <i8 7, i8 7>
+ %r = icmp eq <2 x i8> %v, <i8 -48, i8 -48>
+ ret <2 x i1> %r
+}
+
+define i1 @simplify_cmp_udiv(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_udiv(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X:%.*]], -112
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = udiv exact i8 %x, 12
+ %r = icmp eq i8 %v, 12
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_udiv_fail_missing_exact(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_udiv_fail_missing_exact(
+; CHECK-NEXT: [[X_OFF:%.*]] = add i8 [[X:%.*]], 112
+; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[X_OFF]], 12
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = udiv i8 %x, 12
+ %r = icmp eq i8 %v, 12
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_udiv_fail_overflow(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_udiv_fail_overflow(
+; CHECK-NEXT: ret i1 false
+;
+ %v = udiv exact i8 %x, 12
+ %r = icmp eq i8 %v, 50
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_sdiv(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_sdiv(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X:%.*]], 72
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = sdiv exact i8 %x, 12
+ %r = icmp eq i8 %v, 6
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_sdiv_fail_missing_exact(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_sdiv_fail_missing_exact(
+; CHECK-NEXT: [[X_OFF:%.*]] = add i8 [[X:%.*]], -72
+; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[X_OFF]], 12
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = sdiv i8 %x, 12
+ %r = icmp eq i8 %v, 6
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_sdiv_fail_overflow(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_sdiv_fail_overflow(
+; CHECK-NEXT: ret i1 false
+;
+ %v = sdiv exact i8 %x, 12
+ %r = icmp eq i8 %v, 12
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_zext(i4 %x) {
+; CHECK-LABEL: @simplify_cmp_zext(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i4 [[X:%.*]], -8
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = zext i4 %x to i8
+ %r = icmp eq i8 %v, 8
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_zext_fail_out_of_bounds(i4 %x) {
+; CHECK-LABEL: @simplify_cmp_zext_fail_out_of_bounds(
+; CHECK-NEXT: ret i1 false
+;
+ %v = zext i4 %x to i8
+ %r = icmp eq i8 %v, 16
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_sext(i4 %x) {
+; CHECK-LABEL: @simplify_cmp_sext(
+; CHECK-NEXT: ret i1 false
+;
+ %v = sext i4 %x to i8
+ %r = icmp eq i8 %v, 8
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_sext2(i4 %x) {
+; CHECK-LABEL: @simplify_cmp_sext2(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i4 [[X:%.*]], -8
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = sext i4 %x to i8
+ %r = icmp eq i8 %v, -8
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_sext_fail_out_of_bounds(i4 %x) {
+; CHECK-LABEL: @simplify_cmp_sext_fail_out_of_bounds(
+; CHECK-NEXT: ret i1 false
+;
+ %v = sext i4 %x to i8
+ %r = icmp eq i8 %v, -16
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_shl(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_shl(
+; CHECK-NEXT: [[V_MASK:%.*]] = and i8 [[X:%.*]], 31
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V_MASK]], 7
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = shl i8 %x, 3
+ %r = icmp eq i8 %v, 56
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_shl_fail_shiftout(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_shl_fail_shiftout(
+; CHECK-NEXT: ret i1 false
+;
+ %v = shl i8 %x, 3
+ %r = icmp eq i8 %v, 57
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_shl_fail_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_shl_fail_multiuse(
+; CHECK-NEXT: [[V:%.*]] = shl i8 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 56
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = shl i8 %x, 3
+ %r = icmp eq i8 %v, 56
+ call void @use.i8(i8 %v)
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_shl_okay_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_shl_okay_multiuse(
+; CHECK-NEXT: [[V:%.*]] = shl nuw i8 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X]], 7
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = shl nuw i8 %x, 3
+ %r = icmp eq i8 %v, 56
+ call void @use.i8(i8 %v)
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_shl_okay_multiuse2(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_shl_okay_multiuse2(
+; CHECK-NEXT: [[V:%.*]] = shl nsw i8 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X]], 7
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = shl nsw i8 %x, 3
+ %r = icmp eq i8 %v, 56
+ call void @use.i8(i8 %v)
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_lshr(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_lshr(
+; CHECK-NEXT: [[V_MASK:%.*]] = and i8 [[X:%.*]], -8
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V_MASK]], -64
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = lshr i8 %x, 3
+ %r = icmp eq i8 %v, 24
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_lshr_fail_shiftout(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_lshr_fail_shiftout(
+; CHECK-NEXT: ret i1 false
+;
+ %v = lshr i8 %x, 3
+ %r = icmp eq i8 %v, 56
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_lshr_fail_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_lshr_fail_multiuse(
+; CHECK-NEXT: [[V:%.*]] = lshr i8 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 24
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = lshr i8 %x, 3
+ %r = icmp eq i8 %v, 24
+ call void @use.i8(i8 %v)
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_lshr_okay_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_lshr_okay_multiuse(
+; CHECK-NEXT: [[V:%.*]] = lshr exact i8 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X]], -64
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = lshr exact i8 %x, 3
+ %r = icmp eq i8 %v, 24
+ call void @use.i8(i8 %v)
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_ashr(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_ashr(
+; CHECK-NEXT: [[V_MASK:%.*]] = and i8 [[X:%.*]], -8
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V_MASK]], 96
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = ashr i8 %x, 3
+ %r = icmp eq i8 %v, 12
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_ashr_fail_shiftout(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_ashr_fail_shiftout(
+; CHECK-NEXT: ret i1 false
+;
+ %v = ashr i8 %x, 3
+ %r = icmp eq i8 %v, 56
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_ashr_fail_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_ashr_fail_multiuse(
+; CHECK-NEXT: [[V:%.*]] = ashr i8 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = ashr i8 %x, 3
+ %r = icmp eq i8 %v, 12
+ call void @use.i8(i8 %v)
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_ashr_okay_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_ashr_okay_multiuse(
+; CHECK-NEXT: [[V:%.*]] = ashr exact i8 [[X:%.*]], 3
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X]], -96
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = ashr exact i8 %x, 3
+ %r = icmp eq i8 %v, -12
+ call void @use.i8(i8 %v)
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_bitreverse(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_bitreverse(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X:%.*]], -34
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = call i8 @llvm.bitreverse.i8(i8 %x)
+ %r = icmp eq i8 %v, 123
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_bswap(i16 %x) {
+; CHECK-LABEL: @simplify_cmp_bswap(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i16 [[X:%.*]], 14640
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = call i16 @llvm.bswap.i16(i16 %x)
+ %r = icmp eq i16 %v, 12345
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_fshr(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_fshr(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X:%.*]], -37
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 3)
+ %r = icmp eq i8 %v, 123
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_fshr_fail_not_rotate(i8 %x, i8 %y) {
+; CHECK-LABEL: @simplify_cmp_fshr_fail_not_rotate(
+; CHECK-NEXT: [[V:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 5)
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 123
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = call i8 @llvm.fshr.i8(i8 %x, i8 %y, i8 3)
+ %r = icmp eq i8 %v, 123
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_fshl(i8 %x) {
+; CHECK-LABEL: @simplify_cmp_fshl(
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[X:%.*]], 66
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 7)
+ %r = icmp eq i8 %v, 33
+ ret i1 %r
+}
+
+define i1 @simplify_cmp_fshl_fail_not_rotate(i8 %x, i8 %y) {
+; CHECK-LABEL: @simplify_cmp_fshl_fail_not_rotate(
+; CHECK-NEXT: [[V:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 7)
+; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 33
+; CHECK-NEXT: ret i1 [[R]]
+;
+ %v = call i8 @llvm.fshl.i8(i8 %x, i8 %y, i8 7)
+ %r = icmp eq i8 %v, 33
+ ret i1 %r
+}
+
+define i8 @simplify_switch_or_disjoint(i8 %x) {
+; CHECK-LABEL: @simplify_switch_or_disjoint(
+; CHECK-NEXT: [[V:%.*]] = or disjoint i8 [[X:%.*]], 12
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 44, label [[L44:%.*]]
+; CHECK-NEXT: i8 45, label [[L45:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l44:
+; CHECK-NEXT: ret i8 44
+; CHECK: l45:
+; CHECK-NEXT: ret i8 40
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = or disjoint i8 %x, 12
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 44, label %l44
+ i8 45, label %l45
+ ]
+
+l12:
+ ret i8 12
+l44:
+ ret i8 44
+l45:
+ ret i8 40
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_add(i8 %x) {
+; CHECK-LABEL: @simplify_switch_add(
+; CHECK-NEXT: [[V:%.*]] = add i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 0, label [[L12:%.*]]
+; CHECK-NEXT: i8 32, label [[L44:%.*]]
+; CHECK-NEXT: i8 33, label [[L45:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l44:
+; CHECK-NEXT: ret i8 44
+; CHECK: l45:
+; CHECK-NEXT: ret i8 40
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = add i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 44, label %l44
+ i8 45, label %l45
+ ]
+
+l12:
+ ret i8 12
+l44:
+ ret i8 44
+l45:
+ ret i8 40
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_sub(i8 %x) {
+; CHECK-LABEL: @simplify_switch_sub(
+; CHECK-NEXT: [[V:%.*]] = sub i8 12, [[X:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 0, label [[L12:%.*]]
+; CHECK-NEXT: i8 -32, label [[L44:%.*]]
+; CHECK-NEXT: i8 -33, label [[L45:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l44:
+; CHECK-NEXT: ret i8 44
+; CHECK: l45:
+; CHECK-NEXT: ret i8 40
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = sub i8 12, %x
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 44, label %l44
+ i8 45, label %l45
+ ]
+
+l12:
+ ret i8 12
+l44:
+ ret i8 44
+l45:
+ ret i8 40
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_xor(i8 %x) {
+; CHECK-LABEL: @simplify_switch_xor(
+; CHECK-NEXT: [[V:%.*]] = xor i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 44, label [[L44:%.*]]
+; CHECK-NEXT: i8 45, label [[L45:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l44:
+; CHECK-NEXT: ret i8 44
+; CHECK: l45:
+; CHECK-NEXT: ret i8 40
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = xor i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 44, label %l44
+ i8 45, label %l45
+ ]
+
+l12:
+ ret i8 12
+l44:
+ ret i8 44
+l45:
+ ret i8 40
+default:
+ ret i8 -1
+
+}
+
+define i8 @simplify_switch_mul_udiv(i8 %x) {
+; CHECK-LABEL: @simplify_switch_mul_udiv(
+; CHECK-NEXT: [[V:%.*]] = mul nuw i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 48, label [[L48:%.*]]
+; CHECK-NEXT: i8 60, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = mul nuw i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 48, label %l48
+ i8 60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_mul_udiv_fail_bad_rem(i8 %x) {
+; CHECK-LABEL: @simplify_switch_mul_udiv_fail_bad_rem(
+; CHECK-NEXT: [[V:%.*]] = mul nuw i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 47, label [[L48:%.*]]
+; CHECK-NEXT: i8 60, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = mul nuw i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 47, label %l48
+ i8 60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_mul_sdiv(i8 %x) {
+; CHECK-LABEL: @simplify_switch_mul_sdiv(
+; CHECK-NEXT: [[V:%.*]] = mul nuw i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -12, label [[L12:%.*]]
+; CHECK-NEXT: i8 48, label [[L48:%.*]]
+; CHECK-NEXT: i8 -60, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = mul nuw i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 48, label %l48
+ i8 -60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_mul_sdiv_fail_bad_rem(i8 %x) {
+; CHECK-LABEL: @simplify_switch_mul_sdiv_fail_bad_rem(
+; CHECK-NEXT: [[V:%.*]] = mul nuw i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -12, label [[L12:%.*]]
+; CHECK-NEXT: i8 -47, label [[L48:%.*]]
+; CHECK-NEXT: i8 60, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = mul nuw i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 -47, label %l48
+ i8 60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_udiv(i8 %x) {
+; CHECK-LABEL: @simplify_switch_udiv(
+; CHECK-NEXT: [[V:%.*]] = udiv exact i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 3, label [[L48:%.*]]
+; CHECK-NEXT: i8 9, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = udiv exact i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 3, label %l48
+ i8 9, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_udiv_fail_overflow(i8 %x) {
+; CHECK-LABEL: @simplify_switch_udiv_fail_overflow(
+; CHECK-NEXT: [[V:%.*]] = udiv exact i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 50, label [[L48:%.*]]
+; CHECK-NEXT: i8 9, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = udiv exact i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 50, label %l48
+ i8 9, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_sdiv(i8 %x) {
+; CHECK-LABEL: @simplify_switch_sdiv(
+; CHECK-NEXT: [[V:%.*]] = sdiv exact i8 [[X:%.*]], 12
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 4, label [[L12:%.*]]
+; CHECK-NEXT: i8 -3, label [[L48:%.*]]
+; CHECK-NEXT: i8 7, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = sdiv exact i8 %x, 12
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 4, label %l12
+ i8 -3, label %l48
+ i8 7, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_sdiv_fail_overflow(i8 %x) {
+; CHECK-LABEL: @simplify_switch_sdiv_fail_overflow(
+; CHECK-NEXT: [[V:%.*]] = udiv exact i8 [[X:%.*]], 12
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 4, label [[L12:%.*]]
+; CHECK-NEXT: i8 -3, label [[L48:%.*]]
+; CHECK-NEXT: i8 12, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = udiv exact i8 %x, 12
+ switch i8 %v, label %default[
+ i8 4, label %l12
+ i8 -3, label %l48
+ i8 12, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_zext(i4 %x) {
+; CHECK-LABEL: @simplify_switch_zext(
+; CHECK-NEXT: switch i4 [[X:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i4 4, label [[L12:%.*]]
+; CHECK-NEXT: i4 3, label [[L48:%.*]]
+; CHECK-NEXT: i4 -4, label [[L60:%.*]]
+; CHECK-NEXT: i4 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = zext i4 %x to i8
+ switch i8 %v, label %default[
+ i8 4, label %l12
+ i8 3, label %l48
+ i8 12, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_zext_fail_out_of_bounds(i4 %x) {
+; CHECK-LABEL: @simplify_switch_zext_fail_out_of_bounds(
+; CHECK-NEXT: [[V:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 16, label [[L12:%.*]]
+; CHECK-NEXT: i8 3, label [[L48:%.*]]
+; CHECK-NEXT: i8 12, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = zext i4 %x to i8
+ switch i8 %v, label %default[
+ i8 16, label %l12
+ i8 3, label %l48
+ i8 12, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_sext(i4 %x) {
+; CHECK-LABEL: @simplify_switch_sext(
+; CHECK-NEXT: [[V:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -4, label [[L12:%.*]]
+; CHECK-NEXT: i8 3, label [[L48:%.*]]
+; CHECK-NEXT: i8 12, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = zext i4 %x to i8
+ switch i8 %v, label %default[
+ i8 -4, label %l12
+ i8 3, label %l48
+ i8 12, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_sext_fail_out_of_bounds(i4 %x) {
+; CHECK-LABEL: @simplify_switch_sext_fail_out_of_bounds(
+; CHECK-NEXT: [[V:%.*]] = zext i4 [[X:%.*]] to i8
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -4, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 12, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = zext i4 %x to i8
+ switch i8 %v, label %default[
+ i8 -4, label %l12
+ i8 16, label %l48
+ i8 12, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_shl(i8 %x) {
+; CHECK-LABEL: @simplify_switch_shl(
+; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X:%.*]], 63
+; CHECK-NEXT: switch i8 [[TMP1]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 61, label [[L12:%.*]]
+; CHECK-NEXT: i8 4, label [[L48:%.*]]
+; CHECK-NEXT: i8 15, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = shl i8 %x, 2
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_shl_fail_shiftout(i8 %x) {
+; CHECK-LABEL: @simplify_switch_shl_fail_shiftout(
+; CHECK-NEXT: [[V:%.*]] = shl i8 [[X:%.*]], 2
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -12, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 2, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = shl i8 %x, 2
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 2, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_shl2(i8 %x) {
+; CHECK-LABEL: @simplify_switch_shl2(
+; CHECK-NEXT: [[V:%.*]] = shl nsw i8 [[X:%.*]], 2
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -3, label [[L12:%.*]]
+; CHECK-NEXT: i8 4, label [[L48:%.*]]
+; CHECK-NEXT: i8 15, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = shl nsw i8 %x, 2
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_shl_fail_multiuse(i8 %x) {
+; CHECK-LABEL: @simplify_switch_shl_fail_multiuse(
+; CHECK-NEXT: [[V:%.*]] = shl i8 [[X:%.*]], 2
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -12, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 60, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = shl i8 %x, 2
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_shl3(i8 %x) {
+; CHECK-LABEL: @simplify_switch_shl3(
+; CHECK-NEXT: [[V:%.*]] = shl nuw i8 [[X:%.*]], 2
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 61, label [[L12:%.*]]
+; CHECK-NEXT: i8 4, label [[L48:%.*]]
+; CHECK-NEXT: i8 15, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = shl nuw i8 %x, 2
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 60, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_lshr(i8 %x) {
+; CHECK-LABEL: @simplify_switch_lshr(
+; CHECK-NEXT: [[V:%.*]] = lshr exact i8 [[X:%.*]], 2
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 30, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = lshr exact i8 %x, 2
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 16, label %l48
+ i8 30, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_lshr_fail_shiftout(i8 %x) {
+; CHECK-LABEL: @simplify_switch_lshr_fail_shiftout(
+; CHECK-NEXT: [[V:%.*]] = lshr exact i8 [[X:%.*]], 2
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 12, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 30, label [[L60:%.*]]
+; CHECK-NEXT: i8 -128, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = lshr exact i8 %x, 2
+ switch i8 %v, label %default[
+ i8 12, label %l12
+ i8 16, label %l48
+ i8 30, label %l60
+ i8 128, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_ashr(i8 %x) {
+; CHECK-LABEL: @simplify_switch_ashr(
+; CHECK-NEXT: [[V:%.*]] = ashr exact i8 [[X:%.*]], 2
+; CHECK-NEXT: call void @use.i8(i8 [[V]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -12, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 3, label [[L60:%.*]]
+; CHECK-NEXT: i8 0, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = ashr exact i8 %x, 2
+ call void @use.i8(i8 %v)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 3, label %l60
+ i8 0, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_ashr_fail_shiftout(i8 %x) {
+; CHECK-LABEL: @simplify_switch_ashr_fail_shiftout(
+; CHECK-NEXT: [[V:%.*]] = ashr i8 [[X:%.*]], 2
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -12, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 3, label [[L60:%.*]]
+; CHECK-NEXT: i8 -128, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = ashr i8 %x, 2
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 3, label %l60
+ i8 128, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_bitreverse(i8 %x) {
+; CHECK-LABEL: @simplify_switch_bitreverse(
+; CHECK-NEXT: [[V:%.*]] = call i8 @llvm.bitreverse.i8(i8 [[X:%.*]])
+; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -12, label [[L12:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: i8 30, label [[L60:%.*]]
+; CHECK-NEXT: i8 -128, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = call i8 @llvm.bitreverse.i8(i8 %x)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 30, label %l60
+ i8 128, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i16 @simplify_switch_bswap(i16 %x) {
+; CHECK-LABEL: @simplify_switch_bswap(
+; CHECK-NEXT: [[V:%.*]] = call i16 @llvm.bswap.i16(i16 [[X:%.*]])
+; CHECK-NEXT: switch i16 [[V]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i16 -12, label [[L12:%.*]]
+; CHECK-NEXT: i16 16, label [[L48:%.*]]
+; CHECK-NEXT: i16 30, label [[L60:%.*]]
+; CHECK-NEXT: i16 128, label [[L0:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i16 12
+; CHECK: l48:
+; CHECK-NEXT: ret i16 44
+; CHECK: l60:
+; CHECK-NEXT: ret i16 3
+; CHECK: l0:
+; CHECK-NEXT: ret i16 9
+; CHECK: default:
+; CHECK-NEXT: ret i16 -1
+;
+ %v = call i16 @llvm.bswap.i16(i16 %x)
+ switch i16 %v, label %default[
+ i16 -12, label %l12
+ i16 16, label %l48
+ i16 30, label %l60
+ i16 128, label %l0
+ ]
+
+l12:
+ ret i16 12
+l48:
+ ret i16 44
+l60:
+ ret i16 3
+l0:
+ ret i16 9
+default:
+ ret i16 -1
+}
+
+define i8 @simplify_switch_fshr(i8 %x) {
+; CHECK-LABEL: @simplify_switch_fshr(
+; CHECK-NEXT: switch i8 [[V:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -89, label [[L12:%.*]]
+; CHECK-NEXT: i8 -128, label [[L0:%.*]]
+; CHECK-NEXT: i8 -16, label [[L60:%.*]]
+; CHECK-NEXT: i8 4, label [[L1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 3)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 30, label %l60
+ i8 128, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
+
+define i8 @simplify_switch_fshl(i8 %x) {
+; CHECK-LABEL: @simplify_switch_fshl(
+; CHECK-NEXT: switch i8 [[V:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -98, label [[L12:%.*]]
+; CHECK-NEXT: i8 2, label [[L49:%.*]]
+; CHECK-NEXT: i8 -61, label [[L60:%.*]]
+; CHECK-NEXT: i8 16, label [[L48:%.*]]
+; CHECK-NEXT: ]
+; CHECK: l12:
+; CHECK-NEXT: ret i8 12
+; CHECK: l48:
+; CHECK-NEXT: ret i8 44
+; CHECK: l60:
+; CHECK-NEXT: ret i8 3
+; CHECK: l0:
+; CHECK-NEXT: ret i8 9
+; CHECK: default:
+; CHECK-NEXT: ret i8 -1
+;
+ %v = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 3)
+ switch i8 %v, label %default[
+ i8 -12, label %l12
+ i8 16, label %l48
+ i8 30, label %l60
+ i8 128, label %l0
+ ]
+
+l12:
+ ret i8 12
+l48:
+ ret i8 44
+l60:
+ ret i8 3
+l0:
+ ret i8 9
+default:
+ ret i8 -1
+}
>From 8a5b167aeaf71d6ecc0f74383a8da87a903623ce Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Fri, 22 Mar 2024 15:12:47 -0500
Subject: [PATCH 4/6] [InstCombine] Add helper simplifying Instruction w/
constants with eq/ne Constants; NFC
We want to be able to simplify these types of relationships in
multiple places (simplify `icmp eq/ne` and `switch` statements) so it
makes sense to roll all our logic into a single helper.
---
.../Transforms/InstCombine/InstCombiner.h | 18 ++
.../InstCombine/InstructionCombining.cpp | 228 ++++++++++++++++++
2 files changed, 246 insertions(+)
diff --git a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
index 93090431cbb69f..4b89ea73f16e37 100644
--- a/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
+++ b/llvm/include/llvm/Transforms/InstCombine/InstCombiner.h
@@ -198,6 +198,24 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner {
PatternMatch::m_Value()));
}
+ /// Assumes that we have `Op eq/ne Vals` (either icmp or switch). Will try to
+ /// constant fold `Vals` so that we can use `Op' eq/ne Vals'`. For example if
+ /// we have `Op` as `add X, C0`, it will simplify all `Vals` as `Vals[i] - C0`
+ /// and return `X`.
+ Value *simplifyOpWithConstantEqConsts(Value *Op, BuilderTy &Builder,
+ SmallVector<Constant *> &Vals,
+ bool ReqOneUseAdd = true);
+
+ Value *simplifyOpWithConstantEqConsts(Value *Op, BuilderTy &Builder,
+ Constant *&Val,
+ bool ReqOneUseAdd = true) {
+ SmallVector<Constant *> CVals;
+ CVals.push_back(Val);
+ Value *R = simplifyOpWithConstantEqConsts(Op, Builder, CVals, ReqOneUseAdd);
+ Val = CVals[0];
+ return R;
+ }
+
/// Return nonnull value if V is free to invert under the condition of
/// WillInvertAllUses.
/// If Builder is nonnull, it will return a simplified ~V.
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index b6611cfbbfc1f4..2ce8e0d789c457 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3572,6 +3572,234 @@ Instruction *InstCombinerImpl::visitBranchInst(BranchInst &BI) {
return nullptr;
}
+Value *
+InstCombiner::simplifyOpWithConstantEqConsts(Value *Op, BuilderTy &Builder,
+ SmallVector<Constant *> &Vals,
+ bool ReqOneUseAdd) {
+
+ Operator *I = dyn_cast<Operator>(Op);
+ if (!I)
+ return nullptr;
+
+ auto ReverseAll = [&](function_ref<Constant *(Constant *)> ReverseF) {
+ for (size_t i = 0, e = Vals.size(); i < e; ++i) {
+ Vals[i] = ReverseF(Vals[i]);
+ }
+ };
+
+ SmallVector<const APInt *, 4> ValsAsAPInt;
+ for (Constant *C : Vals) {
+ const APInt *CAPInt;
+ if (!match(C, m_APInt(CAPInt)))
+ break;
+ ValsAsAPInt.push_back(CAPInt);
+ }
+ bool UseAPInt = ValsAsAPInt.size() == Vals.size();
+
+ auto ReverseAllAPInt = [&](function_ref<APInt(const APInt *)> ReverseF) {
+ assert(UseAPInt && "Can't reverse non-apint constants!");
+ for (size_t i = 0, e = Vals.size(); i < e; ++i) {
+ Vals[i] = ConstantInt::get(Vals[i]->getType(), ReverseF(ValsAsAPInt[i]));
+ }
+ };
+
+ Constant *C;
+ switch (I->getOpcode()) {
+ default:
+ break;
+ case Instruction::Or:
+ if (!match(I, m_DisjointOr(m_Value(), m_Value())))
+ break;
+ // Can treat `or disjoint` as add
+ [[fallthrough]];
+ case Instruction::Add:
+ // We get some regressions if we drop the OneUse for add in some cases.
+ // See discussion in D58633.
+ if (ReqOneUseAdd && !I->hasOneUse())
+ break;
+ if (!match(I->getOperand(1), m_ImmConstant(C)))
+ break;
+ // X + C0 == C1 -> X == C1 - C0
+ ReverseAll([&](Constant *Val) { return ConstantExpr::getSub(Val, C); });
+ return I->getOperand(0);
+ case Instruction::Sub:
+ if (!match(I->getOperand(0), m_ImmConstant(C)))
+ break;
+ // C0 - X == C1 -> X == C0 - C1
+ ReverseAll([&](Constant *Val) { return ConstantExpr::getSub(C, Val); });
+ return I->getOperand(1);
+ case Instruction::Xor:
+ if (!match(I->getOperand(1), m_ImmConstant(C)))
+ break;
+ // X ^ C0 == C1 -> X == C1 ^ C0
+ ReverseAll([&](Constant *Val) { return ConstantExpr::getXor(Val, C); });
+ return I->getOperand(0);
+ case Instruction::Mul: {
+ const APInt *MC;
+ if (!UseAPInt || !match(I->getOperand(1), m_APInt(MC)) || MC->isZero())
+ break;
+ OverflowingBinaryOperator *Mul = cast<OverflowingBinaryOperator>(I);
+ if (!Mul->hasNoUnsignedWrap())
+ break;
+
+ // X nuw C0 == C1 -> X == C1 u/ C0 iff C1 u% C0 == 0
+ if (all_of(ValsAsAPInt,
+ [&](const APInt * AC) { return AC->urem(*MC).isZero(); })) {
+ ReverseAllAPInt([&](const APInt *Val) { return Val->udiv(*MC); });
+ return I->getOperand(0);
+ }
+
+ // X nuw C0 == C1 -> X == C1 s/ C0 iff C1 s% C0 == 0
+ if (all_of(ValsAsAPInt, [&](const APInt * AC) {
+ return (!AC->isMinSignedValue() || !MC->isAllOnes()) &&
+ AC->srem(*MC).isZero();
+ })) {
+ ReverseAllAPInt([&](const APInt *Val) { return Val->sdiv(*MC); });
+ return I->getOperand(0);
+ }
+ break;
+ }
+ case Instruction::UDiv:
+ case Instruction::SDiv: {
+ const APInt *DC;
+ if (!UseAPInt)
+ break;
+ if (!UseAPInt || !match(I->getOperand(1), m_APInt(DC)))
+ break;
+ if (!cast<PossiblyExactOperator>(Op)->isExact())
+ break;
+ // X u/ C0 == C1 -> X == C0 * C1 iff C0 * C1 is nuw
+ // X s/ C0 == C1 -> X == C0 * C1 iff C0 * C1 is nsw
+ if (!all_of(ValsAsAPInt, [&](const APInt *AC) {
+ bool Ov;
+ (void)(I->getOpcode() == Instruction::UDiv ? DC->umul_ov(*AC, Ov)
+ : DC->smul_ov(*AC, Ov));
+ return !Ov;
+ }))
+ break;
+
+ ReverseAllAPInt([&](const APInt *Val) { return (*Val) * (*DC); });
+ return I->getOperand(0);
+ }
+ case Instruction::ZExt:
+ case Instruction::SExt: {
+ if (!UseAPInt)
+ break;
+ bool IsZExt = isa<ZExtInst>(I);
+ Type *SrcTy = I->getOperand(0)->getType();
+ unsigned NewWidth = SrcTy->getScalarSizeInBits();
+ // zext(X) == C1 -> X == trunc C1 iff zext(trunc(C1)) == C1
+ // sext(X) == C1 -> X == trunc C1 iff sext(trunc(C1)) == C1
+ if (!all_of(ValsAsAPInt, [&](const APInt *AC) {
+ return IsZExt ? AC->isIntN(NewWidth) : AC->isSignedIntN(NewWidth);
+ }))
+ break;
+
+ for (size_t i = 0, e = Vals.size(); i < e; ++i) {
+ Vals[i] = ConstantInt::get(SrcTy, ValsAsAPInt[i]->trunc(NewWidth));
+ }
+ return I->getOperand(0);
+ }
+ case Instruction::Shl:
+ case Instruction::LShr:
+ case Instruction::AShr: {
+ if (!UseAPInt)
+ break;
+ uint64_t ShAmtC;
+ if (!match(I->getOperand(1), m_ConstantInt(ShAmtC)))
+ break;
+ if (ShAmtC >= I->getType()->getScalarSizeInBits())
+ break;
+
+ // X << C0 == C1 -> X == C1 >> C0 iff C1 >> C0 is exact
+ // X u>> C0 == C1 -> X == C1 << C0 iff C1 << C0 is nuw
+ // X s>> C0 == C1 -> X == C1 << C0 iff C1 << C0 is nsw
+ if (!all_of(ValsAsAPInt, [&](const APInt *AC) {
+ switch (I->getOpcode()) {
+ case Instruction::Shl:
+ return AC->countr_zero() >= ShAmtC;
+ case Instruction::LShr:
+ return AC->countl_zero() >= ShAmtC;
+ case Instruction::AShr:
+ return AC->getNumSignBits() >= ShAmtC;
+ return false;
+ default:
+ llvm_unreachable("Already checked Opcode");
+ }
+ }))
+ break;
+
+ bool HasExact = false, HasNUW = false, HasNSW = false;
+ if (I->getOpcode() == Instruction::Shl) {
+ OverflowingBinaryOperator *Shl = cast<OverflowingBinaryOperator>(I);
+ HasNUW = Shl->hasNoUnsignedWrap();
+ HasNSW = Shl->hasNoSignedWrap();
+ } else {
+ HasExact = cast<PossiblyExactOperator>(Op)->isExact();
+ }
+
+ Value *R = I->getOperand(0);
+ if (!HasExact && !HasNUW && !HasNSW) {
+ if (!I->hasOneUse())
+ break;
+
+ // We may be shifting out 1s from X, so need to mask it.
+ unsigned BitWidth = R->getType()->getScalarSizeInBits();
+ R = Builder.CreateAnd(
+ R, I->getOpcode() == Instruction::Shl
+ ? APInt::getLowBitsSet(BitWidth, BitWidth - ShAmtC)
+ : APInt::getHighBitsSet(BitWidth, BitWidth - ShAmtC));
+ }
+
+ ReverseAllAPInt([&](const APInt *Val) {
+ if (I->getOpcode() == Instruction::Shl)
+ return HasNSW ? Val->ashr(ShAmtC) : Val->lshr(ShAmtC);
+ return Val->shl(ShAmtC);
+ });
+ return R;
+ }
+ case Instruction::Call:
+ case Instruction::Invoke: {
+ if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
+ switch (II->getIntrinsicID()) {
+ default:
+ break;
+ case Intrinsic::bitreverse:
+ if (!UseAPInt)
+ break;
+ // bitreverse(X) == C -> X == bitreverse(C)
+ ReverseAllAPInt([&](const APInt *Val) { return Val->reverseBits(); });
+ return II->getArgOperand(0);
+ case Intrinsic::bswap:
+ if (!UseAPInt)
+ break;
+ // bswap(X) == C -> X == bswap(C)
+ ReverseAllAPInt([&](const APInt *Val) { return Val->byteSwap(); });
+ return II->getArgOperand(0);
+ case Intrinsic::fshr:
+ case Intrinsic::fshl: {
+ if (!UseAPInt)
+ break;
+ if (II->getArgOperand(0) != II->getArgOperand(1))
+ break;
+ const APInt *RotAmtC;
+ if (!match(II->getArgOperand(2), m_APInt(RotAmtC)))
+ break;
+ // rol(X, C0) == C1 -> X == ror(C0, C1)
+ // ror(X, C0) == C1 -> X == rol(C0, C1)
+ ReverseAllAPInt([&](const APInt *Val) {
+ return II->getIntrinsicID() == Intrinsic::fshl ? Val->rotr(*RotAmtC)
+ : Val->rotl(*RotAmtC);
+ });
+ return II->getArgOperand(0);
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) {
Value *Cond = SI.getCondition();
Value *Op0;
>From 1f31efe5fd0bc61ec734d509f0a4b79e26c72c5d Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Fri, 22 Mar 2024 15:12:52 -0500
Subject: [PATCH 5/6] [InstCombine] Use `simplifyOpWithConstantEqConsts` in
`foldICmpEquality`
I dropped the obviously redundant cases, but we still have some
sitting around.
---
.../InstCombine/InstCombineCompares.cpp | 32 +++++--------------
llvm/test/Transforms/InstCombine/icmp-add.ll | 6 ++--
.../InstCombine/icmp-equality-xor.ll | 3 +-
llvm/test/Transforms/InstCombine/icmp-sub.ll | 6 ++--
.../InstCombine/prevent-cmp-merge.ll | 12 +++----
.../Transforms/InstCombine/simplify-cmp-eq.ll | 6 ++--
6 files changed, 21 insertions(+), 44 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index db302d7e526844..19a59bcc96d506 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3572,16 +3572,6 @@ Instruction *InstCombinerImpl::foldICmpEqIntrinsicWithConstant(
return new ICmpInst(Pred, II->getArgOperand(0), ConstantInt::get(Ty, C));
break;
- case Intrinsic::bswap:
- // bswap(A) == C -> A == bswap(C)
- return new ICmpInst(Pred, II->getArgOperand(0),
- ConstantInt::get(Ty, C.byteSwap()));
-
- case Intrinsic::bitreverse:
- // bitreverse(A) == C -> A == bitreverse(C)
- return new ICmpInst(Pred, II->getArgOperand(0),
- ConstantInt::get(Ty, C.reverseBits()));
-
case Intrinsic::ctlz:
case Intrinsic::cttz: {
// ctz(A) == bitwidth(A) -> A == 0 and likewise for !=
@@ -3618,20 +3608,6 @@ Instruction *InstCombinerImpl::foldICmpEqIntrinsicWithConstant(
break;
}
- case Intrinsic::fshl:
- case Intrinsic::fshr:
- if (II->getArgOperand(0) == II->getArgOperand(1)) {
- const APInt *RotAmtC;
- // ror(X, RotAmtC) == C --> X == rol(C, RotAmtC)
- // rol(X, RotAmtC) == C --> X == ror(C, RotAmtC)
- if (match(II->getArgOperand(2), m_APInt(RotAmtC)))
- return new ICmpInst(Pred, II->getArgOperand(0),
- II->getIntrinsicID() == Intrinsic::fshl
- ? ConstantInt::get(Ty, C.rotr(*RotAmtC))
- : ConstantInt::get(Ty, C.rotl(*RotAmtC)));
- }
- break;
-
case Intrinsic::umax:
case Intrinsic::uadd_sat: {
// uadd.sat(a, b) == 0 -> (a | b) == 0
@@ -5456,6 +5432,14 @@ Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) {
Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
const CmpInst::Predicate Pred = I.getPredicate();
+ {
+ Constant *C;
+ if (match(Op1, m_ImmConstant(C))) {
+ if (auto *R = simplifyOpWithConstantEqConsts(Op0, Builder, C))
+ return new ICmpInst(Pred, R, C);
+ }
+ }
+
Value *A, *B, *C, *D;
if (match(Op0, m_Xor(m_Value(A), m_Value(B)))) {
if (A == Op1 || B == Op1) { // (A^B) == A -> B == 0
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index b99ed20d7d431c..5caf881a7d6d4f 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -2371,8 +2371,7 @@ define <2 x i1> @icmp_eq_add_non_splat(<2 x i32> %a) {
define <2 x i1> @icmp_eq_add_undef2(<2 x i32> %a) {
; CHECK-LABEL: @icmp_eq_add_undef2(
-; CHECK-NEXT: [[ADD:%.*]] = add <2 x i32> [[A:%.*]], <i32 5, i32 5>
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[ADD]], <i32 10, i32 undef>
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], <i32 5, i32 undef>
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
%add = add <2 x i32> %a, <i32 5, i32 5>
@@ -2382,8 +2381,7 @@ define <2 x i1> @icmp_eq_add_undef2(<2 x i32> %a) {
define <2 x i1> @icmp_eq_add_non_splat2(<2 x i32> %a) {
; CHECK-LABEL: @icmp_eq_add_non_splat2(
-; CHECK-NEXT: [[ADD:%.*]] = add <2 x i32> [[A:%.*]], <i32 5, i32 5>
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[ADD]], <i32 10, i32 11>
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], <i32 5, i32 6>
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
%add = add <2 x i32> %a, <i32 5, i32 5>
diff --git a/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll b/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll
index f5d5ef32c81e81..f9ba74bcbf7b99 100644
--- a/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll
@@ -136,8 +136,7 @@ define i1 @foo2(i32 %x, i32 %y) {
define <2 x i1> @foo3(<2 x i8> %x) {
; CHECK-LABEL: @foo3(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[XOR:%.*]] = xor <2 x i8> [[X:%.*]], <i8 -2, i8 -1>
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i8> [[XOR]], <i8 9, i8 79>
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i8> [[X:%.*]], <i8 -9, i8 -80>
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
entry:
diff --git a/llvm/test/Transforms/InstCombine/icmp-sub.ll b/llvm/test/Transforms/InstCombine/icmp-sub.ll
index 5645dededf2e4b..422e8116f1b38c 100644
--- a/llvm/test/Transforms/InstCombine/icmp-sub.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-sub.ll
@@ -164,8 +164,7 @@ define <2 x i1> @icmp_eq_sub_non_splat(<2 x i32> %a) {
define <2 x i1> @icmp_eq_sub_undef2(<2 x i32> %a) {
; CHECK-LABEL: @icmp_eq_sub_undef2(
-; CHECK-NEXT: [[SUB:%.*]] = sub <2 x i32> <i32 15, i32 15>, [[A:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[SUB]], <i32 10, i32 undef>
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], <i32 5, i32 undef>
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
%sub = sub <2 x i32> <i32 15, i32 15>, %a
@@ -175,8 +174,7 @@ define <2 x i1> @icmp_eq_sub_undef2(<2 x i32> %a) {
define <2 x i1> @icmp_eq_sub_non_splat2(<2 x i32> %a) {
; CHECK-LABEL: @icmp_eq_sub_non_splat2(
-; CHECK-NEXT: [[SUB:%.*]] = sub <2 x i32> <i32 15, i32 15>, [[A:%.*]]
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[SUB]], <i32 10, i32 11>
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], <i32 5, i32 4>
; CHECK-NEXT: ret <2 x i1> [[CMP]]
;
%sub = sub <2 x i32> <i32 15, i32 15>, %a
diff --git a/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll b/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll
index cd05022b0d35da..0453029fb5e977 100644
--- a/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll
+++ b/llvm/test/Transforms/InstCombine/prevent-cmp-merge.ll
@@ -7,9 +7,9 @@
define zeroext i1 @test1(i32 %lhs, i32 %rhs) {
; CHECK-LABEL: @test1(
-; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[LHS:%.*]], 5
-; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[XOR]], 10
-; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[XOR]], [[RHS:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[LHS:%.*]], 15
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[LHS]], [[RHS:%.*]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[TMP1]], 5
; CHECK-NEXT: [[SEL:%.*]] = or i1 [[CMP1]], [[CMP2]]
; CHECK-NEXT: ret i1 [[SEL]]
;
@@ -23,9 +23,9 @@ define zeroext i1 @test1(i32 %lhs, i32 %rhs) {
define zeroext i1 @test1_logical(i32 %lhs, i32 %rhs) {
; CHECK-LABEL: @test1_logical(
-; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[LHS:%.*]], 5
-; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[XOR]], 10
-; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[XOR]], [[RHS:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[LHS:%.*]], 15
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[LHS]], [[RHS:%.*]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[TMP1]], 5
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]]
; CHECK-NEXT: ret i1 [[SEL]]
;
diff --git a/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll b/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
index d5cd9061763945..94a896b39f2a25 100644
--- a/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
@@ -4,8 +4,7 @@
declare void @use.i8(i8)
define <2 x i1> @simplify_cmp_or_disjoint(<2 x i8> %x) {
; CHECK-LABEL: @simplify_cmp_or_disjoint(
-; CHECK-NEXT: [[V:%.*]] = or disjoint <2 x i8> [[X:%.*]], <i8 12, i8 13>
-; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i8> [[V]], <i8 0, i8 45>
+; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i8> [[X:%.*]], <i8 -12, i8 32>
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%v = or disjoint <2 x i8> %x, <i8 12, i8 13>
@@ -49,8 +48,7 @@ define i1 @simplify_cmp_add_fail_multiuse(i8 %x) {
define <2 x i1> @simplify_cmp_sub(<2 x i8> %x) {
; CHECK-LABEL: @simplify_cmp_sub(
-; CHECK-NEXT: [[V:%.*]] = sub <2 x i8> <i8 12, i8 13>, [[X:%.*]]
-; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i8> [[V]], <i8 0, i8 55>
+; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i8> [[X:%.*]], <i8 12, i8 -42>
; CHECK-NEXT: ret <2 x i1> [[R]]
;
%v = sub <2 x i8> <i8 12, i8 13>, %x
>From f4a845a1a67851566a3e760bdbdaadf535875bd9 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Fri, 22 Mar 2024 16:11:36 -0500
Subject: [PATCH 6/6] [InstCombine] Use `simplifyOpWithConstantEqConsts` in
`visitSwitchInst`
Replaces bespoke logic with logic shared by `visitICmp`. The new logic
is more complete.
---
.../InstCombine/InstructionCombining.cpp | 86 ++----------------
.../2009-02-20-InstCombine-SROA.ll | 16 ++--
.../Transforms/InstCombine/narrow-switch.ll | 16 ++--
.../Transforms/InstCombine/simplify-cmp-eq.ll | 87 +++++++++----------
.../InstCombine/switch-constant-expr.ll | 8 +-
5 files changed, 71 insertions(+), 142 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 2ce8e0d789c457..f4a93434761c63 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3802,85 +3802,17 @@ InstCombiner::simplifyOpWithConstantEqConsts(Value *Op, BuilderTy &Builder,
Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) {
Value *Cond = SI.getCondition();
- Value *Op0;
- ConstantInt *AddRHS;
- if (match(Cond, m_Add(m_Value(Op0), m_ConstantInt(AddRHS)))) {
- // Change 'switch (X+4) case 1:' into 'switch (X) case -3'.
- for (auto Case : SI.cases()) {
- Constant *NewCase = ConstantExpr::getSub(Case.getCaseValue(), AddRHS);
- assert(isa<ConstantInt>(NewCase) &&
- "Result of expression should be constant");
- Case.setValue(cast<ConstantInt>(NewCase));
- }
- return replaceOperand(SI, 0, Op0);
- }
- ConstantInt *SubLHS;
- if (match(Cond, m_Sub(m_ConstantInt(SubLHS), m_Value(Op0)))) {
- // Change 'switch (1-X) case 1:' into 'switch (X) case 0'.
- for (auto Case : SI.cases()) {
- Constant *NewCase = ConstantExpr::getSub(SubLHS, Case.getCaseValue());
- assert(isa<ConstantInt>(NewCase) &&
- "Result of expression should be constant");
- Case.setValue(cast<ConstantInt>(NewCase));
- }
- return replaceOperand(SI, 0, Op0);
- }
-
- uint64_t ShiftAmt;
- if (match(Cond, m_Shl(m_Value(Op0), m_ConstantInt(ShiftAmt))) &&
- ShiftAmt < Op0->getType()->getScalarSizeInBits() &&
- all_of(SI.cases(), [&](const auto &Case) {
- return Case.getCaseValue()->getValue().countr_zero() >= ShiftAmt;
- })) {
- // Change 'switch (X << 2) case 4:' into 'switch (X) case 1:'.
- OverflowingBinaryOperator *Shl = cast<OverflowingBinaryOperator>(Cond);
- if (Shl->hasNoUnsignedWrap() || Shl->hasNoSignedWrap() ||
- Shl->hasOneUse()) {
- Value *NewCond = Op0;
- if (!Shl->hasNoUnsignedWrap() && !Shl->hasNoSignedWrap()) {
- // If the shift may wrap, we need to mask off the shifted bits.
- unsigned BitWidth = Op0->getType()->getScalarSizeInBits();
- NewCond = Builder.CreateAnd(
- Op0, APInt::getLowBitsSet(BitWidth, BitWidth - ShiftAmt));
- }
- for (auto Case : SI.cases()) {
- const APInt &CaseVal = Case.getCaseValue()->getValue();
- APInt ShiftedCase = Shl->hasNoSignedWrap() ? CaseVal.ashr(ShiftAmt)
- : CaseVal.lshr(ShiftAmt);
- Case.setValue(ConstantInt::get(SI.getContext(), ShiftedCase));
- }
- return replaceOperand(SI, 0, NewCond);
- }
- }
+ SmallVector<Constant *> CaseVals;
+ for (const auto &Case : SI.cases())
+ CaseVals.push_back(Case.getCaseValue());
- // 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);
- }
- }
-
- // Fold 'switch(rol(x, C1)) case C2:' to 'switch(x) case rol(C2, -C1):'
- if (match(Cond,
- m_FShl(m_Value(Op0), m_Deferred(Op0), m_ConstantInt(ShiftAmt)))) {
- for (auto &Case : SI.cases()) {
- const APInt NewCase = Case.getCaseValue()->getValue().rotr(ShiftAmt);
- Case.setValue(ConstantInt::get(SI.getContext(), NewCase));
- }
- return replaceOperand(SI, 0, Op0);
+ if (auto *R = simplifyOpWithConstantEqConsts(Cond, Builder, CaseVals,
+ /*ReqOneUseAdd=*/false)) {
+ unsigned i = 0;
+ for (auto &Case : SI.cases())
+ Case.setValue(cast<ConstantInt>(CaseVals[i++]));
+ return replaceOperand(SI, 0, R);
}
KnownBits Known = computeKnownBits(Cond, 0, &SI);
diff --git a/llvm/test/Transforms/InstCombine/2009-02-20-InstCombine-SROA.ll b/llvm/test/Transforms/InstCombine/2009-02-20-InstCombine-SROA.ll
index b532c815567389..978b4e29ed628a 100644
--- a/llvm/test/Transforms/InstCombine/2009-02-20-InstCombine-SROA.ll
+++ b/llvm/test/Transforms/InstCombine/2009-02-20-InstCombine-SROA.ll
@@ -92,11 +92,11 @@ define ptr @_Z3fooRSt6vectorIiSaIiEE(ptr %X) {
; IC-NEXT: [[TMP37:%.*]] = load ptr, ptr [[__FIRST_ADDR_I_I]], align 4
; IC-NEXT: [[TMP38:%.*]] = ptrtoint ptr [[TMP37]] to i32
; IC-NEXT: [[TMP39:%.*]] = sub i32 [[TMP36]], [[TMP38]]
-; IC-NEXT: [[TMP40:%.*]] = ashr i32 [[TMP39]], 2
+; IC-NEXT: [[TMP40:%.*]] = and i32 [[TMP39]], -4
; IC-NEXT: switch i32 [[TMP40]], label [[BB26_I_I:%.*]] [
-; IC-NEXT: i32 1, label [[BB22_I_I:%.*]]
-; IC-NEXT: i32 2, label [[BB18_I_I:%.*]]
-; IC-NEXT: i32 3, label [[BB14_I_I:%.*]]
+; IC-NEXT: i32 4, label [[BB22_I_I:%.*]]
+; IC-NEXT: i32 8, label [[BB18_I_I:%.*]]
+; IC-NEXT: i32 12, label [[BB14_I_I:%.*]]
; IC-NEXT: ]
; IC: bb14.i.i:
; IC-NEXT: [[TMP41:%.*]] = load ptr, ptr [[__FIRST_ADDR_I_I]], align 4
@@ -199,11 +199,11 @@ define ptr @_Z3fooRSt6vectorIiSaIiEE(ptr %X) {
; IC_SROA-NEXT: [[TMP21:%.*]] = ptrtoint ptr [[TMP1]] to i32
; IC_SROA-NEXT: [[TMP22:%.*]] = ptrtoint ptr [[__FIRST_ADDR_I_I_SROA_0_0]] to i32
; IC_SROA-NEXT: [[TMP23:%.*]] = sub i32 [[TMP21]], [[TMP22]]
-; IC_SROA-NEXT: [[TMP24:%.*]] = ashr i32 [[TMP23]], 2
+; IC_SROA-NEXT: [[TMP24:%.*]] = and i32 [[TMP23]], -4
; IC_SROA-NEXT: switch i32 [[TMP24]], label [[BB26_I_I:%.*]] [
-; IC_SROA-NEXT: i32 1, label [[BB22_I_I:%.*]]
-; IC_SROA-NEXT: i32 2, label [[BB18_I_I:%.*]]
-; IC_SROA-NEXT: i32 3, label [[BB14_I_I:%.*]]
+; IC_SROA-NEXT: i32 4, label [[BB22_I_I:%.*]]
+; IC_SROA-NEXT: i32 8, label [[BB18_I_I:%.*]]
+; IC_SROA-NEXT: i32 12, label [[BB14_I_I:%.*]]
; IC_SROA-NEXT: ]
; IC_SROA: bb14.i.i:
; IC_SROA-NEXT: [[TMP25:%.*]] = load i32, ptr [[__FIRST_ADDR_I_I_SROA_0_0]], align 4
diff --git a/llvm/test/Transforms/InstCombine/narrow-switch.ll b/llvm/test/Transforms/InstCombine/narrow-switch.ll
index 05a30b910e5ee0..8fd2bfb8e5cf9e 100644
--- a/llvm/test/Transforms/InstCombine/narrow-switch.ll
+++ b/llvm/test/Transforms/InstCombine/narrow-switch.ll
@@ -104,13 +104,13 @@ return:
define void @trunc64to58(i64 %a) {
; ALL-LABEL: @trunc64to58(
-; CHECK32: switch i58
-; CHECK32-NEXT: i58 0, label %sw.bb1
-; CHECK32-NEXT: i58 18717182647723699, label %sw.bb2
+; CHECK32: switch i4
+; CHECK32-NEXT: i4 0, label %sw.bb1
+; CHECK32-NEXT: i4 1, label %sw.bb2
; CHECK32-NEXT: ]
; CHECK64: switch i64
; CHECK64-NEXT: i64 0, label %sw.bb1
-; CHECK64-NEXT: i64 18717182647723699, label %sw.bb2
+; CHECK64-NEXT: i64 1, label %sw.bb2
; CHECK64-NEXT: ]
;
entry:
@@ -170,10 +170,10 @@ case124:
; condition is evaluated on the original type
define i32 @trunc32to16(i32 %a0) #0 {
; ALL-LABEL: @trunc32to16(
-; ALL: switch i16
-; ALL-NEXT: i16 63, label %sw.bb
-; ALL-NEXT: i16 1, label %sw.bb1
-; ALL-NEXT: i16 100, label %sw.bb2
+; ALL: switch i32
+; ALL-NEXT: i32 1033306112, label %sw.bb
+; ALL-NEXT: i32 1034485760, label %sw.bb1
+; ALL-NEXT: i32 1036779520, label %sw.bb2
; ALL-NEXT: ]
;
entry:
diff --git a/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll b/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
index 94a896b39f2a25..273589957b9c74 100644
--- a/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-cmp-eq.ll
@@ -446,11 +446,10 @@ define i1 @simplify_cmp_fshl_fail_not_rotate(i8 %x, i8 %y) {
define i8 @simplify_switch_or_disjoint(i8 %x) {
; CHECK-LABEL: @simplify_switch_or_disjoint(
-; CHECK-NEXT: [[V:%.*]] = or disjoint i8 [[X:%.*]], 12
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 12, label [[L12:%.*]]
-; CHECK-NEXT: i8 44, label [[L44:%.*]]
-; CHECK-NEXT: i8 45, label [[L45:%.*]]
+; CHECK-NEXT: switch i8 [[V:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 0, label [[L12:%.*]]
+; CHECK-NEXT: i8 32, label [[L44:%.*]]
+; CHECK-NEXT: i8 33, label [[L45:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
; CHECK-NEXT: ret i8 12
@@ -554,10 +553,10 @@ define i8 @simplify_switch_xor(i8 %x) {
; CHECK-LABEL: @simplify_switch_xor(
; CHECK-NEXT: [[V:%.*]] = xor i8 [[X:%.*]], 12
; CHECK-NEXT: call void @use.i8(i8 [[V]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 12, label [[L12:%.*]]
-; CHECK-NEXT: i8 44, label [[L44:%.*]]
-; CHECK-NEXT: i8 45, label [[L45:%.*]]
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 0, label [[L12:%.*]]
+; CHECK-NEXT: i8 32, label [[L44:%.*]]
+; CHECK-NEXT: i8 33, label [[L45:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
; CHECK-NEXT: ret i8 12
@@ -591,10 +590,10 @@ define i8 @simplify_switch_mul_udiv(i8 %x) {
; CHECK-LABEL: @simplify_switch_mul_udiv(
; CHECK-NEXT: [[V:%.*]] = mul nuw i8 [[X:%.*]], 12
; CHECK-NEXT: call void @use.i8(i8 [[V]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 12, label [[L12:%.*]]
-; CHECK-NEXT: i8 48, label [[L48:%.*]]
-; CHECK-NEXT: i8 60, label [[L60:%.*]]
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 1, label [[L12:%.*]]
+; CHECK-NEXT: i8 4, label [[L48:%.*]]
+; CHECK-NEXT: i8 5, label [[L60:%.*]]
; CHECK-NEXT: i8 0, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
@@ -675,10 +674,10 @@ define i8 @simplify_switch_mul_sdiv(i8 %x) {
; CHECK-LABEL: @simplify_switch_mul_sdiv(
; CHECK-NEXT: [[V:%.*]] = mul nuw i8 [[X:%.*]], 12
; CHECK-NEXT: call void @use.i8(i8 [[V]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 -12, label [[L12:%.*]]
-; CHECK-NEXT: i8 48, label [[L48:%.*]]
-; CHECK-NEXT: i8 -60, label [[L60:%.*]]
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -1, label [[L12:%.*]]
+; CHECK-NEXT: i8 4, label [[L48:%.*]]
+; CHECK-NEXT: i8 -5, label [[L60:%.*]]
; CHECK-NEXT: i8 0, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
@@ -759,10 +758,10 @@ define i8 @simplify_switch_udiv(i8 %x) {
; CHECK-LABEL: @simplify_switch_udiv(
; CHECK-NEXT: [[V:%.*]] = udiv exact i8 [[X:%.*]], 12
; CHECK-NEXT: call void @use.i8(i8 [[V]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 12, label [[L12:%.*]]
-; CHECK-NEXT: i8 3, label [[L48:%.*]]
-; CHECK-NEXT: i8 9, label [[L60:%.*]]
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -112, label [[L12:%.*]]
+; CHECK-NEXT: i8 36, label [[L48:%.*]]
+; CHECK-NEXT: i8 108, label [[L60:%.*]]
; CHECK-NEXT: i8 0, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
@@ -843,10 +842,10 @@ define i8 @simplify_switch_sdiv(i8 %x) {
; CHECK-LABEL: @simplify_switch_sdiv(
; CHECK-NEXT: [[V:%.*]] = sdiv exact i8 [[X:%.*]], 12
; CHECK-NEXT: call void @use.i8(i8 [[V]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 4, label [[L12:%.*]]
-; CHECK-NEXT: i8 -3, label [[L48:%.*]]
-; CHECK-NEXT: i8 7, label [[L60:%.*]]
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 48, label [[L12:%.*]]
+; CHECK-NEXT: i8 -36, label [[L48:%.*]]
+; CHECK-NEXT: i8 84, label [[L60:%.*]]
; CHECK-NEXT: i8 0, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
@@ -1290,10 +1289,10 @@ define i8 @simplify_switch_lshr(i8 %x) {
; CHECK-LABEL: @simplify_switch_lshr(
; CHECK-NEXT: [[V:%.*]] = lshr exact i8 [[X:%.*]], 2
; CHECK-NEXT: call void @use.i8(i8 [[V]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 12, label [[L12:%.*]]
-; CHECK-NEXT: i8 16, label [[L48:%.*]]
-; CHECK-NEXT: i8 30, label [[L60:%.*]]
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 48, label [[L12:%.*]]
+; CHECK-NEXT: i8 64, label [[L48:%.*]]
+; CHECK-NEXT: i8 120, label [[L60:%.*]]
; CHECK-NEXT: i8 0, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
@@ -1372,10 +1371,10 @@ define i8 @simplify_switch_ashr(i8 %x) {
; CHECK-LABEL: @simplify_switch_ashr(
; CHECK-NEXT: [[V:%.*]] = ashr exact i8 [[X:%.*]], 2
; CHECK-NEXT: call void @use.i8(i8 [[V]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 -12, label [[L12:%.*]]
-; CHECK-NEXT: i8 16, label [[L48:%.*]]
-; CHECK-NEXT: i8 3, label [[L60:%.*]]
+; CHECK-NEXT: switch i8 [[X]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 -48, label [[L12:%.*]]
+; CHECK-NEXT: i8 64, label [[L48:%.*]]
+; CHECK-NEXT: i8 12, label [[L60:%.*]]
; CHECK-NEXT: i8 0, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
@@ -1452,12 +1451,11 @@ default:
define i8 @simplify_switch_bitreverse(i8 %x) {
; CHECK-LABEL: @simplify_switch_bitreverse(
-; CHECK-NEXT: [[V:%.*]] = call i8 @llvm.bitreverse.i8(i8 [[X:%.*]])
-; CHECK-NEXT: switch i8 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i8 -12, label [[L12:%.*]]
-; CHECK-NEXT: i8 16, label [[L48:%.*]]
-; CHECK-NEXT: i8 30, label [[L60:%.*]]
-; CHECK-NEXT: i8 -128, label [[L0:%.*]]
+; CHECK-NEXT: switch i8 [[V:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i8 47, label [[L12:%.*]]
+; CHECK-NEXT: i8 8, label [[L48:%.*]]
+; CHECK-NEXT: i8 120, label [[L60:%.*]]
+; CHECK-NEXT: i8 1, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
; CHECK-NEXT: ret i8 12
@@ -1492,12 +1490,11 @@ default:
define i16 @simplify_switch_bswap(i16 %x) {
; CHECK-LABEL: @simplify_switch_bswap(
-; CHECK-NEXT: [[V:%.*]] = call i16 @llvm.bswap.i16(i16 [[X:%.*]])
-; CHECK-NEXT: switch i16 [[V]], label [[DEFAULT:%.*]] [
-; CHECK-NEXT: i16 -12, label [[L12:%.*]]
-; CHECK-NEXT: i16 16, label [[L48:%.*]]
-; CHECK-NEXT: i16 30, label [[L60:%.*]]
-; CHECK-NEXT: i16 128, label [[L0:%.*]]
+; CHECK-NEXT: switch i16 [[V:%.*]], label [[DEFAULT:%.*]] [
+; CHECK-NEXT: i16 -2817, label [[L12:%.*]]
+; CHECK-NEXT: i16 4096, label [[L48:%.*]]
+; CHECK-NEXT: i16 7680, label [[L60:%.*]]
+; CHECK-NEXT: i16 -32768, label [[L0:%.*]]
; CHECK-NEXT: ]
; CHECK: l12:
; CHECK-NEXT: ret i16 12
diff --git a/llvm/test/Transforms/InstCombine/switch-constant-expr.ll b/llvm/test/Transforms/InstCombine/switch-constant-expr.ll
index e3a5e2ac8c117c..6b05e468fba99b 100644
--- a/llvm/test/Transforms/InstCombine/switch-constant-expr.ll
+++ b/llvm/test/Transforms/InstCombine/switch-constant-expr.ll
@@ -6,7 +6,7 @@
; PR30486
define i32 @single_case() {
; CHECK-LABEL: @single_case(
-; CHECK-NEXT: switch i32 add (i32 ptrtoint (ptr @g to i32), i32 -1), label [[X:%.*]] [
+; CHECK-NEXT: switch i32 ptrtoint (ptr @g to i32), label [[X:%.*]] [
; CHECK-NEXT: ]
; CHECK: x:
; CHECK-NEXT: ret i32 0
@@ -18,9 +18,9 @@ x:
define i32 @multiple_cases() {
; CHECK-LABEL: @multiple_cases(
-; CHECK-NEXT: switch i32 add (i32 ptrtoint (ptr @g to i32), i32 -1), label [[X:%.*]] [
-; CHECK-NEXT: i32 1, label [[ONE:%.*]]
-; CHECK-NEXT: i32 2, label [[TWO:%.*]]
+; CHECK-NEXT: switch i32 ptrtoint (ptr @g to i32), label [[X:%.*]] [
+; CHECK-NEXT: i32 2, label [[TWO:%.*]]
+; CHECK-NEXT: i32 3, label [[TWO1:%.*]]
; CHECK-NEXT: ]
; CHECK: x:
; CHECK-NEXT: ret i32 0
More information about the llvm-commits
mailing list