Skip to content

[InstCombine] Missed optimization: Fold (sext(a) & c1) == c2 to (a & c3) == trunc(c2) #112646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 1 addition & 3 deletions llvm/test/Transforms/InstCombine/load-cmp.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
232 changes: 232 additions & 0 deletions llvm/test/Transforms/InstCombine/sext-and.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
; 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:%.*]] = and i8 [[X]], -127
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP1]], 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:%.*]] = and i8 [[X]], -127
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP1]], 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:%.*]] = and i8 [[X]], -126
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP1]], 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:%.*]] = and i8 [[X]], -126
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP1]], 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 i8 [[X]], -127
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[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 i8 [[X]], -127
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[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 i8 [[X]], -126
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[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 i8 [[X]], -126
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[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
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some negative tests, such as:

  • c2 = 0x80
  • c2 = 0x80000000
  • c1 = 0x80
  • c1 = 0xffffff00


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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use ne for some of the positive tests?

Copy link
Contributor Author

@leewei05 leewei05 Oct 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added positive tests for ne. Please take another look! Thanks!

Loading