Skip to content

Commit ba7461f

Browse files
committed
[InstCombine] Canonicalize Bit Testing
Implement two new transformations that fold the following common ways to test if the B-th bit is set in an integer V: - (icmp ne (and (lshr V B) 1) 0) --> (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. - (icmp slt (shl V (sub (bw-1) B)) 0) --> (icmp ne (and V (shl 1 B)) 0) Also fold negated variants 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 proofs: - https://alive2.llvm.org/ce/z/vcJJTx - https://alive2.llvm.org/ce/z/5ic_qe Fixes 86813.
1 parent f191143 commit ba7461f

File tree

3 files changed

+60
-47
lines changed

3 files changed

+60
-47
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 19 additions & 5 deletions
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))
@@ -2304,19 +2305,32 @@ Instruction *InstCombinerImpl::foldICmpShlConstant(ICmpInst &Cmp,
23042305
if (C.isZero() || (Pred == ICmpInst::ICMP_SGT ? C.isAllOnes() : C.isOne()))
23052306
return new ICmpInst(Pred, Shl->getOperand(0), Cmp.getOperand(1));
23062307

2308+
unsigned TypeBits = C.getBitWidth();
2309+
Value *X = Shl->getOperand(0);
2310+
Type *ShType = Shl->getType();
2311+
2312+
// (icmp slt (shl X, (sub bw, Y)), 0) --> (icmp ne (and X, (shl 1, Y)), 0)
2313+
// (icmp sgt (shl X, (sub bw, Y)), -1) --> (icmp eq (and X, (shl 1, Y)), 0)
2314+
if (Value * Y;
2315+
Shl->hasOneUse() &&
2316+
(Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SGT) &&
2317+
(Pred == ICmpInst::ICMP_SLT ? C.isZero() : C.isAllOnes()) &&
2318+
match(Shl->getOperand(1),
2319+
m_OneUse(m_Sub(m_SpecificInt(TypeBits - 1), m_Value(Y)))))
2320+
return new ICmpInst(
2321+
Pred == ICmpInst::ICMP_SLT ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ,
2322+
Builder.CreateAnd(X, Builder.CreateShl(ConstantInt::get(ShType, 1), Y)),
2323+
ConstantInt::get(ShType, 0));
2324+
23072325
const APInt *ShiftAmt;
23082326
if (!match(Shl->getOperand(1), m_APInt(ShiftAmt)))
23092327
return foldICmpShlOne(Cmp, Shl, C);
23102328

23112329
// Check that the shift amount is in range. If not, don't perform undefined
23122330
// shifts. When the shift is visited, it will be simplified.
2313-
unsigned TypeBits = C.getBitWidth();
23142331
if (ShiftAmt->uge(TypeBits))
23152332
return nullptr;
23162333

2317-
Value *X = Shl->getOperand(0);
2318-
Type *ShType = Shl->getType();
2319-
23202334
// NSW guarantees that we are only shifting out sign bits from the high bits,
23212335
// so we can ASHR the compare constant without needing a mask and eliminate
23222336
// the shift.

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

Lines changed: 37 additions & 38 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
@@ -622,8 +621,8 @@ define i1 @test_shr_and_1_ne_0(i32 %a, i32 %b) {
622621

623622
define i1 @test_const_shr_and_1_ne_0(i32 %b) {
624623
; CHECK-LABEL: @test_const_shr_and_1_ne_0(
625-
; CHECK-NEXT: [[SHR:%.*]] = lshr i32 42, [[B:%.*]]
626-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SHR]], 1
624+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
625+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[TMP1]], 42
627626
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[AND]], 0
628627
; CHECK-NEXT: ret i1 [[CMP]]
629628
;
@@ -635,8 +634,8 @@ define i1 @test_const_shr_and_1_ne_0(i32 %b) {
635634

636635
define i1 @test_not_const_shr_and_1_ne_0(i32 %b) {
637636
; CHECK-LABEL: @test_not_const_shr_and_1_ne_0(
638-
; CHECK-NEXT: [[SHR:%.*]] = lshr i32 42, [[B:%.*]]
639-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SHR]], 1
637+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
638+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[TMP1]], 42
640639
; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[AND]], 0
641640
; CHECK-NEXT: ret i1 [[CMP_NOT]]
642641
;
@@ -648,8 +647,8 @@ define i1 @test_not_const_shr_and_1_ne_0(i32 %b) {
648647

649648
define i1 @test_const_shr_exact_and_1_ne_0(i32 %b) {
650649
; CHECK-LABEL: @test_const_shr_exact_and_1_ne_0(
651-
; CHECK-NEXT: [[SHR:%.*]] = lshr exact i32 42, [[B:%.*]]
652-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[SHR]], 1
650+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
651+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[TMP1]], 42
653652
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[AND]], 0
654653
; CHECK-NEXT: ret i1 [[CMP]]
655654
;
@@ -742,9 +741,9 @@ define i1 @test_const_shr_and_1_ne_0_multi_use_and_negative(i32 %b) {
742741

743742
define i1 @test_shl_sub_bw_minus_1_slt_0(i32 %a, i32 %b) {
744743
; CHECK-LABEL: @test_shl_sub_bw_minus_1_slt_0(
745-
; CHECK-NEXT: [[SUB:%.*]] = sub i32 31, [[B:%.*]]
746-
; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[A:%.*]], [[SUB]]
747-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[SHL]], 0
744+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
745+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], [[A:%.*]]
746+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP2]], 0
748747
; CHECK-NEXT: ret i1 [[CMP]]
749748
;
750749
%sub = sub i32 31, %b
@@ -755,9 +754,9 @@ define i1 @test_shl_sub_bw_minus_1_slt_0(i32 %a, i32 %b) {
755754

756755
define i1 @test_const_shl_sub_bw_minus_1_slt_0(i32 %b) {
757756
; CHECK-LABEL: @test_const_shl_sub_bw_minus_1_slt_0(
758-
; CHECK-NEXT: [[SUB:%.*]] = sub i32 31, [[B:%.*]]
759-
; CHECK-NEXT: [[SHL:%.*]] = shl i32 42, [[SUB]]
760-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[SHL]], 0
757+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
758+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 42
759+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP2]], 0
761760
; CHECK-NEXT: ret i1 [[CMP]]
762761
;
763762
%sub = sub i32 31, %b
@@ -768,9 +767,9 @@ define i1 @test_const_shl_sub_bw_minus_1_slt_0(i32 %b) {
768767

769768
define i1 @test_not_shl_sub_bw_minus_1_slt_0(i32 %a, i32 %b) {
770769
; CHECK-LABEL: @test_not_shl_sub_bw_minus_1_slt_0(
771-
; CHECK-NEXT: [[SUB:%.*]] = sub i32 31, [[B:%.*]]
772-
; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[A:%.*]], [[SUB]]
773-
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[SHL]], -1
770+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
771+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], [[A:%.*]]
772+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP2]], 0
774773
; CHECK-NEXT: ret i1 [[CMP]]
775774
;
776775
%sub = sub i32 31, %b
@@ -781,9 +780,9 @@ define i1 @test_not_shl_sub_bw_minus_1_slt_0(i32 %a, i32 %b) {
781780

782781
define i1 @test_shl_nuw_sub_bw_minus_1_slt_0(i32 %a, i32 %b) {
783782
; CHECK-LABEL: @test_shl_nuw_sub_bw_minus_1_slt_0(
784-
; CHECK-NEXT: [[SUB:%.*]] = sub i32 31, [[B:%.*]]
785-
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i32 [[A:%.*]], [[SUB]]
786-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[SHL]], 0
783+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
784+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], [[A:%.*]]
785+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[TMP2]], 0
787786
; CHECK-NEXT: ret i1 [[CMP]]
788787
;
789788
%sub = sub i32 31, %b
@@ -794,9 +793,9 @@ define i1 @test_shl_nuw_sub_bw_minus_1_slt_0(i32 %a, i32 %b) {
794793

795794
define i1 @test_not_const_shl_sub_bw_minus_1_slt_0(i32 %b) {
796795
; CHECK-LABEL: @test_not_const_shl_sub_bw_minus_1_slt_0(
797-
; CHECK-NEXT: [[SUB:%.*]] = sub i32 31, [[B:%.*]]
798-
; CHECK-NEXT: [[SHL:%.*]] = shl i32 42, [[SUB]]
799-
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[SHL]], -1
796+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw i32 1, [[B:%.*]]
797+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 42
798+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP2]], 0
800799
; CHECK-NEXT: ret i1 [[CMP]]
801800
;
802801
%sub = sub i32 31, %b
@@ -807,9 +806,9 @@ define i1 @test_not_const_shl_sub_bw_minus_1_slt_0(i32 %b) {
807806

808807
define <8 x i1> @test_shl_sub_bw_minus_1_slt_0_v8i8(<8 x i8> %a, <8 x i8> %b) {
809808
; CHECK-LABEL: @test_shl_sub_bw_minus_1_slt_0_v8i8(
810-
; CHECK-NEXT: [[SUB:%.*]] = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, [[B:%.*]]
811-
; CHECK-NEXT: [[SHL:%.*]] = shl <8 x i8> [[A:%.*]], [[SUB]]
812-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt <8 x i8> [[SHL]], zeroinitializer
809+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw <8 x i8> <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>, [[B:%.*]]
810+
; CHECK-NEXT: [[TMP2:%.*]] = and <8 x i8> [[TMP1]], [[A:%.*]]
811+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne <8 x i8> [[TMP2]], zeroinitializer
813812
; CHECK-NEXT: ret <8 x i1> [[CMP]]
814813
;
815814
%sub = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, %b
@@ -820,9 +819,9 @@ define <8 x i1> @test_shl_sub_bw_minus_1_slt_0_v8i8(<8 x i8> %a, <8 x i8> %b) {
820819

821820
define <8 x i1> @test_const_shl_sub_bw_minus_1_slt_0_v8i8_splat(<8 x i8> %b) {
822821
; CHECK-LABEL: @test_const_shl_sub_bw_minus_1_slt_0_v8i8_splat(
823-
; CHECK-NEXT: [[SUB:%.*]] = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, [[B:%.*]]
824-
; CHECK-NEXT: [[SHL:%.*]] = shl <8 x i8> <i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42>, [[SUB]]
825-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt <8 x i8> [[SHL]], zeroinitializer
822+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw <8 x i8> <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>, [[B:%.*]]
823+
; CHECK-NEXT: [[TMP2:%.*]] = and <8 x i8> [[TMP1]], <i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42>
824+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne <8 x i8> [[TMP2]], zeroinitializer
826825
; CHECK-NEXT: ret <8 x i1> [[CMP]]
827826
;
828827
%sub = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, %b
@@ -846,9 +845,9 @@ define <8 x i1> @test_const_shl_sub_bw_minus_1_slt_0_v8i8_splat_poison_1(<8 x i8
846845

847846
define <8 x i1> @test_const_shl_sub_bw_minus_1_slt_0_v8i8_splat_poison_2(<8 x i8> %b) {
848847
; CHECK-LABEL: @test_const_shl_sub_bw_minus_1_slt_0_v8i8_splat_poison_2(
849-
; CHECK-NEXT: [[SUB:%.*]] = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, [[B:%.*]]
850-
; CHECK-NEXT: [[SHL:%.*]] = shl <8 x i8> <i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 poison>, [[SUB]]
851-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt <8 x i8> [[SHL]], zeroinitializer
848+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw <8 x i8> <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>, [[B:%.*]]
849+
; CHECK-NEXT: [[TMP2:%.*]] = and <8 x i8> [[TMP1]], <i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 42, i8 poison>
850+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne <8 x i8> [[TMP2]], zeroinitializer
852851
; CHECK-NEXT: ret <8 x i1> [[CMP]]
853852
;
854853
%sub = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, %b
@@ -859,9 +858,9 @@ define <8 x i1> @test_const_shl_sub_bw_minus_1_slt_0_v8i8_splat_poison_2(<8 x i8
859858

860859
define <8 x i1> @test_const_shl_sub_bw_minus_1_slt_0_v8i8_nonsplat(<8 x i8> %b) {
861860
; CHECK-LABEL: @test_const_shl_sub_bw_minus_1_slt_0_v8i8_nonsplat(
862-
; CHECK-NEXT: [[SUB:%.*]] = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, [[B:%.*]]
863-
; CHECK-NEXT: [[SHL:%.*]] = shl <8 x i8> <i8 42, i8 43, i8 44, i8 45, i8 46, i8 47, i8 48, i8 49>, [[SUB]]
864-
; CHECK-NEXT: [[CMP:%.*]] = icmp slt <8 x i8> [[SHL]], zeroinitializer
861+
; CHECK-NEXT: [[TMP1:%.*]] = shl nuw <8 x i8> <i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1, i8 1>, [[B:%.*]]
862+
; CHECK-NEXT: [[TMP2:%.*]] = and <8 x i8> [[TMP1]], <i8 42, i8 43, i8 44, i8 45, i8 46, i8 47, i8 48, i8 49>
863+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne <8 x i8> [[TMP2]], zeroinitializer
865864
; CHECK-NEXT: ret <8 x i1> [[CMP]]
866865
;
867866
%sub = sub <8 x i8> <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>, %b

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)