Skip to content

Commit c2e7fad

Browse files
authored
[DemandedBits] Support non-constant shift amounts (#148880)
This patch adds support for the shift operators to handle non-constant shift operands. ashr proof -->https://alive2.llvm.org/ce/z/EN-siK lshr proof --> https://alive2.llvm.org/ce/z/eeGzyB shl proof --> https://alive2.llvm.org/ce/z/dpvbkq
1 parent 4eb1a07 commit c2e7fad

File tree

4 files changed

+598
-1
lines changed

4 files changed

+598
-1
lines changed

llvm/lib/Analysis/DemandedBits.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ void DemandedBits::determineLiveOperandBits(
7676
computeKnownBits(V2, Known2, DL, &AC, UserI, &DT);
7777
}
7878
};
79+
auto GetShiftedRange = [&](uint64_t Min, uint64_t Max, bool ShiftLeft) {
80+
auto ShiftF = [ShiftLeft](const APInt &Mask, unsigned ShiftAmnt) {
81+
return ShiftLeft ? Mask.shl(ShiftAmnt) : Mask.lshr(ShiftAmnt);
82+
};
83+
AB = APInt::getZero(BitWidth);
84+
uint64_t LoopRange = Max - Min;
85+
APInt Mask = AOut;
86+
APInt Shifted = AOut; // AOut | (AOut << 1) | ... | (AOut << (ShiftAmnt - 1)
87+
for (unsigned ShiftAmnt = 1; ShiftAmnt <= LoopRange; ShiftAmnt <<= 1) {
88+
if (LoopRange & ShiftAmnt) {
89+
// Account for (LoopRange - ShiftAmnt, LoopRange]
90+
Mask |= ShiftF(Shifted, LoopRange - ShiftAmnt + 1);
91+
// Clears the low bit.
92+
LoopRange -= ShiftAmnt;
93+
}
94+
// [0, ShiftAmnt) -> [0, ShiftAmnt * 2)
95+
Shifted |= ShiftF(Shifted, ShiftAmnt);
96+
}
97+
AB = ShiftF(Mask, Min);
98+
};
7999

