Skip to content

Commit a041c4e

Browse files
committed
[InstCombine] fold zext of masked bit set/clear
This does not solve PR17101, but it is one of the underlying diffs noted here: https://bugs.llvm.org/show_bug.cgi?id=17101#c8 We could ease the one-use checks for the 'clear' (no 'not' op) half of the transform, but I do not know if that asymmetry would make things better or worse. Proofs: https://rise4fun.com/Alive/uVB Name: masked bit set %sh1 = shl i32 1, %y %and = and i32 %sh1, %x %cmp = icmp ne i32 %and, 0 %r = zext i1 %cmp to i32 => %s = lshr i32 %x, %y %r = and i32 %s, 1 Name: masked bit clear %sh1 = shl i32 1, %y %and = and i32 %sh1, %x %cmp = icmp eq i32 %and, 0 %r = zext i1 %cmp to i32 => %xn = xor i32 %x, -1 %s = lshr i32 %xn, %y %r = and i32 %s, 1
1 parent eb5c026 commit a041c4e

File tree

2 files changed

+54
-31
lines changed

2 files changed

+54
-31
lines changed

Diff for: llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp

+17-3
Original file line numberDiff line numberDiff line change
@@ -922,10 +922,24 @@ Instruction *InstCombiner::transformZExtICmp(ICmpInst *Cmp, ZExtInst &Zext,
922922
}
923923
}
924924

