Skip to content

Commit 462e102

Browse files
authored
[InstCombine] Fold (X / C) < X and (X >> C) < X into X > 0 (#85555)
Proofs: https://alive2.llvm.org/ce/z/52droC This resolves #85313.
1 parent 478c420 commit 462e102

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7163,6 +7163,40 @@ Instruction *InstCombinerImpl::foldICmpCommutative(ICmpInst::Predicate Pred,
71637163
if (Value *V = foldICmpWithLowBitMaskedVal(Pred, Op0, Op1, Q, *this))
71647164
return replaceInstUsesWith(CxtI, V);
71657165

7166+
// Folding (X / Y) pred X => X swap(pred) 0 for constant Y other than 0 or 1
7167+
{
7168+
const APInt *Divisor;
7169+
if (match(Op0, m_UDiv(m_Specific(Op1), m_APInt(Divisor))) &&
7170+
Divisor->ugt(1)) {
7171+
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
7172+
Constant::getNullValue(Op1->getType()));
7173+
}
7174+
7175+
if (!ICmpInst::isUnsigned(Pred) &&
7176+
match(Op0, m_SDiv(m_Specific(Op1), m_APInt(Divisor))) &&
7177+
Divisor->ugt(1)) {
7178+
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
7179+
Constant::getNullValue(Op1->getType()));
7180+
}
7181+
}
7182+
7183+
// Another case of this fold is (X >> Y) pred X => X swap(pred) 0 if Y != 0
7184+
{
7185+
const APInt *Shift;
7186+
if (match(Op0, m_LShr(m_Specific(Op1), m_APInt(Shift))) &&
7187+
!Shift->isZero()) {
7188+
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
7189+
Constant::getNullValue(Op1->getType()));
7190+
}
7191+
7192+
if ((Pred == CmpInst::ICMP_SLT || Pred == CmpInst::ICMP_SGE) &&
7193+
match(Op0, m_AShr(m_Specific(Op1), m_APInt(Shift))) &&
7194+
!Shift->isZero()) {
7195+
return new ICmpInst(ICmpInst::getSwappedPredicate(Pred), Op1,
7196+
Constant::getNullValue(Op1->getType()));
7197+
}
7198+
}
7199+
71667200
return nullptr;
71677201
}
71687202

