[llvm] [PatternMatch] Add m_Ctpop / m_Ctlz / m_Cttz matchers (PR #195520)
Simon Pilgrim via llvm-commits
llvm-commits at lists.llvm.org
Sun May 3 05:24:53 PDT 2026
https://github.com/RKSimon created https://github.com/llvm/llvm-project/pull/195520
None
>From 003d00ce22b44156bc94baa170ed55423dd68e9e Mon Sep 17 00:00:00 2001
From: Simon Pilgrim <llvm-dev at redking.me.uk>
Date: Sun, 3 May 2026 13:23:24 +0100
Subject: [PATCH] [PatternMatch] Add m_Ctpop / m_Ctlz / m_Cttz matchers
---
llvm/include/llvm/IR/PatternMatch.h | 16 ++++++++++++++++
llvm/lib/Analysis/InstructionSimplify.cpp | 3 +--
llvm/lib/Analysis/LazyValueInfo.cpp | 2 +-
llvm/lib/Analysis/ValueTracking.cpp | 5 ++---
llvm/lib/CodeGen/CodeGenPrepare.cpp | 3 +--
.../AggressiveInstCombine.cpp | 12 ++++--------
.../InstCombine/InstCombineAddSub.cpp | 19 ++++++++-----------
.../InstCombine/InstCombineAndOrXor.cpp | 9 +++------
.../InstCombine/InstCombineCasts.cpp | 3 +--
.../InstCombine/InstCombineSelect.cpp | 8 ++++----
.../InstCombine/InstCombineShifts.cpp | 9 ++++-----
.../InstCombineSimplifyDemanded.cpp | 5 ++---
.../InstCombine/InstructionCombining.cpp | 3 +--
13 files changed, 48 insertions(+), 49 deletions(-)
diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index de24f4da336c2..e6650e73ec0ac 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -2927,6 +2927,10 @@ template <typename Opnd0>
inline typename m_Intrinsic_Ty<Opnd0>::Ty m_BSwap(const Opnd0 &Op0) {
return m_Intrinsic<Intrinsic::bswap>(Op0);
}
+template <typename Opnd0>
+inline typename m_Intrinsic_Ty<Opnd0>::Ty m_Ctpop(const Opnd0 &Op0) {
+ return m_Intrinsic<Intrinsic::ctpop>(Op0);
+}
template <typename Opnd0>
inline typename m_Intrinsic_Ty<Opnd0>::Ty m_FAbs(const Opnd0 &Op0) {
@@ -2938,6 +2942,18 @@ inline typename m_Intrinsic_Ty<Opnd0>::Ty m_FCanonicalize(const Opnd0 &Op0) {
return m_Intrinsic<Intrinsic::canonicalize>(Op0);
}
+template <typename Opnd0, typename Opnd1>
+inline typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty m_Ctlz(const Opnd0 &Op0,
+ const Opnd1 &Op1) {
+ return m_Intrinsic<Intrinsic::ctlz>(Op0, Op1);
+}
+
+template <typename Opnd0, typename Opnd1>
+inline typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty m_Cttz(const Opnd0 &Op0,
+ const Opnd1 &Op1) {
+ return m_Intrinsic<Intrinsic::cttz>(Op0, Op1);
+}
+
template <typename Opnd0, typename Opnd1>
inline typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty m_FMinNum(const Opnd0 &Op0,
const Opnd1 &Op1) {
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index e66d6e5f5acf0..af21e46563f94 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -1708,8 +1708,7 @@ static Value *simplifyAndOrOfICmpsWithCtpop(ICmpInst *Cmp0, ICmpInst *Cmp1,
CmpPredicate Pred0, Pred1;
Value *X;
const APInt *C;
- if (!match(Cmp0, m_ICmp(Pred0, m_Intrinsic<Intrinsic::ctpop>(m_Value(X)),
- m_APInt(C))) ||
+ if (!match(Cmp0, m_ICmp(Pred0, m_Ctpop(m_Value(X)), m_APInt(C))) ||
!match(Cmp1, m_ICmp(Pred1, m_Specific(X), m_ZeroInt())) || C->isZero())
return nullptr;
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index 48c346d75ff9b..b70c380dd5466 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -1405,7 +1405,7 @@ std::optional<ValueLatticeElement> LazyValueInfoImpl::getValueFromICmpCondition(
return getValueFromSimpleICmpCondition(SwappedPred, LHS, Offset, ICI,
UseBlockValue);
- if (match(LHS, m_Intrinsic<Intrinsic::ctpop>(m_Specific(Val))))
+ if (match(LHS, m_Ctpop(m_Specific(Val))))
return getValueFromICmpCtpop(EdgePred, RHS);
const APInt *Mask, *C;
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8b82a1d413149..40f1025fed4fb 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2635,8 +2635,7 @@ static bool isImpliedToBeAPowerOfTwoFromCond(const Value *V, bool OrZero,
bool CondIsTrue) {
CmpPredicate Pred;
const APInt *RHSC;
- if (!match(Cond, m_ICmp(Pred, m_Intrinsic<Intrinsic::ctpop>(m_Specific(V)),
- m_APInt(RHSC))))
+ if (!match(Cond, m_ICmp(Pred, m_Ctpop(m_Specific(V)), m_APInt(RHSC))))
return false;
if (!CondIsTrue)
Pred = ICmpInst::getInversePredicate(Pred);
@@ -10621,7 +10620,7 @@ void llvm::findValuesAffectedByCondition(
}
}
- if (HasRHSC && match(A, m_Intrinsic<Intrinsic::ctpop>(m_Value(X))))
+ if (HasRHSC && match(A, m_Ctpop(m_Value(X))))
AddAffected(X);
} else if (match(V, m_FCmp(Pred, m_Value(A), m_Value(B)))) {
AddCmpOperands(A, B);
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 50c8bfcdf0c27..22d7d221c2670 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -1806,8 +1806,7 @@ bool CodeGenPrepare::unfoldPowerOf2Test(CmpInst *Cmp) {
const APInt *C;
// (icmp (ctpop x), c)
- if (!match(Cmp, m_ICmp(Pred, m_Intrinsic<Intrinsic::ctpop>(m_Value(X)),
- m_APIntAllowPoison(C))))
+ if (!match(Cmp, m_ICmp(Pred, m_Ctpop(m_Value(X)), m_APIntAllowPoison(C))))
return false;
// We're only interested in "is power of 2 [or zero]" patterns.
diff --git a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
index f967400ded8da..e449749eaa43d 100644
--- a/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
+++ b/llvm/lib/Transforms/AggressiveInstCombine/AggressiveInstCombine.cpp
@@ -128,8 +128,7 @@ static bool foldSelectSplitCTTZ(Instruction &I) {
return false;
// LoResult: cttz(trunc(SrcVal), _), must use same truncated value
- if (!match(LoResult, m_OneUse(m_Intrinsic<Intrinsic::cttz>(
- m_Specific(LoTrunc), m_Value()))))
+ if (!match(LoResult, m_OneUse(m_Cttz(m_Specific(LoTrunc), m_Value()))))
return false;
// HiResult: add/or_disjoint(cttz(trunc(lshr(SrcVal, N/2)), _), N/2)
@@ -139,8 +138,7 @@ static bool foldSelectSplitCTTZ(Instruction &I) {
return false;
Value *HiCttzArg;
- if (!match(CttzHiCall, m_OneUse(m_Intrinsic<Intrinsic::cttz>(
- m_Value(HiCttzArg), m_Value()))))
+ if (!match(CttzHiCall, m_OneUse(m_Cttz(m_Value(HiCttzArg), m_Value()))))
return false;
if (!match(HiCttzArg,
@@ -223,8 +221,7 @@ static bool foldSelectSplitCTLZ(Instruction &I) {
// HiResult: ctlz(trunc(lshr(SrcVal, N/2)), _)
Value *HiCtlzArg;
- if (!match(HiResult, m_OneUse(m_Intrinsic<Intrinsic::ctlz>(m_Value(HiCtlzArg),
- m_Value()))))
+ if (!match(HiResult, m_OneUse(m_Ctlz(m_Value(HiCtlzArg), m_Value()))))
return false;
if (!match(HiCtlzArg,
@@ -238,8 +235,7 @@ static bool foldSelectSplitCTLZ(Instruction &I) {
return false;
Value *LoCtlzArg;
- if (!match(CtlzLoCall, m_OneUse(m_Intrinsic<Intrinsic::ctlz>(
- m_Value(LoCtlzArg), m_Value()))))
+ if (!match(CtlzLoCall, m_OneUse(m_Ctlz(m_Value(LoCtlzArg), m_Value()))))
return false;
if (!match(LoCtlzArg, m_Trunc(m_Specific(SrcVal))))
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 845b623ef0379..462256c78a48b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1957,8 +1957,8 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
}
// ctpop(A) + ctpop(B) => ctpop(A | B) if A and B have no bits set in common.
- if (match(LHS, m_OneUse(m_Intrinsic<Intrinsic::ctpop>(m_Value(A)))) &&
- match(RHS, m_OneUse(m_Intrinsic<Intrinsic::ctpop>(m_Value(B)))) &&
+ if (match(LHS, m_OneUse(m_Ctpop(m_Value(A)))) &&
+ match(RHS, m_OneUse(m_Ctpop(m_Value(B)))) &&
haveNoCommonBitsSet(A, B, SQ.getWithInstruction(&I)))
return replaceInstUsesWith(
I, Builder.CreateIntrinsic(Intrinsic::ctpop, {I.getType()},
@@ -1970,14 +1970,11 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
// BW - ctlz(A - 1, false)
const APInt *XorC;
CmpPredicate Pred;
- if (match(&I,
- m_c_Add(
- m_ZExt(m_ICmp(Pred, m_Intrinsic<Intrinsic::ctpop>(m_Value(A)),
- m_One())),
- m_OneUse(m_ZExtOrSelf(m_OneUse(m_Xor(
- m_OneUse(m_TruncOrSelf(m_OneUse(
- m_Intrinsic<Intrinsic::ctlz>(m_Deferred(A), m_One())))),
- m_APInt(XorC))))))) &&
+ if (match(&I, m_c_Add(m_ZExt(m_ICmp(Pred, m_Ctpop(m_Value(A)), m_One())),
+ m_OneUse(m_ZExtOrSelf(m_OneUse(
+ m_Xor(m_OneUse(m_TruncOrSelf(m_OneUse(
+ m_Ctlz(m_Deferred(A), m_One())))),
+ m_APInt(XorC))))))) &&
(Pred == ICmpInst::ICMP_UGT || Pred == ICmpInst::ICMP_NE) &&
*XorC == A->getType()->getScalarSizeInBits() - 1) {
Value *Sub = Builder.CreateAdd(A, Constant::getAllOnesValue(A->getType()));
@@ -2971,7 +2968,7 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
// C - ctpop(X) => ctpop(~X) if C is bitwidth
if (match(Op0, m_SpecificInt(BitWidth)) &&
- match(Op1, m_OneUse(m_Intrinsic<Intrinsic::ctpop>(m_Value(X)))))
+ match(Op1, m_OneUse(m_Ctpop(m_Value(X)))))
return replaceInstUsesWith(
I, Builder.CreateIntrinsic(Intrinsic::ctpop, {I.getType()},
{Builder.CreateNot(X)}));
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index d81ed56290476..52d5e28d20915 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -947,8 +947,7 @@ static Value *foldIsPowerOf2OrZero(ICmpInst *Cmp0, ICmpInst *Cmp1, bool IsAnd,
InstCombinerImpl &IC) {
CmpPredicate Pred0, Pred1;
Value *X;
- if (!match(Cmp0, m_ICmp(Pred0, m_Intrinsic<Intrinsic::ctpop>(m_Value(X)),
- m_SpecificInt(1))) ||
+ if (!match(Cmp0, m_ICmp(Pred0, m_Ctpop(m_Value(X)), m_SpecificInt(1))) ||
!match(Cmp1, m_ICmp(Pred1, m_Specific(X), m_ZeroInt())))
return nullptr;
@@ -985,8 +984,7 @@ static Value *foldIsPowerOf2(ICmpInst *Cmp0, ICmpInst *Cmp1, bool JoinedByAnd,
Value *X;
if (JoinedByAnd &&
match(Cmp0, m_SpecificICmp(ICmpInst::ICMP_NE, m_Value(X), m_ZeroInt())) &&
- match(Cmp1, m_SpecificICmp(ICmpInst::ICMP_ULT,
- m_Intrinsic<Intrinsic::ctpop>(m_Specific(X)),
+ match(Cmp1, m_SpecificICmp(ICmpInst::ICMP_ULT, m_Ctpop(m_Specific(X)),
m_SpecificInt(2)))) {
auto *CtPop = cast<Instruction>(Cmp1->getOperand(0));
// Drop range attributes and re-infer them in the next iteration.
@@ -997,8 +995,7 @@ static Value *foldIsPowerOf2(ICmpInst *Cmp0, ICmpInst *Cmp1, bool JoinedByAnd,
// (X == 0) || (ctpop(X) u> 1) --> ctpop(X) != 1
if (!JoinedByAnd &&
match(Cmp0, m_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(X), m_ZeroInt())) &&
- match(Cmp1, m_SpecificICmp(ICmpInst::ICMP_UGT,
- m_Intrinsic<Intrinsic::ctpop>(m_Specific(X)),
+ match(Cmp1, m_SpecificICmp(ICmpInst::ICMP_UGT, m_Ctpop(m_Specific(X)),
m_SpecificInt(1)))) {
auto *CtPop = cast<Instruction>(Cmp1->getOperand(0));
// Drop range attributes and re-infer them in the next iteration.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index f4568852a997d..05508d84c036b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -1201,8 +1201,7 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) {
return I;
// trunc (ctlz_i32(zext(A), B) --> add(ctlz_i16(A, B), C)
- if (match(Src, m_OneUse(m_Intrinsic<Intrinsic::ctlz>(m_ZExt(m_Value(A)),
- m_Value(B))))) {
+ if (match(Src, m_OneUse(m_Ctlz(m_ZExt(m_Value(A)), m_Value(B))))) {
unsigned AWidth = A->getType()->getScalarSizeInBits();
if (AWidth == DestWidth && AWidth > Log2_32(SrcWidth)) {
Value *WidthDiff = ConstantInt::get(A->getType(), SrcWidth - AWidth);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index c37b7ecd98b64..f0b2ddda5dcfe 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1437,7 +1437,7 @@ static Instruction *foldSelectCtlzToCttz(ICmpInst *ICI, Value *TrueVal,
m_Xor(m_Value(Ctlz), m_SpecificInt(BitWidth - 1))))
return nullptr;
- if (!match(Ctlz, m_Intrinsic<Intrinsic::ctlz>()))
+ if (!match(Ctlz, m_Ctlz(m_Value(), m_Value())))
return nullptr;
if (TrueVal != Ctlz && !match(TrueVal, m_SpecificInt(BitWidth)))
@@ -1489,8 +1489,8 @@ static Value *foldSelectCttzCtlz(ICmpInst *ICI, Value *TrueVal, Value *FalseVal,
// Check that 'Count' is a call to intrinsic cttz/ctlz. Also check that the
// input to the cttz/ctlz is used as LHS for the compare instruction.
Value *X;
- if (!match(Count, m_Intrinsic<Intrinsic::cttz>(m_Value(X))) &&
- !match(Count, m_Intrinsic<Intrinsic::ctlz>(m_Value(X))))
+ if (!match(Count, m_Cttz(m_Value(X), m_Value())) &&
+ !match(Count, m_Ctlz(m_Value(X), m_Value())))
return nullptr;
// (X == 0) ? BitWidth : ctz(X)
@@ -4026,7 +4026,7 @@ static Instruction *foldBitCeil(SelectInst &SI, IRBuilderBase &Builder,
!match(TrueVal,
m_OneUse(m_Shl(m_One(), m_OneUse(m_Sub(m_SpecificInt(BitWidth),
m_Value(Ctlz)))))) ||
- !match(Ctlz, m_Intrinsic<Intrinsic::ctlz>(m_Value(CtlzOp), m_Value())) ||
+ !match(Ctlz, m_Ctlz(m_Value(CtlzOp), m_Value())) ||
!isSafeToRemoveBitCeilSelect(Pred, Cond0, Cond1, CtlzOp, BitWidth,
ShouldDropNoWrap))
return nullptr;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
index 2532dce2ca946..3547a79df1355 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
@@ -1105,8 +1105,8 @@ static bool setShiftFlags(BinaryOperator &I, const SimplifyQuery &Q) {
return true;
}
// Infer 'exact' flag if shift amount is cttz(x) on the same operand.
- if (match(I.getOperand(1), m_Intrinsic<Intrinsic::cttz>(
- m_Specific(I.getOperand(0)), m_Value()))) {
+ if (match(I.getOperand(1),
+ m_Cttz(m_Specific(I.getOperand(0)), m_Value()))) {
I.setIsExact();
return true;
}
@@ -1376,8 +1376,7 @@ Instruction *InstCombinerImpl::visitShl(BinaryOperator &I) {
// Canonicalize "extract lowest set bit" using cttz to and-with-negate:
// 1 << (cttz X) --> -X & X
- if (match(Op1,
- m_OneUse(m_Intrinsic<Intrinsic::cttz>(m_Value(X), m_Value())))) {
+ if (match(Op1, m_OneUse(m_Cttz(m_Value(X), m_Value())))) {
Value *NegX = Builder.CreateNeg(X, "neg");
return BinaryOperator::CreateAnd(NegX, X);
}
@@ -1734,7 +1733,7 @@ Instruction *InstCombinerImpl::visitLShr(BinaryOperator &I) {
Value *Shl0_Op0, *Shl0_Op1, *Shl1_Op1;
BinaryOperator *Shl1;
if (match(Op0, m_Shl(m_Value(Shl0_Op0), m_Value(Shl0_Op1))) &&
- match(Op1, m_Intrinsic<Intrinsic::cttz>(m_BinOp(Shl1))) &&
+ match(Op1, m_Cttz(m_BinOp(Shl1), m_Value())) &&
match(Shl1, m_Shl(m_Specific(Shl0_Op0), m_Value(Shl1_Op1))) &&
isKnownToBeAPowerOfTwo(Shl0_Op0, /*OrZero=*/true, &I)) {
auto *Shl0 = cast<BinaryOperator>(Op0);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 26d77dabdfd0b..83ea2d2932ac9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -357,9 +357,8 @@ Value *InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
SimplifyDemandedBits(I, 0, DemandedMask, LHSKnown, Q, Depth + 1))
return I;
Value *LHS, *RHS;
- if (DemandedMask == 1 &&
- match(I->getOperand(0), m_Intrinsic<Intrinsic::ctpop>(m_Value(LHS))) &&
- match(I->getOperand(1), m_Intrinsic<Intrinsic::ctpop>(m_Value(RHS)))) {
+ if (DemandedMask == 1 && match(I->getOperand(0), m_Ctpop(m_Value(LHS))) &&
+ match(I->getOperand(1), m_Ctpop(m_Value(RHS)))) {
// (ctpop(X) ^ ctpop(Y)) & 1 --> ctpop(X^Y) & 1
IRBuilderBase::InsertPointGuard Guard(Builder);
Builder.SetInsertPoint(I);
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index c569cc32b9c98..489fe9f904a14 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -882,8 +882,7 @@ Instruction *InstCombinerImpl::tryFoldInstWithCtpopWithNot(Instruction *I) {
Value *Op;
// Find ctpop.
- if (!match(I->getOperand(1 - ConstIdx),
- m_OneUse(m_Intrinsic<Intrinsic::ctpop>(m_Value(Op)))))
+ if (!match(I->getOperand(1 - ConstIdx), m_OneUse(m_Ctpop(m_Value(Op)))))
return nullptr;
Constant *C;
More information about the llvm-commits
mailing list