Skip to content
Merged
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 + (val - 1)) & -val, num
// to
// icmp eq 0, (and num, val - 1)
// For val being power of two
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 & (val - 1)), 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
}