Skip to content

Commit 170a21e

Browse files
authored
[InstCombine] Extend Fold of Zero-extended Bit Test (#102100)
Previously, (zext (icmp ne (and X, (1 << ShAmt)), 0)) has only been folded if the bit width of X and the result were equal. Use a trunc or zext instruction to also support other bit widths. This is a follow-up to commit 533190a, which introduced a regression: (zext (icmp ne (and (lshr X ShAmt) 1) 0)) is not folded any longer to (zext/trunc (and (lshr X ShAmt) 1)) since the commit introduced the fold of (icmp ne (and (lshr X ShAmt) 1) 0) to (icmp ne (and X (1 << ShAmt)) 0). The change introduced by this commit restores this fold. Alive proof: https://alive2.llvm.org/ce/z/MFkNXs Relates to issue #86813 and pull request #101838.
1 parent 4f07508 commit 170a21e

File tree

2 files changed

+122
-6
lines changed

2 files changed

+122
-6
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -985,19 +985,26 @@ Instruction *InstCombinerImpl::transformZExtICmp(ICmpInst *Cmp,
985985
}
986986
}
987987

988-
if (Cmp->isEquality() && Zext.getType() == Cmp->getOperand(0)->getType()) {
988+
if (Cmp->isEquality()) {
989989
// Test if a bit is clear/set using a shifted-one mask:
990990
// zext (icmp eq (and X, (1 << ShAmt)), 0) --> and (lshr (not X), ShAmt), 1
991991
// zext (icmp ne (and X, (1 << ShAmt)), 0) --> and (lshr X, ShAmt), 1
992992
Value *X, *ShAmt;
993993
if (Cmp->hasOneUse() && match(Cmp->getOperand(1), m_ZeroInt()) &&
994994
match(Cmp->getOperand(0),
995995
m_OneUse(m_c_And(m_Shl(m_One(), m_Value(ShAmt)), m_Value(X))))) {
996-
if (Cmp->getPredicate() == ICmpInst::ICMP_EQ)
997-
X = Builder.CreateNot(X);
998-
Value *Lshr = Builder.CreateLShr(X, ShAmt);
999-
Value *And1 = Builder.CreateAnd(Lshr, ConstantInt::get(X->getType(), 1));
1000-
return replaceInstUsesWith(Zext, And1);
996+
auto *And = cast<BinaryOperator>(Cmp->getOperand(0));
997+
Value *Shift = And->getOperand(X == And->getOperand(0) ? 1 : 0);
998+
if (Zext.getType() == And->getType() ||
999+
Cmp->getPredicate() != ICmpInst::ICMP_EQ || Shift->hasOneUse()) {
1000+
if (Cmp->getPredicate() == ICmpInst::ICMP_EQ)
1001+
X = Builder.CreateNot(X);
1002+
Value *Lshr = Builder.CreateLShr(X, ShAmt);
1003+
Value *And1 =
1004+
Builder.CreateAnd(Lshr, ConstantInt::get(X->getType(), 1));
1005+
return replaceInstUsesWith(
1006+
Zext, Builder.CreateZExtOrTrunc(And1, Zext.getType()));
1007+
}
10011008
}
10021009
}
10031010

llvm/test/Transforms/InstCombine/zext.ll

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,115 @@ define i32 @zext_or_masked_bit_test_uses(i32 %a, i32 %b, i32 %x) {
454454
ret i32 %z
455455
}
456456

457+
define i16 @zext_masked_bit_zero_to_smaller_bitwidth(i32 %a, i32 %b) {
458+
; CHECK-LABEL: @zext_masked_bit_zero_to_smaller_bitwidth(
459+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[A:%.*]], -1
460+
; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP1]], [[B:%.*]]
461+
; CHECK-NEXT: [[TMP3:%.*]] = trunc i32 [[TMP2]] to i16
462+
; CHECK-NEXT: [[Z:%.*]] = and i16 [[TMP3]], 1
463+
; CHECK-NEXT: ret i16 [[Z]]
464+
;
465+
%shl = shl i32 1, %b
466+
%and = and i32 %shl, %a
467+
%cmp = icmp eq i32 %and, 0
468+
%z = zext i1 %cmp to i16
469+
ret i16 %z
470+
}
471+
472+
define <4 x i16> @zext_masked_bit_zero_to_smaller_bitwidth_v4i32(<4 x i32> %a, <4 x i32> %b) {
473+
; CHECK-LABEL: @zext_masked_bit_zero_to_smaller_bitwidth_v4i32(
474+
; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i32> [[A:%.*]], <i32 -1, i32 -1, i32 -1, i32 -1>
475+
; CHECK-NEXT: [[TMP2:%.*]] = lshr <4 x i32> [[TMP1]], [[B:%.*]]
476+
; CHECK-NEXT: [[TMP3:%.*]] = trunc <4 x i32> [[TMP2]] to <4 x i16>
477+
; CHECK-NEXT: [[Z:%.*]] = and <4 x i16> [[TMP3]], <i16 1, i16 1, i16 1, i16 1>
478+
; CHECK-NEXT: ret <4 x i16> [[Z]]
479+
;
480+
%shl = shl <4 x i32> <i32 1, i32 1, i32 1, i32 1>, %b
481+
%and = and <4 x i32> %shl, %a
482+
%cmp = icmp eq <4 x i32> %and, <i32 0, i32 0, i32 0, i32 0>
483+
%z = zext <4 x i1> %cmp to <4 x i16>
484+
ret <4 x i16> %z
485+
}
486+
487+
; Negative test
488+
define i16 @zext_masked_bit_zero_to_smaller_bitwidth_multi_use_shl(i32 %a, i32 %b) {
489+
; CHECK-LABEL: @zext_masked_bit_zero_to_smaller_bitwidth_multi_use_shl(
490+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i32 1, [[B:%.*]]
491+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SHL]], [[A:%.*]]
492+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 0
493+
; CHECK-NEXT: [[Z:%.*]] = zext i1 [[CMP]] to i16
494+
; CHECK-NEXT: call void @use32(i32 [[SHL]])
495+
; CHECK-NEXT: ret i16 [[Z]]
496+
;
497+
%shl = shl i32 1, %b
498+
%and = and i32 %shl, %a
499+
%cmp = icmp eq i32 %and, 0
500+
%z = zext i1 %cmp to i16
501+
call void @use32(i32 %shl)
502+
ret i16 %z
503+
}
504+
505+
define i16 @zext_masked_bit_nonzero_to_smaller_bitwidth(i32 %a, i32 %b) {
506+
; CHECK-LABEL: @zext_masked_bit_nonzero_to_smaller_bitwidth(
507+
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[A:%.*]], [[B:%.*]]
508+
; CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16
509+
; CHECK-NEXT: [[Z:%.*]] = and i16 [[TMP2]], 1
510+
; CHECK-NEXT: ret i16 [[Z]]
511+
;
512+
%shl = shl i32 1, %b
513+
%and = and i32 %shl, %a
514+
%cmp = icmp ne i32 %and, 0
515+
%z = zext i1 %cmp to i16
516+
ret i16 %z
517+
}
518+
519+
define i16 @zext_masked_bit_nonzero_to_smaller_bitwidth_multi_use_shl(i32 %a, i32 %b) {
520+
; CHECK-LABEL: @zext_masked_bit_nonzero_to_smaller_bitwidth_multi_use_shl(
521+
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i32 1, [[B:%.*]]
522+
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[A:%.*]], [[B]]
523+
; CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16
524+
; CHECK-NEXT: [[Z:%.*]] = and i16 [[TMP2]], 1
525+
; CHECK-NEXT: call void @use32(i32 [[SHL]])
526+
; CHECK-NEXT: ret i16 [[Z]]
527+
;
528+
%shl = shl i32 1, %b
529+
%and = and i32 %shl, %a
530+
%cmp = icmp ne i32 %and, 0
531+
%z = zext i1 %cmp to i16
532+
call void @use32(i32 %shl)
533+
ret i16 %z
534+
}
535+
536+
define i64 @zext_masked_bit_zero_to_larger_bitwidth(i32 %a, i32 %b) {
537+
; CHECK-LABEL: @zext_masked_bit_zero_to_larger_bitwidth(
538+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[A:%.*]], -1
539+
; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[TMP1]], [[B:%.*]]
540+
; CHECK-NEXT: [[TMP3:%.*]] = and i32 [[TMP2]], 1
541+
; CHECK-NEXT: [[Z:%.*]] = zext nneg i32 [[TMP3]] to i64
542+
; CHECK-NEXT: ret i64 [[Z]]
543+
;
544+
%shl = shl i32 1, %b
545+
%and = and i32 %shl, %a
546+
%cmp = icmp eq i32 %and, 0
547+
%z = zext i1 %cmp to i64
548+
ret i64 %z
549+
}
550+
551+
define <4 x i64> @zext_masked_bit_zero_to_larger_bitwidth_v4i32(<4 x i32> %a, <4 x i32> %b) {
552+
; CHECK-LABEL: @zext_masked_bit_zero_to_larger_bitwidth_v4i32(
553+
; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i32> [[A:%.*]], <i32 -1, i32 -1, i32 -1, i32 -1>
554+
; CHECK-NEXT: [[TMP2:%.*]] = lshr <4 x i32> [[TMP1]], [[B:%.*]]
555+
; CHECK-NEXT: [[TMP3:%.*]] = and <4 x i32> [[TMP2]], <i32 1, i32 1, i32 1, i32 1>
556+
; CHECK-NEXT: [[Z:%.*]] = zext nneg <4 x i32> [[TMP3]] to <4 x i64>
557+
; CHECK-NEXT: ret <4 x i64> [[Z]]
558+
;
559+
%shl = shl <4 x i32> <i32 1, i32 1, i32 1, i32 1>, %b
560+
%and = and <4 x i32> %shl, %a
561+
%cmp = icmp eq <4 x i32> %and, <i32 0, i32 0, i32 0, i32 0>
562+
%z = zext <4 x i1> %cmp to <4 x i64>
563+
ret <4 x i64> %z
564+
}
565+
457566
define i32 @notneg_zext_wider(i8 %x) {
458567
; CHECK-LABEL: @notneg_zext_wider(
459568
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], -1

0 commit comments

Comments
 (0)