925-
// icmp ne A, B is equal to xor A, B when A and B only really have one bit.
926-
// It is also profitable to transform icmp eq into not(xor(A, B)) because that
927-
// may lead to additional simplifications.
928925
if (Cmp->isEquality() && Zext.getType() == Cmp->getOperand(0)->getType()) {
926+
// Test if a bit is clear/set using a shifted-one mask:
927+
// zext (icmp eq (and X, (1 << ShAmt)), 0) --> and (lshr (not X), ShAmt), 1
928+
// zext (icmp ne (and X, (1 << ShAmt)), 0) --> and (lshr X, ShAmt), 1
929+
Value *X, *ShAmt;
930+
if (Cmp->hasOneUse() && match(Cmp->getOperand(1), m_ZeroInt()) &&
931+
match(Cmp->getOperand(0),
932+
m_OneUse(m_c_And(m_Shl(m_One(), m_Value(ShAmt)), m_Value(X))))) {
933+
if (Cmp->getPredicate() == ICmpInst::ICMP_EQ)
934+
X = Builder.CreateNot(X);
935+
Value *Lshr = Builder.CreateLShr(X, ShAmt);
936+
Value *And1 = Builder.CreateAnd(Lshr, ConstantInt::get(X->getType(), 1));
937+
return replaceInstUsesWith(Zext, And1);
938+
}
939+
940+
// icmp ne A, B is equal to xor A, B when A and B only really have one bit.
941+
// It is also profitable to transform icmp eq into not(xor(A, B)) because
942+
// that may lead to additional simplifications.
929943
if (IntegerType *ITy = dyn_cast<IntegerType>(Zext.getType())) {
930944
Value *LHS = Cmp->getOperand(0);
931945
Value *RHS = Cmp->getOperand(1);

Diff for: llvm/test/Transforms/InstCombine/zext.ll

+37-28
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,9 @@ declare void @use32(i32)
177177

178178
define i32 @masked_bit_set(i32 %x, i32 %y) {
179179
; CHECK-LABEL: @masked_bit_set(
180-
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
181-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SH1]], [[X:%.*]]
182-
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[AND]], 0
183-
; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP]] to i32
184-
; CHECK-NEXT: ret i32 [[R]]
180+
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[X:%.*]], [[Y:%.*]]
181+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1
182+
; CHECK-NEXT: ret i32 [[TMP2]]
185183
;
186184
%sh1 = shl i32 1, %y
187185
%and = and i32 %sh1, %x
@@ -192,11 +190,10 @@ define i32 @masked_bit_set(i32 %x, i32 %y) {
192190

193191
define <2 x i32> @masked_bit_clear(<2 x i32> %x, <2 x i32> %y) {
194192
; CHECK-LABEL: @masked_bit_clear(
195-
; CHECK-NEXT: [[SH1:%.*]] = shl <2 x i32> <i32 1, i32 1>, [[Y:%.*]]
196-
; CHECK-NEXT: [[AND:%.*]] = and <2 x i32> [[SH1]], [[X:%.*]]
197-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[AND]], zeroinitializer
198-
; CHECK-NEXT: [[R:%.*]] = zext <2 x i1> [[CMP]] to <2 x i32>
199-
; CHECK-NEXT: ret <2 x i32> [[R]]
193+
; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i32> [[X:%.*]], <i32 -1, i32 -1>
194+
; CHECK-NEXT: [[TMP2:%.*]] = lshr <2 x i32> [[TMP1]], [[Y:%.*]]
195+
; CHECK-NEXT: [[TMP3:%.*]] = and <2 x i32> [[TMP2]], <i32 1, i32 1>
196+
; CHECK-NEXT: ret <2 x i32> [[TMP3]]
200197
;
201198
%sh1 = shl <2 x i32> <i32 1, i32 1>, %y
202199
%and = and <2 x i32> %sh1, %x
@@ -208,11 +205,9 @@ define <2 x i32> @masked_bit_clear(<2 x i32> %x, <2 x i32> %y) {
208205
define <2 x i32> @masked_bit_set_commute(<2 x i32> %px, <2 x i32> %y) {
209206
; CHECK-LABEL: @masked_bit_set_commute(
210207
; CHECK-NEXT: [[X:%.*]] = srem <2 x i32> <i32 42, i32 3>, [[PX:%.*]]
211-
; CHECK-NEXT: [[SH1:%.*]] = shl <2 x i32> <i32 1, i32 1>, [[Y:%.*]]
212-
; CHECK-NEXT: [[AND:%.*]] = and <2 x i32> [[X]], [[SH1]]
213-
; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i32> [[AND]], zeroinitializer
214-
; CHECK-NEXT: [[R:%.*]] = zext <2 x i1> [[CMP]] to <2 x i32>
215-
; CHECK-NEXT: ret <2 x i32> [[R]]
208+
; CHECK-NEXT: [[TMP1:%.*]] = lshr <2 x i32> [[X]], [[Y:%.*]]
209+
; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i32> [[TMP1]], <i32 1, i32 1>
210+
; CHECK-NEXT: ret <2 x i32> [[TMP2]]
216211
;
217212
%x = srem <2 x i32> <i32 42, i32 3>, %px ; thwart complexity-based canonicalization
218213
%sh1 = shl <2 x i32> <i32 1, i32 1>, %y
@@ -225,11 +220,10 @@ define <2 x i32> @masked_bit_set_commute(<2 x i32> %px, <2 x i32> %y) {
225220
define i32 @masked_bit_clear_commute(i32 %px, i32 %y) {
226221
; CHECK-LABEL: @masked_bit_clear_commute(
227222
; CHECK-NEXT: [[X:%.*]] = srem i32 42, [[PX:%.*]]
228-
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
229-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], [[SH1]]
230-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 0
231-
; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP]] to i32
232-
; CHECK-NEXT: ret i32 [[R]]
223+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X]], -1
224+
; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP1]], [[Y:%.*]]
225+
; CHECK-NEXT: [[TMP3:%.*]] = and i32 [[TMP2]], 1
226+
; CHECK-NEXT: ret i32 [[TMP3]]
233227
;
234228
%x = srem i32 42, %px ; thwart complexity-based canonicalization
235229
%sh1 = shl i32 1, %y
@@ -243,10 +237,9 @@ define i32 @masked_bit_set_use1(i32 %x, i32 %y) {
243237
; CHECK-LABEL: @masked_bit_set_use1(
244238
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
245239
; CHECK-NEXT: call void @use32(i32 [[SH1]])
246-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SH1]], [[X:%.*]]
247-
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[AND]], 0
248-
; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP]] to i32
249-
; CHECK-NEXT: ret i32 [[R]]
240+
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[X:%.*]], [[Y]]
241+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1
242+
; CHECK-NEXT: ret i32 [[TMP2]]
250243
;
251244
%sh1 = shl i32 1, %y
252245
call void @use32(i32 %sh1)
@@ -256,6 +249,8 @@ define i32 @masked_bit_set_use1(i32 %x, i32 %y) {
256249
ret i32 %r
257250
}
258251

