[InstSimplify] Fold X {lshr,udiv} C <u X --> true for nonzero X, non-identity C

This eliminates the bounds check in Rust code like

pub fn mid(data: &[i32]) -> i32 {
  if data.is_empty() { return 0; }
  return data[data.len()/2];
}

(from https://blog.sigplan.org/2021/11/18/undefined-behavior-deserves-a-better-reputation/)

Alive proofs:
lshr https://alive2.llvm.org/ce/z/nyTu8D
udiv https://alive2.llvm.org/ce/z/CNUZH7

Differential Revision: https://reviews.llvm.org/D114279
This commit is contained in:
Erik Desjardins 2021-11-26 16:25:08 -05:00 committed by Sanjay Patel
parent 06f1d63cb1
commit 53b00b8215
2 changed files with 43 additions and 26 deletions

View File

@ -2960,8 +2960,10 @@ static Value *simplifyICmpWithBinOpOnLHS(
return getFalse(ITy);
}
// x >> y <=u x
// x udiv y <=u x.
// x >>u y <=u x --> true.
// x >>u y >u x --> false.
// x udiv y <=u x --> true.
// x udiv y >u x --> false.
if (match(LBO, m_LShr(m_Specific(RHS), m_Value())) ||
match(LBO, m_UDiv(m_Specific(RHS), m_Value()))) {
// icmp pred (X op Y), X
@ -2971,6 +2973,37 @@ static Value *simplifyICmpWithBinOpOnLHS(
return getTrue(ITy);
}
// If x is nonzero:
// x >>u C <u x --> true for C != 0.
// x >>u C != x --> true for C != 0.
// x >>u C >=u x --> false for C != 0.
// x >>u C == x --> false for C != 0.
// x udiv C <u x --> true for C != 1.
// x udiv C != x --> true for C != 1.
// x udiv C >=u x --> false for C != 1.
// x udiv C == x --> false for C != 1.
// TODO: allow non-constant shift amount/divisor
const APInt *C;
if ((match(LBO, m_LShr(m_Specific(RHS), m_APInt(C))) && *C != 0) ||
(match(LBO, m_UDiv(m_Specific(RHS), m_APInt(C))) && *C != 1)) {
if (isKnownNonZero(RHS, Q.DL, 0, Q.AC, Q.CxtI, Q.DT)) {
switch (Pred) {
default:
break;
case ICmpInst::ICMP_EQ:
case ICmpInst::ICMP_UGE:
return getFalse(ITy);
case ICmpInst::ICMP_NE:
case ICmpInst::ICMP_ULT:
return getTrue(ITy);
case ICmpInst::ICMP_UGT:
case ICmpInst::ICMP_ULE:
// UGT/ULE are handled by the more general case just above
llvm_unreachable("Unexpected UGT/ULE, should have been handled");
}
}
}
// (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

View File

@ -582,9 +582,7 @@ define i1 @lshr_nonzero_eq(i32 %x) {
; CHECK-LABEL: @lshr_nonzero_eq(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = lshr i32 [[X]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 false
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)
@ -597,9 +595,7 @@ define i1 @lshr_nonzero_uge(i32 %x) {
; CHECK-LABEL: @lshr_nonzero_uge(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = lshr i32 [[X]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 false
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)
@ -612,9 +608,7 @@ define i1 @lshr_nonzero_ne(i32 %x) {
; CHECK-LABEL: @lshr_nonzero_ne(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = lshr i32 [[X]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)
@ -627,9 +621,7 @@ define i1 @lshr_nonzero_ult(i32 %x) {
; CHECK-LABEL: @lshr_nonzero_ult(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = lshr i32 [[X]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)
@ -998,9 +990,7 @@ define i1 @udiv_nonzero_eq(i32 %x) {
; CHECK-LABEL: @udiv_nonzero_eq(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = udiv i32 [[X]], 3
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 false
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)
@ -1013,9 +1003,7 @@ define i1 @udiv_nonzero_uge(i32 %x) {
; CHECK-LABEL: @udiv_nonzero_uge(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = udiv i32 [[X]], 3
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 false
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)
@ -1028,9 +1016,7 @@ define i1 @udiv_nonzero_ne(i32 %x) {
; CHECK-LABEL: @udiv_nonzero_ne(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = udiv i32 [[X]], 3
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)
@ -1043,9 +1029,7 @@ define i1 @udiv_nonzero_ult(i32 %x) {
; CHECK-LABEL: @udiv_nonzero_ult(
; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i32 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NE_0]])
; CHECK-NEXT: [[LHS:%.*]] = udiv i32 [[X]], 3
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[LHS]], [[X]]
; CHECK-NEXT: ret i1 [[CMP]]
; CHECK-NEXT: ret i1 true
;
%x_ne_0 = icmp ne i32 %x, 0
call void @llvm.assume(i1 %x_ne_0)