Skip to content

Commit

Permalink
[InstSimplify] Fold x*C1/C2 <= x (PR48744)
Browse files Browse the repository at this point in the history
We can fold x*C1/C2 <= x to true if C1 <= C2. This is valid even
if the multiplication is not nuw: https://alive2.llvm.org/ce/z/vULors

The multiplication or division can be replaced by shifts. We don't
handle the case where both are shifts, as that should get folded
away by InstCombine.
  • Loading branch information
nikic committed Jan 17, 2021
1 parent 4bfbfb9 commit a13c0f6
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 28 deletions.
22 changes: 22 additions & 0 deletions llvm/lib/Analysis/InstructionSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2901,6 +2901,28 @@ static Value *simplifyICmpWithBinOpOnLHS(
return getTrue(ITy);
}

// (x*C1)/C2 <= x for C1 <= C2.
// This holds even if the multiplication overflows: Assume that x != 0 and
// arithmetic is modulo M. For overflow to occur we must have C1 >= M/x and
// thus C2 >= M/x. It follows that (x*C1)/C2 <= (M-1)/C2 <= ((M-1)*x)/M < x.
//
// Additionally, either the multiplication and division might be represented
// as shifts:
// (x*C1)>>C2 <= x for C1 < 2**C2.
// (x<<C1)/C2 <= x for 2**C1 < C2.
const APInt *C1, *C2;
if ((match(LBO, m_UDiv(m_Mul(m_Specific(RHS), m_APInt(C1)), m_APInt(C2))) &&
C1->ule(*C2)) ||
(match(LBO, m_LShr(m_Mul(m_Specific(RHS), m_APInt(C1)), m_APInt(C2))) &&
C1->ule(APInt(C2->getBitWidth(), 1) << *C2)) ||
(match(LBO, m_UDiv(m_Shl(m_Specific(RHS), m_APInt(C1)), m_APInt(C2))) &&
(APInt(C1->getBitWidth(), 1) << *C1).ule(*C2))) {
if (Pred == ICmpInst::ICMP_UGT)
return getFalse(ITy);
if (Pred == ICmpInst::ICMP_ULE)
return getTrue(ITy);
}

return nullptr;
}

Expand Down
35 changes: 7 additions & 28 deletions llvm/test/Transforms/InstSimplify/icmp.ll
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ define i1 @poison2(i32 %x) {

define i1 @mul_div_cmp_smaller(i8 %x) {
; CHECK-LABEL: @mul_div_cmp_smaller(
; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], 3
; CHECK-NEXT: [[DIV:%.*]] = udiv i8 [[MUL]], 4
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[DIV]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%mul = mul i8 %x, 3
%div = udiv i8 %mul, 4
Expand All @@ -52,10 +49,7 @@ define i1 @mul_div_cmp_smaller(i8 %x) {

define i1 @mul_div_cmp_equal(i8 %x) {
; CHECK-LABEL: @mul_div_cmp_equal(
; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], 3
; CHECK-NEXT: [[DIV:%.*]] = udiv i8 [[MUL]], 3
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[DIV]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%mul = mul i8 %x, 3
%div = udiv i8 %mul, 3
Expand All @@ -78,10 +72,7 @@ define i1 @mul_div_cmp_greater(i8 %x) {
}
define i1 @mul_div_cmp_ugt(i8 %x) {
; CHECK-LABEL: @mul_div_cmp_ugt(
; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], 3
; CHECK-NEXT: [[DIV:%.*]] = udiv i8 [[MUL]], 4
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[DIV]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 false
;
%mul = mul i8 %x, 3
%div = udiv i8 %mul, 4
Expand Down Expand Up @@ -133,10 +124,7 @@ define i1 @mul_div_cmp_wrong_operand(i8 %x, i8 %y) {

define i1 @mul_lshr_cmp_smaller(i8 %x) {
; CHECK-LABEL: @mul_lshr_cmp_smaller(
; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], 3
; CHECK-NEXT: [[DIV:%.*]] = lshr i8 [[MUL]], 2
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[DIV]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%mul = mul i8 %x, 3
%div = lshr i8 %mul, 2
Expand All @@ -146,10 +134,7 @@ define i1 @mul_lshr_cmp_smaller(i8 %x) {

define i1 @mul_lshr_cmp_equal(i8 %x) {
; CHECK-LABEL: @mul_lshr_cmp_equal(
; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], 4
; CHECK-NEXT: [[DIV:%.*]] = lshr i8 [[MUL]], 2
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[DIV]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%mul = mul i8 %x, 4
%div = lshr i8 %mul, 2
Expand All @@ -172,10 +157,7 @@ define i1 @mul_lshr_cmp_greater(i8 %x) {

define i1 @shl_div_cmp_smaller(i8 %x) {
; CHECK-LABEL: @shl_div_cmp_smaller(
; CHECK-NEXT: [[MUL:%.*]] = shl i8 [[X:%.*]], 2
; CHECK-NEXT: [[DIV:%.*]] = udiv i8 [[MUL]], 5
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[DIV]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%mul = shl i8 %x, 2
%div = udiv i8 %mul, 5
Expand All @@ -185,10 +167,7 @@ define i1 @shl_div_cmp_smaller(i8 %x) {

define i1 @shl_div_cmp_equal(i8 %x) {
; CHECK-LABEL: @shl_div_cmp_equal(
; CHECK-NEXT: [[MUL:%.*]] = shl i8 [[X:%.*]], 2
; CHECK-NEXT: [[DIV:%.*]] = udiv i8 [[MUL]], 4
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[DIV]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%mul = shl i8 %x, 2
%div = udiv i8 %mul, 4
Expand Down

0 comments on commit a13c0f6

Please sign in to comment.