252+
; Negative test
253+
259254
define i32 @masked_bit_set_use2(i32 %x, i32 %y) {
260255
; CHECK-LABEL: @masked_bit_set_use2(
261256
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
@@ -273,6 +268,8 @@ define i32 @masked_bit_set_use2(i32 %x, i32 %y) {
273268
ret i32 %r
274269
}
275270

271+
; Negative test
272+
276273
define i32 @masked_bit_set_use3(i32 %x, i32 %y) {
277274
; CHECK-LABEL: @masked_bit_set_use3(
278275
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
@@ -294,10 +291,10 @@ define i32 @masked_bit_clear_use1(i32 %x, i32 %y) {
294291
; CHECK-LABEL: @masked_bit_clear_use1(
295292
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
296293
; CHECK-NEXT: call void @use32(i32 [[SH1]])
297-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SH1]], [[X:%.*]]
298-
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 0
299-
; CHECK-NEXT: [[R:%.*]] = zext i1 [[CMP]] to i32
300-
; CHECK-NEXT: ret i32 [[R]]
294+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], -1
295+
; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP1]], [[Y]]
296+
; CHECK-NEXT: [[TMP3:%.*]] = and i32 [[TMP2]], 1
297+
; CHECK-NEXT: ret i32 [[TMP3]]
301298
;
302299
%sh1 = shl i32 1, %y
303300
call void @use32(i32 %sh1)
@@ -307,6 +304,8 @@ define i32 @masked_bit_clear_use1(i32 %x, i32 %y) {
307304
ret i32 %r
308305
}
309306

307+
; Negative test
308+
310309
define i32 @masked_bit_clear_use2(i32 %x, i32 %y) {
311310
; CHECK-LABEL: @masked_bit_clear_use2(
312311
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
@@ -324,6 +323,8 @@ define i32 @masked_bit_clear_use2(i32 %x, i32 %y) {
324323
ret i32 %r
325324
}
326325

326+
; Negative test
327+
327328
define i32 @masked_bit_clear_use3(i32 %x, i32 %y) {
328329
; CHECK-LABEL: @masked_bit_clear_use3(
329330
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
@@ -341,6 +342,8 @@ define i32 @masked_bit_clear_use3(i32 %x, i32 %y) {
341342
ret i32 %r
342343
}
343344

345+
; Negative test
346+
344347
define i32 @masked_bits_set(i32 %x, i32 %y) {
345348
; CHECK-LABEL: @masked_bits_set(
346349
; CHECK-NEXT: [[SH1:%.*]] = shl i32 3, [[Y:%.*]]
@@ -356,6 +359,8 @@ define i32 @masked_bits_set(i32 %x, i32 %y) {
356359
ret i32 %r
357360
}
358361

362+
; Negative test
363+
359364
define i32 @div_bit_set(i32 %x, i32 %y) {
360365
; CHECK-LABEL: @div_bit_set(
361366
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
@@ -371,6 +376,8 @@ define i32 @div_bit_set(i32 %x, i32 %y) {
371376
ret i32 %r
372377
}
373378

379+
; Negative test
380+
374381
define i32 @masked_bit_set_nonzero_cmp(i32 %x, i32 %y) {
375382
; CHECK-LABEL: @masked_bit_set_nonzero_cmp(
376383
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]
@@ -386,6 +393,8 @@ define i32 @masked_bit_set_nonzero_cmp(i32 %x, i32 %y) {
386393
ret i32 %r
387394
}
388395

396+
; Negative test
397+
389398
define i32 @masked_bit_wrong_pred(i32 %x, i32 %y) {
390399
; CHECK-LABEL: @masked_bit_wrong_pred(
391400
; CHECK-NEXT: [[SH1:%.*]] = shl i32 1, [[Y:%.*]]

0 commit comments

Comments
 (0)