From d8b5d15f61d601be1381cb9fd38df6a1885f0de2 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Sat, 9 Aug 2025 00:28:26 +0300 Subject: [PATCH 01/13] fold "icmp eq (num + (val - 1)) & -val, num" to "icmp eq 0, (and num, val - 1)" --- .../InstCombine/InstCombineCompares.cpp | 64 +++++++++++++ .../InstCombine/InstCombineInternal.h | 1 + llvm/test/Transforms/InstCombine/icmp-add.ll | 90 +++++++++++++++++++ 3 files changed, 155 insertions(+) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index cf94d28100488..722b03eb53f06 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1320,6 +1320,67 @@ 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::foldNextMultiply(ICmpInst &Cmp) { + Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1); + Value *Neg, *Add, *Num, *Mask, *Value; + CmpInst::Predicate Pred = Cmp.getPredicate(); + const APInt *NegConst, *MaskConst, *NumCost; + + if (Pred != ICmpInst::ICMP_EQ) + return nullptr; + + // Match num + neg + if (!match(Op0, m_And(m_Value(Add), m_Value(Neg)))) + return nullptr; + + // Match num & mask + if (!match(Add, m_Add(m_Value(Num), m_Value(Mask)))) + return nullptr; + + // Check the constant case + if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) { + // Mask + 1 should be a power-of-two + if (!(*MaskConst + 1).isPowerOf2()) + return nullptr; + + // 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 = %val - 1, which can be represented as sub %val, 1 or add %val, -1 + if (!match(Mask, m_Add(m_Value(Value), m_AllOnes())) && + !match(Mask, m_Sub(m_Value(Value), m_One()))) + return nullptr; + + // Value should be a known power-of-two. + if (!isKnownToBeAPowerOfTwo(Value, false, &Cmp)) + return nullptr; + } + + // Guard against weird special-case where Op1 gets optimized to constant. Leave it constant + // fonder. + if (match(Op1, m_APInt(NumCost))) + return nullptr; + + if (!match(Op1, m_Value(Num))) + return nullptr; + + // Create new icmp eq (num & (val - 1)), 0 + auto NewAnd = Builder.CreateAnd(Num, Mask); + auto Zero = llvm::Constant::getNullValue(Num->getType()); + auto ICmp = Builder.CreateICmp(CmpInst::ICMP_EQ, NewAnd, Zero); + + return replaceInstUsesWith(Cmp, ICmp); +} + /// 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 @@ -7644,6 +7705,9 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) { if (Instruction *Res = foldICmpUsingKnownBits(I)) return Res; + if (Instruction *Res = foldNextMultiply(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 diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index c67e27e5b3e7c..5f83cb1b9ae28 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -721,6 +721,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Instruction *foldICmpUsingKnownBits(ICmpInst &Cmp); Instruction *foldICmpWithDominatingICmp(ICmpInst &Cmp); Instruction *foldICmpWithConstant(ICmpInst &Cmp); + Instruction *foldNextMultiply(ICmpInst &Cmp); Instruction *foldICmpUsingBoolRange(ICmpInst &I); Instruction *foldICmpInstWithConstant(ICmpInst &Cmp); Instruction *foldICmpInstWithConstantNotInt(ICmpInst &Cmp); diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index 1a41c1f3e1045..698619ab8aad1 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -3300,3 +3300,93 @@ entry: %cmp = icmp ult i32 %add, 253 ret i1 %cmp } + +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: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]] +; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 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: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]] +; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 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_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 +} From 93fd0c6df54831cd55bf2fd76e72c1f33bc9b882 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Sat, 9 Aug 2025 15:09:47 +0300 Subject: [PATCH 02/13] coding style... --- llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 722b03eb53f06..d30a67c5b0738 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1365,8 +1365,8 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { return nullptr; } - // Guard against weird special-case where Op1 gets optimized to constant. Leave it constant - // fonder. + // Guard against weird special-case where Op1 gets optimized to constant. + // Leave it constant fonder. if (match(Op1, m_APInt(NumCost))) return nullptr; From 914dd1f90e2a069afc52b66db6f597bde9117e43 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Sat, 9 Aug 2025 22:07:01 +0300 Subject: [PATCH 03/13] address review comments --- .../InstCombine/InstCombineCompares.cpp | 30 +++++++---- llvm/test/Transforms/InstCombine/icmp-add.ll | 54 +++++++++++++++++-- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index d30a67c5b0738..6adfbcd11ff91 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1330,16 +1330,21 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { CmpInst::Predicate Pred = Cmp.getPredicate(); const APInt *NegConst, *MaskConst, *NumCost; - if (Pred != ICmpInst::ICMP_EQ) + if (!ICmpInst::isEquality(Pred)) return nullptr; // Match num + neg - if (!match(Op0, m_And(m_Value(Add), m_Value(Neg)))) + if (!match(Op0, m_c_And(m_Value(Add), m_Value(Neg)))) return nullptr; - // Match num & mask - if (!match(Add, m_Add(m_Value(Num), m_Value(Mask)))) - return nullptr; + // Match num & mask and handle commutative care + if (!match(Add, m_c_Add(m_Value(Num), m_Value(Mask)))) { + if (match(Neg, m_c_Add(m_Value(Num), m_Value(Mask)))) { + std::swap(Add, Neg); + } else { + return nullptr; + } + } // Check the constant case if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) { @@ -1355,10 +1360,14 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { if (!match(Neg, m_Sub(m_Zero(), m_Value(Value)))) return nullptr; - // mask = %val - 1, which can be represented as sub %val, 1 or add %val, -1 - if (!match(Mask, m_Add(m_Value(Value), m_AllOnes())) && - !match(Mask, m_Sub(m_Value(Value), m_One()))) - 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)) @@ -1376,9 +1385,8 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { // Create new icmp eq (num & (val - 1)), 0 auto NewAnd = Builder.CreateAnd(Num, Mask); auto Zero = llvm::Constant::getNullValue(Num->getType()); - auto ICmp = Builder.CreateICmp(CmpInst::ICMP_EQ, NewAnd, Zero); - return replaceInstUsesWith(Cmp, ICmp); + return new ICmpInst(Pred, NewAnd, Zero); } /// Fold icmp Pred X, C. diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index 698619ab8aad1..6b0cc07538755 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -3307,8 +3307,8 @@ define i1 @val_is_aligend_sub(i32 %num, i32 %val) { ; 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: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]] -; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 0 +; 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) @@ -3330,8 +3330,8 @@ define i1 @val_is_aligend_add(i32 %num, i32 %val) { ; 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: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]] -; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 0 +; 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) @@ -3347,6 +3347,52 @@ define i1 @val_is_aligend_add(i32 %num, i32 %val) { 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 From fa89f888d571d2811546e32094633dad2401b472 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Sat, 9 Aug 2025 22:17:18 +0300 Subject: [PATCH 04/13] coding style.... --- llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 6adfbcd11ff91..569c4dd513d6d 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1360,7 +1360,8 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { 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 + // 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); From 25aec6bffd7bd4d873a70509ff7a21441e957030 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Sun, 10 Aug 2025 16:38:12 +0300 Subject: [PATCH 05/13] review --- .../InstCombine/InstCombineCompares.cpp | 27 +++----- llvm/test/Transforms/InstCombine/icmp-add.ll | 65 ++++++++++++++++++- 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 569c4dd513d6d..ff4334739b520 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1325,26 +1325,18 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) { // icmp eq 0, (and num, val - 1) // For value being power of two Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { - Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1); + Value *Op0 = Cmp.getOperand(0); Value *Neg, *Add, *Num, *Mask, *Value; - CmpInst::Predicate Pred = Cmp.getPredicate(); - const APInt *NegConst, *MaskConst, *NumCost; - - if (!ICmpInst::isEquality(Pred)) - return nullptr; + CmpPredicate Pred; + const APInt *NegConst, *MaskConst; // Match num + neg - if (!match(Op0, m_c_And(m_Value(Add), m_Value(Neg)))) + if (!match(Op0, m_OneUse(m_c_And(m_Value(Add), m_Value(Neg))))) return nullptr; // Match num & mask and handle commutative care - if (!match(Add, m_c_Add(m_Value(Num), m_Value(Mask)))) { - if (match(Neg, m_c_Add(m_Value(Num), m_Value(Mask)))) { - std::swap(Add, Neg); - } else { - return nullptr; - } - } + if (!match(Op0, m_c_And(m_c_Add(m_Value(Num), m_Value(Mask)), m_Value(Neg)))) + return nullptr; // Check the constant case if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) { @@ -1375,12 +1367,11 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { return nullptr; } - // Guard against weird special-case where Op1 gets optimized to constant. - // Leave it constant fonder. - if (match(Op1, m_APInt(NumCost))) + // Verify that Add and Num are connected by ICmp. + if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Add), m_Specific(Num)))) return nullptr; - if (!match(Op1, m_Value(Num))) + if (!ICmpInst::isEquality(Pred)) return nullptr; // Create new icmp eq (num & (val - 1)), 0 diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index 6b0cc07538755..b8dacd957167f 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -3301,6 +3301,8 @@ entry: 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:%.*]]) @@ -3406,8 +3408,8 @@ define i1 @val_is_aligend_const_pow2(i32 %num) { } ; 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( +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]] @@ -3436,3 +3438,62 @@ define i1 @val_is_aligend_non_pow(i32 %num, i32 %val) { %_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 +} + +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: [[TMP1:%.*]] = and i32 [[NUM]], 4095 +; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0 +; 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 +} From b66f1280c958a6cc161760f45a86e0e25c0974bc Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Sun, 10 Aug 2025 20:02:13 +0300 Subject: [PATCH 06/13] learning llvm... --- .../InstCombine/InstCombineCompares.cpp | 29 +++++++------------ .../InstCombine/InstCombineInternal.h | 2 +- llvm/test/Transforms/InstCombine/icmp-add.ll | 5 ++-- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index ff4334739b520..c15c3b4c0d2ee 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1324,26 +1324,23 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) { // to // icmp eq 0, (and num, val - 1) // For value being power of two -Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { - Value *Op0 = Cmp.getOperand(0); - Value *Neg, *Add, *Num, *Mask, *Value; +Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { + Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1); + Value *Neg, *Num, *Mask, *Value; CmpPredicate Pred; const APInt *NegConst, *MaskConst; - // Match num + neg - if (!match(Op0, m_OneUse(m_c_And(m_Value(Add), m_Value(Neg))))) + if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Num), + m_OneUse(m_c_And( + m_OneUse(m_c_Add(m_Value(Num), m_Value(Mask))), + m_Value(Neg)))))) return nullptr; - // Match num & mask and handle commutative care - if (!match(Op0, m_c_And(m_c_Add(m_Value(Num), m_Value(Mask)), m_Value(Neg)))) + if (!ICmpInst::isEquality(Pred)) return nullptr; // Check the constant case - if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) { - // Mask + 1 should be a power-of-two - if (!(*MaskConst + 1).isPowerOf2()) - return nullptr; - + if (match(Neg, m_APInt(NegConst)) && match(Mask, m_LowBitMask(MaskConst))) { // Neg = -(Mask + 1) if (*NegConst != -(*MaskConst + 1)) return nullptr; @@ -1367,11 +1364,7 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) { return nullptr; } - // Verify that Add and Num are connected by ICmp. - if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Add), m_Specific(Num)))) - return nullptr; - - if (!ICmpInst::isEquality(Pred)) + if (!match(Op0, m_Specific(Num)) && !match(Op1, m_Specific(Num))) return nullptr; // Create new icmp eq (num & (val - 1)), 0 @@ -7705,7 +7698,7 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) { if (Instruction *Res = foldICmpUsingKnownBits(I)) return Res; - if (Instruction *Res = foldNextMultiply(I)) + if (Instruction *Res = foldIsMultipleOfAPowerOfTwo(I)) return Res; // Test if the ICmpInst instruction is used exclusively by a select as diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 5f83cb1b9ae28..2340028ce93dc 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -721,7 +721,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Instruction *foldICmpUsingKnownBits(ICmpInst &Cmp); Instruction *foldICmpWithDominatingICmp(ICmpInst &Cmp); Instruction *foldICmpWithConstant(ICmpInst &Cmp); - Instruction *foldNextMultiply(ICmpInst &Cmp); + Instruction *foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp); Instruction *foldICmpUsingBoolRange(ICmpInst &I); Instruction *foldICmpInstWithConstant(ICmpInst &Cmp); Instruction *foldICmpInstWithConstantNotInt(ICmpInst &Cmp); diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index b8dacd957167f..0b597ebd1c16f 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -3454,12 +3454,13 @@ define i1 @val_is_aligend_const_pow2_multiuse(i32 %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: [[TMP1:%.*]] = and i32 [[NUM]], 4095 -; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0 +; 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 From db1316ba0f75b504176327d943cbf4f5dce3be1d Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Sun, 10 Aug 2025 23:13:04 +0300 Subject: [PATCH 07/13] use m_Defereded instead of m_Specific --- .../lib/Transforms/InstCombine/InstCombineCompares.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index c15c3b4c0d2ee..d0c5d89c0380d 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1325,15 +1325,14 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) { // icmp eq 0, (and num, val - 1) // For value being power of two Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { - Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1); 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_Value(Num), m_Value(Mask))), - m_Value(Neg)))))) + 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)) @@ -1364,9 +1363,6 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { return nullptr; } - if (!match(Op0, m_Specific(Num)) && !match(Op1, m_Specific(Num))) - return nullptr; - // Create new icmp eq (num & (val - 1)), 0 auto NewAnd = Builder.CreateAnd(Num, Mask); auto Zero = llvm::Constant::getNullValue(Num->getType()); From c72f7bbfa891cb5820aa81416ad97b1815af9cb1 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Mon, 11 Aug 2025 20:29:48 +0300 Subject: [PATCH 08/13] don't open code bitwise not --- llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index d0c5d89c0380d..732fb61f56309 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1341,7 +1341,7 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { // Check the constant case if (match(Neg, m_APInt(NegConst)) && match(Mask, m_LowBitMask(MaskConst))) { // Neg = -(Mask + 1) - if (*NegConst != -(*MaskConst + 1)) + if (*NegConst != ~*MaskConst) return nullptr; } else { // Match neg = sub 0, val From 0e9e8e7141ea7a1255f13f23fd0ae6664973c813 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Mon, 11 Aug 2025 22:00:12 +0300 Subject: [PATCH 09/13] drop non-constant path as it happens to be non-profitable --- .../InstCombine/InstCombineCompares.cpp | 32 +--- llvm/test/Transforms/InstCombine/icmp-add.ll | 139 ------------------ 2 files changed, 8 insertions(+), 163 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 732fb61f56309..6274417365f26 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1325,7 +1325,7 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) { // icmp eq 0, (and num, val - 1) // For value being power of two Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { - Value *Neg, *Num, *Mask, *Value; + Value *Neg, *Num, *Mask; CmpPredicate Pred; const APInt *NegConst, *MaskConst; @@ -1338,30 +1338,14 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { 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) - 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; - } - } + // Check only constant case, since it's the only profitable. + // See https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2657/ + if (!match(Neg, m_APInt(NegConst)) || !match(Mask, m_LowBitMask(MaskConst))) + return nullptr; - // Value should be a known power-of-two. - if (!isKnownToBeAPowerOfTwo(Value, false, &Cmp)) - return nullptr; - } + // Neg = -(Mask + 1) + if (*NegConst != ~*MaskConst) + return nullptr; // Create new icmp eq (num & (val - 1)), 0 auto NewAnd = Builder.CreateAnd(Num, Mask); diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index 0b597ebd1c16f..7b4650ec3e1b8 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -3303,98 +3303,6 @@ entry: ; 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 @@ -3421,24 +3329,6 @@ define i1 @val_is_aligend_const_non_pow2(i32 %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 @@ -3469,32 +3359,3 @@ define i1 @val_is_aligend_const_pow2_multiuse1(i32 %num) { %_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 -} From 327aaed2436c97b9bcbc345650efa80cfb1f7727 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Tue, 12 Aug 2025 23:22:29 +0300 Subject: [PATCH 10/13] review --- .../InstCombine/InstCombineCompares.cpp | 29 ++--- llvm/test/Transforms/InstCombine/icmp-add.ll | 119 +++++++++++++++--- 2 files changed, 115 insertions(+), 33 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 6274417365f26..43ca2b5cd7b25 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1323,33 +1323,28 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) { // Fold icmp eq (num + (val - 1)) & -val, num // to // icmp eq 0, (and num, val - 1) -// For value being power of two +// For val being power of two Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { - Value *Neg, *Num, *Mask; + Value *Num; CmpPredicate Pred; - const APInt *NegConst, *MaskConst; + 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_Value(Mask))), - m_Value(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 (!ICmpInst::isEquality(Pred)) - return nullptr; - - // Check only constant case, since it's the only profitable. - // See https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2657/ - if (!match(Neg, m_APInt(NegConst)) || !match(Mask, m_LowBitMask(MaskConst))) + if (*Neg != ~*Mask) return nullptr; - // Neg = -(Mask + 1) - if (*NegConst != ~*MaskConst) + if (!ICmpInst::isEquality(Pred)) return nullptr; // Create new icmp eq (num & (val - 1)), 0 - auto NewAnd = Builder.CreateAnd(Num, Mask); - auto Zero = llvm::Constant::getNullValue(Num->getType()); + auto NewAnd = Builder.CreateAnd(Num, *Mask); + auto Zero = Constant::getNullValue(Num->getType()); return new ICmpInst(Pred, NewAnd, Zero); } diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index 7b4650ec3e1b8..cb428097f2ae1 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -3310,8 +3310,44 @@ define i1 @val_is_aligend_const_pow2(i32 %num) { ; 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 + %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 } @@ -3319,28 +3355,28 @@ define i1 @val_is_aligend_const_pow2(i32 %num) { 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: [[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 - %_2.sroa.0.0 = and i32 %num.biased, -7 - %_0 = icmp eq i32 %_2.sroa.0.0, %num + %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: [[_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: [[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 - %_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 + %num.masked = and i32 %num.biased, -4096 + call void @use(i32 %num.masked) + %_0 = icmp eq i32 %num.masked, %num ret i1 %_0 } @@ -3349,13 +3385,64 @@ 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: [[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) - %_2.sroa.0.0 = and i32 %num.biased, -4096 - %_0 = icmp eq i32 %_2.sroa.0.0, %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_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 } From ee4c1a20522d356824c2ee421673ea5f473cf961 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Wed, 13 Aug 2025 21:38:27 +0300 Subject: [PATCH 11/13] style fixes --- .../Transforms/InstCombine/InstCombineCompares.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 43ca2b5cd7b25..3a9c0922080ca 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1320,10 +1320,10 @@ 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 +/// 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; @@ -1343,8 +1343,8 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { return nullptr; // Create new icmp eq (num & (val - 1)), 0 - auto NewAnd = Builder.CreateAnd(Num, *Mask); - auto Zero = Constant::getNullValue(Num->getType()); + auto *NewAnd = Builder.CreateAnd(Num, *Mask); + auto *Zero = Constant::getNullValue(Num->getType()); return new ICmpInst(Pred, NewAnd, Zero); } From b58e38810f7efe1c2bd5d3b62cad1591bd8b0560 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Wed, 13 Aug 2025 22:41:50 +0300 Subject: [PATCH 12/13] swap operands in comment --- llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 3a9c0922080ca..8aa2693da039e 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1322,7 +1322,7 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) { /// Fold icmp eq (num + (val - 1)) & -val, num /// to -/// icmp eq 0, (and num, val - 1) +/// icmp eq (and num, val - 1), 0 /// For val being power of two Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { Value *Num; From 52ef6a676cd488d11fed3ec1bfd9671fbd4a15e6 Mon Sep 17 00:00:00 2001 From: Pavel Skripkin Date: Wed, 13 Aug 2025 23:59:46 +0300 Subject: [PATCH 13/13] comment fixes --- llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 8aa2693da039e..a64f422c3eede 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1320,10 +1320,10 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) { return nullptr; } -/// Fold icmp eq (num + (val - 1)) & -val, num +/// Fold icmp eq (num + mask) & ~mask, num /// to -/// icmp eq (and num, val - 1), 0 -/// For val being power of two +/// icmp eq (and num, mask), 0 +/// Where mask is a low bit mask. Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { Value *Num; CmpPredicate Pred; @@ -1342,7 +1342,7 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) { if (!ICmpInst::isEquality(Pred)) return nullptr; - // Create new icmp eq (num & (val - 1)), 0 + // Create new icmp eq (num & mask), 0 auto *NewAnd = Builder.CreateAnd(Num, *Mask); auto *Zero = Constant::getNullValue(Num->getType());