llvm/test/Transforms/InstCombine/icmp-div-constant.ll

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,3 +375,144 @@ define i1 @sdiv_eq_smin_use(i32 %x, i32 %y) {
375375
%r = icmp eq i32 %d, -2147483648
376376
ret i1 %r
377377
}
378+
379+
; Fold (X / C) cmp X into X ~cmp 0 (~cmp is the inverse predicate of cmp), for some C != 1
380+
; Alternative form of this fold is when division is replaced with logic right shift
381+
382+
define i1 @sdiv_x_by_const_cmp_x(i32 %x) {
383+
; CHECK-LABEL: @sdiv_x_by_const_cmp_x(
384+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 0
385+
; CHECK-NEXT: ret i1 [[TMP1]]
386+
;
387+
%v = sdiv i32 %x, 13
388+
%r = icmp eq i32 %v, %x
389+
ret i1 %r
390+
}
391+
392+
define i1 @udiv_x_by_const_cmp_x(i32 %x) {
393+
; CHECK-LABEL: @udiv_x_by_const_cmp_x(
394+
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[X:%.*]], 0
395+
; CHECK-NEXT: ret i1 [[TMP1]]
396+
;
397+
%1 = udiv i32 %x, 123
398+
%2 = icmp slt i32 %1, %x
399+
ret i1 %2
400+
}
401+
402+
; Same as above but with right shift instead of division (C != 0)
403+
404+
define i1 @lshr_x_by_const_cmp_x(i32 %x) {
405+
; CHECK-LABEL: @lshr_x_by_const_cmp_x(
406+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 0
407+
; CHECK-NEXT: ret i1 [[TMP1]]
408+
;
409+
%v = lshr i32 %x, 1
410+
%r = icmp eq i32 %v, %x
411+
ret i1 %r
412+
}
413+
414+
define <4 x i1> @lshr_by_const_cmp_sle_value(<4 x i32> %x) {
415+
; CHECK-LABEL: @lshr_by_const_cmp_sle_value(
416+
; CHECK-NEXT: [[R:%.*]] = icmp sgt <4 x i32> [[X:%.*]], <i32 -1, i32 -1, i32 -1, i32 -1>
417+
; CHECK-NEXT: ret <4 x i1> [[R]]
418+
;
419+
%v = lshr <4 x i32> %x, <i32 3, i32 3, i32 3, i32 3>
420+
%r = icmp sle <4 x i32> %v, %x
421+
ret <4 x i1> %r
422+
}
423+
424+
define i1 @lshr_by_const_cmp_sge_value(i32 %x) {
425+
; CHECK-LABEL: @lshr_by_const_cmp_sge_value(
426+
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[X:%.*]], 1
427+
; CHECK-NEXT: ret i1 [[R]]
428+
;
429+
%v = lshr i32 %x, 3
430+
%r = icmp sge i32 %v, %x
431+
ret i1 %r
432+
}
433+
434+
define i1 @ashr_x_by_const_cmp_sge_x(i32 %x) {
435+
; CHECK-LABEL: @ashr_x_by_const_cmp_sge_x(
436+
; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[X:%.*]], 1
437+
; CHECK-NEXT: ret i1 [[R]]
438+
;
439+
%v = ashr i32 %x, 5
440+
%r = icmp sge i32 %v, %x
441+
ret i1 %r
442+
}
443+
444+
; Negative test - constant is 1
445+
446+
define <2 x i1> @udiv_x_by_const_cmp_eq_value_neg(<2 x i32> %x) {
447+
; CHECK-LABEL: @udiv_x_by_const_cmp_eq_value_neg(
448+
; CHECK-NEXT: [[V:%.*]] = udiv <2 x i32> [[X:%.*]], <i32 1, i32 3>
449+
; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[V]], [[X]]
450+
; CHECK-NEXT: ret <2 x i1> [[R]]
451+
;
452+
%v = udiv <2 x i32> %x, <i32 1, i32 3>
453+
%r = icmp eq <2 x i32> %v, %x
454+
ret <2 x i1> %r
455+
}
456+
457+
define <2 x i1> @sdiv_x_by_const_cmp_eq_value_neg(<2 x i32> %x) {
458+
; CHECK-LABEL: @sdiv_x_by_const_cmp_eq_value_neg(
459+
; CHECK-NEXT: [[V:%.*]] = sdiv <2 x i32> [[X:%.*]], <i32 1, i32 3>
460+
; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[V]], [[X]]
461+
; CHECK-NEXT: ret <2 x i1> [[R]]
462+
;
463+
%v = sdiv <2 x i32> %x, <i32 1, i32 3>
464+
%r = icmp eq <2 x i32> %v, %x
465+
ret <2 x i1> %r
466+
}
467+
468+
; Negative test - constant is 0
469+
470+
define <2 x i1> @lshr_x_by_const_cmp_slt_value_neg(<2 x i32> %x) {
471+
; CHECK-LABEL: @lshr_x_by_const_cmp_slt_value_neg(
472+
; CHECK-NEXT: [[V:%.*]] = lshr <2 x i32> [[X:%.*]], <i32 0, i32 2>
473+
; CHECK-NEXT: [[R:%.*]] = icmp slt <2 x i32> [[V]], [[X]]
474+
; CHECK-NEXT: ret <2 x i1> [[R]]
475+
;
476+
%v = lshr <2 x i32> %x, <i32 0, i32 2>
477+
%r = icmp slt <2 x i32> %v, %x
478+
ret <2 x i1> %r
479+
}
480+
481+
; Negative test - unsigned predicate with sdiv
482+
483+
define i1 @sdiv_x_by_const_cmp_ult_value_neg(i32 %x) {
484+
; CHECK-LABEL: @sdiv_x_by_const_cmp_ult_value_neg(
485+
; CHECK-NEXT: [[V:%.*]] = sdiv i32 [[X:%.*]], 3
486+
; CHECK-NEXT: [[R:%.*]] = icmp ult i32 [[V]], [[X]]
487+
; CHECK-NEXT: ret i1 [[R]]
488+
;
489+
%v = sdiv i32 %x, 3
490+
%r = icmp ult i32 %v, %x
491+
ret i1 %r
492+
}
493+
494+
; Negative case - one of the components of a vector is 1
495+
496+
define <4 x i1> @sdiv_x_by_const_cmp_sgt_value_neg(<4 x i32> %x) {
497+
; CHECK-LABEL: @sdiv_x_by_const_cmp_sgt_value_neg(
498+
; CHECK-NEXT: [[V:%.*]] = sdiv <4 x i32> [[X:%.*]], <i32 1, i32 2, i32 3, i32 4>
499+
; CHECK-NEXT: [[R:%.*]] = icmp sgt <4 x i32> [[V]], [[X]]
500+
; CHECK-NEXT: ret <4 x i1> [[R]]
501+
;
502+
%v = sdiv <4 x i32> %x, <i32 1, i32 2, i32 3, i32 4>
503+
%r = icmp sgt <4 x i32> %v, %x
504+
ret <4 x i1> %r
505+
}
506+
507+
; Negative case - ashr only allows sge/slt predicates
508+
509+
define i1 @ashr_x_by_const_cmp_sle_value_neg(i32 %x) {
510+
; CHECK-LABEL: @ashr_x_by_const_cmp_sle_value_neg(
511+
; CHECK-NEXT: [[V:%.*]] = ashr i32 [[X:%.*]], 3
512+
; CHECK-NEXT: [[R:%.*]] = icmp sle i32 [[V]], [[X]]
513+
; CHECK-NEXT: ret i1 [[R]]
514+
;
515+
%v = ashr i32 %x, 3
516+
%r = icmp sle i32 %v, %x
517+
ret i1 %r
518+
}

0 commit comments

Comments
 (0)