Skip to content

[llvm] [InstCombine] fold "icmp eq (X + (V - 1)) & -V, X" to "icmp eq (and X, V - 1), 0" #152851

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

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
32 changes: 32 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,35 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
return nullptr;
}

/// Fold icmp eq (num + mask) & ~mask, num
/// to
/// icmp eq (and num, mask), 0
/// Where mask is a low bit mask.
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
Value *Num;
CmpPredicate Pred;
const APInt *Mask, *Neg;

if (!match(&Cmp,
m_c_ICmp(Pred, m_Value(Num),
m_OneUse(m_c_And(m_OneUse(m_c_Add(m_Deferred(Num),
m_LowBitMask(Mask))),
m_APInt(Neg))))))
return nullptr;

if (*Neg != ~*Mask)
return nullptr;

if (!ICmpInst::isEquality(Pred))
return nullptr;

// Create new icmp eq (num & mask), 0
auto *NewAnd = Builder.CreateAnd(Num, *Mask);
auto *Zero = Constant::getNullValue(Num->getType());

return new ICmpInst(Pred, NewAnd, Zero);
}

/// Fold icmp Pred X, C.
/// TODO: This code structure does not make sense. The saturating add fold
/// should be moved to some other helper and extended as noted below (it is also
Expand Down Expand Up @@ -7644,6 +7673,9 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
if (Instruction *Res = foldICmpUsingKnownBits(I))
return Res;

if (Instruction *Res = foldIsMultipleOfAPowerOfTwo(I))
return Res;

// Test if the ICmpInst instruction is used exclusively by a select as
// part of a minimum or maximum operation. If so, refrain from doing
// any other folding. This helps out other analyses which understand
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
Instruction *foldICmpUsingKnownBits(ICmpInst &Cmp);
Instruction *foldICmpWithDominatingICmp(ICmpInst &Cmp);
Instruction *foldICmpWithConstant(ICmpInst &Cmp);
Instruction *foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp);
Instruction *foldICmpUsingBoolRange(ICmpInst &I);
Instruction *foldICmpInstWithConstant(ICmpInst &Cmp);
Instruction *foldICmpInstWithConstantNotInt(ICmpInst &Cmp);
Expand Down
146 changes: 146 additions & 0 deletions llvm/test/Transforms/InstCombine/icmp-add.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3300,3 +3300,149 @@ entry:
%cmp = icmp ult i32 %add, 253
ret i1 %cmp
}

; PR 152851

define i1 @val_is_aligend_const_pow2(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
%num.masked = and i32 %num.biased, -4096
%_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_const_pow2_add_commute(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_add_commute(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 4095, %num
%num.masked = and i32 %num.biased, -4096
%_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_const_pow2_and_commute(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_and_commute(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
%num.masked = and i32 -4096, %num.biased
%_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_const_pow2_icm_commute(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_icm_commute(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
%num.masked = and i32 %num.biased, -4096
%_0 = icmp eq i32 %num, %num.masked
ret i1 %_0
}

; Should not work for non-power-of-two cases
define i1 @val_is_aligend_const_non_pow2(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_non_pow2(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 6
; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -7
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 6
%num.masked = and i32 %num.biased, -7
%_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_const_pow2_multiuse(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -4096
; CHECK-NEXT: call void @use(i32 [[NUM_MASKED]])
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
%num.masked = and i32 %num.biased, -4096
call void @use(i32 %num.masked)
%_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}

; Applies since number of instructions do not change
define i1 @val_is_aligend_const_pow2_multiuse1(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse1(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
; CHECK-NEXT: call void @use(i32 [[NUM_BIASED]])
; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -4096
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
call void @use(i32 %num.biased)
%num.masked = and i32 %num.biased, -4096
%_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_const_pow2_ne(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_ne(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
; CHECK-NEXT: [[_0:%.*]] = icmp ne i32 [[TMP1]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
%num.masked = and i32 %num.biased, -4096
%_0 = icmp ne i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_const_mismatch(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_mismatch(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -4095
; CHECK-NEXT: [[_0:%.*]] = icmp ne i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
%num.masked = and i32 %num.biased, -4095
%_0 = icmp ne i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_const_mismatch1(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_mismatch1(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], -4096
; CHECK-NEXT: [[NUM_MASKED:%.*]] = add i32 [[TMP1]], 4096
; CHECK-NEXT: [[_0:%.*]] = icmp ne i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4096
%num.masked = and i32 %num.biased, -4096
%_0 = icmp ne i32 %num.masked, %num
ret i1 %_0
}

define i1 @val_is_aligend_pred_mismatch(i32 %num) {
; CHECK-LABEL: @val_is_aligend_pred_mismatch(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], -4096
; CHECK-NEXT: [[NUM_MASKED:%.*]] = add i32 [[TMP1]], 4096
; CHECK-NEXT: [[_0:%.*]] = icmp sge i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4096
%num.masked = and i32 %num.biased, -4096
%_0 = icmp sge i32 %num.masked, %num
ret i1 %_0
}