80100
switch (UserI->getOpcode()) {
81101
default: break;
@@ -183,6 +203,17 @@ void DemandedBits::determineLiveOperandBits(
183203
AB |= APInt::getHighBitsSet(BitWidth, ShiftAmt+1);
184204
else if (S->hasNoUnsignedWrap())
185205
AB |= APInt::getHighBitsSet(BitWidth, ShiftAmt);
206+
} else {
207+
ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr);
208+
uint64_t Min = Known.getMinValue().getLimitedValue(BitWidth - 1);
209+
uint64_t Max = Known.getMaxValue().getLimitedValue(BitWidth - 1);
210+
// similar to Lshr case
211+
GetShiftedRange(Min, Max, /*ShiftLeft=*/false);
212+
const auto *S = cast<ShlOperator>(UserI);
213+
if (S->hasNoSignedWrap())
214+
AB |= APInt::getHighBitsSet(BitWidth, Max + 1);
215+
else if (S->hasNoUnsignedWrap())
216+
AB |= APInt::getHighBitsSet(BitWidth, Max);
186217
}
187218
}
188219
break;
@@ -197,6 +228,24 @@ void DemandedBits::determineLiveOperandBits(
197228
// (they must be zero).
198229
if (cast<LShrOperator>(UserI)->isExact())
199230
AB |= APInt::getLowBitsSet(BitWidth, ShiftAmt);
231+
} else {
232+
ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr);
233+
uint64_t Min = Known.getMinValue().getLimitedValue(BitWidth - 1);
234+
uint64_t Max = Known.getMaxValue().getLimitedValue(BitWidth - 1);
235+
// Suppose AOut == 0b0000 0001
236+
// [min, max] = [1, 3]
237+
// iteration 1 shift by 1 mask is 0b0000 0011
238+
// iteration 2 shift by 2 mask is 0b0000 1111
239+
// iteration 3, shiftAmnt = 4 > max - min, we stop.
240+
//
241+
// After the iterations we need one more shift by min,
242+
// to move from 0b0000 1111 to --> 0b0001 1110.
243+
// The loop populates the mask relative to (0,...,max-min),
244+
// but we need coverage from (min, max).
245+
// This is why the shift by min is needed.
246+
GetShiftedRange(Min, Max, /*ShiftLeft=*/true);
247+
if (cast<LShrOperator>(UserI)->isExact())
248+
AB |= APInt::getLowBitsSet(BitWidth, Max);
200249
}
201250
}
202251
break;
@@ -217,6 +266,26 @@ void DemandedBits::determineLiveOperandBits(
217266
// (they must be zero).
218267
if (cast<AShrOperator>(UserI)->isExact())
219268
AB |= APInt::getLowBitsSet(BitWidth, ShiftAmt);
269+
} else {
270+
ComputeKnownBits(BitWidth, UserI->getOperand(1), nullptr);
271+
uint64_t Min = Known.getMinValue().getLimitedValue(BitWidth - 1);
272+
uint64_t Max = Known.getMaxValue().getLimitedValue(BitWidth - 1);
273+
GetShiftedRange(Min, Max, /*ShiftLeft=*/true);
274+
if (Max &&
275+
(AOut & APInt::getHighBitsSet(BitWidth, Max)).getBoolValue()) {
276+
// Suppose AOut = 0011 1100
277+
// [min, max] = [1, 3]
278+
// ShiftAmount = 1 : Mask is 1000 0000
279+
// ShiftAmount = 2 : Mask is 1100 0000
280+
// ShiftAmount = 3 : Mask is 1110 0000
281+
// The Mask with Max covers every case in [min, max],
282+
// so we are done
283+
AB.setSignBit();
284+
}
285+
// If the shift is exact, then the low bits are not dead
286+
// (they must be zero).
287+
if (cast<AShrOperator>(UserI)->isExact())
288+
AB |= APInt::getLowBitsSet(BitWidth, Max);
220289
}
221290
}
222291
break;
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
; RUN: opt -S -disable-output -passes="print<demanded-bits>" < %s 2>&1 | FileCheck %s
2+
3+
define i8 @test_ashr_const_amount_4(i32 %a) {
4+
; CHECK-LABEL: 'test_ashr_const_amount_4'
5+
; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 4
6+
; CHECK-DAG: DemandedBits: 0xff0 for %a in %ashr = ashr i32 %a, 4
7+
; CHECK-DAG: DemandedBits: 0xffffffff for 4 in %ashr = ashr i32 %a, 4
8+
; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8
9+
; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8
10+
;
11+
%ashr = ashr i32 %a, 4
12+
%ashr.t = trunc i32 %ashr to i8
13+
ret i8 %ashr.t
14+
}
15+
16+
define i8 @test_ashr_const_amount_5(i32 %a) {
17+
; CHECK-LABEL: 'test_ashr_const_amount_5'
18+
; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 5
19+
; CHECK-DAG: DemandedBits: 0x1fe0 for %a in %ashr = ashr i32 %a, 5
20+
; CHECK-DAG: DemandedBits: 0xffffffff for 5 in %ashr = ashr i32 %a, 5
21+
; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8
22+
; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8
23+
;
24+
%ashr = ashr i32 %a, 5
25+
%ashr.t = trunc i32 %ashr to i8
26+
ret i8 %ashr.t
27+
}
28+
29+
define i8 @test_ashr_const_amount_8(i32 %a) {
30+
; CHECK-LABEL: 'test_ashr_const_amount_8'
31+
; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 8
32+
; CHECK-DAG: DemandedBits: 0xff00 for %a in %ashr = ashr i32 %a, 8
33+
; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %ashr = ashr i32 %a, 8
34+
; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8
35+
; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8
36+
;
37+
%ashr = ashr i32 %a, 8
38+
%ashr.t = trunc i32 %ashr to i8
39+
ret i8 %ashr.t
40+
}
41+
42+
define i8 @test_ashr_const_amount_9(i32 %a) {
43+
44+
; CHECK-LABEL: 'test_ashr_const_amount_9'
45+
; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8
46+
; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8
47+
; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, 8
48+
; CHECK-DAG: DemandedBits: 0xff00 for %a in %ashr = ashr i32 %a, 8
49+
; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %ashr = ashr i32 %a, 8
50+
;
51+
%ashr = ashr i32 %a, 8
52+
%ashr.t = trunc i32 %ashr to i8
53+
ret i8 %ashr.t
54+
}
55+
56+
define i8 @test_ashr(i32 %a, i32 %b) {
57+
; CHECK-LABEL: 'test_ashr'
58+
; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, %b
59+
; CHECK-DAG: DemandedBits: 0xffffffff for %a in %ashr = ashr i32 %a, %b
60+
; CHECK-DAG: DemandedBits: 0xffffffff for %b in %ashr = ashr i32 %a, %b
61+
; CHECK-DAG: DemandedBits: 0xff for %ashr.t = trunc i32 %ashr to i8
62+
; CHECK-DAG: DemandedBits: 0xff for %ashr in %ashr.t = trunc i32 %ashr to i8
63+
;
64+
%ashr = ashr i32 %a, %b
65+
%ashr.t = trunc i32 %ashr to i8
66+
ret i8 %ashr.t
67+
}
68+
69+
define i8 @test_ashr_range_1(i32 %a, i32 %b) {
70+
; CHECK-LABEL: 'test_ashr_range_1'
71+
; CHECK-DAG: DemandedBits: 0xff for %shl.t = trunc i32 %ashr to i8
72+
; CHECK-DAG: DemandedBits: 0xff for %ashr in %shl.t = trunc i32 %ashr to i8
73+
; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3
74+
; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3
75+
; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3
76+
; CHECK-DAG: DemandedBits: 0xff for %ashr = ashr i32 %a, %b2
77+
; CHECK-DAG: DemandedBits: 0x7ff for %a in %ashr = ashr i32 %a, %b2
78+
; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %ashr = ashr i32 %a, %b2
79+
;
80+
%b2 = and i32 %b, 3
81+
%ashr = ashr i32 %a, %b2
82+
%shl.t = trunc i32 %ashr to i8
83+
ret i8 %shl.t
84+
}
85+
86+
define i32 @test_ashr_range_2(i32 %a, i32 %b) {
87+
; CHECK-LABEL: 'test_ashr_range_2'
88+
; CHECK-DAG: DemandedBits: 0xffffffff for %b2 = and i32 %b, 3
89+
; CHECK-DAG: DemandedBits: 0x3 for %b in %b2 = and i32 %b, 3
90+
; CHECK-DAG: DemandedBits: 0xffffffff for 3 in %b2 = and i32 %b, 3
91+
; CHECK-DAG: DemandedBits: 0xffffffff for %ashr = ashr i32 %a, %b2
92+
; CHECK-DAG: DemandedBits: 0xffffffff for %a in %ashr = ashr i32 %a, %b2
93+
; CHECK-DAG: DemandedBits: 0xffffffff for %b2 in %ashr = ashr i32 %a, %b2
94+
;
95+
%b2 = and i32 %b, 3
96+
%ashr = ashr i32 %a, %b2
97+
ret i32 %ashr
98+
}
99+
100+
define i32 @test_ashr_range_3(i32 %a, i32 %b) {
101+
; CHECK-LABEL: 'test_ashr_range_3'
102+
; CHECK-DAG: DemandedBits: 0xffff for %ashr = ashr i32 %a, %b
103+
; CHECK-DAG: DemandedBits: 0xffffffff for %a in %ashr = ashr i32 %a, %b
104+
; CHECK-DAG: DemandedBits: 0xffffffff for %b in %ashr = ashr i32 %a, %b
105+
; CHECK-DAG: DemandedBits: 0xffffffff for %shl = shl i32 %ashr, 16
106+
; CHECK-DAG: DemandedBits: 0xffff for %ashr in %shl = shl i32 %ashr, 16
107+
; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %shl = shl i32 %ashr, 16
108+
;
109+
%ashr = ashr i32 %a, %b
110+
%shl = shl i32 %ashr, 16
111+
ret i32 %shl
112+
}
113+
define i32 @test_ashr_range_4(i32 %a, i32 %b) {
114+
; CHECK-LABEL: 'test_ashr_range_4'
115+
; CHECK-DAG: DemandedBits: 0xffffffff for %shr = lshr i32 %ashr, 8
116+
; CHECK-DAG: DemandedBits: 0xffffff00 for %ashr in %shr = lshr i32 %ashr, 8
117+
; CHECK-DAG: DemandedBits: 0xffffffff for 8 in %shr = lshr i32 %ashr, 8
118+
; CHECK-DAG: DemandedBits: 0xffffff00 for %ashr = ashr i32 %a, %b
119+
; CHECK-DAG: DemandedBits: 0xffffff00 for %a in %ashr = ashr i32 %a, %b
120+
; CHECK-DAG: DemandedBits: 0xffffffff for %b in %ashr = ashr i32 %a, %b
121+
%ashr = ashr i32 %a, %b
122+
%shr = lshr i32 %ashr, 8
123+
ret i32 %shr
124+
}
125+
126+
define i32 @test_ashr_range_5(i32 %a, i32 %b) {
127+
; CHECK-LABEL: 'test_ashr_range_5'
128+
; CHECK-DAG: DemandedBits: 0xffffffff for %2 = and i32 %1, 255
129+
; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = and i32 %1, 255
130+
; CHECK-DAG: DemandedBits: 0xffffffff for 255 in %2 = and i32 %1, 255
131+
; CHECK-DAG: DemandedBits: 0xff for %1 = ashr i32 %a, %b
132+
; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = ashr i32 %a, %b
133+
; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = ashr i32 %a, %b
134+
;
135+
%1 = ashr i32 %a, %b
136+
%2 = and i32 %1, 255
137+
ret i32 %2
138+
}
139+
140+
define i32 @test_ashr_range_6(i32 %a, i32 %b) {
141+
; CHECK-LABEL: 'test_ashr_range_6'
142+
; CHECK-DAG: DemandedBits: 0xffff0000 for %lshr.1 = ashr i32 %a, %b
143+
; CHECK-DAG: DemandedBits: 0xffff0000 for %a in %lshr.1 = ashr i32 %a, %b
144+
; CHECK-DAG: DemandedBits: 0xffffffff for %b in %lshr.1 = ashr i32 %a, %b
145+
; CHECK-DAG: DemandedBits: 0xffffffff for %lshr.2 = ashr i32 %lshr.1, 16
146+
; CHECK-DAG: DemandedBits: 0xffff0000 for %lshr.1 in %lshr.2 = ashr i32 %lshr.1, 16
147+
; CHECK-DAG: DemandedBits: 0xffffffff for 16 in %lshr.2 = ashr i32 %lshr.1, 16
148+
;
149+
%lshr.1 = ashr i32 %a, %b
150+
%lshr.2 = ashr i32 %lshr.1, 16
151+
ret i32 %lshr.2
152+
}
153+
154+
define i8 @test_ashr_var_amount(i32 %a, i32 %b){
155+
; CHECK-LABEL: 'test_ashr_var_amount'
156+
; CHECK-DAG: DemandedBits: 0xff for %4 = ashr i32 %1, %3
157+
; CHECK-DAG: DemandedBits: 0xffffffff for %1 in %4 = ashr i32 %1, %3
158+
; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = ashr i32 %1, %3
159+
; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8
160+
; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8
161+
; CHECK-DAG: DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b
162+
; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b
163+
; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b
164+
; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32
165+
; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32
166+
; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8
167+
; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8
168+
;
169+
%1 = add nsw i32 %a, %b
170+
%2 = trunc i32 %1 to i8
171+
%3 = zext i8 %2 to i32
172+
%4 = ashr i32 %1, %3
173+
%5 = trunc i32 %4 to i8
174+
ret i8 %5
175+
}
176+
177+
define i8 @test_ashr_var_amount_nsw(i32 %a, i32 %b){
178+
; CHECK-LABEL 'test_ashr_var_amount_nsw'
179+
; CHECK-DAG: DemandedBits: 0xff for %5 = trunc i32 %4 to i8
180+
; CHECK-DAG: DemandedBits: 0xff for %4 in %5 = trunc i32 %4 to i8
181+
; CHECK-DAG: DemandedBits: 0xffffffff for %1 = add nsw i32 %a, %b
182+
; CHECK-DAG: DemandedBits: 0xffffffff for %a in %1 = add nsw i32 %a, %b
183+
; CHECK-DAG: DemandedBits: 0xffffffff for %b in %1 = add nsw i32 %a, %b
184+
; CHECK-DAG: DemandedBits: 0xff for %2 = trunc i32 %1 to i8
185+
; CHECK-DAG: DemandedBits: 0xff for %1 in %2 = trunc i32 %1 to i8
186+
; CHECK-DAG: DemandedBits: 0xffffffff for %3 = zext i8 %2 to i32
187+
; CHECK-DAG: DemandedBits: 0xff for %2 in %3 = zext i8 %2 to i32
188+
; CHECK-DAG: DemandedBits: 0xff for %4 = ashr exact i32 %1, %3
189+
; CHECK-DAG: DemandedBits: 0xffffffff for %1 in %4 = ashr exact i32 %1, %3
190+
; CHECK-DAG: DemandedBits: 0xffffffff for %3 in %4 = ashr exact i32 %1, %3
191+
;
192+
%1 = add nsw i32 %a, %b
193+
%2 = trunc i32 %1 to i8
194+
%3 = zext i8 %2 to i32
195+
%4 = ashr exact i32 %1, %3
196+
%5 = trunc i32 %4 to i8
197+
ret i8 %5
198+
}

0 commit comments

Comments
 (0)