Skip to content

Commit 533190a

Browse files
authored
[InstCombine] Canonicalize Bit Testing by Shifting to Bit 0 (#101838)
Implement a new transformation that fold the bit-testing expression (icmp ne (and (lshr V B) 1) 0) to (icmp ne (and V (shl 1 B)) 0) for constant V. This rule already existed for non-constant V and constants other than 1; this restriction to non-constant V has been added in commit c3b2111 to fix an infinite loop. Avoid the infinite loop by allowing constant V only if the shift instruction is an lshr and the constant is 1. Also fold the negated variant of the LHS. This transformation necessitates an adaption of existing tests in `icmp-and-shift.ll` and `load-cmp.ll`. One test in `icmp-and-shift.ll`, which previously was a negative test, now gets folded. Rename it to indicate that it is a positive test. Alive proof: https://alive2.llvm.org/ce/z/vcJJTx Relates to issue #86813.
1 parent 4aff3f6 commit 533190a

File tree

3 files changed

+143
-10
lines changed

3 files changed

+143
-10
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1725,7 +1725,8 @@ Instruction *InstCombinerImpl::foldICmpAndShift(ICmpInst &Cmp,
17251725
// preferable because it allows the C2 << Y expression to be hoisted out of a
17261726
// loop if Y is invariant and X is not.
17271727
if (Shift->hasOneUse() && C1.isZero() && Cmp.isEquality() &&
1728-
!Shift->isArithmeticShift() && !isa<Constant>(Shift->getOperand(0))) {
1728+
!Shift->isArithmeticShift() &&
1729+
((!IsShl && C2.isOne()) || !isa<Constant>(Shift->getOperand(0)))) {
17291730
// Compute C2 << Y.
17301731
Value *NewShift =
17311732
IsShl ? Builder.CreateLShr(And->getOperand(1), Shift->getOperand(1))

llvm/test/Transforms/InstCombine/icmp-and-shift.ll

Lines changed: 137 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -404,11 +404,10 @@ define <2 x i32> @icmp_ne_and_pow2_lshr_pow2_vec(<2 x i32> %0) {
404404
ret <2 x i32> %conv
405405
}
406406

407-
define i32 @icmp_eq_and1_lshr_pow2_negative1(i32 %0) {
408-
; CHECK-LABEL: @icmp_eq_and1_lshr_pow2_negative1(
409-
; CHECK-NEXT: [[LSHR:%.*]] = lshr i32 7, [[TMP0:%.*]]
410-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[LSHR]], 1
411-
; CHECK-NEXT: [[CONV:%.*]] = xor i32 [[AND]], 1
407+
define i32 @icmp_eq_and1_lshr_pow2_minus_one(i32 %0) {
408+
; CHECK-LABEL: @icmp_eq_and1_lshr_pow2_minus_one(
409+
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[TMP0:%.*]], 2
410+
; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32
412411
; CHECK-NEXT: ret i32 [[CONV]]
413412
;
414413
%lshr = lshr i32 7, %0
@@ -606,3 +605,136 @@ define i1 @fold_ne_rhs_fail_shift_not_1s(i8 %x, i8 %yy) {
606605
%r = icmp ne i8 %and, 0
607606
ret i1 %r
608607
}
608+
609+
define i1 @test_shr_and_1_ne_0(i32 %a, i32 %b) {
610+
; CHECK-LABEL: @test_shr_and_1_ne_0(
611+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
612+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], [[A:%.*]]
613+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP2]], 0
614+
; CHECK-NEXT: ret i1 [[CMP]]
615+
;
616+
%shr = lshr i32 %a, %b
617+
%and = and i32 %shr, 1
618+
%cmp = icmp ne i32 %and, 0
619+
ret i1 %cmp
620+
}
621+
622+
define i1 @test_const_shr_and_1_ne_0(i32 %b) {
623+
; CHECK-LABEL: @test_const_shr_and_1_ne_0(
624+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
625+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[TMP1]], 42
626+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[AND]], 0
627+
; CHECK-NEXT: ret i1 [[CMP]]
628+
;
629+
%shr = lshr i32 42, %b
630+
%and = and i32 %shr, 1
631+
%cmp = icmp ne i32 %and, 0
632+
ret i1 %cmp
633+
}
634+
635+
define i1 @test_not_const_shr_and_1_ne_0(i32 %b) {
636+
; CHECK-LABEL: @test_not_const_shr_and_1_ne_0(
637+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
638+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[TMP1]], 42
639+
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[AND]], 0
640+
; CHECK-NEXT: ret i1 [[CMP_NOT]]
641+
;
642+
%shr = lshr i32 42, %b
643+
%and = and i32 %shr, 1
644+
%cmp = icmp eq i32 %and, 0
645+
ret i1 %cmp
646+
}
647+
648+
define i1 @test_const_shr_exact_and_1_ne_0(i32 %b) {
649+
; CHECK-LABEL: @test_const_shr_exact_and_1_ne_0(
650+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
651+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[TMP1]], 42
652+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[AND]], 0
653+
; CHECK-NEXT: ret i1 [[CMP]]
654+
;
655+
%shr = lshr exact i32 42, %b
656+
%and = and i32 %shr, 1
657+
%cmp = icmp ne i32 %and, 0
658+
ret i1 %cmp
659+
}
660+
661+
define i1 @test_const_shr_and_2_ne_0_negative(i32 %b) {
662+
; CHECK-LABEL: @test_const_shr_and_2_ne_0_negative(
663+
; CHECK-NEXT: [[SHR:%.*]] = lshr i32 42, [[B:%.*]]
664+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SHR]], 2
665+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[AND]], 0
666+
; CHECK-NEXT: ret i1 [[CMP]]
667+
;
668+
%shr = lshr i32 42, %b
669+
%and = and i32 %shr, 2
670+
%cmp = icmp eq i32 %and, 0
671+
ret i1 %cmp
672+
}
673+
674+
define <8 x i1> @test_const_shr_and_1_ne_0_v8i8_splat_negative(<8 x i8> %b) {
675+
; CHECK-LABEL: @test_const_shr_and_1_ne_0_v8i8_splat_negative(
676+
; CHECK-NEXT: [[SHR:%.*]] = lshr <8 x i8> <i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42>, [[B:%.*]]
677+
; CHECK-NEXT: [[CMP:%.*]] = trunc <8 x i8> [[SHR]] to <8 x i1>
678+
; CHECK-NEXT: ret <8 x i1> [[CMP]]
679+
;
680+
%shr = lshr <8 x i8> <i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42>, %b
681+
%and = and <8 x i8> %shr, <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>
682+
%cmp = icmp ne <8 x i8> %and, zeroinitializer
683+
ret <8 x i1> %cmp
684+
}
685+
686+
define <8 x i1> @test_const_shr_and_1_ne_0_v8i8_nonsplat_negative(<8 x i8> %b) {
687+
; CHECK-LABEL: @test_const_shr_and_1_ne_0_v8i8_nonsplat_negative(
688+
; CHECK-NEXT: [[SHR:%.*]] = lshr <8 x i8> <i8 42, i8 43, i8 44, i8 45, i8 46, i8 47, i8 48, i8 49>, [[B:%.*]]
689+
; CHECK-NEXT: [[CMP:%.*]] = trunc <8 x i8> [[SHR]] to <8 x i1>
690+
; CHECK-NEXT: ret <8 x i1> [[CMP]]
691+
;
692+
%shr = lshr <8 x i8> <i8 42, i8 43, i8 44, i8 45, i8 46, i8 47, i8 48, i8 49>, %b
693+
%and = and <8 x i8> %shr, <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>
694+
%cmp = icmp ne <8 x i8> %and, zeroinitializer
695+
ret <8 x i1> %cmp
696+
}
697+
698+
define i1 @test_const_shr_and_1_ne_0_i1_negative(i1 %b) {
699+
; CHECK-LABEL: @test_const_shr_and_1_ne_0_i1_negative(
700+
; CHECK-NEXT: ret i1 true
701+
;
702+
%shr = lshr i1 1, %b
703+
%and = and i1 %shr, 1
704+
%cmp = icmp ne i1 %and, 0
705+
ret i1 %cmp
706+
}
707+
708+
define i1 @test_const_shr_and_1_ne_0_multi_use_lshr_negative(i32 %b) {
709+
; CHECK-LABEL: @test_const_shr_and_1_ne_0_multi_use_lshr_negative(
710+
; CHECK-NEXT: [[SHR:%.*]] = lshr i32 42, [[B:%.*]]
711+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SHR]], 1
712+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[AND]], 0
713+
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[SHR]], [[B]]
714+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[CMP1]], [[CMP2]]
715+
; CHECK-NEXT: ret i1 [[RET]]
716+
;
717+
%shr = lshr i32 42, %b
718+
%and = and i32 %shr, 1
719+
%cmp1 = icmp ne i32 %and, 0
720+
%cmp2 = icmp eq i32 %b, %shr
721+
%ret = and i1 %cmp1, %cmp2
722+
ret i1 %ret
723+
}
724+
725+
define i1 @test_const_shr_and_1_ne_0_multi_use_and_negative(i32 %b) {
726+
; CHECK-LABEL: @test_const_shr_and_1_ne_0_multi_use_and_negative(
727+
; CHECK-NEXT: [[SHR:%.*]] = lshr i32 42, [[B:%.*]]
728+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SHR]], 1
729+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i32 [[AND]], 0
730+
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[AND]], [[B]]
731+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[CMP1]], [[CMP2]]
732+
; CHECK-NEXT: ret i1 [[RET]]
733+
;
734+
%shr = lshr i32 42, %b
735+
%and = and i32 %shr, 1
736+
%cmp1 = icmp ne i32 %and, 0
737+
%cmp2 = icmp eq i32 %b, %and
738+
%ret = and i1 %cmp1, %cmp2
739+
ret i1 %ret
740+
}

llvm/test/Transforms/InstCombine/load-cmp.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ define i1 @test3(i32 %X) {
109109

110110
define i1 @test4(i32 %X) {
111111
; CHECK-LABEL: @test4(
112-
; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 933, [[X:%.*]]
113-
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 1
112+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[X:%.*]]
113+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 933
114114
; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[TMP2]], 0
115115
; CHECK-NEXT: ret i1 [[R]]
116116
;
@@ -123,8 +123,8 @@ define i1 @test4(i32 %X) {
123123
define i1 @test4_i16(i16 %X) {
124124
; CHECK-LABEL: @test4_i16(
125125
; CHECK-NEXT: [[TMP1:%.*]] = zext nneg i16 [[X:%.*]] to i32
126-
; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 933, [[TMP1]]
127-
; CHECK-NEXT: [[TMP3:%.*]] = and i32 [[TMP2]], 1
126+
; CHECK-NEXT: [[TMP2:%.*]] = shl nuw i32 1, [[TMP1]]
127+
; CHECK-NEXT: [[TMP3:%.*]] = and i32 [[TMP2]], 933
128128
; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[TMP3]], 0
129129
; CHECK-NEXT: ret i1 [[R]]
130130
;

0 commit comments

Comments
 (0)