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

Sanjay Patel via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 26 13:49:43 PST 2021


Author: Erik Desjardins
Date: 2021-11-26T16:48:33-05:00
New Revision: 53b00b821582a95c5186bd76e7e7d12d2697a630

URL: https://github.com/llvm/llvm-project/commit/53b00b821582a95c5186bd76e7e7d12d2697a630
DIFF: https://github.com/llvm/llvm-project/commit/53b00b821582a95c5186bd76e7e7d12d2697a630.diff

LOG: [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

Added: 
    

Modified: 
    llvm/lib/Analysis/InstructionSimplify.cpp
    llvm/test/Transforms/InstSimplify/compare.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index a411c4338e235..cab3455b98197 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 <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

diff  --git a/llvm/test/Transforms/InstSimplify/compare.ll b/llvm/test/Transforms/InstSimplify/compare.ll
index 223ad6466fa40..dce8f95ef7f1d 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)


        


More information about the llvm-commits mailing list