[llvm] [InstCombine] Missed optimization: Fold (sext(a) & c1) == c2 to (a & c3) == trunc(c2) (PR #112646)
Lee Wei via llvm-commits
llvm-commits at lists.llvm.org
Sun Nov 3 20:09:21 PST 2024
https://github.com/leewei05 updated https://github.com/llvm/llvm-project/pull/112646
>From 1eaac255e2a7f54e7a8cf00cec8b024440cad9e6 Mon Sep 17 00:00:00 2001
From: Lee <lee10202013 at gmail.com>
Date: Sat, 12 Oct 2024 15:13:47 -0600
Subject: [PATCH 1/2] Add pre-commit tests
---
llvm/test/Transforms/InstCombine/sext-and.ll | 236 +++++++++++++++++++
1 file changed, 236 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/sext-and.ll
diff --git a/llvm/test/Transforms/InstCombine/sext-and.ll b/llvm/test/Transforms/InstCombine/sext-and.ll
new file mode 100644
index 00000000000000..377805391b92c6
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sext-and.ll
@@ -0,0 +1,236 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use(i8)
+
+define i1 @fold_sext_to_and(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -2147483647
+ %3 = icmp eq i32 %2, 1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and1(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and1(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -2147483647
+ %3 = icmp ne i32 %2, 1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and2(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and2(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 2
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, 1073741826
+ %3 = icmp eq i32 %2, 2
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and3(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and3(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 2
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, 1073741826
+ %3 = icmp ne i32 %2, 2
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_multi_use(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: call void @use(i32 [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ call void @use(i32 %1)
+ %2 = and i32 %1, -2147483647
+ %3 = icmp eq i32 %2, 1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_multi_use1(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use1(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: call void @use(i32 [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ call void @use(i32 %1)
+ %2 = and i32 %1, -2147483647
+ %3 = icmp ne i32 %2, 1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_multi_use2(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use2(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: call void @use(i32 [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 2
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ call void @use(i32 %1)
+ %2 = and i32 %1, 1073741826
+ %3 = icmp eq i32 %2, 2
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_multi_use3(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use3(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT: call void @use(i32 [[TMP1]])
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 2
+; CHECK-NEXT: ret i1 [[TMP3]]
+;
+ %1 = sext i8 %x to i32
+ call void @use(i32 %1)
+ %2 = and i32 %1, 1073741826
+ %3 = icmp ne i32 %2, 2
+ ret i1 %3
+}
+
+; Negative tests
+
+define i1 @fold_sext_to_and_wrong(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -2147483647
+ %3 = icmp eq i32 %2, -1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong2(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong2(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -2147483647
+ %3 = icmp eq i32 %2, 128
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong3(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong3(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, 128
+ %3 = icmp eq i32 %2, -2147483648
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong4(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong4(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, 128
+ %3 = icmp eq i32 %2, 1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong5(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong5(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -256
+ %3 = icmp eq i32 %2, 1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong6(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong6(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -2147483647
+ %3 = icmp ne i32 %2, -1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong7(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong7(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -2147483647
+ %3 = icmp ne i32 %2, 128
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong8(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong8(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, 128
+ %3 = icmp ne i32 %2, -2147483648
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong9(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong9(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, 128
+ %3 = icmp ne i32 %2, 1
+ ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong10(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong10(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: ret i1 true
+;
+ %1 = sext i8 %x to i32
+ %2 = and i32 %1, -256
+ %3 = icmp ne i32 %2, 1
+ ret i1 %3
+}
>From 1ddd277ddf9daf62c737f571ae530c692d12a4e1 Mon Sep 17 00:00:00 2001
From: Lee <lee10202013 at gmail.com>
Date: Thu, 24 Oct 2024 21:06:30 -0600
Subject: [PATCH 2/2] Apply transformation
---
.../InstCombine/InstCombineCompares.cpp | 26 ++++++++++++++
llvm/test/Transforms/InstCombine/load-cmp.ll | 4 +--
llvm/test/Transforms/InstCombine/sext-and.ll | 36 +++++++++----------
3 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 6bb39cabb0988b..680e39f03902e1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7716,6 +7716,32 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
if (Instruction *Res = foldReductionIdiom(I, Builder, DL))
return Res;
+ {
+ Value *A;
+ const APInt *C1, *C2;
+ ICmpInst::Predicate Pred = I.getPredicate();
+ if (ICmpInst::isEquality(Pred)) {
+ // sext(a) & c1 == c2 --> a & c3 == trunc(c2)
+ // sext(a) & c1 != c2 --> a & c3 != trunc(c2)
+ if (match(Op0, m_And(m_SExt(m_Value(A)), m_APInt(C1))) &&
+ match(Op1, m_APInt(C2))) {
+ Type *InputTy = A->getType();
+ unsigned InputBitWidth = InputTy->getScalarSizeInBits();
+ // c2 must be non-negative at the bitwidth of a.
+ if (C2->getActiveBits() < InputBitWidth) {
+ APInt TruncC1 = C1->trunc(InputBitWidth);
+ // Check if there are 1s in C1 high bits of size InputBitWidth.
+ if (C1->uge(APInt::getOneBitSet(C1->getBitWidth(), InputBitWidth)))
+ TruncC1.setBit(InputBitWidth - 1);
+ Value *AndInst = Builder.CreateAnd(A, TruncC1);
+ return new ICmpInst(
+ Pred, AndInst,
+ ConstantInt::get(InputTy, C2->trunc(InputBitWidth)));
+ }
+ }
+ }
+ }
+
return Changed ? &I : nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/load-cmp.ll b/llvm/test/Transforms/InstCombine/load-cmp.ll
index 8e39fe33cded88..12be81b8f815d0 100644
--- a/llvm/test/Transforms/InstCombine/load-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/load-cmp.ll
@@ -312,9 +312,7 @@ define i1 @test10_struct_arr_i64(i64 %x) {
define i1 @test10_struct_arr_noinbounds_i16(i16 %x) {
; CHECK-LABEL: @test10_struct_arr_noinbounds_i16(
-; CHECK-NEXT: [[TMP1:%.*]] = sext i16 [[X:%.*]] to i32
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 268435455
-; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT: [[R:%.*]] = icmp ne i16 [[X:%.*]], 1
; CHECK-NEXT: ret i1 [[R]]
;
%p = getelementptr [4 x %Foo], ptr @GStructArr, i32 0, i16 %x, i32 2
diff --git a/llvm/test/Transforms/InstCombine/sext-and.ll b/llvm/test/Transforms/InstCombine/sext-and.ll
index 377805391b92c6..e08bbbd83d8845 100644
--- a/llvm/test/Transforms/InstCombine/sext-and.ll
+++ b/llvm/test/Transforms/InstCombine/sext-and.ll
@@ -6,9 +6,8 @@ declare void @use(i8)
define i1 @fold_sext_to_and(i8 %x) {
; CHECK-LABEL: define i1 @fold_sext_to_and(
; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP1]], 1
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
@@ -20,9 +19,8 @@ define i1 @fold_sext_to_and(i8 %x) {
define i1 @fold_sext_to_and1(i8 %x) {
; CHECK-LABEL: define i1 @fold_sext_to_and1(
; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP1]], 1
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
@@ -34,9 +32,8 @@ define i1 @fold_sext_to_and1(i8 %x) {
define i1 @fold_sext_to_and2(i8 %x) {
; CHECK-LABEL: define i1 @fold_sext_to_and2(
; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
-; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 2
+; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -126
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP1]], 2
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
@@ -48,9 +45,8 @@ define i1 @fold_sext_to_and2(i8 %x) {
define i1 @fold_sext_to_and3(i8 %x) {
; CHECK-LABEL: define i1 @fold_sext_to_and3(
; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 2
+; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -126
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP1]], 2
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
@@ -64,8 +60,8 @@ define i1 @fold_sext_to_and_multi_use(i8 %x) {
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: call void @use(i32 [[TMP1]])
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP2]], 1
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
@@ -80,8 +76,8 @@ define i1 @fold_sext_to_and_multi_use1(i8 %x) {
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: call void @use(i32 [[TMP1]])
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP2]], 1
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
@@ -96,8 +92,8 @@ define i1 @fold_sext_to_and_multi_use2(i8 %x) {
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: call void @use(i32 [[TMP1]])
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
-; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 2
+; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -126
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP2]], 2
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
@@ -112,8 +108,8 @@ define i1 @fold_sext_to_and_multi_use3(i8 %x) {
; CHECK-SAME: i8 [[X:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
; CHECK-NEXT: call void @use(i32 [[TMP1]])
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1073741826
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 2
+; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -126
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP2]], 2
; CHECK-NEXT: ret i1 [[TMP3]]
;
%1 = sext i8 %x to i32
More information about the llvm-commits
mailing list