Skip to content

Commit 6034ab3

Browse files
authored
[InstCombine] Add CTLZ -> CTTZ simplification (#164733)
This PR adds the simplification `ctlz(~x & (x - 1)) -> bitwidth - cttz(x, false)` ([Alive2](https://alive2.llvm.org/ce/z/vVDRCu)). Closes issue #164436
1 parent bf55333 commit 6034ab3

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,18 @@ static Instruction *foldCttzCtlz(IntrinsicInst &II, InstCombinerImpl &IC) {
582582
IC.Builder.CreateBinaryIntrinsic(Intrinsic::ctlz, C, Op1);
583583
return BinaryOperator::CreateSub(ConstCtlz, X);
584584
}
585+
586+
// ctlz(~x & (x - 1)) -> bitwidth - cttz(x, false)
587+
if (Op0->hasOneUse() &&
588+
match(Op0,
589+
m_c_And(m_Not(m_Value(X)), m_Add(m_Deferred(X), m_AllOnes())))) {
590+
Type *Ty = II.getType();
591+
unsigned BitWidth = Ty->getScalarSizeInBits();
592+
auto *Cttz = IC.Builder.CreateIntrinsic(Intrinsic::cttz, Ty,
593+
{X, IC.Builder.getFalse()});
594+
auto *Bw = ConstantInt::get(Ty, APInt(BitWidth, BitWidth));
595+
return IC.replaceInstUsesWith(II, IC.Builder.CreateSub(Bw, Cttz));
596+
}
585597
}
586598

587599
// cttz(Pow2) -> Log2(Pow2)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
2+
; RUN: opt < %s -S -passes=instcombine | FileCheck %s
3+
4+
; ctpop(~i & (i - 1)) -> bitwidth - cttz(i, false)
5+
define i8 @ctlz_to_sub_bw_cttz(i8 %a0) {
6+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz(
7+
; CHECK-SAME: i8 [[A0:%.*]]) {
8+
; CHECK-NEXT: [[TMP1:%.*]] = call range(i8 0, 9) i8 @llvm.cttz.i8(i8 [[A0]], i1 false)
9+
; CHECK-NEXT: [[CLZ:%.*]] = sub nuw nsw i8 8, [[TMP1]]
10+
; CHECK-NEXT: ret i8 [[CLZ]]
11+
;
12+
%dec = add i8 %a0, -1
13+
%not = xor i8 %a0, -1
14+
%and = and i8 %dec, %not
15+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 false)
16+
ret i8 %clz
17+
}
18+
19+
define i8 @ctlz_to_sub_bw_cttz_poison(i8 %a0) {
20+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz_poison(
21+
; CHECK-SAME: i8 [[A0:%.*]]) {
22+
; CHECK-NEXT: [[TMP1:%.*]] = call range(i8 0, 9) i8 @llvm.cttz.i8(i8 [[A0]], i1 false)
23+
; CHECK-NEXT: [[CLZ:%.*]] = sub nuw nsw i8 8, [[TMP1]]
24+
; CHECK-NEXT: ret i8 [[CLZ]]
25+
;
26+
%dec = add i8 %a0, -1
27+
%not = xor i8 %a0, -1
28+
%and = and i8 %dec, %not
29+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 true)
30+
ret i8 %clz
31+
}
32+
33+
define i8 @ctlz_to_sub_bw_cttz_different_add(i8 %a0) {
34+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz_different_add(
35+
; CHECK-SAME: i8 [[A0:%.*]]) {
36+
; CHECK-NEXT: [[DEC:%.*]] = add i8 [[A0]], 1
37+
; CHECK-NEXT: [[NOT:%.*]] = xor i8 [[A0]], -1
38+
; CHECK-NEXT: [[AND:%.*]] = and i8 [[DEC]], [[NOT]]
39+
; CHECK-NEXT: [[CLZ:%.*]] = tail call range(i8 0, 9) i8 @llvm.ctlz.i8(i8 [[AND]], i1 false)
40+
; CHECK-NEXT: ret i8 [[CLZ]]
41+
;
42+
%dec = add i8 %a0, 1
43+
%not = xor i8 %a0, -1
44+
%and = and i8 %dec, %not
45+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 false)
46+
ret i8 %clz
47+
}
48+
49+
define i8 @ctlz_to_sub_bw_cttz_different_xor(i8 %a0) {
50+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz_different_xor(
51+
; CHECK-SAME: i8 [[A0:%.*]]) {
52+
; CHECK-NEXT: [[DEC:%.*]] = add i8 [[A0]], -1
53+
; CHECK-NEXT: [[NOT:%.*]] = xor i8 [[A0]], 1
54+
; CHECK-NEXT: [[AND:%.*]] = and i8 [[DEC]], [[NOT]]
55+
; CHECK-NEXT: [[CLZ:%.*]] = tail call range(i8 0, 9) i8 @llvm.ctlz.i8(i8 [[AND]], i1 false)
56+
; CHECK-NEXT: ret i8 [[CLZ]]
57+
;
58+
%dec = add i8 %a0, -1
59+
%not = xor i8 %a0, 1
60+
%and = and i8 %dec, %not
61+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 false)
62+
ret i8 %clz
63+
}
64+
65+
declare void @use(i8)
66+
67+
define i8 @ctlz_to_sub_bw_cttz_multi_use_dec(i8 %a0) {
68+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz_multi_use_dec(
69+
; CHECK-SAME: i8 [[A0:%.*]]) {
70+
; CHECK-NEXT: [[DEC:%.*]] = add i8 [[A0]], -1
71+
; CHECK-NEXT: call void @use(i8 [[DEC]])
72+
; CHECK-NEXT: [[TMP1:%.*]] = call range(i8 0, 9) i8 @llvm.cttz.i8(i8 [[A0]], i1 false)
73+
; CHECK-NEXT: [[CLZ:%.*]] = sub nuw nsw i8 8, [[TMP1]]
74+
; CHECK-NEXT: ret i8 [[CLZ]]
75+
;
76+
%dec = add i8 %a0, -1
77+
call void @use(i8 %dec)
78+
%not = xor i8 %a0, -1
79+
%and = and i8 %dec, %not
80+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 false)
81+
ret i8 %clz
82+
}
83+
84+
define i8 @ctlz_to_sub_bw_cttz_multi_use_not(i8 %a0) {
85+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz_multi_use_not(
86+
; CHECK-SAME: i8 [[A0:%.*]]) {
87+
; CHECK-NEXT: [[NOT:%.*]] = xor i8 [[A0]], -1
88+
; CHECK-NEXT: call void @use(i8 [[NOT]])
89+
; CHECK-NEXT: [[TMP1:%.*]] = call range(i8 0, 9) i8 @llvm.cttz.i8(i8 [[A0]], i1 false)
90+
; CHECK-NEXT: [[CLZ:%.*]] = sub nuw nsw i8 8, [[TMP1]]
91+
; CHECK-NEXT: ret i8 [[CLZ]]
92+
;
93+
%dec = add i8 %a0, -1
94+
%not = xor i8 %a0, -1
95+
call void @use(i8 %not)
96+
%and = and i8 %dec, %not
97+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 false)
98+
ret i8 %clz
99+
}
100+
101+
define i8 @ctlz_to_sub_bw_cttz_multi_use_and(i8 %a0) {
102+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz_multi_use_and(
103+
; CHECK-SAME: i8 [[A0:%.*]]) {
104+
; CHECK-NEXT: [[DEC:%.*]] = add i8 [[A0]], -1
105+
; CHECK-NEXT: [[NOT:%.*]] = xor i8 [[A0]], -1
106+
; CHECK-NEXT: [[AND:%.*]] = and i8 [[DEC]], [[NOT]]
107+
; CHECK-NEXT: call void @use(i8 [[AND]])
108+
; CHECK-NEXT: [[CLZ:%.*]] = tail call range(i8 0, 9) i8 @llvm.ctlz.i8(i8 [[AND]], i1 false)
109+
; CHECK-NEXT: ret i8 [[CLZ]]
110+
;
111+
%dec = add i8 %a0, -1
112+
%not = xor i8 %a0, -1
113+
%and = and i8 %dec, %not
114+
call void @use(i8 %and)
115+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 false)
116+
ret i8 %clz
117+
}
118+
119+
define i8 @ctlz_to_sub_bw_cttz_commute_and(i8 %a0) {
120+
; CHECK-LABEL: define i8 @ctlz_to_sub_bw_cttz_commute_and(
121+
; CHECK-SAME: i8 [[A0:%.*]]) {
122+
; CHECK-NEXT: [[TMP1:%.*]] = call range(i8 0, 9) i8 @llvm.cttz.i8(i8 [[A0]], i1 false)
123+
; CHECK-NEXT: [[CLZ:%.*]] = sub nuw nsw i8 8, [[TMP1]]
124+
; CHECK-NEXT: ret i8 [[CLZ]]
125+
;
126+
%dec = add i8 %a0, -1
127+
%not = xor i8 %a0, -1
128+
%and = and i8 %not, %dec
129+
%clz = tail call i8 @llvm.ctlz.i8(i8 %and, i1 false)
130+
ret i8 %clz
131+
}
132+
133+
define <2 x i8> @ctlz_to_sub_bw_cttz_vec_splat(<2 x i8> %a0) {
134+
; CHECK-LABEL: define <2 x i8> @ctlz_to_sub_bw_cttz_vec_splat(
135+
; CHECK-SAME: <2 x i8> [[A0:%.*]]) {
136+
; CHECK-NEXT: [[TMP1:%.*]] = call range(i8 0, 9) <2 x i8> @llvm.cttz.v2i8(<2 x i8> [[A0]], i1 false)
137+
; CHECK-NEXT: [[CLZ:%.*]] = sub nuw nsw <2 x i8> splat (i8 8), [[TMP1]]
138+
; CHECK-NEXT: ret <2 x i8> [[CLZ]]
139+
;
140+
%dec = add <2 x i8> %a0, <i8 -1, i8 -1>
141+
%not = xor <2 x i8> %a0, <i8 -1, i8 -1>
142+
%and = and <2 x i8> %dec, %not
143+
%clz = tail call <2 x i8>@llvm.ctlz.v2i8(<2 x i8> %and, i1 false)
144+
ret <2 x i8> %clz
145+
}

0 commit comments

Comments
 (0)