Skip to content
Merged
53 changes: 53 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,56 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
return nullptr;
}

// Fold icmp eq (num + (val - 1)) & -val, num
// to
// icmp eq 0, (and num, val - 1)
// For value being power of two
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
Value *Neg, *Num, *Mask, *Value;
CmpPredicate Pred;
const APInt *NegConst, *MaskConst;

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

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

// Check the constant case
if (match(Neg, m_APInt(NegConst)) && match(Mask, m_LowBitMask(MaskConst))) {
// Neg = -(Mask + 1)
if (*NegConst != -(*MaskConst + 1))
return nullptr;
} else {
// Match neg = sub 0, val
if (!match(Neg, m_Sub(m_Zero(), m_Value(Value))))
return nullptr;

// mask = add %val, -1. No commutative here, since it's canonical
// representation for sub %val, -1
if (!match(Mask, m_Add(m_Value(Value), m_AllOnes()))) {
if (match(Num, m_Add(m_Value(Value), m_AllOnes()))) {
std::swap(Mask, Num);
} else {
return nullptr;
}
}

// Value should be a known power-of-two.
if (!isKnownToBeAPowerOfTwo(Value, false, &Cmp))
return nullptr;
}

// Create new icmp eq (num & (val - 1)), 0
auto NewAnd = Builder.CreateAnd(Num, Mask);
auto Zero = llvm::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 +7694,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
198 changes: 198 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,201 @@ entry:
%cmp = icmp ult i32 %add, 253
ret i1 %cmp
}

; PR 152851

define i1 @val_is_aligend_sub(i32 %num, i32 %val) {
; CHECK-LABEL: @val_is_aligend_sub(
; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[NUM:%.*]])
; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM1:%.*]], [[NEG]]
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
%pow = icmp eq i32 %1, 1
call void @llvm.assume(i1 %pow)

%mask = sub i32 %val, 1
%neg = sub nsw i32 0, %val

%num.biased = add i32 %num, %mask
%_2.sroa.0.0 = and i32 %num.biased, %neg
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}

define i1 @val_is_aligend_add(i32 %num, i32 %val) {
; CHECK-LABEL: @val_is_aligend_add(
; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[NUM:%.*]])
; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM1:%.*]], [[NEG]]
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
%pow = icmp eq i32 %1, 1
call void @llvm.assume(i1 %pow)

%mask = add i32 %val, -1
%neg = sub nsw i32 0, %val

%num.biased = add i32 %num, %mask
%_2.sroa.0.0 = and i32 %num.biased, %neg
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}

define i1 @val_is_aligend_add_commute_add(i32 %num, i32 %val) {
; CHECK-LABEL: @val_is_aligend_add_commute_add(
; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM:%.*]], [[MASK]]
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
%pow = icmp eq i32 %1, 1
call void @llvm.assume(i1 %pow)

%mask = add i32 %val, -1
%neg = sub nsw i32 0, %val

%num.biased = add i32 %mask, %num
%_2.sroa.0.0 = and i32 %num.biased, %neg
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}

define i1 @val_is_aligend_add_commute_and(i32 %num, i32 %val) {
; CHECK-LABEL: @val_is_aligend_add_commute_and(
; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM:%.*]], [[MASK]]
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
%pow = icmp eq i32 %1, 1
call void @llvm.assume(i1 %pow)

%mask = add i32 %val, -1
%neg = sub nsw i32 0, %val

%num.biased = add i32 %mask, %num
%_2.sroa.0.0 = and i32 %neg, %num.biased
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}

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
%_2.sroa.0.0 = and i32 %num.biased, -4096
%_0 = icmp eq i32 %_2.sroa.0.0, %num
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: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -7
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 6
%_2.sroa.0.0 = and i32 %num.biased, -7
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}

define i1 @val_is_aligend_non_pow(i32 %num, i32 %val) {
; CHECK-LABEL: @val_is_aligend_non_pow(
; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL:%.*]], -1
; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[VAL]]
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], [[MASK]]
; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], [[NEG]]
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%mask = add i32 %val, -1
%neg = sub nsw i32 0, %val

%num.biased = add i32 %num, %mask
%_2.sroa.0.0 = and i32 %num.biased, %neg
%_0 = icmp eq i32 %_2.sroa.0.0, %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: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -4096
; CHECK-NEXT: call void @use(i32 [[_2_SROA_0_0]])
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
%_2.sroa.0.0 = and i32 %num.biased, -4096
call void @use(i32 %_2.sroa.0.0)
%_0 = icmp eq i32 %_2.sroa.0.0, %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: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -4096
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
call void @use(i32 %num.biased)
%_2.sroa.0.0 = and i32 %num.biased, -4096
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}

define i1 @val_is_aligend_add_multiuse(i32 %num, i32 %val) {
; CHECK-LABEL: @val_is_aligend_add_multiuse(
; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[VAL]]
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], [[MASK]]
; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], [[NEG]]
; CHECK-NEXT: call void @use(i32 [[_2_SROA_0_0]])
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
%pow = icmp eq i32 %1, 1
call void @llvm.assume(i1 %pow)

%mask = add i32 %val, -1
%neg = sub nsw i32 0, %val

%num.biased = add i32 %num, %mask
%_2.sroa.0.0 = and i32 %num.biased, %neg

call void @use(i32 %_2.sroa.0.0)

%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}