From 53b00b821582a95c5186bd76e7e7d12d2697a630 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Fri, 26 Nov 2021 16:25:08 -0500 Subject: [PATCH] [InstSimplify] Fold X {lshr,udiv} C 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 --- llvm/lib/Analysis/InstructionSimplify.cpp | 37 ++++++++++++++++++-- llvm/test/Transforms/InstSimplify/compare.ll | 32 +++++------------ 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index a411c4338e23..cab3455b9819 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -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 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 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 diff --git a/llvm/test/Transforms/InstSimplify/compare.ll b/llvm/test/Transforms/InstSimplify/compare.ll index 223ad6466fa4..dce8f95ef7f1 100644 --- a/llvm/test/Transforms/InstSimplify/compare.ll +++ b/llvm/test/Transforms/InstSimplify/compare.ll @@ -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)